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