jail.c revision 194869
150472Speter/*-
23515Sache * Copyright (c) 2009 James Gritton.
3217309Snwhitehorn * All rights reserved.
4156813Sru *
5217309Snwhitehorn * Redistribution and use in source and binary forms, with or without
6252129Sbapt * modification, are permitted provided that the following conditions
7251843Sbapt * are met:
8251843Sbapt * 1. Redistributions of source code must retain the above copyright
9251843Sbapt *    notice, this list of conditions and the following disclaimer.
10251843Sbapt * 2. Redistributions in binary form must reproduce the above copyright
11251843Sbapt *    notice, this list of conditions and the following disclaimer in the
12251843Sbapt *    documentation and/or other materials provided with the distribution.
13217309Snwhitehorn *
14217309Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1512983Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16275040Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17275040Sdteske * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18275040Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19251843Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20217309Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21251845Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
223515Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233515Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/lib/libjail/jail.c 194869 2009-06-24 18:18:35Z jamie $");
29
30#include <sys/param.h>
31#include <sys/types.h>
32#include <sys/jail.h>
33#include <sys/socket.h>
34#include <sys/sysctl.h>
35
36#include <arpa/inet.h>
37#include <netinet/in.h>
38
39#include <errno.h>
40#include <inttypes.h>
41#include <stdio.h>
42#include <stdarg.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include "jail.h"
47
48#define	SJPARAM		"security.jail.param"
49
50#define JPS_IN_ADDR	1
51#define JPS_IN6_ADDR	2
52
53#define ARRAY_SANITY	5
54#define ARRAY_SLOP	5
55
56
57static int jailparam_vlist(struct jailparam **jpp, va_list ap);
58static int jailparam_type(struct jailparam *jp);
59static char *noname(const char *name);
60static char *nononame(const char *name);
61
62char jail_errmsg[JAIL_ERRMSGLEN];
63
64
65/*
66 * Import a null-terminated parameter list and set a jail with the flags
67 * and parameters.
68 */
69int
70jail_setv(int flags, ...)
71{
72	va_list ap;
73	struct jailparam *jp;
74	int njp;
75
76	va_start(ap, flags);
77	njp = jailparam_vlist(&jp, ap);
78	va_end(ap);
79	if (njp < 0)
80		return (njp);
81	return (jailparam_set(jp, njp, flags));
82}
83
84/*
85 * Read a null-terminated parameter list, get the referenced jail, and export
86 * the parameters to the list.
87 */
88int
89jail_getv(int flags, ...)
90{
91	va_list ap, tap;
92	struct jailparam *jp;
93	char *valarg;
94	const char *value;
95	int njp, i, jid, namekey, zero;
96
97	va_start(ap, flags);
98	va_copy(tap, ap);
99	njp = jailparam_vlist(&jp, tap);
100	va_end(tap);
101	if (njp < 0)
102		return (njp);
103	/*
104	 * See if the name is the search key.  If so, we don't want to write
105	 * it back in case it's a read-only string.
106	 */
107	namekey = 1;
108	zero = 0;
109	for (i = 0; i < njp; i++) {
110		if (!strcmp(jp->jp_name, "lastjid") ||
111		    (!strcmp(jp->jp_name, "jid") &&
112		     memcmp(jp->jp_value, &zero, sizeof(zero))))
113			namekey = 0;
114	}
115	jid = jailparam_get(jp, njp, flags);
116	if (jid < 0) {
117		va_end(ap);
118		return (-1);
119	}
120	for (i = 0; i < njp; i++) {
121		(void)va_arg(ap, char *);
122		value = jailparam_export(jp + i);
123		if (value == NULL) {
124			va_end(ap);
125			return (-1);
126		}
127		valarg = va_arg(ap, char *);
128		if (!namekey || strcmp(jp[i].jp_name, "name"))
129			/* It's up to the caller to ensure there's room. */
130			strcpy(valarg, value);
131	}
132	va_end(ap);
133	return (jid);
134}
135
136/*
137 * Return a list of all known parameters.
138 */
139int
140jailparam_all(struct jailparam **jpp)
141{
142	struct jailparam *jp;
143	char *nname;
144	size_t mlen1, mlen2, buflen;
145	int njp, nlist;
146	int mib1[CTL_MAXNAME], mib2[CTL_MAXNAME - 2];
147	char buf[MAXPATHLEN];
148
149	njp = 0;
150	nlist = 32;
151	jp = malloc(nlist * sizeof(*jp));
152	if (jp == NULL) {
153		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
154		return (-1);
155	}
156	mib1[0] = 0;
157	mib1[1] = 2;
158	mlen1 = CTL_MAXNAME - 2;
159	if (sysctlnametomib(SJPARAM, mib1 + 2, &mlen1) < 0) {
160		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
161		    "sysctlnametomib(" SJPARAM "): %s", strerror(errno));
162		goto error;
163	}
164	for (;; njp++) {
165		/* Get the next parameter. */
166		mlen2 = sizeof(mib2);
167		if (sysctl(mib1, mlen1 + 2, mib2, &mlen2, NULL, 0) < 0) {
168			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
169			    "sysctl(0.2): %s", strerror(errno));
170			goto error;
171		}
172		if (mib2[0] != mib1[2] || mib2[1] != mib1[3] ||
173		    mib2[2] != mib1[4])
174			break;
175		/* Convert it to an ascii name. */
176		memcpy(mib1 + 2, mib2, mlen2);
177		mlen1 = mlen2 / sizeof(int);
178		mib1[1] = 1;
179		buflen = sizeof(buf);
180		if (sysctl(mib1, mlen1 + 2, buf, &buflen, NULL, 0) < 0) {
181			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
182			    "sysctl(0.1): %s", strerror(errno));
183			goto error;
184		}
185		/* Add the parameter to the list */
186		if (njp >= nlist) {
187			nlist *= 2;
188			jp = realloc(jp, nlist * sizeof(jp));
189			if (jp == NULL) {
190				jailparam_free(jp, njp);
191				return (-1);
192			}
193		}
194		if (jailparam_init(jp + njp, buf + sizeof(SJPARAM)) < 0)
195			goto error;
196		if (jailparam_type(jp + njp) < 0) {
197			njp++;
198			goto error;
199		}
200		/* Convert nobool parameters to bool. */
201		if (jp[njp].jp_flags & JP_NOBOOL) {
202			nname = nononame(jp[njp].jp_name);
203			if (nname == NULL) {
204				njp++;
205				goto error;
206			}
207			free(jp[njp].jp_name);
208			jp[njp].jp_name = nname;
209			jp[njp].jp_flags ^= JP_BOOL | JP_NOBOOL;
210		}
211		mib1[1] = 2;
212	}
213	jp = realloc(jp, njp * sizeof(*jp));
214	*jpp = jp;
215	return (njp);
216
217 error:
218	jailparam_free(jp, njp);
219	free(jp);
220	return (-1);
221}
222
223/*
224 * Clear a jail parameter and copy in its name.
225 */
226int
227jailparam_init(struct jailparam *jp, const char *name)
228{
229
230	memset(jp, 0, sizeof(*jp));
231	jp->jp_name = strdup(name);
232	if (jp->jp_name == NULL) {
233		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
234		return (-1);
235	}
236	return (0);
237}
238
239/*
240 * Put a name and value into a jail parameter element, converting the value
241 * to internal form.
242 */
243int
244jailparam_import(struct jailparam *jp, const char *value)
245{
246	char *p, *ep, *tvalue;
247	const char *avalue;
248	int i, nval, fw;
249
250	if (!jp->jp_ctltype && jailparam_type(jp) < 0)
251		goto error;
252	if (value == NULL)
253		return (0);
254	if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) {
255		jp->jp_value = strdup(value);
256		if (!jp->jp_value) {
257			strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
258			goto error;
259		}
260		return (0);
261	}
262	nval = 1;
263	if (jp->jp_elemlen) {
264		if (value[0] == '\0' || (value[0] == '-' && value[1] == '\0')) {
265			jp->jp_value = strdup("");
266			if (value == NULL) {
267				strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
268				goto error;
269			}
270			jp->jp_valuelen = 0;
271			return (0);
272		}
273		for (p = strchr(value, ','); p; p = strchr(p + 1, ','))
274			nval++;
275		jp->jp_valuelen = jp->jp_elemlen * nval;
276	}
277	jp->jp_value = malloc(jp->jp_valuelen);
278	if (!jp->jp_value) {
279		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
280		goto error;
281	}
282	avalue = value;
283	for (i = 0; i < nval; i++) {
284		fw = nval == 1 ? strlen(avalue) : strcspn(avalue, ",");
285		switch (jp->jp_ctltype & CTLTYPE) {
286		case CTLTYPE_INT:
287			if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) {
288				if (!strncasecmp(avalue, "true", 4))
289					((int *)jp->jp_value)[i] = 1;
290				else if (!strncasecmp(avalue, "false", 5))
291					((int *)jp->jp_value)[i] = 0;
292				else {
293					snprintf(jail_errmsg,
294					    JAIL_ERRMSGLEN,
295					   "%s: unknown boolean value \"%.*s\"",
296					    jp->jp_name, fw, avalue);
297					errno = EINVAL;
298					goto error;
299				}
300				break;
301			}
302			((int *)jp->jp_value)[i] = strtol(avalue, &ep, 10);
303		integer_test:
304			if (ep != avalue + fw) {
305				snprintf(jail_errmsg, JAIL_ERRMSGLEN,
306				    "%s: non-integer value \"%.*s\"",
307				    jp->jp_name, fw, avalue);
308				errno = EINVAL;
309				goto error;
310			}
311			break;
312		case CTLTYPE_UINT:
313			((unsigned *)jp->jp_value)[i] =
314			    strtoul(avalue, &ep, 10);
315			goto integer_test;
316		case CTLTYPE_LONG:
317			((long *)jp->jp_value)[i] = strtol(avalue, &ep, 10);
318			goto integer_test;
319		case CTLTYPE_ULONG:
320			((unsigned long *)jp->jp_value)[i] =
321			    strtoul(avalue, &ep, 10);
322			goto integer_test;
323		case CTLTYPE_QUAD:
324			((int64_t *)jp->jp_value)[i] =
325			    strtoimax(avalue, &ep, 10);
326			goto integer_test;
327		case CTLTYPE_STRUCT:
328			tvalue = alloca(fw + 1);
329			strlcpy(tvalue, avalue, fw + 1);
330			switch (jp->jp_structtype) {
331			case JPS_IN_ADDR:
332				if (inet_pton(AF_INET, tvalue,
333				    &((struct in_addr *)jp->jp_value)[i]) != 1)
334				{
335					snprintf(jail_errmsg,
336					    JAIL_ERRMSGLEN,
337					    "%s: not an IPv4 address: %s",
338					    jp->jp_name, tvalue);
339					errno = EINVAL;
340					goto error;
341				}
342				break;
343			case JPS_IN6_ADDR:
344				if (inet_pton(AF_INET6, tvalue,
345				    &((struct in6_addr *)jp->jp_value)[i]) != 1)
346				{
347					snprintf(jail_errmsg,
348					    JAIL_ERRMSGLEN,
349					    "%s: not an IPv6 address: %s",
350					    jp->jp_name, tvalue);
351					errno = EINVAL;
352					goto error;
353				}
354				break;
355			default:
356				goto unknown_type;
357			}
358			break;
359		default:
360		unknown_type:
361			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
362			    "unknown type for %s", jp->jp_name);
363			errno = ENOENT;
364			goto error;
365		}
366		avalue += fw + 1;
367	}
368	return (0);
369
370 error:
371	free(jp->jp_value);
372	jp->jp_value = NULL;
373	return (-1);
374}
375
376/*
377 * Put a name and value into a jail parameter element, copying the value
378 * but not altering it.
379 */
380int
381jailparam_import_raw(struct jailparam *jp, void *value, size_t valuelen)
382{
383
384	jp->jp_value = value;
385	jp->jp_valuelen = valuelen;
386	jp->jp_flags |= JP_RAWVALUE;
387	return (0);
388}
389
390/*
391 * Run the jail_set and jail_get system calls on a parameter list.
392 */
393int
394jailparam_set(struct jailparam *jp, unsigned njp, int flags)
395{
396	struct iovec *jiov;
397	char *nname;
398	int i, jid;
399	unsigned j;
400
401	jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1));
402	for (i = j = 0; j < njp; j++) {
403		jiov[i].iov_base = jp[j].jp_name;
404		jiov[i].iov_len = strlen(jp[j].jp_name) + 1;
405		i++;
406		if (jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) {
407			/*
408			 * Set booleans without values.  If one have a value of
409			 * zero, change it to (or from) its "no" counterpart.
410			 */
411			jiov[i].iov_base = NULL;
412			jiov[i].iov_len = 0;
413			if (jp[j].jp_value != NULL &&
414			    jp[j].jp_valuelen == sizeof(int) &&
415			    !*(int *)jp[j].jp_value) {
416				nname = jp[j].jp_flags & JP_BOOL
417				    ? noname(jiov[i].iov_base)
418				    : nononame(jiov[i].iov_base);
419				if (nname == NULL)
420					return (-1);
421				free(jp[j].jp_name);
422				jiov[i].iov_base = jp[j].jp_name = nname;
423			}
424		} else {
425			jiov[i].iov_base = jp[j].jp_value;
426			jiov[i].iov_len =
427			    (jp[j].jp_ctltype & CTLTYPE) == CTLTYPE_STRING
428			    ? strlen(jp[j].jp_value) + 1
429			    : jp[j].jp_valuelen;
430		}
431		i++;
432	}
433	*(const void **)&jiov[i].iov_base = "errmsg";
434	jiov[i].iov_len = sizeof("errmsg");
435	i++;
436	jiov[i].iov_base = jail_errmsg;
437	jiov[i].iov_len = JAIL_ERRMSGLEN;
438	i++;
439	jail_errmsg[0] = 0;
440	jid = jail_set(jiov, i, flags);
441	if (jid < 0 && !jail_errmsg[0])
442		snprintf(jail_errmsg, sizeof(jail_errmsg), "jail_set: %s",
443		    strerror(errno));
444	return (jid);
445}
446
447int
448jailparam_get(struct jailparam *jp, unsigned njp, int flags)
449{
450	struct iovec *jiov;
451	struct jailparam *jp_lastjid, *jp_jid, *jp_name, *jp_key;
452	int i, ai, ki, jid, arrays, sanity;
453	unsigned j;
454
455	/* Find the key and any array parameters. */
456	jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1));
457	jp_lastjid = jp_jid = jp_name = NULL;
458	arrays = 0;
459	for (ai = j = 0; j < njp; j++) {
460		if (!strcmp(jp[j].jp_name, "lastjid"))
461			jp_lastjid = jp + j;
462		else if (!strcmp(jp[j].jp_name, "jid"))
463			jp_jid = jp + j;
464		else if (!strcmp(jp[j].jp_name, "name"))
465			jp_name = jp + j;
466		else if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) {
467			arrays = 1;
468			jiov[ai].iov_base = jp[j].jp_name;
469			jiov[ai].iov_len = strlen(jp[j].jp_name) + 1;
470			ai++;
471			jiov[ai].iov_base = NULL;
472			jiov[ai].iov_len = 0;
473			ai++;
474		}
475	}
476	jp_key = jp_lastjid ? jp_lastjid :
477	    jp_jid && jp_jid->jp_valuelen == sizeof(int) &&
478	    *(int *)jp_jid->jp_value ? jp_jid : jp_name;
479	if (jp_key == NULL || jp_key->jp_value == NULL) {
480		strlcpy(jail_errmsg, "no jail specified", JAIL_ERRMSGLEN);
481		errno = ENOENT;
482		return (-1);
483	}
484	ki = ai;
485	jiov[ki].iov_base = jp_key->jp_name;
486	jiov[ki].iov_len = strlen(jp_key->jp_name) + 1;
487	ki++;
488	jiov[ki].iov_base = jp_key->jp_value;
489	jiov[ki].iov_len = (jp_key->jp_ctltype & CTLTYPE) == CTLTYPE_STRING
490	    ? strlen(jp_key->jp_value) + 1 : jp_key->jp_valuelen;
491	ki++;
492	*(const void **)&jiov[ki].iov_base = "errmsg";
493	jiov[ki].iov_len = sizeof("errmsg");
494	ki++;
495	jiov[ki].iov_base = jail_errmsg;
496	jiov[ki].iov_len = JAIL_ERRMSGLEN;
497	ki++;
498	jail_errmsg[0] = 0;
499	if (arrays && jail_get(jiov, ki, flags) < 0) {
500		if (!jail_errmsg[0])
501			snprintf(jail_errmsg, sizeof(jail_errmsg),
502			    "jail_get: %s", strerror(errno));
503		return (-1);
504	}
505	/* Allocate storage for all parameters. */
506	for (ai = j = 0, i = ki; j < njp; j++) {
507		if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) {
508			ai++;
509			jiov[ai].iov_len += jp[j].jp_elemlen * ARRAY_SLOP;
510			jiov[ai].iov_base = jp[j].jp_value =
511			    malloc(jiov[ai].iov_len);
512			if (jiov[ai].iov_base == NULL) {
513				strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
514				return (-1);
515			}
516			memset(jiov[ai].iov_base, 0, jiov[ai].iov_len);
517			ai++;
518		} else if (jp + j != jp_key) {
519			jiov[i].iov_base = jp[j].jp_name;
520			jiov[i].iov_len = strlen(jp[j].jp_name) + 1;
521			i++;
522			jiov[i].iov_base = jp[j].jp_value =
523			    malloc(jp[j].jp_valuelen);
524			if (jiov[i].iov_base == NULL) {
525				strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
526				return (-1);
527			}
528			jiov[i].iov_len = jp[j].jp_valuelen;
529			memset(jiov[i].iov_base, 0, jiov[i].iov_len);
530			i++;
531		}
532	}
533	/*
534	 * Get the prison.  If there are array elements, retry a few times
535	 * in case their sizes changed from under us.
536	 */
537	for (sanity = 0;; sanity++) {
538		jid = jail_get(jiov, i, flags);
539		if (jid >= 0 || !arrays || sanity == ARRAY_SANITY ||
540		    errno != EINVAL || jail_errmsg[0])
541			break;
542		for (ai = j = 0; j < njp; j++) {
543			if (jp[j].jp_elemlen &&
544			    !(jp[j].jp_flags & JP_RAWVALUE)) {
545				ai++;
546				free(jiov[ai].iov_base);
547				jiov[ai].iov_base = jp[j].jp_value = NULL;
548				jiov[ai].iov_len = 0;
549				ai++;
550			}
551		}
552		if (jail_get(jiov, ki, flags) < 0)
553			break;
554		for (ai = j = 0; j < njp; j++) {
555			if (jp[j].jp_elemlen &&
556			    !(jp[j].jp_flags & JP_RAWVALUE)) {
557				ai++;
558				jiov[ai].iov_len +=
559				    jp[j].jp_elemlen * ARRAY_SLOP;
560				jiov[ai].iov_base = jp[j].jp_value =
561				    malloc(jiov[ai].iov_len);
562				if (jiov[ai].iov_base == NULL) {
563					strerror_r(errno, jail_errmsg,
564					    JAIL_ERRMSGLEN);
565					return (-1);
566				}
567				memset(jiov[ai].iov_base, 0, jiov[ai].iov_len);
568				ai++;
569			}
570		}
571	}
572	if (jid < 0 && !jail_errmsg[0])
573		snprintf(jail_errmsg, sizeof(jail_errmsg),
574		    "jail_get: %s", strerror(errno));
575	for (ai = j = 0, i = ki; j < njp; j++) {
576		if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) {
577			ai++;
578			jp[j].jp_valuelen = jiov[ai].iov_len;
579			ai++;
580		} else if (jp + j != jp_key) {
581			i++;
582			jp[j].jp_valuelen = jiov[i].iov_len;
583			i++;
584		}
585	}
586	return (jid);
587}
588
589/*
590 * Convert a jail parameter's value to external form.
591 */
592char *
593jailparam_export(struct jailparam *jp)
594{
595	char *value, *tvalue, **values;
596	size_t valuelen;
597	int i, nval;
598	char valbuf[INET6_ADDRSTRLEN];
599
600	if (!jp->jp_ctltype && jailparam_type(jp) < 0)
601		return (NULL);
602	if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) {
603		value = strdup(jp->jp_value);
604		if (value == NULL)
605			strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
606		return (value);
607	}
608	nval = jp->jp_elemlen ? jp->jp_valuelen / jp->jp_elemlen : 1;
609	if (nval == 0) {
610		value = strdup("");
611		if (value == NULL)
612			strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
613		return (value);
614	}
615	values = alloca(nval * sizeof(char *));
616	valuelen = 0;
617	for (i = 0; i < nval; i++) {
618		switch (jp->jp_ctltype & CTLTYPE) {
619		case CTLTYPE_INT:
620			if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) {
621				strlcpy(valbuf,
622				    ((int *)jp->jp_value)[i] ? "true" : "false",
623				    sizeof(valbuf));
624				break;
625			}
626			snprintf(valbuf, sizeof(valbuf), "%d",
627			    ((int *)jp->jp_value)[i]);
628			break;
629		case CTLTYPE_UINT:
630			snprintf(valbuf, sizeof(valbuf), "%u",
631			    ((unsigned *)jp->jp_value)[i]);
632			break;
633		case CTLTYPE_LONG:
634			snprintf(valbuf, sizeof(valbuf), "%ld",
635			    ((long *)jp->jp_value)[i]);
636			break;
637		case CTLTYPE_ULONG:
638			snprintf(valbuf, sizeof(valbuf), "%lu",
639			    ((unsigned long *)jp->jp_value)[i]);
640			break;
641		case CTLTYPE_QUAD:
642			snprintf(valbuf, sizeof(valbuf), "%jd",
643			    (intmax_t)((int64_t *)jp->jp_value)[i]);
644			break;
645		case CTLTYPE_STRUCT:
646			switch (jp->jp_structtype) {
647			case JPS_IN_ADDR:
648				if (inet_ntop(AF_INET,
649				    &((struct in_addr *)jp->jp_value)[i],
650				    valbuf, sizeof(valbuf)) == NULL) {
651					strerror_r(errno, jail_errmsg,
652					    JAIL_ERRMSGLEN);
653
654					return (NULL);
655				}
656				break;
657			case JPS_IN6_ADDR:
658				if (inet_ntop(AF_INET6,
659				    &((struct in6_addr *)jp->jp_value)[i],
660				    valbuf, sizeof(valbuf)) == NULL) {
661					strerror_r(errno, jail_errmsg,
662					    JAIL_ERRMSGLEN);
663
664					return (NULL);
665				}
666				break;
667			default:
668				goto unknown_type;
669			}
670			break;
671		default:
672		unknown_type:
673			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
674			    "unknown type for %s", jp->jp_name);
675			errno = ENOENT;
676			return (NULL);
677		}
678		valuelen += strlen(valbuf) + 1;
679		values[i] = alloca(valuelen);
680		strcpy(values[i], valbuf);
681	}
682	value = malloc(valuelen + 1);
683	if (value == NULL)
684		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
685	else {
686		tvalue = value;
687		for (i = 0; i < nval; i++) {
688			strcpy(tvalue, values[i]);
689			if (i < nval - 1) {
690				tvalue += strlen(values[i]);
691				*tvalue++ = ',';
692			}
693		}
694	}
695	return (value);
696}
697
698/*
699 * Free the contents of a jail parameter list (but not thst list itself).
700 */
701void
702jailparam_free(struct jailparam *jp, unsigned njp)
703{
704	unsigned j;
705
706	for (j = 0; j < njp; j++) {
707		free(jp[j].jp_name);
708		if (!(jp[j].jp_flags & JP_RAWVALUE))
709			free(jp[j].jp_value);
710	}
711}
712
713/*
714 * Create and import an array of jail parameters, given a list of name and
715 * value strings, terminated by a null name.
716 */
717static int
718jailparam_vlist(struct jailparam **jpp, va_list ap)
719{
720	va_list tap;
721	struct jailparam *jp;
722	char *name, *value;
723	int njp;
724
725	va_copy(tap, ap);
726	for (njp = 0; va_arg(tap, char *) != NULL; njp++)
727		(void)va_arg(tap, char *);
728	va_end(tap);
729	jp = calloc(njp, sizeof(struct jailparam));
730	if (jp == NULL) {
731		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
732		return (-1);
733	}
734
735	for (njp = 0; (name = va_arg(ap, char *)) != NULL; njp++) {
736		value = va_arg(ap, char *);
737		if (jailparam_init(jp + njp, name) < 0 ||
738		    jailparam_import(jp + njp, value) < 0) {
739			jailparam_free(jp, njp);
740			free(jp);
741			return (-1);
742		}
743	}
744	*jpp = jp;
745	return (njp);
746}
747
748/*
749 * Find a parameter's type and size from its MIB.
750 */
751static int
752jailparam_type(struct jailparam *jp)
753{
754	char *p, *nname;
755	size_t miblen, desclen;
756	int isarray;
757	struct {
758	    int i;
759	    char s[MAXPATHLEN];
760	} desc;
761	int mib[CTL_MAXNAME];
762
763	/* The "lastjid" parameter isn't real. */
764	if (!strcmp(jp->jp_name, "lastjid")) {
765		jp->jp_valuelen = sizeof(int);
766		jp->jp_ctltype = CTLTYPE_INT | CTLFLAG_WR;
767		return (0);
768	}
769
770	/* Find the sysctl that describes the parameter. */
771	mib[0] = 0;
772	mib[1] = 3;
773	snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", jp->jp_name);
774	miblen = sizeof(mib) - 2 * sizeof(int);
775	if (sysctl(mib, 2, mib + 2, &miblen, desc.s, strlen(desc.s)) < 0) {
776		if (errno != ENOENT) {
777			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
778			    "sysctl(0.3.%s): %s", jp->jp_name, strerror(errno));
779			return (-1);
780		}
781		/*
782		 * The parameter probably doesn't exist.  But it might be
783		 * the "no" counterpart to a boolean.
784		 */
785		nname = nononame(jp->jp_name);
786		if (nname != NULL) {
787			snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname);
788			free(nname);
789			miblen = sizeof(mib) - 2 * sizeof(int);
790			if (sysctl(mib, 2, mib + 2, &miblen, desc.s,
791			    strlen(desc.s)) >= 0) {
792				mib[1] = 4;
793				desclen = sizeof(desc);
794				if (sysctl(mib, (miblen / sizeof(int)) + 2,
795					   &desc, &desclen, NULL, 0) < 0) {
796					snprintf(jail_errmsg,
797					    JAIL_ERRMSGLEN,
798					    "sysctl(0.4.%s): %s", desc.s,
799					    strerror(errno));
800					return (-1);
801				}
802				if ((desc.i & CTLTYPE) == CTLTYPE_INT &&
803				    desc.s[0] == 'B') {
804					jp->jp_ctltype = desc.i;
805					jp->jp_flags |= JP_NOBOOL;
806					jp->jp_valuelen = sizeof(int);
807					return (0);
808				}
809			}
810		}
811		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
812		    "unknown parameter: %s", jp->jp_name);
813		errno = ENOENT;
814		return (-1);
815	}
816	mib[1] = 4;
817	desclen = sizeof(desc);
818	if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen,
819	    NULL, 0) < 0) {
820		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
821		    "sysctl(0.4.%s): %s", jp->jp_name, strerror(errno));
822		return (-1);
823	}
824	/* See if this is an array type. */
825	p = strchr(desc.s, '\0');
826	isarray  = 0;
827	if (p - 2 < desc.s || strcmp(p - 2, ",a"))
828		isarray = 0;
829	else {
830		isarray = 1;
831		p[-2] = 0;
832	}
833	/* Look for types we understand */
834	jp->jp_ctltype = desc.i;
835	switch (desc.i & CTLTYPE) {
836	case CTLTYPE_INT:
837		if (desc.s[0] == 'B')
838			jp->jp_flags |=
839			    (desc.s[1] == 'N') ? JP_NOBOOL : JP_BOOL;
840	case CTLTYPE_UINT:
841		jp->jp_valuelen = sizeof(int);
842		break;
843	case CTLTYPE_LONG:
844	case CTLTYPE_ULONG:
845		jp->jp_valuelen = sizeof(long);
846		break;
847	case CTLTYPE_QUAD:
848		jp->jp_valuelen = sizeof(int64_t);
849		break;
850	case CTLTYPE_STRING:
851		desc.s[0] = 0;
852		desclen = sizeof(desc.s);
853		if (sysctl(mib + 2, miblen / sizeof(int), desc.s, &desclen,
854		    NULL, 0) < 0) {
855			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
856			    "sysctl(" SJPARAM ".%s): %s", jp->jp_name,
857			    strerror(errno));
858			return (-1);
859		}
860		jp->jp_valuelen = strtoul(desc.s, NULL, 10);
861		break;
862	case CTLTYPE_STRUCT:
863		if (!strcmp(desc.s, "S,in_addr")) {
864			jp->jp_structtype = JPS_IN_ADDR;
865			jp->jp_valuelen = sizeof(struct in_addr);
866		} else if (!strcmp(desc.s, "S,in6_addr")) {
867			jp->jp_structtype = JPS_IN6_ADDR;
868			jp->jp_valuelen = sizeof(struct in6_addr);
869		} else {
870			desclen = 0;
871			if (sysctl(mib + 2, miblen / sizeof(int),
872			    NULL, &jp->jp_valuelen, NULL, 0) < 0) {
873				snprintf(jail_errmsg, JAIL_ERRMSGLEN,
874				    "sysctl(" SJPARAM ".%s): %s", jp->jp_name,
875				    strerror(errno));
876				return (-1);
877			}
878		}
879		break;
880	case CTLTYPE_NODE:
881		/*
882		 * A node isn't normally a parameter, but may be a boolean
883		 * if its "no" counterpart exists.
884		 */
885		nname = noname(jp->jp_name);
886		if (nname == NULL)
887			return (-1);
888		mib[1] = 3;
889		snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname);
890		free(nname);
891		miblen = sizeof(mib) - 2 * sizeof(int);
892		if (sysctl(mib, 2, mib + 2, &miblen, desc.s,
893		    strlen(desc.s)) < 0) {
894			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
895				 "unknown parameter: %s", jp->jp_name);
896			return (-1);
897		}
898		mib[1] = 4;
899		desclen = sizeof(desc);
900		if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen,
901		    NULL, 0) < 0) {
902			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
903			    "sysctl(0.4.%s): %s", desc.s, strerror(errno));
904			return (-1);
905		}
906		if ((desc.i & CTLTYPE) != CTLTYPE_INT || desc.s[0] != 'B') {
907			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
908				 "unknown parameter: %s", jp->jp_name);
909			errno = ENOENT;
910			return (-1);
911		}
912		jp->jp_valuelen = sizeof(int);
913		jp->jp_ctltype = desc.i;
914		jp->jp_flags |= JP_BOOL;
915		break;
916	default:
917		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
918		    "unknown type for %s", jp->jp_name);
919		errno = ENOENT;
920		return (-1);
921	}
922	if (isarray) {
923		jp->jp_elemlen = jp->jp_valuelen;
924		jp->jp_valuelen = 0;
925	}
926	return (0);
927}
928
929/*
930 * Change a boolean parameter name into its "no" counterpart or vice versa.
931 */
932static char *
933noname(const char *name)
934{
935	char *nname, *p;
936
937	nname = malloc(strlen(name) + 3);
938	if (nname == NULL) {
939		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
940		return (NULL);
941	}
942	p = strrchr(name, '.');
943	if (p != NULL)
944		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
945	else
946		sprintf(nname, "no%s", name);
947	return (nname);
948}
949
950static char *
951nononame(const char *name)
952{
953	char *p, *nname;
954
955	p = strrchr(name, '.');
956	if (strncmp(p ? p + 1 : name, "no", 2)) {
957		snprintf(jail_errmsg, sizeof(jail_errmsg),
958		    "mismatched boolean: %s", name);
959		errno = EINVAL;
960		return (NULL);
961	}
962	nname = malloc(strlen(name) - 1);
963	if (nname == NULL) {
964		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
965		return (NULL);
966	}
967	if (p != NULL)
968		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
969	else
970		strcpy(nname, name + 2);
971	return (nname);
972}
973