Skip to content

Commit

Permalink
Add helper binary for bulk certificate processing.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
busykai committed Apr 6, 2018
1 parent f7f936a commit a9f59fa
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.o
clrtrust-helper
clrtrust
25 changes: 21 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
148 changes: 148 additions & 0 deletions clrtrust-helper.c
Original file line number Diff line number Diff line change
@@ -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: <filename>\t<fingerprint or hash>.
*
* 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 <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

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"
" <filename><TAB><fingerprint or subject hash>\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;
}
46 changes: 34 additions & 12 deletions clrtrust → clrtrust.in
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}

Expand Down

0 comments on commit a9f59fa

Please sign in to comment.