From 05a16147fb39fb323ecdf2b53f175816196eb9d7 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Wed, 9 Sep 2020 09:08:09 +0000 Subject: [PATCH] certctl: fix hashed link generation with duplicate subjects Currently, certctl rehash will just keep clobbering .0 rather than incrementing the suffix upon encountering a duplicate. Do this, and do it for blacklisted certs as well. This also improves the situation with the blacklist to be a little less flakey, comparing cert fingerprints for all certs with a matching subject hash in the blacklist to determine if the cert we're looking at can be installed. Future work needs to completely revamp the blacklist to align more with how it's described in PR 246614. In particular, /etc/ssl/blacklisted should go away to avoid potential confusion -- OpenSSL will not read it, it's basically certctl internal. PR: 246614 Reviewed by: Michael Osipov Tested by: Michael Osipov With suggestions from: Michael Osipov MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D26167 --- usr.sbin/certctl/certctl.sh | 61 ++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh index aaddfeb3115b..b687a7706041 100755 --- a/usr.sbin/certctl/certctl.sh +++ b/usr.sbin/certctl/certctl.sh @@ -30,7 +30,7 @@ ############################################################ CONFIGURATION : ${DESTDIR:=} -: ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$|\.0$"} +: ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$"} : ${VERBOSE:=0} ############################################################ GLOBALS @@ -56,31 +56,58 @@ do_hash() fi } +get_decimal() +{ + local checkdir hash decimal + + checkdir=$1 + hash=$2 + decimal=0 + + while [ -e "$checkdir/$hash.$decimal" ]; do + decimal=$((decimal + 1)) + done + + echo ${decimal} + return 0 +} + create_trusted_link() { - local hash + local blisthash certhash hash + local suffix hash=$( do_hash "$1" ) || return - if [ -e "$BLACKLISTDESTDIR/$hash.0" ]; then - echo "Skipping blacklisted certificate $1 ($BLACKLISTDESTDIR/$hash.0)" - return 1 - fi - [ $VERBOSE -gt 0 ] && echo "Adding $hash.0 to trust store" - [ $NOOP -eq 0 ] && install ${INSTALLFLAGS} -lrs $(realpath "$1") "$CERTDESTDIR/$hash.0" + certhash=$( openssl x509 -sha1 -in "$1" -noout -fingerprint ) + for blistfile in $(find $BLACKLISTDESTDIR -name "$hash.*"); do + blisthash=$( openssl x509 -sha1 -in "$blistfile" -noout -fingerprint ) + if [ "$certhash" = "$blisthash" ]; then + echo "Skipping blacklisted certificate $1 ($blistfile)" + return 1 + fi + done + suffix=$(get_decimal "$CERTDESTDIR" "$hash") + [ $VERBOSE -gt 0 ] && echo "Adding $hash.$suffix to trust store" + [ $NOOP -eq 0 ] && \ + install ${INSTALLFLAGS} -lrs $(realpath "$1") "$CERTDESTDIR/$hash.$suffix" } create_blacklisted() { local hash srcfile filename + local suffix # If it exists as a file, we'll try that; otherwise, we'll scan if [ -e "$1" ]; then hash=$( do_hash "$1" ) || return srcfile=$(realpath "$1") - filename="$hash.0" + suffix=$(get_decimal "$BLACKLISTDESTDIR" "$hash") + filename="$hash.$suffix" elif [ -e "${CERTDESTDIR}/$1" ]; then srcfile=$(realpath "${CERTDESTDIR}/$1") - filename="$1" + hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//') + suffix=$(get_decimal "$BLACKLISTDESTDIR" "$hash") + filename="$hash.$suffix" else return fi @@ -115,7 +142,7 @@ do_list() if [ -e "$1" ]; then cd "$1" - for CFILE in *.0; do + for CFILE in *.[0-9]; do if [ ! -s "$CFILE" ]; then echo "Unable to read $CFILE" >&2 ERRORS=$(( $ERRORS + 1 )) @@ -174,14 +201,20 @@ cmd_blacklist() cmd_unblacklist() { - local BFILE hash + local BFILE blisthash certhash hash shift # verb for BFILE in "$@"; do if [ -s "$BFILE" ]; then hash=$( do_hash "$BFILE" ) - echo "Removing $hash.0 from blacklist" - [ $NOOP -eq 0 ] && rm -f "$BLACKLISTDESTDIR/$hash.0" + certhash=$( openssl x509 -sha1 -in "$BFILE" -noout -fingerprint ) + for BLISTEDFILE in $(find $BLACKLISTDESTDIR -name "$hash.*"); do + blisthash=$( openssl x509 -sha1 -in "$BLISTEDFILE" -noout -fingerprint ) + if [ "$certhash" = "$blisthash" ]; then + echo "Removing $(basename "$BLISTEDFILE") from blacklist" + [ $NOOP -eq 0 ] && rm -f $BLISTEDFILE + fi + done elif [ -e "$BLACKLISTDESTDIR/$BFILE" ]; then echo "Removing $BFILE from blacklist" [ $NOOP -eq 0 ] && rm -f "$BLACKLISTDESTDIR/$BFILE"