3 # SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 # Copyright 2018 Allan Jude <allanjude@freebsd.org>
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted providing that the following conditions
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.
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.
30 ############################################################ CONFIGURATION
34 : ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$"}
37 ############################################################ GLOBALS
44 ############################################################ FUNCTIONS
50 if hash=$( openssl x509 -noout -subject_hash -in "$1" ); then
55 ERRORS=$(( $ERRORS + 1 ))
62 local checkdir hash decimal
68 while [ -e "$checkdir/$hash.$decimal" ]; do
69 decimal=$((decimal + 1))
78 local blisthash certhash hash
81 hash=$( do_hash "$1" ) || return
82 certhash=$( openssl x509 -sha1 -in "$1" -noout -fingerprint )
83 for blistfile in $(find $UNTRUSTDESTDIR -name "$hash.*"); do
84 blisthash=$( openssl x509 -sha1 -in "$blistfile" -noout -fingerprint )
85 if [ "$certhash" = "$blisthash" ]; then
86 echo "Skipping untrusted certificate $1 ($blistfile)"
90 suffix=$(get_decimal "$CERTDESTDIR" "$hash")
91 [ $VERBOSE -gt 0 ] && echo "Adding $hash.$suffix to trust store"
93 install ${INSTALLFLAGS} -lrs $(realpath "$1") "$CERTDESTDIR/$hash.$suffix"
96 # Accepts either dot-hash form from `certctl list` or a path to a valid cert.
99 local hash srcfile filename
102 # If it exists as a file, we'll try that; otherwise, we'll scan
104 hash=$( do_hash "$1" ) || return
105 srcfile=$(realpath "$1")
106 suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash")
107 filename="$hash.$suffix"
108 echo "$srcfile" "$hash.$suffix"
109 elif [ -e "${CERTDESTDIR}/$1" ]; then
110 srcfile=$(realpath "${CERTDESTDIR}/$1")
111 hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//')
112 suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash")
113 filename="$hash.$suffix"
114 echo "$srcfile" "$hash.$suffix"
120 local srcfile filename
122 set -- $(resolve_certname "$1")
126 if [ -z "$srcfile" -o -z "$filename" ]; then
130 [ $VERBOSE -gt 0 ] && echo "Adding $filename to untrusted list"
131 [ $NOOP -eq 0 ] && install ${INSTALLFLAGS} -lrs "$srcfile" "$UNTRUSTDESTDIR/$filename"
136 local CFUNC CSEARCH CPATH CFILE
144 for CPATH in "$@"; do
145 [ -d "$CPATH" ] || continue
146 echo "Scanning $CPATH for certificates..."
147 for CFILE in $(ls -1 "${CPATH}" | grep -Ee "${FILEPAT}"); do
148 [ -e "$CPATH/$CFILE" ] || continue
149 [ $VERBOSE -gt 0 ] && echo "Reading $CFILE"
150 "$CFUNC" "$CPATH/$CFILE"
161 for CFILE in *.[0-9]; do
162 if [ ! -s "$CFILE" ]; then
163 echo "Unable to read $CFILE" >&2
164 ERRORS=$(( $ERRORS + 1 ))
168 if [ $VERBOSE -eq 0 ]; then
169 subject=$( openssl x509 -noout -subject -nameopt multiline -in "$CFILE" |
170 sed -n '/commonName/s/.*= //p' )
173 subject=$( openssl x509 -noout -subject -in "$CFILE" )
174 printf "%s\t%s\n" "$CFILE" "$subject"
183 if [ $NOOP -eq 0 ]; then
184 if [ -e "$CERTDESTDIR" ]; then
185 find "$CERTDESTDIR" -type link -delete
187 mkdir -p "$CERTDESTDIR"
189 if [ -e "$UNTRUSTDESTDIR" ]; then
190 find "$UNTRUSTDESTDIR" -type link -delete
192 mkdir -p "$UNTRUSTDESTDIR"
196 do_scan create_untrusted "$UNTRUSTPATH"
197 do_scan create_trusted_link "$TRUSTPATH"
202 echo "Listing Trusted Certificates:"
203 do_list "$CERTDESTDIR"
211 [ $NOOP -eq 0 ] && mkdir -p "$UNTRUSTDESTDIR"
212 for BFILE in "$@"; do
213 echo "Adding $BFILE to untrusted list"
214 create_untrusted "$BFILE"
220 local BFILE blisthash certhash hash
223 for BFILE in "$@"; do
224 if [ -s "$BFILE" ]; then
225 hash=$( do_hash "$BFILE" )
226 certhash=$( openssl x509 -sha1 -in "$BFILE" -noout -fingerprint )
227 for BLISTEDFILE in $(find $UNTRUSTDESTDIR -name "$hash.*"); do
228 blisthash=$( openssl x509 -sha1 -in "$BLISTEDFILE" -noout -fingerprint )
229 if [ "$certhash" = "$blisthash" ]; then
230 echo "Removing $(basename "$BLISTEDFILE") from untrusted list"
231 [ $NOOP -eq 0 ] && rm -f $BLISTEDFILE
234 elif [ -e "$UNTRUSTDESTDIR/$BFILE" ]; then
235 echo "Removing $BFILE from untrusted list"
236 [ $NOOP -eq 0 ] && rm -f "$UNTRUSTDESTDIR/$BFILE"
238 echo "Cannot find $BFILE" >&2
239 ERRORS=$(( $ERRORS + 1 ))
246 echo "Listing Untrusted Certificates:"
247 do_list "$UNTRUSTDESTDIR"
253 echo "Manage the TLS trusted certificates on the system"
254 echo " $SCRIPTNAME [-v] list"
255 echo " List trusted certificates"
256 echo " $SCRIPTNAME [-v] untrusted"
257 echo " List untrusted certificates"
258 echo " $SCRIPTNAME [-nUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash"
259 echo " Generate hash links for all certificates"
260 echo " $SCRIPTNAME [-nv] untrust <file>"
261 echo " Add <file> to the list of untrusted certificates"
262 echo " $SCRIPTNAME [-nv] trust <file>"
263 echo " Remove <file> from the list of untrusted certificates"
267 ############################################################ MAIN
269 while getopts D:d:M:nUv flag; do
271 D) DESTDIR=${OPTARG} ;;
272 d) DISTBASE=${OPTARG} ;;
273 M) METALOG=${OPTARG} ;;
276 v) VERBOSE=$(( $VERBOSE + 1 )) ;;
279 shift $(( $OPTIND - 1 ))
283 : ${METALOG:=${DESTDIR}/METALOG}
285 [ $UNPRIV -eq 1 ] && INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR}"
286 : ${LOCALBASE:=$(sysctl -n user.localbase)}
287 : ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs}
288 : ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted}
289 : ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs}
290 : ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted}
292 [ $# -gt 0 ] || usage
295 rehash) cmd_rehash ;;
296 blacklist) cmd_untrust "$@" ;;
297 untrust) cmd_untrust "$@" ;;
298 trust) cmd_trust "$@" ;;
299 unblacklist) cmd_trust "$@" ;;
300 untrusted) cmd_untrusted ;;
301 blacklisted) cmd_untrusted ;;
302 *) usage # NOTREACHED
306 [ $ERRORS -gt 0 ] && echo "Encountered $ERRORS errors" >&2
309 ################################################################################
311 ################################################################################