diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8 index 4387745462f9..6eff3b910bbe 100644 --- a/usr.sbin/certctl/certctl.8 +++ b/usr.sbin/certctl/certctl.8 @@ -1,128 +1,133 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .\" Copyright 2018 Allan Jude .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted providing that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY .\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, .\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd January 7, 2021 +.Dd July 13, 2022 .Dt CERTCTL 8 .Os .Sh NAME .Nm certctl .Nd "tool for managing trusted and blacklist TLS certificates" .Sh SYNOPSIS .Nm .Op Fl v .Ic list .Nm .Op Fl v .Ic blacklisted .Nm .Op Fl nUv .Op Fl D Ar destdir .Op Fl M Ar metalog .Ic rehash .Nm .Op Fl nv .Ic blacklist Ar file .Nm .Op Fl nv .Ic unblacklist Ar file .Sh DESCRIPTION The .Nm utility manages the list of TLS Certificate Authorities that are trusted by applications that use OpenSSL. .Pp Flags: .Bl -tag -width 4n .It Fl D Ar destdir Specify the DESTDIR (overriding values from the environment). +.It Fl d Ar distbase +Specify the DISTBASE (overriding values from the environment). .It Fl M Ar metalog Specify the path of the METALOG file (default: $DESTDIR/METALOG). .It Fl n No-Op mode, do not actually perform any actions. .It Fl v Be verbose, print details about actions before performing them. .It Fl U Unprivileged mode, do not change the ownership of created links. Do record the ownership in the METALOG file. .El .Pp Primary command functions: .Bl -tag -width blacklisted .It Ic list List all currently trusted certificate authorities. .It Ic blacklisted List all currently blacklisted certificates. .It Ic rehash Rebuild the list of trusted certificate authorities by scanning all directories in .Ev TRUSTPATH and all blacklisted certificates in .Ev BLACKLISTPATH . A symbolic link to each trusted certificate is placed in .Ev CERTDESTDIR and each blacklisted certificate in .Ev BLACKLISTDESTDIR . .It Ic blacklist Add the specified file to the blacklist. .It Ic unblacklist Remove the specified file from the blacklist. .El .Sh ENVIRONMENT .Bl -tag -width BLACKLISTDESTDIR .It Ev DESTDIR Alternate destination directory to operate on. +.It Ev DISTBASE +Additional path component to include when operating on certificate directories. .It Ev TRUSTPATH List of paths to search for trusted certificates. Default: -.Pa /usr/share/certs/trusted -.Pa /usr/local/share/certs /usr/local/etc/ssl/certs +.Pa /usr/share/certs/trusted +.Pa /usr/local/share/certs +.Pa /usr/local/etc/ssl/certs .It Ev BLACKLISTPATH List of paths to search for blacklisted certificates. Default: -.Pa /usr/share/certs/blacklisted -.Pa /usr/local/etc/ssl/blacklisted +.Pa /usr/share/certs/blacklisted +.Pa /usr/local/etc/ssl/blacklisted .It Ev CERTDESTDIR Destination directory for symbolic links to trusted certificates. Default: -.Pa /etc/ssl/certs +.Pa /etc/ssl/certs .It Ev BLACKLISTDESTDIR Destination directory for symbolic links to blacklisted certificates. Default: -.Pa /etc/ssl/blacklisted +.Pa /etc/ssl/blacklisted .It Ev EXTENSIONS List of file extensions to read as certificate files. Default: *.pem *.crt *.cer *.crl *.0 .El .Sh SEE ALSO .Xr openssl 1 .Sh HISTORY .Nm first appeared in .Fx 12.2 .Sh AUTHORS .An Allan Jude Aq Mt allanjude@freebsd.org diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh index fb966df95c55..1324ca00d6f3 100755 --- a/usr.sbin/certctl/certctl.sh +++ b/usr.sbin/certctl/certctl.sh @@ -1,304 +1,306 @@ #!/bin/sh #- # SPDX-License-Identifier: BSD-2-Clause # # Copyright 2018 Allan Jude # # Redistribution and use in source and binary forms, with or without # modification, are permitted providing that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ ############################################################ CONFIGURATION : ${DESTDIR:=} +: ${DISTBASE:=} : ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$"} : ${VERBOSE:=0} ############################################################ GLOBALS SCRIPTNAME="${0##*/}" ERRORS=0 NOOP=0 UNPRIV=0 ############################################################ FUNCTIONS do_hash() { local hash if hash=$( openssl x509 -noout -subject_hash -in "$1" ); then echo "$hash" return 0 else echo "Error: $1" >&2 ERRORS=$(( $ERRORS + 1 )) return 1 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 blisthash certhash hash local suffix hash=$( do_hash "$1" ) || return 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" } # Accepts either dot-hash form from `certctl list` or a path to a valid cert. resolve_certname() { 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") suffix=$(get_decimal "$BLACKLISTDESTDIR" "$hash") filename="$hash.$suffix" echo "$srcfile" "$hash.$suffix" elif [ -e "${CERTDESTDIR}/$1" ]; then srcfile=$(realpath "${CERTDESTDIR}/$1") hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//') suffix=$(get_decimal "$BLACKLISTDESTDIR" "$hash") filename="$hash.$suffix" echo "$srcfile" "$hash.$suffix" fi } create_blacklisted() { local srcfile filename set -- $(resolve_certname "$1") srcfile=$1 filename=$2 if [ -z "$srcfile" -o -z "$filename" ]; then return fi [ $VERBOSE -gt 0 ] && echo "Adding $filename to blacklist" [ $NOOP -eq 0 ] && install ${INSTALLFLAGS} -lrs "$srcfile" "$BLACKLISTDESTDIR/$filename" } do_scan() { local CFUNC CSEARCH CPATH CFILE local oldIFS="$IFS" CFUNC="$1" CSEARCH="$2" IFS=: set -- $CSEARCH IFS="$oldIFS" for CPATH in "$@"; do [ -d "$CPATH" ] || continue echo "Scanning $CPATH for certificates..." for CFILE in $(ls -1 "${CPATH}" | grep -Ee "${FILEPAT}"); do [ -e "$CPATH/$CFILE" ] || continue [ $VERBOSE -gt 0 ] && echo "Reading $CFILE" "$CFUNC" "$CPATH/$CFILE" done done } do_list() { local CFILE subject if [ -e "$1" ]; then cd "$1" for CFILE in *.[0-9]; do if [ ! -s "$CFILE" ]; then echo "Unable to read $CFILE" >&2 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 } cmd_rehash() { if [ $NOOP -eq 0 ]; then if [ -e "$CERTDESTDIR" ]; then find "$CERTDESTDIR" -type link -delete else mkdir -p "$CERTDESTDIR" fi if [ -e "$BLACKLISTDESTDIR" ]; then find "$BLACKLISTDESTDIR" -type link -delete else mkdir -p "$BLACKLISTDESTDIR" fi fi do_scan create_blacklisted "$BLACKLISTPATH" do_scan create_trusted_link "$TRUSTPATH" } cmd_list() { echo "Listing Trusted Certificates:" do_list "$CERTDESTDIR" } cmd_blacklist() { local BPATH shift # verb [ $NOOP -eq 0 ] && mkdir -p "$BLACKLISTDESTDIR" for BFILE in "$@"; do echo "Adding $BFILE to blacklist" create_blacklisted "$BFILE" done } cmd_unblacklist() { local BFILE blisthash certhash hash shift # verb for BFILE in "$@"; do if [ -s "$BFILE" ]; then hash=$( do_hash "$BFILE" ) 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" else echo "Cannot find $BFILE" >&2 ERRORS=$(( $ERRORS + 1 )) fi done } cmd_blacklisted() { echo "Listing Blacklisted Certificates:" do_list "$BLACKLISTDESTDIR" } usage() { exec >&2 echo "Manage the TLS trusted certificates on the system" echo " $SCRIPTNAME [-v] list" echo " List trusted certificates" echo " $SCRIPTNAME [-v] blacklisted" echo " List blacklisted certificates" - echo " $SCRIPTNAME [-nUv] [-D ] [-M ] rehash" + echo " $SCRIPTNAME [-nUv] [-D ] [-d ] [-M ] rehash" echo " Generate hash links for all certificates" echo " $SCRIPTNAME [-nv] blacklist " echo " Add to the list of blacklisted certificates" echo " $SCRIPTNAME [-nv] unblacklist " echo " Remove from the list of blacklisted certificates" exit 64 } ############################################################ MAIN -while getopts D:M:nUv flag; do +while getopts D:d:M:nUv flag; do case "$flag" in D) DESTDIR=${OPTARG} ;; + d) DISTBASE=${OPTARG} ;; M) METALOG=${OPTARG} ;; n) NOOP=1 ;; U) UNPRIV=1 ;; v) VERBOSE=$(( $VERBOSE + 1 )) ;; esac done shift $(( $OPTIND - 1 )) : ${METALOG:=${DESTDIR}/METALOG} INSTALLFLAGS= [ $UNPRIV -eq 1 ] && INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR}" : ${LOCALBASE:=$(sysctl -n user.localbase)} -: ${TRUSTPATH:=${DESTDIR}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs} -: ${BLACKLISTPATH:=${DESTDIR}/usr/share/certs/blacklisted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted} -: ${CERTDESTDIR:=${DESTDIR}/etc/ssl/certs} -: ${BLACKLISTDESTDIR:=${DESTDIR}/etc/ssl/blacklisted} +: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs} +: ${BLACKLISTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/blacklisted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted} +: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs} +: ${BLACKLISTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/blacklisted} [ $# -gt 0 ] || usage case "$1" in list) cmd_list ;; rehash) cmd_rehash ;; blacklist) cmd_blacklist "$@" ;; unblacklist) cmd_unblacklist "$@" ;; blacklisted) cmd_blacklisted ;; *) usage # NOTREACHED esac retval=$? [ $ERRORS -gt 0 ] && echo "Encountered $ERRORS errors" >&2 exit $retval ################################################################################ # END ################################################################################