Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile +++ usr.sbin/Makefile @@ -92,6 +92,7 @@ tcpdump \ traceroute \ trpt \ + trustctl \ tzsetup \ ugidfw \ vigr \ Index: usr.sbin/trustctl/Makefile =================================================================== --- /dev/null +++ usr.sbin/trustctl/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SCRIPTS=trustctl.sh +#MAN= trustctl.8 + +.include Index: usr.sbin/trustctl/trustctl.sh =================================================================== --- /dev/null +++ usr.sbin/trustctl/trustctl.sh @@ -0,0 +1,196 @@ +#!/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$ + +: ${SEARCHPATH:=/usr/share/certs/trusted /usr/local/share/certs /usr/local/etc/ssl/certs} +: ${BLACKLISTPATH:=/usr/share/certs/blacklisted /usr/local/etc/ssl/blacklisted} +: ${CERTDESTDIR:=/etc/ssl/certs} +: ${BLACKLISTDESTDIR:=/etc/ssl/blacklisted} +: ${EXTENSIONS:="*.pem *.crt *.cer *.crl *.0"} + +SCRIPTNAME=$(basename $0) +ERRORS=0 +VERBOSE=0 + +do_hash () { + hash=$(openssl x509 -noout -subject_hash -in $1) + if [ $? -eq 0 ]; then + echo $hash + return 0 + else + echo "Error: $1" >&2 + ERRORS=$(( $ERRORS + 1 )) + return 1 + fi +} + +create_trusted_link () { + hash=$(do_hash $1) || return 1 + 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" + ln -fs $1 $CERTDESTDIR/${hash}.0 +} + +create_blacklisted () { + hash=$(do_hash $1) + [ $VERBOSE -gt 0 ] && echo "Adding ${hash}.0 to blacklist" + cp $1 $BLACKLISTDESTDIR/${hash}.0 +} + +do_scan () { + cd $2 + for CFILE in $EXTENSIONS; do + if [ -e $CFILE ]; then + [ $VERBOSE -gt 0 ] && echo "Reading $CFILE" + $1 $2/$CFILE + fi + done +} + +do_list () { + cd $1 + for CFILE in *.0; do + if [ -s $CFILE ]; then + subject="" + if [ $VERBOSE -eq 0 ]; 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 + echo -e "${CFILE}\t${subject}" + else + echo "Unable to read $CFILE" >&2 + ERRORS=$(( $ERRORS + 1 )) + fi + done +} + +cmd_rehash () { + rm -rf $CERTDESTDIR + mkdir -p $CERTDESTDIR + mkdir -p $BLACKLISTDESTDIR + for BPATH in $BLACKLISTPATH; do + if [ -d $BPATH ]; then + echo "Scanning $BPATH for blacklisted certificates..." + do_scan create_blacklisted $BPATH + fi + done + for CPATH in $SEARCHPATH; do + if [ -d $CPATH ]; then + echo "Scanning $CPATH for trusted certificates..." + do_scan create_trusted_link $CPATH + fi + done +} + +cmd_list () { + echo "Listing Trusted Certificates:" + do_list $CERTDESTDIR +} + +cmd_blacklist () { + # remove the verb argument + shift + mkdir -p $BLACKLISTDESTDIR + for BFILE in $@; do + echo "Adding $BFILE to blacklist" + create_blacklisted $BFILE + done +} + +cmd_unblacklist () { + # remove the verb argument + shift + for BFILE in $@; do + if [ -s $BFILE ]; then + hash=$(do_hash $BFILE) + echo "Removing ${hash}.0 from blacklist" + rm -f $BLACKLISTDESTDIR/${hash}.0 + elif [ -e $BLACKLISTDESTDIR/$BFILE ]; then + echo "Removing $BFILE from blacklist" + 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 () { + echo "Manage the TLS trusted certificates on the system" + echo " $SCRIPTNAME list" + echo " List trusted certificates" + echo " $SCRIPTNAME blacklisted" + echo " List blacklisted certificates" + echo " $SCRIPTNAME rehash" + echo " Rehash the list of trusted certificates" + echo " $SCRIPTNAME blacklist " + echo " Add to the list of blacklisted certificates" + echo " $SCRIPTNAME unblacklist " + echo " Remove from the list of blacklisted certificates" + exit 64 +} + +[ $# -gt 0 ] || usage +case $1 in + list) + cmd_list + ;; + rehash) + cmd_rehash + ;; + blacklist) + cmd_blacklist $@ + ;; + unblacklist) + cmd_unblacklist $@ + ;; + blacklisted) + cmd_blacklisted + ;; + *) + usage + ;; +esac + +if [ $ERRORS -gt 0 ]; then + echo "Encountered $ERRORS errors" >&2 + exit 1 +fi +