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