Page MenuHomeFreeBSD

D51896.id160368.diff
No OneTemporary

D51896.id160368.diff

diff --git a/Makefile.inc1 b/Makefile.inc1
--- a/Makefile.inc1
+++ b/Makefile.inc1
@@ -1021,7 +1021,8 @@
.endif
.if make(distributeworld)
-CERTCTLDESTDIR= ${DESTDIR}/${DISTDIR}/base
+CERTCTLDESTDIR= ${DESTDIR}/${DISTDIR}
+CERTCTLFLAGS+= -d /base
.else
CERTCTLDESTDIR= ${DESTDIR}
.endif
diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8
--- 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 August 11, 2025
+.Dd August 14, 2025
.Dt CERTCTL 8
.Os
.Sh NAME
@@ -63,6 +63,8 @@
command.
.It Fl D Ar destdir
Specify the DESTDIR (overriding values from the environment).
+.It Fl d Ar distbase
+Specify the DISTBASE (overriding values from the environment).
.It Fl l
When listing installed (trusted or untrusted) certificates, show the
full path and distinguished name for each certificate.
@@ -117,7 +119,13 @@
.Sh ENVIRONMENT
.Bl -tag -width UNTRUSTDESTDIR
.It Ev DESTDIR
-Alternate destination directory to operate on.
+Absolute path to an alternate destination directory to operate on
+instead of the file system root, e.g.
+.Dq Li /tmp/install .
+.It Ev DISTBASE
+Additional path component to include when operating on certificate directories.
+This should start with a slash, e.g.
+.Dq Li /base .
.It Ev LOCALBASE
Location for local programs.
Defaults to the value of the user.localbase sysctl which is usually
@@ -125,22 +133,22 @@
.It Ev TRUSTPATH
List of paths to search for trusted certificates.
Default:
-.Pa ${DESTDIR}/usr/share/certs/trusted
+.Pa ${DESTDIR}${DISTBASE}/usr/share/certs/trusted
.Pa ${DESTDIR}${LOCALBASE}/share/certs/trusted
.Pa ${DESTDIR}${LOCALBASE}/share/certs
.It Ev UNTRUSTPATH
List of paths to search for untrusted certificates.
Default:
-.Pa ${DESTDIR}/usr/share/certs/untrusted
+.Pa ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted
.Pa ${DESTDIR}${LOCALBASE}/share/certs/untrusted
.It Ev TRUSTDESTDIR
Destination directory for symbolic links to trusted certificates.
Default:
-.Pa ${DESTDIR}/etc/ssl/certs
+.Pa ${DESTDIR}${DISTBASE}/etc/ssl/certs
.It Ev UNTRUSTDESTDIR
Destination directory for symbolic links to untrusted certificates.
Default:
-.Pa ${DESTDIR}/etc/ssl/untrusted
+.Pa ${DESTDIR}${DISTBASE}/etc/ssl/untrusted
.It Ev BUNDLE
File name of bundle to produce.
.El
diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c
--- a/usr.sbin/certctl/certctl.c
+++ b/usr.sbin/certctl/certctl.c
@@ -63,6 +63,7 @@
static const char *localbase;
static const char *destdir;
+static const char *distbase;
static const char *metalog;
static const char *uname = "root";
@@ -99,6 +100,28 @@
static FILE *mlf;
+/*
+ * Remove duplicate and trailing slashes from a path.
+ */
+static char *
+normalize_path(const char *str)
+{
+ char *buf, *dst;
+
+ if ((buf = malloc(strlen(str))) == NULL)
+ err(1, NULL);
+ for (dst = buf; *str != '\0'; dst++) {
+ if ((*dst = *str++) == '/') {
+ while (*str == '/')
+ str++;
+ if (*str == '\0')
+ break;
+ }
+ }
+ *dst = '\0';
+ return (buf);
+}
+
/*
* Split a colon-separated list into a NULL-terminated array.
*/
@@ -124,14 +147,14 @@
}
/*
- * Expand %L into LOCALBASE and prefix DESTDIR.
+ * Expand %L into LOCALBASE and prefix DESTDIR and DISTBASE as needed.
*/
static char *
expand_path(const char *template)
{
if (template[0] == '%' && template[1] == 'L')
return (xasprintf("%s%s%s", destdir, localbase, template + 2));
- return (xasprintf("%s%s", destdir, template));
+ return (xasprintf("%s%s%s", destdir, distbase, template));
}
/*
@@ -155,6 +178,9 @@
/*
* If destdir is a prefix of path, returns a pointer to the rest of path,
* otherwise returns path.
+ *
+ * Note that this intentionally does not strip distbase from the path!
+ * Unlike destdir, distbase is expected to be included in the metalog.
*/
static const char *
unexpand_path(const char *path)
@@ -294,7 +320,6 @@
name = X509_get_subject_name(x509);
cert->hash = X509_NAME_hash_ex(name, NULL, NULL, NULL);
cert->name = X509_NAME_oneline(name, NULL, 0);
- cert->path = xstrdup(unexpand_path(path));
if (RB_INSERT(cert_tree, tree, cert) != NULL)
errx(1, "unexpected duplicate");
info("%08lx: %s", cert->hash, strrchr(cert->name, '=') + 1);
@@ -488,9 +513,10 @@
free(tmppath);
tmppath = NULL;
}
+ fflush(f);
/* emit metalog */
if (mlf != NULL) {
- fprintf(mlf, "%s/%s type=file "
+ fprintf(mlf, ".%s/%s type=file "
"uname=%s gname=%s mode=%#o size=%ld\n",
unexpand_path(dir), path,
uname, gname, mode, ftell(f));
@@ -561,7 +587,7 @@
}
if (ret == 0 && mlf != NULL) {
fprintf(mlf,
- "%s/%s type=file uname=%s gname=%s mode=%#o size=%ld\n",
+ ".%s/%s type=file uname=%s gname=%s mode=%#o size=%ld\n",
unexpand_path(dir), file, uname, gname, mode, ftell(f));
}
fclose(f);
@@ -925,6 +951,12 @@
if (destdir == NULL &&
(destdir = getenv("DESTDIR")) == NULL)
destdir = "";
+ destdir = normalize_path(destdir);
+
+ if (distbase == NULL &&
+ (distbase = getenv("DISTBASE")) == NULL)
+ distbase = "";
+ distbase = normalize_path(distbase);
if (unprivileged && metalog == NULL &&
(metalog = getenv("METALOG")) == NULL)
@@ -966,6 +998,7 @@
info("localbase:\t%s", localbase);
info("destdir:\t%s", destdir);
+ info("distbase:\t%s", distbase);
info("unprivileged:\t%s", unprivileged ? "true" : "false");
info("verbose:\t%s", verbose ? "true" : "false");
}
@@ -987,11 +1020,11 @@
static void
usage(void)
{
- fprintf(stderr, "usage: certctl [-lv] [-D destdir] list\n"
- " certctl [-lv] [-D destdir] untrusted\n"
- " certctl [-BnUv] [-D destdir] [-M metalog] rehash\n"
- " certctl [-nv] [-D destdir] untrust <file>\n"
- " certctl [-nv] [-D destdir] trust <file>\n");
+ fprintf(stderr, "usage: certctl [-lv] [-D destdir] [-d distbase] list\n"
+ " certctl [-lv] [-D destdir] [-d distbase] untrusted\n"
+ " certctl [-BnUv] [-D destdir] [-d distbase] [-M metalog] rehash\n"
+ " certctl [-nv] [-D destdir] [-d distbase] untrust <file>\n"
+ " certctl [-nv] [-D destdir] [-d distbase] trust <file>\n");
exit(1);
}
@@ -1001,7 +1034,7 @@
const char *command;
int opt;
- while ((opt = getopt(argc, argv, "BcD:g:lL:M:no:Uv")) != -1)
+ while ((opt = getopt(argc, argv, "BcD:d:g:lL:M:no:Uv")) != -1)
switch (opt) {
case 'B':
nobundle = true;
@@ -1012,6 +1045,9 @@
case 'D':
destdir = optarg;
break;
+ case 'd':
+ distbase = optarg;
+ break;
case 'g':
gname = optarg;
break;
diff --git a/usr.sbin/certctl/tests/certctl_test.sh b/usr.sbin/certctl/tests/certctl_test.sh
--- a/usr.sbin/certctl/tests/certctl_test.sh
+++ b/usr.sbin/certctl/tests/certctl_test.sh
@@ -65,36 +65,50 @@
export DESTDIR="$PWD"
# Create input directories
- mkdir -p usr/share/certs/trusted
- mkdir -p usr/share/certs/untrusted
- mkdir -p usr/local/share/certs
+ mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/trusted
+ mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted
+ mkdir -p ${DESTDIR}/usr/local/share/certs
# Create output directories
- mkdir -p etc/ssl/certs
- mkdir -p etc/ssl/untrusted
+ mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/certs
+ mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/untrusted
# Generate a random key
keyname="testkey"
gen_key ${keyname}
# Generate certificates
+ :>metalog.expect
+ metalog() {
+ echo ".${DISTBASE}$@ type=file" >>metalog.expect
+ }
set1 | while read crtname hash ; do
gen_crt ${crtname} ${keyname}
- mv ${crtname}.crt usr/share/certs/trusted
+ mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted
+ metalog "/etc/ssl/certs/${hash}.0"
done
+ local c=0
coll | while read crtname hash ; do
gen_crt ${crtname} ${keyname}
- mv ${crtname}.crt usr/share/certs/trusted
+ mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted
+ metalog "/etc/ssl/certs/${hash}.${c}"
+ c=$((c+1))
done
set2 | while read crtname hash ; do
gen_crt ${crtname} ${keyname}
openssl x509 -in ${crtname}.crt
rm ${crtname}.crt
+ metalog "/etc/ssl/certs/${hash}.0"
done >usr/local/share/certs/bundle.crt
set3 | while read crtname hash ; do
gen_crt ${crtname} ${keyname}
- mv ${crtname}.crt usr/share/certs/untrusted
+ mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted
+ metalog "/etc/ssl/untrusted/${hash}.0"
done
+ metalog "/etc/ssl/cert.pem"
+ unfunction metalog
+ sort metalog.expect >metalog.expect.sorted
+ mv metalog.expect.sorted metalog.expect
}
check_trusted() {
@@ -102,12 +116,12 @@
local subject="$(subject ${crtname})"
local c=${2:-1}
- atf_check -o match:"found: ${c}\$" \
+ atf_check -e ignore -o match:"found: ${c}\$" \
openssl storeutl -noout -subject "${subject}" \
- etc/ssl/certs
- atf_check -o match:"found: 0\$" \
+ ${DESTDIR}${DISTBASE}/etc/ssl/certs
+ atf_check -e ignore -o not-match:"found: [1-9]" \
openssl storeutl -noout -subject "${subject}" \
- etc/ssl/untrusted
+ ${DESTDIR}${DISTBASE}/etc/ssl/untrusted
}
check_untrusted() {
@@ -115,23 +129,25 @@
local subject="$(subject ${crtname})"
local c=${2:-1}
- atf_check -o match:"found: 0\$" \
+ atf_check -e ignore -o not-match:"found: [1-9]" \
openssl storeutl -noout -subject "${subject}" \
- etc/ssl/certs
- atf_check -o match:"found: ${c}\$" \
+ ${DESTDIR}/${DISTBASE}/etc/ssl/certs
+ atf_check -e ignore -o match:"found: ${c}\$" \
openssl storeutl -noout -subject "${subject}" \
- etc/ssl/untrusted
+ ${DESTDIR}/${DISTBASE}/etc/ssl/untrusted
}
check_in_bundle() {
+ local b=${DISTBASE}${DISTBASE+/}
local crtfile=$1
local line
line=$(tail +5 "${crtfile}" | head -1)
- atf_check grep -q "${line}" etc/ssl/cert.pem
+ atf_check grep -q "${line}" ${DESTDIR}${DISTBASE}/etc/ssl/cert.pem
}
check_not_in_bundle() {
+ local b=${DISTBASE}${DISTBASE+/}
local crtfile=$1
local line
@@ -150,7 +166,7 @@
atf_check certctl rehash
# Verify non-colliding trusted certificates
- (set1 ; set2) > trusted
+ (set1; set2) >trusted
while read crtname hash ; do
check_trusted "${crtname}"
done <trusted
@@ -167,7 +183,7 @@
check_untrusted "${crtname}"
done <untrusted
- # Verify bundle; storeutl is no help here
+ # Verify bundle
for f in etc/ssl/certs/*.? ; do
check_in_bundle "${f}"
done
@@ -213,9 +229,41 @@
check_not_in_bundle ${crtfile}
}
+atf_test_case metalog
+metalog_head()
+{
+ atf_set "descr" "Verify the metalog"
+}
+metalog_body()
+{
+ export DISTBASE=/base
+ certctl_setup
+
+ # certctl gets DESTDIR and DISTBASE from environment
+ rm -f metalog.orig
+ atf_check certctl -U -M metalog.orig rehash
+ sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short
+ atf_check diff -u metalog.expect metalog.short
+
+ # certctl gets DESTDIR and DISTBASE from command line
+ rm -f metalog.orig
+ atf_check env -uDESTDIR -uDISTBASE \
+ certctl -D ${DESTDIR} -d ${DISTBASE} -U -M metalog.orig rehash
+ sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short
+ atf_check diff -u metalog.expect metalog.short
+
+ # as above, but intentionally add trailing slashes
+ rm -f metalog.orig
+ atf_check env -uDESTDIR -uDISTBASE \
+ certctl -D ${DESTDIR}// -d ${DISTBASE}/ -U -M metalog.orig rehash
+ sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short
+ atf_check diff -u metalog.expect metalog.short
+}
+
atf_init_test_cases()
{
atf_add_test_case rehash
atf_add_test_case trust
atf_add_test_case untrust
+ atf_add_test_case metalog
}

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 30, 5:38 AM (19 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28095105
Default Alt Text
D51896.id160368.diff (11 KB)

Event Timeline