The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=a401c8cb26b22688087ad7c5ee527718459df15a

commit a401c8cb26b22688087ad7c5ee527718459df15a
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2023-10-05 14:50:01 +0000
Commit:     Dag-Erling Smørgrav <[email protected]>
CommitDate: 2023-10-05 15:11:22 +0000

    certctl: Split certificate bundles before processing.
    
    This allows 'certctl rehash' to do the right thing when ca_root_nss is
    installed, instead of linking the entire bundle to the hash of the
    first certificate it contains.
    
    MFC after:      3 days
    Reviewed by:    allanjude
    Differential Revision:  https://reviews.freebsd.org/D42087
---
 usr.sbin/certctl/certctl.sh | 99 ++++++++++++++++++++++++++++-----------------
 1 file changed, 63 insertions(+), 36 deletions(-)

diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh
index 02d055102c33..b7d3a95bc7d7 100755
--- a/usr.sbin/certctl/certctl.sh
+++ b/usr.sbin/certctl/certctl.sh
@@ -32,7 +32,6 @@ set -u
 
 : ${DESTDIR:=}
 : ${DISTBASE:=}
-: ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$"}
 
 ############################################################ GLOBALS
 
@@ -63,6 +62,16 @@ perform()
        fi
 }
 
+cert_files_in()
+{
+       find -L "$@" -type f \( \
+            -name '*.pem' -or \
+            -name '*.crt' -or \
+            -name '*.cer' -or \
+            -name '*.crl' \
+       \) 2>/dev/null
+}
+
 do_hash()
 {
        local hash
@@ -93,23 +102,32 @@ get_decimal()
        return 0
 }
 
-create_trusted_link()
+create_trusted()
 {
        local hash certhash otherfile otherhash
        local suffix
+       local link=${2:+-lm}
 
        hash=$(do_hash "$1") || return
        certhash=$(openssl x509 -sha1 -in "$1" -noout -fingerprint)
        for otherfile in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do
                otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout 
-fingerprint)
                if [ "$certhash" = "$otherhash" ] ; then
-                       info "Skipping untrusted certificate $1 ($otherfile)"
+                       info "Skipping untrusted certificate $hash ($otherfile)"
                        return 1
                fi
        done
+       for otherfile in $(find $CERTDESTDIR -name "$hash.*") ; do
+               otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout 
-fingerprint)
+               if [ "$certhash" = "$otherhash" ] ; then
+                       verbose "Skipping duplicate entry for certificate $hash"
+                       return 0
+               fi
+       done
        suffix=$(get_decimal "$CERTDESTDIR" "$hash")
        verbose "Adding $hash.$suffix to trust store"
-       perform install ${INSTALLFLAGS} -lrs "$(realpath "$1")" 
"$CERTDESTDIR/$hash.$suffix"
+       perform install ${INSTALLFLAGS} -m 0444 ${link} \
+               "$(realpath "$1")" "$CERTDESTDIR/$hash.$suffix"
 }
 
 # Accepts either dot-hash form from `certctl list` or a path to a valid cert.
