diff options
Diffstat (limited to 'usr.sbin')
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; |