Index: stable/12/lib/libsecureboot/verify_file.c =================================================================== --- stable/12/lib/libsecureboot/verify_file.c (revision 364960) +++ stable/12/lib/libsecureboot/verify_file.c (revision 364961) @@ -1,540 +1,558 @@ /*- - * Copyright (c) 2017-2018, Juniper Networks, Inc. + * Copyright (c) 2017-2020, Juniper Networks, 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ /* * Routines to verify files loaded. */ #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include "libsecureboot.h" #include #include #ifdef UNIT_TEST # include # define panic warn /* * define MANIFEST_SKIP to Skip - in tests/tvo.c so that * tvo can control the value we use in find_manifest() */ extern char *Destdir; extern size_t DestdirLen; extern char *Skip; # undef MANIFEST_SKIP # define MANIFEST_SKIP Skip # undef VE_DEBUG_LEVEL #endif /* * We sometimes need to know if input is verified or not. * The extra slot is for tracking most recently opened. */ static int ve_status[SOPEN_MAX+1]; static int ve_status_state; struct verify_status; struct verify_status *verified_files = NULL; static int loaded_manifests = 0; /* have we loaded anything? */ #define VE_STATUS_NONE 1 #define VE_STATUS_VALID 2 /** * @brief set ve status for fd */ static void ve_status_set(int fd, int ves) { if (fd >= 0 && fd < SOPEN_MAX) { ve_status[fd] = ves; ve_status_state = VE_STATUS_VALID; } ve_status[SOPEN_MAX] = ves; } /** * @brief get ve status of fd * * What we return depends on ve_status_state. * * @return * @li ve_status[fd] if ve_status_state is valid * @li ve_status[SOPEN_MAX] if ve_status_state is none * @li VE_NOT_CHECKED if ve_status_state uninitialized */ int ve_status_get(int fd) { if (!ve_status_state) { return (VE_NOT_CHECKED); } if (ve_status_state == VE_STATUS_VALID && fd >= 0 && fd < SOPEN_MAX) return (ve_status[fd]); return (ve_status[SOPEN_MAX]); /* most recent */ } /** * @brief track verify status * * occasionally loader will make multiple calls * for the same file, we need only check it once. */ struct verify_status { dev_t vs_dev; ino_t vs_ino; int vs_status; struct verify_status *vs_next; }; int is_verified(struct stat *stp) { struct verify_status *vsp; if (stp->st_ino > 0) { for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) { if (stp->st_dev == vsp->vs_dev && stp->st_ino == vsp->vs_ino) return (vsp->vs_status); } } return (VE_NOT_CHECKED); } /* most recent first, since most likely to see repeated calls. */ void add_verify_status(struct stat *stp, int status) { struct verify_status *vsp; vsp = malloc(sizeof(struct verify_status)); vsp->vs_next = verified_files; vsp->vs_dev = stp->st_dev; vsp->vs_ino = stp->st_ino; vsp->vs_status = status; verified_files = vsp; } /** * @brief * load specified manifest if verified */ int load_manifest(const char *name, const char *prefix, const char *skip, struct stat *stp) { struct stat st; size_t n; int rc; char *content; rc = VE_FINGERPRINT_NONE; n = strlen(name); if (n > 4) { if (!stp) { stp = &st; if (stat(name, &st) < 0 || !S_ISREG(st.st_mode)) return (rc); } rc = is_verified(stp); if (rc != VE_NOT_CHECKED) { return (rc); } /* loader has no sense of time */ ve_utc_set(stp->st_mtime); content = (char *)verify_signed(name, VEF_VERBOSE); if (content) { #ifdef UNIT_TEST if (DestdirLen > 0 && strncmp(name, Destdir, DestdirLen) == 0) { name += DestdirLen; if (prefix && strncmp(prefix, Destdir, DestdirLen) == 0) prefix += DestdirLen; } #endif fingerprint_info_add(name, prefix, skip, content, stp); add_verify_status(stp, VE_VERIFIED); loaded_manifests = 1; /* we are verifying! */ DEBUG_PRINTF(3, ("loaded: %s %s %s\n", name, prefix, skip)); rc = VE_VERIFIED; } else { rc = VE_FINGERPRINT_WRONG; add_verify_status(stp, rc); /* remember */ } } return (rc); } static int find_manifest(const char *name) { struct stat st; char buf[MAXPATHLEN]; char *prefix; char *skip; const char **tp; int rc; strncpy(buf, name, MAXPATHLEN - 1); if (!(prefix = strrchr(buf, '/'))) return (-1); *prefix = '\0'; prefix = strdup(buf); rc = VE_FINGERPRINT_NONE; for (tp = manifest_names; *tp; tp++) { snprintf(buf, sizeof(buf), "%s/%s", prefix, *tp); DEBUG_PRINTF(5, ("looking for %s\n", buf)); if (stat(buf, &st) == 0 && st.st_size > 0) { #ifdef MANIFEST_SKIP_ALWAYS /* very unlikely */ skip = MANIFEST_SKIP_ALWAYS; #else #ifdef MANIFEST_SKIP /* rare */ if (*tp[0] == '.') { skip = MANIFEST_SKIP; } else #endif skip = NULL; #endif rc = load_manifest(buf, skip ? prefix : NULL, skip, &st); break; } } free(prefix); return (rc); } #ifdef LOADER_VERIEXEC_TESTING # define ACCEPT_NO_FP_DEFAULT VE_MUST + 1 #else # define ACCEPT_NO_FP_DEFAULT VE_MUST #endif #ifndef VE_VERBOSE_DEFAULT # define VE_VERBOSE_DEFAULT 0 #endif static int severity_guess(const char *filename) { const char *cp; /* Some files like *.conf and *.hints may be unsigned */ if ((cp = strrchr(filename, '.'))) { if (strcmp(cp, ".conf") == 0 || strcmp(cp, ".cookie") == 0 || strcmp(cp, ".hints") == 0) return (VE_TRY); + if (strcmp(cp, ".4th") == 0 || + strcmp(cp, ".lua") == 0 || + strcmp(cp, ".rc") == 0) + return (VE_MUST); } return (VE_WANT); } static int Verifying = -1; /* 0 if not verifying */ static void verify_tweak(int fd, off_t off, struct stat *stp, char *tweak, int *accept_no_fp, int *verbose) { if (strcmp(tweak, "off") == 0) { Verifying = 0; } else if (strcmp(tweak, "strict") == 0) { /* anything caller wants verified must be */ *accept_no_fp = VE_WANT; *verbose = 1; /* warn of anything unverified */ /* treat self test failure as fatal */ if (!ve_self_tests()) { panic("verify self tests failed"); } } else if (strcmp(tweak, "modules") == 0) { /* modules/kernel must be verified */ *accept_no_fp = VE_MUST; } else if (strcmp(tweak, "try") == 0) { /* best effort: always accept no fp */ *accept_no_fp = VE_MUST + 1; } else if (strcmp(tweak, "verbose") == 0) { *verbose = 1; } else if (strcmp(tweak, "quiet") == 0) { *verbose = 0; } else if (strncmp(tweak, "trust", 5) == 0) { /* content is trust anchor to add or revoke */ unsigned char *ucp; size_t num; if (off > 0) lseek(fd, 0, SEEK_SET); ucp = read_fd(fd, stp->st_size); if (ucp == NULL) return; if (strstr(tweak, "revoke")) { num = ve_trust_anchors_revoke(ucp, stp->st_size); DEBUG_PRINTF(3, ("revoked %d trust anchors\n", (int) num)); } else { num = ve_trust_anchors_add_buf(ucp, stp->st_size); DEBUG_PRINTF(3, ("added %d trust anchors\n", (int) num)); } } } #ifndef VE_DEBUG_LEVEL # define VE_DEBUG_LEVEL 0 #endif static int getenv_int(const char *var, int def) { const char *cp; char *ep; long val; val = def; cp = getenv(var); if (cp && *cp) { val = strtol(cp, &ep, 0); if ((ep && *ep) || val != (int)val) { val = def; } } return (int)val; } /** * @brief prepare to verify an open file * * @param[in] fd * open descriptor * * @param[in] filename * path we opened and will use to lookup fingerprint * * @param[in] stp * stat pointer so we can check file type */ int verify_prep(int fd, const char *filename, off_t off, struct stat *stp, const char *caller) { int rc; if (Verifying < 0) { Verifying = ve_trust_init(); #ifndef UNIT_TEST ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL)); #endif /* initialize ve_status with default result */ rc = Verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING; ve_status_set(0, rc); ve_status_state = VE_STATUS_NONE; if (Verifying) { ve_self_tests(); ve_anchor_verbose_set(1); } } if (!Verifying || fd < 0) return (0); if (stp) { if (fstat(fd, stp) < 0 || !S_ISREG(stp->st_mode)) return (0); } DEBUG_PRINTF(2, ("verify_prep: caller=%s,fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n", caller, fd, filename, (long long)off, (long long)stp->st_dev, (long long)stp->st_ino)); rc = is_verified(stp); DEBUG_PRINTF(4,("verify_prep: is_verified()->%d\n", rc)); if (rc == VE_NOT_CHECKED) { rc = find_manifest(filename); } else { ve_status_set(fd, rc); } return (rc); } /** * @brief verify an open file * * @param[in] fd * open descriptor * * @param[in] filename * path we opened and will use to lookup fingerprint * * @param[in] off * current offset in fd, must be restored on return * * @param[in] severity * indicator of how to handle case of missing fingerprint * * We look for a signed manifest relative to the filename * just opened and verify/load it if needed. * * We then use verify_fd() in libve to actually verify that hash for * open file. If it returns < 0 we look at the severity arg to decide * what to do about it. * * If verify_fd() returns VE_FINGERPRINT_NONE we accept it if severity * is < accept_no_fp. * * @return >= 0 on success < 0 on failure */ int verify_file(int fd, const char *filename, off_t off, int severity, const char *caller) { static int once; static int accept_no_fp = ACCEPT_NO_FP_DEFAULT; static int verbose = VE_VERBOSE_DEFAULT; struct stat st; char *cp; int rc; rc = verify_prep(fd, filename, off, &st, caller); if (!rc) return (0); if (!once) { once++; verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT); } if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) { if (severity <= VE_GUESS) severity = severity_guess(filename); #ifdef VE_PCR_SUPPORT /* * Only update pcr with things that must verify * these tend to be processed in a more deterministic * order, which makes our pseudo pcr more useful. */ ve_pcr_updating_set((severity == VE_MUST)); #endif #ifdef UNIT_TEST if (DestdirLen > 0 && strncmp(filename, Destdir, DestdirLen) == 0) { filename += DestdirLen; } #endif if ((rc = verify_fd(fd, filename, off, &st)) >= 0) { if (verbose || severity > VE_WANT) { #if defined(VE_DEBUG_LEVEL) && VE_DEBUG_LEVEL > 0 printf("%serified %s %llu,%llu\n", (rc == VE_FINGERPRINT_IGNORE) ? "Unv" : "V", filename, (long long)st.st_dev, (long long)st.st_ino); #else printf("%serified %s\n", (rc == VE_FINGERPRINT_IGNORE) ? "Unv" : "V", filename); #endif } if (severity < VE_MUST) { /* not a kernel or module */ if ((cp = strrchr(filename, '/'))) { cp++; if (strncmp(cp, "loader.ve.", 10) == 0) { cp += 10; verify_tweak(fd, off, &st, cp, &accept_no_fp, &verbose); } } } add_verify_status(&st, rc); ve_status_set(fd, rc); return (rc); } if (severity || verbose || rc == VE_FINGERPRINT_WRONG) printf("Unverified: %s\n", ve_error_get()); if (rc == VE_FINGERPRINT_UNKNOWN && severity < VE_MUST) rc = VE_UNVERIFIED_OK; else if (rc == VE_FINGERPRINT_NONE && severity < accept_no_fp) rc = VE_UNVERIFIED_OK; add_verify_status(&st, rc); } #ifdef LOADER_VERIEXEC_TESTING else if (rc != VE_FINGERPRINT_WRONG) { /* * We have not loaded any manifest and * not because of verication failure. * Most likely reason is we have none. * Allow boot to proceed if we are just testing. */ return (VE_UNVERIFIED_OK); } #endif if (rc == VE_FINGERPRINT_WRONG && severity > accept_no_fp) panic("cannot continue"); ve_status_set(fd, rc); return (rc); } /** * @brief get hex string for pcr value and export * * In case we are doing measured boot, provide * value of the "pcr" data we have accumulated. */ void verify_pcr_export(void) { #ifdef VE_PCR_SUPPORT char hexbuf[br_sha256_SIZE * 2 + 2]; unsigned char hbuf[br_sha256_SIZE]; char *hinfo; char *hex; ssize_t hlen; hlen = ve_pcr_get(hbuf, sizeof(hbuf)); if (hlen > 0) { hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen); if (hex) { hex[hlen*2] = '\0'; /* clobber newline */ setenv("loader.ve.pcr", hex, 1); DEBUG_PRINTF(1, ("%s: setenv(loader.ve.pcr, %s\n", __func__, hex)); hinfo = ve_pcr_hashed_get(1); if (hinfo) { setenv("loader.ve.hashed", hinfo, 1); DEBUG_PRINTF(1, ("%s: setenv(loader.ve.hashed, %s\n", __func__, hinfo)); + if ((hlen = strlen(hinfo)) > KENV_MVALLEN) { + /* + * bump kenv_mvallen + * roundup to multiple of KENV_MVALLEN + */ + char mvallen[16]; + + hlen += KENV_MVALLEN - + (hlen % KENV_MVALLEN); + if (snprintf(mvallen, sizeof(mvallen), + "%d", (int) hlen) < sizeof(mvallen)) + setenv("kenv_mvallen", mvallen, 1); + } free(hinfo); } } } #endif } Index: stable/12/stand/common/install.c =================================================================== --- stable/12/stand/common/install.c (revision 364960) +++ stable/12/stand/common/install.c (revision 364961) @@ -1,366 +1,409 @@ /*- * Copyright (c) 2008-2014, Juniper Networks, Inc. * 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 ``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 "bootstrap.h" extern struct in_addr servip; extern int pkgfs_init(const char *, struct fs_ops *); extern void pkgfs_cleanup(void); COMMAND_SET(install, "install", "install software package", command_install); static char *inst_kernel; static char **inst_modules; static char *inst_rootfs; static char *inst_loader_rc; static int setpath(char **what, char *val) { char *path; size_t len; int rel; len = strlen(val) + 1; rel = (val[0] != '/') ? 1 : 0; path = malloc(len + rel); if (path == NULL) return (ENOMEM); path[0] = '/'; strcpy(path + rel, val); *what = path; return (0); } static int setmultipath(char ***what, char *val) { char *s, *v; int count, error, idx; count = 0; v = val; do { count++; s = strchr(v, ','); v = (s == NULL) ? NULL : s + 1; } while (v != NULL); *what = calloc(count + 1, sizeof(char *)); if (*what == NULL) return (ENOMEM); for (idx = 0; idx < count; idx++) { s = strchr(val, ','); if (s != NULL) *s++ = '\0'; error = setpath(*what + idx, val); if (error) return (error); val = s; } return (0); } static int read_metatags(int fd) { char buf[1024]; char *p, *tag, *val; ssize_t fsize; int error; fsize = read(fd, buf, sizeof(buf)); if (fsize == -1) return (errno); /* * Assume that if we read a whole buffer worth of data, we * haven't read the entire file. In other words, the buffer * size must always be larger than the file size. That way * we can append a '\0' and use standard string operations. * Return an error if this is not possible. */ if (fsize == sizeof(buf)) return (ENOMEM); buf[fsize] = '\0'; error = 0; tag = buf; while (!error && *tag != '\0') { val = strchr(tag, '='); if (val == NULL) { error = EINVAL; break; } *val++ = '\0'; p = strchr(val, '\n'); if (p == NULL) { error = EINVAL; break; } *p++ = '\0'; if (strcmp(tag, "KERNEL") == 0) error = setpath(&inst_kernel, val); else if (strcmp(tag, "MODULES") == 0) error = setmultipath(&inst_modules, val); else if (strcmp(tag, "ROOTFS") == 0) error = setpath(&inst_rootfs, val); else if (strcmp(tag, "LOADER_RC") == 0) error = setpath(&inst_loader_rc, val); tag = p; } return (error); } static void cleanup(void) { u_int i; if (inst_kernel != NULL) { free(inst_kernel); inst_kernel = NULL; } if (inst_modules != NULL) { i = 0; while (inst_modules[i] != NULL) free(inst_modules[i++]); free(inst_modules); inst_modules = NULL; } if (inst_rootfs != NULL) { free(inst_rootfs); inst_rootfs = NULL; } if (inst_loader_rc != NULL) { free(inst_loader_rc); inst_loader_rc = NULL; } pkgfs_cleanup(); } /* * usage: install URL - * where: URL = (tftp|file)://[host]/ + * where: URL = tftp://[host]/ + * or file://[devname[:fstype]]/ */ static int install(char *pkgname) { static char buf[256]; struct fs_ops *proto; struct preloaded_file *fp; - char *s, *currdev; - const char *devname; + char *e, *s, *currdev; + char *devname; + size_t devnamelen; int error, fd, i, local; s = strstr(pkgname, "://"); if (s == NULL) goto invalid_url; i = s - pkgname; + s += 3; + if (*s == '\0') + goto invalid_url; + + devname = NULL; + devnamelen = 0; + proto = NULL; + local = 0; + if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { devname = "net0"; + devnamelen = 4; proto = &tftp_fsops; - local = 0; } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { currdev = getenv("currdev"); - if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) { - devname = "pxe0"; - proto = NULL; + local = 1; + + if (*s == '/') { /* file:/// */ + if (devname == NULL) + devname = currdev; + if (devname == NULL) + devname = "disk1"; + } else { /* file://devname[:fstype]/ */ + devname = s; + e = strchr(devname, '/'); + if (!e) + goto invalid_url; + devnamelen = e - devname; + s = e; /* consume devname */ + } + if ((e = strchr(devname, ':')) != NULL) { + /* could be :fstype */ + devnamelen = e - devname; + switch (e[1]) { + case '\0': /* just currdev */ + break; + case 'd': + proto = &dosfs_fsops; + break; #ifdef HOSTPROG - } else if (currdev != NULL && strcmp(currdev, "host0:") == 0) { - extern struct fs_ops host_fsops; + case 'h': + { + extern struct fs_ops host_fsops; - devname = "host0"; - proto = &host_fsops; + proto = &host_fsops; + } + break; #endif - } else { - devname = "disk1"; + case 'u': + proto = &ufs_fsops; + break; + } + } + if (proto == NULL && strncmp(devname, "disk", 4) == 0) { proto = &dosfs_fsops; } - local = 1; - } else - goto invalid_url; + } - s += 3; - if (*s == '\0') + if (devname == NULL) goto invalid_url; + if (devnamelen == 0) { + /* default is currdev which ends with ':' */ + devnamelen = strlen(devname); + if (devname[devnamelen - 1] == ':') + devnamelen--; + } + if (*s != '/' ) { if (local) goto invalid_url; pkgname = strchr(s, '/'); if (pkgname == NULL) goto invalid_url; *pkgname = '\0'; servip.s_addr = inet_addr(s); if (servip.s_addr == htonl(INADDR_NONE)) goto invalid_url; setenv("serverip", inet_ntoa(servip), 1); if (proto == &tftp_fsops) { tftpip.s_addr = servip.s_addr; } *pkgname = '/'; } else pkgname = s; - if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) { + i = snprintf(buf, sizeof(buf), "%.*s:%s", + (int) devnamelen, devname, pkgname); + if (i >= (int) sizeof(buf)) { command_errmsg = "package name too long"; return (CMD_ERROR); } - sprintf(buf, "%s:%s", devname, pkgname); setenv("install_package", buf, 1); error = pkgfs_init(buf, proto); if (error) { command_errmsg = "cannot open package"; goto fail; } /* * Point of no return: unload anything that may have been * loaded and prune the environment from harmful variables. */ unload(); unsetenv("vfs.root.mountfrom"); /* * read the metatags file. */ fd = open("/metatags", O_RDONLY); if (fd != -1) { error = read_metatags(fd); close(fd); if (error) { command_errmsg = "cannot load metatags"; goto fail; } } s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; error = mod_loadkld(s, 0, NULL); if (error) { command_errmsg = "cannot load kernel from package"; goto fail; } /* If there is a loader.rc in the package, execute it */ s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc; fd = open(s, O_RDONLY); if (fd != -1) { close(fd); error = inter_include(s); if (error == CMD_ERROR) goto fail; } i = 0; while (inst_modules != NULL && inst_modules[i] != NULL) { error = mod_loadkld(inst_modules[i], 0, NULL); if (error) { command_errmsg = "cannot load module(s) from package"; goto fail; } i++; } s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; if (file_loadraw(s, "mfs_root", 1) == NULL) { error = errno; command_errmsg = "cannot load root file system"; goto fail; } cleanup(); fp = file_findfile(NULL, NULL); if (fp != NULL) file_formats[fp->f_loader]->l_exec(fp); error = CMD_ERROR; command_errmsg = "unable to start installation"; fail: sprintf(buf, "%s (error %d)", command_errmsg, error); cleanup(); unload(); exclusive_file_system = NULL; command_errmsg = buf; /* buf is static. */ return (CMD_ERROR); invalid_url: command_errmsg = "invalid URL"; return (CMD_ERROR); } static int command_install(int argc, char *argv[]) { int argidx; unsetenv("install_format"); argidx = 1; while (1) { if (argc == argidx) { command_errmsg = "usage: install [--format] "; return (CMD_ERROR); } if (!strcmp(argv[argidx], "--format")) { setenv("install_format", "yes", 1); argidx++; continue; } break; } return (install(argv[argidx])); } Index: stable/12/stand/uboot/lib/net.c =================================================================== --- stable/12/stand/uboot/lib/net.c (revision 364960) +++ stable/12/stand/uboot/lib/net.c (revision 364961) @@ -1,364 +1,364 @@ /*- * Copyright (c) 2000-2001 Benno Rice * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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 "api_public.h" #include "glue.h" #include "libuboot.h" #include "dev_net.h" static int net_probe(struct netif *, void *); static int net_match(struct netif *, void *); static void net_init(struct iodesc *, void *); static ssize_t net_get(struct iodesc *, void **, time_t); static ssize_t net_put(struct iodesc *, void *, size_t); static void net_end(struct netif *); extern struct netif_stats net_stats[]; struct netif_dif net_ifs[] = { /* dif_unit dif_nsel dif_stats dif_private */ { 0, 1, &net_stats[0], 0, }, }; struct netif_stats net_stats[nitems(net_ifs)]; struct netif_driver uboot_net = { "uboot_eth", /* netif_bname */ net_match, /* netif_match */ net_probe, /* netif_probe */ net_init, /* netif_init */ net_get, /* netif_get */ net_put, /* netif_put */ net_end, /* netif_end */ net_ifs, /* netif_ifs */ nitems(net_ifs) /* netif_nifs */ }; struct uboot_softc { uint32_t sc_pad; uint8_t sc_rxbuf[ETHER_MAX_LEN]; uint8_t sc_txbuf[ETHER_MAX_LEN + PKTALIGN]; uint8_t *sc_txbufp; int sc_handle; /* device handle for ub_dev_xxx */ }; static struct uboot_softc uboot_softc; /* * get_env_net_params() * * Attempt to obtain all the parms we need for netbooting from the U-Boot * environment. If we fail to obtain the values it may still be possible to * netboot; the net_dev code will attempt to get the values from bootp, rarp, * and other such sources. * * If rootip.s_addr is non-zero net_dev assumes the required global variables * are set and skips the bootp inquiry. For that reason, we don't set rootip * until we've verified that we have at least the minimum required info. * * This is called from netif_init() which can result in it getting called * multiple times, by design. The network code at higher layers zeroes out * rootip when it closes a network interface, so if it gets opened again we have * to obtain all this info again. */ static void get_env_net_params() { char *envstr; in_addr_t rootaddr, serveraddr; /* * Silently get out right away if we don't have rootpath, because none * of the other info we obtain below is sufficient to boot without it. * * If we do have rootpath, copy it into the global var and also set * dhcp.root-path in the env. If we don't get all the other info from * the u-boot env below, we will still try dhcp/bootp, but the server- * provided path will not replace the user-provided value we set here. */ if ((envstr = ub_env_get("rootpath")) == NULL) return; strlcpy(rootpath, envstr, sizeof(rootpath)); setenv("dhcp.root-path", rootpath, 0); /* * Our own IP address must be valid. Silently get out if it's not set, * but whine if it's there and we can't parse it. */ if ((envstr = ub_env_get("ipaddr")) == NULL) return; if ((myip.s_addr = inet_addr(envstr)) == INADDR_NONE) { printf("Could not parse ipaddr '%s'\n", envstr); return; } /* * Netmask is optional, default to the "natural" netmask for our IP, but * whine if it was provided and we couldn't parse it. */ if ((envstr = ub_env_get("netmask")) != NULL && (netmask = inet_addr(envstr)) == INADDR_NONE) { printf("Could not parse netmask '%s'\n", envstr); } if (netmask == INADDR_NONE) { if (IN_CLASSA(myip.s_addr)) netmask = IN_CLASSA_NET; else if (IN_CLASSB(myip.s_addr)) netmask = IN_CLASSB_NET; else netmask = IN_CLASSC_NET; } /* * Get optional serverip before rootpath; the latter can override it. * Whine only if it's present but can't be parsed. */ serveraddr = INADDR_NONE; if ((envstr = ub_env_get("serverip")) != NULL) { if ((serveraddr = inet_addr(envstr)) == INADDR_NONE) printf("Could not parse serverip '%s'\n", envstr); } /* * There must be a rootpath. It may be ip:/path or it may be just the * path in which case the ip needs to be in serverip. */ rootaddr = net_parse_rootpath(); if (rootaddr == INADDR_NONE) rootaddr = serveraddr; if (rootaddr == INADDR_NONE) { printf("No server address for rootpath '%s'\n", envstr); return; } rootip.s_addr = rootaddr; /* * Gateway IP is optional unless rootip is on a different net in which * case whine if it's missing or we can't parse it, and set rootip addr * to zero, which signals to other network code that network params * aren't set (so it will try dhcp, bootp, etc). */ envstr = ub_env_get("gatewayip"); if (!SAMENET(myip, rootip, netmask)) { if (envstr == NULL) { printf("Need gatewayip for a root server on a " "different network.\n"); rootip.s_addr = 0; return; } - if ((gateip.s_addr = inet_addr(envstr) == INADDR_NONE)) { + if ((gateip.s_addr = inet_addr(envstr)) == INADDR_NONE) { printf("Could not parse gatewayip '%s'\n", envstr); rootip.s_addr = 0; return; } } } static int net_match(struct netif *nif, void *machdep_hint) { char **a = (char **)machdep_hint; if (memcmp("net", *a, 3) == 0) return (1); printf("net_match: could not match network device\n"); return (0); } static int net_probe(struct netif *nif, void *machdep_hint) { struct device_info *di; int i; for (i = 0; i < devs_no; i++) if ((di = ub_dev_get(i)) != NULL) if (di->type == DEV_TYP_NET) break; if (i == devs_no) { printf("net_probe: no network devices found, maybe not" " enumerated yet..?\n"); return (-1); } #if defined(NETIF_DEBUG) printf("net_probe: network device found: %d\n", i); #endif uboot_softc.sc_handle = i; return (0); } static ssize_t net_put(struct iodesc *desc, void *pkt, size_t len) { struct netif *nif = desc->io_netif; struct uboot_softc *sc = nif->nif_devdata; size_t sendlen; ssize_t rv; #if defined(NETIF_DEBUG) struct ether_header *eh; printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len); eh = pkt; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); printf("type: 0x%x\n", eh->ether_type & 0xffff); #endif if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) { sendlen = ETHER_MIN_LEN - ETHER_CRC_LEN; bzero(sc->sc_txbufp, sendlen); } else sendlen = len; memcpy(sc->sc_txbufp, pkt, len); rv = ub_dev_send(sc->sc_handle, sc->sc_txbufp, sendlen); #if defined(NETIF_DEBUG) printf("net_put: ub_send returned %d\n", rv); #endif if (rv == 0) rv = len; else rv = -1; return (rv); } static ssize_t net_get(struct iodesc *desc, void **pkt, time_t timeout) { struct netif *nif = desc->io_netif; struct uboot_softc *sc = nif->nif_devdata; time_t t; int err, rlen; size_t len; char *buf; #if defined(NETIF_DEBUG) printf("net_get: pkt %p, timeout %d\n", pkt, timeout); #endif t = getsecs(); len = sizeof(sc->sc_rxbuf); do { err = ub_dev_recv(sc->sc_handle, sc->sc_rxbuf, len, &rlen); if (err != 0) { printf("net_get: ub_dev_recv() failed, error=%d\n", err); rlen = 0; break; } } while ((rlen == -1 || rlen == 0) && (getsecs() - t < timeout)); #if defined(NETIF_DEBUG) printf("net_get: received len %d (%x)\n", rlen, rlen); #endif if (rlen > 0) { buf = malloc(rlen + ETHER_ALIGN); if (buf == NULL) return (-1); memcpy(buf + ETHER_ALIGN, sc->sc_rxbuf, rlen); *pkt = buf; return ((ssize_t)rlen); } return (-1); } static void net_init(struct iodesc *desc, void *machdep_hint) { struct netif *nif = desc->io_netif; struct uboot_softc *sc; struct device_info *di; int err; sc = nif->nif_devdata = &uboot_softc; if ((err = ub_dev_open(sc->sc_handle)) != 0) panic("%s%d: initialisation failed with error %d", nif->nif_driver->netif_bname, nif->nif_unit, err); /* Get MAC address */ di = ub_dev_get(sc->sc_handle); memcpy(desc->myea, di->di_net.hwaddr, 6); if (memcmp (desc->myea, "\0\0\0\0\0\0", 6) == 0) { panic("%s%d: empty ethernet address!", nif->nif_driver->netif_bname, nif->nif_unit); } /* Attempt to get netboot params from the u-boot env. */ get_env_net_params(); if (myip.s_addr != 0) desc->myip = myip; #if defined(NETIF_DEBUG) printf("network: %s%d attached to %s\n", nif->nif_driver->netif_bname, nif->nif_unit, ether_sprintf(desc->myea)); #endif /* Set correct alignment for TX packets */ sc->sc_txbufp = sc->sc_txbuf; if ((unsigned long)sc->sc_txbufp % PKTALIGN) sc->sc_txbufp += PKTALIGN - (unsigned long)sc->sc_txbufp % PKTALIGN; } static void net_end(struct netif *nif) { struct uboot_softc *sc = nif->nif_devdata; int err; if ((err = ub_dev_close(sc->sc_handle)) != 0) panic("%s%d: net_end failed with error %d", nif->nif_driver->netif_bname, nif->nif_unit, err); } Index: stable/12/sys/kern/kern_environment.c =================================================================== --- stable/12/sys/kern/kern_environment.c (revision 364960) +++ stable/12/sys/kern/kern_environment.c (revision 364961) @@ -1,964 +1,1008 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1998 Michael Smith * 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. */ /* * The unified bootloader passes us a pointer to a preserved copy of * bootstrap/kernel environment variables. We convert them to a * dynamic array of strings later when the VM subsystem is up. * * We make these available through the kenv(2) syscall for userland * and through kern_getenv()/freeenv() kern_setenv() kern_unsetenv() testenv() for * the kernel. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *_getenv_dynamic_locked(const char *name, int *idx); static char *_getenv_dynamic(const char *name, int *idx); static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment"); #define KENV_SIZE 512 /* Maximum number of environment strings */ +static uma_zone_t kenv_zone; +static int kenv_mvallen = KENV_MVALLEN; + /* pointer to the config-generated static environment */ char *kern_envp; /* pointer to the md-static environment */ char *md_envp; static int md_env_len; static int md_env_pos; static char *kernenv_next(char *); /* dynamic environment variables */ char **kenvp; struct mtx kenv_lock; /* * No need to protect this with a mutex since SYSINITS are single threaded. */ bool dynamic_kenv; #define KENV_CHECK if (!dynamic_kenv) \ panic("%s: called before SI_SUB_KMEM", __func__) +static char *getenv_string_buffer(const char *); + int sys_kenv(td, uap) struct thread *td; struct kenv_args /* { int what; const char *name; char *value; int len; } */ *uap; { char *name, *value, *buffer = NULL; size_t len, done, needed, buflen; int error, i; KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false")); error = 0; if (uap->what == KENV_DUMP) { #ifdef MAC error = mac_kenv_check_dump(td->td_ucred); if (error) return (error); #endif done = needed = 0; buflen = uap->len; - if (buflen > KENV_SIZE * (KENV_MNAMELEN + KENV_MVALLEN + 2)) + if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2)) buflen = KENV_SIZE * (KENV_MNAMELEN + - KENV_MVALLEN + 2); + kenv_mvallen + 2); if (uap->len > 0 && uap->value != NULL) buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO); mtx_lock(&kenv_lock); for (i = 0; kenvp[i] != NULL; i++) { len = strlen(kenvp[i]) + 1; needed += len; len = min(len, buflen - done); /* * If called with a NULL or insufficiently large * buffer, just keep computing the required size. */ if (uap->value != NULL && buffer != NULL && len > 0) { bcopy(kenvp[i], buffer + done, len); done += len; } } mtx_unlock(&kenv_lock); if (buffer != NULL) { error = copyout(buffer, uap->value, done); free(buffer, M_TEMP); } td->td_retval[0] = ((done == needed) ? 0 : needed); return (error); } switch (uap->what) { case KENV_SET: error = priv_check(td, PRIV_KENV_SET); if (error) return (error); break; case KENV_UNSET: error = priv_check(td, PRIV_KENV_UNSET); if (error) return (error); break; } name = malloc(KENV_MNAMELEN + 1, M_TEMP, M_WAITOK); error = copyinstr(uap->name, name, KENV_MNAMELEN + 1, NULL); if (error) goto done; switch (uap->what) { case KENV_GET: #ifdef MAC error = mac_kenv_check_get(td->td_ucred, name); if (error) goto done; #endif value = kern_getenv(name); if (value == NULL) { error = ENOENT; goto done; } len = strlen(value) + 1; if (len > uap->len) len = uap->len; error = copyout(value, uap->value, len); freeenv(value); if (error) goto done; td->td_retval[0] = len; break; case KENV_SET: len = uap->len; if (len < 1) { error = EINVAL; goto done; } - if (len > KENV_MVALLEN + 1) - len = KENV_MVALLEN + 1; + if (len > kenv_mvallen + 1) + len = kenv_mvallen + 1; value = malloc(len, M_TEMP, M_WAITOK); error = copyinstr(uap->value, value, len, NULL); if (error) { free(value, M_TEMP); goto done; } #ifdef MAC error = mac_kenv_check_set(td->td_ucred, name, value); if (error == 0) #endif kern_setenv(name, value); free(value, M_TEMP); break; case KENV_UNSET: #ifdef MAC error = mac_kenv_check_unset(td->td_ucred, name); if (error) goto done; #endif error = kern_unsetenv(name); if (error) error = ENOENT; break; default: error = EINVAL; break; } done: free(name, M_TEMP); return (error); } /* * Populate the initial kernel environment. * * This is called very early in MD startup, either to provide a copy of the * environment obtained from a boot loader, or to provide an empty buffer into * which MD code can store an initial environment using kern_setenv() calls. * * kern_envp is set to the static_env generated by config(8). This implements * the env keyword described in config(5). * * If len is non-zero, the caller is providing an empty buffer. The caller will * subsequently use kern_setenv() to add up to len bytes of initial environment * before the dynamic environment is available. * * If len is zero, the caller is providing a pre-loaded buffer containing * environment strings. Additional strings cannot be added until the dynamic * environment is available. The memory pointed to must remain stable at least * until sysinit runs init_dynamic_kenv() and preferably until after SI_SUB_KMEM * is finished so that subr_hints routines may continue to use it until the * environments have been fully merged at the end of the pass. If no initial * environment is available from the boot loader, passing a NULL pointer allows * the static_env to be installed if it is configured. In this case, any call * to kern_setenv() prior to the setup of the dynamic environment will result in * a panic. */ void init_static_kenv(char *buf, size_t len) { char *eval; KASSERT(!dynamic_kenv, ("kenv: dynamic_kenv already initialized")); /* * Suitably sized means it must be able to hold at least one empty * variable, otherwise things go belly up if a kern_getenv call is * made without a prior call to kern_setenv as we have a malformed * environment. */ KASSERT(len == 0 || len >= 2, ("kenv: static env must be initialized or suitably sized")); KASSERT(len == 0 || (*buf == '\0' && *(buf + 1) == '\0'), ("kenv: sized buffer must be initially empty")); /* * We may be called twice, with the second call needed to relocate * md_envp after enabling paging. md_envp is then garbage if it is * not null and the relocation will move it. Discard it so as to * not crash using its old value in our first call to kern_getenv(). * * The second call gives the same environment as the first except * in silly configurations where the static env disables itself. * * Other env calls don't handle possibly-garbage pointers, so must * not be made between enabling paging and calling here. */ md_envp = NULL; md_env_len = 0; md_env_pos = 0; /* * Give the static environment a chance to disable the loader(8) * environment first. This is done with loader_env.disabled=1. * * static_env and static_hints may both be disabled, but in slightly * different ways. For static_env, we just don't setup kern_envp and * it's as if a static env wasn't even provided. For static_hints, * we effectively zero out the buffer to stop the rest of the kernel * from being able to use it. * * We're intentionally setting this up so that static_hints.disabled may * be specified in either the MD env or the static env. This keeps us * consistent in our new world view. * * As a warning, the static environment may not be disabled in any way * if the static environment has disabled the loader environment. */ kern_envp = static_env; eval = kern_getenv("loader_env.disabled"); if (eval == NULL || strcmp(eval, "1") != 0) { md_envp = buf; md_env_len = len; md_env_pos = 0; eval = kern_getenv("static_env.disabled"); if (eval != NULL && strcmp(eval, "1") == 0) { kern_envp[0] = '\0'; kern_envp[1] = '\0'; } } eval = kern_getenv("static_hints.disabled"); if (eval != NULL && strcmp(eval, "1") == 0) { static_hints[0] = '\0'; static_hints[1] = '\0'; } } static void init_dynamic_kenv_from(char *init_env, int *curpos) { char *cp, *cpnext, *eqpos, *found; size_t len; int i; if (init_env && *init_env != '\0') { found = NULL; i = *curpos; for (cp = init_env; cp != NULL; cp = cpnext) { cpnext = kernenv_next(cp); len = strlen(cp) + 1; - if (len > KENV_MNAMELEN + 1 + KENV_MVALLEN + 1) { + if (len > KENV_MNAMELEN + 1 + kenv_mvallen + 1) { printf( "WARNING: too long kenv string, ignoring %s\n", cp); goto sanitize; } eqpos = strchr(cp, '='); if (eqpos == NULL) { printf( "WARNING: malformed static env value, ignoring %s\n", cp); goto sanitize; } *eqpos = 0; /* * De-dupe the environment as we go. We don't add the * duplicated assignments because config(8) will flip * the order of the static environment around to make * kernel processing match the order of specification * in the kernel config. */ found = _getenv_dynamic_locked(cp, NULL); *eqpos = '='; if (found != NULL) goto sanitize; if (i > KENV_SIZE) { printf( "WARNING: too many kenv strings, ignoring %s\n", cp); goto sanitize; } kenvp[i] = malloc(len, M_KENV, M_WAITOK); strcpy(kenvp[i++], cp); sanitize: explicit_bzero(cp, len - 1); } *curpos = i; } } /* * Setup the dynamic kernel environment. */ static void init_dynamic_kenv(void *data __unused) { int dynamic_envpos; + int size; + TUNABLE_INT_FETCH("kenv_mvallen", &kenv_mvallen); + size = KENV_MNAMELEN + 1 + kenv_mvallen + 1; + + kenv_zone = uma_zcreate("kenv", size, NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV, M_WAITOK | M_ZERO); dynamic_envpos = 0; init_dynamic_kenv_from(md_envp, &dynamic_envpos); init_dynamic_kenv_from(kern_envp, &dynamic_envpos); kenvp[dynamic_envpos] = NULL; mtx_init(&kenv_lock, "kernel environment", NULL, MTX_DEF); dynamic_kenv = true; } SYSINIT(kenv, SI_SUB_KMEM + 1, SI_ORDER_FIRST, init_dynamic_kenv, NULL); void freeenv(char *env) { if (dynamic_kenv && env != NULL) { explicit_bzero(env, strlen(env)); - free(env, M_KENV); + uma_zfree(kenv_zone, env); } } /* * Internal functions for string lookup. */ static char * _getenv_dynamic_locked(const char *name, int *idx) { char *cp; int len, i; len = strlen(name); for (cp = kenvp[0], i = 0; cp != NULL; cp = kenvp[++i]) { if ((strncmp(cp, name, len) == 0) && (cp[len] == '=')) { if (idx != NULL) *idx = i; return (cp + len + 1); } } return (NULL); } static char * _getenv_dynamic(const char *name, int *idx) { mtx_assert(&kenv_lock, MA_OWNED); return (_getenv_dynamic_locked(name, idx)); } static char * _getenv_static_from(char *chkenv, const char *name) { char *cp, *ep; int len; for (cp = chkenv; cp != NULL; cp = kernenv_next(cp)) { for (ep = cp; (*ep != '=') && (*ep != 0); ep++) ; if (*ep != '=') continue; len = ep - cp; ep++; if (!strncmp(name, cp, len) && name[len] == 0) return (ep); } return (NULL); } static char * _getenv_static(const char *name) { char *val; val = _getenv_static_from(md_envp, name); if (val != NULL) return (val); val = _getenv_static_from(kern_envp, name); if (val != NULL) return (val); return (NULL); } /* * Look up an environment variable by name. * Return a pointer to the string if found. * The pointer has to be freed with freeenv() * after use. */ char * kern_getenv(const char *name) { - char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; char *ret; if (dynamic_kenv) { - if (getenv_string(name, buf, sizeof(buf))) { - ret = strdup(buf, M_KENV); - } else { - ret = NULL; + ret = getenv_string_buffer(name); + if (ret == NULL) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "getenv"); } } else ret = _getenv_static(name); return (ret); } /* * Test if an environment variable is defined. */ int testenv(const char *name) { char *cp; if (dynamic_kenv) { mtx_lock(&kenv_lock); cp = _getenv_dynamic(name, NULL); mtx_unlock(&kenv_lock); } else cp = _getenv_static(name); if (cp != NULL) return (1); return (0); } /* * Set an environment variable in the MD-static environment. This cannot * feasibly be done on config(8)-generated static environments as they don't * generally include space for extra variables. */ static int setenv_static(const char *name, const char *value) { int len; if (md_env_pos >= md_env_len) return (-1); /* Check space for x=y and two nuls */ len = strlen(name) + strlen(value); if (len + 3 < md_env_len - md_env_pos) { len = sprintf(&md_envp[md_env_pos], "%s=%s", name, value); md_env_pos += len+1; md_envp[md_env_pos] = '\0'; return (0); } else return (-1); } /* * Set an environment variable by name. */ int kern_setenv(const char *name, const char *value) { char *buf, *cp, *oldenv; int namelen, vallen, i; if (!dynamic_kenv && md_env_len > 0) return (setenv_static(name, value)); KENV_CHECK; namelen = strlen(name) + 1; if (namelen > KENV_MNAMELEN + 1) return (-1); vallen = strlen(value) + 1; - if (vallen > KENV_MVALLEN + 1) + if (vallen > kenv_mvallen + 1) return (-1); buf = malloc(namelen + vallen, M_KENV, M_WAITOK); sprintf(buf, "%s=%s", name, value); mtx_lock(&kenv_lock); cp = _getenv_dynamic(name, &i); if (cp != NULL) { oldenv = kenvp[i]; kenvp[i] = buf; mtx_unlock(&kenv_lock); free(oldenv, M_KENV); } else { /* We add the option if it wasn't found */ for (i = 0; (cp = kenvp[i]) != NULL; i++) ; /* Bounds checking */ if (i < 0 || i >= KENV_SIZE) { free(buf, M_KENV); mtx_unlock(&kenv_lock); return (-1); } kenvp[i] = buf; kenvp[i + 1] = NULL; mtx_unlock(&kenv_lock); } return (0); } /* * Unset an environment variable string. */ int kern_unsetenv(const char *name) { char *cp, *oldenv; int i, j; KENV_CHECK; mtx_lock(&kenv_lock); cp = _getenv_dynamic(name, &i); if (cp != NULL) { oldenv = kenvp[i]; for (j = i + 1; kenvp[j] != NULL; j++) kenvp[i++] = kenvp[j]; kenvp[i] = NULL; mtx_unlock(&kenv_lock); explicit_bzero(oldenv, strlen(oldenv)); free(oldenv, M_KENV); return (0); } mtx_unlock(&kenv_lock); return (-1); } /* + * Return a buffer containing the string value from an environment variable + */ +static char * +getenv_string_buffer(const char *name) +{ + char *cp, *ret; + int len; + + if (dynamic_kenv) { + len = KENV_MNAMELEN + 1 + kenv_mvallen + 1; + ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO); + mtx_lock(&kenv_lock); + cp = _getenv_dynamic(name, NULL); + if (cp != NULL) + strlcpy(ret, cp, len); + mtx_unlock(&kenv_lock); + if (cp == NULL) { + uma_zfree(kenv_zone, ret); + ret = NULL; + } + } else + ret = _getenv_static(name); + + return (ret); +} + +/* * Return a string value from an environment variable. */ int getenv_string(const char *name, char *data, int size) { char *cp; if (dynamic_kenv) { mtx_lock(&kenv_lock); cp = _getenv_dynamic(name, NULL); if (cp != NULL) strlcpy(data, cp, size); mtx_unlock(&kenv_lock); } else { cp = _getenv_static(name); if (cp != NULL) strlcpy(data, cp, size); } return (cp != NULL); } /* * Return an array of integers at the given type size and signedness. */ int getenv_array(const char *name, void *pdata, int size, int *psize, int type_size, bool allow_signed) { - char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; uint8_t shift; int64_t value; int64_t old; + char *buf; char *end; char *ptr; int n; + int rc; - if (getenv_string(name, buf, sizeof(buf)) == 0) + if ((buf = getenv_string_buffer(name)) == NULL) return (0); + rc = 0; /* assume failure */ /* get maximum number of elements */ size /= type_size; n = 0; for (ptr = buf; *ptr != 0; ) { value = strtoq(ptr, &end, 0); /* check if signed numbers are allowed */ if (value < 0 && !allow_signed) goto error; /* check for invalid value */ if (ptr == end) goto error; /* check for valid suffix */ switch (*end) { case 't': case 'T': shift = 40; end++; break; case 'g': case 'G': shift = 30; end++; break; case 'm': case 'M': shift = 20; end++; break; case 'k': case 'K': shift = 10; end++; break; case ' ': case '\t': case ',': case 0: shift = 0; break; default: /* garbage after numeric value */ goto error; } /* skip till next value, if any */ while (*end == '\t' || *end == ',' || *end == ' ') end++; /* update pointer */ ptr = end; /* apply shift */ old = value; value <<= shift; /* overflow check */ if ((value >> shift) != old) goto error; /* check for buffer overflow */ if (n >= size) goto error; /* store value according to type size */ switch (type_size) { case 1: if (allow_signed) { if (value < SCHAR_MIN || value > SCHAR_MAX) goto error; } else { if (value < 0 || value > UCHAR_MAX) goto error; } ((uint8_t *)pdata)[n] = (uint8_t)value; break; case 2: if (allow_signed) { if (value < SHRT_MIN || value > SHRT_MAX) goto error; } else { if (value < 0 || value > USHRT_MAX) goto error; } ((uint16_t *)pdata)[n] = (uint16_t)value; break; case 4: if (allow_signed) { if (value < INT_MIN || value > INT_MAX) goto error; } else { if (value > UINT_MAX) goto error; } ((uint32_t *)pdata)[n] = (uint32_t)value; break; case 8: ((uint64_t *)pdata)[n] = (uint64_t)value; break; default: goto error; } n++; } *psize = n * type_size; if (n != 0) - return (1); /* success */ + rc = 1; /* success */ error: - return (0); /* failure */ + if (dynamic_kenv) + uma_zfree(kenv_zone, buf); + return (rc); } /* * Return an integer value from an environment variable. */ int getenv_int(const char *name, int *data) { quad_t tmp; int rval; rval = getenv_quad(name, &tmp); if (rval) *data = (int) tmp; return (rval); } /* * Return an unsigned integer value from an environment variable. */ int getenv_uint(const char *name, unsigned int *data) { quad_t tmp; int rval; rval = getenv_quad(name, &tmp); if (rval) *data = (unsigned int) tmp; return (rval); } /* * Return an int64_t value from an environment variable. */ int getenv_int64(const char *name, int64_t *data) { quad_t tmp; int64_t rval; rval = getenv_quad(name, &tmp); if (rval) *data = (int64_t) tmp; return (rval); } /* * Return an uint64_t value from an environment variable. */ int getenv_uint64(const char *name, uint64_t *data) { quad_t tmp; uint64_t rval; rval = getenv_quad(name, &tmp); if (rval) *data = (uint64_t) tmp; return (rval); } /* * Return a long value from an environment variable. */ int getenv_long(const char *name, long *data) { quad_t tmp; int rval; rval = getenv_quad(name, &tmp); if (rval) *data = (long) tmp; return (rval); } /* * Return an unsigned long value from an environment variable. */ int getenv_ulong(const char *name, unsigned long *data) { quad_t tmp; int rval; rval = getenv_quad(name, &tmp); if (rval) *data = (unsigned long) tmp; return (rval); } /* * Return a quad_t value from an environment variable. */ int getenv_quad(const char *name, quad_t *data) { - char value[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1]; - char *vtp; + char *value, *vtp; quad_t iv; - if (!getenv_string(name, value, sizeof(value))) + value = getenv_string_buffer(name); + if (value == NULL) return (0); iv = strtoq(value, &vtp, 0); - if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) + if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { + freeenv(value); return (0); + } switch (vtp[0]) { case 't': case 'T': iv *= 1024; /* FALLTHROUGH */ case 'g': case 'G': iv *= 1024; /* FALLTHROUGH */ case 'm': case 'M': iv *= 1024; /* FALLTHROUGH */ case 'k': case 'K': iv *= 1024; case '\0': break; default: + freeenv(value); return (0); } + freeenv(value); *data = iv; return (1); } /* * Find the next entry after the one which (cp) falls within, return a * pointer to its start or NULL if there are no more. */ static char * kernenv_next(char *cp) { if (cp != NULL) { while (*cp != 0) cp++; cp++; if (*cp == 0) cp = NULL; } return (cp); } void tunable_int_init(void *data) { struct tunable_int *d = (struct tunable_int *)data; TUNABLE_INT_FETCH(d->path, d->var); } void tunable_long_init(void *data) { struct tunable_long *d = (struct tunable_long *)data; TUNABLE_LONG_FETCH(d->path, d->var); } void tunable_ulong_init(void *data) { struct tunable_ulong *d = (struct tunable_ulong *)data; TUNABLE_ULONG_FETCH(d->path, d->var); } void tunable_int64_init(void *data) { struct tunable_int64 *d = (struct tunable_int64 *)data; TUNABLE_INT64_FETCH(d->path, d->var); } void tunable_uint64_init(void *data) { struct tunable_uint64 *d = (struct tunable_uint64 *)data; TUNABLE_UINT64_FETCH(d->path, d->var); } void tunable_quad_init(void *data) { struct tunable_quad *d = (struct tunable_quad *)data; TUNABLE_QUAD_FETCH(d->path, d->var); } void tunable_str_init(void *data) { struct tunable_str *d = (struct tunable_str *)data; TUNABLE_STR_FETCH(d->path, d->var, d->size); } Index: stable/12 =================================================================== --- stable/12 (revision 364960) +++ stable/12 (revision 364961) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r361710,361933,361986,362124,362127,362231,363117,364443