[2/3] Add certctl(8)

This is a simple utility to hash all trusted on the system into
/etc/ssl/certs. It also allows the user to blacklist certificates they do
not trust.

This work was done primarily by allanjude@, with minor contributions by
myself.

No objection from:	secteam
Differential Revision:	https://reviews.freebsd.org/D16857
This commit is contained in:
Kyle Evans 2019-10-02 01:05:53 +00:00
parent f27f39db77
commit ccdcb388ba
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=352949
4 changed files with 358 additions and 0 deletions

View File

@ -124,6 +124,9 @@ SUBDIR.${MK_BLUETOOTH}+= bluetooth
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

View File

@ -0,0 +1,6 @@
# $FreeBSD$
SCRIPTS=certctl.sh
MAN= certctl.8
.include <bsd.prog.mk>

119
usr.sbin/certctl/certctl.8 Normal file
View File

@ -0,0 +1,119 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD
.\"
.\" 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.
.\"
.\" $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 <DESTDIR>/usr/share/certs/trusted
.Pa <DESTDIR>/usr/local/share/certs <DESTDIR>/usr/local/etc/ssl/certs
.It Ev BLACKLISTPATH
List of paths to search for blacklisted certificates.
Default:
.Pa <DESTDIR>/usr/share/certs/blacklisted
.Pa <DESTDIR>/usr/local/etc/ssl/blacklisted
.It Ev CERTDESTDIR
Destination directory for symbolic links to trusted certificates.
Default:
.Pa <DESTDIR>/etc/ssl/certs
.It Ev BLACKLISTDESTDIR
Destination directory for symbolic links to blacklisted certificates.
Default:
.Pa <DESTDIR>/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

230
usr.sbin/certctl/certctl.sh Executable file
View File

@ -0,0 +1,230 @@
#!/bin/sh
#-
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
#
# 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.
#
# $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 <file>"
echo " Add <file> to the list of blacklisted certificates"
echo " $SCRIPTNAME [-nv] unblacklist <file>"
echo " Remove <file> 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
################################################################################