Home | History | Annotate | Line # | Download | only in certctl
certctl.sh revision 1.2
      1  1.1  riastrad #!/bin/sh
      2  1.1  riastrad 
      3  1.2  riastrad #	$NetBSD: certctl.sh,v 1.2 2023/08/28 22:25:32 riastradh Exp $
      4  1.1  riastrad #
      5  1.1  riastrad # Copyright (c) 2023 The NetBSD Foundation, Inc.
      6  1.1  riastrad # All rights reserved.
      7  1.1  riastrad #
      8  1.1  riastrad # Redistribution and use in source and binary forms, with or without
      9  1.1  riastrad # modification, are permitted provided that the following conditions
     10  1.1  riastrad # are met:
     11  1.1  riastrad # 1. Redistributions of source code must retain the above copyright
     12  1.1  riastrad #    notice, this list of conditions and the following disclaimer.
     13  1.1  riastrad # 2. Redistributions in binary form must reproduce the above copyright
     14  1.1  riastrad #    notice, this list of conditions and the following disclaimer in the
     15  1.1  riastrad #    documentation and/or other materials provided with the distribution.
     16  1.1  riastrad #
     17  1.1  riastrad # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     18  1.1  riastrad # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     19  1.1  riastrad # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20  1.1  riastrad # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     21  1.1  riastrad # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22  1.1  riastrad # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23  1.1  riastrad # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24  1.1  riastrad # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25  1.1  riastrad # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26  1.1  riastrad # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27  1.1  riastrad # POSSIBILITY OF SUCH DAMAGE.
     28  1.1  riastrad #
     29  1.1  riastrad 
     30  1.1  riastrad set -o pipefail
     31  1.1  riastrad set -Ceu
     32  1.1  riastrad 
     33  1.1  riastrad progname=$(basename -- "$0")
     34  1.1  riastrad 
     35  1.1  riastrad ### Options and arguments
     36  1.1  riastrad 
     37  1.1  riastrad usage()
     38  1.1  riastrad {
     39  1.1  riastrad 	exec >&2
     40  1.1  riastrad 	printf 'Usage: %s %s\n' \
     41  1.1  riastrad 	    "$progname" \
     42  1.1  riastrad 	    "[-nv] [-C <config>] [-c <certsdir>] [-u <untrusted>]"
     43  1.1  riastrad 	printf '               <cmd> <args>...\n'
     44  1.1  riastrad 	printf '       %s list\n' "$progname"
     45  1.1  riastrad 	printf '       %s rehash\n' "$progname"
     46  1.1  riastrad 	printf '       %s trust <cert>\n' "$progname"
     47  1.1  riastrad 	printf '       %s untrust <cert>\n' "$progname"
     48  1.1  riastrad 	printf '       %s untrusted\n' "$progname"
     49  1.1  riastrad 	exit 1
     50  1.1  riastrad }
     51  1.1  riastrad 
     52  1.1  riastrad certsdir=/etc/openssl/certs
     53  1.1  riastrad config=/etc/openssl/certs.conf
     54  1.1  riastrad distrustdir=/etc/openssl/untrusted
     55  1.1  riastrad nflag=false			# dry run
     56  1.1  riastrad vflag=false			# verbose
     57  1.1  riastrad 
     58  1.1  riastrad # Options used by FreeBSD:
     59  1.1  riastrad #
     60  1.1  riastrad #	-D destdir
     61  1.1  riastrad #	-M metalog
     62  1.1  riastrad #	-U		(unprivileged)
     63  1.1  riastrad #	-d distbase
     64  1.1  riastrad #
     65  1.1  riastrad while getopts C:c:nu:v f; do
     66  1.1  riastrad 	case $f in
     67  1.1  riastrad 	C)	config=$OPTARG;;
     68  1.1  riastrad 	c)	certsdir=$OPTARG;;
     69  1.1  riastrad 	n)	nflag=true;;
     70  1.1  riastrad 	u)	distrustdir=$OPTARG;;
     71  1.1  riastrad 	v)	vflag=true;;
     72  1.1  riastrad 	\?)	usage;;
     73  1.1  riastrad 	esac
     74  1.1  riastrad done
     75  1.1  riastrad shift $((OPTIND - 1))
     76  1.1  riastrad 
     77  1.1  riastrad if [ $# -lt 1 ]; then
     78  1.1  riastrad 	usage
     79  1.1  riastrad fi
     80  1.1  riastrad cmd=$1
     81  1.1  riastrad 
     82  1.1  riastrad ### Global state
     83  1.1  riastrad 
     84  1.1  riastrad config_paths=
     85  1.1  riastrad config_manual=false
     86  1.1  riastrad tmpfile=
     87  1.1  riastrad 
     88  1.1  riastrad # If tmpfile is set to nonempty, clean it up on exit.
     89  1.1  riastrad 
     90  1.1  riastrad trap 'test -n "$tmpfile" && rm -f "$tmpfile"' EXIT HUP INT TERM
     91  1.1  riastrad 
     92  1.1  riastrad ### Subroutines
     93  1.1  riastrad 
     94  1.1  riastrad # error <msg> ...
     95  1.1  riastrad #
     96  1.1  riastrad #	Print an error message to stderr.
     97  1.1  riastrad #
     98  1.1  riastrad #	Does not exit the process.
     99  1.1  riastrad #
    100  1.1  riastrad error()
    101  1.1  riastrad {
    102  1.1  riastrad 	echo "$progname:" "$@" >&2
    103  1.1  riastrad }
    104  1.1  riastrad 
    105  1.1  riastrad # run <cmd> <args>...
    106  1.1  riastrad #
    107  1.1  riastrad #	Print a command if verbose, and run it unless it's a dry run.
    108  1.1  riastrad #
    109  1.1  riastrad run()
    110  1.1  riastrad {
    111  1.1  riastrad 	local t q cmdline
    112  1.1  riastrad 
    113  1.1  riastrad 	if $vflag; then	       # print command if verbose
    114  1.1  riastrad 		for t; do
    115  1.1  riastrad 			case $t in
    116  1.1  riastrad 			''|*[^[:alnum:]+,-./:=_@]*)
    117  1.1  riastrad 				# empty or unsafe -- quotify
    118  1.1  riastrad 				;;
    119  1.1  riastrad 			*)
    120  1.1  riastrad 				# nonempty and safe-only -- no quotify
    121  1.1  riastrad 				cmdline="${cmdline:+$cmdline }$t"
    122  1.1  riastrad 				continue
    123  1.1  riastrad 				;;
    124  1.1  riastrad 			esac
    125  1.1  riastrad 			q=$(printf '%s' "$t" | sed -e "s/'/'\\\''/g'")
    126  1.1  riastrad 			cmdline="${cmdline:+$cmdline }'$q'"
    127  1.1  riastrad 		done
    128  1.1  riastrad 		printf '%s\n' "$cmdline"
    129  1.1  riastrad 	fi
    130  1.1  riastrad 	if ! $nflag; then	# skip command if dry run
    131  1.1  riastrad 		"$@"
    132  1.1  riastrad 	fi
    133  1.1  riastrad }
    134  1.1  riastrad 
    135  1.1  riastrad # configure
    136  1.1  riastrad #
    137  1.1  riastrad #	Parse the configuration file, initializing config_*.
    138  1.1  riastrad #
    139  1.1  riastrad configure()
    140  1.1  riastrad {
    141  1.1  riastrad 	local lineno status formatok vconfig line contline op path vpath vop
    142  1.1  riastrad 
    143  1.1  riastrad 	# Count line numbers, record a persistent error status to
    144  1.1  riastrad 	# return at the end, and record whether we got a format line.
    145  1.1  riastrad 	lineno=0
    146  1.1  riastrad 	status=0
    147  1.1  riastrad 	formatok=false
    148  1.1  riastrad 
    149  1.1  riastrad 	# vis the config name for terminal-safe error messages.
    150  1.1  riastrad 	vconfig=$(printf '%s' "$config" | vis -M)
    151  1.1  riastrad 
    152  1.1  riastrad 	# Read and process each line of the config file.
    153  1.1  riastrad 	while read -r line; do
    154  1.1  riastrad 		lineno=$((lineno + 1))
    155  1.1  riastrad 
    156  1.1  riastrad 		# If the line ends in an odd number of backslashes, it
    157  1.1  riastrad 		# has a continuation line, so read on.
    158  1.1  riastrad 		while expr "$line" : '^\(\\\\\)*\\' >/dev/null ||
    159  1.1  riastrad 		    expr "$line" : '^.*[^\\]\(\\\\\)*\\$' >/dev/null; do
    160  1.1  riastrad 			if ! read -r contline; then
    161  1.1  riastrad 				error "$vconfig:$lineno: premature end of file"
    162  1.1  riastrad 				return 1
    163  1.1  riastrad 			fi
    164  1.1  riastrad 			line="$line$contline"
    165  1.1  riastrad 		done
    166  1.1  riastrad 
    167  1.1  riastrad 		# Skip blank lines and comments.
    168  1.1  riastrad 		case $line in
    169  1.1  riastrad 		''|'#'*)
    170  1.1  riastrad 			continue
    171  1.1  riastrad 			;;
    172  1.1  riastrad 		esac
    173  1.1  riastrad 
    174  1.1  riastrad 		# Require the first non-blank/comment line to identify
    175  1.1  riastrad 		# the config file format.
    176  1.1  riastrad 		if ! $formatok; then
    177  1.1  riastrad 			if [ "$line" = "netbsd-certctl 20230816" ]; then
    178  1.1  riastrad 				formatok=true
    179  1.1  riastrad 				continue
    180  1.1  riastrad 			else
    181  1.1  riastrad 				error "$vconfig:$lineno: missing format line"
    182  1.1  riastrad 				status=1
    183  1.1  riastrad 				break
    184  1.1  riastrad 			fi
    185  1.1  riastrad 		fi
    186  1.1  riastrad 
    187  1.1  riastrad 		# Split the line into words and dispatch on the first.
    188  1.1  riastrad 		set -- $line
    189  1.1  riastrad 		op=$1
    190  1.1  riastrad 		case $op in
    191  1.1  riastrad 		manual)
    192  1.1  riastrad 			config_manual=true
    193  1.1  riastrad 			;;
    194  1.1  riastrad 		path)
    195  1.1  riastrad 			if [ $# -lt 2 ]; then
    196  1.1  riastrad 				error "$vconfig:$lineno: missing path"
    197  1.1  riastrad 				status=1
    198  1.1  riastrad 				continue
    199  1.1  riastrad 			fi
    200  1.1  riastrad 			if [ $# -gt 3 ]; then
    201  1.1  riastrad 				error "$vconfig:$lineno: excess args"
    202  1.1  riastrad 				status=1
    203  1.1  riastrad 				continue
    204  1.1  riastrad 			fi
    205  1.1  riastrad 
    206  1.1  riastrad 			# Unvis the path.  Hack: if the user has had
    207  1.1  riastrad 			# the audacity to choose a path ending in
    208  1.1  riastrad 			# newlines, prevent the shell from consuming
    209  1.1  riastrad 			# them so we don't choke on their subterfuge.
    210  1.1  riastrad 			path=$(printf '%s.' "$2" | unvis)
    211  1.1  riastrad 			path=${path%.}
    212  1.1  riastrad 
    213  1.1  riastrad 			# Ensure the path is absolute.  It is unclear
    214  1.1  riastrad 			# what directory it should be relative to if
    215  1.1  riastrad 			# not.
    216  1.1  riastrad 			case $path in
    217  1.1  riastrad 			/*)
    218  1.1  riastrad 				;;
    219  1.1  riastrad 			*)
    220  1.1  riastrad 				error "$vconfig:$lineno:" \
    221  1.1  riastrad 				    "relative path forbidden"
    222  1.1  riastrad 				status=1
    223  1.1  riastrad 				continue
    224  1.1  riastrad 				;;
    225  1.1  riastrad 			esac
    226  1.1  riastrad 
    227  1.1  riastrad 			# Record the vis-encoded path in a
    228  1.1  riastrad 			# space-separated list.
    229  1.1  riastrad 			vpath=$(printf '%s' "$path" | vis -M)
    230  1.1  riastrad 			config_paths="$config_paths $vpath"
    231  1.1  riastrad 			;;
    232  1.1  riastrad 		*)
    233  1.1  riastrad 			vop=$(printf '%s' "$op" | vis -M)
    234  1.1  riastrad 			error "$vconfig:$lineno: unknown command: $vop"
    235  1.1  riastrad 			;;
    236  1.1  riastrad 		esac
    237  1.2  riastrad 	done <$config || status=$?
    238  1.1  riastrad 
    239  1.1  riastrad 	return $status
    240  1.1  riastrad }
    241  1.1  riastrad 
    242  1.1  riastrad # list_default_trusted
    243  1.1  riastrad #
    244  1.1  riastrad #	List the vis-encoded certificate paths and their base names,
    245  1.1  riastrad #	separated by a space, for the certificates that are trusted by
    246  1.1  riastrad #	default according to the configuration.
    247  1.1  riastrad #
    248  1.1  riastrad #	No order guaranteed; caller must sort.
    249  1.1  riastrad #
    250  1.1  riastrad list_default_trusted()
    251  1.1  riastrad {
    252  1.1  riastrad 	local vpath path cert base vcert vbase
    253  1.1  riastrad 
    254  1.1  riastrad 	for vpath in $config_paths; do
    255  1.1  riastrad 		path=$(printf '%s.' "$vpath" | unvis)
    256  1.1  riastrad 		path=${path%.}
    257  1.1  riastrad 
    258  1.1  riastrad 		# Enumerate the .pem, .cer, and .crt files.
    259  1.1  riastrad 		for cert in "$path"/*.pem "$path"/*.cer "$path"/*.crt; do
    260  1.1  riastrad 			# vis the certificate path.
    261  1.1  riastrad 			vcert=$(printf '%s' "$cert" | vis -M)
    262  1.1  riastrad 
    263  1.1  riastrad 			# If the file doesn't exist, then either:
    264  1.1  riastrad 			#
    265  1.1  riastrad 			# (a) it's a broken symlink, so fail;
    266  1.1  riastrad 			# or
    267  1.1  riastrad 			# (b) the shell glob failed to match,
    268  1.1  riastrad 			#     so ignore it and move on.
    269  1.1  riastrad 			if [ ! -e "$cert" ]; then
    270  1.1  riastrad 				if [ -h "$cert" ]; then
    271  1.1  riastrad 					error "broken symlink: $vcert"
    272  1.1  riastrad 					status=1
    273  1.1  riastrad 				fi
    274  1.1  riastrad 				continue
    275  1.1  riastrad 			fi
    276  1.1  riastrad 
    277  1.1  riastrad 			# Print the vis-encoded absolute path to the
    278  1.1  riastrad 			# certificate and base name on a single line.
    279  1.1  riastrad 			vbase=$(basename -- "$vcert.")
    280  1.1  riastrad 			vbase=${vbase%.}
    281  1.1  riastrad 			printf '%s %s\n' "$vcert" "$vbase"
    282  1.1  riastrad 		done
    283  1.1  riastrad 	done
    284  1.1  riastrad }
    285  1.1  riastrad 
    286  1.1  riastrad # list_distrusted
    287  1.1  riastrad #
    288  1.1  riastrad #	List the vis-encoded certificate paths and their base names,
    289  1.1  riastrad #	separated by a space, for the certificates that have been
    290  1.1  riastrad #	distrusted by the user.
    291  1.1  riastrad #
    292  1.1  riastrad #	No order guaranteed; caller must sort.
    293  1.1  riastrad #
    294  1.1  riastrad list_distrusted()
    295  1.1  riastrad {
    296  1.1  riastrad 	local status link vlink cert vcert
    297  1.1  riastrad 
    298  1.1  riastrad 	status=0
    299  1.1  riastrad 
    300  1.1  riastrad 	for link in "$distrustdir"/*; do
    301  1.1  riastrad 		# vis the link for terminal-safe error messages.
    302  1.1  riastrad 		vlink=$(printf '%s' "$link" | vis -M)
    303  1.1  riastrad 
    304  1.1  riastrad 		# The distrust directory must only have symlinks to
    305  1.1  riastrad 		# certificates.  If we find a non-symlink, print a
    306  1.1  riastrad 		# warning and arrange to fail.
    307  1.1  riastrad 		if [ ! -h "$link" ]; then
    308  1.1  riastrad 			if [ ! -e "$link" ] && \
    309  1.1  riastrad 			    [ "$link" = "$distrustdir/*" ]; then
    310  1.1  riastrad 				# Shell glob matched nothing -- just
    311  1.1  riastrad 				# ignore it.
    312  1.1  riastrad 				break
    313  1.1  riastrad 			fi
    314  1.1  riastrad 			error "distrusted non-symlink: $vlink"
    315  1.1  riastrad 			status=1
    316  1.1  riastrad 			continue
    317  1.1  riastrad 		fi
    318  1.1  riastrad 
    319  1.1  riastrad 		# Read the target of the symlink, nonrecursively.  If
    320  1.1  riastrad 		# the user has had the audacity to make a symlink whose
    321  1.1  riastrad 		# target ends in newline, prevent the shell from
    322  1.1  riastrad 		# consuming them so we don't choke on their subterfuge.
    323  1.1  riastrad 		cert=$(readlink -n -- "$link" && printf .)
    324  1.1  riastrad 		cert=${cert%.}
    325  1.1  riastrad 
    326  1.1  riastrad 		# Warn if the target is relative.  Although it is clear
    327  1.1  riastrad 		# what directory it would be relative to, there might
    328  1.1  riastrad 		# be issues with canonicalization.
    329  1.1  riastrad 		case $cert in
    330  1.1  riastrad 		/*)
    331  1.1  riastrad 			;;
    332  1.1  riastrad 		*)
    333  1.1  riastrad 			vlink=$(printf '%s' "$link" | vis -M)
    334  1.1  riastrad 			vcert=$(printf '%s' "$cert" | vis -M)
    335  1.1  riastrad 			error "distrusted relative symlink: $vlink -> $vcert"
    336  1.1  riastrad 			;;
    337  1.1  riastrad 		esac
    338  1.1  riastrad 
    339  1.1  riastrad 		# Print the vis-encoded absolute path to the
    340  1.1  riastrad 		# certificate and base name on a single line.
    341  1.1  riastrad 		vcert=$(printf '%s' "$cert" | vis -M)
    342  1.1  riastrad 		vbase=$(basename -- "$vcert.")
    343  1.1  riastrad 		vbase=${vbase%.}
    344  1.1  riastrad 		printf '%s %s\n' "$vcert" "$vbase"
    345  1.1  riastrad 	done
    346  1.1  riastrad 
    347  1.1  riastrad 	return $status
    348  1.1  riastrad }
    349  1.1  riastrad 
    350  1.1  riastrad # list_trusted
    351  1.1  riastrad #
    352  1.1  riastrad #	List the trusted certificates, excluding the distrusted one, as
    353  1.1  riastrad #	one vis(3) line per certificate.  Reject duplicate base names,
    354  1.1  riastrad #	since we will be creating symlinks to the same base names in
    355  1.1  riastrad #	the certsdir.  Sorted lexicographically by vis-encoding.
    356  1.1  riastrad #
    357  1.1  riastrad list_trusted()
    358  1.1  riastrad {
    359  1.1  riastrad 
    360  1.1  riastrad 	# XXX Use dev/ino to match files instead of symlink targets?
    361  1.1  riastrad 
    362  1.1  riastrad 	{
    363  1.1  riastrad 		list_default_trusted \
    364  1.1  riastrad 		| while read -r vcert vbase; do
    365  1.1  riastrad 			printf 'trust %s %s\n' "$vcert" "$vbase"
    366  1.1  riastrad 		done
    367  1.1  riastrad 
    368  1.1  riastrad 		# XXX Find a good way to list the default-untrusted
    369  1.1  riastrad 		# certificates, so if you have already distrusted one
    370  1.1  riastrad 		# and it is removed from default-trust on update,
    371  1.1  riastrad 		# nothing warns about this.
    372  1.1  riastrad 
    373  1.1  riastrad 		# list_default_untrusted \
    374  1.1  riastrad 		# | while read -r vcert vbase; do
    375  1.1  riastrad 		# 	printf 'distrust %s %s\n' "$vcert" "$vbase"
    376  1.1  riastrad 		# done
    377  1.1  riastrad 
    378  1.1  riastrad 		list_distrusted \
    379  1.1  riastrad 		| while read -r vcert vbase; do
    380  1.1  riastrad 			printf 'distrust %s %s\n' "$vcert" "$vbase"
    381  1.1  riastrad 		done
    382  1.1  riastrad 	} | awk -v progname="$progname" '
    383  1.1  riastrad 		BEGIN			{ status = 0 }
    384  1.1  riastrad 		$1 == "trust" && $3 in trust && $2 != trust[$3] {
    385  1.1  riastrad 			printf "%s: duplicate base name %s\n  %s\n  %s\n", \
    386  1.1  riastrad 			    progname, $3, trust[$3], $2 >"/dev/stderr"
    387  1.1  riastrad 			status = 1
    388  1.1  riastrad 			next
    389  1.1  riastrad 		}
    390  1.1  riastrad 		$1 == "trust"		{ trust[$3] = $2 }
    391  1.1  riastrad 		$1 == "distrust" && !trust[$3] && !distrust[$3] {
    392  1.1  riastrad 			printf "%s: distrusted certificate not found: %s\n", \
    393  1.1  riastrad 			    progname, $3 >"/dev/stderr"
    394  1.1  riastrad 			status = 1
    395  1.1  riastrad 		}
    396  1.1  riastrad 		$1 == "distrust" && $2 in trust && $2 != trust[$3] {
    397  1.1  riastrad 			printf "%s: distrusted certificate %s" \
    398  1.1  riastrad 			    " has multiple paths\n" \
    399  1.1  riastrad 			    "  %s\n  %s\n",
    400  1.1  riastrad 			    progname, $3, trust[$3], $2 >"/dev/stderr"
    401  1.1  riastrad 			status = 1
    402  1.1  riastrad 		}
    403  1.1  riastrad 		$1 == "distrust"	{ distrust[$3] = 1 }
    404  1.1  riastrad 		END			{
    405  1.1  riastrad 			for (vbase in trust) {
    406  1.1  riastrad 				if (!distrust[vbase])
    407  1.1  riastrad 					print trust[vbase]
    408  1.1  riastrad 			}
    409  1.1  riastrad 			exit status
    410  1.1  riastrad 		}
    411  1.1  riastrad 	' | sort -u
    412  1.1  riastrad }
    413  1.1  riastrad 
    414  1.1  riastrad # rehash
    415  1.1  riastrad #
    416  1.1  riastrad #	Delete and rebuild certsdir.
    417  1.1  riastrad #
    418  1.1  riastrad rehash()
    419  1.1  riastrad {
    420  1.1  riastrad 	local vcert cert certbase hash counter bundle vbundle
    421  1.1  riastrad 
    422  1.1  riastrad 	# If manual operation is enabled, refuse to rehash the
    423  1.1  riastrad 	# certsdir, but succeed anyway so this can safely be used in
    424  1.1  riastrad 	# automated scripts.
    425  1.1  riastrad 	if $config_manual; then
    426  1.1  riastrad 		error "manual certificates enabled, not rehashing"
    427  1.1  riastrad 		return
    428  1.1  riastrad 	fi
    429  1.1  riastrad 
    430  1.1  riastrad 	# Delete the active certificates symlink cache.
    431  1.1  riastrad 	run rm -rf "$certsdir"
    432  1.1  riastrad 	run mkdir "$certsdir"
    433  1.1  riastrad 
    434  1.1  riastrad 	# Create a temporary file for the single-file bundle.  This
    435  1.1  riastrad 	# will be automatically deleted on normal exit or
    436  1.1  riastrad 	# SIGHUP/SIGINT/SIGTERM.
    437  1.1  riastrad 	if ! $nflag; then
    438  1.1  riastrad 		tmpfile=$(mktemp -t "$progname.XXXXXX")
    439  1.1  riastrad 	fi
    440  1.1  riastrad 
    441  1.1  riastrad 	# Recreate symlinks for all of the trusted certificates.
    442  1.1  riastrad 	list_trusted \
    443  1.1  riastrad 	| while read -r vcert; do
    444  1.1  riastrad 		cert=$(printf '%s.' "$vcert" | unvis)
    445  1.1  riastrad 		cert=${cert%.}
    446  1.1  riastrad 		run ln -s -- "$cert" "$certsdir"
    447  1.1  riastrad 
    448  1.1  riastrad 		# Add the certificate to the single-file bundle.
    449  1.1  riastrad 		if ! $nflag; then
    450  1.1  riastrad 			cat -- "$cert" >>$tmpfile
    451  1.1  riastrad 		fi
    452  1.1  riastrad 	done
    453  1.1  riastrad 
    454  1.1  riastrad 	# Hash the directory with openssl.
    455  1.1  riastrad 	#
    456  1.1  riastrad 	# XXX Pass `-v' to openssl in a way that doesn't mix with our
    457  1.1  riastrad 	# shell-safe verbose commands?  (Need to handle `-n' too.)
    458  1.1  riastrad 	run openssl rehash -- "$certsdir"
    459  1.1  riastrad 
    460  1.1  riastrad 	# Install the single-file bundle.
    461  1.1  riastrad 	bundle=$certsdir/ca-certificates.crt
    462  1.1  riastrad 	vbundle=$(printf '%s' "$bundle" | vis -M)
    463  1.1  riastrad 	$vflag && printf '# create %s\n' "$vbundle"
    464  1.1  riastrad 	if ! $nflag; then
    465  1.1  riastrad 		cp -- "$tmpfile" "$bundle"
    466  1.1  riastrad 		rm -f -- "$tmpfile"
    467  1.1  riastrad 		tmpfile=
    468  1.1  riastrad 	fi
    469  1.1  riastrad }
    470  1.1  riastrad 
    471  1.1  riastrad ### Commands
    472  1.1  riastrad 
    473  1.1  riastrad usage_list()
    474  1.1  riastrad {
    475  1.1  riastrad 	exec >&2
    476  1.1  riastrad 	printf 'Usage: %s list\n' "$progname"
    477  1.1  riastrad 	exit 1
    478  1.1  riastrad }
    479  1.1  riastrad cmd_list()
    480  1.1  riastrad {
    481  1.1  riastrad 	test $# -eq 1 || usage_list
    482  1.1  riastrad 
    483  1.1  riastrad 	configure
    484  1.1  riastrad 
    485  1.1  riastrad 	list_trusted \
    486  1.1  riastrad 	| while read -r vcert vbase; do
    487  1.1  riastrad 		printf '%s\n' "$vcert"
    488  1.1  riastrad 	done
    489  1.1  riastrad }
    490  1.1  riastrad 
    491  1.1  riastrad usage_rehash()
    492  1.1  riastrad {
    493  1.1  riastrad 	exec >&2
    494  1.1  riastrad 	printf 'Usage: %s rehash\n' "$progname"
    495  1.1  riastrad 	exit 1
    496  1.1  riastrad }
    497  1.1  riastrad cmd_rehash()
    498  1.1  riastrad {
    499  1.1  riastrad 	test $# -eq 1 || usage_rehash
    500  1.1  riastrad 
    501  1.1  riastrad 	configure
    502  1.1  riastrad 
    503  1.1  riastrad 	rehash
    504  1.1  riastrad }
    505  1.1  riastrad 
    506  1.1  riastrad usage_trust()
    507  1.1  riastrad {
    508  1.1  riastrad 	exec >&2
    509  1.1  riastrad 	printf 'Usage: %s trust <cert>\n' "$progname"
    510  1.1  riastrad 	exit 1
    511  1.1  riastrad }
    512  1.1  riastrad cmd_trust()
    513  1.1  riastrad {
    514  1.1  riastrad 	local cert vcert certbase vcertbase
    515  1.1  riastrad 
    516  1.1  riastrad 	test $# -eq 2 || usage_trust
    517  1.1  riastrad 	cert=$2
    518  1.1  riastrad 
    519  1.1  riastrad 	configure
    520  1.1  riastrad 
    521  1.1  riastrad 	# XXX Accept base name.
    522  1.1  riastrad 
    523  1.1  riastrad 	# vis the certificate path for terminal-safe error messages.
    524  1.1  riastrad 	vcert=$(printf '%s' "$cert" | vis -M)
    525  1.1  riastrad 
    526  1.1  riastrad 	# Verify the certificate actually exists.
    527  1.1  riastrad 	if [ ! -f "$cert" ]; then
    528  1.1  riastrad 		error "no such certificate: $vcert"
    529  1.1  riastrad 		return 1
    530  1.1  riastrad 	fi
    531  1.1  riastrad 
    532  1.1  riastrad 	# Verify we currently distrust a certificate by this base name.
    533  1.1  riastrad 	certbase=$(basename -- "$cert.")
    534  1.1  riastrad 	certbase=${certbase%.}
    535  1.1  riastrad 	if [ ! -h "$distrustdir/$certbase" ]; then
    536  1.1  riastrad 		error "not currently distrusted: $vcert"
    537  1.1  riastrad 		return 1
    538  1.1  riastrad 	fi
    539  1.1  riastrad 
    540  1.1  riastrad 	# Verify the certificate we distrust by this base name is the
    541  1.1  riastrad 	# same one.
    542  1.1  riastrad 	target=$(readlink -n -- "$distrustdir/$certbase" && printf .)
    543  1.1  riastrad 	target=${target%.}
    544  1.1  riastrad 	if [ "$cert" != "$target" ]; then
    545  1.1  riastrad 		vcertbase=$(basename -- "$vcert")
    546  1.1  riastrad 		error "distrusted $vcertbase does not point to $vcert"
    547  1.1  riastrad 		return 1
    548  1.1  riastrad 	fi
    549  1.1  riastrad 
    550  1.1  riastrad 	# Remove the link from the distrusted directory, and rehash --
    551  1.1  riastrad 	# quietly, so verbose output emphasizes the distrust part and
    552  1.1  riastrad 	# not the whole certificate set.
    553  1.1  riastrad 	run rm -- "$distrustdir/$certbase"
    554  1.1  riastrad 	$vflag && echo '# rehash'
    555  1.1  riastrad 	vflag=false
    556  1.1  riastrad 	rehash
    557  1.1  riastrad }
    558  1.1  riastrad 
    559  1.1  riastrad usage_untrust()
    560  1.1  riastrad {
    561  1.1  riastrad 	exec >&2
    562  1.1  riastrad 	printf 'Usage: %s untrust <cert>\n' "$progname"
    563  1.1  riastrad 	exit 1
    564  1.1  riastrad }
    565  1.1  riastrad cmd_untrust()
    566  1.1  riastrad {
    567  1.1  riastrad 	local cert vcert certbase vcertbase target vtarget
    568  1.1  riastrad 
    569  1.1  riastrad 	test $# -eq 2 || usage_untrust
    570  1.1  riastrad 	cert=$2
    571  1.1  riastrad 
    572  1.1  riastrad 	configure
    573  1.1  riastrad 
    574  1.1  riastrad 	# vis the certificate path for terminal-safe error messages.
    575  1.1  riastrad 	vcert=$(printf '%s' "$cert" | vis -M)
    576  1.1  riastrad 
    577  1.1  riastrad 	# Verify the certificate actually exists.  Otherwise, you might
    578  1.1  riastrad 	# fail to distrust a certificate you intended to distrust,
    579  1.1  riastrad 	# e.g. if you made a typo in its path.
    580  1.1  riastrad 	if [ ! -f "$cert" ]; then
    581  1.1  riastrad 		error "no such certificate: $vcert"
    582  1.1  riastrad 		return 1
    583  1.1  riastrad 	fi
    584  1.1  riastrad 
    585  1.1  riastrad 	# Check whether this certificate is already distrusted.
    586  1.1  riastrad 	# - If the same base name points to the same path, stop here.
    587  1.1  riastrad 	# - Otherwise, fail noisily.
    588  1.1  riastrad 	certbase=$(basename "$cert.")
    589  1.1  riastrad 	certbase=${certbase%.}
    590  1.1  riastrad 	if [ -h "$distrustdir/$certbase" ]; then
    591  1.1  riastrad 		target=$(readlink -n -- "$distrustdir/$certbase" && printf .)
    592  1.1  riastrad 		target=${target%.}
    593  1.1  riastrad 		if [ "$target" = "$cert" ]; then
    594  1.1  riastrad 			$vflag && echo '# already distrusted'
    595  1.1  riastrad 			return
    596  1.1  riastrad 		fi
    597  1.1  riastrad 		vcertbase=$(printf '%s' "$certbase" | vis -M)
    598  1.1  riastrad 		vtarget=$(printf '%s' "$target" | vis -M)
    599  1.1  riastrad 		error "distrusted $vcertbase at different path $vtarget"
    600  1.1  riastrad 		return 1
    601  1.1  riastrad 	fi
    602  1.1  riastrad 
    603  1.1  riastrad 	# Create the distrustdir if needed, create a symlink in it, and
    604  1.1  riastrad 	# rehash -- quietly, so verbose output emphasizes the distrust
    605  1.1  riastrad 	# part and not the whole certificate set.
    606  1.1  riastrad 	test -d "$distrustdir" || run mkdir -- "$distrustdir"
    607  1.1  riastrad 	run ln -s -- "$cert" "$distrustdir"
    608  1.1  riastrad 	$vflag && echo '# rehash'
    609  1.1  riastrad 	vflag=false
    610  1.1  riastrad 	rehash
    611  1.1  riastrad }
    612  1.1  riastrad 
    613  1.1  riastrad usage_untrusted()
    614  1.1  riastrad {
    615  1.1  riastrad 	exec >&2
    616  1.1  riastrad 	printf 'Usage: %s untrusted\n' "$progname"
    617  1.1  riastrad 	exit 1
    618  1.1  riastrad }
    619  1.1  riastrad cmd_untrusted()
    620  1.1  riastrad {
    621  1.1  riastrad 	test $# -eq 1 || usage_untrusted
    622  1.1  riastrad 
    623  1.1  riastrad 	configure
    624  1.1  riastrad 
    625  1.1  riastrad 	list_distrusted \
    626  1.1  riastrad 	| while read -r vcert vbase; do
    627  1.1  riastrad 		printf '%s\n' "$vcert"
    628  1.1  riastrad 	done
    629  1.1  riastrad }
    630  1.1  riastrad 
    631  1.1  riastrad ### Main
    632  1.1  riastrad 
    633  1.1  riastrad # We accept the following aliases for user interface compatibility with
    634  1.1  riastrad # FreeBSD:
    635  1.1  riastrad #
    636  1.1  riastrad #	blacklist = untrust
    637  1.1  riastrad #	blacklisted = untrusted
    638  1.1  riastrad #	unblacklist = trust
    639  1.1  riastrad 
    640  1.1  riastrad case $cmd in
    641  1.1  riastrad list)	cmd_list "$@"
    642  1.1  riastrad 	;;
    643  1.1  riastrad rehash)	cmd_rehash "$@"
    644  1.1  riastrad 	;;
    645  1.1  riastrad trust|unblacklist)
    646  1.1  riastrad 	cmd_trust "$@"
    647  1.1  riastrad 	;;
    648  1.1  riastrad untrust|blacklist)
    649  1.1  riastrad 	cmd_untrust "$@"
    650  1.1  riastrad 	;;
    651  1.1  riastrad untrusted|blacklisted)
    652  1.1  riastrad 	cmd_untrusted "$@"
    653  1.1  riastrad 	;;
    654  1.1  riastrad *)	vcmd=$(printf '%s' "$cmd" | vis -M)
    655  1.1  riastrad 	printf '%s: unknown command: %s\n' "$progname" "$vcmd" >&2
    656  1.1  riastrad 	usage
    657  1.1  riastrad 	;;
    658  1.1  riastrad esac
    659