Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 May 2025 16:00:00 GMT
From:      Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= <des@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: fb33dd91ae8a - releng/14.3 - certctl: Split certificate bundles before processing.
Message-ID:  <202505301600.54UG004c081869@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch releng/14.3 has been updated by des:

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

commit fb33dd91ae8a5a3cc07e2eac5637b96e88e11bd7
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-10-05 14:50:01 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-05-30 15:59:32 +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
    
    (cherry picked from commit a401c8cb26b22688087ad7c5ee527718459df15a)
    
    certctl: Fix recent regressions.
    
    - If an untrusted certificate is also found in the list of trusted
      certificate, issue a warning and skip it, but don't fail.
    - Split on -+BEGIN CERTIFICATE-+ instead of "Certificate:" since
      that's what we're really looking for.
    
    Also fix a long-standing bug: .crl files are not certificates, so we
    should not include them when searching for certificates.
    
    Reported by:    madpilot, netchild, tijl
    Reviewed by:    netchild, allanjude
    Differential Revision:  https://reviews.freebsd.org/D42276
    
    (cherry picked from commit 87945a082980260b52507ad5bfb3a0ce773a80da)
    
    certctl: Convert line endings before inspecting files.
    
    This ensures that certificate files or bundles with DOS or Mac line
    endings are recognized as such and handled identically to those with
    Unix line endings.
    
    PR:             274952
    Reviewed by:    allanjude
    Differential Revision:  https://reviews.freebsd.org/D42490
    
    (cherry picked from commit f7d16a627efa8ba610eb9b8a12dd67b6cdbb2542)
    
    certctl: Revert to symlinks.
    
    Unfortunately tar will not be able to extract base.txz to a system where
    /etc and /usr are not on the same filesystem if the certificates are
    hard links.
    
    PR:             277828
    Reviewed by:    mp
    Differential Revision:  https://reviews.freebsd.org/D44496
    
    (cherry picked from commit 3fed4f0db53a66a0135bea5c491431eb3348cf51)
    (cherry picked from commit 9c34a6876a60dc10fda6ad6a0cbe8f99a372aadc)
    
    Approved by:    re (cperciva)
---
 usr.sbin/certctl/certctl.sh | 105 ++++++++++++++++++++++++++++----------------
 1 file changed, 68 insertions(+), 37 deletions(-)

diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh
index 5af8fecb93ac..458f5c53682f 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,20 @@ perform()
 	fi
 }
 
+cert_files_in()
+{
+	find -L "$@" -type f \( \
+	     -name '*.pem' -or \
+	     -name '*.crt' -or \
+	     -name '*.cer' \
+	\) 2>/dev/null
+}
+
+eolcvt()
+{
+	cat "$@" | tr -s '\r' '\n'
+}
+
 do_hash()
 {
 	local hash
@@ -93,23 +106,32 @@ get_decimal()
 	return 0
 }
 
-create_trusted_link()
+create_trusted()
 {
 	local hash certhash otherfile otherhash
 	local suffix
+	local link=${2:+-lrs}
 
 	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)"
-			return 1
+			info "Skipping untrusted certificate $hash ($otherfile)"
+			return 0
+		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 +159,7 @@ resolve_certname()
 create_untrusted()
 {
 	local srcfile filename
+	local link=${2:+-lrs}
 
 	set -- $(resolve_certname "$1")
 	srcfile=$1
@@ -147,12 +170,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 +184,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 $(eolcvt "$CFILE" | egrep -c '^-+BEGIN CERTIFICATE-+$') in
+		0)
+			;;
+		1)
+			"$CFUNC" "$CFILE" link
+			;;
+		*)
+			verbose "Multiple certificates found, splitting..."
+			SPLITDIR=$(mktemp -d)
+			eolcvt "$CFILE" | egrep '^(---|[0-9A-Za-z/+=]+$)' | \
+				split -p '^-+BEGIN CERTIFICATE-+$' - "$SPLITDIR/x"
+			for CERT in $(find "$SPLITDIR" -type f) ; do
+				"$CFUNC" "$CERT"
+			done
+			rm -rf "$SPLITDIR"
+			;;
+		esac
 	done
 }
 
@@ -175,43 +210,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()



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202505301600.54UG004c081869>