From a9f59fa07bc46753dfebd8befd6a49901848a81f Mon Sep 17 00:00:00 2001 From: Arzhan Kinzhalin Date: Fri, 6 Apr 2018 12:40:58 -0700 Subject: [PATCH] Add helper binary for bulk certificate processing. Instead of launching 'openssl x509' for each file, use small binary to process files in bulk. This optimization improves performance more than 7 times (measured on store generation). Performance of 'clrtrust generate' is important for first-time booting. --- .gitignore | 3 + Makefile | 25 +++++-- clrtrust-helper.c | 148 ++++++++++++++++++++++++++++++++++++++++ clrtrust => clrtrust.in | 46 +++++++++---- 4 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 .gitignore create mode 100644 clrtrust-helper.c rename clrtrust => clrtrust.in (95%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78a6ad6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +clrtrust-helper +clrtrust diff --git a/Makefile b/Makefile index b976ebd..68d4ea6 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,27 @@ # Copyright 2017 Intel Corporation. -all: - @true + +LDFLAGS := -lssl -lcrypto +CFLAGS := -W -Wall -Werror -std=gnu9x + +BINDIR := /usr/bin +LIBEXECDIR := /usr/libexec + +.PHONY: all install check clean + +build: clrtrust-helper clrtrust + +clrtrust-helper: clrtrust-helper.o + +clrtrust: clrtrust.in + cat clrtrust.in | sed -e 's:LIBEXEC_CONFIG_VALUE:$(LIBEXECDIR):' > $@ + chmod +x clrtrust install: - install -D --mode=0755 clrtrust ${INSTALL_ROOT}/usr/bin/clrtrust + install -D --mode=0755 clrtrust ${INSTALL_ROOT}${BINDIR}/clrtrust + install -D --mode=0755 clrtrust-helper ${INSTALL_ROOT}${LIBEXECDIR}/clrtrust-helper -check: +check: build bats -t test +clean: + rm -rf clrtrust-helper clrtrust-helper.o clrtrust diff --git a/clrtrust-helper.c b/clrtrust-helper.c new file mode 100644 index 0000000..03cbea3 --- /dev/null +++ b/clrtrust-helper.c @@ -0,0 +1,148 @@ + +/* This is a helper application which is meant to be used with clrtrust, the + * Clear Linux Trust Store management tool. It is created for performance + * reasons, to process certificates in bulk as opposed to running openssl for + * each certificate file. + * + * It reads the list of files, one filename per line, from the standard input + * and produces output in form of: \t. + * + * Two modes are supported. If '-f' switch is specified on the command line, for + * each file a SHA-1 fingerprint is calculated. If '-s' is specified, then + * subject hash is produced. + */ + +#include +#include +#include +#include +#include +#include + +typedef enum { + MODE_FINGER = 1, /* output fingerprint */ + MODE_HASH, /* output subject hash */ + MODE_INVALID = 100 +} runmode_t; + +void print_help() { + char *help = "clrtrust-helper [-f|-s]\n" + "Helper utility for clrtrust. Reads the list of files from" + " stdin, one per line, and produces either fingerprint or" + " subject hash for each file on stdout in form:\n" + " \n"; + puts(help); +} + +int main(int argc, char **argv) { + + runmode_t runmode = MODE_INVALID; /* global switch: whether to produce fingerprint + or subject hash. */ + + BIO *inbio = NULL; + + /* fingerprint-related variables */ + const EVP_MD *finger_type = NULL; + unsigned int finger_sz; + unsigned char finger[EVP_MAX_MD_SIZE]; + + /* subjecthash-related variables */ + unsigned long subject_hash; + + unsigned int i; + char c; + + char *fname = NULL; + size_t sz = 0; + + while ((c = getopt(argc, argv, "fs")) != -1) { + switch (c) { + case 'f': + if (runmode == MODE_INVALID) { + runmode = MODE_FINGER; + } else { + print_help(); + return 1; + } + break; + case 's': + if (runmode == MODE_INVALID) { + runmode = MODE_HASH; + } else { + print_help(); + return 1; + } + break; + default: + printf("Unrecognized option -%c.\n", c); + print_help(); + return 1; + } + } + + if (runmode == MODE_INVALID) { + print_help(); + return 1; + } + + OpenSSL_add_all_algorithms(); + + finger_type = EVP_sha1(); + + inbio = BIO_new(BIO_s_file()); + + while (getline(&fname, &sz, stdin) != -1) { + int fname_len = strlen(fname); + int err = 1; + FILE *fp = NULL; + X509 *cert = NULL; + + if (fname_len < 1) continue; + if (fname[fname_len-1] == '\n') { + fname[fname_len-1] = '\0'; + } + + if (!(fp = fopen(fname, "r"))) { + goto wrap_up; + } + + if (!BIO_set_fp(inbio, fp, BIO_NOCLOSE)) { + goto wrap_up; + } + + if (!(cert = PEM_read_bio_X509(inbio, NULL, 0, NULL))) { + goto wrap_up; + } + + switch (runmode) { + case MODE_FINGER: + if (!X509_digest(cert, finger_type, finger, &finger_sz)) { + goto wrap_up; + } + printf("%s\t", fname); + for (i=0; i < finger_sz; i++) { + printf("%02X", finger[i]); + if (i < finger_sz-1) printf(":"); + } + putc('\n', stdout); + err = 0; + break; + case MODE_HASH: + subject_hash = X509_subject_name_hash(cert); + printf("%s\t%08lx\n", fname, subject_hash); + err = 0; + break; + default: + return 1; + } + +wrap_up: + if (fp) fclose(fp); + if (cert) X509_free(cert); + if (err) printf("%s\tERROR\n", fname); + } + + if (fname) free(fname); + + return 0; +} diff --git a/clrtrust b/clrtrust.in similarity index 95% rename from clrtrust rename to clrtrust.in index 5530306..11d0538 100755 --- a/clrtrust +++ b/clrtrust.in @@ -108,6 +108,30 @@ EINVAL=22 # invalid argument EBADST=127 # invalid state / health check does not pass EERR=255 # general error +##### LOCATION OF THE HELPER +# always favor env override. if not, prefer the helper that is found next to the +# script itself (it indicates the development environment). if not, look at the +# configured location. +LIBEXEC_DIR=LIBEXEC_CONFIG_VALUE +CLR_HELPER_NAME=clrtrust-helper +if [ -n "${CLR_LIBEXEC_DIR}" ]; then # environment override + CLR_HELPER_CMD="${CLR_LIBEXEC_DIR}/${CLR_HELPER_NAME}" +elif [ -x "$(dirname $0)/${CLR_HELPER_NAME}" ]; then + CLR_HELPER_CMD="$(dirname $0)/${CLR_HELPER_NAME}" +elif [ -x "${LIBEXEC_DIR}/${CLR_HELPER_NAME}" ]; then + CLR_HELPER_CMD="${LIBEXEC_DIR}/${CLR_HELPER_NAME}" +fi + +if [ ! -x "$CLR_HELPER_CMD" ]; then + 1>&2 echo "Cannot locate the helper executable (${CLR_HELPER_NAME})" + exit $EBADST +fi + +# get absolute path to the helper (to be able to run in subshells) +if [[ "$CLR_HELPER_CMD" != /* ]]; then + CLR_HELPER_CMD=$(realpath "${CLR_HELPER_CMD}") +fi + ##### LOCK FILE PATH # absolute dirs where we can have lock, must be writeable. potentially, the # choice of lock directory could differ between two parallel runs of clrtrust, @@ -135,15 +159,7 @@ find_certs() { local dir=$1 if [ -z $dir ]; then return 255; fi if [ ! -d $dir ]; then return 255; fi - find $dir -maxdepth 1 -type f | while read f; do - finger=$(openssl x509 -in "${f}" -noout -fingerprint -sha1 2>/dev/null) - if [ $? -ne 0 ]; then - 1>&2 echo "WARNING: file ${f} is not a certificate" - continue - fi - finger=${finger#SHA1 Fingerprint=} - printf "%s\t%s\n" "${f}" "${finger}" - done + find $dir -maxdepth 1 -type f | "${CLR_HELPER_CMD}" -f return 0 } @@ -166,14 +182,20 @@ c_rehash_internal() { fi ( cd "$dir" - find . -maxdepth 1 -type f | while read f; do - name=$(openssl x509 -in "$f" -noout -subject_hash 2>/dev/null) + hashes=$(find . -maxdepth 1 -type f | "${CLR_HELPER_CMD}" -s) + if [ $? -ne 0 ]; then + return 1 + fi + echo "$hashes" | while IFS=$'\t' read f h; do lnno=0 - while ! ln -s "$f" "${name}.${lnno}" && ((lnno++ < 100)); do + while ! ln -s "$f" "${h}.${lnno}" && ((lnno++ < 100)); do : done 2>/dev/null done ) + if [ $? -ne 0 ]; then + return 1 + fi return 0 }