1#!/bin/sh 2#- 3# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4# 5# Copyright 2018 Allan Jude <allanjude@freebsd.org> 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted providing that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26# POSSIBILITY OF SUCH DAMAGE. 27# 28# $FreeBSD$ 29 30############################################################ CONFIGURATION 31 32: ${DESTDIR:=} 33: ${TRUSTPATH:=${DESTDIR}/usr/share/certs/trusted:${DESTDIR}/usr/local/share/certs:${DESTDIR}/usr/local/etc/ssl/certs} 34: ${BLACKLISTPATH:=${DESTDIR}/usr/share/certs/blacklisted:${DESTDIR}/usr/local/etc/ssl/blacklisted} 35: ${CERTDESTDIR:=${DESTDIR}/etc/ssl/certs} 36: ${BLACKLISTDESTDIR:=${DESTDIR}/etc/ssl/blacklisted} 37: ${EXTENSIONS:="*.pem *.crt *.cer *.crl *.0"} 38: ${VERBOSE:=0} 39 40############################################################ GLOBALS 41 42SCRIPTNAME="${0##*/}" 43ERRORS=0 44NOOP=0 45 46############################################################ FUNCTIONS 47 48do_hash() 49{ 50 local hash 51 52 if hash=$( openssl x509 -noout -subject_hash -in "$1" ); then 53 echo "$hash" 54 return 0 55 else 56 echo "Error: $1" >&2 57 ERRORS=$(( $ERRORS + 1 )) 58 return 1 59 fi 60} 61 62create_trusted_link() 63{ 64 local hash 65 66 hash=$( do_hash "$1" ) || return 67 if [ -e "$BLACKLISTDESTDIR/$hash.0" ]; then 68 echo "Skipping blacklisted certificate $1 ($BLACKLISTDESTDIR/$hash.0)" 69 return 1 70 fi 71 [ $VERBOSE -gt 0 ] && echo "Adding $hash.0 to trust store" 72 [ $NOOP -eq 0 ] && ln -fs $(realpath "$1") "$CERTDESTDIR/$hash.0" 73} 74 75create_blacklisted() 76{ 77 local hash srcfile filename 78 79 # If it exists as a file, we'll try that; otherwise, we'll scan 80 if [ -e "$1" ]; then 81 hash=$( do_hash "$1" ) || return 82 srcfile=$(realpath "$1") 83 filename="$hash.0" 84 elif [ -e "${CERTDESTDIR}/$1" ]; then 85 srcfile=$(realpath "${CERTDESTDIR}/$1") 86 filename="$1" 87 else 88 return 89 fi 90 [ $VERBOSE -gt 0 ] && echo "Adding $filename to blacklist" 91 [ $NOOP -eq 0 ] && ln -fs "$srcfile" "$BLACKLISTDESTDIR/$filename" 92} 93 94do_scan() 95{ 96 local CFUNC CSEARCH CPATH CFILE 97 local oldIFS="$IFS" 98 CFUNC="$1" 99 CSEARCH="$2" 100 101 IFS=: 102 set -- $CSEARCH 103 IFS="$oldIFS" 104 for CPATH in "$@"; do 105 [ -d "$CPATH" ] || continue 106 echo "Scanning $CPATH for certificates..." 107 cd "$CPATH" 108 for CFILE in $EXTENSIONS; do 109 [ -e "$CFILE" ] || continue 110 [ $VERBOSE -gt 0 ] && echo "Reading $CFILE" 111 "$CFUNC" "$CPATH/$CFILE" 112 done 113 cd - 114 done 115} 116 117do_list() 118{ 119 local CFILE subject 120 121 if [ -e "$1" ]; then 122 cd "$1" 123 for CFILE in *.0; do 124 if [ ! -s "$CFILE" ]; then 125 echo "Unable to read $CFILE" >&2 126 ERRORS=$(( $ERRORS + 1 )) 127 continue 128 fi 129 subject= 130 if [ $VERBOSE -eq 0 ]; then 131 subject=$( openssl x509 -noout -subject -nameopt multiline -in "$CFILE" | 132 sed -n '/commonName/s/.*= //p' ) 133 fi 134 [ "$subject" ] || 135 subject=$( openssl x509 -noout -subject -in "$CFILE" ) 136 printf "%s\t%s\n" "$CFILE" "$subject" 137 done 138 cd - 139 fi 140} 141 142cmd_rehash() 143{ 144 145 [ $NOOP -eq 0 ] && rm -rf "$CERTDESTDIR" 146 [ $NOOP -eq 0 ] && mkdir -p "$CERTDESTDIR" 147 [ $NOOP -eq 0 ] && mkdir -p "$BLACKLISTDESTDIR" 148 149 do_scan create_blacklisted "$BLACKLISTPATH" 150 do_scan create_trusted_link "$TRUSTPATH" 151} 152 153cmd_list() 154{ 155 echo "Listing Trusted Certificates:" 156 do_list "$CERTDESTDIR" 157} 158 159cmd_blacklist() 160{ 161 local BPATH 162 163 shift # verb 164 [ $NOOP -eq 0 ] && mkdir -p "$BLACKLISTDESTDIR" 165 for BFILE in "$@"; do 166 echo "Adding $BFILE to blacklist" 167 create_blacklisted "$BFILE" 168 done 169} 170 171cmd_unblacklist() 172{ 173 local BFILE hash 174 175 shift # verb 176 for BFILE in "$@"; do 177 if [ -s "$BFILE" ]; then 178 hash=$( do_hash "$BFILE" ) 179 echo "Removing $hash.0 from blacklist" 180 [ $NOOP -eq 0 ] && rm -f "$BLACKLISTDESTDIR/$hash.0" 181 elif [ -e "$BLACKLISTDESTDIR/$BFILE" ]; then 182 echo "Removing $BFILE from blacklist" 183 [ $NOOP -eq 0 ] && rm -f "$BLACKLISTDESTDIR/$BFILE" 184 else 185 echo "Cannot find $BFILE" >&2 186 ERRORS=$(( $ERRORS + 1 )) 187 fi 188 done 189} 190 191cmd_blacklisted() 192{ 193 echo "Listing Blacklisted Certificates:" 194 do_list "$BLACKLISTDESTDIR" 195} 196 197usage() 198{ 199 exec >&2 200 echo "Manage the TLS trusted certificates on the system" 201 echo " $SCRIPTNAME [-v] list" 202 echo " List trusted certificates" 203 echo " $SCRIPTNAME [-v] blacklisted" 204 echo " List blacklisted certificates" 205 echo " $SCRIPTNAME [-nv] rehash" 206 echo " Generate hash links for all certificates" 207 echo " $SCRIPTNAME [-nv] blacklist <file>" 208 echo " Add <file> to the list of blacklisted certificates" 209 echo " $SCRIPTNAME [-nv] unblacklist <file>" 210 echo " Remove <file> from the list of blacklisted certificates" 211 exit 64 212} 213 214############################################################ MAIN 215 216while getopts nv flag; do 217 case "$flag" in 218 n) NOOP=1 ;; 219 v) VERBOSE=$(( $VERBOSE + 1 )) ;; 220 esac 221done 222shift $(( $OPTIND - 1 )) 223 224[ $# -gt 0 ] || usage 225case "$1" in 226list) cmd_list ;; 227rehash) cmd_rehash ;; 228blacklist) cmd_blacklist "$@" ;; 229unblacklist) cmd_unblacklist "$@" ;; 230blacklisted) cmd_blacklisted ;; 231*) usage # NOTREACHED 232esac 233 234retval=$? 235[ $ERRORS -gt 0 ] && echo "Encountered $ERRORS errors" >&2 236exit $retval 237 238################################################################################ 239# END 240################################################################################ 241