Index: head/usr.sbin/Makefile =================================================================== --- head/usr.sbin/Makefile +++ head/usr.sbin/Makefile @@ -124,6 +124,9 @@ SUBDIR.${MK_BOOTPARAMD}+= bootparamd SUBDIR.${MK_BSDINSTALL}+= bsdinstall SUBDIR.${MK_BSNMP}+= bsnmpd +.if ${MK_CAROOT} != "no" +SUBDIR.${MK_OPENSSL}+= certctl +.endif SUBDIR.${MK_CXGBETOOL}+= cxgbetool SUBDIR.${MK_DIALOG}+= bsdconfig SUBDIR.${MK_EFI}+= efivar efidp efibootmgr Index: head/usr.sbin/certctl/Makefile =================================================================== --- head/usr.sbin/certctl/Makefile +++ head/usr.sbin/certctl/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SCRIPTS=certctl.sh +MAN= certctl.8 + +.include Index: head/usr.sbin/certctl/certctl.8 =================================================================== --- head/usr.sbin/certctl/certctl.8 +++ head/usr.sbin/certctl/certctl.8 @@ -0,0 +1,119 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" 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 February 19, 2019 +.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 nv +.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 n +No-Op mode, do not actually perform any actions. +.It Fl v +be verbose, print details about actions before performing them. +.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 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 +.It Ev BLACKLISTPATH +List of paths to search for blacklisted certificates. +Default: +.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 +.It Ev BLACKLISTDESTDIR +Destination directory for symbolic links to blacklisted certificates. +Default: +.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.0 +.Sh AUTHORS +.An Allan Jude Aq Mt allanjude@freebsd.org Index: head/usr.sbin/certctl/certctl.sh =================================================================== --- head/usr.sbin/certctl/certctl.sh +++ head/usr.sbin/certctl/certctl.sh @@ -0,0 +1,230 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# 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:=} +: ${TRUSTPATH:=${DESTDIR}/usr/share/certs/trusted:${DESTDIR}/usr/local/share/certs:${DESTDIR}/usr/local/etc/ssl/certs} +: ${BLACKLISTPATH:=${DESTDIR}/usr/share/certs/blacklisted:${DESTDIR}/usr/local/etc/ssl/blacklisted} +: ${CERTDESTDIR:=${DESTDIR}/etc/ssl/certs} +: ${BLACKLISTDESTDIR:=${DESTDIR}/etc/ssl/blacklisted} +: ${EXTENSIONS:="*.pem *.crt *.cer *.crl *.0"} +: ${VERBOSE:=0} + +############################################################ GLOBALS + +SCRIPTNAME="${0##*/}" +ERRORS=0 +NOOP=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 +} + +create_trusted_link() +{ + local hash + + 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 ] && ln -fs "$1" "$CERTDESTDIR/$hash.0" +} + +create_blacklisted() +{ + local hash + + hash=$( do_hash "$1" ) || return + [ $VERBOSE -gt 0 ] && echo "Adding $hash.0 to blacklist" + [ $NOOP -eq 0 ] && ln -fs "$1" "$BLACKLISTDESTDIR/$hash.0" +} + +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..." + cd "$CPATH" + for CFILE in $EXTENSIONS; do + [ -e "$CFILE" ] || continue + [ $VERBOSE -gt 0 ] && echo "Reading $CFILE" + "$CFUNC" "$CPATH/$CFILE" + done + cd - + done +} + +do_list() +{ + local CFILE subject + + if [ -e "$1" ]; then + cd "$1" + for CFILE in *.0; 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() +{ + + [ $NOOP -eq 0 ] && rm -rf "$CERTDESTDIR" + [ $NOOP -eq 0 ] && mkdir -p "$CERTDESTDIR" + [ $NOOP -eq 0 ] && mkdir -p "$BLACKLISTDESTDIR" + + 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 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" + 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 [-nv] 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 nv flag; do + case "$flag" in + n) NOOP=1 ;; + v) VERBOSE=$(( $VERBOSE + 1 )) ;; + esac +done +shift $(( $OPTIND - 1 )) + +[ $# -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 +################################################################################