jail.c revision 195870
121308Sache/*- 221308Sache * Copyright (c) 2009 James Gritton. 321308Sache * All rights reserved. 421308Sache * 521308Sache * Redistribution and use in source and binary forms, with or without 621308Sache * modification, are permitted provided that the following conditions 721308Sache * are met: 821308Sache * 1. Redistributions of source code must retain the above copyright 921308Sache * notice, this list of conditions and the following disclaimer. 1021308Sache * 2. Redistributions in binary form must reproduce the above copyright 1121308Sache * notice, this list of conditions and the following disclaimer in the 1221308Sache * documentation and/or other materials provided with the distribution. 1321308Sache * 1421308Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1521308Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1621308Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1721308Sache * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1821308Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1921308Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2021308Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2121308Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2221308Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2321308Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2421308Sache * SUCH DAMAGE. 2521308Sache */ 2621308Sache 2721308Sache#include <sys/cdefs.h> 2821308Sache__FBSDID("$FreeBSD: head/lib/libjail/jail.c 195870 2009-07-25 14:48:57Z jamie $"); 2921308Sache 3021308Sache#include <sys/param.h> 3121308Sache#include <sys/types.h> 3221308Sache#include <sys/jail.h> 3321308Sache#include <sys/socket.h> 3421308Sache#include <sys/sysctl.h> 3521308Sache 3621308Sache#include <arpa/inet.h> 3721308Sache#include <netinet/in.h> 3821308Sache 3921308Sache#include <errno.h> 4021308Sache#include <inttypes.h> 4121308Sache#include <stdio.h> 4221308Sache#include <stdarg.h> 4321308Sache#include <stdlib.h> 4421308Sache#include <string.h> 4521308Sache 4621308Sache#include "jail.h" 4721308Sache 4821308Sache#define SJPARAM "security.jail.param" 4921308Sache 5021308Sache#define JPS_IN_ADDR 1 5121308Sache#define JPS_IN6_ADDR 2 5221308Sache 5321308Sache#define ARRAY_SANITY 5 5421308Sache#define ARRAY_SLOP 5 5521308Sache 5621308Sache 5721308Sachestatic int jailparam_import_enum(const char **values, int nvalues, 5821308Sache const char *valstr, size_t valsize, int *value); 5921308Sachestatic int jailparam_vlist(struct jailparam **jpp, va_list ap); 6021308Sachestatic int jailparam_type(struct jailparam *jp); 6121308Sachestatic char *noname(const char *name); 6221308Sachestatic char *nononame(const char *name); 6321308Sache 6421308Sachechar jail_errmsg[JAIL_ERRMSGLEN]; 6521308Sache 6621308Sachestatic const char *bool_values[] = { "false", "true" }; 6721308Sachestatic const char *jailsys_values[] = { "disable", "new", "inherit" }; 6821308Sache 6921308Sache 7021308Sache/* 7121308Sache * Import a null-terminated parameter list and set a jail with the flags 7221308Sache * and parameters. 7321308Sache */ 7421308Sacheint 7521308Sachejail_setv(int flags, ...) 7621308Sache{ 7721308Sache va_list ap; 7821308Sache struct jailparam *jp; 7921308Sache int njp; 8021308Sache 8121308Sache va_start(ap, flags); 8221308Sache njp = jailparam_vlist(&jp, ap); 8321308Sache va_end(ap); 8421308Sache if (njp < 0) 8521308Sache return (njp); 8621308Sache return (jailparam_set(jp, njp, flags)); 8721308Sache} 8821308Sache 8921308Sache/* 9021308Sache * Read a null-terminated parameter list, get the referenced jail, and export 9121308Sache * the parameters to the list. 9226497Sache */ 9321308Sacheint 9421308Sachejail_getv(int flags, ...) 9521308Sache{ 9626497Sache va_list ap, tap; 9721308Sache struct jailparam *jp; 9821308Sache char *valarg; 9926497Sache const char *value; 10026497Sache int njp, i, jid, namekey, zero; 10126497Sache 10221308Sache va_start(ap, flags); 10321308Sache va_copy(tap, ap); 10421308Sache njp = jailparam_vlist(&jp, tap); 10521308Sache va_end(tap); 10621308Sache if (njp < 0) 10721308Sache return (njp); 10821308Sache /* 10921308Sache * See if the name is the search key. If so, we don't want to write 11021308Sache * it back in case it's a read-only string. 11121308Sache */ 11221308Sache namekey = 1; 11321308Sache zero = 0; 11421308Sache for (i = 0; i < njp; i++) { 11521308Sache if (!strcmp(jp->jp_name, "lastjid") || 11621308Sache (!strcmp(jp->jp_name, "jid") && 11721308Sache memcmp(jp->jp_value, &zero, sizeof(zero)))) 11821308Sache namekey = 0; 11921308Sache } 12021308Sache jid = jailparam_get(jp, njp, flags); 12121308Sache if (jid < 0) { 12221308Sache va_end(ap); 12321308Sache return (-1); 12421308Sache } 12521308Sache for (i = 0; i < njp; i++) { 12621308Sache (void)va_arg(ap, char *); 12721308Sache value = jailparam_export(jp + i); 12821308Sache if (value == NULL) { 12921308Sache va_end(ap); 13021308Sache return (-1); 13121308Sache } 13221308Sache valarg = va_arg(ap, char *); 13321308Sache if (!namekey || strcmp(jp[i].jp_name, "name")) 13421308Sache /* It's up to the caller to ensure there's room. */ 13521308Sache strcpy(valarg, value); 13621308Sache } 13721308Sache va_end(ap); 13821308Sache return (jid); 13921308Sache} 14021308Sache 14121308Sache/* 14221308Sache * Return a list of all known parameters. 14321308Sache */ 14421308Sacheint 14521308Sachejailparam_all(struct jailparam **jpp) 14621308Sache{ 14721308Sache struct jailparam *jp; 14821308Sache size_t mlen1, mlen2, buflen; 14921308Sache int njp, nlist; 15021308Sache int mib1[CTL_MAXNAME], mib2[CTL_MAXNAME - 2]; 15121308Sache char buf[MAXPATHLEN]; 15221308Sache 15321308Sache njp = 0; 15421308Sache nlist = 32; 15521308Sache jp = malloc(nlist * sizeof(*jp)); 15621308Sache if (jp == NULL) { 15721308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 15821308Sache return (-1); 15921308Sache } 16021308Sache mib1[0] = 0; 16121308Sache mib1[1] = 2; 16221308Sache mlen1 = CTL_MAXNAME - 2; 16321308Sache if (sysctlnametomib(SJPARAM, mib1 + 2, &mlen1) < 0) { 16421308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 16521308Sache "sysctlnametomib(" SJPARAM "): %s", strerror(errno)); 16621308Sache goto error; 16721308Sache } 16821308Sache for (;; njp++) { 16921308Sache /* Get the next parameter. */ 17021308Sache mlen2 = sizeof(mib2); 17121308Sache if (sysctl(mib1, mlen1 + 2, mib2, &mlen2, NULL, 0) < 0) { 17221308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 17321308Sache "sysctl(0.2): %s", strerror(errno)); 17421308Sache goto error; 17521308Sache } 17621308Sache if (mib2[0] != mib1[2] || mib2[1] != mib1[3] || 17721308Sache mib2[2] != mib1[4]) 17821308Sache break; 17921308Sache /* Convert it to an ascii name. */ 18021308Sache memcpy(mib1 + 2, mib2, mlen2); 18121308Sache mlen1 = mlen2 / sizeof(int); 18221308Sache mib1[1] = 1; 18321308Sache buflen = sizeof(buf); 18421308Sache if (sysctl(mib1, mlen1 + 2, buf, &buflen, NULL, 0) < 0) { 18521308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 18621308Sache "sysctl(0.1): %s", strerror(errno)); 18721308Sache goto error; 18821308Sache } 18921308Sache if (buf[buflen - 2] == '.') 19021308Sache buf[buflen - 2] = '\0'; 19121308Sache /* Add the parameter to the list */ 19221308Sache if (njp >= nlist) { 19321308Sache nlist *= 2; 19421308Sache jp = realloc(jp, nlist * sizeof(jp)); 19521308Sache if (jp == NULL) { 19621308Sache jailparam_free(jp, njp); 19721308Sache return (-1); 19821308Sache } 19921308Sache } 20021308Sache if (jailparam_init(jp + njp, buf + sizeof(SJPARAM)) < 0) 20121308Sache goto error; 20221308Sache if (jailparam_type(jp + njp) < 0) { 20321308Sache njp++; 20421308Sache goto error; 20521308Sache } 20621308Sache mib1[1] = 2; 20721308Sache } 20821308Sache jp = realloc(jp, njp * sizeof(*jp)); 20921308Sache *jpp = jp; 21026497Sache return (njp); 21121308Sache 21221308Sache error: 21321308Sache jailparam_free(jp, njp); 21421308Sache free(jp); 21521308Sache return (-1); 21621308Sache} 21721308Sache 21821308Sache/* 21921308Sache * Clear a jail parameter and copy in its name. 22021308Sache */ 22121308Sacheint 22221308Sachejailparam_init(struct jailparam *jp, const char *name) 22321308Sache{ 22421308Sache 22521308Sache memset(jp, 0, sizeof(*jp)); 22621308Sache jp->jp_name = strdup(name); 22721308Sache if (jp->jp_name == NULL) { 22821308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 22921308Sache return (-1); 23021308Sache } 23121308Sache return (0); 23221308Sache} 23321308Sache 23421308Sache/* 23521308Sache * Put a name and value into a jail parameter element, converting the value 23621308Sache * to internal form. 23721308Sache */ 23821308Sacheint 23921308Sachejailparam_import(struct jailparam *jp, const char *value) 24021308Sache{ 24121308Sache char *p, *ep, *tvalue; 24221308Sache const char *avalue; 24321308Sache int i, nval, fw; 24421308Sache 24521308Sache if (!jp->jp_ctltype && jailparam_type(jp) < 0) 24621308Sache return (-1); 24721308Sache if (value == NULL) 24821308Sache return (0); 24921308Sache if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) { 25021308Sache jp->jp_value = strdup(value); 25121308Sache if (jp->jp_value == NULL) { 25221308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 25321308Sache return (-1); 25421308Sache } 25521308Sache return (0); 25621308Sache } 25721308Sache nval = 1; 25821308Sache if (jp->jp_elemlen) { 25921308Sache if (value[0] == '\0' || (value[0] == '-' && value[1] == '\0')) { 26021308Sache jp->jp_value = strdup(""); 26121308Sache if (jp->jp_value == NULL) { 26221308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 26321308Sache return (-1); 26421308Sache } 26521308Sache jp->jp_valuelen = 0; 26621308Sache return (0); 26721308Sache } 26821308Sache for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) 26921308Sache nval++; 27021308Sache jp->jp_valuelen = jp->jp_elemlen * nval; 27121308Sache } 27221308Sache jp->jp_value = malloc(jp->jp_valuelen); 27321308Sache if (jp->jp_value == NULL) { 27421308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 27521308Sache return (-1); 27621308Sache } 27721308Sache avalue = value; 27821308Sache for (i = 0; i < nval; i++) { 27921308Sache fw = nval == 1 ? strlen(avalue) : strcspn(avalue, ","); 28021308Sache switch (jp->jp_ctltype & CTLTYPE) { 28121308Sache case CTLTYPE_INT: 28221308Sache if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) { 28321308Sache if (!jailparam_import_enum(bool_values, 2, 28421308Sache avalue, fw, &((int *)jp->jp_value)[i])) { 28521308Sache snprintf(jail_errmsg, 28621308Sache JAIL_ERRMSGLEN, "%s: " 28721308Sache "unknown boolean value \"%.*s\"", 28821308Sache jp->jp_name, fw, avalue); 28921308Sache errno = EINVAL; 29021308Sache goto error; 29121308Sache } 29221308Sache break; 29321308Sache } 29421308Sache if (jp->jp_flags & JP_JAILSYS) { 29521308Sache /* 29621308Sache * Allow setting a jailsys parameter to "new" 29721308Sache * in a booleanesque fashion. 29821308Sache */ 29921308Sache if (value[0] == '\0') 30021308Sache ((int *)jp->jp_value)[i] = JAIL_SYS_NEW; 30121308Sache else if (!jailparam_import_enum(jailsys_values, 30221308Sache sizeof(jailsys_values) / 30321308Sache sizeof(jailsys_values[0]), avalue, fw, 30421308Sache &((int *)jp->jp_value)[i])) { 30521308Sache snprintf(jail_errmsg, 30621308Sache JAIL_ERRMSGLEN, "%s: " 30721308Sache "unknown jailsys value \"%.*s\"", 30821308Sache jp->jp_name, fw, avalue); 30921308Sache errno = EINVAL; 31021308Sache goto error; 31121308Sache } 31221308Sache break; 31321308Sache } 31421308Sache ((int *)jp->jp_value)[i] = strtol(avalue, &ep, 10); 31521308Sache integer_test: 31621308Sache if (ep != avalue + fw) { 31721308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 31821308Sache "%s: non-integer value \"%.*s\"", 31921308Sache jp->jp_name, fw, avalue); 32021308Sache errno = EINVAL; 32121308Sache goto error; 32221308Sache } 32321308Sache break; 32421308Sache case CTLTYPE_UINT: 32521308Sache ((unsigned *)jp->jp_value)[i] = 32621308Sache strtoul(avalue, &ep, 10); 32721308Sache goto integer_test; 32821308Sache case CTLTYPE_LONG: 32921308Sache ((long *)jp->jp_value)[i] = strtol(avalue, &ep, 10); 33021308Sache goto integer_test; 33121308Sache case CTLTYPE_ULONG: 33221308Sache ((unsigned long *)jp->jp_value)[i] = 33321308Sache strtoul(avalue, &ep, 10); 33421308Sache goto integer_test; 33521308Sache case CTLTYPE_QUAD: 33621308Sache ((int64_t *)jp->jp_value)[i] = 33721308Sache strtoimax(avalue, &ep, 10); 33821308Sache goto integer_test; 33921308Sache case CTLTYPE_STRUCT: 34021308Sache tvalue = alloca(fw + 1); 34121308Sache strlcpy(tvalue, avalue, fw + 1); 34221308Sache switch (jp->jp_structtype) { 34321308Sache case JPS_IN_ADDR: 34421308Sache if (inet_pton(AF_INET, tvalue, 34521308Sache &((struct in_addr *)jp->jp_value)[i]) != 1) 34621308Sache { 34721308Sache snprintf(jail_errmsg, 34821308Sache JAIL_ERRMSGLEN, 34921308Sache "%s: not an IPv4 address: %s", 35021308Sache jp->jp_name, tvalue); 35121308Sache errno = EINVAL; 35221308Sache goto error; 35321308Sache } 35421308Sache break; 35521308Sache case JPS_IN6_ADDR: 35621308Sache if (inet_pton(AF_INET6, tvalue, 35721308Sache &((struct in6_addr *)jp->jp_value)[i]) != 1) 35821308Sache { 35921308Sache snprintf(jail_errmsg, 36021308Sache JAIL_ERRMSGLEN, 36121308Sache "%s: not an IPv6 address: %s", 36221308Sache jp->jp_name, tvalue); 36321308Sache errno = EINVAL; 36421308Sache goto error; 36521308Sache } 36621308Sache break; 36721308Sache default: 36821308Sache goto unknown_type; 36921308Sache } 37021308Sache break; 37121308Sache default: 37221308Sache unknown_type: 37321308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 37421308Sache "unknown type for %s", jp->jp_name); 37521308Sache errno = ENOENT; 37621308Sache goto error; 37721308Sache } 37821308Sache avalue += fw + 1; 37921308Sache } 38021308Sache return (0); 38121308Sache 38221308Sache error: 38321308Sache free(jp->jp_value); 38421308Sache jp->jp_value = NULL; 38521308Sache return (-1); 38621308Sache} 38721308Sache 38821308Sachestatic int 38926497Sachejailparam_import_enum(const char **values, int nvalues, const char *valstr, 39021308Sache size_t valsize, int *value) 39121308Sache{ 39221308Sache char *ep; 39321308Sache int i; 39421308Sache 39521308Sache for (i = 0; i < nvalues; i++) 39621308Sache if (valsize == strlen(values[i]) && 39721308Sache !strncasecmp(valstr, values[i], valsize)) { 39821308Sache *value = i; 39921308Sache return 1; 40021308Sache } 40121308Sache *value = strtol(valstr, &ep, 10); 40226497Sache return (ep == valstr + valsize); 40321308Sache} 40421308Sache 40521308Sache/* 40621308Sache * Put a name and value into a jail parameter element, copying the value 40721308Sache * but not altering it. 40821308Sache */ 40921308Sacheint 41021308Sachejailparam_import_raw(struct jailparam *jp, void *value, size_t valuelen) 41121308Sache{ 41221308Sache 41321308Sache jp->jp_value = value; 41426497Sache jp->jp_valuelen = valuelen; 41526497Sache jp->jp_flags |= JP_RAWVALUE; 41626497Sache return (0); 41726497Sache} 41826497Sache 41926497Sache/* 42026497Sache * Run the jail_set and jail_get system calls on a parameter list. 42126497Sache */ 42226497Sacheint 42326497Sachejailparam_set(struct jailparam *jp, unsigned njp, int flags) 42426497Sache{ 42526497Sache struct iovec *jiov; 42626497Sache char *nname; 42726497Sache int i, jid, bool0; 42826497Sache unsigned j; 42926497Sache 43026497Sache jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1)); 43126497Sache bool0 = 0; 43226497Sache for (i = j = 0; j < njp; j++) { 43326497Sache jiov[i].iov_base = jp[j].jp_name; 43426497Sache jiov[i].iov_len = strlen(jp[j].jp_name) + 1; 43526497Sache i++; 43626497Sache if (jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) { 43726497Sache /* 43826497Sache * Set booleans without values. If one has a value of 43926497Sache * zero, change it to (or from) its "no" counterpart. 44026497Sache */ 44126497Sache jiov[i].iov_base = NULL; 44226497Sache jiov[i].iov_len = 0; 44326497Sache if (jp[j].jp_value != NULL && 44426497Sache jp[j].jp_valuelen == sizeof(int) && 44526497Sache !*(int *)jp[j].jp_value) { 44626497Sache bool0 = 1; 44726497Sache nname = jp[j].jp_flags & JP_BOOL 44826497Sache ? noname(jp[j].jp_name) 44926497Sache : nononame(jp[j].jp_name); 45026497Sache if (nname == NULL) { 45126497Sache njp = j; 45226497Sache jid = -1; 45326497Sache goto done; 45426497Sache } 45526497Sache jiov[i - 1].iov_base = nname; 45626497Sache jiov[i - 1].iov_len = strlen(nname) + 1; 45726497Sache 45826497Sache } 45926497Sache } else { 46026497Sache /* 46121308Sache * Try to fill in missing values with an empty string. 46221308Sache */ 46321308Sache if (jp[j].jp_value == NULL && jp[j].jp_valuelen > 0 && 46421308Sache jailparam_import(jp + j, "") < 0) { 46521308Sache njp = j; 46621308Sache jid = -1; 46721308Sache goto done; 46821308Sache } 46921308Sache jiov[i].iov_base = jp[j].jp_value; 47021308Sache jiov[i].iov_len = 47121308Sache (jp[j].jp_ctltype & CTLTYPE) == CTLTYPE_STRING 47221308Sache ? strlen(jp[j].jp_value) + 1 47321308Sache : jp[j].jp_valuelen; 47421308Sache } 47521308Sache i++; 47621308Sache } 47721308Sache *(const void **)&jiov[i].iov_base = "errmsg"; 47821308Sache jiov[i].iov_len = sizeof("errmsg"); 47921308Sache i++; 48021308Sache jiov[i].iov_base = jail_errmsg; 48121308Sache jiov[i].iov_len = JAIL_ERRMSGLEN; 48221308Sache i++; 48321308Sache jail_errmsg[0] = 0; 48421308Sache jid = jail_set(jiov, i, flags); 48521308Sache if (jid < 0 && !jail_errmsg[0]) 48621308Sache snprintf(jail_errmsg, sizeof(jail_errmsg), "jail_set: %s", 48721308Sache strerror(errno)); 48821308Sache done: 48921308Sache if (bool0) 49021308Sache for (j = 0; j < njp; j++) 49121308Sache if ((jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) && 49221308Sache jp[j].jp_value != NULL && 49321308Sache jp[j].jp_valuelen == sizeof(int) && 49421308Sache !*(int *)jp[j].jp_value) 49521308Sache free(jiov[j * 2].iov_base); 49621308Sache return (jid); 49721308Sache} 49821308Sache 49921308Sacheint 50021308Sachejailparam_get(struct jailparam *jp, unsigned njp, int flags) 50121308Sache{ 50221308Sache struct iovec *jiov; 50321308Sache struct jailparam *jp_lastjid, *jp_jid, *jp_name, *jp_key; 50421308Sache int i, ai, ki, jid, arrays, sanity; 50521308Sache unsigned j; 50621308Sache 50721308Sache /* 50821308Sache * Get the types for all parameters. 50921308Sache * Find the key and any array parameters. 51021308Sache */ 51121308Sache jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1)); 51221308Sache jp_lastjid = jp_jid = jp_name = NULL; 51321308Sache arrays = 0; 51421308Sache for (ai = j = 0; j < njp; j++) { 51521308Sache if (!jp[j].jp_ctltype && jailparam_type(jp + j) < 0) 51621308Sache return (-1); 51721308Sache if (!strcmp(jp[j].jp_name, "lastjid")) 51821308Sache jp_lastjid = jp + j; 51921308Sache else if (!strcmp(jp[j].jp_name, "jid")) 52021308Sache jp_jid = jp + j; 52121308Sache else if (!strcmp(jp[j].jp_name, "name")) 52221308Sache jp_name = jp + j; 52321308Sache else if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { 52421308Sache arrays = 1; 52521308Sache jiov[ai].iov_base = jp[j].jp_name; 52621308Sache jiov[ai].iov_len = strlen(jp[j].jp_name) + 1; 52721308Sache ai++; 52821308Sache jiov[ai].iov_base = NULL; 52921308Sache jiov[ai].iov_len = 0; 53021308Sache ai++; 53121308Sache } 53221308Sache } 53321308Sache jp_key = jp_lastjid ? jp_lastjid : 53421308Sache jp_jid && jp_jid->jp_valuelen == sizeof(int) && 53521308Sache *(int *)jp_jid->jp_value ? jp_jid : jp_name; 53621308Sache if (jp_key == NULL || jp_key->jp_value == NULL) { 53721308Sache strlcpy(jail_errmsg, "no jail specified", JAIL_ERRMSGLEN); 53821308Sache errno = ENOENT; 53921308Sache return (-1); 54021308Sache } 54121308Sache ki = ai; 54221308Sache jiov[ki].iov_base = jp_key->jp_name; 54321308Sache jiov[ki].iov_len = strlen(jp_key->jp_name) + 1; 54421308Sache ki++; 54521308Sache jiov[ki].iov_base = jp_key->jp_value; 54621308Sache jiov[ki].iov_len = (jp_key->jp_ctltype & CTLTYPE) == CTLTYPE_STRING 54721308Sache ? strlen(jp_key->jp_value) + 1 : jp_key->jp_valuelen; 54821308Sache ki++; 54921308Sache *(const void **)&jiov[ki].iov_base = "errmsg"; 55021308Sache jiov[ki].iov_len = sizeof("errmsg"); 55121308Sache ki++; 55221308Sache jiov[ki].iov_base = jail_errmsg; 55321308Sache jiov[ki].iov_len = JAIL_ERRMSGLEN; 55421308Sache ki++; 55521308Sache jail_errmsg[0] = 0; 55621308Sache if (arrays && jail_get(jiov, ki, flags) < 0) { 55721308Sache if (!jail_errmsg[0]) 55821308Sache snprintf(jail_errmsg, sizeof(jail_errmsg), 55921308Sache "jail_get: %s", strerror(errno)); 56021308Sache return (-1); 56121308Sache } 56221308Sache /* Allocate storage for all parameters. */ 56321308Sache for (ai = j = 0, i = ki; j < njp; j++) { 56421308Sache if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { 56521308Sache ai++; 56621308Sache jiov[ai].iov_len += jp[j].jp_elemlen * ARRAY_SLOP; 56721308Sache if (jp[j].jp_valuelen >= jiov[ai].iov_len) 56821308Sache jiov[ai].iov_len = jp[j].jp_valuelen; 56921308Sache else { 57021308Sache jp[j].jp_valuelen = jiov[ai].iov_len; 57121308Sache if (jp[j].jp_value != NULL) 57221308Sache free(jp[j].jp_value); 57321308Sache jp[j].jp_value = malloc(jp[j].jp_valuelen); 57421308Sache if (jp[j].jp_value == NULL) { 57521308Sache strerror_r(errno, jail_errmsg, 57621308Sache JAIL_ERRMSGLEN); 57721308Sache return (-1); 57826497Sache } 57921308Sache } 58021308Sache jiov[ai].iov_base = jp[j].jp_value; 58121308Sache memset(jiov[ai].iov_base, 0, jiov[ai].iov_len); 58221308Sache ai++; 58321308Sache } else if (jp + j != jp_key) { 58421308Sache jiov[i].iov_base = jp[j].jp_name; 58521308Sache jiov[i].iov_len = strlen(jp[j].jp_name) + 1; 58621308Sache i++; 58721308Sache if (jp[j].jp_value == NULL && 58821308Sache !(jp[j].jp_flags & JP_RAWVALUE)) { 58921308Sache jp[j].jp_value = malloc(jp[j].jp_valuelen); 59021308Sache if (jp[j].jp_value == NULL) { 59121308Sache strerror_r(errno, jail_errmsg, 59221308Sache JAIL_ERRMSGLEN); 59321308Sache return (-1); 59421308Sache } 59521308Sache } 59621308Sache jiov[i].iov_base = jp[j].jp_value; 59721308Sache jiov[i].iov_len = jp[j].jp_valuelen; 59821308Sache memset(jiov[i].iov_base, 0, jiov[i].iov_len); 59921308Sache i++; 60021308Sache } 60121308Sache } 60221308Sache /* 60321308Sache * Get the prison. If there are array elements, retry a few times 60421308Sache * in case their sizes changed from under us. 60521308Sache */ 60621308Sache for (sanity = 0;; sanity++) { 60721308Sache jid = jail_get(jiov, i, flags); 60821308Sache if (jid >= 0 || !arrays || sanity == ARRAY_SANITY || 60921308Sache errno != EINVAL || jail_errmsg[0]) 61021308Sache break; 61121308Sache for (ai = j = 0; j < njp; j++) { 61221308Sache if (jp[j].jp_elemlen && 61321308Sache !(jp[j].jp_flags & JP_RAWVALUE)) { 61421308Sache ai++; 61521308Sache jiov[ai].iov_base = NULL; 61621308Sache jiov[ai].iov_len = 0; 61721308Sache ai++; 61821308Sache } 61921308Sache } 62021308Sache if (jail_get(jiov, ki, flags) < 0) 62121308Sache break; 62221308Sache for (ai = j = 0; j < njp; j++) { 62321308Sache if (jp[j].jp_elemlen && 62421308Sache !(jp[j].jp_flags & JP_RAWVALUE)) { 62521308Sache ai++; 62621308Sache jiov[ai].iov_len += 62721308Sache jp[j].jp_elemlen * ARRAY_SLOP; 62821308Sache if (jp[j].jp_valuelen >= jiov[ai].iov_len) 62921308Sache jiov[ai].iov_len = jp[j].jp_valuelen; 63021308Sache else { 63121308Sache jp[j].jp_valuelen = jiov[ai].iov_len; 63221308Sache if (jp[j].jp_value != NULL) 63321308Sache free(jp[j].jp_value); 63421308Sache jp[j].jp_value = 63521308Sache malloc(jiov[ai].iov_len); 63621308Sache if (jp[j].jp_value == NULL) { 63721308Sache strerror_r(errno, jail_errmsg, 63821308Sache JAIL_ERRMSGLEN); 63921308Sache return (-1); 64021308Sache } 64121308Sache } 64221308Sache jiov[ai].iov_base = jp[j].jp_value; 64321308Sache memset(jiov[ai].iov_base, 0, jiov[ai].iov_len); 64421308Sache ai++; 64521308Sache } 64621308Sache } 64721308Sache } 64821308Sache if (jid < 0 && !jail_errmsg[0]) 64921308Sache snprintf(jail_errmsg, sizeof(jail_errmsg), 65021308Sache "jail_get: %s", strerror(errno)); 65121308Sache for (ai = j = 0, i = ki; j < njp; j++) { 65221308Sache if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { 65321308Sache ai++; 65421308Sache jp[j].jp_valuelen = jiov[ai].iov_len; 65521308Sache ai++; 65621308Sache } else if (jp + j != jp_key) { 65721308Sache i++; 65821308Sache jp[j].jp_valuelen = jiov[i].iov_len; 65921308Sache i++; 66021308Sache } 66121308Sache } 66221308Sache return (jid); 66321308Sache} 66421308Sache 66521308Sache/* 66621308Sache * Convert a jail parameter's value to external form. 66721308Sache */ 66821308Sachechar * 66921308Sachejailparam_export(struct jailparam *jp) 67021308Sache{ 67121308Sache char *value, *tvalue, **values; 67221308Sache size_t valuelen; 67321308Sache int i, nval, ival; 67421308Sache char valbuf[INET6_ADDRSTRLEN]; 67521308Sache 67621308Sache if (!jp->jp_ctltype && jailparam_type(jp) < 0) 67721308Sache return (NULL); 67821308Sache if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) { 67921308Sache value = strdup(jp->jp_value); 68021308Sache if (value == NULL) 68121308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 68221308Sache return (value); 68321308Sache } 68421308Sache nval = jp->jp_elemlen ? jp->jp_valuelen / jp->jp_elemlen : 1; 68521308Sache if (nval == 0) { 68621308Sache value = strdup(""); 68721308Sache if (value == NULL) 68821308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 68921308Sache return (value); 69021308Sache } 69121308Sache values = alloca(nval * sizeof(char *)); 69221308Sache valuelen = 0; 69321308Sache for (i = 0; i < nval; i++) { 69421308Sache switch (jp->jp_ctltype & CTLTYPE) { 69521308Sache case CTLTYPE_INT: 69621308Sache ival = ((int *)jp->jp_value)[i]; 69721308Sache if ((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) && 69821308Sache (unsigned)ival < 2) { 69921308Sache strlcpy(valbuf, bool_values[ival], 70021308Sache sizeof(valbuf)); 70121308Sache break; 70221308Sache } 70321308Sache if ((jp->jp_flags & JP_JAILSYS) && 70421308Sache (unsigned)ival < sizeof(jailsys_values) / 70521308Sache sizeof(jailsys_values[0])) { 70621308Sache strlcpy(valbuf, jailsys_values[ival], 70721308Sache sizeof(valbuf)); 70821308Sache break; 70921308Sache } 71021308Sache snprintf(valbuf, sizeof(valbuf), "%d", ival); 71121308Sache break; 71221308Sache case CTLTYPE_UINT: 71321308Sache snprintf(valbuf, sizeof(valbuf), "%u", 71421308Sache ((unsigned *)jp->jp_value)[i]); 71521308Sache break; 71621308Sache case CTLTYPE_LONG: 71721308Sache snprintf(valbuf, sizeof(valbuf), "%ld", 71821308Sache ((long *)jp->jp_value)[i]); 71921308Sache break; 72021308Sache case CTLTYPE_ULONG: 72121308Sache snprintf(valbuf, sizeof(valbuf), "%lu", 72221308Sache ((unsigned long *)jp->jp_value)[i]); 72321308Sache break; 72421308Sache case CTLTYPE_QUAD: 72521308Sache snprintf(valbuf, sizeof(valbuf), "%jd", 72621308Sache (intmax_t)((int64_t *)jp->jp_value)[i]); 72721308Sache break; 72821308Sache case CTLTYPE_STRUCT: 72921308Sache switch (jp->jp_structtype) { 73021308Sache case JPS_IN_ADDR: 73121308Sache if (inet_ntop(AF_INET, 73221308Sache &((struct in_addr *)jp->jp_value)[i], 73321308Sache valbuf, sizeof(valbuf)) == NULL) { 73421308Sache strerror_r(errno, jail_errmsg, 73521308Sache JAIL_ERRMSGLEN); 73621308Sache 73721308Sache return (NULL); 73821308Sache } 73921308Sache break; 74021308Sache case JPS_IN6_ADDR: 74121308Sache if (inet_ntop(AF_INET6, 74221308Sache &((struct in6_addr *)jp->jp_value)[i], 74321308Sache valbuf, sizeof(valbuf)) == NULL) { 74421308Sache strerror_r(errno, jail_errmsg, 74521308Sache JAIL_ERRMSGLEN); 74621308Sache 74721308Sache return (NULL); 74821308Sache } 74921308Sache break; 75021308Sache default: 75121308Sache goto unknown_type; 75221308Sache } 75321308Sache break; 75421308Sache default: 75521308Sache unknown_type: 75621308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 75721308Sache "unknown type for %s", jp->jp_name); 75821308Sache errno = ENOENT; 75921308Sache return (NULL); 76021308Sache } 76121308Sache valuelen += strlen(valbuf) + 1; 76221308Sache values[i] = alloca(valuelen); 76321308Sache strcpy(values[i], valbuf); 76421308Sache } 76521308Sache value = malloc(valuelen + 1); 76621308Sache if (value == NULL) 76721308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 76821308Sache else { 76921308Sache tvalue = value; 77021308Sache for (i = 0; i < nval; i++) { 77121308Sache strcpy(tvalue, values[i]); 77221308Sache if (i < nval - 1) { 77321308Sache tvalue += strlen(values[i]); 77421308Sache *tvalue++ = ','; 77521308Sache } 77621308Sache } 77721308Sache } 77821308Sache return (value); 77921308Sache} 78021308Sache 78121308Sache/* 78221308Sache * Free the contents of a jail parameter list (but not thst list itself). 78321308Sache */ 78421308Sachevoid 78521308Sachejailparam_free(struct jailparam *jp, unsigned njp) 78621308Sache{ 78721308Sache unsigned j; 78821308Sache 78921308Sache for (j = 0; j < njp; j++) { 79021308Sache free(jp[j].jp_name); 79121308Sache if (!(jp[j].jp_flags & JP_RAWVALUE)) 79221308Sache free(jp[j].jp_value); 79321308Sache } 79421308Sache} 79521308Sache 79621308Sache/* 79721308Sache * Create and import an array of jail parameters, given a list of name and 79821308Sache * value strings, terminated by a null name. 79921308Sache */ 80021308Sachestatic int 80121308Sachejailparam_vlist(struct jailparam **jpp, va_list ap) 80221308Sache{ 80321308Sache va_list tap; 80421308Sache struct jailparam *jp; 80521308Sache char *name, *value; 80621308Sache int njp; 80721308Sache 80821308Sache va_copy(tap, ap); 80921308Sache for (njp = 0; va_arg(tap, char *) != NULL; njp++) 81021308Sache (void)va_arg(tap, char *); 81121308Sache va_end(tap); 81221308Sache jp = calloc(njp, sizeof(struct jailparam)); 81321308Sache if (jp == NULL) { 81421308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 81521308Sache return (-1); 81621308Sache } 81721308Sache 81821308Sache for (njp = 0; (name = va_arg(ap, char *)) != NULL; njp++) { 81921308Sache value = va_arg(ap, char *); 82021308Sache if (jailparam_init(jp + njp, name) < 0 || 82121308Sache jailparam_import(jp + njp, value) < 0) { 82221308Sache jailparam_free(jp, njp); 82321308Sache free(jp); 82421308Sache return (-1); 82521308Sache } 82621308Sache } 82721308Sache *jpp = jp; 82821308Sache return (njp); 82921308Sache} 83021308Sache 83121308Sache/* 83221308Sache * Find a parameter's type and size from its MIB. 83321308Sache */ 83421308Sachestatic int 83521308Sachejailparam_type(struct jailparam *jp) 83621308Sache{ 83721308Sache char *p, *nname; 83821308Sache size_t miblen, desclen; 83921308Sache int isarray; 84021308Sache struct { 84121308Sache int i; 84221308Sache char s[MAXPATHLEN]; 84321308Sache } desc; 84421308Sache int mib[CTL_MAXNAME]; 84521308Sache 84621308Sache /* The "lastjid" parameter isn't real. */ 84721308Sache if (!strcmp(jp->jp_name, "lastjid")) { 84821308Sache jp->jp_valuelen = sizeof(int); 84921308Sache jp->jp_ctltype = CTLTYPE_INT | CTLFLAG_WR; 85021308Sache return (0); 85121308Sache } 85221308Sache 85321308Sache /* Find the sysctl that describes the parameter. */ 85421308Sache mib[0] = 0; 85521308Sache mib[1] = 3; 85621308Sache snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", jp->jp_name); 85721308Sache miblen = sizeof(mib) - 2 * sizeof(int); 85821308Sache if (sysctl(mib, 2, mib + 2, &miblen, desc.s, strlen(desc.s)) < 0) { 85921308Sache if (errno != ENOENT) { 86021308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 86121308Sache "sysctl(0.3.%s): %s", jp->jp_name, strerror(errno)); 86221308Sache return (-1); 86321308Sache } 86421308Sache /* 86521308Sache * The parameter probably doesn't exist. But it might be 86621308Sache * the "no" counterpart to a boolean. 86721308Sache */ 86821308Sache nname = nononame(jp->jp_name); 86921308Sache if (nname != NULL) { 87021308Sache snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname); 87121308Sache free(nname); 87221308Sache miblen = sizeof(mib) - 2 * sizeof(int); 87321308Sache if (sysctl(mib, 2, mib + 2, &miblen, desc.s, 87421308Sache strlen(desc.s)) >= 0) { 87521308Sache mib[1] = 4; 87621308Sache desclen = sizeof(desc); 87721308Sache if (sysctl(mib, (miblen / sizeof(int)) + 2, 87821308Sache &desc, &desclen, NULL, 0) < 0) { 87921308Sache snprintf(jail_errmsg, 88021308Sache JAIL_ERRMSGLEN, 88121308Sache "sysctl(0.4.%s): %s", desc.s, 88221308Sache strerror(errno)); 88321308Sache return (-1); 88421308Sache } 88521308Sache if ((desc.i & CTLTYPE) == CTLTYPE_INT && 88621308Sache desc.s[0] == 'B') { 88721308Sache jp->jp_ctltype = desc.i; 88821308Sache jp->jp_flags |= JP_NOBOOL; 88921308Sache jp->jp_valuelen = sizeof(int); 89021308Sache return (0); 89121308Sache } 89221308Sache } 89321308Sache } 89421308Sache unknown_parameter: 89521308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 89621308Sache "unknown parameter: %s", jp->jp_name); 89721308Sache errno = ENOENT; 89821308Sache return (-1); 89921308Sache } 90021308Sache mib_desc: 90121308Sache mib[1] = 4; 90221308Sache desclen = sizeof(desc); 90321308Sache if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen, 90421308Sache NULL, 0) < 0) { 90521308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 90621308Sache "sysctl(0.4.%s): %s", jp->jp_name, strerror(errno)); 90721308Sache return (-1); 90821308Sache } 90921308Sache /* See if this is an array type. */ 91021308Sache p = strchr(desc.s, '\0'); 91121308Sache isarray = 0; 91221308Sache if (p - 2 < desc.s || strcmp(p - 2, ",a")) 91321308Sache isarray = 0; 91421308Sache else { 91521308Sache isarray = 1; 91621308Sache p[-2] = 0; 91721308Sache } 91821308Sache /* Look for types we understand */ 91921308Sache jp->jp_ctltype = desc.i; 92021308Sache switch (desc.i & CTLTYPE) { 92121308Sache case CTLTYPE_INT: 92221308Sache if (desc.s[0] == 'B') 92321308Sache jp->jp_flags |= JP_BOOL; 92421308Sache else if (!strcmp(desc.s, "E,jailsys")) 92521308Sache jp->jp_flags |= JP_JAILSYS; 92621308Sache case CTLTYPE_UINT: 92721308Sache jp->jp_valuelen = sizeof(int); 92821308Sache break; 92921308Sache case CTLTYPE_LONG: 93021308Sache case CTLTYPE_ULONG: 93121308Sache jp->jp_valuelen = sizeof(long); 93221308Sache break; 93321308Sache case CTLTYPE_QUAD: 93421308Sache jp->jp_valuelen = sizeof(int64_t); 93521308Sache break; 93621308Sache case CTLTYPE_STRING: 93721308Sache desc.s[0] = 0; 93821308Sache desclen = sizeof(desc.s); 93921308Sache if (sysctl(mib + 2, miblen / sizeof(int), desc.s, &desclen, 94021308Sache NULL, 0) < 0) { 94121308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 94221308Sache "sysctl(" SJPARAM ".%s): %s", jp->jp_name, 94321308Sache strerror(errno)); 94421308Sache return (-1); 94521308Sache } 94621308Sache jp->jp_valuelen = strtoul(desc.s, NULL, 10); 94721308Sache break; 94821308Sache case CTLTYPE_STRUCT: 94921308Sache if (!strcmp(desc.s, "S,in_addr")) { 95021308Sache jp->jp_structtype = JPS_IN_ADDR; 95121308Sache jp->jp_valuelen = sizeof(struct in_addr); 95221308Sache } else if (!strcmp(desc.s, "S,in6_addr")) { 95321308Sache jp->jp_structtype = JPS_IN6_ADDR; 95421308Sache jp->jp_valuelen = sizeof(struct in6_addr); 95521308Sache } else { 95621308Sache desclen = 0; 95721308Sache if (sysctl(mib + 2, miblen / sizeof(int), 95821308Sache NULL, &jp->jp_valuelen, NULL, 0) < 0) { 95921308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 96021308Sache "sysctl(" SJPARAM ".%s): %s", jp->jp_name, 96121308Sache strerror(errno)); 96221308Sache return (-1); 96321308Sache } 96421308Sache } 96521308Sache break; 96621308Sache case CTLTYPE_NODE: 96721308Sache /* A node might be described by an empty-named child. */ 96821308Sache mib[1] = 1; 96921308Sache mib[(miblen / sizeof(int)) + 2] = 97021308Sache mib[(miblen / sizeof(int)) + 1] - 1; 97121308Sache miblen += sizeof(int); 97221308Sache desclen = sizeof(desc.s); 97321308Sache if (sysctl(mib, (miblen / sizeof(int)) + 2, desc.s, &desclen, 97421308Sache NULL, 0) < 0) { 97521308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 97621308Sache "sysctl(0.1): %s", strerror(errno)); 97721308Sache return (-1); 97821308Sache } 97921308Sache if (desc.s[desclen - 2] != '.') 98021308Sache goto unknown_parameter; 98121308Sache goto mib_desc; 98221308Sache default: 98321308Sache snprintf(jail_errmsg, JAIL_ERRMSGLEN, 98421308Sache "unknown type for %s", jp->jp_name); 98521308Sache errno = ENOENT; 98621308Sache return (-1); 98721308Sache } 98821308Sache if (isarray) { 98921308Sache jp->jp_elemlen = jp->jp_valuelen; 99021308Sache jp->jp_valuelen = 0; 99121308Sache } 99221308Sache return (0); 99321308Sache} 99421308Sache 99521308Sache/* 99621308Sache * Change a boolean parameter name into its "no" counterpart or vice versa. 99721308Sache */ 99821308Sachestatic char * 99921308Sachenoname(const char *name) 100021308Sache{ 100121308Sache char *nname, *p; 100221308Sache 100321308Sache nname = malloc(strlen(name) + 3); 100421308Sache if (nname == NULL) { 100521308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 100621308Sache return (NULL); 100721308Sache } 100821308Sache p = strrchr(name, '.'); 100921308Sache if (p != NULL) 101021308Sache sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); 101121308Sache else 101221308Sache sprintf(nname, "no%s", name); 101321308Sache return (nname); 101421308Sache} 101521308Sache 101621308Sachestatic char * 101721308Sachenononame(const char *name) 101821308Sache{ 101921308Sache char *p, *nname; 102021308Sache 102121308Sache p = strrchr(name, '.'); 102221308Sache if (strncmp(p ? p + 1 : name, "no", 2)) { 102321308Sache snprintf(jail_errmsg, sizeof(jail_errmsg), 102421308Sache "mismatched boolean: %s", name); 102521308Sache errno = EINVAL; 102621308Sache return (NULL); 102721308Sache } 102821308Sache nname = malloc(strlen(name) - 1); 102921308Sache if (nname == NULL) { 103021308Sache strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 103121308Sache return (NULL); 103221308Sache } 103321308Sache if (p != NULL) 103421308Sache sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); 103521308Sache else 103621308Sache strcpy(nname, name + 2); 103721308Sache return (nname); 103821308Sache} 103921308Sache