aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/autofs/common.c1
-rw-r--r--usr.sbin/certctl/Makefile11
-rw-r--r--usr.sbin/certctl/certctl.894
-rw-r--r--usr.sbin/certctl/certctl.c1065
-rwxr-xr-xusr.sbin/certctl/certctl.sh366
-rw-r--r--usr.sbin/certctl/tests/Makefile5
-rw-r--r--usr.sbin/certctl/tests/certctl.subr44
-rw-r--r--usr.sbin/certctl/tests/certctl_test.sh221
-rw-r--r--usr.sbin/chroot/chroot.c38
-rw-r--r--usr.sbin/freebsd-update/freebsd-update.sh2
-rw-r--r--usr.sbin/jail/config.c2
-rwxr-xr-xusr.sbin/jail/tests/jail_basic_test.sh15
-rw-r--r--usr.sbin/lpr/lpc/lpc.c2
-rw-r--r--usr.sbin/makefs/zfs.c2
-rw-r--r--usr.sbin/makefs/zfs/vdev.c2
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile32
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile.depend18
-rw-r--r--usr.sbin/rpc.ypupdated/update.c328
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbdelete.c68
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbupdate.c147
-rwxr-xr-xusr.sbin/rpc.ypupdated/ypupdate32
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_extern.h32
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_main.c287
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_server.c227
-rw-r--r--usr.sbin/syslogd/syslogd.82
-rw-r--r--usr.sbin/watchdogd/watchdogd.c112
27 files changed, 2652 insertions, 504 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 90c23dc26cc5..dcfe2037f8ce 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -174,6 +174,7 @@ SUBDIR.${MK_NETGRAPH}+= flowctl
SUBDIR.${MK_NETGRAPH}+= ngctl
SUBDIR.${MK_NETGRAPH}+= nghook
SUBDIR.${MK_NIS}+= rpc.yppasswdd
+SUBDIR.${MK_NIS}+= rpc.ypupdated
SUBDIR.${MK_NIS}+= rpc.ypxfrd
SUBDIR.${MK_NIS}+= ypbind
SUBDIR.${MK_NIS}+= ypldap
diff --git a/usr.sbin/autofs/common.c b/usr.sbin/autofs/common.c
index 6b98214162ae..2dd7c290cebc 100644
--- a/usr.sbin/autofs/common.c
+++ b/usr.sbin/autofs/common.c
@@ -153,6 +153,7 @@ create_directory(const char *path)
}
}
+ free(partial);
free(tofree);
}
diff --git a/usr.sbin/certctl/Makefile b/usr.sbin/certctl/Makefile
index 88c024daf7e6..6900f0ce3b65 100644
--- a/usr.sbin/certctl/Makefile
+++ b/usr.sbin/certctl/Makefile
@@ -1,5 +1,14 @@
+.include <src.opts.mk>
+
PACKAGE= certctl
-SCRIPTS=certctl.sh
+PROG= certctl
MAN= certctl.8
+LIBADD= crypto
+HAS_TESTS=
+SUBDIR.${MK_TESTS}= tests
+
+.ifdef BOOTSTRAPPING
+CFLAGS+=-DBOOTSTRAPPING
+.endif
.include <bsd.prog.mk>
diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8
index 7e49bb89e2ac..97bdc840c359 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 July 17, 2025
+.Dd August 11, 2025
.Dt CERTCTL 8
.Os
.Sh NAME
@@ -32,63 +32,83 @@
.Nd "tool for managing trusted and untrusted TLS certificates"
.Sh SYNOPSIS
.Nm
-.Op Fl v
+.Op Fl lv
.Ic list
.Nm
-.Op Fl v
+.Op Fl lv
.Ic untrusted
.Nm
-.Op Fl cnUv
+.Op Fl BnUv
.Op Fl D Ar destdir
.Op Fl M Ar metalog
.Ic rehash
.Nm
-.Op Fl cnv
-.Ic untrust Ar file
+.Op Fl nv
+.Ic untrust Ar
.Nm
-.Op Fl cnv
-.Ic trust Ar file
+.Op Fl nv
+.Ic trust Ar
.Sh DESCRIPTION
The
.Nm
utility manages the list of TLS Certificate Authorities that are trusted by
applications that use OpenSSL.
.Pp
-Flags:
+The following options are available:
.Bl -tag -width 4n
-.It Fl c
-Copy certificates instead of linking to them.
+.It Fl B
+Do not generate a bundle.
+This option is only valid in conjunction with the
+.Ic rehash
+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.
.It Fl M Ar metalog
-Specify the path of the METALOG file (default: $DESTDIR/METALOG).
+Specify the path of the METALOG file
+.Po
+default:
+.Pa ${DESTDIR}/METALOG
+.Pc .
+This option is only valid in conjunction with the
+.Ic rehash
+command.
.It Fl n
-No-Op mode, do not actually perform any actions.
+Dry-run mode.
+Do not actually perform any actions except write the metalog.
.It Fl v
-Be verbose, print details about actions before performing them.
+Verbose mode.
+Print detailed information about each action taken.
.It Fl U
-Unprivileged mode, do not change the ownership of created links.
-Do record the ownership in the METALOG file.
+Unprivileged mode.
+Do not attempt to set the ownership of created files.
+This option is only valid in conjunction with the
+.Fl M
+option and the
+.Ic rehash
+command.
.El
.Pp
Primary command functions:
.Bl -tag -width untrusted
.It Ic list
-List all currently trusted certificate authorities.
+List all currently trusted certificates.
.It Ic untrusted
List all currently untrusted certificates.
.It Ic rehash
-Rebuild the list of trusted certificate authorities by scanning all directories
+Rebuild the list of trusted certificates by scanning all directories
in
.Ev TRUSTPATH
and all untrusted certificates in
.Ev UNTRUSTPATH .
-A symbolic link to each trusted certificate is placed in
+A copy of each trusted certificate is placed in
.Ev CERTDESTDIR
and each untrusted certificate in
.Ev UNTRUSTDESTDIR .
+In addition, a bundle containing the trusted certificates is placed in
+.Ev BUNDLEFILE .
.It Ic untrust
Add the specified file to the untrusted list.
.It Ic trust
@@ -98,8 +118,6 @@ Remove the specified file from the untrusted list.
.Bl -tag -width UNTRUSTDESTDIR
.It Ev DESTDIR
Alternate destination directory to operate on.
-.It Ev DISTBASE
-Additional path component to include when operating on certificate directories.
.It Ev LOCALBASE
Location for local programs.
Defaults to the value of the user.localbase sysctl which is usually
@@ -107,32 +125,34 @@ Defaults to the value of the user.localbase sysctl which is usually
.It Ev TRUSTPATH
List of paths to search for trusted certificates.
Default:
-.Pa <DESTDIR><DISTBASE>/usr/share/certs/trusted
-.Pa <DESTDIR><DISTBASE>/usr/local/share/certs
-.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/certs
+.Pa ${DESTDIR}/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><DISTBASE>/usr/share/certs/untrusted
-.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/untrusted
-.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/blacklisted
-.It Ev CERTDESTDIR
+.Pa ${DESTDIR}/usr/share/certs/untrusted
+.Pa ${DESTDIR}${LOCALBASE}/share/certs/untrusted
+.It Ev TRUSTDESTDIR
Destination directory for symbolic links to trusted certificates.
Default:
-.Pa <DESTDIR><DISTBASE>/etc/ssl/certs
+.Pa ${DESTDIR}/etc/ssl/certs
.It Ev UNTRUSTDESTDIR
Destination directory for symbolic links to untrusted certificates.
Default:
-.Pa <DESTDIR><DISTBASE>/etc/ssl/untrusted
-.It Ev EXTENSIONS
-List of file extensions to read as certificate files.
-Default: *.pem *.crt *.cer *.crl *.0
+.Pa ${DESTDIR}/etc/ssl/untrusted
+.It Ev BUNDLE
+File name of bundle to produce.
.El
.Sh SEE ALSO
.Xr openssl 1
.Sh HISTORY
.Nm
first appeared in
-.Fx 12.2
+.Fx 12.2 .
.Sh AUTHORS
-.An Allan Jude Aq Mt allanjude@freebsd.org
+.An -nosplit
+The original shell implementation was written by
+.An Allan Jude Aq Mt allanjude@FreeBSD.org .
+The current C implementation was written by
+.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org .
diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c
new file mode 100644
index 000000000000..365870167aeb
--- /dev/null
+++ b/usr.sbin/certctl/certctl.c
@@ -0,0 +1,1065 @@
+/*-
+ * 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();
+}
diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh
deleted file mode 100755
index 2bde651de126..000000000000
--- a/usr.sbin/certctl/certctl.sh
+++ /dev/null
@@ -1,366 +0,0 @@
-#!/bin/sh
-#-
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright 2018 Allan Jude <allanjude@freebsd.org>
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted providing 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 AUTHOR ``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 AUTHOR 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 -u
-
-############################################################ CONFIGURATION
-
-: ${DESTDIR:=}
-: ${DISTBASE:=}
-
-############################################################ GLOBALS
-
-SCRIPTNAME="${0##*/}"
-LINK=-lrs
-ERRORS=0
-NOOP=false
-UNPRIV=false
-VERBOSE=false
-
-############################################################ FUNCTIONS
-
-info()
-{
- echo "${0##*/}: $@" >&2
-}
-
-verbose()
-{
- if "${VERBOSE}" ; then
- info "$@"
- fi
-}
-
-perform()
-{
- if ! "${NOOP}" ; then
- "$@"
- fi
-}
-
-cert_files_in()
-{
- find -L "$@" -type f \( \
- -name '*.pem' -or \
- -name '*.crt' -or \
- -name '*.cer' \
- \) 2>/dev/null
-}
-
-eolcvt()
-{
- cat "$@" | tr -s '\r' '\n'
-}
-
-do_hash()
-{
- local hash
-
- if hash=$(openssl x509 -noout -subject_hash -in "$1") ; then
- echo "$hash"
- return 0
- else
- info "Error: $1"
- ERRORS=$((ERRORS + 1))
- return 1
- fi
-}
-
-get_decimal()
-{
- local checkdir hash decimal
-
- checkdir=$1
- hash=$2
- decimal=0
-
- while [ -e "$checkdir/$hash.$decimal" ] ; do
- decimal=$((decimal + 1))
- done
-
- echo ${decimal}
- return 0
-}
-
-create_trusted()
-{
- local hash certhash otherfile otherhash
- local suffix
-
- hash=$(do_hash "$1") || return
- certhash=$(openssl x509 -sha1 -in "$1" -noout -fingerprint)
- for otherfile in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do
- otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint)
- if [ "$certhash" = "$otherhash" ] ; then
- info "Skipping untrusted certificate $hash ($otherfile)"
- return 0
- fi
- done
- for otherfile in $(find $CERTDESTDIR -name "$hash.*") ; do
- otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint)
- if [ "$certhash" = "$otherhash" ] ; then
- verbose "Skipping duplicate entry for certificate $hash"
- return 0
- fi
- done
- suffix=$(get_decimal "$CERTDESTDIR" "$hash")
- verbose "Adding $hash.$suffix to trust store"
- perform install ${INSTALLFLAGS} -m 0444 ${LINK} \
- "$(realpath "$1")" "$CERTDESTDIR/$hash.$suffix"
-}
-
-# Accepts either dot-hash form from `certctl list` or a path to a valid cert.
-resolve_certname()
-{
- local hash srcfile filename
- local suffix
-
- # If it exists as a file, we'll try that; otherwise, we'll scan
- if [ -e "$1" ] ; then
- hash=$(do_hash "$1") || return
- srcfile=$(realpath "$1")
- suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash")
- filename="$hash.$suffix"
- echo "$srcfile" "$hash.$suffix"
- elif [ -e "${CERTDESTDIR}/$1" ] ; then
- srcfile=$(realpath "${CERTDESTDIR}/$1")
- hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//')
- suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash")
- filename="$hash.$suffix"
- echo "$srcfile" "$hash.$suffix"
- fi
-}
-
-create_untrusted()
-{
- local srcfile filename
-
- set -- $(resolve_certname "$1")
- srcfile=$1
- filename=$2
-
- if [ -z "$srcfile" -o -z "$filename" ] ; then
- return
- fi
-
- verbose "Adding $filename to untrusted list"
- perform install ${INSTALLFLAGS} -m 0444 ${LINK} \
- "$srcfile" "$UNTRUSTDESTDIR/$filename"
-}
-
-do_scan()
-{
- local CFUNC CSEARCH CPATH CFILE CERT SPLITDIR
- local oldIFS="$IFS"
- CFUNC="$1"
- CSEARCH="$2"
-
- IFS=:
- set -- $CSEARCH
- IFS="$oldIFS"
- for CFILE in $(cert_files_in "$@") ; do
- verbose "Reading $CFILE"
- case $(eolcvt "$CFILE" | egrep -c '^-+BEGIN CERTIFICATE-+$') in
- 0)
- ;;
- 1)
- "$CFUNC" "$CFILE"
- ;;
- *)
- verbose "Multiple certificates found, splitting..."
- SPLITDIR=$(mktemp -d)
- eolcvt "$CFILE" | egrep '^(---|[0-9A-Za-z/+=]+$)' | \
- split -p '^-+BEGIN CERTIFICATE-+$' - "$SPLITDIR/x"
- for CERT in $(find "$SPLITDIR" -type f) ; do
- "$CFUNC" "$CERT"
- done
- rm -rf "$SPLITDIR"
- ;;
- esac
- done
-}
-
-do_list()
-{
- local CFILE subject
-
- for CFILE in $(find "$@" \( -type f -or -type l \) -name '*.[0-9]') ; do
- if [ ! -s "$CFILE" ] ; then
- info "Unable to read $CFILE"
- ERRORS=$((ERRORS + 1))
- continue
- fi
- subject=
- if ! "$VERBOSE" ; then
- subject=$(openssl x509 -noout -subject -nameopt multiline -in "$CFILE" | sed -n '/commonName/s/.*= //p')
- fi
- if [ -z "$subject" ] ; then
- subject=$(openssl x509 -noout -subject -in "$CFILE")
- fi
- printf "%s\t%s\n" "${CFILE##*/}" "$subject"
- done
-}
-
-cmd_rehash()
-{
-
- if [ -e "$CERTDESTDIR" ] ; then
- perform find "$CERTDESTDIR" \( -type f -or -type l \) -delete
- else
- perform install -d -m 0755 "$CERTDESTDIR"
- fi
- if [ -e "$UNTRUSTDESTDIR" ] ; then
- perform find "$UNTRUSTDESTDIR" \( -type f -or -type l \) -delete
- else
- perform install -d -m 0755 "$UNTRUSTDESTDIR"
- fi
-
- do_scan create_untrusted "$UNTRUSTPATH"
- do_scan create_trusted "$TRUSTPATH"
-}
-
-cmd_list()
-{
- info "Listing Trusted Certificates:"
- do_list "$CERTDESTDIR"
-}
-
-cmd_untrust()
-{
- local UTFILE
-
- shift # verb
- perform install -d -m 0755 "$UNTRUSTDESTDIR"
- for UTFILE in "$@"; do
- info "Adding $UTFILE to untrusted list"
- create_untrusted "$UTFILE"
- done
-}
-
-cmd_trust()
-{
- local UTFILE untrustedhash certhash hash
-
- shift # verb
- for UTFILE in "$@"; do
- if [ -s "$UTFILE" ] ; then
- hash=$(do_hash "$UTFILE")
- certhash=$(openssl x509 -sha1 -in "$UTFILE" -noout -fingerprint)
- for UNTRUSTEDFILE in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do
- untrustedhash=$(openssl x509 -sha1 -in "$UNTRUSTEDFILE" -noout -fingerprint)
- if [ "$certhash" = "$untrustedhash" ] ; then
- info "Removing $(basename "$UNTRUSTEDFILE") from untrusted list"
- perform rm -f $UNTRUSTEDFILE
- fi
- done
- elif [ -e "$UNTRUSTDESTDIR/$UTFILE" ] ; then
- info "Removing $UTFILE from untrusted list"
- perform rm -f "$UNTRUSTDESTDIR/$UTFILE"
- else
- info "Cannot find $UTFILE"
- ERRORS=$((ERRORS + 1))
- fi
- done
-}
-
-cmd_untrusted()
-{
- info "Listing Untrusted Certificates:"
- do_list "$UNTRUSTDESTDIR"
-}
-
-usage()
-{
- exec >&2
- echo "Manage the TLS trusted certificates on the system"
- echo " $SCRIPTNAME [-v] list"
- echo " List trusted certificates"
- echo " $SCRIPTNAME [-v] untrusted"
- echo " List untrusted certificates"
- echo " $SCRIPTNAME [-cnUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash"
- echo " Rehash all trusted and untrusted certificates"
- echo " $SCRIPTNAME [-cnv] untrust <file>"
- echo " Add <file> to the list of untrusted certificates"
- echo " $SCRIPTNAME [-cnv] trust <file>"
- echo " Remove <file> from the list of untrusted certificates"
- exit 64
-}
-
-############################################################ MAIN
-
-while getopts cD:d:M:nUv flag; do
- case "$flag" in
- c) LINK=-c ;;
- D) DESTDIR=${OPTARG} ;;
- d) DISTBASE=${OPTARG} ;;
- M) METALOG=${OPTARG} ;;
- n) NOOP=true ;;
- U) UNPRIV=true ;;
- v) VERBOSE=true ;;
- esac
-done
-shift $((OPTIND - 1))
-
-DESTDIR=${DESTDIR%/}
-
-if ! [ -z "${CERTCTL_VERBOSE:-}" ] ; then
- VERBOSE=true
-fi
-: ${METALOG:=${DESTDIR}/METALOG}
-INSTALLFLAGS=
-if "$UNPRIV" ; then
- INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR:-/} -o root -g wheel"
-fi
-: ${LOCALBASE:=$(sysctl -n user.localbase)}
-: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs}
-: ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted}
-: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs}
-: ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted}
-
-[ $# -gt 0 ] || usage
-case "$1" in
-list) cmd_list ;;
-rehash) cmd_rehash ;;
-blacklist) cmd_untrust "$@" ;;
-untrust) cmd_untrust "$@" ;;
-trust) cmd_trust "$@" ;;
-unblacklist) cmd_trust "$@" ;;
-untrusted) cmd_untrusted ;;
-blacklisted) cmd_untrusted ;;
-*) usage # NOTREACHED
-esac
-
-retval=$?
-if [ $ERRORS -gt 0 ] ; then
- info "Encountered $ERRORS errors"
-fi
-exit $retval
-
-################################################################################
-# END
-################################################################################
diff --git a/usr.sbin/certctl/tests/Makefile b/usr.sbin/certctl/tests/Makefile
new file mode 100644
index 000000000000..da301c3ded03
--- /dev/null
+++ b/usr.sbin/certctl/tests/Makefile
@@ -0,0 +1,5 @@
+PACKAGE= tests
+ATF_TESTS_SH= certctl_test
+${PACKAGE}FILES+= certctl.subr
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/certctl/tests/certctl.subr b/usr.sbin/certctl/tests/certctl.subr
new file mode 100644
index 000000000000..841cc1781e69
--- /dev/null
+++ b/usr.sbin/certctl/tests/certctl.subr
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# Generate a random name
+rand_name() {
+ local length=${1:-32}
+
+ jot -r -c -s '' ${length} A Z
+}
+
+# Generate a subject for a given name
+subject() {
+ local crtname=$1
+
+ echo "/CN=${crtname}/O=FreeBSD/OU=Test/"
+}
+
+# Generate a key
+gen_key() {
+ local keyname=$1
+
+ env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \
+ openssl genrsa -out ${keyname}.key
+}
+
+# Generate a certificate for a given name, key, and serial number
+gen_crt() {
+ local crtname=$1
+ local keyname=${2:-${crtname}}
+ local serial=${3:-1}
+
+ if ! [ -f "${keyname}".key ]; then
+ gen_key "${keyname}"
+ fi
+ env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \
+ openssl req -x509 -new \
+ -subj="$(subject ${crtname})" \
+ -set_serial ${serial} \
+ -key ${keyname}.key \
+ -out ${crtname}.crt
+}
diff --git a/usr.sbin/certctl/tests/certctl_test.sh b/usr.sbin/certctl/tests/certctl_test.sh
new file mode 100644
index 000000000000..4e236d5bfae8
--- /dev/null
+++ b/usr.sbin/certctl/tests/certctl_test.sh
@@ -0,0 +1,221 @@
+#
+# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. $(atf_get_srcdir)/certctl.subr
+
+# Random sets of eight non-colliding names
+set1()
+{
+ cat <<EOF
+AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB 0ca83bbe
+UYSYXKDNNJTYOQPBGIKQDHRJYZHTDPKK 0d9a6512
+LODHGFXMZYKGOKAYGWTMMYQJYHDATDDM 4e6219f5
+NBBTQHJLHKBFFFWJTHHSNKOQYMGLHLPW 5dd76abc
+BJFAQZXZHYQLIDDPCAQFPDMNXICUXBXW ad68573d
+IOKNTHVEVVIJMNMYAVILMEMQQWLVRESN b577803d
+BHGMAJJGNJPIVMHMFCUTJLGFROJICEKN c98a6338
+HCRFQMGDQJALMLUQNXMPGLXFLLJRODJW f50c6379
+EOF
+}
+
+set2()
+{
+ cat <<EOF
+GOHKZTSKIPDSYNLMGYXGLROPTATELXIU 30789c88
+YOOTYHEGHZIYFXOBLNKENPSJUDGOPJJU 7fadbc13
+ETRINNYBGKIENAVGOKVJYFSSHFZIJZRH 8ed664af
+DBFGMFFMRNLPQLQPOLXOEUVLCRXLRSWT 8f34355e
+WFOPBQPLQFHDHZOUQFEIDGSYDUOTSNDQ ac0471df
+HMNETZMGNIWRGXQCVZXVZGWSGFBRRDQC b32f1472
+SHFYBXDVAUACBFPPAIGDAQIAGYOYGMQE baca75fa
+PCBGDNVPYCDGNRQSGRSLXFHYKXLAVLHW ddeeae01
+EOF
+}
+
+set3()
+{
+ cat <<EOF
+NJWIRLPWAIICVJBKXXHFHLCPAERZATRL 000aa2e5
+RJAENDPOCZQEVCPFUWOWDXPCSMYJPVYC 021b95a3
+PQUQDSWHBNVLBTNBGONYRLGZZVEFXVLO 071e8c50
+VZEXRKJUPZSFBDWBOLUZXOGLNTEAPCZM 3af7bb9b
+ZXOWOXQTXNZMAMZIWVFDZDJEWOOAGAOH 48d5c7cc
+KQSFQYVJMFTMADIHJIWGSQISWKSHRYQO 509f5ba1
+AIECYSLWZOIEPJWWUTWSQXCNCIHHZHYI 8cb0c503
+RFHWDJZEPOFLMPGXAHVEJFHCDODAPVEV 9ae4e049
+EOF
+}
+
+# Random set of three colliding names
+collhash=f2888ce3
+coll()
+{
+ cat <<EOF
+EJFTZEOANQLOYPEHWWXBWEWEFVKHMSNA $collhash
+LEMRWZAZLKZLPPSFLNLQZVGKKBEOFYWG $collhash
+ZWUPHYWKKTVEFBJOLLPDAIKGRDFVXZID $collhash
+EOF
+}
+
+certctl_setup()
+{
+ export DESTDIR="$PWD"
+
+ # Create input directories
+ mkdir -p usr/share/certs/trusted
+ mkdir -p usr/share/certs/untrusted
+ mkdir -p usr/local/share/certs
+
+ # Create output directories
+ mkdir -p etc/ssl/certs
+ mkdir -p etc/ssl/untrusted
+
+ # Generate a random key
+ keyname="testkey"
+ gen_key ${keyname}
+
+ # Generate certificates
+ set1 | while read crtname hash ; do
+ gen_crt ${crtname} ${keyname}
+ mv ${crtname}.crt usr/share/certs/trusted
+ done
+ coll | while read crtname hash ; do
+ gen_crt ${crtname} ${keyname}
+ mv ${crtname}.crt usr/share/certs/trusted
+ done
+ set2 | while read crtname hash ; do
+ gen_crt ${crtname} ${keyname}
+ openssl x509 -in ${crtname}.crt
+ rm ${crtname}.crt
+ done >usr/local/share/certs/bundle.crt
+ set3 | while read crtname hash ; do
+ gen_crt ${crtname} ${keyname}
+ mv ${crtname}.crt usr/share/certs/untrusted
+ done
+}
+
+check_trusted() {
+ local crtname=$1
+ local subject="$(subject ${crtname})"
+ local c=${2:-1}
+
+ atf_check -o match:"found: ${c}\$" \
+ openssl storeutl -noout -subject "${subject}" \
+ etc/ssl/certs
+ atf_check -o match:"found: 0\$" \
+ openssl storeutl -noout -subject "${subject}" \
+ etc/ssl/untrusted
+}
+
+check_untrusted() {
+ local crtname=$1
+ local subject="$(subject ${crtname})"
+ local c=${2:-1}
+
+ atf_check -o match:"found: 0\$" \
+ openssl storeutl -noout -subject "${subject}" \
+ etc/ssl/certs
+ atf_check -o match:"found: ${c}\$" \
+ openssl storeutl -noout -subject "${subject}" \
+ etc/ssl/untrusted
+}
+
+check_in_bundle() {
+ local crtfile=$1
+ local line
+
+ line=$(tail +5 "${crtfile}" | head -1)
+ atf_check grep -q "${line}" etc/ssl/cert.pem
+}
+
+check_not_in_bundle() {
+ local crtfile=$1
+ local line
+
+ line=$(tail +5 "${crtfile}" | head -1)
+ atf_check -s exit:1 grep -q "${line}" etc/ssl/cert.pem
+}
+
+atf_test_case rehash
+rehash_head()
+{
+ atf_set "descr" "Test the rehash command"
+}
+rehash_body()
+{
+ certctl_setup
+ atf_check certctl rehash
+
+ # Verify non-colliding trusted certificates
+ (set1 ; set2) > trusted
+ while read crtname hash ; do
+ check_trusted "${crtname}"
+ done <trusted
+
+ # Verify colliding trusted certificates
+ coll >coll
+ while read crtname hash ; do
+ check_trusted "${crtname}" $(wc -l <coll)
+ done <coll
+
+ # Verify untrusted certificates
+ set3 >untrusted
+ while read crtname hash ; do
+ check_untrusted "${crtname}"
+ done <untrusted
+
+ # Verify bundle; storeutl is no help here
+ for f in etc/ssl/certs/*.? ; do
+ check_in_bundle "${f}"
+ done
+ for f in etc/ssl/untrusted/*.? ; do
+ check_not_in_bundle "${f}"
+ done
+}
+
+atf_test_case trust
+trust_head()
+{
+ atf_set "descr" "Test the trust command"
+}
+trust_body()
+{
+ certctl_setup
+ atf_check certctl rehash
+ crtname=NJWIRLPWAIICVJBKXXHFHLCPAERZATRL
+ crtfile=usr/share/certs/untrusted/${crtname}.crt
+ check_untrusted ${crtname}
+ check_not_in_bundle ${crtfile}
+ atf_check -e match:"was previously untrusted" \
+ certctl trust ${crtfile}
+ check_trusted ${crtname}
+ check_in_bundle ${crtfile}
+}
+
+atf_test_case untrust
+untrust_head()
+{
+ atf_set "descr" "Test the untrust command"
+}
+untrust_body()
+{
+ certctl_setup
+ atf_check certctl rehash
+ crtname=AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB
+ crtfile=usr/share/certs/trusted/${crtname}.crt
+ check_trusted "${crtname}"
+ check_in_bundle ${crtfile}
+ atf_check certctl untrust "${crtfile}"
+ check_untrusted "${crtname}"
+ check_not_in_bundle ${crtfile}
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case rehash
+ atf_add_test_case trust
+ atf_add_test_case untrust
+}
diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c
index d9fb29474d87..e1af0a4131d3 100644
--- a/usr.sbin/chroot/chroot.c
+++ b/usr.sbin/chroot/chroot.c
@@ -103,7 +103,9 @@ main(int argc, char *argv[])
gid = 0;
uid = 0;
+ gids = 0;
user = group = grouplist = NULL;
+ gidlist = NULL;
nonprivileged = false;
while ((ch = getopt(argc, argv, "G:g:u:n")) != -1) {
switch(ch) {
@@ -119,6 +121,11 @@ main(int argc, char *argv[])
break;
case 'G':
grouplist = optarg;
+
+ /*
+ * XXX Why not allow us to drop all of our supplementary
+ * groups?
+ */
if (*grouplist == '\0')
usage();
break;
@@ -139,23 +146,20 @@ main(int argc, char *argv[])
if (group != NULL)
gid = resolve_group(group);
- ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
- if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
- err(1, "malloc");
- /* Populate the egid slot in our groups to avoid accidents. */
- if (gid == 0)
- gidlist[0] = getegid();
- else
- gidlist[0] = gid;
- for (gids = 1;
- (p = strsep(&grouplist, ",")) != NULL && gids < ngroups_max; ) {
- if (*p == '\0')
- continue;
-
- gidlist[gids++] = resolve_group(p);
+ if (grouplist != NULL) {
+ ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
+ err(1, "malloc");
+ for (gids = 0; (p = strsep(&grouplist, ",")) != NULL &&
+ gids < ngroups_max; ) {
+ if (*p == '\0')
+ continue;
+
+ gidlist[gids++] = resolve_group(p);
+ }
+ if (p != NULL && gids == ngroups_max)
+ errx(1, "too many supplementary groups provided");
}
- if (p != NULL && gids == ngroups_max)
- errx(1, "too many supplementary groups provided");
if (user != NULL)
uid = resolve_user(user);
@@ -175,7 +179,7 @@ main(int argc, char *argv[])
err(1, "%s", argv[0]);
}
- if (gids && setgroups(gids, gidlist) == -1)
+ if (gidlist != NULL && setgroups(gids, gidlist) == -1)
err(1, "setgroups");
if (group && setgid(gid) == -1)
err(1, "setgid");
diff --git a/usr.sbin/freebsd-update/freebsd-update.sh b/usr.sbin/freebsd-update/freebsd-update.sh
index c388e76644dc..81040431ea79 100644
--- a/usr.sbin/freebsd-update/freebsd-update.sh
+++ b/usr.sbin/freebsd-update/freebsd-update.sh
@@ -3000,7 +3000,7 @@ install_from_index () {
if [ -z "${LINK}" ]; then
# Create a file, without setting flags.
gunzip < files/${HASH}.gz > ${HASH}
- install -S -o ${OWNER} -g ${GROUP} \
+ install -o ${OWNER} -g ${GROUP} \
-m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
rm ${HASH}
else
diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c
index 70de82e662e7..1bad04ccde68 100644
--- a/usr.sbin/jail/config.c
+++ b/usr.sbin/jail/config.c
@@ -189,7 +189,7 @@ load_config(const char *cfname)
* jail is created or found.
*/
if (j->intparams[KP_NAME] == NULL)
- add_param(j, j->intparams[KP_JID], KP_NAME, NULL);
+ add_param(j, NULL, KP_NAME, j->name);
/* Resolve any variable substitutions. */
pgen = 0;
diff --git a/usr.sbin/jail/tests/jail_basic_test.sh b/usr.sbin/jail/tests/jail_basic_test.sh
index 509900e8569c..6802da7b049a 100755
--- a/usr.sbin/jail/tests/jail_basic_test.sh
+++ b/usr.sbin/jail/tests/jail_basic_test.sh
@@ -198,7 +198,7 @@ clean_jails()
fi
while read jail; do
- if jls -e -j "$jail"; then
+ if jls -c -j "$jail"; then
jail -r "$jail"
fi
done < jails.lst
@@ -211,10 +211,23 @@ jid_name_set_body()
echo "basejail" >> jails.lst
echo "$jid { name = basejail; persist; }" > jail.conf
atf_check -o match:"$jid: created" jail -f jail.conf -c "$jid"
+ # Confirm that we didn't override the explicitly-set name with the jid
+ # as the name.
+ atf_check -o match:"basejail" jls -j "$jid" name
+ atf_check -o match:"$jid: removed" jail -f jail.conf -r "$jid"
+
+ echo "$jid { host.hostname = \"\${name}\"; persist; }" > jail.conf
+ atf_check -o match:"$jid: created" jail -f jail.conf -c "$jid"
+ # Confirm that ${name} expanded and expanded correctly to the
+ # jid-implied name.
+ atf_check -o match:"$jid" jls -j "$jid" host.hostname
atf_check -o match:"$jid: removed" jail -f jail.conf -r "$jid"
echo "basejail { jid = $jid; persist; }" > jail.conf
atf_check -o match:"basejail: created" jail -f jail.conf -c basejail
+ # Confirm that our jid assigment in the definition worked out and we
+ # did in-fact create the jail there.
+ atf_check -o match:"$jid" jls -j "basejail" jid
atf_check -o match:"basejail: removed" jail -f jail.conf -r basejail
}
diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c
index a3da852de46e..b4db5bb2e29f 100644
--- a/usr.sbin/lpr/lpc/lpc.c
+++ b/usr.sbin/lpr/lpc/lpc.c
@@ -358,6 +358,8 @@ ingroup(const char *grname)
err(1, "getgroups");
}
gid = gptr->gr_gid;
+ if (gid == getegid())
+ return(1);
for (i = 0; i < ngroups; i++)
if (gid == groups[i])
return(1);
diff --git a/usr.sbin/makefs/zfs.c b/usr.sbin/makefs/zfs.c
index 8d50c450541b..e33a182e5c8f 100644
--- a/usr.sbin/makefs/zfs.c
+++ b/usr.sbin/makefs/zfs.c
@@ -596,7 +596,7 @@ pool_labels_write(zfs_opt_t *zfs)
* checksum is calculated in vdev_label_write().
*/
for (size_t uoff = 0; uoff < sizeof(label->vl_uberblock);
- uoff += (1 << zfs->ashift)) {
+ uoff += ASHIFT_UBERBLOCK_SIZE(zfs->ashift)) {
ub = (uberblock_t *)(&label->vl_uberblock[0] + uoff);
ub->ub_magic = UBERBLOCK_MAGIC;
ub->ub_version = SPA_VERSION;
diff --git a/usr.sbin/makefs/zfs/vdev.c b/usr.sbin/makefs/zfs/vdev.c
index afcce402cb13..a2423e180ca3 100644
--- a/usr.sbin/makefs/zfs/vdev.c
+++ b/usr.sbin/makefs/zfs/vdev.c
@@ -200,7 +200,7 @@ vdev_label_write(zfs_opt_t *zfs, int ind, const vdev_label_t *labelp)
* per sector; for example, with an ashift of 12 we end up with
* 128KB/4KB=32 copies of the uberblock in the ring.
*/
- blksz = 1 << zfs->ashift;
+ blksz = ASHIFT_UBERBLOCK_SIZE(zfs->ashift);
assert(sizeof(label->vl_uberblock) % blksz == 0);
for (size_t roff = 0; roff < sizeof(label->vl_uberblock);
roff += blksz) {
diff --git a/usr.sbin/rpc.ypupdated/Makefile b/usr.sbin/rpc.ypupdated/Makefile
new file mode 100644
index 000000000000..78ee19fc7a6d
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile
@@ -0,0 +1,32 @@
+.PATH: ${SRCTOP}/usr.sbin/ypserv ${SRCTOP}/libexec/ypxfr
+
+PACKAGE=yp
+PROG= rpc.ypupdated
+MAN=
+SRCS= ypupdate_prot_svc.c ypupdate_prot.h ypupdated_main.c \
+ yp_error.c update.c ypupdated_server.c \
+ yp_dblookup.c yp_dbwrite.c yp_dbdelete.c yp_dbupdate.c
+
+#CFLAGS+= -DYP
+CFLAGS+= -I${SRCTOP}/usr.sbin/ypserv -I. -I${SRCTOP}/libexec/ypxfr
+
+WARNS?= 1
+
+LIBADD= rpcsvc
+
+CLEANFILES= ypupdate_prot_svc.c ypupdate_prot.h
+
+RPCDIR= ${SYSROOT:U${DESTDIR}}/usr/include/rpcsvc
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypupdated_main.c can see it.
+ypupdate_prot_svc.c: ${RPCDIR}/ypupdate_prot.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${.ALLSRC} | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypupdate_prot.h: ${RPCDIR}/ypupdate_prot.x
+ ${RPCGEN} -h -o ${.TARGET} ${.ALLSRC}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.ypupdated/Makefile.depend b/usr.sbin/rpc.ypupdated/Makefile.depend
new file mode 100644
index 000000000000..352a225b19c6
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librpcsvc \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/rpc.ypupdated/update.c b/usr.sbin/rpc.ypupdated/update.c
new file mode 100644
index 000000000000..332ed752acc4
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/update.c
@@ -0,0 +1,328 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * Copyright (C) 1986, 1989, Sun Microsystems, Inc.
+ */
+
+/*
+ * Administrative tool to add a new user to the publickey database
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#endif /* YP */
+#include <pwd.h>
+#include <string.h>
+#include <sys/resource.h>
+#include "ypupdated_extern.h"
+
+#ifdef YP
+#define MAXMAPNAMELEN 256
+#else
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+#endif
+
+#ifdef YP
+static char SHELL[] = "/bin/sh";
+static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */
+static char PKMAP[] = "publickey.byname";
+static char UPDATEFILE[] = "updaters";
+static char PKFILE[] = "/etc/publickey";
+#endif /* YP */
+
+#ifdef YP
+static int _openchild(char *, FILE **, FILE **);
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the yp status, which is zero
+ * if there is no access violation.
+ */
+int
+mapupdate(char *requester, char *mapname, u_int op, u_int keylen, char *key,
+ u_int datalen, char *data)
+{
+ char updater[MAXMAPNAMELEN + 40];
+ FILE *childargs;
+ FILE *childrslt;
+#ifdef WEXITSTATUS
+ int status;
+#else
+ union wait status;
+#endif
+ pid_t pid;
+ u_int yperrno;
+
+
+#ifdef DEBUG
+ printf("%s %s\n", key, data);
+#endif
+ (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */
+ UPDATEFILE, mapname);
+ pid = _openchild(updater, &childargs, &childrslt);
+ if (pid < 0) {
+ return (YPERR_YPERR);
+ }
+
+ /*
+ * Write to child
+ */
+ (void)fprintf(childargs, "%s\n", requester);
+ (void)fprintf(childargs, "%u\n", op);
+ (void)fprintf(childargs, "%u\n", keylen);
+ (void)fwrite(key, (int)keylen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fprintf(childargs, "%u\n", datalen);
+ (void)fwrite(data, (int)datalen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fclose(childargs);
+
+ /*
+ * Read from child
+ */
+ (void)fscanf(childrslt, "%d", &yperrno);
+ (void)fclose(childrslt);
+
+ (void)wait(&status);
+#ifdef WEXITSTATUS
+ if (WEXITSTATUS(status) != 0)
+#else
+ if (status.w_retcode != 0)
+#endif
+ return (YPERR_YPERR);
+ return (yperrno);
+}
+
+/*
+ * returns pid, or -1 for failure
+ */
+static int
+_openchild(char *command, FILE **fto, FILE **ffrom)
+{
+ int i;
+ pid_t pid;
+ int pdto[2];
+ int pdfrom[2];
+ char *com;
+ struct rlimit rl;
+
+ if (pipe(pdto) < 0) {
+ goto error1;
+ }
+ if (pipe(pdfrom) < 0) {
+ goto error2;
+ }
+ switch (pid = fork()) {
+ case -1:
+ goto error3;
+
+ case 0:
+ /*
+ * child: read from pdto[0], write into pdfrom[1]
+ */
+ (void)close(0);
+ (void)dup(pdto[0]);
+ (void)close(1);
+ (void)dup(pdfrom[1]);
+ getrlimit(RLIMIT_NOFILE, &rl);
+ for (i = rl.rlim_max - 1; i >= 3; i--) {
+ (void) close(i);
+ }
+ com = malloc((unsigned) strlen(command) + 6);
+ if (com == NULL) {
+ _exit(~0);
+ }
+ (void)sprintf(com, "exec %s", command);
+ execl(SHELL, basename(SHELL), "-c", com, (char *)NULL);
+ _exit(~0);
+
+ default:
+ /*
+ * parent: write into pdto[1], read from pdfrom[0]
+ */
+ *fto = fdopen(pdto[1], "w");
+ (void)close(pdto[0]);
+ *ffrom = fdopen(pdfrom[0], "r");
+ (void)close(pdfrom[1]);
+ break;
+ }
+ return (pid);
+
+ /*
+ * error cleanup and return
+ */
+error3:
+ (void)close(pdfrom[0]);
+ (void)close(pdfrom[1]);
+error2:
+ (void)close(pdto[0]);
+ (void)close(pdto[1]);
+error1:
+ return (-1);
+}
+
+static char *
+basename(char *path)
+{
+ char *p;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ return (path);
+ } else {
+ return (p + 1);
+ }
+}
+
+#else /* YP */
+
+static int match(char *, char *);
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the status, which is zero
+ * if there is no access violation. This function updates
+ * the local file and then shuts up.
+ */
+int
+localupdate(char *name, char *filename, u_int op, u_int keylen __unused,
+ char *key, u_int datalen __unused, char *data)
+{
+ char line[256];
+ FILE *rf;
+ FILE *wf;
+ char *tmpname;
+ int err;
+
+ /*
+ * Check permission
+ */
+ if (strcmp(name, key) != 0) {
+ return (ERR_ACCESS);
+ }
+ if (strcmp(name, "nobody") == 0) {
+ /*
+ * Can't change "nobody"s key.
+ */
+ return (ERR_ACCESS);
+ }
+
+ /*
+ * Open files
+ */
+ tmpname = malloc(strlen(filename) + 4);
+ if (tmpname == NULL) {
+ return (ERR_MALLOC);
+ }
+ sprintf(tmpname, "%s.tmp", filename);
+ rf = fopen(filename, "r");
+ if (rf == NULL) {
+ err = ERR_READ;
+ goto cleanup;
+ }
+ wf = fopen(tmpname, "w");
+ if (wf == NULL) {
+ fclose(rf);
+ err = ERR_WRITE;
+ goto cleanup;
+ }
+ err = -1;
+ while (fgets(line, sizeof (line), rf)) {
+ if (err < 0 && match(line, name)) {
+ switch (op) {
+ case YPOP_INSERT:
+ err = ERR_KEY;
+ break;
+ case YPOP_STORE:
+ case YPOP_CHANGE:
+ fprintf(wf, "%s %s\n", key, data);
+ err = 0;
+ break;
+ case YPOP_DELETE:
+ /* do nothing */
+ err = 0;
+ break;
+ }
+ } else {
+ fputs(line, wf);
+ }
+ }
+ if (err < 0) {
+ switch (op) {
+ case YPOP_CHANGE:
+ case YPOP_DELETE:
+ err = ERR_KEY;
+ break;
+ case YPOP_INSERT:
+ case YPOP_STORE:
+ err = 0;
+ fprintf(wf, "%s %s\n", key, data);
+ break;
+ }
+ }
+ fclose(wf);
+ fclose(rf);
+ if (err == 0) {
+ if (rename(tmpname, filename) < 0) {
+ err = ERR_DBASE;
+ goto cleanup;
+ }
+ } else {
+ if (unlink(tmpname) < 0) {
+ err = ERR_DBASE;
+ goto cleanup;
+ }
+ }
+cleanup:
+ free(tmpname);
+ return (err);
+}
+
+static int
+match(char *line, char *name)
+{
+ int len;
+
+ len = strlen(name);
+ return (strncmp(line, name, len) == 0 &&
+ (line[len] == ' ' || line[len] == '\t'));
+}
+#endif /* !YP */
diff --git a/usr.sbin/rpc.ypupdated/yp_dbdelete.c b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
new file mode 100644
index 000000000000..d07a8fcd2ff9
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
@@ -0,0 +1,68 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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.
+ */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+int
+yp_del_record(DB *dbp, DBT *key)
+{
+ int rval;
+
+ if ((rval = (dbp->del)(dbp,key,0))) {
+ switch (rval) {
+ case 1:
+ return(YP_FALSE);
+ break;
+ case -1:
+ default:
+ (void)(dbp->close)(dbp);
+ return(YP_BADDB);
+ break;
+ }
+ }
+
+ return(YP_TRUE);
+}
diff --git a/usr.sbin/rpc.ypupdated/yp_dbupdate.c b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
new file mode 100644
index 000000000000..af17bf5244ef
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
@@ -0,0 +1,147 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/fcntl.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <db.h>
+#include <unistd.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/ypupdate_prot.h>
+#include "ypxfr_extern.h"
+#include "ypupdated_extern.h"
+
+static int
+yp_domake(char *map, char *domain)
+{
+ int pid;
+
+ switch ((pid = fork())) {
+ case 0:
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, map, domain, (char *)NULL);
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ return(YPERR_YPERR);
+ break;
+ default:
+ children++;
+ break;
+ }
+
+ return(0);
+}
+
+int
+ypmap_update(char *netname, char *map, unsigned int op, unsigned int keylen,
+ char *keyval, unsigned int datlen, char *datval)
+{
+ DB *dbp;
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ char *yp_last = "YP_LAST_MODIFIED";
+ char yplastbuf[32];
+ char *domptr;
+ int rval = 0;
+
+ if ((domptr = strchr(netname, '@')) == NULL)
+ return(ERR_ACCESS);
+ domptr++;
+
+
+ dbp = yp_open_db_rw(domptr, map, O_RDWR);
+ if (dbp == NULL)
+ return(ERR_DBASE);
+
+ key.data = keyval;
+ key.size = keylen;
+ data.data = datval;
+ data.size = datlen;
+
+ switch (op) {
+ case YPOP_DELETE: /* delete this entry */
+ rval = yp_del_record(dbp, &key);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_INSERT: /* add, do not change */
+ rval = yp_put_record(dbp, &key, &data, 0);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_STORE: /* add, or change */
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_CHANGE: /* change, do not add */
+ if (yp_get_record(domptr, map, &key, &data, 0) != YP_TRUE) {
+ rval = ERR_KEY;
+ break;
+ }
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ default:
+ yp_error("unknown update command: (%d)", op);
+ }
+
+ if (rval) {
+ (void)(dbp->close)(dbp);
+ return(rval);
+ }
+
+ snprintf(yplastbuf, sizeof(yplastbuf), "%jd", (intmax_t)time(NULL));
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domptr, map);
+ (void)(dbp->close)(dbp);
+ return(ERR_DBASE);
+ }
+
+ (void)(dbp->close)(dbp);
+ return(yp_domake(map, domptr));
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdate b/usr.sbin/rpc.ypupdated/ypupdate
new file mode 100755
index 000000000000..8795ef3baf80
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdate
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.ypupdatedd to propagate NIS maps
+# after the master map databases have been modified. It expects
+# to be passed two arguments: the name of the map that was updated
+# and the name of the domain where the map resides.
+# These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+#
+
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ /usr/bin/touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ /bin/date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 2>&1
+else
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 >> $LOGFILE
+fi
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_extern.h b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
new file mode 100644
index 000000000000..90968df36748
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
@@ -0,0 +1,32 @@
+/*
+ */
+
+#include <db.h>
+
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef MAP_UPPATE
+#define MAP_UPDATE "ypupdate"
+#endif
+
+#define MAP_UPDATE_PATH YPLIBDIR MAP_UPDATE
+
+extern int children;
+extern void ypu_prog_1(struct svc_req *, register SVCXPRT *);
+extern int localupdate(char *, char *, u_int, u_int, char *, u_int, char *);
+extern int ypmap_update(char *, char *, u_int, u_int, char *, u_int, char *);
+extern int yp_del_record(DB *, DBT *);
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_main.c b/usr.sbin/rpc.ypupdated/ypupdated_main.c
new file mode 100644
index 000000000000..dae956594da0
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_main.c
@@ -0,0 +1,287 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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.
+ */
+
+#include <sys/cdefs.h>
+#include "ypupdate_prot.h"
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <rpc/rpc_com.h>
+#include <string.h> /* strcmp */
+#include <signal.h>
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+int debug;
+
+char *progname = "rpc.ypupdated";
+char *yp_dir = "/var/yp/";
+
+static void
+_msgout(char* msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, "%s", msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void
+ypupdated_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypupdated process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void
+reaper(int sig)
+{
+ int status;
+
+ if (sig == SIGHUP) {
+#ifdef foo
+ load_securenets();
+#endif
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ exit(0);
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "rpc.ypupdatedd [-p path]\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != -1) {
+ switch (ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+#ifdef foo
+ load_securenets();
+#endif
+
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1) {
+ yp_error("failed to register AUTH_DES flavor");
+ exit(1);
+ }
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypupdatedd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ if (daemon(0,0)) {
+ err(1, "cannot fork");
+ }
+ openlog("rpc.ypupdated", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypupdated_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_server.c b/usr.sbin/rpc.ypupdated/ypupdated_server.c
new file mode 100644
index 000000000000..47e52401cd6e
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_server.c
@@ -0,0 +1,227 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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.
+ *
+ * ypupdate server implementation
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#include <sys/param.h>
+#include <rpcsvc/yp.h>
+#include "ypupdate_prot.h"
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+int children = 0;
+int forked = 0;
+
+/*
+ * Try to avoid spoofing: if a client chooses to use a very large
+ * window and then tries a bunch of randomly chosen encrypted timestamps,
+ * there's a chance he might stumble onto a valid combination.
+ * We therefore reject any RPCs with a window size larger than a preset
+ * value.
+ */
+#ifndef WINDOW
+#define WINDOW (60*60)
+#endif
+
+static enum auth_stat
+yp_checkauth(struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+
+ switch (svcreq->rq_cred.oa_flavor) {
+ case AUTH_DES:
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ if (des_cred->adc_fullname.window > WINDOW) {
+ yp_error("warning: client-specified window size \
+was too large -- possible spoof attempt");
+ return(AUTH_BADCRED);
+ }
+ return(AUTH_OK);
+ break;
+ case AUTH_UNIX:
+ case AUTH_NONE:
+ yp_error("warning: client didn't use DES authentication");
+ return(AUTH_TOOWEAK);
+ break;
+ default:
+ yp_error("client used unknown auth flavor");
+ return(AUTH_REJECTEDCRED);
+ break;
+ }
+}
+
+unsigned int *
+ypu_change_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_insert_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_delete_1_svc(struct ypdelete_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_store_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8
index fa61e78eaf3e..d39d9fdc8f5a 100644
--- a/usr.sbin/syslogd/syslogd.8
+++ b/usr.sbin/syslogd/syslogd.8
@@ -403,7 +403,7 @@ The message can contain a priority code,
which should be a preceding
decimal number in angle braces,
for example,
-.Sq Aq 5 .
+.Sq <5> .
This priority code should map into the priorities defined in the
include file
.In sys/syslog.h .
diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c
index 88b467486da1..27123f2143d0 100644
--- a/usr.sbin/watchdogd/watchdogd.c
+++ b/usr.sbin/watchdogd/watchdogd.c
@@ -63,25 +63,25 @@
static long fetchtimeout(int opt,
const char *longopt, const char *myoptarg, int zero_ok);
static void parseargs(int, char *[]);
-static int seconds_to_pow2ns(int);
static void sighandler(int);
static void watchdog_loop(void);
static int watchdog_init(void);
static int watchdog_onoff(int onoff);
-static int watchdog_patpat(u_int timeout);
+static int watchdog_patpat(sbintime_t);
static void usage(void);
-static int tstotv(struct timeval *tv, struct timespec *ts);
static int tvtohz(struct timeval *tv);
static int debugging = 0;
static int end_program = 0;
static const char *pidfile = _PATH_VARRUN "watchdogd.pid";
-static u_int timeout = WD_TO_128SEC;
+static sbintime_t timeout = 128 * SBT_1S;
static u_int exit_timeout = WD_TO_NEVER;
static u_int pretimeout = 0;
static u_int timeout_sec;
static u_int nap = 10;
+#ifdef notyet
static int passive = 0;
+#endif
static int is_daemon = 0;
static int is_dry_run = 0; /* do not arm the watchdog, only
report on timing of the watch
@@ -174,38 +174,23 @@ main(int argc, char *argv[])
pidfile_remove(pfh);
return (EX_OK);
} else {
- if (passive)
- timeout |= WD_PASSIVE;
- else
- timeout |= WD_ACTIVE;
if (watchdog_patpat(timeout) < 0)
err(EX_OSERR, "patting the dog");
return (EX_OK);
}
}
-static void
-pow2ns_to_ts(int pow2ns, struct timespec *ts)
-{
- uint64_t ns;
-
- ns = 1ULL << pow2ns;
- ts->tv_sec = ns / 1000000000ULL;
- ts->tv_nsec = ns % 1000000000ULL;
-}
-
/*
* Convert a timeout in seconds to N where 2^N nanoseconds is close to
* "seconds".
*
* The kernel expects the timeouts for watchdogs in "2^N nanosecond format".
*/
-static u_int
-parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg)
+static sbintime_t
+parse_timeout_to_sbt(char opt, const char *longopt, const char *myoptarg)
{
- double a;
- u_int rv;
- struct timespec ts;
+ long a;
+ sbintime_t rv;
struct timeval tv;
int ticks;
char shortopt[] = "- ";
@@ -216,19 +201,17 @@ parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg)
a = fetchtimeout(opt, longopt, myoptarg, 1);
if (a == 0)
- rv = WD_TO_NEVER;
+ rv = 0;
else
- rv = seconds_to_pow2ns(a);
- pow2ns_to_ts(rv, &ts);
- tstotv(&tv, &ts);
+ rv = a * SBT_1S;
+ tv = sbttotv(rv);
ticks = tvtohz(&tv);
if (debugging) {
printf("Timeout for %s%s "
- "is 2^%d nanoseconds "
- "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n",
+ "is "
+ "(in: %s sec -> out: %jd sec %ld us -> %d ticks)\n",
longopt ? "-" : "", longopt ? longopt : shortopt,
- rv,
- myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks);
+ myoptarg, (intmax_t)tv.tv_sec, tv.tv_usec, ticks);
}
if (ticks <= 0) {
errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt);
@@ -364,7 +347,7 @@ watchdog_loop(void)
}
if (failed == 0)
- watchdog_patpat(timeout|WD_ACTIVE);
+ watchdog_patpat(timeout);
waited = watchdog_check_dogfunction_time(&ts_start, &ts_end);
if (nap - waited > 0)
@@ -387,13 +370,22 @@ try_end:
* to keep the watchdog from firing.
*/
static int
-watchdog_patpat(u_int t)
+watchdog_patpat(sbintime_t sbt)
{
if (is_dry_run)
return 0;
- return ioctl(fd, WDIOCPATPAT, &t);
+ return ioctl(fd, WDIOC_SETTIMEOUT, &sbt);
+}
+
+static int
+watchdog_control(u_int control)
+{
+ if (is_dry_run)
+ return (0);
+
+ return ioctl(fd, WDIOC_CONTROL, &control);
}
/*
@@ -420,7 +412,7 @@ watchdog_onoff(int onoff)
warn("setting WDIOC_SETSOFT %d", softtimeout_set);
return (error);
}
- error = watchdog_patpat((timeout|WD_ACTIVE));
+ error = watchdog_patpat(timeout);
if (error) {
warn("watchdog_patpat failed");
goto failsafe;
@@ -452,12 +444,12 @@ watchdog_onoff(int onoff)
}
}
/* pat one more time for good measure */
- return watchdog_patpat((timeout|WD_ACTIVE));
+ return watchdog_patpat(timeout);
} else {
- return watchdog_patpat(exit_timeout);
+ return watchdog_control(WD_CTRL_DISABLE);
}
failsafe:
- watchdog_patpat(exit_timeout);
+ watchdog_control(WD_CTRL_DISABLE);
return (error);
}
@@ -567,15 +559,6 @@ timeout_act_str2int(const char *lopt, const char *acts)
return rv;
}
-int
-tstotv(struct timeval *tv, struct timespec *ts)
-{
-
- tv->tv_sec = ts->tv_sec;
- tv->tv_usec = ts->tv_nsec / 1000;
- return 0;
-}
-
/*
* Convert a timeval to a number of ticks.
* Mostly copied from the kernel.
@@ -647,30 +630,6 @@ tvtohz(struct timeval *tv)
return ((int)ticks);
}
-static int
-seconds_to_pow2ns(int seconds)
-{
- uint64_t power;
- uint64_t ns;
- uint64_t shifted;
-
- if (seconds <= 0)
- errx(1, "seconds %d < 0", seconds);
- ns = ((uint64_t)seconds) * 1000000000ULL;
- power = flsll(ns);
- shifted = 1ULL << power;
- if (shifted <= ns) {
- power++;
- }
- if (debugging) {
- printf("shifted %lld\n", (long long)shifted);
- printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n",
- seconds, (long long)ns, (int)power);
- }
- return (power);
-}
-
-
/*
* Handle the few command line arguments supported.
*/
@@ -683,8 +642,7 @@ parseargs(int argc, char *argv[])
const char *lopt;
/* Get the default value of timeout_sec from the default timeout. */
- pow2ns_to_ts(timeout, &ts);
- timeout_sec = ts.tv_sec;
+ timeout_sec = sbintime_getsec(timeout);
/*
* if we end with a 'd' aka 'watchdogd' then we are the daemon program,
@@ -727,10 +685,10 @@ parseargs(int argc, char *argv[])
break;
case 't':
timeout_sec = atoi(optarg);
- timeout = parse_timeout_to_pow2ns(c, NULL, optarg);
+ timeout = parse_timeout_to_sbt(c, NULL, optarg);
if (debugging)
- printf("Timeout is 2^%d nanoseconds\n",
- timeout);
+ printf("Timeout is %d\n",
+ (int)(timeout / SBT_1S));
break;
case 'T':
carp_thresh_seconds =
@@ -740,7 +698,7 @@ parseargs(int argc, char *argv[])
do_timedog = 1;
break;
case 'x':
- exit_timeout = parse_timeout_to_pow2ns(c, NULL, optarg);
+ exit_timeout = parse_timeout_to_sbt(c, NULL, optarg);
if (exit_timeout != 0)
exit_timeout |= WD_ACTIVE;
break;