From nobody Tue May 5 22:31:23 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4g9Csm04RFz6cmYl for ; Tue, 05 May 2026 22:31:24 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4g9Csl6H3Mz4Ql4 for ; Tue, 05 May 2026 22:31:23 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1778020283; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=vOWeIQJSmtA2Khd2mHwMKIG/YgJCvka+2Tkjsc3W3hc=; b=juyZ+U2N91sdc0ZG6szG0kbDuxYzkHg6ri0FL7gmLIjh1wKUUocQKKwERjylbJs/yh3T2I 0mzlSHJ+ZeDCI2FwL/YxOFd6A5JYOEO5SneBRrF1yRvWGm4hTZ4Vv1DpQhb3e3DKsguxi9 jWRLLzjvHg6zfh9AQoTOSSiaFZMoRyJDJqUdXyIag9dEMm6CbJgaSvK2/vlkxR1I217vGT aMTWG9+2Oc+yNGqDPUrBzB4RZhuZaVLu4Fm+2WcC/1xtAYVCNtAW7KHMj1XwC+DD8Wm6V1 MgRZvkMj35W03uaNLbLIBCuCNALkREnvpvFhiwFiS5w1X6tzLW2wKaumIA1Dkg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1778020283; a=rsa-sha256; cv=none; b=ats1eLZwsZwdIABPH1nAT3bOLiytRgSN1sxSpPEky/EyXWI/JcSeER02ixwqCaanE91veu O+CtNVE7z2THCdkZ4WahaSB58FUSB7hvyyYF+mXSn5q2gighdUqsIOEe+Lqmk1DvshlQN7 X0UqaOV8xH9VcPEYCGXR4nLayP4/d8wbPuBwBij/JfhYxp+gTsyqqUpCWHvinZRIKTqhwA 39NjdQzWiz83upm7MNllJhmUfDlg4fJM2IipB6Gteb37gAS3MBg1t26UbvTpLnSLIcknc3 cPnckHIMhUfVENxLEoDwx9O5qASWJ5r/c3YcMOR4HyquSX+6BFG7uXM5HaJObQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1778020283; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=vOWeIQJSmtA2Khd2mHwMKIG/YgJCvka+2Tkjsc3W3hc=; b=EB2ejf+UUWGnc3YYsmqRnyp3yHQFdF5lPDL1f6kkJhtmeIw/VOUaf6Bya/7QhyNFWtDRK2 hRJzaTD0M0mBKnvJCzP0U4pL2CJkXgvxd+U4r0SYpI36e1kxuvGIVP0jzx01GiG8w/mVUW iBDPmKjlm9UDmUp5LNaJpF1BVnCOeXQ60hQdszDzU4vwADKeJTCnzfnOY+DseZv/A44M0D k4zaN8yQvC0wq2dUufbWKFy3T31aaqUKRCuP6kXAVtFx9QrvJ3rdflpKY3tgz9/Y57UMz+ 24QtJE5qoXeS7lqjPCe4hmohiXuIYzsngxJFMOEfxwIL3QF7QdupFaiHf/yMCw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4g9Csl5tRwz1Qnj for ; Tue, 05 May 2026 22:31:23 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 22b19 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 05 May 2026 22:31:23 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: 2fef18ff5943 - main - certctl: Unstickify (un)trusted certificates List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 2fef18ff594328a771b6aa659e8ffa5a7e076540 Auto-Submitted: auto-generated Date: Tue, 05 May 2026 22:31:23 +0000 Message-Id: <69fa6fbb.22b19.f8209b6@gitrepo.freebsd.org> The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=2fef18ff594328a771b6aa659e8ffa5a7e076540 commit 2fef18ff594328a771b6aa659e8ffa5a7e076540 Author: Dag-Erling Smørgrav AuthorDate: 2026-05-05 22:30:52 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-05-05 22:30:52 +0000 certctl: Unstickify (un)trusted certificates Ever since certctl was rewritten in C, the rehash command has reingested TRUSTDESTDIR / UNTRUSTDESTDIR in addition to TRUSTPATH / UNTRUSTPATH. This seemed like a good idea at the time but was, in retrospect, a mistake, as it means a (un)trusted certificate remains (un)trusted forever (or at least until it expires) even if it is removed from (UN)TRUSTPATH. Among other issues, it causes ports QA to fail for any port that either installs certificates or depends on a port that does. Although this behavior was undocumented, the change may surprise users who have added certificates manually, so update the manual page to point it out and add prominent warnings to the trust and untrust commands. PR: 290078 MFC after: 1 week Reviewed by: kevans, bcr Differential Revision: https://reviews.freebsd.org/D56617 --- usr.sbin/certctl/certctl.8 | 15 ++++++- usr.sbin/certctl/certctl.c | 81 ++++++++++++++++++++++------------ usr.sbin/certctl/tests/certctl_test.sh | 3 +- 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8 index 2d38ce020c04..7df89e4d35d0 100644 --- a/usr.sbin/certctl/certctl.8 +++ b/usr.sbin/certctl/certctl.8 @@ -24,7 +24,7 @@ .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd December 3, 2025 +.Dd April 24, 2026 .Dt CERTCTL 8 .Os .Sh NAME @@ -113,8 +113,19 @@ In addition, a bundle containing the trusted certificates is placed in .Ev BUNDLE . .It Ic untrust Add the specified file to the untrusted list. +Note that the next +.Ic rehash +will remove it unless a copy of it is also placed somewhere in a +directory included in +.Ev UNTRUSTPATH . .It Ic trust -Remove the specified file from the untrusted list. +Add the specified file to the trusted list, unless it is already +untrusted. +Note that the next +.Ic rehash +will remove it unless a copy of it is also placed somewhere in a +directory included in +.Ev TRUSTPATH . .El .Sh ENVIRONMENT .Bl -tag -width UNTRUSTDESTDIR diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c index a53ed7b2b4b2..0a07703bf37b 100644 --- a/usr.sbin/certctl/certctl.c +++ b/usr.sbin/certctl/certctl.c @@ -636,23 +636,18 @@ write_bundle(const char *dir, const char *file, struct cert_tree *tree) * Returns the number of certificates loaded. */ static unsigned int -load_trusted(bool all, struct cert_tree *exclude) +load_trusted(void) { unsigned int i, n; int ret; /* load external trusted certs */ - for (i = n = 0; all && trusted_paths[i] != NULL; i++) { - ret = read_certs(trusted_paths[i], &trusted, exclude); + for (i = n = 0; trusted_paths[i] != NULL; i++) { + ret = read_certs(trusted_paths[i], &trusted, &untrusted); if (ret > 0) n += ret; } - /* load installed trusted certs */ - ret = read_certs(trusted_dest, &trusted, exclude); - if (ret > 0) - n += ret; - info("%d trusted certificates found", n); return (n); } @@ -663,24 +658,19 @@ load_trusted(bool all, struct cert_tree *exclude) * Returns the number of certificates loaded. */ static unsigned int -load_untrusted(bool all) +load_untrusted(void) { char *path; unsigned int i, n; int ret; /* load external untrusted certs */ - for (i = n = 0; all && untrusted_paths[i] != NULL; i++) { + for (i = n = 0; untrusted_paths[i] != NULL; i++) { ret = read_certs(untrusted_paths[i], &untrusted, NULL); if (ret > 0) n += ret; } - /* load installed untrusted certs */ - ret = read_certs(untrusted_dest, &untrusted, NULL); - if (ret > 0) - n += ret; - /* load legacy untrusted certs */ path = expand_path(LEGACY_PATH); ret = read_certs(path, &untrusted, NULL); @@ -796,8 +786,8 @@ certctl_list(int argc, char **argv __unused) { if (argc > 1) usage(); - /* load trusted certificates */ - load_trusted(false, NULL); + /* load installed trusted certificates */ + read_certs(trusted_dest, &trusted, NULL); /* list them */ list_certs(&trusted); free_certs(&trusted); @@ -814,8 +804,8 @@ certctl_untrusted(int argc, char **argv __unused) { if (argc > 1) usage(); - /* load untrusted certificates */ - load_untrusted(false); + /* load installed untrusted certificates */ + read_certs(untrusted_dest, &untrusted, NULL); /* list them */ list_certs(&untrusted); free_certs(&untrusted); @@ -842,10 +832,10 @@ certctl_rehash(int argc, char **argv __unused) } /* load untrusted certs first */ - load_untrusted(true); + load_untrusted(); /* load trusted certs, excluding any that are already untrusted */ - load_trusted(true, &untrusted); + load_trusted(); /* save everything */ ret = save_all(); @@ -859,7 +849,8 @@ certctl_rehash(int argc, char **argv __unused) } /* - * Manually add one or more certificates to the list of trusted certificates. + * Manually add one or more certificates to the list of trusted + * certificates. * * Returns 0 on success and -1 on failure. */ @@ -875,10 +866,10 @@ certctl_trust(int argc, char **argv) usage(); /* load untrusted certs first */ - load_untrusted(true); + load_untrusted(); /* load trusted certs, excluding any that are already untrusted */ - load_trusted(true, &untrusted); + load_trusted(); /* now load the additional trusted certificates */ n = 0; @@ -891,9 +882,9 @@ certctl_trust(int argc, char **argv) warnx("no new trusted certificates found"); free_certs(&untrusted); free_certs(&trusted); - free_certs(&extra); return (0); } + warnx("%u new trusted certificate%s found", n, n > 1 ? "s" : ""); /* * For each new trusted cert, move it from the extra list to the @@ -906,10 +897,16 @@ certctl_trust(int argc, char **argv) RB_INSERT(cert_tree, &trusted, cert); if ((other = RB_FIND(cert_tree, &untrusted, cert)) != NULL) { warnx("%s was previously untrusted", cert->name); + warnx("source of untrust: %s", other->path); RB_REMOVE(cert_tree, &untrusted, other); free_cert(other); } } + warnx("This operation is not persistent. To persistently add"); + warnx("trusted certificates to the system store, copy them to"); + warnx("one of these directories, then run `certctl rehash`:"); + for (i = 0; trusted_paths[i] != NULL; i++) + warnx(" %s", trusted_paths[i]); /* save everything */ ret = save_all(); @@ -929,6 +926,8 @@ certctl_trust(int argc, char **argv) static int certctl_untrust(int argc, char **argv) { + struct cert_tree extra = RB_INITIALIZER(&extra); + struct cert *cert, *other, *tmp; unsigned int n; int i, ret; @@ -936,23 +935,47 @@ certctl_untrust(int argc, char **argv) usage(); /* load untrusted certs first */ - load_untrusted(true); + load_untrusted(); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(); /* now load the additional untrusted certificates */ n = 0; for (i = 1; i < argc; i++) { - ret = read_cert(argv[i], &untrusted, NULL); + ret = read_cert(argv[i], &extra, NULL); if (ret > 0) n += ret; } if (n == 0) { warnx("no new untrusted certificates found"); free_certs(&untrusted); + free_certs(&trusted); return (0); } + warnx("%u new untrusted certificate%s found", n, n > 1 ? "s" : ""); - /* load trusted certs, excluding any that are already untrusted */ - load_trusted(true, &untrusted); + /* + * For each new untrusted cert, move it from the extra list to the + * untrusted list, then check if a matching certificate exists on + * the trusted list. If that is the case, warn the user, then + * remove the matching certificate from the trusted list. + */ + RB_FOREACH_SAFE(cert, cert_tree, &extra, tmp) { + RB_REMOVE(cert_tree, &extra, cert); + RB_INSERT(cert_tree, &untrusted, cert); + if ((other = RB_FIND(cert_tree, &trusted, cert)) != NULL) { + warnx("%s was previously trusted", cert->name); + warnx("source of trust: %s", other->path); + RB_REMOVE(cert_tree, &trusted, other); + free_cert(other); + } + } + warnx("This operation is not persistent. To persistently add"); + warnx("untrusted certificates to the system store, copy them to"); + warnx("one of these directories, then run `certctl rehash`:"); + for (i = 0; untrusted_paths[i] != NULL; i++) + warnx(" %s", untrusted_paths[i]); /* save everything */ ret = save_all(); diff --git a/usr.sbin/certctl/tests/certctl_test.sh b/usr.sbin/certctl/tests/certctl_test.sh index 74749db0b3f5..133d65854c42 100644 --- a/usr.sbin/certctl/tests/certctl_test.sh +++ b/usr.sbin/certctl/tests/certctl_test.sh @@ -271,7 +271,8 @@ untrust_body() crtfile=usr/share/certs/trusted/${crtname}.crt check_trusted "${crtname}" check_in_bundle ${crtfile} - atf_check certctl untrust "${crtfile}" + atf_check -e match:"1 new untrusted" \ + certctl untrust "${crtfile}" check_untrusted "${crtname}" check_not_in_bundle ${crtfile} }