aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/certctl/certctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/certctl/certctl.c')
-rw-r--r--usr.sbin/certctl/certctl.c1065
1 files changed, 0 insertions, 1065 deletions
diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c
deleted file mode 100644
index 365870167aeb..000000000000
--- a/usr.sbin/certctl/certctl.c
+++ /dev/null
@@ -1,1065 +0,0 @@
-/*-
- * Copyright (c) 2023-2025 Dag-Erling Smørgrav <des@FreeBSD.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <sys/sysctl.h>
-#include <sys/stat.h>
-#include <sys/tree.h>
-
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <fts.h>
-#include <paths.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-
-#define info(fmt, ...) \
- do { \
- if (verbose) \
- fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
- } while (0)
-
-static char *
-xasprintf(const char *fmt, ...)
-{
- va_list ap;
- char *str;
- int ret;
-
- va_start(ap, fmt);
- ret = vasprintf(&str, fmt, ap);
- va_end(ap);
- if (ret < 0 || str == NULL)
- err(1, NULL);
- return (str);
-}
-
-static char *
-xstrdup(const char *str)
-{
- char *dup;
-
- if ((dup = strdup(str)) == NULL)
- err(1, NULL);
- return (dup);
-}
-
-static void usage(void);
-
-static bool dryrun;
-static bool longnames;
-static bool nobundle;
-static bool unprivileged;
-static bool verbose;
-
-static const char *localbase;
-static const char *destdir;
-static const char *metalog;
-
-static const char *uname = "root";
-static const char *gname = "wheel";
-
-static const char *const default_trusted_paths[] = {
- "/usr/share/certs/trusted",
- "%L/share/certs/trusted",
- "%L/share/certs",
- NULL
-};
-static char **trusted_paths;
-
-static const char *const default_untrusted_paths[] = {
- "/usr/share/certs/untrusted",
- "%L/share/certs/untrusted",
- NULL
-};
-static char **untrusted_paths;
-
-static char *trusted_dest;
-static char *untrusted_dest;
-static char *bundle_dest;
-
-#define SSL_PATH "/etc/ssl"
-#define TRUSTED_DIR "certs"
-#define TRUSTED_PATH SSL_PATH "/" TRUSTED_DIR
-#define UNTRUSTED_DIR "untrusted"
-#define UNTRUSTED_PATH SSL_PATH "/" UNTRUSTED_DIR
-#define LEGACY_DIR "blacklisted"
-#define LEGACY_PATH SSL_PATH "/" LEGACY_DIR
-#define BUNDLE_FILE "cert.pem"
-#define BUNDLE_PATH SSL_PATH "/" BUNDLE_FILE
-
-static FILE *mlf;
-
-/*
- * Split a colon-separated list into a NULL-terminated array.
- */
-static char **
-split_paths(const char *str)
-{
- char **paths;
- const char *p, *q;
- unsigned int i, n;
-
- for (p = str, n = 1; *p; p++) {
- if (*p == ':')
- n++;
- }
- if ((paths = calloc(n + 1, sizeof(*paths))) == NULL)
- err(1, NULL);
- for (p = q = str, i = 0; i < n; i++, p = q + 1) {
- q = strchrnul(p, ':');
- if ((paths[i] = strndup(p, q - p)) == NULL)
- err(1, NULL);
- }
- return (paths);
-}
-
-/*
- * Expand %L into LOCALBASE and prefix DESTDIR.
- */
-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));
-}
-
-/*
- * Expand an array of paths.
- */
-static char **
-expand_paths(const char *const *templates)
-{
- char **paths;
- unsigned int i, n;
-
- for (n = 0; templates[n] != NULL; n++)
- continue;
- if ((paths = calloc(n + 1, sizeof(*paths))) == NULL)
- err(1, NULL);
- for (i = 0; i < n; i++)
- paths[i] = expand_path(templates[i]);
- return (paths);
-}
-
-/*
- * If destdir is a prefix of path, returns a pointer to the rest of path,
- * otherwise returns path.
- */
-static const char *
-unexpand_path(const char *path)
-{
- const char *p = path;
- const char *q = destdir;
-
- while (*p && *p == *q) {
- p++;
- q++;
- }
- return (*q == '\0' && *p == '/' ? p : path);
-}
-
-/*
- * X509 certificate in a rank-balanced tree.
- */
-struct cert {
- RB_ENTRY(cert) entry;
- unsigned long hash;
- char *name;
- X509 *x509;
- char *path;
-};
-
-static void
-free_cert(struct cert *cert)
-{
- free(cert->name);
- X509_free(cert->x509);
- free(cert->path);
- free(cert);
-}
-
-static int
-certcmp(const struct cert *a, const struct cert *b)
-{
- return (X509_cmp(a->x509, b->x509));
-}
-
-RB_HEAD(cert_tree, cert);
-static struct cert_tree trusted = RB_INITIALIZER(&trusted);
-static struct cert_tree untrusted = RB_INITIALIZER(&untrusted);
-RB_GENERATE_STATIC(cert_tree, cert, entry, certcmp);
-
-static void
-free_certs(struct cert_tree *tree)
-{
- struct cert *cert, *tmp;
-
- RB_FOREACH_SAFE(cert, cert_tree, tree, tmp) {
- RB_REMOVE(cert_tree, tree, cert);
- free_cert(cert);
- }
-}
-
-static struct cert *
-find_cert(struct cert_tree *haystack, X509 *x509)
-{
- struct cert needle = { .x509 = x509 };
-
- return (RB_FIND(cert_tree, haystack, &needle));
-}
-
-/*
- * File containing a certificate in a rank-balanced tree sorted by
- * certificate hash and disambiguating counter. This is needed because
- * the certificate hash function is prone to collisions, necessitating a
- * counter to distinguish certificates that hash to the same value.
- */
-struct file {
- RB_ENTRY(file) entry;
- const struct cert *cert;
- unsigned int c;
-};
-
-static int
-filecmp(const struct file *a, const struct file *b)
-{
- if (a->cert->hash > b->cert->hash)
- return (1);
- if (a->cert->hash < b->cert->hash)
- return (-1);
- return (a->c - b->c);
-}
-
-RB_HEAD(file_tree, file);
-RB_GENERATE_STATIC(file_tree, file, entry, filecmp);
-
-/*
- * Lexicographical sort for scandir().
- */
-static int
-lexisort(const struct dirent **d1, const struct dirent **d2)
-{
- return (strcmp((*d1)->d_name, (*d2)->d_name));
-}
-
-/*
- * Read certificate(s) from a single file and insert them into a tree.
- * Ignore certificates that already exist in the tree. If exclude is not
- * null, also ignore certificates that exist in exclude.
- *
- * Returns the number certificates added to the tree, or -1 on failure.
- */
-static int
-read_cert(const char *path, struct cert_tree *tree, struct cert_tree *exclude)
-{
- FILE *f;
- X509 *x509;
- X509_NAME *name;
- struct cert *cert;
- unsigned long hash;
- int ni, no;
-
- if ((f = fopen(path, "r")) == NULL) {
- warn("%s", path);
- return (-1);
- }
- for (ni = no = 0;
- (x509 = PEM_read_X509(f, NULL, NULL, NULL)) != NULL;
- ni++) {
- hash = X509_subject_name_hash(x509);
- if (exclude && find_cert(exclude, x509)) {
- info("%08lx: excluded", hash);
- X509_free(x509);
- continue;
- }
- if (find_cert(tree, x509)) {
- info("%08lx: duplicate", hash);
- X509_free(x509);
- continue;
- }
- if ((cert = calloc(1, sizeof(*cert))) == NULL)
- err(1, NULL);
- cert->x509 = x509;
- 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);
- no++;
- }
- /*
- * ni is the number of certificates we found in the file.
- * no is the number of certificates that weren't already in our
- * tree or on the exclusion list.
- */
- if (ni == 0)
- warnx("%s: no valid certificates found", path);
- fclose(f);
- return (no);
-}
-
-/*
- * Load all certificates found in the specified path into a tree,
- * optionally excluding those that already exist in a different tree.
- *
- * Returns the number of certificates added to the tree, or -1 on failure.
- */
-static int
-read_certs(const char *path, struct cert_tree *tree, struct cert_tree *exclude)
-{
- struct stat sb;
- char *paths[] = { (char *)(uintptr_t)path, NULL };
- FTS *fts;
- FTSENT *ent;
- int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
- int ret, total = 0;
-
- if (stat(path, &sb) != 0) {
- return (-1);
- } else if (!S_ISDIR(sb.st_mode)) {
- errno = ENOTDIR;
- return (-1);
- }
- if ((fts = fts_open(paths, fts_options, NULL)) == NULL)
- err(1, "fts_open()");
- while ((ent = fts_read(fts)) != NULL) {
- if (ent->fts_info != FTS_F) {
- if (ent->fts_info == FTS_ERR)
- warnc(ent->fts_errno, "fts_read()");
- continue;
- }
- info("found %s", ent->fts_path);
- ret = read_cert(ent->fts_path, tree, exclude);
- if (ret > 0)
- total += ret;
- }
- fts_close(fts);
- return (total);
-}
-
-/*
- * Save the contents of a cert tree to disk.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-write_certs(const char *dir, struct cert_tree *tree)
-{
- struct file_tree files = RB_INITIALIZER(&files);
- struct cert *cert;
- struct file *file, *tmp;
- struct dirent **dents, **ent;
- char *path, *tmppath = NULL;
- FILE *f;
- mode_t mode = 0444;
- int cmp, d, fd, ndents, ret = 0;
-
- /*
- * Start by generating unambiguous file names for each certificate
- * and storing them in lexicographical order
- */
- RB_FOREACH(cert, cert_tree, tree) {
- if ((file = calloc(1, sizeof(*file))) == NULL)
- err(1, NULL);
- file->cert = cert;
- for (file->c = 0; file->c < INT_MAX; file->c++)
- if (RB_INSERT(file_tree, &files, file) == NULL)
- break;
- if (file->c == INT_MAX)
- errx(1, "unable to disambiguate %08lx", cert->hash);
- free(cert->path);
- cert->path = xasprintf("%08lx.%d", cert->hash, file->c);
- }
- /*
- * Open and scan the directory.
- */
- if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0 ||
-#ifdef BOOTSTRAPPING
- (ndents = scandir(dir, &dents, NULL, lexisort))
-#else
- (ndents = fdscandir(d, &dents, NULL, lexisort))
-#endif
- < 0)
- err(1, "%s", dir);
- /*
- * Iterate over the directory listing and the certificate listing
- * in parallel. If the directory listing gets ahead of the
- * certificate listing, we need to write the current certificate
- * and advance the certificate listing. If the certificate
- * listing is ahead of the directory listing, we need to delete
- * the current file and advance the directory listing. If they
- * are neck and neck, we have a match and could in theory compare
- * the two, but in practice it's faster to just replace the
- * current file with the current certificate (and advance both).
- */
- ent = dents;
- file = RB_MIN(file_tree, &files);
- for (;;) {
- if (ent < dents + ndents) {
- /* skip directories */
- if ((*ent)->d_type == DT_DIR) {
- free(*ent++);
- continue;
- }
- if (file != NULL) {
- /* compare current dirent to current cert */
- path = file->cert->path;
- cmp = strcmp((*ent)->d_name, path);
- } else {
- /* trailing files in directory */
- path = NULL;
- cmp = -1;
- }
- } else {
- if (file != NULL) {
- /* trailing certificates */
- path = file->cert->path;
- cmp = 1;
- } else {
- /* end of both lists */
- path = NULL;
- break;
- }
- }
- if (cmp < 0) {
- /* a file on disk with no matching certificate */
- info("removing %s/%s", dir, (*ent)->d_name);
- if (!dryrun)
- (void)unlinkat(d, (*ent)->d_name, 0);
- free(*ent++);
- continue;
- }
- if (cmp == 0) {
- /* a file on disk with a matching certificate */
- info("replacing %s/%s", dir, (*ent)->d_name);
- if (dryrun) {
- fd = open(_PATH_DEVNULL, O_WRONLY);
- } else {
- tmppath = xasprintf(".%s", path);
- fd = openat(d, tmppath,
- O_CREAT | O_WRONLY | O_TRUNC, mode);
- if (!unprivileged && fd >= 0)
- (void)fchmod(fd, mode);
- }
- free(*ent++);
- } else {
- /* a certificate with no matching file */
- info("writing %s/%s", dir, path);
- if (dryrun) {
- fd = open(_PATH_DEVNULL, O_WRONLY);
- } else {
- tmppath = xasprintf(".%s", path);
- fd = openat(d, tmppath,
- O_CREAT | O_WRONLY | O_EXCL, mode);
- }
- }
- /* write the certificate */
- if (fd < 0 ||
- (f = fdopen(fd, "w")) == NULL ||
- !PEM_write_X509(f, file->cert->x509)) {
- if (tmppath != NULL && fd >= 0) {
- int serrno = errno;
- (void)unlinkat(d, tmppath, 0);
- errno = serrno;
- }
- err(1, "%s/%s", dir, tmppath ? tmppath : path);
- }
- /* rename temp file if applicable */
- if (tmppath != NULL) {
- if (ret == 0 && renameat(d, tmppath, d, path) != 0) {
- warn("%s/%s", dir, path);
- ret = -1;
- }
- if (ret != 0)
- (void)unlinkat(d, tmppath, 0);
- free(tmppath);
- tmppath = NULL;
- }
- /* emit metalog */
- if (mlf != NULL) {
- fprintf(mlf, "%s/%s type=file "
- "uname=%s gname=%s mode=%#o size=%ld\n",
- unexpand_path(dir), path,
- uname, gname, mode, ftell(f));
- }
- fclose(f);
- /* advance certificate listing */
- tmp = RB_NEXT(file_tree, &files, file);
- RB_REMOVE(file_tree, &files, file);
- free(file);
- file = tmp;
- }
- free(dents);
- close(d);
- return (ret);
-}
-
-/*
- * Save all certs in a tree to a single file (bundle).
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-write_bundle(const char *dir, const char *file, struct cert_tree *tree)
-{
- struct cert *cert;
- char *tmpfile = NULL;
- FILE *f;
- int d, fd, ret = 0;
- mode_t mode = 0444;
-
- if (dir != NULL) {
- if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0)
- err(1, "%s", dir);
- } else {
- dir = ".";
- d = AT_FDCWD;
- }
- info("writing %s/%s", dir, file);
- if (dryrun) {
- fd = open(_PATH_DEVNULL, O_WRONLY);
- } else {
- tmpfile = xasprintf(".%s", file);
- fd = openat(d, tmpfile, O_WRONLY | O_CREAT | O_EXCL, mode);
- }
- if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
- if (tmpfile != NULL && fd >= 0) {
- int serrno = errno;
- (void)unlinkat(d, tmpfile, 0);
- errno = serrno;
- }
- err(1, "%s/%s", dir, tmpfile ? tmpfile : file);
- }
- RB_FOREACH(cert, cert_tree, tree) {
- if (!PEM_write_X509(f, cert->x509)) {
- warn("%s/%s", dir, tmpfile ? tmpfile : file);
- ret = -1;
- break;
- }
- }
- if (tmpfile != NULL) {
- if (ret == 0 && renameat(d, tmpfile, d, file) != 0) {
- warn("%s/%s", dir, file);
- ret = -1;
- }
- if (ret != 0)
- (void)unlinkat(d, tmpfile, 0);
- free(tmpfile);
- }
- if (ret == 0 && mlf != NULL) {
- fprintf(mlf,
- "%s/%s type=file uname=%s gname=%s mode=%#o size=%ld\n",
- unexpand_path(dir), file, uname, gname, mode, ftell(f));
- }
- fclose(f);
- if (d != AT_FDCWD)
- close(d);
- return (ret);
-}
-
-/*
- * Load trusted certificates.
- *
- * Returns the number of certificates loaded.
- */
-static unsigned int
-load_trusted(bool all, struct cert_tree *exclude)
-{
- 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);
- 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);
-}
-
-/*
- * Load untrusted certificates.
- *
- * Returns the number of certificates loaded.
- */
-static unsigned int
-load_untrusted(bool all)
-{
- char *path;
- unsigned int i, n;
- int ret;
-
- /* load external untrusted certs */
- for (i = n = 0; all && 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);
- if (ret > 0) {
- warnx("certificates found in legacy directory %s",
- path);
- n += ret;
- } else if (ret == 0) {
- warnx("legacy directory %s can safely be deleted",
- path);
- }
- free(path);
-
- info("%d untrusted certificates found", n);
- return (n);
-}
-
-/*
- * Save trusted certificates.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-save_trusted(void)
-{
- int ret;
-
- /* save untrusted certs */
- ret = write_certs(trusted_dest, &trusted);
- return (ret);
-}
-
-/*
- * Save untrusted certificates.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-save_untrusted(void)
-{
- int ret;
-
- ret = write_certs(untrusted_dest, &untrusted);
- return (ret);
-}
-
-/*
- * Save certificate bundle.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-save_bundle(void)
-{
- char *dir, *file, *sep;
- int ret;
-
- if ((sep = strrchr(bundle_dest, '/')) == NULL) {
- dir = NULL;
- file = bundle_dest;
- } else {
- dir = xasprintf("%.*s", (int)(sep - bundle_dest), bundle_dest);
- file = sep + 1;
- }
- ret = write_bundle(dir, file, &trusted);
- free(dir);
- return (ret);
-}
-
-/*
- * Save everything.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-save_all(void)
-{
- int ret = 0;
-
- ret |= save_untrusted();
- ret |= save_trusted();
- if (!nobundle)
- ret |= save_bundle();
- return (ret);
-}
-
-/*
- * List the contents of a certificate tree.
- */
-static void
-list_certs(struct cert_tree *tree)
-{
- struct cert *cert;
- char *path, *name;
-
- RB_FOREACH(cert, cert_tree, tree) {
- path = longnames ? NULL : strrchr(cert->path, '/');
- name = longnames ? NULL : strrchr(cert->name, '=');
- printf("%s\t%s\n", path ? path + 1 : cert->path,
- name ? name + 1 : cert->name);
- }
-}
-
-/*
- * Load installed trusted certificates, then list them.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-certctl_list(int argc, char **argv __unused)
-{
- if (argc > 1)
- usage();
- /* load trusted certificates */
- load_trusted(false, NULL);
- /* list them */
- list_certs(&trusted);
- free_certs(&trusted);
- return (0);
-}
-
-/*
- * Load installed untrusted certificates, then list them.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-certctl_untrusted(int argc, char **argv __unused)
-{
- if (argc > 1)
- usage();
- /* load untrusted certificates */
- load_untrusted(false);
- /* list them */
- list_certs(&untrusted);
- free_certs(&untrusted);
- return (0);
-}
-
-/*
- * Load trusted and untrusted certificates from all sources, then
- * regenerate both the hashed directories and the bundle.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-certctl_rehash(int argc, char **argv __unused)
-{
- int ret;
-
- if (argc > 1)
- usage();
-
- if (unprivileged && (mlf = fopen(metalog, "a")) == NULL) {
- warn("%s", metalog);
- return (-1);
- }
-
- /* load untrusted certs first */
- load_untrusted(true);
-
- /* load trusted certs, excluding any that are already untrusted */
- load_trusted(true, &untrusted);
-
- /* save everything */
- ret = save_all();
-
- /* clean up */
- free_certs(&untrusted);
- free_certs(&trusted);
- if (mlf != NULL)
- fclose(mlf);
- return (ret);
-}
-
-/*
- * Manually add one or more certificates to the list of trusted certificates.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-certctl_trust(int argc, char **argv)
-{
- struct cert_tree extra = RB_INITIALIZER(&extra);
- struct cert *cert, *other, *tmp;
- unsigned int n;
- int i, ret;
-
- if (argc < 2)
- usage();
-
- /* load untrusted certs first */
- load_untrusted(true);
-
- /* load trusted certs, excluding any that are already untrusted */
- load_trusted(true, &untrusted);
-
- /* now load the additional trusted certificates */
- n = 0;
- for (i = 1; i < argc; i++) {
- ret = read_cert(argv[i], &extra, &trusted);
- if (ret > 0)
- n += ret;
- }
- if (n == 0) {
- warnx("no new trusted certificates found");
- free_certs(&untrusted);
- free_certs(&trusted);
- free_certs(&extra);
- return (0);
- }
-
- /*
- * For each new trusted cert, move it from the extra list to the
- * trusted list, then check if a matching certificate exists on
- * the untrusted list. If that is the case, warn the user, then
- * remove the matching certificate from the untrusted list.
- */
- RB_FOREACH_SAFE(cert, cert_tree, &extra, tmp) {
- RB_REMOVE(cert_tree, &extra, cert);
- RB_INSERT(cert_tree, &trusted, cert);
- if ((other = RB_FIND(cert_tree, &untrusted, cert)) != NULL) {
- warnx("%s was previously untrusted", cert->name);
- RB_REMOVE(cert_tree, &untrusted, other);
- free_cert(other);
- }
- }
-
- /* save everything */
- ret = save_all();
-
- /* clean up */
- free_certs(&untrusted);
- free_certs(&trusted);
- return (ret);
-}
-
-/*
- * Manually add one or more certificates to the list of untrusted
- * certificates.
- *
- * Returns 0 on success and -1 on failure.
- */
-static int
-certctl_untrust(int argc, char **argv)
-{
- unsigned int n;
- int i, ret;
-
- if (argc < 2)
- usage();
-
- /* load untrusted certs first */
- load_untrusted(true);
-
- /* now load the additional untrusted certificates */
- n = 0;
- for (i = 1; i < argc; i++) {
- ret = read_cert(argv[i], &untrusted, NULL);
- if (ret > 0)
- n += ret;
- }
- if (n == 0) {
- warnx("no new untrusted certificates found");
- free_certs(&untrusted);
- return (0);
- }
-
- /* load trusted certs, excluding any that are already untrusted */
- load_trusted(true, &untrusted);
-
- /* save everything */
- ret = save_all();
-
- /* clean up */
- free_certs(&untrusted);
- free_certs(&trusted);
- return (ret);
-}
-
-static void
-set_defaults(void)
-{
- const char *value;
- char *str;
- size_t len;
-
- if (localbase == NULL &&
- (localbase = getenv("LOCALBASE")) == NULL) {
- if ((str = malloc((len = PATH_MAX) + 1)) == NULL)
- err(1, NULL);
- while (sysctlbyname("user.localbase", str, &len, NULL, 0) < 0) {
- if (errno != ENOMEM)
- err(1, "sysctl(user.localbase)");
- if ((str = realloc(str, len + 1)) == NULL)
- err(1, NULL);
- }
- str[len] = '\0';
- localbase = str;
- }
-
- if (destdir == NULL &&
- (destdir = getenv("DESTDIR")) == NULL)
- destdir = "";
-
- if (unprivileged && metalog == NULL &&
- (metalog = getenv("METALOG")) == NULL)
- metalog = xasprintf("%s/METALOG", destdir);
-
- if (!verbose) {
- if ((value = getenv("CERTCTL_VERBOSE")) != NULL) {
- if (value[0] != '\0') {
- verbose = true;
- }
- }
- }
-
- if ((value = getenv("TRUSTPATH")) != NULL)
- trusted_paths = split_paths(value);
- else
- trusted_paths = expand_paths(default_trusted_paths);
-
- if ((value = getenv("UNTRUSTPATH")) != NULL)
- untrusted_paths = split_paths(value);
- else
- untrusted_paths = expand_paths(default_untrusted_paths);
-
- if ((value = getenv("TRUSTDESTDIR")) != NULL ||
- (value = getenv("CERTDESTDIR")) != NULL)
- trusted_dest = xstrdup(value);
- else
- trusted_dest = expand_path(TRUSTED_PATH);
-
- if ((value = getenv("UNTRUSTDESTDIR")) != NULL)
- untrusted_dest = xstrdup(value);
- else
- untrusted_dest = expand_path(UNTRUSTED_PATH);
-
- if ((value = getenv("BUNDLE")) != NULL)
- bundle_dest = xstrdup(value);
- else
- bundle_dest = expand_path(BUNDLE_PATH);
-
- info("localbase:\t%s", localbase);
- info("destdir:\t%s", destdir);
- info("unprivileged:\t%s", unprivileged ? "true" : "false");
- info("verbose:\t%s", verbose ? "true" : "false");
-}
-
-typedef int (*main_t)(int, char **);
-
-static struct {
- const char *name;
- main_t func;
-} commands[] = {
- { "list", certctl_list },
- { "untrusted", certctl_untrusted },
- { "rehash", certctl_rehash },
- { "untrust", certctl_untrust },
- { "trust", certctl_trust },
- { 0 },
-};
-
-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");
- exit(1);
-}
-
-int
-main(int argc, char *argv[])
-{
- const char *command;
- int opt;
-
- while ((opt = getopt(argc, argv, "BcD:g:lL:M:no:Uv")) != -1)
- switch (opt) {
- case 'B':
- nobundle = true;
- break;
- case 'c':
- /* ignored for compatibility */
- break;
- case 'D':
- destdir = optarg;
- break;
- case 'g':
- gname = optarg;
- break;
- case 'l':
- longnames = true;
- break;
- case 'L':
- localbase = optarg;
- break;
- case 'M':
- metalog = optarg;
- break;
- case 'n':
- dryrun = true;
- break;
- case 'o':
- uname = optarg;
- break;
- case 'U':
- unprivileged = true;
- break;
- case 'v':
- verbose = true;
- break;
- default:
- usage();
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1)
- usage();
-
- command = *argv;
-
- if ((nobundle || unprivileged || metalog != NULL) &&
- strcmp(command, "rehash") != 0)
- usage();
- if (!unprivileged && metalog != NULL) {
- warnx("-M may only be used in conjunction with -U");
- usage();
- }
-
- set_defaults();
-
- for (unsigned i = 0; commands[i].name != NULL; i++)
- if (strcmp(command, commands[i].name) == 0)
- exit(!!commands[i].func(argc, argv));
- usage();
-}