From d7a947866b9bd344817c72d64bd0898316b12cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Wed, 11 Nov 2015 20:13:41 +0200 Subject: main/ca-certificates: fix and optimize update-ca-certificates fixes #4772 --- main/ca-certificates/APKBUILD | 8 +- main/ca-certificates/update-ca.c | 299 +++++++++++++++++++-------------------- 2 files changed, 150 insertions(+), 157 deletions(-) diff --git a/main/ca-certificates/APKBUILD b/main/ca-certificates/APKBUILD index 6b5ee01c05..e6a74ecfa2 100644 --- a/main/ca-certificates/APKBUILD +++ b/main/ca-certificates/APKBUILD @@ -7,7 +7,7 @@ _nmu="+nmu${pkgver#*_p}" [ "$_nmu" = "+nmu${pkgver}" ] && _nmu="" _ver=${pkgver} -pkgrel=2 +pkgrel=3 pkgdesc="Common CA certificates PEM files" url="http://packages.debian.org/sid/ca-certificates" arch="all" @@ -65,8 +65,8 @@ EOF } md5sums="717455f13fb31fd014a11a468ea3895d ca-certificates_20150426.tar.xz -0fb7db0c3c2fb694ad80b71a3093f57d update-ca.c" +a4d6874d07ecee797b4c0f6a76f663bd update-ca.c" sha256sums="37dbaa93ed64cc4ae93ac295f9248fbc741bd51376438cfb1257f17efab5494f ca-certificates_20150426.tar.xz -2bf5b1455e95026cf0a946526b398fc4512e7a9f1daeb7b949c8c0ce12d54761 update-ca.c" +ee01326ae155ae4f31fc6eb3e53ce667dce1eb4150d74e96f0d4f1eea3faf085 update-ca.c" sha512sums="920dfc512c018c5338bf07b6a6afcb664d9bfba659d4233ca9e87471d5e0ed05de054c96f3d7e6091549aa6deb46106a79f7f982696081f9b2164e18133eb34d ca-certificates_20150426.tar.xz -8c9cfad84039f089f990862cf1b7624a73ec0f600707834e0c05a6d1910a4779ecd7c685884d25fc8597563539ea8860235784f1d6878be0e2393ba02b2e04ed update-ca.c" +97fc20703cca75418e38721b45020f9aec9e3e32ed2bc6e1c6632f5755a46602502291b5c780d8da371525f4ee2e74f94e8573f159cb8023591f23e6759d8afc update-ca.c" diff --git a/main/ca-certificates/update-ca.c b/main/ca-certificates/update-ca.c index c58d16c838..f13da69fb2 100644 --- a/main/ca-certificates/update-ca.c +++ b/main/ca-certificates/update-ca.c @@ -6,189 +6,188 @@ #include #include #include -#include #include #include +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + #define CERTSDIR "/usr/share/ca-certificates/" #define LOCALCERTSDIR "/usr/local/share/ca-certificates/" #define ETCCERTSDIR "/etc/ssl/certs/" #define CERTBUNDLE "ca-certificates.crt" #define CERTSCONF "/etc/ca-certificates.conf" +static const char *last_component(const char *path) +{ + const char *c = strrchr(path, '/'); + if (c) return c + 1; + return path; +} static bool str_begins(const char* str, const char* prefix) { return !strncmp(str, prefix, strlen(prefix)); } -/* A string pair */ -struct pair -{ - char** first; - char** second; +struct hash_item { + struct hash_item *next; + char *key; + char *value; +}; - /* Total size */ - unsigned size; - /* Fill-level */ - unsigned count; +struct hash { + struct hash_item *items[256]; }; -static void pair_free(struct pair* data) +static unsigned int hash_string(const char *str) { - int i = 0; - for (i = 0; i < data->size; i++) { - free(data->first[i]); - free(data->second[i]); - } - free(data->first); - free(data->second); - free(data); + unsigned long h = 5381; + for (; *str; str++) + h = (h << 5) + h + *str; + return h; } -static struct pair* pair_alloc(int size) +static void hash_init(struct hash *h) { - struct pair* d = (struct pair*) malloc(sizeof(struct pair)); - d->size = size; - d->count = 0; - d->first = calloc(size, sizeof(char** )); - d->second = calloc(size, sizeof(char** )); - - return d; + memset(h, 0, sizeof *h); } -static const char* -get_pair(struct pair* data, const char* key, int* pos) +static struct hash_item *hash_get(struct hash *h, const char *key) { - int i = 0; - for (i = 0; i < data->size; i++) { - *pos = i; - if (data->second[i] && data->first[i]) { - if (str_begins(key, data->second[i])) - return data->first[i]; - else if (str_begins(key, data->first[i])) - return data->second[i]; - } + unsigned int bucket = hash_string(key) % ARRAY_SIZE(h->items); + struct hash_item *item; + + for (item = h->items[bucket]; item; item = item->next) + if (strcmp(item->key, key) == 0) + return item; + return NULL; +} + +static void hash_foreach(struct hash *h, void (*cb)(struct hash_item *)) +{ + struct hash_item *item; + int i; + + for (i = 0; i < ARRAY_SIZE(h->items); i++) { + for (item = h->items[i]; item; item = item->next) + cb(item); } - - return 0; } -static bool -add_ca_from_pem(struct pair* data, const char* ca, const char* pem) +static bool hash_add(struct hash *h, const char *key, const char *value) { - int count = data->count++; - if (count >= data->size) + unsigned int bucket = hash_string(key) % ARRAY_SIZE(h->items); + size_t keylen = strlen(key), valuelen = strlen(value); + struct hash_item *i; + + i = malloc(sizeof(struct hash_item) + keylen + 1 + valuelen + 1); + if (!i) return false; - - data->first[count] = strdup(ca); - data->second[count] = strdup(pem); + i->key = (char*)(i+1); + strcpy(i->key, key); + i->value = i->key + keylen + 1; + strcpy(i->value, value); + + i->next = h->items[bucket]; + h->items[bucket] = i; return true; } static bool copyfile(const char* source, int output) { - int input; - if ((input = open(source, O_RDONLY)) == -1) - return -1; - off_t bytes = 0; struct stat fileinfo = {0}; - fstat(input, &fileinfo); - int result = sendfile(output, input, &bytes, fileinfo.st_size); + ssize_t result; + int in_fd; + + if ((in_fd = open(source, O_RDONLY)) == -1) + return false; + + if (fstat(in_fd, &fileinfo) < 0) { + close(in_fd); + return false; + } - close(input); + result = sendfile(output, in_fd, &bytes, fileinfo.st_size); + close(in_fd); - return (fileinfo.st_size == result); + return fileinfo.st_size == result; } -typedef void (*proc_path)(const char*, struct pair*, int); +typedef void (*proc_path)(const char *fullpath, struct hash *, int); -static void proc_localglobaldir(const char* path, struct pair* d, int tmpfile_fd) +static void proc_localglobaldir(const char *fullpath, struct hash *h, int tmpfile_fd) { - /* basename() requires we duplicate the string */ - char* base = strdup(path); - char* tmp_file = strdup(basename(base)); - int base_len = strlen(tmp_file); - char* actual_file = 0; - - /* Snip off the .crt suffix*/ - if (base_len > 4 && strcmp(&tmp_file[base_len - 4], ".crt") == 0) - tmp_file[base_len - 4] = 0; - - bool build_string = asprintf(&actual_file, "%s%s%s", "ca-cert-", - tmp_file, ".pem") != -1; - - if (base_len > 0 && build_string) { - char* s; - for (s = actual_file; *s != 0; s++) { - switch(*s) { - case ',': - case ' ': - *s = '_'; - break; - case ')': - case '(': - *s = '='; - break; - default: - break; - } - } + const char *fname = last_component(fullpath); + size_t flen = strlen(fname); + char *s, *actual_file = NULL; + + /* Snip off the .crt suffix */ + if (flen > 4 && strcmp(&fname[flen-4], ".crt") == 0) + flen -= 4; + + if (asprintf(&actual_file, "%s%.*s%s", + "ca-cert-", + flen, fname, + ".pem") == -1) { + fprintf(stderr, "Cannot open path: %s\n", fullpath); + return; + } - if (add_ca_from_pem(d, path, actual_file)) { - if (copyfile(path, tmpfile_fd) == -1) - printf("Cant copy %s\n", path); - } else { - printf("Warn! Cannot add: %s\n", path); + for (s = actual_file; *s; s++) { + switch(*s) { + case ',': + case ' ': + *s = '_'; + break; + case ')': + case '(': + *s = '='; + break; + default: + break; } - } else { - printf("Can't open path: %s\n", path); } - free(base); + if (!hash_add(h, actual_file, fullpath)) + fprintf(stderr, "Warning! Cannot hash: %s\n", fullpath); + if (!copyfile(fullpath, tmpfile_fd)) + fprintf(stderr, "Warning! Cannot copy to bundle: %s\n", fullpath); free(actual_file); - free(tmp_file); } -static void proc_etccertsdir(const char* path, struct pair* d, int tmpfile_fd) +static void proc_etccertsdir(const char* fullpath, struct hash* h, int tmpfile_fd) { - struct stat statbuf; + char linktarget[SYMLINK_MAX]; + ssize_t linklen; - if (lstat(path, &statbuf) == -1) + linklen = readlink(fullpath, linktarget, sizeof(linktarget)-1); + if (linklen < 0) return; + linktarget[linklen] = 0; - char* fullpath = (char*) malloc(sizeof(char*) * statbuf.st_size + 1); - if (readlink(path, fullpath, statbuf.st_size + 1) == -1) - return; - - char* base = strdup(path); - const char* actual_file = basename(base); - int pos = -1; - const char* target = get_pair(d, actual_file, &pos); - - if (!target) { + struct hash_item *item = hash_get(h, last_component(fullpath)); + if (!item) { /* Symlink exists but is not wanted * Delete it if it points to 'our' directory */ - if (str_begins(fullpath, CERTSDIR) || str_begins(fullpath, LOCALCERTSDIR)) - remove(fullpath); - } else if (strncmp(fullpath, target, strlen(fullpath)) != 0) { + if (str_begins(linktarget, CERTSDIR) || str_begins(linktarget, LOCALCERTSDIR)) + unlink(fullpath); + } else if (strcmp(linktarget, item->value) != 0) { /* Symlink exists but points wrong */ - if (symlink(target, path) == -1) - printf("Warning! Can't link %s -> %s\n", target, path); + unlink(fullpath); + if (symlink(item->value, fullpath) < 0) + fprintf(stderr, "Warning! Cannot update symlink %s -> %s\n", item->value, fullpath); + item->value = 0; } else { /* Symlink exists and is ok */ - memset(d->first[pos], 0, strlen(d->first[pos])); + item->value = 0; } - - free(base); - free(fullpath); } -static bool file_readline(const char* file, struct pair* d, int tmpfile_fd) +static bool read_global_ca_list(const char* file, struct hash* d, int tmpfile_fd) { FILE * fp = fopen(file, "r"); if (fp == NULL) @@ -199,14 +198,13 @@ static bool file_readline(const char* file, struct pair* d, int tmpfile_fd) ssize_t read; while ((read = getline(&line, &len, fp)) != -1) { + /* getline returns number of bytes in buffer, and buffer + * contains delimeter if it was found */ + if (read > 0 && line[read-1] == '\n') + line[read-1] = 0; if (str_begins(line, "#") || str_begins(line, "!")) continue; - char* newline = strstr(line, "\n"); - if (newline) { - line[newline - line] = '\0'; - } - char* fullpath = 0; if (asprintf(&fullpath,"%s%s", CERTSDIR, line) != -1) { proc_localglobaldir(fullpath, d, tmpfile_fd); @@ -215,9 +213,7 @@ static bool file_readline(const char* file, struct pair* d, int tmpfile_fd) } fclose(fp); - if (line) - free(line); - + free(line); return true; } @@ -241,7 +237,7 @@ static bool is_filetype(const char* path, filetype file_check) return false; } -static bool dir_readfiles(struct pair* d, const char* path, +static bool dir_readfiles(struct hash* d, const char* path, filetype allowed_file_type, proc_path path_processor, int tmpfile_fd) @@ -255,7 +251,6 @@ static bool dir_readfiles(struct pair* d, const char* path, if (str_begins(dirp->d_name, ".")) continue; - int size = strlen(path) + strlen(dirp->d_name); char* fullpath = 0; if (asprintf(&fullpath, "%s%s", path, dirp->d_name) != -1) { if (is_filetype(fullpath, allowed_file_type)) @@ -268,45 +263,46 @@ static bool dir_readfiles(struct pair* d, const char* path, return closedir(dp) == 0; } +static void update_ca_symlink(struct hash_item *item) +{ + if (!item->value) + return; + + char* newpath = 0; + bool build_str = asprintf(&newpath, "%s%s", ETCCERTSDIR, item->key); + if (!build_str || symlink(item->value, newpath) == -1) + fprintf(stderr, "Warning! Cannot symlink %s -> %s\n", + item->value, newpath); + free(newpath); +} + int main(int a, char **v) { - struct pair* calinks = pair_alloc(256); + struct hash _calinks, *calinks = &_calinks; const char* bundle = "bundleXXXXXX"; - int etccertslen = strlen(ETCCERTSDIR); char* tmpfile = 0; if (asprintf(&tmpfile, "%s%s", ETCCERTSDIR, bundle) == -1) - return 0; + return 1; int fd = mkstemp(tmpfile); if (fd == -1) { - printf("Failed to open temporary file %s for ca bundle\n", tmpfile); - exit(0); + fprintf(stderr, "Failed to open temporary file %s for ca bundle\n", tmpfile); + return 1; } fchmod(fd, 0644); + hash_init(calinks); + /* Handle global CA certs from config file */ - file_readline(CERTSCONF, calinks, fd); + read_global_ca_list(CERTSCONF, calinks, fd); /* Handle local CA certificates */ dir_readfiles(calinks, LOCALCERTSDIR, FILE_REGULAR, &proc_localglobaldir, fd); /* Update etc cert dir for additions and deletions*/ dir_readfiles(calinks, ETCCERTSDIR, FILE_LINK, &proc_etccertsdir, fd); - - int i = 0; - for (i = 0; i < calinks->count; i++) { - if (!strlen(calinks->first[i])) - continue; - int file_len = strlen(calinks->second[i]); - char* newpath = 0; - bool build_str = asprintf(&newpath, "%s%s", ETCCERTSDIR, - calinks->second[i]) != -1; - if (!build_str || symlink(calinks->first[i], newpath) == -1) - printf("Warning! Can't link %s -> %s\n", - calinks->first[i], newpath); - free(newpath); - } + hash_foreach(calinks, update_ca_symlink); /* Update hashes and the bundle */ if (fd != -1) { @@ -318,16 +314,13 @@ int main(int a, char **v) } } - pair_free(calinks); free(tmpfile); /* Execute c_rehash */ int nullfd = open("/dev/null", O_WRONLY); if (nullfd == -1) - return 0; - if (dup2(nullfd, STDOUT_FILENO) == -1) - return 0; - + return 1; + dup2(nullfd, STDOUT_FILENO); char* c_rehash_args[] = { "/usr/bin/c_rehash", ETCCERTSDIR, 0 }; execve(c_rehash_args[0], c_rehash_args, NULL); -- cgit v1.2.3