Module Name: src
Committed By: riastradh
Date: Sat Aug 26 05:27:15 UTC 2023
Modified Files:
src/distrib/sets/lists/base: mi
src/distrib/sets/lists/man: mi
src/distrib/sets/lists/tests: mi
src/etc: Makefile
src/etc/mtree: NetBSD.dist.tests
src/tests/usr.sbin: Makefile
src/usr.sbin: Makefile
Added Files:
src/tests/usr.sbin/certctl: Makefile Makefile.inc t_certctl.sh
src/tests/usr.sbin/certctl/certs1: DigiCert_Global_Root_CA.pem
Explicitly_Distrust_DigiNotar_Root_CA.pem Makefile
src/tests/usr.sbin/certctl/certs2: GTS_Root_R1.pem
GlobalSign_Root_CA_-_R3.pem Makefile
src/tests/usr.sbin/certctl/certs3:
Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
Makefile
src/tests/usr.sbin/certctl/certs4: AC_RAIZ_FNMT-RCM.pem
DigiCert_Global_Root_CA.pem Makefile
src/usr.sbin/certctl: Makefile certctl.8 certctl.sh certs.conf
Log Message:
certctl(8): New tool for managing OpenSSL CA certificates.
Same command-line syntax as FreeBSD, clearer semantics about which
parts are config and which parts are cache.
To generate a diff of this commit:
cvs rdiff -u -r1.1325 -r1.1326 src/distrib/sets/lists/base/mi
cvs rdiff -u -r1.1763 -r1.1764 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.1291 -r1.1292 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.466 -r1.467 src/etc/Makefile
cvs rdiff -u -r1.199 -r1.200 src/etc/mtree/NetBSD.dist.tests
cvs rdiff -u -r1.7 -r1.8 src/tests/usr.sbin/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/usr.sbin/certctl/Makefile \
src/tests/usr.sbin/certctl/Makefile.inc \
src/tests/usr.sbin/certctl/t_certctl.sh
cvs rdiff -u -r0 -r1.1 \
src/tests/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem \
src/tests/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem
\
src/tests/usr.sbin/certctl/certs1/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/usr.sbin/certctl/certs2/GTS_Root_R1.pem \
src/tests/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem \
src/tests/usr.sbin/certctl/certs2/Makefile
cvs rdiff -u -r0 -r1.1 \
src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
\
src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
\
src/tests/usr.sbin/certctl/certs3/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem \
src/tests/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem \
src/tests/usr.sbin/certctl/certs4/Makefile
cvs rdiff -u -r1.291 -r1.292 src/usr.sbin/Makefile
cvs rdiff -u -r0 -r1.1 src/usr.sbin/certctl/Makefile \
src/usr.sbin/certctl/certctl.8 src/usr.sbin/certctl/certctl.sh \
src/usr.sbin/certctl/certs.conf
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/base/mi
diff -u src/distrib/sets/lists/base/mi:1.1325 src/distrib/sets/lists/base/mi:1.1326
--- src/distrib/sets/lists/base/mi:1.1325 Tue Aug 1 06:35:55 2023
+++ src/distrib/sets/lists/base/mi Sat Aug 26 05:27:13 2023
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1325 2023/08/01 06:35:55 mrg Exp $
+# $NetBSD: mi,v 1.1326 2023/08/26 05:27:13 riastradh Exp $
#
# Note: Don't delete entries from here - mark them as "obsolete" instead,
# unless otherwise stated below.
@@ -114,6 +114,7 @@
./etc/openldap base-ldap-root
./etc/openssl base-crypto-root
./etc/openssl/certs base-crypto-root
+./etc/openssl/certs.conf base-sysutil-bin
./etc/openssl/misc base-crypto-root
./etc/openssl/private base-crypto-root
./etc/pam.d base-sys-root
@@ -1457,6 +1458,7 @@
./usr/sbin/btpand base-sysutil-bin
./usr/sbin/btuartd base-obsolete obsolete
./usr/sbin/catman base-man-bin
+./usr/sbin/certctl base-sysutil-bin
./usr/sbin/chat base-ppp-bin
./usr/sbin/chown base-sysutil-bin
./usr/sbin/chroot base-sysutil-bin
Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1763 src/distrib/sets/lists/man/mi:1.1764
--- src/distrib/sets/lists/man/mi:1.1763 Fri Aug 11 23:04:29 2023
+++ src/distrib/sets/lists/man/mi Sat Aug 26 05:27:13 2023
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1763 2023/08/11 23:04:29 christos Exp $
+# $NetBSD: mi,v 1.1764 2023/08/26 05:27:13 riastradh Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -2648,6 +2648,7 @@
./usr/share/man/cat8/ccdconfig.0 man-sysutil-catman .cat
./usr/share/man/cat8/cesfic/MAKEDEV.0 man-obsolete obsolete
./usr/share/man/cat8/cesfic/makedev.0 man-obsolete obsolete
+./usr/share/man/cat8/certctl.0 man-sysutil-catman .cat
./usr/share/man/cat8/cgdconfig.0 man-sysutil-catman .cat
./usr/share/man/cat8/chat.0 man-ppp-catman .cat
./usr/share/man/cat8/chown.0 man-sysutil-catman .cat
@@ -5902,6 +5903,7 @@
./usr/share/man/html8/canconfig.html man-netutil-htmlman html
./usr/share/man/html8/catman.html man-man-htmlman html
./usr/share/man/html8/ccdconfig.html man-sysutil-htmlman html
+./usr/share/man/html8/certctl.html man-sysutil-htmlman html
./usr/share/man/html8/cgdconfig.html man-sysutil-htmlman html
./usr/share/man/html8/chat.html man-ppp-htmlman html
./usr/share/man/html8/chown.html man-sysutil-htmlman html
@@ -9203,6 +9205,7 @@
./usr/share/man/man8/ccdconfig.8 man-sysutil-man .man
./usr/share/man/man8/cesfic/MAKEDEV.8 man-obsolete obsolete
./usr/share/man/man8/cesfic/makedev.8 man-obsolete obsolete
+./usr/share/man/man8/certctl.8 man-sysutil-man .man
./usr/share/man/man8/cgdconfig.8 man-sysutil-man .man
./usr/share/man/man8/chat.8 man-ppp-man .man
./usr/share/man/man8/chown.8 man-sysutil-man .man
Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1291 src/distrib/sets/lists/tests/mi:1.1292
--- src/distrib/sets/lists/tests/mi:1.1291 Sun Aug 20 19:36:56 2023
+++ src/distrib/sets/lists/tests/mi Sat Aug 26 05:27:14 2023
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1291 2023/08/20 19:36:56 riastradh Exp $
+# $NetBSD: mi,v 1.1292 2023/08/26 05:27:14 riastradh Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -7564,6 +7564,22 @@
./usr/tests/usr.sbin tests-usr.sbin-tests compattestfile,atf
./usr/tests/usr.sbin/Atffile tests-usr.sbin-tests compattestfile,atf
./usr/tests/usr.sbin/Kyuafile tests-usr.sbin-tests compattestfile,atf,kyua
+./usr/tests/usr.sbin/certctl tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/Atffile tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/Kyuafile tests-usr.sbin-tests compattestfile,atf,kyua
+./usr/tests/usr.sbin/certctl/certs1 tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs2 tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs2/GTS_Root_R1.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs3 tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs4 tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem tests-usr.sbin-tests compattestfile,atf
+./usr/tests/usr.sbin/certctl/t_certctl tests-usr.sbin-tests compattestfile,atf
./usr/tests/usr.sbin/cpuctl tests-usr.sbin-tests compattestfile,atf
./usr/tests/usr.sbin/cpuctl/Atffile tests-usr.sbin-tests compattestfile,atf
./usr/tests/usr.sbin/cpuctl/Kyuafile tests-usr.sbin-tests compattestfile,atf,kyua
Index: src/etc/Makefile
diff -u src/etc/Makefile:1.466 src/etc/Makefile:1.467
--- src/etc/Makefile:1.466 Sun Aug 21 07:10:03 2022
+++ src/etc/Makefile Sat Aug 26 05:27:14 2023
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.466 2022/08/21 07:10:03 lukem Exp $
+# $NetBSD: Makefile,v 1.467 2023/08/26 05:27:14 riastradh Exp $
# from: @(#)Makefile 8.7 (Berkeley) 5/25/95
# Environment variables without default values:
@@ -348,6 +348,7 @@ install-etc-files: .PHONY .MAKE check_DE
.endfor
${MAKEDIRTARGET} ${NETBSDSRCDIR}/external/bsd/dhcpcd/sbin/dhcpcd configinstall
${MAKEDIRTARGET} ${NETBSDSRCDIR}/usr.bin/mail configinstall
+ ${MAKEDIRTARGET} ${NETBSDSRCDIR}/usr.sbin/certctl configinstall
.if (${MKPF} != "no")
${MAKEDIRTARGET} ${NETBSDSRCDIR}/usr.sbin/pf configinstall
.endif
Index: src/etc/mtree/NetBSD.dist.tests
diff -u src/etc/mtree/NetBSD.dist.tests:1.199 src/etc/mtree/NetBSD.dist.tests:1.200
--- src/etc/mtree/NetBSD.dist.tests:1.199 Sun Aug 20 10:27:11 2023
+++ src/etc/mtree/NetBSD.dist.tests Sat Aug 26 05:27:14 2023
@@ -1,4 +1,4 @@
-# $NetBSD: NetBSD.dist.tests,v 1.199 2023/08/20 10:27:11 riastradh Exp $
+# $NetBSD: NetBSD.dist.tests,v 1.200 2023/08/26 05:27:14 riastradh Exp $
./usr/libdata/debug/usr/tests
./usr/libdata/debug/usr/tests/atf
@@ -486,6 +486,11 @@
./usr/tests/usr.bin/xlint/xlint
./usr/tests/usr.bin/ztest
./usr/tests/usr.sbin
+./usr/tests/usr.sbin/certctl
+./usr/tests/usr.sbin/certctl/certs1
+./usr/tests/usr.sbin/certctl/certs2
+./usr/tests/usr.sbin/certctl/certs3
+./usr/tests/usr.sbin/certctl/certs4
./usr/tests/usr.sbin/cpuctl
./usr/tests/usr.sbin/execsnoop
./usr/tests/usr.sbin/inetd
Index: src/tests/usr.sbin/Makefile
diff -u src/tests/usr.sbin/Makefile:1.7 src/tests/usr.sbin/Makefile:1.8
--- src/tests/usr.sbin/Makefile:1.7 Sun Aug 29 09:54:18 2021
+++ src/tests/usr.sbin/Makefile Sat Aug 26 05:27:14 2023
@@ -1,8 +1,9 @@
-# $NetBSD: Makefile,v 1.7 2021/08/29 09:54:18 christos Exp $
+# $NetBSD: Makefile,v 1.8 2023/08/26 05:27:14 riastradh Exp $
.include <bsd.own.mk>
TESTSDIR= ${TESTSBASE}/usr.sbin
+TESTS_SUBDIRS+= certctl
TESTS_SUBDIRS+= cpuctl
TESTS_SUBDIRS+= execsnoop
TESTS_SUBDIRS+= inetd
Index: src/usr.sbin/Makefile
diff -u src/usr.sbin/Makefile:1.291 src/usr.sbin/Makefile:1.292
--- src/usr.sbin/Makefile:1.291 Mon Jun 6 10:56:29 2022
+++ src/usr.sbin/Makefile Sat Aug 26 05:27:15 2023
@@ -1,11 +1,11 @@
-# $NetBSD: Makefile,v 1.291 2022/06/06 10:56:29 nia Exp $
+# $NetBSD: Makefile,v 1.292 2023/08/26 05:27:15 riastradh Exp $
# from: @(#)Makefile 5.20 (Berkeley) 6/12/93
.include <bsd.own.mk>
SUBDIR= ac accton acpitools altq apm apmd arp autofs \
bad144 bootp bta2dpd btattach btconfig btdevctl bthcid btpand catman \
- chroot cnwctl cpuctl crash \
+ certctl chroot cnwctl cpuctl crash \
dev_mkdb diskpart dumpfs dumplfs \
edquota eeprom envstat etcupdate extattrctl \
flashctl fssconfig fstyp fusermount fwctl \
Added files:
Index: src/tests/usr.sbin/certctl/Makefile
diff -u /dev/null src/tests/usr.sbin/certctl/Makefile:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/Makefile Sat Aug 26 05:27:14 2023
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+.include "Makefile.inc" # TESTSDIR
+
+SUBDIR= certs1 certs2 certs3 certs4
+
+TESTS_SH= t_certctl
+
+.include <bsd.test.mk>
Index: src/tests/usr.sbin/certctl/Makefile.inc
diff -u /dev/null src/tests/usr.sbin/certctl/Makefile.inc:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/Makefile.inc Sat Aug 26 05:27:14 2023
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile.inc,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+TESTSDIR= ${TESTSBASE}/usr.sbin/certctl
+
+.include <bsd.own.mk>
Index: src/tests/usr.sbin/certctl/t_certctl.sh
diff -u /dev/null src/tests/usr.sbin/certctl/t_certctl.sh:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/t_certctl.sh Sat Aug 26 05:27:14 2023
@@ -0,0 +1,333 @@
+#!/bin/sh
+
+# $NetBSD: t_certctl.sh,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+CERTCTL="certctl -C certs.conf -c certs -u untrusted"
+
+# setupconf <subdir>...
+#
+# Create certs/ and set up certs.conf to search the specified
+# subdirectories of the source directory.
+#
+setupconf()
+{
+ local sep subdir dir
+
+ mkdir certs
+ cat <<EOF >certs.conf
+netbsd-certctl 20230816
+
+# comment at line start
+ # comment not at line start, plus some intentional whitespace
+
+# THE WHITESPACE ABOVE IS INTENTIONAL, DO NOT DELETE
+EOF
+ # Start with a continuation line separator; then switch to
+ # non-continuation lines.
+ sep=$(printf ' \\\n\t')
+ for subdir; do
+ dir=$(atf_get_srcdir)/$subdir
+ cat <<EOF >>certs.conf
+path$sep$(printf '%s' "$dir" | vis -M)
+EOF
+ sep=' '
+ done
+}
+
+# check_empty
+#
+# Verify the certs directory is empty after dry runs or after
+# clearing the directory.
+#
+check_empty()
+{
+ local why
+
+ why=${1:-dry run}
+ for x in certs/*; do
+ if [ -e "$x" -o -h "$x" ]; then
+ atf_fail "certs/ should be empty after $why"
+ fi
+ done
+}
+
+# check_nonempty
+#
+# Verify the certs directory is nonempty.
+#
+check_nonempty()
+{
+ for x in certs/*.0; do
+ test -e "$x" && test -h "$x" && return
+ done
+ atf_fail "certs/ should be nonempty"
+}
+
+# checks <certsN>...
+#
+# Run various checks with certctl.
+#
+checks()
+{
+ local certs1 diginotar_base diginotar diginotar_hash subdir srcdir
+
+ certs1=$(atf_get_srcdir)/certs1
+ diginotar_base=Explicitly_Distrust_DigiNotar_Root_CA.pem
+ diginotar=$certs1/$diginotar_base
+ diginotar_hash=$(openssl x509 -hash -noout <$diginotar)
+
+ # Do a dry run of rehash and make sure the directory is still
+ # empty.
+ atf_check -s exit:0 $CERTCTL -n rehash
+ check_empty
+
+ # Distrust and trust one CA, as a dry run. The trust should
+ # fail because it's not currently distrusted.
+ atf_check -s exit:0 $CERTCTL -n untrust "$diginotar"
+ check_empty
+ atf_check -s not-exit:0 -e match:currently \
+ $CERTCTL -n trust "$diginotar"
+ check_empty
+
+ # Do a real rehash, not a dry run.
+ atf_check -s exit:0 $CERTCTL rehash
+
+ # Make sure all the certificates are trusted.
+ for subdir; do
+ case $subdir in
+ /*) srcdir=$subdir;;
+ *) srcdir=$(atf_get_srcdir)/$subdir;;
+ esac
+ for cert in "$srcdir"/*.pem; do
+ # Verify the certificate is linked by its base name.
+ certbase=$(basename "$cert")
+ atf_check -s exit:0 -o inline:"$cert" \
+ readlink -n "certs/$certbase"
+
+ # Verify the certificate is linked by a hash.
+ hash=$(openssl x509 -hash -noout <$cert)
+ counter=0
+ found=false
+ while [ $counter -lt 10 ]; do
+ if cmp -s "certs/$hash.$counter" "$cert"; then
+ found=true
+ break
+ fi
+ counter=$((counter + 1))
+ done
+ if ! $found; then
+ atf_fail "missing $cert"
+ fi
+
+ # Delete both links.
+ rm "certs/$certbase"
+ rm "certs/$hash.$counter"
+ done
+ done
+
+ # Verify the certificate bundle is there and delete it.
+ #
+ # XXX Verify its content.
+ atf_check -s exit:0 test -f certs/ca-certificates.crt
+ atf_check -s exit:0 test ! -h certs/ca-certificates.crt
+ rm certs/ca-certificates.crt
+
+ # Make sure after deleting everything there's nothing left.
+ check_empty "removing all expected certificates"
+
+ # Distrust, trust, and re-distrust one CA, and verify that it
+ # ceases to appear, reappears, and again ceases to appear.
+ # (This one has no subject hash collisions to worry about, so
+ # we hard-code the `.0' suffix.)
+ atf_check -s exit:0 $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 test -e "untrusted/$diginotar_base"
+ atf_check -s exit:0 test -h "untrusted/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_hash.0"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_hash.0"
+ check_nonempty
+
+ atf_check -s exit:0 $CERTCTL trust "$diginotar"
+ atf_check -s exit:0 test ! -e "untrusted/$diginotar_base"
+ atf_check -s exit:0 test ! -h "untrusted/$diginotar_base"
+ atf_check -s exit:0 test -e "certs/$diginotar_base"
+ atf_check -s exit:0 test -h "certs/$diginotar_base"
+ atf_check -s exit:0 test -e "certs/$diginotar_hash.0"
+ atf_check -s exit:0 test -h "certs/$diginotar_hash.0"
+ rm "certs/$diginotar_base"
+ rm "certs/$diginotar_hash.0"
+ check_nonempty
+
+ atf_check -s exit:0 $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 test -e "untrusted/$diginotar_base"
+ atf_check -s exit:0 test -h "untrusted/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_hash.0"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_hash.0"
+ check_nonempty
+}
+
+atf_test_case empty
+empty_head()
+{
+ atf_set "descr" "Test empty certificates store"
+}
+empty_body()
+{
+ setupconf # no directories
+ check_empty "empty cert path"
+ atf_check -s exit:0 $CERTCTL -n rehash
+ check_empty
+ atf_check -s exit:0 $CERTCTL rehash
+ atf_check -s exit:0 test -f certs/ca-certificates.crt
+ atf_check -s exit:0 test \! -h certs/ca-certificates.crt
+ atf_check -s exit:0 test \! -s certs/ca-certificates.crt
+ atf_check -s exit:0 rm certs/ca-certificates.crt
+ check_empty "empty cert path"
+}
+
+atf_test_case onedir
+onedir_head()
+{
+ atf_set "descr" "Test one certificates directory"
+}
+onedir_body()
+{
+ setupconf certs1
+ checks certs1
+}
+
+atf_test_case twodir
+twodir_head()
+{
+ atf_set "descr" "Test two certificates directories"
+}
+twodir_body()
+{
+ setupconf certs1 certs2
+ checks certs1 certs2
+}
+
+atf_test_case collidehash
+collidehash_head()
+{
+ atf_set "descr" "Test colliding hashes"
+}
+collidehash_body()
+{
+ # certs3 has two certificates with the same subject hash
+ setupconf certs1 certs3
+ checks certs1 certs3
+}
+
+atf_test_case collidebase
+collidebase_head()
+{
+ atf_set "descr" "Test colliding base names"
+}
+collidebase_body()
+{
+ # certs1 and certs4 both have DigiCert_Global_Root_CA.pem,
+ # which should cause list and rehash to fail and mention
+ # duplicates.
+ setupconf certs1 certs4
+ atf_check -s not-exit:0 -o ignore -e match:duplicate $CERTCTL list
+ atf_check -s not-exit:0 -o ignore -e match:duplicate $CERTCTL rehash
+}
+
+atf_test_case manual
+manual_head()
+{
+ atf_set "descr" "Test manual operation"
+}
+manual_body()
+{
+ local certs1 diginotar_base diginotar diginotar_hash
+
+ certs1=$(atf_get_srcdir)/certs1
+ diginotar_base=Explicitly_Distrust_DigiNotar_Root_CA.pem
+ diginotar=$certs1/$diginotar_base
+ diginotar_hash=$(openssl x509 -hash -noout <$diginotar)
+
+ setupconf certs1 certs2
+ cat <<EOF >>certs.conf
+manual
+EOF
+ touch certs/bogus.pem
+ ln -s bogus.pem certs/0123abcd.0
+
+ # Listing shouldn't mention anything in the certs/ cache.
+ atf_check -s exit:0 -o not-match:bogus $CERTCTL list
+ atf_check -s exit:0 -o not-match:bogus $CERTCTL untrusted
+
+ # Rehashing and changing the configuration should succeed, but
+ # mention `manual' in a warning message and should not touch
+ # the cache.
+ atf_check -s exit:0 -e match:manual $CERTCTL rehash
+ atf_check -s exit:0 -e match:manual $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 -e match:manual $CERTCTL trust "$diginotar"
+
+ # The files we created should still be there.
+ atf_check -s exit:0 test -f certs/bogus.pem
+ atf_check -s exit:0 test -h certs/0123abcd.0
+}
+
+atf_test_case evilpath
+evilpath_head()
+{
+ atf_set "descr" "Test certificate paths with evil characters"
+}
+evilpath_body()
+{
+ local evildir
+
+ evildir="$(printf 'evil\n.')"
+ evildir=${evildir%.}
+ mkdir "$evildir"
+
+ cp -p "$(atf_get_srcdir)/certs2"/*.pem "$evildir"/
+
+ setupconf certs1
+ cat <<EOF >>certs.conf
+path $(printf '%s' "$(pwd)/$evildir" | vis -M)
+EOF
+ checks certs1 "$(pwd)/$evildir"
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case collidebase
+ atf_add_test_case collidehash
+ atf_add_test_case empty
+ atf_add_test_case evilpath
+ atf_add_test_case manual
+ atf_add_test_case onedir
+ atf_add_test_case twodir
+}
Index: src/tests/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFijCCA3KgAwIBAgIQD////////////////////zANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJOTDESMBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdp
+Tm90YXIgUm9vdCBDQTEgMB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmww
+HhcNMDcwNzI3MTcxOTM3WhcNMjUwMzMxMTgxOTIyWjBfMQswCQYDVQQGEwJOTDES
+MBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdpTm90YXIgUm9vdCBDQTEg
+MB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmwwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCssFjBAL3YIQgLK5r+blYwBZ8bd5AQQVzDDYcRd46B
+8cp86Yxq7Th0Nbva3/m7wAk3tJZzgX0zGpg595NvlX89ubF1h7pRSOiLcD6VBMXY
+tsMW2YiwsYcdcNqGtA8Ui3rPENF0NqISe3eGSnnme98CEWilToauNFibJBN4ViIl
+HgGLS1Fx+4LMWZZpiFpoU8W5DQI3y0u8ZkqQfioLBQftFl9VkHXYRskbg+IIvvEj
+zJkd1ioPgyAVWCeCLvriIsJJsbkBgWqdbZ1Ad2h2TiEqbYRAhU52mXyC8/O3AlnU
+JgEbjt+tUwbRrhjd4rI6y9eIOI6sWym5GdOY+RgDz0iChmYLG2kPyes4iHomGgVM
+ktck1JbyrFIto0fVUvY//s6EBnCmqj6i8rZWNBhXouSBbefK8GrTx5FrAoNBfBXv
+a5pkXuPQPOWx63tdhvvL5ndJzaNl3Pe5nLjkC1+Tz8wwGjIczhxjlaX56uF0i57p
+K6kwe6AYHw4YC+VbqdPRbB4HZ4+RS6mKvNJmqpMBiLKR+jFc1abBUggJzQpjotMi
+puih2TkGl/VujQKQjBR7P4DNG5y6xFhyI6+2Vp/GekIzKQc/gsnmHwUNzUwoNovT
+yD4cxojvXu6JZOkd69qJfjKmadHdzIif0dDJZiHcBmfFlHqabWJMfczgZICynkeO
+owIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUiGi/4I41xDs4a2L3KDuEgcgM100wDQYJKoZIhvcNAQEFBQADggIBADsC
+jcs8MOhuoK3yc7NfniUTBAXT9uOLuwt5zlPe5JbF0a9zvNXD0EBVfEB/zRtfCdXy
+fJ9oHbtdzno5wozWmHvFg1Wo1X1AyuAe94leY12hE8JdiraKfADzI8PthV9xdvBo
+Y6pFITlIYXg23PFDk9Qlx/KAZeFTAnVR/Ho67zerhChXDNjU1JlWbOOi/lmEtDHo
+M/hklJRRl6s5xUvt2t2AC298KQ3EjopyDedTFLJgQT2EkTFoPSdE2+Xe9PpjRchM
+Ppj1P0G6Tss3DbpmmPHdy59c91Q2gmssvBNhl0L4eLvMyKKfyvBovWsdst+Nbwed
+2o5nx0ceyrm/KkKRt2NTZvFCo+H0Wk1Ya7XkpDOtXHAd3ODy63MUkZoDweoAZbwH
+/M8SESIsrqC9OuCiKthZ6SnTGDWkrBFfGbW1G/8iSlzGeuQX7yCpp/Q/rYqnmgQl
+nQ7KN+ZQ/YxCKQSa7LnPS3K94gg2ryMvYuXKAdNw23yCIywWMQzGNgeQerEfZ1jE
+O1hZibCMjFCz2IbLaKPECudpSyDOwR5WS5WpI2jYMNjD67BVUc3l/Su49bsRn1NU
+9jQZjHkJNsphFyUXC4KYcwx3dMPVDceoEkzHp1RxRy4sGn3J4ys7SN4nhKdjNrN9
+j6BkOSQNPXuHr2ZcdBtLc7LljPCGmbjlxd+Ewbfr
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs1/Makefile
diff -u /dev/null src/tests/usr.sbin/certctl/certs1/Makefile:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs1/Makefile Sat Aug 26 05:27:14 2023
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs1
+
+FILES+= DigiCert_Global_Root_CA.pem
+FILES+= Explicitly_Distrust_DigiNotar_Root_CA.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
Index: src/tests/usr.sbin/certctl/certs2/GTS_Root_R1.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs2/GTS_Root_R1.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs2/GTS_Root_R1.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
+27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
+Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
+TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
+qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
+szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
+Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
+MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
+wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
+aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
+VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
+AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
+C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
+QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
+h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
+7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
+ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
+MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
+Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
+6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
+0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
+2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
+bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs2/Makefile
diff -u /dev/null src/tests/usr.sbin/certctl/certs2/Makefile:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs2/Makefile Sat Aug 26 05:27:14 2023
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs2
+
+FILES+= GTS_Root_R1.pem
+FILES+= GlobalSign_Root_CA_-_R3.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
Index: src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1
+MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc
+tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd
+IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j
+b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC
+AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw
+ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m
+iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF
+Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ
+hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P
+Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE
+EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV
+1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t
+CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR
+5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw
+f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9
+ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK
+GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
+MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
+VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
+ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
+AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
+661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
+am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
+ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
+PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
+3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
+SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
+3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
+ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
+StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
+Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
+jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs3/Makefile
diff -u /dev/null src/tests/usr.sbin/certctl/certs3/Makefile:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs3/Makefile Sat Aug 26 05:27:14 2023
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs3
+
+FILES+= Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
+FILES+= Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
Index: src/tests/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem Sat Aug 26 05:27:14 2023
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx
+CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ
+WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ
+BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG
+Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/
+yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf
+BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz
+WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF
+tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z
+374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC
+IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL
+mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7
+wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS
+MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2
+ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet
+UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H
+YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3
+LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
+nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1
+RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM
+LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf
+77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N
+JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm
+fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp
+6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp
+1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B
+9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok
+RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv
+uu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem
diff -u /dev/null src/tests/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem Sat Aug 26 05:27:15 2023
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
Index: src/tests/usr.sbin/certctl/certs4/Makefile
diff -u /dev/null src/tests/usr.sbin/certctl/certs4/Makefile:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/tests/usr.sbin/certctl/certs4/Makefile Sat Aug 26 05:27:15 2023
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:15 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs4
+
+FILES+= AC_RAIZ_FNMT-RCM.pem
+FILES+= DigiCert_Global_Root_CA.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
Index: src/usr.sbin/certctl/Makefile
diff -u /dev/null src/usr.sbin/certctl/Makefile:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/usr.sbin/certctl/Makefile Sat Aug 26 05:27:15 2023
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:15 riastradh Exp $
+#
+
+MAN= certctl.8
+SCRIPTS= certctl.sh
+
+FILESDIR= /etc/openssl
+CONFIGFILES= certs.conf
+
+.include <bsd.prog.mk>
Index: src/usr.sbin/certctl/certctl.8
diff -u /dev/null src/usr.sbin/certctl/certctl.8:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/usr.sbin/certctl/certctl.8 Sat Aug 26 05:27:15 2023
@@ -0,0 +1,337 @@
+.\" $NetBSD: certctl.8,v 1.1 2023/08/26 05:27:15 riastradh Exp $
+.\"
+.\" Copyright (c) 2023 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd August 16, 2023
+.Dt CERTCTL 8
+.Os
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh NAME
+.Nm certctl
+.Nd configure OpenSSL certificate trust anchors
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SYNOPSIS
+.Nm
+.Op Fl nv
+.Op Fl C Ar config
+.Op Fl c Ar certsdir
+.Op Fl u Ar distrustdir
+.Ar cmd
+.Op Ar args...
+.\""""""""""""""""""
+.Nm
+.Oo Ar options Oc Cm list
+.Nm
+.Oo Ar options Oc Cm rehash
+.Nm
+.Oo Ar options Oc Cm trust Ar cert
+.Nm
+.Oo Ar options Oc Cm untrust Ar cert
+.Nm
+.Oo Ar options Oc Cm untrusted
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh DESCRIPTION
+The
+.Nm
+utility manages certificates used by OpenSSL-based applications as
+trust anchors for certificate validation in HTTPS or other purposes.
+.Nm
+allows configuring the set of certificates and persistently excluding
+individual certificates.
+.Pp
+For HTTPS certificate validation, OpenSSL applications typically
+use either a directory at
+.Pa /etc/openssl/certs
+of hashed certificates in PEM format, with names like
+.Pa "3513523f.0"
+used for lookup
+.Pq see Xr openssl_rehash 1 ,
+or a single-file bundle at
+.Pa /etc/openssl/certs/ca-certificates.crt
+concatenating all the certificates in PEM format.
+.Pp
+.Nm
+scans all directories in the certificate search path specified by the
+configuration file
+.Ar config
+.Pq default: Pa /etc/openssl/certs.conf
+for files called
+.Pa *.cer ,
+.Pa *.crt ,
+or
+.Pa *.pem
+in PEM format, except for those that have been excluded by
+.Nm Cm untrust ,
+and keeps
+.Ar certsdir
+.Pq default: Pa /etc/openssl/certs
+populated with symlinks to them.
+.Pp
+.Nm
+treats
+.Ar config
+and
+.Ar distrustdir
+as configuration, and
+.Ar certsdir
+strictly as a cache that can be safely deleted and rebuilt with
+.Nm Cm rehash .
+.Nm
+can also be instructed not to touch
+.Ar certsdir
+at all by putting
+.Cm manual
+in
+.Ar config .
+.
+.\""""""""""""""""""""""""""""""""""""""
+.Ss Commands
+.Bl -tag -width Cm
+.\""""""""""""""""""
+.It Cm list
+List absolute paths to trusted certificates, one per line, in
+.Xr vis 1
+format to encode any shell metacharacters, that
+.Nm Cm rehash
+would use to populate the
+.Ar certsdir
+cache.
+.\""""""""""""""""""
+.It Cm rehash
+Populate
+.Ar certsdir
+with all trusted certificates, excluding any from
+.Nm Cm untrust .
+.\""""""""""""""""""
+.It Cm trust Ar cert
+Allow
+.Ar cert
+to be included in the certificate cache if it is in the certificate
+search path, and rehash the certificate cache.
+In other words, reverse the persistent effect of
+.Nm Cm untrust Ar cert .
+.Pp
+.Ar cert
+must be the full absolute path to a certificate that has been excluded
+by
+.Nm Cm untrust Ar cert .
+.Pp
+This does not add a new certificate which is not in the search path.
+To do that, you can create a directory to hold it and put that
+directory in the search path.
+.\""""""""""""""""""
+.It Cm untrust Ar cert
+Persistently prevent
+.Ar
+from being included in the certificate cache, and rehash the
+certificate cache.
+.Pp
+.Ar cert
+must be the full absolute path to a certificate that is in the
+certificate search path.
+.\""""""""""""""""""
+.It Cm untrusted
+List absolute paths to untrusted certificates, one per line, in
+.Xr vis 1
+format to encode any shell metacharacters, that have been excluded by
+.Nm Cm untrust
+so that
+.Nm Cm rehash
+will not put them in
+.Ar certsdir .
+.\""""""""""""""""""
+.El
+.\""""""""""""""""""""""""""""""""""""""
+.Ss Configuration file
+The configuration file is a plain text file of lines separated by
+.Tn US-ASCII
+line feeds.
+.Pp
+.Pp
+The first line must be:
+.Dl netbsd-certctl 20230816
+.Pp
+Lines with only whitespace, or whitespace followed by the comment
+character
+.Ql #
+are ignored.
+Each line has a directive and arguments separated by whitespace, and
+may be extended by
+.Ql \e
+to continuation lines.
+.Bl -tag -width Cm
+.\""""""""""""""""""
+.It Cm path Ar dir
+Add
+.Ar dir
+to the certificate search path.
+.Ar dir
+must be an absolute pathname,
+.Xr vis 3 Ns -encoded .
+.Pp
+All certificates must have unique base names across all directories
+in the certificate search path.
+.\""""""""""""""""""
+.It Cm manual
+Manual override.
+If specified,
+.Nm
+will
+.Em not
+modify
+.Ar certsdir ,
+but may still check consistency of the configuration when run and
+update
+.Ar distrustdir .
+.\""""""""""""""""""
+.El
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh FILES
+.Bl -tag -width Pa
+.It Pa /etc/openssl/certs
+Default directory of hashed HTTPS CA certificates.
+.It Pa /etc/openssl/certs/ca-certificates.crt
+Default single-file HTTPS CA certificate bundle.
+.It Pa /etc/openssl/certs.conf
+Default configuration file for HTTPS CA certificates.
+.It Pa /etc/openssl/untrusted
+Default
+.Ar untrusted
+directory of excluded HTTPS CA certificates.
+.It Pa /usr/share/certs/mozilla/all
+All root CA certificates published by Mozilla, including untrustworthy
+certificates.
+.It Pa /usr/share/certs/mozilla/code
+All root CA certificates published by Mozilla for use in code-signing.
+.It Pa /usr/share/certs/mozilla/email
+All root CA certificates published by Mozilla for use in email
+authentication.
+.It Pa /usr/share/certs/mozilla/server
+All root CA certificates published by Mozilla for use in HTTPS server
+authentication.
+.El
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh EXAMPLES
+Example configuration file
+.Pq Pa /etc/openssl/certs.conf :
+.Bd -literal -offset indent
+netbsd-certctl 20230816
+
+# Blank lines and comments are ignored.
+# Comments begin with a `#' sign.
+
+# Gather certificates from files called *.cer, *.crt, and *.pem
+# under these directories.
+path /usr/share/certs/mozilla/server
+path /usr/pkg/share/chromium-cacerts
+
+# If the next line is uncommented, certctl(8) will decline to
+# touch /etc/openssl/certs.
+#manual
+.Ed
+.Pp
+Exclude a certificate:
+.Bd -literal -offset indent
+$ certctl untrust /usr/share/certs/mozilla/server/GTS_Root_R1.pem
+.Ed
+.Pp
+There is no need to run
+.Nm Cm rehash
+explicitly after
+.Nm Cm untrust ,
+but if you do, the setting will persist.
+.Pp
+Rebuild the hashed certificate cache at
+.Pa /etc/myapplication/certs
+from
+.Pa /etc/myapplication/certs.conf
+and
+.Pa /etc/myapplication/untrusted :
+.Bd -literal -offset indent
+$ certctl -c /etc/myapplication/certs \e
+ -C /etc/myapplication/certs.conf \e
+ -u /etc/myapplication/untrusted
+.Ed
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh DIAGNOSTICS
+.Ex -std
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh COMPATIBILITY
+The
+.Nm
+utility is mostly compatible with a utility of the same name in
+.Fx .
+Differences:
+.Bl -enum
+.\""""""""""""""""""
+.It
+.Fx Nm
+supports destdir/metalog handling;
+.Nx Nm
+does not.
+.\""""""""""""""""""
+.It
+.Fx Nm
+treats
+.Pa /etc/ssl/certs
+and
+.Pa /etc/ssl/untrusted
+both as configuration
+.Em and
+as caches;
+.Nx Nm
+treats
+.Pa /etc/openssl/certs.conf
+and
+.Pa /etc/openssl/untrusted
+as configuration, and treats
+.Pa /etc/openssl/certs
+strictly as a cache.
+.Fx Nm
+will forget any
+.Nm Cm untrust
+settings on
+.Nm Cm rehash ,
+but
+.Nx Nm
+will remember them.
+.\""""""""""""""""""
+.It
+.Fx Nm
+takes configuration through environment variables;
+.Nx Nm
+takes configuration through a file and command-line arguments.
+.El
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SEE ALSO
+.Xr openssl 1 ,
+.Xr openssl_rehash 1
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh HISTORY
+.Nm
+first appeared in
+.Nx 10.0 .
+A utility of the same name previously appeared in
+.Fx 12.2 .
Index: src/usr.sbin/certctl/certctl.sh
diff -u /dev/null src/usr.sbin/certctl/certctl.sh:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/usr.sbin/certctl/certctl.sh Sat Aug 26 05:27:15 2023
@@ -0,0 +1,658 @@
+#!/bin/sh
+
+# $NetBSD: certctl.sh,v 1.1 2023/08/26 05:27:15 riastradh Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+set -o pipefail
+set -Ceu
+
+progname=$(basename -- "$0")
+
+### Options and arguments
+
+usage()
+{
+ exec >&2
+ printf 'Usage: %s %s\n' \
+ "$progname" \
+ "[-nv] [-C <config>] [-c <certsdir>] [-u <untrusted>]"
+ printf ' <cmd> <args>...\n'
+ printf ' %s list\n' "$progname"
+ printf ' %s rehash\n' "$progname"
+ printf ' %s trust <cert>\n' "$progname"
+ printf ' %s untrust <cert>\n' "$progname"
+ printf ' %s untrusted\n' "$progname"
+ exit 1
+}
+
+certsdir=/etc/openssl/certs
+config=/etc/openssl/certs.conf
+distrustdir=/etc/openssl/untrusted
+nflag=false # dry run
+vflag=false # verbose
+
+# Options used by FreeBSD:
+#
+# -D destdir
+# -M metalog
+# -U (unprivileged)
+# -d distbase
+#
+while getopts C:c:nu:v f; do
+ case $f in
+ C) config=$OPTARG;;
+ c) certsdir=$OPTARG;;
+ n) nflag=true;;
+ u) distrustdir=$OPTARG;;
+ v) vflag=true;;
+ \?) usage;;
+ esac
+done
+shift $((OPTIND - 1))
+
+if [ $# -lt 1 ]; then
+ usage
+fi
+cmd=$1
+
+### Global state
+
+config_paths=
+config_manual=false
+tmpfile=
+
+# If tmpfile is set to nonempty, clean it up on exit.
+
+trap 'test -n "$tmpfile" && rm -f "$tmpfile"' EXIT HUP INT TERM
+
+### Subroutines
+
+# error <msg> ...
+#
+# Print an error message to stderr.
+#
+# Does not exit the process.
+#
+error()
+{
+ echo "$progname:" "$@" >&2
+}
+
+# run <cmd> <args>...
+#
+# Print a command if verbose, and run it unless it's a dry run.
+#
+run()
+{
+ local t q cmdline
+
+ if $vflag; then # print command if verbose
+ for t; do
+ case $t in
+ ''|*[^[:alnum:]+,-./:=_@]*)
+ # empty or unsafe -- quotify
+ ;;
+ *)
+ # nonempty and safe-only -- no quotify
+ cmdline="${cmdline:+$cmdline }$t"
+ continue
+ ;;
+ esac
+ q=$(printf '%s' "$t" | sed -e "s/'/'\\\''/g'")
+ cmdline="${cmdline:+$cmdline }'$q'"
+ done
+ printf '%s\n' "$cmdline"
+ fi
+ if ! $nflag; then # skip command if dry run
+ "$@"
+ fi
+}
+
+# configure
+#
+# Parse the configuration file, initializing config_*.
+#
+configure()
+{
+ local lineno status formatok vconfig line contline op path vpath vop
+
+ # Count line numbers, record a persistent error status to
+ # return at the end, and record whether we got a format line.
+ lineno=0
+ status=0
+ formatok=false
+
+ # vis the config name for terminal-safe error messages.
+ vconfig=$(printf '%s' "$config" | vis -M)
+
+ # Read and process each line of the config file.
+ while read -r line; do
+ lineno=$((lineno + 1))
+
+ # If the line ends in an odd number of backslashes, it
+ # has a continuation line, so read on.
+ while expr "$line" : '^\(\\\\\)*\\' >/dev/null ||
+ expr "$line" : '^.*[^\\]\(\\\\\)*\\$' >/dev/null; do
+ if ! read -r contline; then
+ error "$vconfig:$lineno: premature end of file"
+ return 1
+ fi
+ line="$line$contline"
+ done
+
+ # Skip blank lines and comments.
+ case $line in
+ ''|'#'*)
+ continue
+ ;;
+ esac
+
+ # Require the first non-blank/comment line to identify
+ # the config file format.
+ if ! $formatok; then
+ if [ "$line" = "netbsd-certctl 20230816" ]; then
+ formatok=true
+ continue
+ else
+ error "$vconfig:$lineno: missing format line"
+ status=1
+ break
+ fi
+ fi
+
+ # Split the line into words and dispatch on the first.
+ set -- $line
+ op=$1
+ case $op in
+ manual)
+ config_manual=true
+ ;;
+ path)
+ if [ $# -lt 2 ]; then
+ error "$vconfig:$lineno: missing path"
+ status=1
+ continue
+ fi
+ if [ $# -gt 3 ]; then
+ error "$vconfig:$lineno: excess args"
+ status=1
+ continue
+ fi
+
+ # Unvis the path. Hack: if the user has had
+ # the audacity to choose a path ending in
+ # newlines, prevent the shell from consuming
+ # them so we don't choke on their subterfuge.
+ path=$(printf '%s.' "$2" | unvis)
+ path=${path%.}
+
+ # Ensure the path is absolute. It is unclear
+ # what directory it should be relative to if
+ # not.
+ case $path in
+ /*)
+ ;;
+ *)
+ error "$vconfig:$lineno:" \
+ "relative path forbidden"
+ status=1
+ continue
+ ;;
+ esac
+
+ # Record the vis-encoded path in a
+ # space-separated list.
+ vpath=$(printf '%s' "$path" | vis -M)
+ config_paths="$config_paths $vpath"
+ ;;
+ *)
+ vop=$(printf '%s' "$op" | vis -M)
+ error "$vconfig:$lineno: unknown command: $vop"
+ ;;
+ esac
+ done <$config
+
+ return $status
+}
+
+# list_default_trusted
+#
+# List the vis-encoded certificate paths and their base names,
+# separated by a space, for the certificates that are trusted by
+# default according to the configuration.
+#
+# No order guaranteed; caller must sort.
+#
+list_default_trusted()
+{
+ local vpath path cert base vcert vbase
+
+ for vpath in $config_paths; do
+ path=$(printf '%s.' "$vpath" | unvis)
+ path=${path%.}
+
+ # Enumerate the .pem, .cer, and .crt files.
+ for cert in "$path"/*.pem "$path"/*.cer "$path"/*.crt; do
+ # vis the certificate path.
+ vcert=$(printf '%s' "$cert" | vis -M)
+
+ # If the file doesn't exist, then either:
+ #
+ # (a) it's a broken symlink, so fail;
+ # or
+ # (b) the shell glob failed to match,
+ # so ignore it and move on.
+ if [ ! -e "$cert" ]; then
+ if [ -h "$cert" ]; then
+ error "broken symlink: $vcert"
+ status=1
+ fi
+ continue
+ fi
+
+ # Print the vis-encoded absolute path to the
+ # certificate and base name on a single line.
+ vbase=$(basename -- "$vcert.")
+ vbase=${vbase%.}
+ printf '%s %s\n' "$vcert" "$vbase"
+ done
+ done
+}
+
+# list_distrusted
+#
+# List the vis-encoded certificate paths and their base names,
+# separated by a space, for the certificates that have been
+# distrusted by the user.
+#
+# No order guaranteed; caller must sort.
+#
+list_distrusted()
+{
+ local status link vlink cert vcert
+
+ status=0
+
+ for link in "$distrustdir"/*; do
+ # vis the link for terminal-safe error messages.
+ vlink=$(printf '%s' "$link" | vis -M)
+
+ # The distrust directory must only have symlinks to
+ # certificates. If we find a non-symlink, print a
+ # warning and arrange to fail.
+ if [ ! -h "$link" ]; then
+ if [ ! -e "$link" ] && \
+ [ "$link" = "$distrustdir/*" ]; then
+ # Shell glob matched nothing -- just
+ # ignore it.
+ break
+ fi
+ error "distrusted non-symlink: $vlink"
+ status=1
+ continue
+ fi
+
+ # Read the target of the symlink, nonrecursively. If
+ # the user has had the audacity to make a symlink whose
+ # target ends in newline, prevent the shell from
+ # consuming them so we don't choke on their subterfuge.
+ cert=$(readlink -n -- "$link" && printf .)
+ cert=${cert%.}
+
+ # Warn if the target is relative. Although it is clear
+ # what directory it would be relative to, there might
+ # be issues with canonicalization.
+ case $cert in
+ /*)
+ ;;
+ *)
+ vlink=$(printf '%s' "$link" | vis -M)
+ vcert=$(printf '%s' "$cert" | vis -M)
+ error "distrusted relative symlink: $vlink -> $vcert"
+ ;;
+ esac
+
+ # Print the vis-encoded absolute path to the
+ # certificate and base name on a single line.
+ vcert=$(printf '%s' "$cert" | vis -M)
+ vbase=$(basename -- "$vcert.")
+ vbase=${vbase%.}
+ printf '%s %s\n' "$vcert" "$vbase"
+ done
+
+ return $status
+}
+
+# list_trusted
+#
+# List the trusted certificates, excluding the distrusted one, as
+# one vis(3) line per certificate. Reject duplicate base names,
+# since we will be creating symlinks to the same base names in
+# the certsdir. Sorted lexicographically by vis-encoding.
+#
+list_trusted()
+{
+
+ # XXX Use dev/ino to match files instead of symlink targets?
+
+ {
+ list_default_trusted \
+ | while read -r vcert vbase; do
+ printf 'trust %s %s\n' "$vcert" "$vbase"
+ done
+
+ # XXX Find a good way to list the default-untrusted
+ # certificates, so if you have already distrusted one
+ # and it is removed from default-trust on update,
+ # nothing warns about this.
+
+ # list_default_untrusted \
+ # | while read -r vcert vbase; do
+ # printf 'distrust %s %s\n' "$vcert" "$vbase"
+ # done
+
+ list_distrusted \
+ | while read -r vcert vbase; do
+ printf 'distrust %s %s\n' "$vcert" "$vbase"
+ done
+ } | awk -v progname="$progname" '
+ BEGIN { status = 0 }
+ $1 == "trust" && $3 in trust && $2 != trust[$3] {
+ printf "%s: duplicate base name %s\n %s\n %s\n", \
+ progname, $3, trust[$3], $2 >"/dev/stderr"
+ status = 1
+ next
+ }
+ $1 == "trust" { trust[$3] = $2 }
+ $1 == "distrust" && !trust[$3] && !distrust[$3] {
+ printf "%s: distrusted certificate not found: %s\n", \
+ progname, $3 >"/dev/stderr"
+ status = 1
+ }
+ $1 == "distrust" && $2 in trust && $2 != trust[$3] {
+ printf "%s: distrusted certificate %s" \
+ " has multiple paths\n" \
+ " %s\n %s\n",
+ progname, $3, trust[$3], $2 >"/dev/stderr"
+ status = 1
+ }
+ $1 == "distrust" { distrust[$3] = 1 }
+ END {
+ for (vbase in trust) {
+ if (!distrust[vbase])
+ print trust[vbase]
+ }
+ exit status
+ }
+ ' | sort -u
+}
+
+# rehash
+#
+# Delete and rebuild certsdir.
+#
+rehash()
+{
+ local vcert cert certbase hash counter bundle vbundle
+
+ # If manual operation is enabled, refuse to rehash the
+ # certsdir, but succeed anyway so this can safely be used in
+ # automated scripts.
+ if $config_manual; then
+ error "manual certificates enabled, not rehashing"
+ return
+ fi
+
+ # Delete the active certificates symlink cache.
+ run rm -rf "$certsdir"
+ run mkdir "$certsdir"
+
+ # Create a temporary file for the single-file bundle. This
+ # will be automatically deleted on normal exit or
+ # SIGHUP/SIGINT/SIGTERM.
+ if ! $nflag; then
+ tmpfile=$(mktemp -t "$progname.XXXXXX")
+ fi
+
+ # Recreate symlinks for all of the trusted certificates.
+ list_trusted \
+ | while read -r vcert; do
+ cert=$(printf '%s.' "$vcert" | unvis)
+ cert=${cert%.}
+ run ln -s -- "$cert" "$certsdir"
+
+ # Add the certificate to the single-file bundle.
+ if ! $nflag; then
+ cat -- "$cert" >>$tmpfile
+ fi
+ done
+
+ # Hash the directory with openssl.
+ #
+ # XXX Pass `-v' to openssl in a way that doesn't mix with our
+ # shell-safe verbose commands? (Need to handle `-n' too.)
+ run openssl rehash -- "$certsdir"
+
+ # Install the single-file bundle.
+ bundle=$certsdir/ca-certificates.crt
+ vbundle=$(printf '%s' "$bundle" | vis -M)
+ $vflag && printf '# create %s\n' "$vbundle"
+ if ! $nflag; then
+ cp -- "$tmpfile" "$bundle"
+ rm -f -- "$tmpfile"
+ tmpfile=
+ fi
+}
+
+### Commands
+
+usage_list()
+{
+ exec >&2
+ printf 'Usage: %s list\n' "$progname"
+ exit 1
+}
+cmd_list()
+{
+ test $# -eq 1 || usage_list
+
+ configure
+
+ list_trusted \
+ | while read -r vcert vbase; do
+ printf '%s\n' "$vcert"
+ done
+}
+
+usage_rehash()
+{
+ exec >&2
+ printf 'Usage: %s rehash\n' "$progname"
+ exit 1
+}
+cmd_rehash()
+{
+ test $# -eq 1 || usage_rehash
+
+ configure
+
+ rehash
+}
+
+usage_trust()
+{
+ exec >&2
+ printf 'Usage: %s trust <cert>\n' "$progname"
+ exit 1
+}
+cmd_trust()
+{
+ local cert vcert certbase vcertbase
+
+ test $# -eq 2 || usage_trust
+ cert=$2
+
+ configure
+
+ # XXX Accept base name.
+
+ # vis the certificate path for terminal-safe error messages.
+ vcert=$(printf '%s' "$cert" | vis -M)
+
+ # Verify the certificate actually exists.
+ if [ ! -f "$cert" ]; then
+ error "no such certificate: $vcert"
+ return 1
+ fi
+
+ # Verify we currently distrust a certificate by this base name.
+ certbase=$(basename -- "$cert.")
+ certbase=${certbase%.}
+ if [ ! -h "$distrustdir/$certbase" ]; then
+ error "not currently distrusted: $vcert"
+ return 1
+ fi
+
+ # Verify the certificate we distrust by this base name is the
+ # same one.
+ target=$(readlink -n -- "$distrustdir/$certbase" && printf .)
+ target=${target%.}
+ if [ "$cert" != "$target" ]; then
+ vcertbase=$(basename -- "$vcert")
+ error "distrusted $vcertbase does not point to $vcert"
+ return 1
+ fi
+
+ # Remove the link from the distrusted directory, and rehash --
+ # quietly, so verbose output emphasizes the distrust part and
+ # not the whole certificate set.
+ run rm -- "$distrustdir/$certbase"
+ $vflag && echo '# rehash'
+ vflag=false
+ rehash
+}
+
+usage_untrust()
+{
+ exec >&2
+ printf 'Usage: %s untrust <cert>\n' "$progname"
+ exit 1
+}
+cmd_untrust()
+{
+ local cert vcert certbase vcertbase target vtarget
+
+ test $# -eq 2 || usage_untrust
+ cert=$2
+
+ configure
+
+ # vis the certificate path for terminal-safe error messages.
+ vcert=$(printf '%s' "$cert" | vis -M)
+
+ # Verify the certificate actually exists. Otherwise, you might
+ # fail to distrust a certificate you intended to distrust,
+ # e.g. if you made a typo in its path.
+ if [ ! -f "$cert" ]; then
+ error "no such certificate: $vcert"
+ return 1
+ fi
+
+ # Check whether this certificate is already distrusted.
+ # - If the same base name points to the same path, stop here.
+ # - Otherwise, fail noisily.
+ certbase=$(basename "$cert.")
+ certbase=${certbase%.}
+ if [ -h "$distrustdir/$certbase" ]; then
+ target=$(readlink -n -- "$distrustdir/$certbase" && printf .)
+ target=${target%.}
+ if [ "$target" = "$cert" ]; then
+ $vflag && echo '# already distrusted'
+ return
+ fi
+ vcertbase=$(printf '%s' "$certbase" | vis -M)
+ vtarget=$(printf '%s' "$target" | vis -M)
+ error "distrusted $vcertbase at different path $vtarget"
+ return 1
+ fi
+
+ # Create the distrustdir if needed, create a symlink in it, and
+ # rehash -- quietly, so verbose output emphasizes the distrust
+ # part and not the whole certificate set.
+ test -d "$distrustdir" || run mkdir -- "$distrustdir"
+ run ln -s -- "$cert" "$distrustdir"
+ $vflag && echo '# rehash'
+ vflag=false
+ rehash
+}
+
+usage_untrusted()
+{
+ exec >&2
+ printf 'Usage: %s untrusted\n' "$progname"
+ exit 1
+}
+cmd_untrusted()
+{
+ test $# -eq 1 || usage_untrusted
+
+ configure
+
+ list_distrusted \
+ | while read -r vcert vbase; do
+ printf '%s\n' "$vcert"
+ done
+}
+
+### Main
+
+# We accept the following aliases for user interface compatibility with
+# FreeBSD:
+#
+# blacklist = untrust
+# blacklisted = untrusted
+# unblacklist = trust
+
+case $cmd in
+list) cmd_list "$@"
+ ;;
+rehash) cmd_rehash "$@"
+ ;;
+trust|unblacklist)
+ cmd_trust "$@"
+ ;;
+untrust|blacklist)
+ cmd_untrust "$@"
+ ;;
+untrusted|blacklisted)
+ cmd_untrusted "$@"
+ ;;
+*) vcmd=$(printf '%s' "$cmd" | vis -M)
+ printf '%s: unknown command: %s\n' "$progname" "$vcmd" >&2
+ usage
+ ;;
+esac
Index: src/usr.sbin/certctl/certs.conf
diff -u /dev/null src/usr.sbin/certctl/certs.conf:1.1
--- /dev/null Sat Aug 26 05:27:15 2023
+++ src/usr.sbin/certctl/certs.conf Sat Aug 26 05:27:15 2023
@@ -0,0 +1,13 @@
+netbsd-certctl 20230816
+
+# $NetBSD: certs.conf,v 1.1 2023/08/26 05:27:15 riastradh Exp $
+#
+# Configuration file for certctl(8) to manage HTTPS root CA
+# certificates in /etc/openssl/certs.
+#
+
+path /usr/share/certs/mozilla/server
+
+# For manual control over /etc/openssl/certs, e.g. if you want to
+# install a separate CA bundle from pkgsrc, uncomment the next line:
+#manual