summaryrefslogtreecommitdiffstats
path: root/main/openssl/c_rehash.c
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2013-12-31 13:43:38 +0200
committerTimo Teräs <timo.teras@iki.fi>2013-12-31 13:44:51 +0200
commit94998965c2563e48e8ce172ac6b3a3db819740f8 (patch)
treea2af073e06cf5a601c6f36c055503e528cd97e77 /main/openssl/c_rehash.c
parentf9b9fff33279f090d6d32345a12f28562db3f137 (diff)
downloadaports-94998965c2563e48e8ce172ac6b3a3db819740f8.tar.bz2
aports-94998965c2563e48e8ce172ac6b3a3db819740f8.tar.xz
main/openssl: rewrite c_rehash in C for speed
fixes #2533
Diffstat (limited to 'main/openssl/c_rehash.c')
-rw-r--r--main/openssl/c_rehash.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/main/openssl/c_rehash.c b/main/openssl/c_rehash.c
new file mode 100644
index 000000000..a46ec4533
--- /dev/null
+++ b/main/openssl/c_rehash.c
@@ -0,0 +1,235 @@
+/* c_rehash.c - C implementation based on the Perl and shell versions
+ *
+ * Copyright (c) 2013 Timo Teräs <timo.teras@iki.fi>
+ * 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 <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+#define countof(x) (sizeof(x) / sizeof(x[0]))
+
+struct hash_info {
+ struct hash_info *next;
+ int type;
+ unsigned long hash;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+};
+
+enum Type {
+ TYPE_CERT = 0,
+ TYPE_CRL,
+ MAX_TYPES,
+};
+
+static const EVP_MD *evpmd;
+static int evpmdsize;
+static const char *prefixes[MAX_TYPES] = { "", "r" };
+static struct hash_info *hash_table[257];
+
+static int get_link_id(int type, unsigned long hash, unsigned char *digest)
+{
+ 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;
+}
+
+static int link_file(int dirfd, const char *filename, int type, unsigned long hash, unsigned char *digest)
+{
+ char linkfn[32];
+ int id;
+
+ id = get_link_id(type, hash, digest);
+ if (id < 0) {
+ fprintf(stderr, "WARNING: Skipping duplicate certificate in file %s\n",
+ filename);
+ return -1;
+ }
+
+ 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);
+
+ 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)
+{
+ STACK_OF(X509_INFO) *inf;
+ X509_INFO *x;
+ BIO *b;
+ int i, count = 0;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+
+ b = BIO_openat(dirfd, filename);
+ if (!b)
+ return -1;
+
+ inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL);
+ BIO_free(b);
+ if (!inf)
+ return -1;
+
+ for(i = 0; i < sk_X509_INFO_num(inf); i++) {
+ x = sk_X509_INFO_value(inf, i);
+ if (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) {
+ 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) {
+ fprintf(stderr,
+ "WARNING: %s does not contain a certificate or CRL: skipping\n",
+ filename);
+ }
+
+ return count;
+}
+
+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;
+}
+
+static int hash_dir(const char *dirname)
+{
+ struct dirent *de;
+ struct stat st;
+ int dirfd;
+ DIR *d;
+
+ fprintf(stdout, "Doing %s\n", dirname);
+ dirfd = open(dirname, O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0) {
+ perror(dirname);
+ return -1;
+ }
+ 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 (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);
+ }
+ closedir(d);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ const char *env;
+ int i;
+
+ evpmd = EVP_sha1();
+ evpmdsize = EVP_MD_size(evpmd);
+
+ if (argc > 1) {
+ for (i = 1; i < argc; i++)
+ hash_dir(argv[i]);
+ } else if ((env = getenv("SSL_CERT_DIR")) != NULL) {
+ char *e, *m;
+ m = strdup(env);
+ for (e = strtok(m, ":"); e != NULL; e = strtok(NULL, ":"))
+ hash_dir(e);
+ free(m);
+ } else {
+ hash_dir("/etc/ssl/certs");
+ }
+
+ return 0;
+}