@@ -137,6 +155,7 @@ resolve_certname()
 create_untrusted()
 {
        local srcfile filename
+       local link=${2:+-lm}
 
        set -- $(resolve_certname "$1")
        srcfile=$1
@@ -147,12 +166,13 @@ create_untrusted()
        fi
 
        verbose "Adding $filename to untrusted list"
-       perform install ${INSTALLFLAGS} -lrs "$srcfile" 
"$UNTRUSTDESTDIR/$filename"
+       perform install ${INSTALLFLAGS} -m 0444 ${link} \
+               "$srcfile" "$UNTRUSTDESTDIR/$filename"
 }
 
 do_scan()
 {
-       local CFUNC CSEARCH CPATH CFILE
+       local CFUNC CSEARCH CPATH CFILE CERT SPLITDIR
        local oldIFS="$IFS"
        CFUNC="$1"
        CSEARCH="$2"
@@ -160,14 +180,25 @@ do_scan()
        IFS=:
        set -- $CSEARCH
        IFS="$oldIFS"
-       for CPATH in "$@"; do
-               [ -d "$CPATH" ] || continue
-               info "Scanning $CPATH for certificates..."
-               for CFILE in $(ls -1 "${CPATH}" | grep -Ee "${FILEPAT}") ; do
-                       [ -e "$CPATH/$CFILE" ] || continue
-                       verbose "Reading $CFILE"
-                       "$CFUNC" "$CPATH/$CFILE"
-               done
+       for CFILE in $(cert_files_in "$@") ; do
+               verbose "Reading $CFILE"
+               case $(grep -c '^Certificate:$' "$CFILE") in
+               0)
+                       ;;
+               1)
+                       "$CFUNC" "$CFILE" link
+                       ;;
+               *)
+                       verbose "Multiple certificates found, splitting..."
+                       SPLITDIR=$(mktemp -d)
+                       egrep '^[^#]' "$CFILE" | \
+                               split -p '^Certificate:$' - "$SPLITDIR/x"
+                       for CERT in $(find "$SPLITDIR" -type f) ; do
+                               "$CFUNC" "$CERT"
+                       done
+                       rm -rf "$SPLITDIR"
+                       ;;
+               esac
        done
 }
 
@@ -175,43 +206,39 @@ do_list()
 {
        local CFILE subject
 
-       if [ -e "$1" ] ; then
-               cd "$1"
-               for CFILE in *.[0-9] ; do
-                       if [ ! -s "$CFILE" ] ; then
-                               info "Unable to read $CFILE"
-                               ERRORS=$((ERRORS + 1))
-                               continue
-                       fi
-                       subject=
-                       if [ $VERBOSE -eq 0 ] ; then
-                               subject=$(openssl x509 -noout -subject -nameopt 
multiline -in "$CFILE" |
-                                   sed -n '/commonName/s/.*= //p')
-                       fi
-                       [ "$subject" ] ||
-                           subject=$(openssl x509 -noout -subject -in "$CFILE")
-                       printf "%s\t%s\n" "$CFILE" "$subject"
-               done
-               cd -
-       fi
+       for CFILE in $(find "$@" \( -type f -or -type l \) -name '*.[0-9]') ; do
+               if [ ! -s "$CFILE" ] ; then
+                       info "Unable to read $CFILE"
+                       ERRORS=$((ERRORS + 1))
+                       continue
+               fi
+               subject=
+               if ! "$VERBOSE" ; then
+                       subject=$(openssl x509 -noout -subject -nameopt 
multiline -in "$CFILE" | sed -n '/commonName/s/.*= //p')
+               fi
+               if [ -z "$subject" ] ; then
+                       subject=$(openssl x509 -noout -subject -in "$CFILE")
+               fi
+               printf "%s\t%s\n" "${CFILE##*/}" "$subject"
+       done
 }
 
 cmd_rehash()
 {
 
        if [ -e "$CERTDESTDIR" ] ; then
-               perform find "$CERTDESTDIR" -type link -delete
+               perform find "$CERTDESTDIR" \( -type f -or -type l \) -delete
        else
                perform install -d -m 0755 "$CERTDESTDIR"
        fi
        if [ -e "$UNTRUSTDESTDIR" ] ; then
-               perform find "$UNTRUSTDESTDIR" -type link -delete
+               perform find "$UNTRUSTDESTDIR" \( -type f -or -type l \) -delete
        else
                perform install -d -m 0755 "$UNTRUSTDESTDIR"
        fi
 
        do_scan create_untrusted "$UNTRUSTPATH"
-       do_scan create_trusted_link "$TRUSTPATH"
+       do_scan create_trusted "$TRUSTPATH"
 }
 
 cmd_list()

Reply via email to