opnsense-src/usr.sbin/certctl/certctl.sh
Ed Maste 5469872913 certctl: Set METALOG ownership to root:wheel
This sets the correct ownership values when building base.txz

install(1) does not validate the arguments passed to -o or -g
(see PR283355) so there's no need to have the passwd db available
for now.  Future work includes plumbing the appropriate passwd db
path through certctl, and validating uid and gid in install(1).

PR:		283340
Reviewed by:	jrtc27
Differential Revision: https://reviews.freebsd.org/D48506

(cherry picked from commit 10fa3f2518d4582c98d74527f79af9f30b1eceab)
(cherry picked from commit 98bebc20cef7527ccb15f8defc9d52e803a0d506)
(cherry picked from commit 4d15b58365ea706129bedfdb37e0c5e8661a640f)
2025-02-20 08:07:17 -05:00

335 lines
8.1 KiB
Bash
Executable file

#!/bin/sh
#-
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright 2018 Allan Jude <allanjude@freebsd.org>
#
# 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.
#
set -u
############################################################ CONFIGURATION
: ${DESTDIR:=}
: ${DISTBASE:=}
: ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$"}
############################################################ GLOBALS
SCRIPTNAME="${0##*/}"
ERRORS=0
NOOP=false
UNPRIV=false
VERBOSE=false
############################################################ FUNCTIONS
info()
{
echo "${0##*/}: $@" >&2
}
verbose()
{
if "${VERBOSE}" ; then
info "$@"
fi
}
perform()
{
if ! "${NOOP}" ; then
"$@"
fi
}
do_hash()
{
local hash
if hash=$(openssl x509 -noout -subject_hash -in "$1") ; then
echo "$hash"
return 0
else
info "Error: $1"
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 hash certhash otherfile otherhash
local suffix
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
fi
done
suffix=$(get_decimal "$CERTDESTDIR" "$hash")
verbose "Adding $hash.$suffix to trust store"
perform 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 "$UNTRUSTDESTDIR" "$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 "$UNTRUSTDESTDIR" "$hash")
filename="$hash.$suffix"
echo "$srcfile" "$hash.$suffix"
fi
}
create_untrusted()
{
local srcfile filename
set -- $(resolve_certname "$1")
srcfile=$1
filename=$2
if [ -z "$srcfile" -o -z "$filename" ] ; then
return
fi
verbose "Adding $filename to untrusted list"
perform install ${INSTALLFLAGS} -lrs "$srcfile" "$UNTRUSTDESTDIR/$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
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
done
}
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
}
cmd_rehash()
{
if [ -e "$CERTDESTDIR" ] ; then
perform find "$CERTDESTDIR" -type link -delete
else
perform install -d -m 0755 "$CERTDESTDIR"
fi
if [ -e "$UNTRUSTDESTDIR" ] ; then
perform find "$UNTRUSTDESTDIR" -type link -delete
else
perform install -d -m 0755 "$UNTRUSTDESTDIR"
fi
do_scan create_untrusted "$UNTRUSTPATH"
do_scan create_trusted_link "$TRUSTPATH"
}
cmd_list()
{
info "Listing Trusted Certificates:"
do_list "$CERTDESTDIR"
}
cmd_untrust()
{
local UTFILE
shift # verb
perform install -d -m 0755 "$UNTRUSTDESTDIR"
for UTFILE in "$@"; do
info "Adding $UTFILE to untrusted list"
create_untrusted "$UTFILE"
done
}
cmd_trust()
{
local UTFILE untrustedhash certhash hash
shift # verb
for UTFILE in "$@"; do
if [ -s "$UTFILE" ] ; then
hash=$(do_hash "$UTFILE")
certhash=$(openssl x509 -sha1 -in "$UTFILE" -noout -fingerprint)
for UNTRUSTEDFILE in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do
untrustedhash=$(openssl x509 -sha1 -in "$UNTRUSTEDFILE" -noout -fingerprint)
if [ "$certhash" = "$untrustedhash" ] ; then
info "Removing $(basename "$UNTRUSTEDFILE") from untrusted list"
perform rm -f $UNTRUSTEDFILE
fi
done
elif [ -e "$UNTRUSTDESTDIR/$UTFILE" ] ; then
info "Removing $UTFILE from untrusted list"
perform rm -f "$UNTRUSTDESTDIR/$UTFILE"
else
info "Cannot find $UTFILE"
ERRORS=$((ERRORS + 1))
fi
done
}
cmd_untrusted()
{
info "Listing Untrusted Certificates:"
do_list "$UNTRUSTDESTDIR"
}
usage()
{
exec >&2
echo "Manage the TLS trusted certificates on the system"
echo " $SCRIPTNAME [-v] list"
echo " List trusted certificates"
echo " $SCRIPTNAME [-v] untrusted"
echo " List untrusted certificates"
echo " $SCRIPTNAME [-nUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash"
echo " Generate hash links for all certificates"
echo " $SCRIPTNAME [-nv] untrust <file>"
echo " Add <file> to the list of untrusted certificates"
echo " $SCRIPTNAME [-nv] trust <file>"
echo " Remove <file> from the list of untrusted certificates"
exit 64
}
############################################################ MAIN
while getopts D:d:M:nUv flag; do
case "$flag" in
D) DESTDIR=${OPTARG} ;;
d) DISTBASE=${OPTARG} ;;
M) METALOG=${OPTARG} ;;
n) NOOP=true ;;
U) UNPRIV=true ;;
v) VERBOSE=true ;;
esac
done
shift $((OPTIND - 1))
DESTDIR=${DESTDIR%/}
if ! [ -z "${CERTCTL_VERBOSE:-}" ] ; then
VERBOSE=true
fi
: ${METALOG:=${DESTDIR}/METALOG}
INSTALLFLAGS=
if "$UNPRIV" ; then
INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR} -o root -g wheel"
fi
: ${LOCALBASE:=$(sysctl -n user.localbase)}
: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs}
: ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted}
: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs}
: ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted}
[ $# -gt 0 ] || usage
case "$1" in
list) cmd_list ;;
rehash) cmd_rehash ;;
blacklist) cmd_untrust "$@" ;;
untrust) cmd_untrust "$@" ;;
trust) cmd_trust "$@" ;;
unblacklist) cmd_trust "$@" ;;
untrusted) cmd_untrusted ;;
blacklisted) cmd_untrusted ;;
*) usage # NOTREACHED
esac
retval=$?
if [ $ERRORS -gt 0 ] ; then
info "Encountered $ERRORS errors"
fi
exit $retval
################################################################################
# END
################################################################################