From e7eaf0e63ea44d0f5f51197cf727c94c0da10fda Mon Sep 17 00:00:00 2001 From: Juan RP Date: Mon, 20 Oct 2014 13:57:01 +0200 Subject: [PATCH] libressl: use the latest c_rehash C version. --- srcpkgs/libressl/files/c_rehash.c | 376 +++++++++++++++++++----------- srcpkgs/libressl/template | 2 +- 2 files changed, 238 insertions(+), 140 deletions(-) diff --git a/srcpkgs/libressl/files/c_rehash.c b/srcpkgs/libressl/files/c_rehash.c index a669529d29..e288c3f327 100644 --- a/srcpkgs/libressl/files/c_rehash.c +++ b/srcpkgs/libressl/files/c_rehash.c @@ -1,214 +1,312 @@ -/* c_rehash.c - C implementation based on the Perl and shell versions +/* c_rehash.c - Create hash symlinks for certificates + * C implementation based on the original Perl and shell versions * - * Copyright (c) 2013 Timo Teräs + * Copyright (c) 2013-2014 Timo Teräs * All rights reserved. * * This software is licensed under the MIT License. * Full license available at: http://opensource.org/licenses/MIT */ -#define _POSIX_C_SOURCE 200809L -#define _GNU_SOURCE -#include +/* + * Submitted to OpenSSL: + * http://rt.openssl.org/Ticket/Display.html?id=3505&user=guest&pass=guest + */ #include -#include +#include #include #include #include -#include #include #include #include #include -#define countof(x) (sizeof(x) / sizeof(x[0])) +#define MAX_COLLISIONS 256 +#define countof(x) (sizeof(x) / sizeof(x[0])) -struct hash_info { - struct hash_info *next; - int type; - unsigned long hash; +#if 0 +#define DEBUG(args...) fprintf(stderr, args) +#else +#define DEBUG(args...) +#endif + +struct entry_info { + struct entry_info *next; + char *filename; + unsigned short old_id; + unsigned char need_symlink; unsigned char digest[EVP_MAX_MD_SIZE]; }; +struct bucket_info { + struct bucket_info *next; + struct entry_info *first_entry, *last_entry; + unsigned int hash; + unsigned short type; + unsigned short num_needed; +}; + enum Type { TYPE_CERT = 0, - TYPE_CRL, - MAX_TYPES, + TYPE_CRL }; -static const EVP_MD *evpmd; +static const char *symlink_extensions[] = { "", "r" }; +static const char *file_extensions[] = { "pem", "crt", "cer", "crl" }; + +static int old_compat = 1; static int evpmdsize; -static const char *prefixes[MAX_TYPES] = { "", "r" }; -static struct hash_info *hash_table[257]; +static const EVP_MD *evpmd; -static int get_link_id(int type, unsigned long hash, unsigned char *digest) +static struct bucket_info *hash_table[257]; + +static void bit_set(unsigned char *set, unsigned bit) { - unsigned int bucket = hash % countof(hash_table); - struct hash_info *hi; - int count = 0; - - for (hi = hash_table[bucket]; hi; hi = hi->next) { - if (hi->type != type || hi->hash != hash) - continue; - if (memcmp(digest, hi->digest, evpmdsize) == 0) - return -1; - count++; - } - - hi = malloc(sizeof(*hi)); - hi->next = hash_table[bucket]; - hi->type = type; - hi->hash = hash; - memcpy(hi->digest, digest, evpmdsize); - hash_table[bucket] = hi; - - return count; + set[bit / 8] |= 1 << (bit % 8); } -static int link_file(int dirfd, const char *filename, int type, unsigned long hash, unsigned char *digest) +static int bit_isset(unsigned char *set, unsigned bit) { - char linkfn[32]; - int id; + return set[bit / 8] & (1 << (bit % 8)); +} - id = get_link_id(type, hash, digest); - if (id < 0) { - fprintf(stderr, "WARNING: Skipping duplicate certificate in file %s\n", - filename); - return -1; +static void add_entry( + int type, unsigned int hash, + const char *filename, const unsigned char *digest, + int need_symlink, unsigned short old_id) +{ + struct bucket_info *bi; + struct entry_info *ei, *found = NULL; + unsigned int ndx = (type + hash) % countof(hash_table); + + for (bi = hash_table[ndx]; bi; bi = bi->next) + if (bi->type == type && bi->hash == hash) + break; + if (!bi) { + bi = calloc(1, sizeof(*bi)); + if (!bi) return; + bi->next = hash_table[ndx]; + bi->type = type; + bi->hash = hash; + hash_table[ndx] = bi; } - snprintf(linkfn, sizeof(linkfn), - "%08lx.%s%d", hash, prefixes[type], id); - fprintf(stdout, "%s => %s\n", linkfn, filename); - if (symlinkat(filename, dirfd, linkfn) < 0) - perror(linkfn); + for (ei = bi->first_entry; ei; ei = ei->next) { + if (digest && memcmp(digest, ei->digest, evpmdsize) == 0) { + fprintf(stderr, + "WARNING: Skipping duplicate certificate in file %s\n", + filename); + return; + } + if (!strcmp(filename, ei->filename)) { + found = ei; + if (!digest) break; + } + } + ei = found; + if (!ei) { + if (bi->num_needed >= MAX_COLLISIONS) return; + ei = calloc(1, sizeof(*ei)); + if (!ei) return; + ei->old_id = ~0; + ei->filename = strdup(filename); + if (bi->last_entry) bi->last_entry->next = ei; + if (!bi->first_entry) bi->first_entry = ei; + bi->last_entry = ei; + } + + if (old_id < ei->old_id) ei->old_id = old_id; + if (need_symlink && !ei->need_symlink) { + ei->need_symlink = 1; + bi->num_needed++; + memcpy(ei->digest, digest, evpmdsize); + } +} + +static int handle_symlink(const char *filename, const char *fullpath) +{ + static char xdigit[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15 + }; + char linktarget[NAME_MAX], *endptr; + unsigned int hash = 0; + unsigned char ch; + int i, type, id; + ssize_t n; + + for (i = 0; i < 8; i++) { + ch = filename[i] - '0'; + if (ch >= countof(xdigit) || xdigit[ch] < 0) + return -1; + hash <<= 4; + hash += xdigit[ch]; + } + if (filename[i++] != '.') return -1; + for (type = countof(symlink_extensions) - 1; type > 0; type--) + if (strcasecmp(symlink_extensions[type], &filename[i]) == 0) + break; + i += strlen(symlink_extensions[type]); + + id = strtoul(&filename[i], &endptr, 10); + if (*endptr != 0) return -1; + + n = readlink(fullpath, linktarget, sizeof(linktarget)); + if (n >= sizeof(linktarget) || n < 0) return -1; + linktarget[n] = 0; + + DEBUG("Found existing symlink %s for %08x (%d), certname %s\n", + filename, hash, type, linktarget); + add_entry(type, hash, linktarget, NULL, 0, id); return 0; } -static BIO *BIO_openat(int dirfd, const char *filename) -{ - FILE *fp; - BIO *bio; - int fd; - - fd = openat(dirfd, filename, O_RDONLY); - if (fd < 0) { - perror(filename); - return NULL; - } - fp = fdopen(fd, "r"); - if (fp == NULL) { - close(fd); - return NULL; - } - bio = BIO_new_fp(fp, BIO_CLOSE); - if (!bio) { - fclose(fp); - return NULL; - } - return bio; -} - -static int hash_file(int dirfd, const char *filename) +static int handle_certificate(const char *filename, const char *fullpath) { STACK_OF(X509_INFO) *inf; X509_INFO *x; BIO *b; - int i, count = 0; + const char *ext; unsigned char digest[EVP_MAX_MD_SIZE]; + X509_NAME *name = NULL; + int i, type, ret = -1; - b = BIO_openat(dirfd, filename); - if (!b) - return -1; + ext = strrchr(filename, '.'); + if (ext == NULL) return 0; + for (i = 0; i < countof(file_extensions); i++) { + if (strcasecmp(file_extensions[i], ext+1) == 0) + break; + } + if (i >= countof(file_extensions)) return -1; + b = BIO_new_file(fullpath, "r"); + if (!b) return -1; inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL); BIO_free(b); - if (!inf) - return -1; + if (!inf) return -1; - for(i = 0; i < sk_X509_INFO_num(inf); i++) { - x = sk_X509_INFO_value(inf, i); + if (sk_X509_INFO_num(inf) == 1) { + x = sk_X509_INFO_value(inf, 0); if (x->x509) { + type = TYPE_CERT; + name = X509_get_subject_name(x->x509); X509_digest(x->x509, evpmd, digest, NULL); - link_file(dirfd, filename, TYPE_CERT, - X509_subject_name_hash(x->x509), digest); - count++; - } - if (x->crl) { + } else if (x->crl) { + type = TYPE_CRL; + name = X509_CRL_get_issuer(x->crl); X509_CRL_digest(x->crl, evpmd, digest, NULL); - link_file(dirfd, filename, TYPE_CRL, - X509_NAME_hash(X509_CRL_get_issuer(x->crl)), - digest); - count++; } - } - sk_X509_INFO_pop_free(inf, X509_INFO_free); - - if (count == 0) { + if (name) + add_entry(type, X509_NAME_hash(name), filename, digest, 1, ~0); + if (name && old_compat) + add_entry(type, X509_NAME_hash_old(name), filename, digest, 1, ~0); + } else { fprintf(stderr, - "WARNING: %s does not contain a certificate or CRL: skipping\n", + "WARNING: %s does not contain exactly one certificate or CRL: skipping\n", filename); } - return count; -} + sk_X509_INFO_pop_free(inf, X509_INFO_free); -static int is_hash_filename(const char *fn) -{ - int i; - - for (i = 0; i < 8; i++) - if (!isxdigit(fn[i])) - return 0; - if (fn[i++] != '.') - return 0; - if (fn[i] == 'r') i++; - for (; fn[i] != 0; i++) - if (!isdigit(fn[i])) - return 0; - return 1; + return ret; } static int hash_dir(const char *dirname) { + struct bucket_info *bi, *nextbi; + struct entry_info *ei, *nextei; struct dirent *de; struct stat st; - int dirfd; + unsigned char idmask[MAX_COLLISIONS / 8]; + int i, n, nextid, buflen, ret = -1; + const char *pathsep; + char *buf; DIR *d; - fprintf(stdout, "Doing %s\n", dirname); - dirfd = open(dirname, O_RDONLY | O_DIRECTORY); - if (dirfd < 0) { - perror(dirname); + if (access(dirname, R_OK|W_OK|X_OK) != 0) return -1; - } + + buflen = strlen(dirname); + pathsep = (buflen && dirname[buflen-1] == '/') ? "" : "/"; + buflen += NAME_MAX + 2; + buf = malloc(buflen); + if (buf == NULL) + goto err; + + printf("Doing %s\n", dirname); d = opendir(dirname); - if (!d) { - close(dirfd); - return -1; - } - while ((de = readdir(d)) != NULL) { - if (fstatat(dirfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) - continue; - if (!S_ISLNK(st.st_mode)) - continue; - if (!is_hash_filename(de->d_name)) - continue; + if (!d) goto err; - if (unlinkat(dirfd, de->d_name, 0) < 0) - perror(de->d_name); - } - - rewinddir(d); while ((de = readdir(d)) != NULL) { - if (fnmatch("*.pem", de->d_name, FNM_NOESCAPE) == 0) - hash_file(dirfd, de->d_name); + if (snprintf(buf, buflen, "%s%s%s", dirname, pathsep, de->d_name) >= buflen) + continue; + if (lstat(buf, &st) < 0) + continue; + if (S_ISLNK(st.st_mode) && handle_symlink(de->d_name, buf) == 0) + continue; + handle_certificate(de->d_name, buf); } closedir(d); - return 0; + for (i = 0; i < countof(hash_table); i++) { + for (bi = hash_table[i]; bi; bi = nextbi) { + nextbi = bi->next; + DEBUG("Type %d, hash %08x, num entries %d:\n", bi->type, bi->hash, bi->num_needed); + + nextid = 0; + memset(idmask, 0, (bi->num_needed+7)/8); + for (ei = bi->first_entry; ei; ei = ei->next) + if (ei->old_id < bi->num_needed) + bit_set(idmask, ei->old_id); + + for (ei = bi->first_entry; ei; ei = nextei) { + nextei = ei->next; + DEBUG("\t(old_id %d, need_symlink %d) Cert %s\n", + ei->old_id, ei->need_symlink, + ei->filename); + + if (ei->old_id < bi->num_needed) { + /* Link exists, and is used as-is */ + snprintf(buf, buflen, "%08x.%s%d", bi->hash, symlink_extensions[bi->type], ei->old_id); + printf("%s => %s\n", buf, ei->filename); + } else if (ei->need_symlink) { + /* New link needed (it may replace something) */ + while (bit_isset(idmask, nextid)) + nextid++; + + snprintf(buf, buflen, "%s%s%n%08x.%s%d", + dirname, pathsep, &n, bi->hash, + symlink_extensions[bi->type], + nextid); + printf("%s => %s\n", &buf[n], ei->filename); + unlink(buf); + symlink(ei->filename, buf); + } else { + /* Link to be deleted */ + snprintf(buf, buflen, "%s%s%n%08x.%s%d", + dirname, pathsep, &n, bi->hash, + symlink_extensions[bi->type], + ei->old_id); + DEBUG("nuke %s\n", &buf[n]); + unlink(buf); + } + free(ei->filename); + free(ei); + } + free(bi); + } + hash_table[i] = NULL; + } + + ret = 0; +err: + free(buf); + return ret; } int main(int argc, char **argv) diff --git a/srcpkgs/libressl/template b/srcpkgs/libressl/template index 1dfb2309f1..1138a75dbf 100644 --- a/srcpkgs/libressl/template +++ b/srcpkgs/libressl/template @@ -1,7 +1,7 @@ # Template file for 'libressl' pkgname=libressl version=2.1.1 -revision=3 +revision=4 bootstrap=yes build_style=gnu-configure short_desc="FREE version of the SSL/TLS protocol forked from OpenSSL"