jail.c revision 195011
1/*-
2 * Copyright (c) 2009 James Gritton.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * 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 195011 2009-06-25 22:42:19Z 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		return (-1);
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 == NULL) {
257			strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
258			return (-1);
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 (jp->jp_value == NULL) {
267				strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
268				return (-1);
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 == NULL) {
279		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
280		return (-1);
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, bool0;
399	unsigned j;
400
401	jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1));
402	bool0 = 0;
403	for (i = j = 0; j < njp; j++) {
404		jiov[i].iov_base = jp[j].jp_name;
405		jiov[i].iov_len = strlen(jp[j].jp_name) + 1;
406		i++;
407		if (jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) {
408			/*
409			 * Set booleans without values.  If one has a value of
410			 * zero, change it to (or from) its "no" counterpart.
411			 */
412			jiov[i].iov_base = NULL;
413			jiov[i].iov_len = 0;
414			if (jp[j].jp_value != NULL &&
415			    jp[j].jp_valuelen == sizeof(int) &&
416			    !*(int *)jp[j].jp_value) {
417				bool0 = 1;
418				nname = jp[j].jp_flags & JP_BOOL
419				    ? noname(jp[j].jp_name)
420				    : nononame(jp[j].jp_name);
421				if (nname == NULL) {
422					njp = j;
423					jid = -1;
424					goto done;
425				}
426				jiov[i - 1].iov_base = nname;
427				jiov[i - 1].iov_len = strlen(nname) + 1;
428
429			}
430		} else {
431			jiov[i].iov_base = jp[j].jp_value;
432			jiov[i].iov_len =
433			    (jp[j].jp_ctltype & CTLTYPE) == CTLTYPE_STRING
434			    ? strlen(jp[j].jp_value) + 1
435			    : jp[j].jp_valuelen;
436		}
437		i++;
438	}
439	*(const void **)&jiov[i].iov_base = "errmsg";
440	jiov[i].iov_len = sizeof("errmsg");
441	i++;
442	jiov[i].iov_base = jail_errmsg;
443	jiov[i].iov_len = JAIL_ERRMSGLEN;
444	i++;
445	jail_errmsg[0] = 0;
446	jid = jail_set(jiov, i, flags);
447	if (jid < 0 && !jail_errmsg[0])
448		snprintf(jail_errmsg, sizeof(jail_errmsg), "jail_set: %s",
449		    strerror(errno));
450 done:
451	if (bool0)
452		for (j = 0; j < njp; j++)
453			if ((jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) &&
454			    jp[j].jp_value != NULL &&
455			    jp[j].jp_valuelen == sizeof(int) &&
456			    !*(int *)jp[j].jp_value)
457				free(jiov[j * 2].iov_base);
458	return (jid);
459}
460
461int
462jailparam_get(struct jailparam *jp, unsigned njp, int flags)
463{
464	struct iovec *jiov;
465	struct jailparam *jp_lastjid, *jp_jid, *jp_name, *jp_key;
466	int i, ai, ki, jid, arrays, sanity;
467	unsigned j;
468
469	/*
470	 * Get the types for all parameters.
471	 * Find the key and any array parameters.
472	 */
473	jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1));
474	jp_lastjid = jp_jid = jp_name = NULL;
475	arrays = 0;
476	for (ai = j = 0; j < njp; j++) {
477		if (!jp[j].jp_ctltype && jailparam_type(jp + j) < 0)
478			return (-1);
479		if (!strcmp(jp[j].jp_name, "lastjid"))
480			jp_lastjid = jp + j;
481		else if (!strcmp(jp[j].jp_name, "jid"))
482			jp_jid = jp + j;
483		else if (!strcmp(jp[j].jp_name, "name"))
484			jp_name = jp + j;
485		else if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) {
486			arrays = 1;
487			jiov[ai].iov_base = jp[j].jp_name;
488			jiov[ai].iov_len = strlen(jp[j].jp_name) + 1;
489			ai++;
490			jiov[ai].iov_base = NULL;
491			jiov[ai].iov_len = 0;
492			ai++;
493		}
494	}
495	jp_key = jp_lastjid ? jp_lastjid :
496	    jp_jid && jp_jid->jp_valuelen == sizeof(int) &&
497	    *(int *)jp_jid->jp_value ? jp_jid : jp_name;
498	if (jp_key == NULL || jp_key->jp_value == NULL) {
499		strlcpy(jail_errmsg, "no jail specified", JAIL_ERRMSGLEN);
500		errno = ENOENT;
501		return (-1);
502	}
503	ki = ai;
504	jiov[ki].iov_base = jp_key->jp_name;
505	jiov[ki].iov_len = strlen(jp_key->jp_name) + 1;
506	ki++;
507	jiov[ki].iov_base = jp_key->jp_value;
508	jiov[ki].iov_len = (jp_key->jp_ctltype & CTLTYPE) == CTLTYPE_STRING
509	    ? strlen(jp_key->jp_value) + 1 : jp_key->jp_valuelen;
510	ki++;
511	*(const void **)&jiov[ki].iov_base = "errmsg";
512	jiov[ki].iov_len = sizeof("errmsg");
513	ki++;
514	jiov[ki].iov_base = jail_errmsg;
515	jiov[ki].iov_len = JAIL_ERRMSGLEN;
516	ki++;
517	jail_errmsg[0] = 0;
518	if (arrays && jail_get(jiov, ki, flags) < 0) {
519		if (!jail_errmsg[0])
520			snprintf(jail_errmsg, sizeof(jail_errmsg),
521			    "jail_get: %s", strerror(errno));
522		return (-1);
523	}
524	/* Allocate storage for all parameters. */
525	for (ai = j = 0, i = ki; j < njp; j++) {
526		if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) {
527			ai++;
528			jiov[ai].iov_len += jp[j].jp_elemlen * ARRAY_SLOP;
529			if (jp[j].jp_valuelen >= jiov[ai].iov_len)
530				jiov[ai].iov_len = jp[j].jp_valuelen;
531			else {
532				jp[j].jp_valuelen = jiov[ai].iov_len;
533				if (jp[j].jp_value != NULL)
534					free(jp[j].jp_value);
535				jp[j].jp_value = malloc(jp[j].jp_valuelen);
536				if (jp[j].jp_value == NULL) {
537					strerror_r(errno, jail_errmsg,
538					    JAIL_ERRMSGLEN);
539					return (-1);
540				}
541			}
542			jiov[ai].iov_base = jp[j].jp_value;
543			memset(jiov[ai].iov_base, 0, jiov[ai].iov_len);
544			ai++;
545		} else if (jp + j != jp_key) {
546			jiov[i].iov_base = jp[j].jp_name;
547			jiov[i].iov_len = strlen(jp[j].jp_name) + 1;
548			i++;
549			if (jp[j].jp_value == NULL &&
550			    !(jp[j].jp_flags & JP_RAWVALUE)) {
551				jp[j].jp_value = malloc(jp[j].jp_valuelen);
552				if (jp[j].jp_value == NULL) {
553					strerror_r(errno, jail_errmsg,
554					    JAIL_ERRMSGLEN);
555					return (-1);
556				}
557			}
558			jiov[i].iov_base = jp[j].jp_value;
559			jiov[i].iov_len = jp[j].jp_valuelen;
560			memset(jiov[i].iov_base, 0, jiov[i].iov_len);
561			i++;
562		}
563	}
564	/*
565	 * Get the prison.  If there are array elements, retry a few times
566	 * in case their sizes changed from under us.
567	 */
568	for (sanity = 0;; sanity++) {
569		jid = jail_get(jiov, i, flags);
570		if (jid >= 0 || !arrays || sanity == ARRAY_SANITY ||
571		    errno != EINVAL || jail_errmsg[0])
572			break;
573		for (ai = j = 0; j < njp; j++) {
574			if (jp[j].jp_elemlen &&
575			    !(jp[j].jp_flags & JP_RAWVALUE)) {
576				ai++;
577				jiov[ai].iov_base = NULL;
578				jiov[ai].iov_len = 0;
579				ai++;
580			}
581		}
582		if (jail_get(jiov, ki, flags) < 0)
583			break;
584		for (ai = j = 0; j < njp; j++) {
585			if (jp[j].jp_elemlen &&
586			    !(jp[j].jp_flags & JP_RAWVALUE)) {
587				ai++;
588				jiov[ai].iov_len +=
589				    jp[j].jp_elemlen * ARRAY_SLOP;
590				if (jp[j].jp_valuelen >= jiov[ai].iov_len)
591					jiov[ai].iov_len = jp[j].jp_valuelen;
592				else {
593					jp[j].jp_valuelen = jiov[ai].iov_len;
594					if (jp[j].jp_value != NULL)
595						free(jp[j].jp_value);
596					jp[j].jp_value =
597					    malloc(jiov[ai].iov_len);
598					if (jp[j].jp_value == NULL) {
599						strerror_r(errno, jail_errmsg,
600						    JAIL_ERRMSGLEN);
601						return (-1);
602					}
603				}
604				jiov[ai].iov_base = jp[j].jp_value;
605				memset(jiov[ai].iov_base, 0, jiov[ai].iov_len);
606				ai++;
607			}
608		}
609	}
610	if (jid < 0 && !jail_errmsg[0])
611		snprintf(jail_errmsg, sizeof(jail_errmsg),
612		    "jail_get: %s", strerror(errno));
613	for (ai = j = 0, i = ki; j < njp; j++) {
614		if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) {
615			ai++;
616			jp[j].jp_valuelen = jiov[ai].iov_len;
617			ai++;
618		} else if (jp + j != jp_key) {
619			i++;
620			jp[j].jp_valuelen = jiov[i].iov_len;
621			i++;
622		}
623	}
624	return (jid);
625}
626
627/*
628 * Convert a jail parameter's value to external form.
629 */
630char *
631jailparam_export(struct jailparam *jp)
632{
633	char *value, *tvalue, **values;
634	size_t valuelen;
635	int i, nval;
636	char valbuf[INET6_ADDRSTRLEN];
637
638	if (!jp->jp_ctltype && jailparam_type(jp) < 0)
639		return (NULL);
640	if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) {
641		value = strdup(jp->jp_value);
642		if (value == NULL)
643			strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
644		return (value);
645	}
646	nval = jp->jp_elemlen ? jp->jp_valuelen / jp->jp_elemlen : 1;
647	if (nval == 0) {
648		value = strdup("");
649		if (value == NULL)
650			strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
651		return (value);
652	}
653	values = alloca(nval * sizeof(char *));
654	valuelen = 0;
655	for (i = 0; i < nval; i++) {
656		switch (jp->jp_ctltype & CTLTYPE) {
657		case CTLTYPE_INT:
658			if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) {
659				strlcpy(valbuf,
660				    ((int *)jp->jp_value)[i] ? "true" : "false",
661				    sizeof(valbuf));
662				break;
663			}
664			snprintf(valbuf, sizeof(valbuf), "%d",
665			    ((int *)jp->jp_value)[i]);
666			break;
667		case CTLTYPE_UINT:
668			snprintf(valbuf, sizeof(valbuf), "%u",
669			    ((unsigned *)jp->jp_value)[i]);
670			break;
671		case CTLTYPE_LONG:
672			snprintf(valbuf, sizeof(valbuf), "%ld",
673			    ((long *)jp->jp_value)[i]);
674			break;
675		case CTLTYPE_ULONG:
676			snprintf(valbuf, sizeof(valbuf), "%lu",
677			    ((unsigned long *)jp->jp_value)[i]);
678			break;
679		case CTLTYPE_QUAD:
680			snprintf(valbuf, sizeof(valbuf), "%jd",
681			    (intmax_t)((int64_t *)jp->jp_value)[i]);
682			break;
683		case CTLTYPE_STRUCT:
684			switch (jp->jp_structtype) {
685			case JPS_IN_ADDR:
686				if (inet_ntop(AF_INET,
687				    &((struct in_addr *)jp->jp_value)[i],
688				    valbuf, sizeof(valbuf)) == NULL) {
689					strerror_r(errno, jail_errmsg,
690					    JAIL_ERRMSGLEN);
691
692					return (NULL);
693				}
694				break;
695			case JPS_IN6_ADDR:
696				if (inet_ntop(AF_INET6,
697				    &((struct in6_addr *)jp->jp_value)[i],
698				    valbuf, sizeof(valbuf)) == NULL) {
699					strerror_r(errno, jail_errmsg,
700					    JAIL_ERRMSGLEN);
701
702					return (NULL);
703				}
704				break;
705			default:
706				goto unknown_type;
707			}
708			break;
709		default:
710		unknown_type:
711			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
712			    "unknown type for %s", jp->jp_name);
713			errno = ENOENT;
714			return (NULL);
715		}
716		valuelen += strlen(valbuf) + 1;
717		values[i] = alloca(valuelen);
718		strcpy(values[i], valbuf);
719	}
720	value = malloc(valuelen + 1);
721	if (value == NULL)
722		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
723	else {
724		tvalue = value;
725		for (i = 0; i < nval; i++) {
726			strcpy(tvalue, values[i]);
727			if (i < nval - 1) {
728				tvalue += strlen(values[i]);
729				*tvalue++ = ',';
730			}
731		}
732	}
733	return (value);
734}
735
736/*
737 * Free the contents of a jail parameter list (but not thst list itself).
738 */
739void
740jailparam_free(struct jailparam *jp, unsigned njp)
741{
742	unsigned j;
743
744	for (j = 0; j < njp; j++) {
745		free(jp[j].jp_name);
746		if (!(jp[j].jp_flags & JP_RAWVALUE))
747			free(jp[j].jp_value);
748	}
749}
750
751/*
752 * Create and import an array of jail parameters, given a list of name and
753 * value strings, terminated by a null name.
754 */
755static int
756jailparam_vlist(struct jailparam **jpp, va_list ap)
757{
758	va_list tap;
759	struct jailparam *jp;
760	char *name, *value;
761	int njp;
762
763	va_copy(tap, ap);
764	for (njp = 0; va_arg(tap, char *) != NULL; njp++)
765		(void)va_arg(tap, char *);
766	va_end(tap);
767	jp = calloc(njp, sizeof(struct jailparam));
768	if (jp == NULL) {
769		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
770		return (-1);
771	}
772
773	for (njp = 0; (name = va_arg(ap, char *)) != NULL; njp++) {
774		value = va_arg(ap, char *);
775		if (jailparam_init(jp + njp, name) < 0 ||
776		    jailparam_import(jp + njp, value) < 0) {
777			jailparam_free(jp, njp);
778			free(jp);
779			return (-1);
780		}
781	}
782	*jpp = jp;
783	return (njp);
784}
785
786/*
787 * Find a parameter's type and size from its MIB.
788 */
789static int
790jailparam_type(struct jailparam *jp)
791{
792	char *p, *nname;
793	size_t miblen, desclen;
794	int isarray;
795	struct {
796	    int i;
797	    char s[MAXPATHLEN];
798	} desc;
799	int mib[CTL_MAXNAME];
800
801	/* The "lastjid" parameter isn't real. */
802	if (!strcmp(jp->jp_name, "lastjid")) {
803		jp->jp_valuelen = sizeof(int);
804		jp->jp_ctltype = CTLTYPE_INT | CTLFLAG_WR;
805		return (0);
806	}
807
808	/* Find the sysctl that describes the parameter. */
809	mib[0] = 0;
810	mib[1] = 3;
811	snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", jp->jp_name);
812	miblen = sizeof(mib) - 2 * sizeof(int);
813	if (sysctl(mib, 2, mib + 2, &miblen, desc.s, strlen(desc.s)) < 0) {
814		if (errno != ENOENT) {
815			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
816			    "sysctl(0.3.%s): %s", jp->jp_name, strerror(errno));
817			return (-1);
818		}
819		/*
820		 * The parameter probably doesn't exist.  But it might be
821		 * the "no" counterpart to a boolean.
822		 */
823		nname = nononame(jp->jp_name);
824		if (nname != NULL) {
825			snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname);
826			free(nname);
827			miblen = sizeof(mib) - 2 * sizeof(int);
828			if (sysctl(mib, 2, mib + 2, &miblen, desc.s,
829			    strlen(desc.s)) >= 0) {
830				mib[1] = 4;
831				desclen = sizeof(desc);
832				if (sysctl(mib, (miblen / sizeof(int)) + 2,
833					   &desc, &desclen, NULL, 0) < 0) {
834					snprintf(jail_errmsg,
835					    JAIL_ERRMSGLEN,
836					    "sysctl(0.4.%s): %s", desc.s,
837					    strerror(errno));
838					return (-1);
839				}
840				if ((desc.i & CTLTYPE) == CTLTYPE_INT &&
841				    desc.s[0] == 'B') {
842					jp->jp_ctltype = desc.i;
843					jp->jp_flags |= JP_NOBOOL;
844					jp->jp_valuelen = sizeof(int);
845					return (0);
846				}
847			}
848		}
849		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
850		    "unknown parameter: %s", jp->jp_name);
851		errno = ENOENT;
852		return (-1);
853	}
854	mib[1] = 4;
855	desclen = sizeof(desc);
856	if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen,
857	    NULL, 0) < 0) {
858		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
859		    "sysctl(0.4.%s): %s", jp->jp_name, strerror(errno));
860		return (-1);
861	}
862	/* See if this is an array type. */
863	p = strchr(desc.s, '\0');
864	isarray  = 0;
865	if (p - 2 < desc.s || strcmp(p - 2, ",a"))
866		isarray = 0;
867	else {
868		isarray = 1;
869		p[-2] = 0;
870	}
871	/* Look for types we understand */
872	jp->jp_ctltype = desc.i;
873	switch (desc.i & CTLTYPE) {
874	case CTLTYPE_INT:
875		if (desc.s[0] == 'B')
876			jp->jp_flags |=
877			    (desc.s[1] == 'N') ? JP_NOBOOL : JP_BOOL;
878	case CTLTYPE_UINT:
879		jp->jp_valuelen = sizeof(int);
880		break;
881	case CTLTYPE_LONG:
882	case CTLTYPE_ULONG:
883		jp->jp_valuelen = sizeof(long);
884		break;
885	case CTLTYPE_QUAD:
886		jp->jp_valuelen = sizeof(int64_t);
887		break;
888	case CTLTYPE_STRING:
889		desc.s[0] = 0;
890		desclen = sizeof(desc.s);
891		if (sysctl(mib + 2, miblen / sizeof(int), desc.s, &desclen,
892		    NULL, 0) < 0) {
893			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
894			    "sysctl(" SJPARAM ".%s): %s", jp->jp_name,
895			    strerror(errno));
896			return (-1);
897		}
898		jp->jp_valuelen = strtoul(desc.s, NULL, 10);
899		break;
900	case CTLTYPE_STRUCT:
901		if (!strcmp(desc.s, "S,in_addr")) {
902			jp->jp_structtype = JPS_IN_ADDR;
903			jp->jp_valuelen = sizeof(struct in_addr);
904		} else if (!strcmp(desc.s, "S,in6_addr")) {
905			jp->jp_structtype = JPS_IN6_ADDR;
906			jp->jp_valuelen = sizeof(struct in6_addr);
907		} else {
908			desclen = 0;
909			if (sysctl(mib + 2, miblen / sizeof(int),
910			    NULL, &jp->jp_valuelen, NULL, 0) < 0) {
911				snprintf(jail_errmsg, JAIL_ERRMSGLEN,
912				    "sysctl(" SJPARAM ".%s): %s", jp->jp_name,
913				    strerror(errno));
914				return (-1);
915			}
916		}
917		break;
918	case CTLTYPE_NODE:
919		/*
920		 * A node isn't normally a parameter, but may be a boolean
921		 * if its "no" counterpart exists.
922		 */
923		nname = noname(jp->jp_name);
924		if (nname == NULL)
925			return (-1);
926		mib[1] = 3;
927		snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname);
928		free(nname);
929		miblen = sizeof(mib) - 2 * sizeof(int);
930		if (sysctl(mib, 2, mib + 2, &miblen, desc.s,
931		    strlen(desc.s)) < 0) {
932			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
933				 "unknown parameter: %s", jp->jp_name);
934			return (-1);
935		}
936		mib[1] = 4;
937		desclen = sizeof(desc);
938		if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen,
939		    NULL, 0) < 0) {
940			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
941			    "sysctl(0.4.%s): %s", desc.s, strerror(errno));
942			return (-1);
943		}
944		if ((desc.i & CTLTYPE) != CTLTYPE_INT || desc.s[0] != 'B') {
945			snprintf(jail_errmsg, JAIL_ERRMSGLEN,
946				 "unknown parameter: %s", jp->jp_name);
947			errno = ENOENT;
948			return (-1);
949		}
950		jp->jp_valuelen = sizeof(int);
951		jp->jp_ctltype = desc.i;
952		jp->jp_flags |= JP_BOOL;
953		break;
954	default:
955		snprintf(jail_errmsg, JAIL_ERRMSGLEN,
956		    "unknown type for %s", jp->jp_name);
957		errno = ENOENT;
958		return (-1);
959	}
960	if (isarray) {
961		jp->jp_elemlen = jp->jp_valuelen;
962		jp->jp_valuelen = 0;
963	}
964	return (0);
965}
966
967/*
968 * Change a boolean parameter name into its "no" counterpart or vice versa.
969 */
970static char *
971noname(const char *name)
972{
973	char *nname, *p;
974
975	nname = malloc(strlen(name) + 3);
976	if (nname == NULL) {
977		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
978		return (NULL);
979	}
980	p = strrchr(name, '.');
981	if (p != NULL)
982		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
983	else
984		sprintf(nname, "no%s", name);
985	return (nname);
986}
987
988static char *
989nononame(const char *name)
990{
991	char *p, *nname;
992
993	p = strrchr(name, '.');
994	if (strncmp(p ? p + 1 : name, "no", 2)) {
995		snprintf(jail_errmsg, sizeof(jail_errmsg),
996		    "mismatched boolean: %s", name);
997		errno = EINVAL;
998		return (NULL);
999	}
1000	nname = malloc(strlen(name) - 1);
1001	if (nname == NULL) {
1002		strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
1003		return (NULL);
1004	}
1005	if (p != NULL)
1006		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
1007	else
1008		strcpy(nname, name + 2);
1009	return (nname);
1010}
1011