Index: head/sbin/nvmecontrol/comnd.c =================================================================== --- head/sbin/nvmecontrol/comnd.c (revision 367815) +++ head/sbin/nvmecontrol/comnd.c (revision 367816) @@ -1,344 +1,344 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2019 Netflix, Inc * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "comnd.h" static struct cmd top; static void print_tree(const struct cmd *f) { if (f->parent != NULL) print_tree(f->parent); if (f->name != NULL) fprintf(stderr, " %s", f->name); } static void print_usage(const struct cmd *f) { fprintf(stderr, " %s", getprogname()); print_tree(f->parent); fprintf(stderr, " %-15s - %s\n", f->name, f->descr); } static void gen_usage(const struct cmd *t) { struct cmd *walker; fprintf(stderr, "usage:\n"); SLIST_FOREACH(walker, &t->subcmd, link) { print_usage(walker); } exit(EX_USAGE); } int cmd_dispatch(int argc, char *argv[], const struct cmd *t) { struct cmd *walker; if (t == NULL) t = ⊤ if (argv[1] == NULL) { gen_usage(t); return (1); } SLIST_FOREACH(walker, &t->subcmd, link) { if (strcmp(argv[1], walker->name) == 0) { walker->fn(walker, argc-1, &argv[1]); return (0); } } fprintf(stderr, "Unknown command: %s\n", argv[1]); gen_usage(t); return (1); } static void arg_suffix(char *buf, size_t len, arg_type at) { switch (at) { case arg_none: break; case arg_string: strlcat(buf, "=", len); break; case arg_path: strlcat(buf, "=", len); break; default: strlcat(buf, "=", len); break; } } void arg_help(int argc __unused, char * const *argv, const struct cmd *f) { int i; char buf[31]; const struct opts *opts = f->opts; const struct args *args = f->args; // XXX walk up the cmd list... if (argv[optind]) fprintf(stderr, "Unknown argument: %s\n", argv[optind]); fprintf(stderr, "Usage:\n %s", getprogname()); print_tree(f); if (opts) fprintf(stderr, " "); if (args) { while (args->descr != NULL) { fprintf(stderr, " %s", args->descr); args++; } } fprintf(stderr, "\n\n%s\n", f->descr); if (opts != NULL) { fprintf(stderr, "Options:\n"); for (i = 0; opts[i].long_arg != NULL; i++) { *buf = '\0'; if (isprint(opts[i].short_arg)) { snprintf(buf, sizeof(buf), " -%c, ", opts[i].short_arg); } else { strlcpy(buf, " ", sizeof(buf)); } strlcat(buf, "--", sizeof(buf)); strlcat(buf, opts[i].long_arg, sizeof(buf)); arg_suffix(buf, sizeof(buf), opts[i].at); fprintf(stderr, "%-30.30s - %s\n", buf, opts[i].descr); } } exit(EX_USAGE); } static int find_long(struct option *lopts, int ch) { int i; for (i = 0; lopts[i].val != ch && lopts[i].name != NULL; i++) continue; return (i); } int arg_parse(int argc, char * const * argv, const struct cmd *f) { int i, n, idx, ch; uint64_t v; struct option *lopts; char *shortopts, *p; const struct opts *opts = f->opts; const struct args *args = f->args; if (opts == NULL) n = 0; else for (n = 0; opts[n].long_arg != NULL;) n++; lopts = malloc((n + 2) * sizeof(struct option)); if (lopts == NULL) err(EX_OSERR, "option memory"); p = shortopts = malloc((2 * n + 3) * sizeof(char)); if (shortopts == NULL) err(EX_OSERR, "shortopts memory"); idx = 0; for (i = 0; i < n; i++) { lopts[i].name = opts[i].long_arg; lopts[i].has_arg = opts[i].at == arg_none ? no_argument : required_argument; lopts[i].flag = NULL; lopts[i].val = opts[i].short_arg; if (isprint(opts[i].short_arg)) { *p++ = opts[i].short_arg; if (lopts[i].has_arg) *p++ = ':'; } } lopts[n].name = "help"; lopts[n].has_arg = no_argument; lopts[n].flag = NULL; lopts[n].val = '?'; *p++ = '?'; *p++ = '\0'; memset(lopts + n + 1, 0, sizeof(struct option)); while ((ch = getopt_long(argc, argv, shortopts, lopts, &idx)) != -1) { /* * If ch != 0, we've found a short option, and we have to * look it up lopts table. Otherwise idx is valid. */ if (ch != 0) idx = find_long(lopts, ch); if (idx == n) arg_help(argc, argv, f); switch (opts[idx].at) { case arg_none: *(bool *)opts[idx].ptr = true; break; case arg_string: case arg_path: *(const char **)opts[idx].ptr = optarg; break; case arg_uint8: v = strtoul(optarg, NULL, 0); if (v > 0xff) goto bad_arg; *(uint8_t *)opts[idx].ptr = v; break; case arg_uint16: v = strtoul(optarg, NULL, 0); if (v > 0xffff) goto bad_arg; *(uint16_t *)opts[idx].ptr = v; break; case arg_uint32: v = strtoul(optarg, NULL, 0); if (v > 0xffffffffu) goto bad_arg; *(uint32_t *)opts[idx].ptr = v; break; case arg_uint64: v = strtoul(optarg, NULL, 0); if (v > 0xffffffffffffffffull) goto bad_arg; *(uint64_t *)opts[idx].ptr = v; break; case arg_size: if (expand_number(optarg, &v) < 0) goto bad_arg; *(uint64_t *)opts[idx].ptr = v; break; } } if (args) { while (args->descr) { if (optind >= argc) { fprintf(stderr, "Missing arg %s\n", args->descr); arg_help(argc, argv, f); free(lopts); free(shortopts); return (1); } *(char **)args->ptr = argv[optind++]; args++; } } free(lopts); free(shortopts); return (0); bad_arg: fprintf(stderr, "Bad value to --%s: %s\n", opts[idx].long_arg, optarg); free(lopts); free(shortopts); exit(EX_USAGE); } /* * Loads all the .so's from the specified directory. */ void -cmd_load_dir(const char *dir __unused, cmd_load_cb_t cb __unused, void *argp __unused) +cmd_load_dir(const char *dir, cmd_load_cb_t cb, void *argp) { DIR *d; struct dirent *dent; char *path = NULL; void *h; d = opendir(dir); if (d == NULL) return; for (dent = readdir(d); dent != NULL; dent = readdir(d)) { if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0) continue; asprintf(&path, "%s/%s", dir, dent->d_name); if (path == NULL) err(EX_OSERR, "Can't malloc for path, giving up."); if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL) warnx("Can't load %s: %s", path, dlerror()); else { if (cb != NULL) cb(argp, h); } free(path); path = NULL; } closedir(d); } void cmd_register(struct cmd *up, struct cmd *cmd) { struct cmd *walker, *last; if (up == NULL) up = ⊤ SLIST_INIT(&cmd->subcmd); cmd->parent = up; last = NULL; SLIST_FOREACH(walker, &up->subcmd, link) { if (strcmp(walker->name, cmd->name) > 0) break; last = walker; } if (last == NULL) { SLIST_INSERT_HEAD(&up->subcmd, cmd, link); } else { SLIST_INSERT_AFTER(last, cmd, link); } } void cmd_init(void) { } Index: head/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.c (revision 367815) +++ head/sbin/nvmecontrol/nvmecontrol.c (revision 367816) @@ -1,190 +1,193 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" static void print_bytes(void *data, uint32_t length) { uint32_t i, j; uint8_t *p, *end; end = (uint8_t *)data + length; for (i = 0; i < length; i++) { p = (uint8_t *)data + (i*16); printf("%03x: ", i*16); for (j = 0; j < 16 && p < end; j++) printf("%02x ", *p++); if (p >= end) break; printf("\n"); } printf("\n"); } static void print_dwords(void *data, uint32_t length) { uint32_t *p; uint32_t i, j; p = (uint32_t *)data; length /= sizeof(uint32_t); for (i = 0; i < length; i+=8) { printf("%03x: ", i*4); for (j = 0; j < 8; j++) printf("%08x ", p[i+j]); printf("\n"); } printf("\n"); } void print_hex(void *data, uint32_t length) { if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0) print_dwords(data, length); else print_bytes(data, length); } int read_controller_data(int fd, struct nvme_controller_data *cdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(1); pt.buf = cdata; pt.len = sizeof(*cdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) return (errno); /* Convert data to host endian */ nvme_controller_data_swapbytes(cdata); if (nvme_completion_is_error(&pt.cpl)) return (EIO); return (0); } int read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0); pt.buf = nsdata; pt.len = sizeof(*nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) return (errno); /* Convert data to host endian */ nvme_namespace_data_swapbytes(nsdata); if (nvme_completion_is_error(&pt.cpl)) return (EIO); return (0); } int open_dev(const char *str, int *fd, int write, int exit_on_error) { char full_path[64]; snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); *fd = open(full_path, write ? O_RDWR : O_RDONLY); if (*fd < 0) { if (exit_on_error) { err(EX_OSFILE, "could not open %s%s", full_path, write ? " for write" : ""); } else return (errno); } return (0); } void get_nsid(int fd, char **ctrlr_str, uint32_t *nsid) { struct nvme_get_nsid gnsid; if (ioctl(fd, NVME_GET_NSID, &gnsid) < 0) err(EX_OSERR, "NVME_GET_NSID ioctl failed"); if (ctrlr_str != NULL) *ctrlr_str = strndup(gnsid.cdev, sizeof(gnsid.cdev)); if (nsid != NULL) *nsid = gnsid.nsid; } int main(int argc, char *argv[]) { + static char dir[MAXPATHLEN]; cmd_init(); cmd_load_dir("/lib/nvmecontrol", NULL, NULL); - cmd_load_dir(_PATH_LOCALBASE "/lib/nvmecontrol", NULL, NULL); + snprintf(dir, MAXPATHLEN, "%s/lib/nvmecontrol", getlocalbase()); + cmd_load_dir(dir, NULL, NULL); cmd_dispatch(argc, argv, NULL); return (0); } Index: head/usr.sbin/mailwrapper/mailwrapper.c =================================================================== --- head/usr.sbin/mailwrapper/mailwrapper.c (revision 367815) +++ head/usr.sbin/mailwrapper/mailwrapper.c (revision 367816) @@ -1,176 +1,176 @@ /* $OpenBSD: mailwrapper.c,v 1.18 2007/11/06 14:39:19 otto Exp $ */ /* $NetBSD: mailwrapper.c,v 1.9 2003/03/09 08:10:43 mjl Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1998 * Perry E. Metzger. 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 acknowledgment: * This product includes software developed for the NetBSD Project * by Perry E. Metzger. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" struct arglist { size_t argc, maxc; char **argv; }; int main(int, char *[], char *[]); static void initarg(struct arglist *); static void addarg(struct arglist *, const char *); static void initarg(struct arglist *al) { al->argc = 0; al->maxc = 10; if ((al->argv = calloc(al->maxc, sizeof(char *))) == NULL) err(EX_TEMPFAIL, "calloc"); } static void addarg(struct arglist *al, const char *arg) { if (al->argc == al->maxc) { al->maxc <<= 1; al->argv = realloc(al->argv, al->maxc * sizeof(char *)); if (al->argv == NULL) err(EX_TEMPFAIL, "realloc"); } if (arg == NULL) al->argv[al->argc++] = NULL; else if ((al->argv[al->argc++] = strdup(arg)) == NULL) err(EX_TEMPFAIL, "strdup"); } int main(int argc, char *argv[], char *envp[]) { FILE *config; char *line, *cp, *from, *to, *ap; const char *progname; char localmailerconf[MAXPATHLEN]; const char *mailerconf; size_t len, lineno = 0; int i; struct arglist al; /* change __progname to mailwrapper so we get sensible error messages */ progname = getprogname(); setprogname("mailwrapper"); initarg(&al); addarg(&al, argv[0]); snprintf(localmailerconf, MAXPATHLEN, "%s/etc/mail/mailer.conf", - getenv("LOCALBASE") ? getenv("LOCALBASE") : _PATH_LOCALBASE); + getlocalbase()); mailerconf = localmailerconf; if ((config = fopen(localmailerconf, "r")) == NULL) mailerconf = _PATH_MAILERCONF; if (config == NULL && ((config = fopen(mailerconf, "r")) == NULL)) { addarg(&al, NULL); openlog(getprogname(), LOG_PID, LOG_MAIL); syslog(LOG_INFO, "cannot open %s, using %s as default MTA", mailerconf, _PATH_DEFAULTMTA); closelog(); execve(_PATH_DEFAULTMTA, al.argv, envp); err(EX_OSERR, "cannot exec %s", _PATH_DEFAULTMTA); /*NOTREACHED*/ } for (;;) { if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL) { if (feof(config)) errx(EX_CONFIG, "no mapping in %s", mailerconf); err(EX_CONFIG, "cannot parse line %lu", (u_long)lineno); } #define WS " \t\n" cp = line; cp += strspn(cp, WS); if (cp[0] == '\0') { /* empty line */ free(line); continue; } if ((from = strsep(&cp, WS)) == NULL || cp == NULL) goto parse_error; cp += strspn(cp, WS); if ((to = strsep(&cp, WS)) == NULL) goto parse_error; if (strcmp(from, progname) == 0) { for (ap = strsep(&cp, WS); ap != NULL; ap = strsep(&cp, WS)) { if (*ap) addarg(&al, ap); } break; } free(line); } (void)fclose(config); for (i = 1; i < argc; i++) addarg(&al, argv[i]); addarg(&al, NULL); execve(to, al.argv, envp); err(EX_OSERR, "cannot exec %s", to); /*NOTREACHED*/ parse_error: errx(EX_CONFIG, "parse error in %s at line %lu", mailerconf, (u_long)lineno); /*NOTREACHED*/ } Index: head/usr.sbin/pkg/Makefile =================================================================== --- head/usr.sbin/pkg/Makefile (revision 367815) +++ head/usr.sbin/pkg/Makefile (revision 367816) @@ -1,30 +1,30 @@ # $FreeBSD$ _BRANCH!= ${MAKE} -C ${SRCTOP}/release -V BRANCH BRANCH?= ${_BRANCH} .if ${BRANCH:MCURRENT} != "" PKGCONFBRANCH?= latest .else . if ${BRANCH:MBETA*} || ${BRANCH:MRC*} || ${BRANCH:MRELEASE*} PKGCONFBRANCH?= quarterly . else . if ${MACHINE} != "amd64" && ${MACHINE} != "i386" PKGCONFBRANCH?= quarterly . else PKGCONFBRANCH?= latest . endif . endif .endif CONFS= FreeBSD.conf.${PKGCONFBRANCH} CONFSNAME= FreeBSD.conf CONFSDIR= /etc/pkg CONFSMODE= 644 PROG= pkg SRCS= pkg.c dns_utils.c config.c MAN= pkg.7 CFLAGS+=-I${SRCTOP}/contrib/libucl/include .PATH: ${SRCTOP}/contrib/libucl/include -LIBADD= archive fetch ucl sbuf crypto ssl +LIBADD= archive fetch ucl sbuf crypto ssl util .include Index: head/usr.sbin/pkg/config.c =================================================================== --- head/usr.sbin/pkg/config.c (revision 367815) +++ head/usr.sbin/pkg/config.c (revision 367816) @@ -1,537 +1,537 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2014 Baptiste Daroussin * Copyright (c) 2013 Bryan Drewery * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include +#include #include #include #include #include "config.h" #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ struct config_value { char *value; STAILQ_ENTRY(config_value) next; }; struct config_entry { uint8_t type; const char *key; const char *val; char *value; STAILQ_HEAD(, config_value) *list; bool envset; bool main_only; /* Only set in pkg.conf. */ }; static struct config_entry c[] = { [PACKAGESITE] = { PKG_CONFIG_STRING, "PACKAGESITE", URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest", NULL, NULL, false, false, }, [ABI] = { PKG_CONFIG_STRING, "ABI", NULL, NULL, NULL, false, true, }, [MIRROR_TYPE] = { PKG_CONFIG_STRING, "MIRROR_TYPE", "SRV", NULL, NULL, false, false, }, [ASSUME_ALWAYS_YES] = { PKG_CONFIG_BOOL, "ASSUME_ALWAYS_YES", "NO", NULL, NULL, false, true, }, [SIGNATURE_TYPE] = { PKG_CONFIG_STRING, "SIGNATURE_TYPE", NULL, NULL, NULL, false, false, }, [FINGERPRINTS] = { PKG_CONFIG_STRING, "FINGERPRINTS", NULL, NULL, NULL, false, false, }, [REPOS_DIR] = { PKG_CONFIG_LIST, "REPOS_DIR", NULL, NULL, NULL, false, true, }, [PUBKEY] = { PKG_CONFIG_STRING, "PUBKEY", NULL, NULL, NULL, false, false } }; static int pkg_get_myabi(char *dest, size_t sz) { struct utsname uts; char machine_arch[255]; size_t len; int error; error = uname(&uts); if (error) return (errno); len = sizeof(machine_arch); error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0); if (error) return (errno); machine_arch[len] = '\0'; /* * Use __FreeBSD_version rather than kernel version (uts.release) for * use in jails. This is equivalent to the value of uname -U. */ snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000, machine_arch); return (error); } static void subst_packagesite(const char *abi) { char *newval; const char *variable_string; const char *oldval; if (c[PACKAGESITE].value != NULL) oldval = c[PACKAGESITE].value; else oldval = c[PACKAGESITE].val; if ((variable_string = strstr(oldval, "${ABI}")) == NULL) return; asprintf(&newval, "%.*s%s%s", (int)(variable_string - oldval), oldval, abi, variable_string + strlen("${ABI}")); if (newval == NULL) errx(EXIT_FAILURE, "asprintf"); free(c[PACKAGESITE].value); c[PACKAGESITE].value = newval; } static int boolstr_to_bool(const char *str) { if (str != NULL && (strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 || str[0] == '1')) return (true); return (false); } static void config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype) { struct sbuf *buf = sbuf_new_auto(); const ucl_object_t *cur, *seq; ucl_object_iter_t it = NULL, itseq = NULL; struct config_entry *temp_config; struct config_value *cv; const char *key; int i; size_t j; /* Temporary config for configs that may be disabled. */ temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry)); while ((cur = ucl_iterate_object(obj, &it, true))) { key = ucl_object_key(cur); if (key == NULL) continue; sbuf_clear(buf); if (conftype == CONFFILE_PKG) { for (j = 0; j < strlen(key); ++j) sbuf_putc(buf, key[j]); sbuf_finish(buf); } else if (conftype == CONFFILE_REPO) { if (strcasecmp(key, "url") == 0) sbuf_cpy(buf, "PACKAGESITE"); else if (strcasecmp(key, "mirror_type") == 0) sbuf_cpy(buf, "MIRROR_TYPE"); else if (strcasecmp(key, "signature_type") == 0) sbuf_cpy(buf, "SIGNATURE_TYPE"); else if (strcasecmp(key, "fingerprints") == 0) sbuf_cpy(buf, "FINGERPRINTS"); else if (strcasecmp(key, "pubkey") == 0) sbuf_cpy(buf, "PUBKEY"); else if (strcasecmp(key, "enabled") == 0) { if ((cur->type != UCL_BOOLEAN) || !ucl_object_toboolean(cur)) goto cleanup; } else continue; sbuf_finish(buf); } for (i = 0; i < CONFIG_SIZE; i++) { if (strcmp(sbuf_data(buf), c[i].key) == 0) break; } /* Silently skip unknown keys to be future compatible. */ if (i == CONFIG_SIZE) continue; /* env has priority over config file */ if (c[i].envset) continue; /* Parse sequence value ["item1", "item2"] */ switch (c[i].type) { case PKG_CONFIG_LIST: if (cur->type != UCL_ARRAY) { warnx("Skipping invalid array " "value for %s.\n", c[i].key); continue; } temp_config[i].list = malloc(sizeof(*temp_config[i].list)); STAILQ_INIT(temp_config[i].list); while ((seq = ucl_iterate_object(cur, &itseq, true))) { if (seq->type != UCL_STRING) continue; cv = malloc(sizeof(struct config_value)); cv->value = strdup(ucl_object_tostring(seq)); STAILQ_INSERT_TAIL(temp_config[i].list, cv, next); } break; case PKG_CONFIG_BOOL: temp_config[i].value = strdup(ucl_object_toboolean(cur) ? "yes" : "no"); break; default: /* Normal string value. */ temp_config[i].value = strdup(ucl_object_tostring(cur)); break; } } /* Repo is enabled, copy over all settings from temp_config. */ for (i = 0; i < CONFIG_SIZE; i++) { if (c[i].envset) continue; /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */ if (conftype != CONFFILE_PKG && c[i].main_only == true) continue; switch (c[i].type) { case PKG_CONFIG_LIST: c[i].list = temp_config[i].list; break; default: c[i].value = temp_config[i].value; break; } } cleanup: free(temp_config); sbuf_delete(buf); } /*- * Parse new repo style configs in style: * Name: * URL: * MIRROR_TYPE: * etc... */ static void parse_repo_file(ucl_object_t *obj) { ucl_object_iter_t it = NULL; const ucl_object_t *cur; const char *key; while ((cur = ucl_iterate_object(obj, &it, true))) { key = ucl_object_key(cur); if (key == NULL) continue; if (cur->type != UCL_OBJECT) continue; config_parse(cur, CONFFILE_REPO); } } static int read_conf_file(const char *confpath, pkg_conf_file_t conftype) { struct ucl_parser *p; ucl_object_t *obj = NULL; p = ucl_parser_new(0); if (!ucl_parser_add_file(p, confpath)) { if (errno != ENOENT) errx(EXIT_FAILURE, "Unable to parse configuration " "file %s: %s", confpath, ucl_parser_get_error(p)); ucl_parser_free(p); /* no configuration present */ return (1); } obj = ucl_parser_get_object(p); if (obj->type != UCL_OBJECT) warnx("Invalid configuration format, ignoring the " "configuration file %s", confpath); else { if (conftype == CONFFILE_PKG) config_parse(obj, conftype); else if (conftype == CONFFILE_REPO) parse_repo_file(obj); } ucl_object_unref(obj); ucl_parser_free(p); return (0); } static int load_repositories(const char *repodir) { struct dirent *ent; DIR *d; char *p; size_t n; char path[MAXPATHLEN]; int ret; ret = 0; if ((d = opendir(repodir)) == NULL) return (1); while ((ent = readdir(d))) { /* Trim out 'repos'. */ if ((n = strlen(ent->d_name)) <= 5) continue; p = &ent->d_name[n - 5]; if (strcmp(p, ".conf") == 0) { snprintf(path, sizeof(path), "%s%s%s", repodir, repodir[strlen(repodir) - 1] == '/' ? "" : "/", ent->d_name); if (access(path, F_OK) == 0 && read_conf_file(path, CONFFILE_REPO)) { ret = 1; goto cleanup; } } } cleanup: closedir(d); return (ret); } int config_init(void) { char *val; int i; const char *localbase; char *env_list_item; char confpath[MAXPATHLEN]; struct config_value *cv; char abi[BUFSIZ]; for (i = 0; i < CONFIG_SIZE; i++) { val = getenv(c[i].key); if (val != NULL) { c[i].envset = true; switch (c[i].type) { case PKG_CONFIG_LIST: /* Split up comma-separated items from env. */ c[i].list = malloc(sizeof(*c[i].list)); STAILQ_INIT(c[i].list); for (env_list_item = strtok(val, ","); env_list_item != NULL; env_list_item = strtok(NULL, ",")) { cv = malloc(sizeof(struct config_value)); cv->value = strdup(env_list_item); STAILQ_INSERT_TAIL(c[i].list, cv, next); } break; default: c[i].val = val; break; } } } /* Read LOCALBASE/etc/pkg.conf first. */ - localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _PATH_LOCALBASE; - snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", - localbase); + localbase = getlocalbase(); + snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); if (access(confpath, F_OK) == 0 && read_conf_file(confpath, CONFFILE_PKG)) goto finalize; /* Then read in all repos from REPOS_DIR list of directories. */ if (c[REPOS_DIR].list == NULL) { c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list)); STAILQ_INIT(c[REPOS_DIR].list); cv = malloc(sizeof(struct config_value)); cv->value = strdup("/etc/pkg"); STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next); cv = malloc(sizeof(struct config_value)); if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0) goto finalize; STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next); } STAILQ_FOREACH(cv, c[REPOS_DIR].list, next) if (load_repositories(cv->value)) goto finalize; finalize: if (c[ABI].val == NULL && c[ABI].value == NULL) { if (pkg_get_myabi(abi, BUFSIZ) != 0) errx(EXIT_FAILURE, "Failed to determine the system " "ABI"); c[ABI].val = abi; } subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val); return (0); } int config_string(pkg_config_key k, const char **val) { if (c[k].type != PKG_CONFIG_STRING) return (-1); if (c[k].value != NULL) *val = c[k].value; else *val = c[k].val; return (0); } int config_bool(pkg_config_key k, bool *val) { const char *value; if (c[k].type != PKG_CONFIG_BOOL) return (-1); *val = false; if (c[k].value != NULL) value = c[k].value; else value = c[k].val; if (boolstr_to_bool(value)) *val = true; return (0); } void config_finish(void) { int i; for (i = 0; i < CONFIG_SIZE; i++) free(c[i].value); } Index: head/usr.sbin/pkg/pkg.c =================================================================== --- head/usr.sbin/pkg/pkg.c (revision 367815) +++ head/usr.sbin/pkg/pkg.c (revision 367816) @@ -1,1134 +1,1133 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012-2014 Baptiste Daroussin * Copyright (c) 2013 Bryan Drewery * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include -#include #include #include #include #include "dns_utils.h" #include "config.h" struct sig_cert { char *name; unsigned char *sig; int siglen; unsigned char *cert; int certlen; bool trusted; }; struct pubkey { unsigned char *sig; int siglen; }; typedef enum { HASH_UNKNOWN, HASH_SHA256, } hash_t; struct fingerprint { hash_t type; char *name; char hash[BUFSIZ]; STAILQ_ENTRY(fingerprint) next; }; STAILQ_HEAD(fingerprint_list, fingerprint); static int extract_pkg_static(int fd, char *p, int sz) { struct archive *a; struct archive_entry *ae; char *end; int ret, r; ret = -1; a = archive_read_new(); if (a == NULL) { warn("archive_read_new"); return (ret); } archive_read_support_filter_all(a); archive_read_support_format_tar(a); if (lseek(fd, 0, 0) == -1) { warn("lseek"); goto cleanup; } if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { warnx("archive_read_open_fd: %s", archive_error_string(a)); goto cleanup; } ae = NULL; while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { end = strrchr(archive_entry_pathname(ae), '/'); if (end == NULL) continue; if (strcmp(end, "/pkg-static") == 0) { r = archive_read_extract(a, ae, ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); strlcpy(p, archive_entry_pathname(ae), sz); break; } } if (r == ARCHIVE_OK) ret = 0; else warnx("failed to extract pkg-static: %s", archive_error_string(a)); cleanup: archive_read_free(a); return (ret); } static int install_pkg_static(const char *path, const char *pkgpath, bool force) { int pstat; pid_t pid; switch ((pid = fork())) { case -1: return (-1); case 0: if (force) execl(path, "pkg-static", "add", "-f", pkgpath, (char *)NULL); else execl(path, "pkg-static", "add", pkgpath, (char *)NULL); _exit(1); default: break; } while (waitpid(pid, &pstat, 0) == -1) if (errno != EINTR) return (-1); if (WEXITSTATUS(pstat)) return (WEXITSTATUS(pstat)); else if (WIFSIGNALED(pstat)) return (128 & (WTERMSIG(pstat))); return (pstat); } static int fetch_to_fd(const char *url, char *path) { struct url *u; struct dns_srvinfo *mirrors, *current; struct url_stat st; FILE *remote; /* To store _https._tcp. + hostname + \0 */ int fd; int retry, max_retry; ssize_t r; char buf[10240]; char zone[MAXHOSTNAMELEN + 13]; static const char *mirror_type = NULL; max_retry = 3; current = mirrors = NULL; remote = NULL; if (mirror_type == NULL && config_string(MIRROR_TYPE, &mirror_type) != 0) { warnx("No MIRROR_TYPE defined"); return (-1); } if ((fd = mkstemp(path)) == -1) { warn("mkstemp()"); return (-1); } retry = max_retry; if ((u = fetchParseURL(url)) == NULL) { warn("fetchParseURL('%s')", url); return (-1); } while (remote == NULL) { if (retry == max_retry) { if (strcmp(u->scheme, "file") != 0 && strcasecmp(mirror_type, "srv") == 0) { snprintf(zone, sizeof(zone), "_%s._tcp.%s", u->scheme, u->host); mirrors = dns_getsrvinfo(zone); current = mirrors; } } if (mirrors != NULL) { strlcpy(u->host, current->host, sizeof(u->host)); u->port = current->port; } remote = fetchXGet(u, &st, ""); if (remote == NULL) { --retry; if (retry <= 0) goto fetchfail; if (mirrors == NULL) { sleep(1); } else { current = current->next; if (current == NULL) current = mirrors; } } } while ((r = fread(buf, 1, sizeof(buf), remote)) > 0) { if (write(fd, buf, r) != r) { warn("write()"); goto fetchfail; } } if (r != 0) { warn("An error occurred while fetching pkg(8)"); goto fetchfail; } if (ferror(remote)) goto fetchfail; goto cleanup; fetchfail: if (fd != -1) { close(fd); fd = -1; unlink(path); } cleanup: if (remote != NULL) fclose(remote); return fd; } static struct fingerprint * parse_fingerprint(ucl_object_t *obj) { const ucl_object_t *cur; ucl_object_iter_t it = NULL; const char *function, *fp, *key; struct fingerprint *f; hash_t fct = HASH_UNKNOWN; function = fp = NULL; while ((cur = ucl_iterate_object(obj, &it, true))) { key = ucl_object_key(cur); if (cur->type != UCL_STRING) continue; if (strcasecmp(key, "function") == 0) { function = ucl_object_tostring(cur); continue; } if (strcasecmp(key, "fingerprint") == 0) { fp = ucl_object_tostring(cur); continue; } } if (fp == NULL || function == NULL) return (NULL); if (strcasecmp(function, "sha256") == 0) fct = HASH_SHA256; if (fct == HASH_UNKNOWN) { warnx("Unsupported hashing function: %s", function); return (NULL); } f = calloc(1, sizeof(struct fingerprint)); f->type = fct; strlcpy(f->hash, fp, sizeof(f->hash)); return (f); } static void free_fingerprint_list(struct fingerprint_list* list) { struct fingerprint *fingerprint, *tmp; STAILQ_FOREACH_SAFE(fingerprint, list, next, tmp) { free(fingerprint->name); free(fingerprint); } free(list); } static struct fingerprint * load_fingerprint(const char *dir, const char *filename) { ucl_object_t *obj = NULL; struct ucl_parser *p = NULL; struct fingerprint *f; char path[MAXPATHLEN]; f = NULL; snprintf(path, MAXPATHLEN, "%s/%s", dir, filename); p = ucl_parser_new(0); if (!ucl_parser_add_file(p, path)) { warnx("%s: %s", path, ucl_parser_get_error(p)); ucl_parser_free(p); return (NULL); } obj = ucl_parser_get_object(p); if (obj->type == UCL_OBJECT) f = parse_fingerprint(obj); if (f != NULL) f->name = strdup(filename); ucl_object_unref(obj); ucl_parser_free(p); return (f); } static struct fingerprint_list * load_fingerprints(const char *path, int *count) { DIR *d; struct dirent *ent; struct fingerprint *finger; struct fingerprint_list *fingerprints; *count = 0; fingerprints = calloc(1, sizeof(struct fingerprint_list)); if (fingerprints == NULL) return (NULL); STAILQ_INIT(fingerprints); if ((d = opendir(path)) == NULL) { free(fingerprints); return (NULL); } while ((ent = readdir(d))) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; finger = load_fingerprint(path, ent->d_name); if (finger != NULL) { STAILQ_INSERT_TAIL(fingerprints, finger, next); ++(*count); } } closedir(d); return (fingerprints); } static void sha256_hash(unsigned char hash[SHA256_DIGEST_LENGTH], char out[SHA256_DIGEST_LENGTH * 2 + 1]) { int i; for (i = 0; i < SHA256_DIGEST_LENGTH; i++) sprintf(out + (i * 2), "%02x", hash[i]); out[SHA256_DIGEST_LENGTH * 2] = '\0'; } static void sha256_buf(char *buf, size_t len, char out[SHA256_DIGEST_LENGTH * 2 + 1]) { unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha256; out[0] = '\0'; SHA256_Init(&sha256); SHA256_Update(&sha256, buf, len); SHA256_Final(hash, &sha256); sha256_hash(hash, out); } static int sha256_fd(int fd, char out[SHA256_DIGEST_LENGTH * 2 + 1]) { int my_fd; FILE *fp; char buffer[BUFSIZ]; unsigned char hash[SHA256_DIGEST_LENGTH]; size_t r; int ret; SHA256_CTX sha256; my_fd = -1; fp = NULL; r = 0; ret = 1; out[0] = '\0'; /* Duplicate the fd so that fclose(3) does not close it. */ if ((my_fd = dup(fd)) == -1) { warnx("dup"); goto cleanup; } if ((fp = fdopen(my_fd, "rb")) == NULL) { warnx("fdopen"); goto cleanup; } SHA256_Init(&sha256); while ((r = fread(buffer, 1, BUFSIZ, fp)) > 0) SHA256_Update(&sha256, buffer, r); if (ferror(fp) != 0) { warnx("fread"); goto cleanup; } SHA256_Final(hash, &sha256); sha256_hash(hash, out); ret = 0; cleanup: if (fp != NULL) fclose(fp); else if (my_fd != -1) close(my_fd); (void)lseek(fd, 0, SEEK_SET); return (ret); } static EVP_PKEY * load_public_key_file(const char *file) { EVP_PKEY *pkey; BIO *bp; char errbuf[1024]; bp = BIO_new_file(file, "r"); if (!bp) errx(EXIT_FAILURE, "Unable to read %s", file); if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL) warnx("ici: %s", ERR_error_string(ERR_get_error(), errbuf)); BIO_free(bp); return (pkey); } static EVP_PKEY * load_public_key_buf(const unsigned char *cert, int certlen) { EVP_PKEY *pkey; BIO *bp; char errbuf[1024]; bp = BIO_new_mem_buf(__DECONST(void *, cert), certlen); if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL) warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); BIO_free(bp); return (pkey); } static bool rsa_verify_cert(int fd, const char *sigfile, const unsigned char *key, int keylen, unsigned char *sig, int siglen) { EVP_MD_CTX *mdctx; EVP_PKEY *pkey; char sha256[(SHA256_DIGEST_LENGTH * 2) + 2]; char errbuf[1024]; bool ret; pkey = NULL; mdctx = NULL; ret = false; SSL_load_error_strings(); /* Compute SHA256 of the package. */ if (lseek(fd, 0, 0) == -1) { warn("lseek"); goto cleanup; } if ((sha256_fd(fd, sha256)) == -1) { warnx("Error creating SHA256 hash for package"); goto cleanup; } if (sigfile != NULL) { if ((pkey = load_public_key_file(sigfile)) == NULL) { warnx("Error reading public key"); goto cleanup; } } else { if ((pkey = load_public_key_buf(key, keylen)) == NULL) { warnx("Error reading public key"); goto cleanup; } } /* Verify signature of the SHA256(pkg) is valid. */ if ((mdctx = EVP_MD_CTX_create()) == NULL) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } if (EVP_DigestVerifyUpdate(mdctx, sha256, strlen(sha256)) != 1) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } if (EVP_DigestVerifyFinal(mdctx, sig, siglen) != 1) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } ret = true; printf("done\n"); goto cleanup; error: printf("failed\n"); cleanup: if (pkey) EVP_PKEY_free(pkey); if (mdctx) EVP_MD_CTX_destroy(mdctx); ERR_free_strings(); return (ret); } static struct pubkey * read_pubkey(int fd) { struct pubkey *pk; struct sbuf *sig; char buf[4096]; int r; if (lseek(fd, 0, 0) == -1) { warn("lseek"); return (NULL); } sig = sbuf_new_auto(); while ((r = read(fd, buf, sizeof(buf))) >0) { sbuf_bcat(sig, buf, r); } sbuf_finish(sig); pk = calloc(1, sizeof(struct pubkey)); pk->siglen = sbuf_len(sig); pk->sig = calloc(1, pk->siglen); memcpy(pk->sig, sbuf_data(sig), pk->siglen); sbuf_delete(sig); return (pk); } static struct sig_cert * parse_cert(int fd) { int my_fd; struct sig_cert *sc; FILE *fp; struct sbuf *buf, *sig, *cert; char *line; size_t linecap; ssize_t linelen; buf = NULL; my_fd = -1; sc = NULL; line = NULL; linecap = 0; if (lseek(fd, 0, 0) == -1) { warn("lseek"); return (NULL); } /* Duplicate the fd so that fclose(3) does not close it. */ if ((my_fd = dup(fd)) == -1) { warnx("dup"); return (NULL); } if ((fp = fdopen(my_fd, "rb")) == NULL) { warn("fdopen"); close(my_fd); return (NULL); } sig = sbuf_new_auto(); cert = sbuf_new_auto(); while ((linelen = getline(&line, &linecap, fp)) > 0) { if (strcmp(line, "SIGNATURE\n") == 0) { buf = sig; continue; } else if (strcmp(line, "CERT\n") == 0) { buf = cert; continue; } else if (strcmp(line, "END\n") == 0) { break; } if (buf != NULL) sbuf_bcat(buf, line, linelen); } fclose(fp); /* Trim out unrelated trailing newline */ sbuf_setpos(sig, sbuf_len(sig) - 1); sbuf_finish(sig); sbuf_finish(cert); sc = calloc(1, sizeof(struct sig_cert)); sc->siglen = sbuf_len(sig); sc->sig = calloc(1, sc->siglen); memcpy(sc->sig, sbuf_data(sig), sc->siglen); sc->certlen = sbuf_len(cert); sc->cert = strdup(sbuf_data(cert)); sbuf_delete(sig); sbuf_delete(cert); return (sc); } static bool verify_pubsignature(int fd_pkg, int fd_sig) { struct pubkey *pk; const char *pubkey; bool ret; pk = NULL; pubkey = NULL; ret = false; if (config_string(PUBKEY, &pubkey) != 0) { warnx("No CONFIG_PUBKEY defined"); goto cleanup; } if ((pk = read_pubkey(fd_sig)) == NULL) { warnx("Error reading signature"); goto cleanup; } /* Verify the signature. */ printf("Verifying signature with public key %s... ", pubkey); if (rsa_verify_cert(fd_pkg, pubkey, NULL, 0, pk->sig, pk->siglen) == false) { fprintf(stderr, "Signature is not valid\n"); goto cleanup; } ret = true; cleanup: if (pk) { free(pk->sig); free(pk); } return (ret); } static bool verify_signature(int fd_pkg, int fd_sig) { struct fingerprint_list *trusted, *revoked; struct fingerprint *fingerprint; struct sig_cert *sc; bool ret; int trusted_count, revoked_count; const char *fingerprints; char path[MAXPATHLEN]; char hash[SHA256_DIGEST_LENGTH * 2 + 1]; sc = NULL; trusted = revoked = NULL; ret = false; /* Read and parse fingerprints. */ if (config_string(FINGERPRINTS, &fingerprints) != 0) { warnx("No CONFIG_FINGERPRINTS defined"); goto cleanup; } snprintf(path, MAXPATHLEN, "%s/trusted", fingerprints); if ((trusted = load_fingerprints(path, &trusted_count)) == NULL) { warnx("Error loading trusted certificates"); goto cleanup; } if (trusted_count == 0 || trusted == NULL) { fprintf(stderr, "No trusted certificates found.\n"); goto cleanup; } snprintf(path, MAXPATHLEN, "%s/revoked", fingerprints); if ((revoked = load_fingerprints(path, &revoked_count)) == NULL) { warnx("Error loading revoked certificates"); goto cleanup; } /* Read certificate and signature in. */ if ((sc = parse_cert(fd_sig)) == NULL) { warnx("Error parsing certificate"); goto cleanup; } /* Explicitly mark as non-trusted until proven otherwise. */ sc->trusted = false; /* Parse signature and pubkey out of the certificate */ sha256_buf(sc->cert, sc->certlen, hash); /* Check if this hash is revoked */ if (revoked != NULL) { STAILQ_FOREACH(fingerprint, revoked, next) { if (strcasecmp(fingerprint->hash, hash) == 0) { fprintf(stderr, "The package was signed with " "revoked certificate %s\n", fingerprint->name); goto cleanup; } } } STAILQ_FOREACH(fingerprint, trusted, next) { if (strcasecmp(fingerprint->hash, hash) == 0) { sc->trusted = true; sc->name = strdup(fingerprint->name); break; } } if (sc->trusted == false) { fprintf(stderr, "No trusted fingerprint found matching " "package's certificate\n"); goto cleanup; } /* Verify the signature. */ printf("Verifying signature with trusted certificate %s... ", sc->name); if (rsa_verify_cert(fd_pkg, NULL, sc->cert, sc->certlen, sc->sig, sc->siglen) == false) { fprintf(stderr, "Signature is not valid\n"); goto cleanup; } ret = true; cleanup: if (trusted) free_fingerprint_list(trusted); if (revoked) free_fingerprint_list(revoked); if (sc) { free(sc->cert); free(sc->sig); free(sc->name); free(sc); } return (ret); } static int bootstrap_pkg(bool force) { int fd_pkg, fd_sig; int ret; char url[MAXPATHLEN]; char tmppkg[MAXPATHLEN]; char tmpsig[MAXPATHLEN]; const char *packagesite; const char *signature_type; char pkgstatic[MAXPATHLEN]; fd_sig = -1; ret = -1; if (config_string(PACKAGESITE, &packagesite) != 0) { warnx("No PACKAGESITE defined"); return (-1); } if (config_string(SIGNATURE_TYPE, &signature_type) != 0) { warnx("Error looking up SIGNATURE_TYPE"); return (-1); } printf("Bootstrapping pkg from %s, please wait...\n", packagesite); /* Support pkg+http:// for PACKAGESITE which is the new format in 1.2 to avoid confusion on why http://pkg.FreeBSD.org has no A record. */ if (strncmp(URL_SCHEME_PREFIX, packagesite, strlen(URL_SCHEME_PREFIX)) == 0) packagesite += strlen(URL_SCHEME_PREFIX); snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite); snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); if ((fd_pkg = fetch_to_fd(url, tmppkg)) == -1) goto fetchfail; if (signature_type != NULL && strcasecmp(signature_type, "NONE") != 0) { if (strcasecmp(signature_type, "FINGERPRINTS") == 0) { snprintf(tmpsig, MAXPATHLEN, "%s/pkg.txz.sig.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.sig", packagesite); if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto fetchfail; } if (verify_signature(fd_pkg, fd_sig) == false) goto cleanup; } else if (strcasecmp(signature_type, "PUBKEY") == 0) { snprintf(tmpsig, MAXPATHLEN, "%s/pkg.txz.pubkeysig.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.pubkeysig", packagesite); if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto fetchfail; } if (verify_pubsignature(fd_pkg, fd_sig) == false) goto cleanup; } else { warnx("Signature type %s is not supported for " "bootstrapping.", signature_type); goto cleanup; } } if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, tmppkg, force); goto cleanup; fetchfail: warnx("Error fetching %s: %s", url, fetchLastErrString); if (fetchLastErrCode == FETCH_RESOLV) { fprintf(stderr, "Address resolution failed for %s.\n", packagesite); fprintf(stderr, "Consider changing PACKAGESITE.\n"); } else { fprintf(stderr, "A pre-built version of pkg could not be found for " "your system.\n"); fprintf(stderr, "Consider changing PACKAGESITE or installing it from " "ports: 'ports-mgmt/pkg'.\n"); } cleanup: if (fd_sig != -1) { close(fd_sig); unlink(tmpsig); } if (fd_pkg != -1) { close(fd_pkg); unlink(tmppkg); } return (ret); } static const char confirmation_message[] = "The package management tool is not yet installed on your system.\n" "Do you want to fetch and install it now? [y/N]: "; static const char non_interactive_message[] = "The package management tool is not yet installed on your system.\n" "Please set ASSUME_ALWAYS_YES=yes environment variable to be able to bootstrap " "in non-interactive (stdin not being a tty)\n"; static int pkg_query_yes_no(void) { int ret, c; fflush(stdout); c = getchar(); if (c == 'y' || c == 'Y') ret = 1; else ret = 0; while (c != '\n' && c != EOF) c = getchar(); return (ret); } static int bootstrap_pkg_local(const char *pkgpath, bool force) { char path[MAXPATHLEN]; char pkgstatic[MAXPATHLEN]; const char *signature_type; int fd_pkg, fd_sig, ret; fd_sig = -1; ret = -1; fd_pkg = open(pkgpath, O_RDONLY); if (fd_pkg == -1) err(EXIT_FAILURE, "Unable to open %s", pkgpath); if (config_string(SIGNATURE_TYPE, &signature_type) != 0) { warnx("Error looking up SIGNATURE_TYPE"); goto cleanup; } if (signature_type != NULL && strcasecmp(signature_type, "NONE") != 0) { if (strcasecmp(signature_type, "FINGERPRINTS") == 0) { snprintf(path, sizeof(path), "%s.sig", pkgpath); if ((fd_sig = open(path, O_RDONLY)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto cleanup; } if (verify_signature(fd_pkg, fd_sig) == false) goto cleanup; } else if (strcasecmp(signature_type, "PUBKEY") == 0) { snprintf(path, sizeof(path), "%s.pubkeysig", pkgpath); if ((fd_sig = open(path, O_RDONLY)) == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto cleanup; } if (verify_pubsignature(fd_pkg, fd_sig) == false) goto cleanup; } else { warnx("Signature type %s is not supported for " "bootstrapping.", signature_type); goto cleanup; } } if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, pkgpath, force); cleanup: close(fd_pkg); if (fd_sig != -1) close(fd_sig); return (ret); } int main(int argc, char *argv[]) { char pkgpath[MAXPATHLEN]; const char *pkgarg; int i; bool bootstrap_only, force, yes; bootstrap_only = false; force = false; pkgarg = NULL; yes = false; - snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", - getenv("LOCALBASE") ? getenv("LOCALBASE") : _PATH_LOCALBASE); + snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", getlocalbase()); if (argc > 1 && strcmp(argv[1], "bootstrap") == 0) { bootstrap_only = true; if (argc > 3) { fprintf(stderr, "Too many arguments\nUsage: pkg bootstrap [-f]\n"); exit(EXIT_FAILURE); } if (argc == 3 && strcmp(argv[2], "-f") == 0) { force = true; } else if (argc == 3) { fprintf(stderr, "Invalid argument specified\nUsage: pkg bootstrap [-f]\n"); exit(EXIT_FAILURE); } } if ((bootstrap_only && force) || access(pkgpath, X_OK) == -1) { /* * To allow 'pkg -N' to be used as a reliable test for whether * a system is configured to use pkg, don't bootstrap pkg * when that argument is given as argv[1]. */ if (argv[1] != NULL && strcmp(argv[1], "-N") == 0) errx(EXIT_FAILURE, "pkg is not installed"); config_init(); if (argc > 1 && strcmp(argv[1], "add") == 0) { if (argc > 2 && strcmp(argv[2], "-f") == 0) { force = true; pkgarg = argv[3]; } else pkgarg = argv[2]; if (pkgarg == NULL) { fprintf(stderr, "Path to pkg.txz required\n"); exit(EXIT_FAILURE); } if (access(pkgarg, R_OK) == -1) { fprintf(stderr, "No such file: %s\n", pkgarg); exit(EXIT_FAILURE); } if (bootstrap_pkg_local(pkgarg, force) != 0) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } /* * Do not ask for confirmation if either of stdin or stdout is * not tty. Check the environment to see if user has answer * tucked in there already. */ config_bool(ASSUME_ALWAYS_YES, &yes); if (!yes) { for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-y") == 0 || strcmp(argv[i], "--yes") == 0) { yes = true; break; } } } if (!yes) { if (!isatty(fileno(stdin))) { fprintf(stderr, non_interactive_message); exit(EXIT_FAILURE); } printf("%s", confirmation_message); if (pkg_query_yes_no() == 0) exit(EXIT_FAILURE); } if (bootstrap_pkg(force) != 0) exit(EXIT_FAILURE); config_finish(); if (bootstrap_only) exit(EXIT_SUCCESS); } else if (bootstrap_only) { printf("pkg already bootstrapped at %s\n", pkgpath); exit(EXIT_SUCCESS); } execv(pkgpath, argv); /* NOT REACHED */ return (EXIT_FAILURE); }