From de6f37045c093865da6d233be60dbc3db241f383 Mon Sep 17 00:00:00 2001 From: Jamie Gritton Date: Wed, 24 Jun 2009 18:18:35 +0000 Subject: [PATCH] Add libjail, a (somewhat) simpler interface to the jail_set and jail_get system calls and the security.jail.param sysctls. Approved by: bz (mentor) --- gnu/usr.bin/groff/tmac/mdoc.local | 1 + lib/Makefile | 4 +- lib/libjail/Makefile | 29 + lib/libjail/jail.3 | 275 +++++++++ lib/libjail/jail.c | 972 ++++++++++++++++++++++++++++++ lib/libjail/jail.h | 68 +++ lib/libjail/jail_getid.c | 104 ++++ share/mk/bsd.libnames.mk | 1 + usr.bin/killall/Makefile | 2 + usr.bin/killall/killall.c | 17 +- usr.sbin/jail/Makefile | 4 +- usr.sbin/jail/jail.c | 205 +------ usr.sbin/jexec/Makefile | 4 +- usr.sbin/jexec/jexec.c | 20 +- usr.sbin/jls/Makefile | 3 + usr.sbin/jls/jls.c | 577 +++++------------- 16 files changed, 1634 insertions(+), 652 deletions(-) create mode 100644 lib/libjail/Makefile create mode 100644 lib/libjail/jail.3 create mode 100644 lib/libjail/jail.c create mode 100644 lib/libjail/jail.h create mode 100644 lib/libjail/jail_getid.c diff --git a/gnu/usr.bin/groff/tmac/mdoc.local b/gnu/usr.bin/groff/tmac/mdoc.local index b3d428877543..a963b7e77704 100644 --- a/gnu/usr.bin/groff/tmac/mdoc.local +++ b/gnu/usr.bin/groff/tmac/mdoc.local @@ -47,6 +47,7 @@ .ds doc-str-Lb-libfetch File Transfer Library (libfetch, \-lfetch) .ds doc-str-Lb-libgeom Userland API Library for kernel GEOM subsystem (libgeom, \-lgeom) .ds doc-str-Lb-libipx IPX Address Conversion Support Library (libipx, \-lipx) +.ds doc-str-Lb-libjail Jail Library (libjail, \-ljail) .ds doc-str-Lb-libkiconv Kernel side iconv library (libkiconv, \-lkiconv) .ds doc-str-Lb-libkse N:M Threading Library (libkse, \-lkse) .ds doc-str-Lb-libmd Message Digest (MD4, MD5, etc.) Support Library (libmd, \-lmd) diff --git a/lib/Makefile b/lib/Makefile index ec866e527008..7cacb704f6a4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,8 +35,8 @@ SUBDIR= ${_csu} libc libbsm libauditd libcom_err libcrypt libelf libkvm msun \ libcalendar libcam libcompat libdevinfo libdevstat libdisk \ libdwarf libedit libexpat libfetch libftpio libgeom ${_libgpib} \ ${_libgssapi} ${_librpcsec_gss} libipsec \ - ${_libipx} libkiconv libmagic libmemstat ${_libmilter} ${_libmp} \ - ${_libncp} ${_libngatm} libopie libpam libpcap \ + ${_libipx} libjail libkiconv libmagic libmemstat ${_libmilter} \ + ${_libmp} ${_libncp} ${_libngatm} libopie libpam libpcap \ ${_libpmc} libproc librt ${_libsdp} ${_libsm} ${_libsmb} \ ${_libsmdb} \ ${_libsmutil} libstand ${_libtelnet} ${_libthr} libthread_db libufs \ diff --git a/lib/libjail/Makefile b/lib/libjail/Makefile new file mode 100644 index 000000000000..678d59e08014 --- /dev/null +++ b/lib/libjail/Makefile @@ -0,0 +1,29 @@ +# $FreeBSD$ + +LIB= jail +SHLIBDIR?= /lib +SHLIB_MAJOR= 1 +SRCS= jail.c jail_getid.c +INCS= jail.h + +MAN= jail.3 + +MLINKS+=jail.3 jail_getid.3 +MLINKS+=jail.3 jail_getname.3 +MLINKS+=jail.3 jail_getv.3 +MLINKS+=jail.3 jail_setv.3 +MLINKS+=jail.3 jailparam.3 +MLINKS+=jail.3 jailparam_all.3 +MLINKS+=jail.3 jailparam_init.3 +MLINKS+=jail.3 jailparam_import.3 +MLINKS+=jail.3 jailparam_import_raw.3 +MLINKS+=jail.3 jailparam_get.3 +MLINKS+=jail.3 jailparam_set.3 +MLINKS+=jail.3 jailparam_export.3 +MLINKS+=jail.3 jailparam_free.3 + +CFLAGS+=-I${.CURDIR} + +WARNS?= 6 + +.include diff --git a/lib/libjail/jail.3 b/lib/libjail/jail.3 new file mode 100644 index 000000000000..7fec5856d2d2 --- /dev/null +++ b/lib/libjail/jail.3 @@ -0,0 +1,275 @@ +.\" Copyright (c) 2009 James Gritton. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 24, 2009 +.Dt JAIL 3 +.Os +.Sh NAME +.Nm jail_getid , +.Nm jail_getname , +.Nm jail_setv , +.Nm jail_getv , +.Nm jailparam_all , +.Nm jailparam_init , +.Nm jailparam_import , +.Nm jailparam_import_raw , +.Nm jailparam_set , +.Nm jailparam_get , +.Nm jailparam_export , +.Nm jailparam_free , +.Nd create and manage system jails +.Sh LIBRARY +.Lb libjail +.Sh SYNOPSIS +.In sys/param.h +.In sys/jail.h +.In jail.h +.Vt extern char jail_errmsg[] ; +.Ft int +.Fn jail_getid "const char *name" +.Ft char * +.Fn jail_getname "int jid" +.Ft int +.Fn jail_setv "int flags" ... +.Ft int +.Fn jail_getv "int flags" ... +.Ft int +.Fn jailparam_all "struct jailparam **jpp" +.Ft int +.Fn jailparam_init "struct jailparam *jp" "const char *name" +.Ft int +.Fn jailparam_import "struct jailparam *jp" "const char *value" +.Ft int +.Fn jailparam_import_raw "struct jailparam *jp" "void *value" "size_t valuelen" +.Ft int +.Fn jailparam_set "struct jailparam *jp" "unsigned njp" "int flags" +.Ft int +.Fn jailparam_get "struct jailparam *jp" "unsigned njp" "int flags" +.Ft char * +.Fn jailparam_export "struct jailparam *jp" +.Ft void +.Fn jailparam_free "struct jailparam *jp" "unsigned njp" +.Sh DESCRIPTION +The +.Nm jail +library is an interface to the +.Xr jail_set 2 +and +.Xr jail_get 2 +system calls, and the +.Va security.jail.param +MIB entries. +It simplifies the conversion of prison parameters between internal and +string formats, allowing the setting and querying of prisons without +knowing the parameter formats. +.Pp +The +.Fn jail_getid +function returns the JID of the jail identified by +.Ar name , +or \-1 if the jail does not exist. +.Pp +The +.Fn jail_getname +function returns the name of the jail identified by +.Ar jid , +or NULL if the jail does not exist. +.Pp +The +.Fn jail_setv +function takes a null-terminated list of name and value strings, +and passes it to +.Xr jail_set 2 . +.Pp +The +.Fn jail_getv +function takes a null-terminated list of name and value strings, +and passes it to +.Xr jail_get 2 . +It is the caller's responsibility to ensure that the value strings point +to buffers large enough to hold the string representation of the +returned parameters. +.Pp +The +.Fn jailparam_all +function sets +.Ar jpp +to a list of all known jail parameters, and returns the number of +parameters. +The list should later be freed with +.Fn jailparam_free +and +.Xr free 3 . +.Pp +The +.Fn jailparam_init +function clears a parameter record and copies the +.Ar name +to it. After use, it should be freed with +.Fn jailparam_free . +.Pp +The +.Fn jailparam_import +function adds a +.Ar value +to a parameter record, converting it from a string to its native form. +The +.Fn jailparam_import_raw +function adds a value without performing any conversion. +.Pp +The +.Fn jailparam_set +function passes a list of parameters to +.Xr jail_set 2 . +The parameters are assumed to have been created with +.Fn jailparam_init +and +.Fn jailparam_import . +.Pp +The +.Fn jailparam_get +function function passes a list of parameters to +.Xr jail_get 2 . +The parameters are assumed to have been created with +.Fn jailparam_init +or +.Fn jailparam_list , +with one parameter (the key) having been given a value with +.Fn jailparam_import . +.Pp +The +.Fn jailparam_export +function returns the string equivalent of a parameter value. +The returned string should freed after use. +.Pp +The +.Fn jailparam_free +function frees the stored names and values in a parameter list. +If the list itself came from +.Fn jailparam_all , +it should be freed as well. +.Sh EXAMPLES +Set the hostname of jail +.Dq foo +to +.Dq foo.bar : +.Bd -literal -offset indent +jail_setv(JAIL_UPDATE, "name", "foo", "host.hostname", "foo.bar", + NULL); +.Ed +.Pp +OR: +.Bd -literal -offset indent +struct jailparam params[2]; +jailparam_init(¶ms[0], "name"); +jailparam_import(¶ms[0], "foo"); +jailparam_init(¶ms[1], "host.hostname"); +jailparam_import(¶ms[1], "foo.bar"); +jailparam_set(params, 2, JAIL_UPDATE); +jailparam_free(params, 2); +.Ed +.Pp +Retrieve the hostname of jail +.Dq foo : +.Bd -literal -offset indent +char hostname[MAXHOSTNAMELEN]; +jail_getv(0, "name", "foo", "host.hostname", hostname, NULL); +.Ed +.Pp +OR: +.Bd -literal -offset indent +struct jailparam params[2]; +jailparam_init(¶ms[0], "name"); +jailparam_import(¶ms[0], "foo"); +jailparam_init(¶ms[1], "host.hostname"); +jailparam_get(params, 2, 0); +hostname = jailparam_export(¶ms[1]); +jailparam_free(params, 2); +.Ed +.Sh RETURN VALUES +The +.Fn jail_getid , +.Fn jail_setv , +.Fn jail_getv , +.Fn jailparam_set +and +.Fn jailparam_get +functions return a JID on success, or \-1 on error. +.Pp +The +.Fn jail_getname +and +.Fn jailparam_export +functions return a dynamically allocated string on success, or NULL on error. +.Pp +The +.Fn jailparam_all +function returns the number of parameters on success, or \-1 on error. +.Pp +The +.Fn jailparam_init , +.Fn jailparam_import +and +.Fn jailparam_import_raw +functions return 0 on success, or \-1 on error. +.Pp +Whenever an error is returned, +.Va errno +is set, and the global string +.Va jail_errmsg +contains a descrption of the error, possibly from +.Xr jail_set 2 +or +.Xr jail_get 2 . +.Sh ERRORS +The +.Nm jail +functions may return errors from +.Xr jail_set 2 , +.Xr jail_get 2 , +.Xr malloc 3 +or +.Xr sysctl 3 . +In addition, the following errors are possible: +.Bl -tag -width Er +.It Bq Er EINVAL +A prameter value cannot be convert from the passed string to its +internal form. +.It Bq Er ENOENT +The named parameter does not exist. +.It Bq Er ENOENT +A parameter is of an unknown type. +.Sh SEE ALSO +.Xr jail 2 , +.Xr jail 8 , +.Xr sysctl 3 +.Sh HISTORY +The +.Nm jail +library first appeared in +.Fx 8.0 . +.Sh AUTHORS +.An James Gritton diff --git a/lib/libjail/jail.c b/lib/libjail/jail.c new file mode 100644 index 000000000000..f4a173a64904 --- /dev/null +++ b/lib/libjail/jail.c @@ -0,0 +1,972 @@ +/*- + * Copyright (c) 2009 James Gritton. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "jail.h" + +#define SJPARAM "security.jail.param" + +#define JPS_IN_ADDR 1 +#define JPS_IN6_ADDR 2 + +#define ARRAY_SANITY 5 +#define ARRAY_SLOP 5 + + +static int jailparam_vlist(struct jailparam **jpp, va_list ap); +static int jailparam_type(struct jailparam *jp); +static char *noname(const char *name); +static char *nononame(const char *name); + +char jail_errmsg[JAIL_ERRMSGLEN]; + + +/* + * Import a null-terminated parameter list and set a jail with the flags + * and parameters. + */ +int +jail_setv(int flags, ...) +{ + va_list ap; + struct jailparam *jp; + int njp; + + va_start(ap, flags); + njp = jailparam_vlist(&jp, ap); + va_end(ap); + if (njp < 0) + return (njp); + return (jailparam_set(jp, njp, flags)); +} + +/* + * Read a null-terminated parameter list, get the referenced jail, and export + * the parameters to the list. + */ +int +jail_getv(int flags, ...) +{ + va_list ap, tap; + struct jailparam *jp; + char *valarg; + const char *value; + int njp, i, jid, namekey, zero; + + va_start(ap, flags); + va_copy(tap, ap); + njp = jailparam_vlist(&jp, tap); + va_end(tap); + if (njp < 0) + return (njp); + /* + * See if the name is the search key. If so, we don't want to write + * it back in case it's a read-only string. + */ + namekey = 1; + zero = 0; + for (i = 0; i < njp; i++) { + if (!strcmp(jp->jp_name, "lastjid") || + (!strcmp(jp->jp_name, "jid") && + memcmp(jp->jp_value, &zero, sizeof(zero)))) + namekey = 0; + } + jid = jailparam_get(jp, njp, flags); + if (jid < 0) { + va_end(ap); + return (-1); + } + for (i = 0; i < njp; i++) { + (void)va_arg(ap, char *); + value = jailparam_export(jp + i); + if (value == NULL) { + va_end(ap); + return (-1); + } + valarg = va_arg(ap, char *); + if (!namekey || strcmp(jp[i].jp_name, "name")) + /* It's up to the caller to ensure there's room. */ + strcpy(valarg, value); + } + va_end(ap); + return (jid); +} + +/* + * Return a list of all known parameters. + */ +int +jailparam_all(struct jailparam **jpp) +{ + struct jailparam *jp; + char *nname; + size_t mlen1, mlen2, buflen; + int njp, nlist; + int mib1[CTL_MAXNAME], mib2[CTL_MAXNAME - 2]; + char buf[MAXPATHLEN]; + + njp = 0; + nlist = 32; + jp = malloc(nlist * sizeof(*jp)); + if (jp == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (-1); + } + mib1[0] = 0; + mib1[1] = 2; + mlen1 = CTL_MAXNAME - 2; + if (sysctlnametomib(SJPARAM, mib1 + 2, &mlen1) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctlnametomib(" SJPARAM "): %s", strerror(errno)); + goto error; + } + for (;; njp++) { + /* Get the next parameter. */ + mlen2 = sizeof(mib2); + if (sysctl(mib1, mlen1 + 2, mib2, &mlen2, NULL, 0) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(0.2): %s", strerror(errno)); + goto error; + } + if (mib2[0] != mib1[2] || mib2[1] != mib1[3] || + mib2[2] != mib1[4]) + break; + /* Convert it to an ascii name. */ + memcpy(mib1 + 2, mib2, mlen2); + mlen1 = mlen2 / sizeof(int); + mib1[1] = 1; + buflen = sizeof(buf); + if (sysctl(mib1, mlen1 + 2, buf, &buflen, NULL, 0) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(0.1): %s", strerror(errno)); + goto error; + } + /* Add the parameter to the list */ + if (njp >= nlist) { + nlist *= 2; + jp = realloc(jp, nlist * sizeof(jp)); + if (jp == NULL) { + jailparam_free(jp, njp); + return (-1); + } + } + if (jailparam_init(jp + njp, buf + sizeof(SJPARAM)) < 0) + goto error; + if (jailparam_type(jp + njp) < 0) { + njp++; + goto error; + } + /* Convert nobool parameters to bool. */ + if (jp[njp].jp_flags & JP_NOBOOL) { + nname = nononame(jp[njp].jp_name); + if (nname == NULL) { + njp++; + goto error; + } + free(jp[njp].jp_name); + jp[njp].jp_name = nname; + jp[njp].jp_flags ^= JP_BOOL | JP_NOBOOL; + } + mib1[1] = 2; + } + jp = realloc(jp, njp * sizeof(*jp)); + *jpp = jp; + return (njp); + + error: + jailparam_free(jp, njp); + free(jp); + return (-1); +} + +/* + * Clear a jail parameter and copy in its name. + */ +int +jailparam_init(struct jailparam *jp, const char *name) +{ + + memset(jp, 0, sizeof(*jp)); + jp->jp_name = strdup(name); + if (jp->jp_name == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (-1); + } + return (0); +} + +/* + * Put a name and value into a jail parameter element, converting the value + * to internal form. + */ +int +jailparam_import(struct jailparam *jp, const char *value) +{ + char *p, *ep, *tvalue; + const char *avalue; + int i, nval, fw; + + if (!jp->jp_ctltype && jailparam_type(jp) < 0) + goto error; + if (value == NULL) + return (0); + if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) { + jp->jp_value = strdup(value); + if (!jp->jp_value) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + goto error; + } + return (0); + } + nval = 1; + if (jp->jp_elemlen) { + if (value[0] == '\0' || (value[0] == '-' && value[1] == '\0')) { + jp->jp_value = strdup(""); + if (value == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + goto error; + } + jp->jp_valuelen = 0; + return (0); + } + for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) + nval++; + jp->jp_valuelen = jp->jp_elemlen * nval; + } + jp->jp_value = malloc(jp->jp_valuelen); + if (!jp->jp_value) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + goto error; + } + avalue = value; + for (i = 0; i < nval; i++) { + fw = nval == 1 ? strlen(avalue) : strcspn(avalue, ","); + switch (jp->jp_ctltype & CTLTYPE) { + case CTLTYPE_INT: + if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) { + if (!strncasecmp(avalue, "true", 4)) + ((int *)jp->jp_value)[i] = 1; + else if (!strncasecmp(avalue, "false", 5)) + ((int *)jp->jp_value)[i] = 0; + else { + snprintf(jail_errmsg, + JAIL_ERRMSGLEN, + "%s: unknown boolean value \"%.*s\"", + jp->jp_name, fw, avalue); + errno = EINVAL; + goto error; + } + break; + } + ((int *)jp->jp_value)[i] = strtol(avalue, &ep, 10); + integer_test: + if (ep != avalue + fw) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "%s: non-integer value \"%.*s\"", + jp->jp_name, fw, avalue); + errno = EINVAL; + goto error; + } + break; + case CTLTYPE_UINT: + ((unsigned *)jp->jp_value)[i] = + strtoul(avalue, &ep, 10); + goto integer_test; + case CTLTYPE_LONG: + ((long *)jp->jp_value)[i] = strtol(avalue, &ep, 10); + goto integer_test; + case CTLTYPE_ULONG: + ((unsigned long *)jp->jp_value)[i] = + strtoul(avalue, &ep, 10); + goto integer_test; + case CTLTYPE_QUAD: + ((int64_t *)jp->jp_value)[i] = + strtoimax(avalue, &ep, 10); + goto integer_test; + case CTLTYPE_STRUCT: + tvalue = alloca(fw + 1); + strlcpy(tvalue, avalue, fw + 1); + switch (jp->jp_structtype) { + case JPS_IN_ADDR: + if (inet_pton(AF_INET, tvalue, + &((struct in_addr *)jp->jp_value)[i]) != 1) + { + snprintf(jail_errmsg, + JAIL_ERRMSGLEN, + "%s: not an IPv4 address: %s", + jp->jp_name, tvalue); + errno = EINVAL; + goto error; + } + break; + case JPS_IN6_ADDR: + if (inet_pton(AF_INET6, tvalue, + &((struct in6_addr *)jp->jp_value)[i]) != 1) + { + snprintf(jail_errmsg, + JAIL_ERRMSGLEN, + "%s: not an IPv6 address: %s", + jp->jp_name, tvalue); + errno = EINVAL; + goto error; + } + break; + default: + goto unknown_type; + } + break; + default: + unknown_type: + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "unknown type for %s", jp->jp_name); + errno = ENOENT; + goto error; + } + avalue += fw + 1; + } + return (0); + + error: + free(jp->jp_value); + jp->jp_value = NULL; + return (-1); +} + +/* + * Put a name and value into a jail parameter element, copying the value + * but not altering it. + */ +int +jailparam_import_raw(struct jailparam *jp, void *value, size_t valuelen) +{ + + jp->jp_value = value; + jp->jp_valuelen = valuelen; + jp->jp_flags |= JP_RAWVALUE; + return (0); +} + +/* + * Run the jail_set and jail_get system calls on a parameter list. + */ +int +jailparam_set(struct jailparam *jp, unsigned njp, int flags) +{ + struct iovec *jiov; + char *nname; + int i, jid; + unsigned j; + + jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1)); + for (i = j = 0; j < njp; j++) { + jiov[i].iov_base = jp[j].jp_name; + jiov[i].iov_len = strlen(jp[j].jp_name) + 1; + i++; + if (jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) { + /* + * Set booleans without values. If one have a value of + * zero, change it to (or from) its "no" counterpart. + */ + jiov[i].iov_base = NULL; + jiov[i].iov_len = 0; + if (jp[j].jp_value != NULL && + jp[j].jp_valuelen == sizeof(int) && + !*(int *)jp[j].jp_value) { + nname = jp[j].jp_flags & JP_BOOL + ? noname(jiov[i].iov_base) + : nononame(jiov[i].iov_base); + if (nname == NULL) + return (-1); + free(jp[j].jp_name); + jiov[i].iov_base = jp[j].jp_name = nname; + } + } else { + jiov[i].iov_base = jp[j].jp_value; + jiov[i].iov_len = + (jp[j].jp_ctltype & CTLTYPE) == CTLTYPE_STRING + ? strlen(jp[j].jp_value) + 1 + : jp[j].jp_valuelen; + } + i++; + } + *(const void **)&jiov[i].iov_base = "errmsg"; + jiov[i].iov_len = sizeof("errmsg"); + i++; + jiov[i].iov_base = jail_errmsg; + jiov[i].iov_len = JAIL_ERRMSGLEN; + i++; + jail_errmsg[0] = 0; + jid = jail_set(jiov, i, flags); + if (jid < 0 && !jail_errmsg[0]) + snprintf(jail_errmsg, sizeof(jail_errmsg), "jail_set: %s", + strerror(errno)); + return (jid); +} + +int +jailparam_get(struct jailparam *jp, unsigned njp, int flags) +{ + struct iovec *jiov; + struct jailparam *jp_lastjid, *jp_jid, *jp_name, *jp_key; + int i, ai, ki, jid, arrays, sanity; + unsigned j; + + /* Find the key and any array parameters. */ + jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1)); + jp_lastjid = jp_jid = jp_name = NULL; + arrays = 0; + for (ai = j = 0; j < njp; j++) { + if (!strcmp(jp[j].jp_name, "lastjid")) + jp_lastjid = jp + j; + else if (!strcmp(jp[j].jp_name, "jid")) + jp_jid = jp + j; + else if (!strcmp(jp[j].jp_name, "name")) + jp_name = jp + j; + else if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { + arrays = 1; + jiov[ai].iov_base = jp[j].jp_name; + jiov[ai].iov_len = strlen(jp[j].jp_name) + 1; + ai++; + jiov[ai].iov_base = NULL; + jiov[ai].iov_len = 0; + ai++; + } + } + jp_key = jp_lastjid ? jp_lastjid : + jp_jid && jp_jid->jp_valuelen == sizeof(int) && + *(int *)jp_jid->jp_value ? jp_jid : jp_name; + if (jp_key == NULL || jp_key->jp_value == NULL) { + strlcpy(jail_errmsg, "no jail specified", JAIL_ERRMSGLEN); + errno = ENOENT; + return (-1); + } + ki = ai; + jiov[ki].iov_base = jp_key->jp_name; + jiov[ki].iov_len = strlen(jp_key->jp_name) + 1; + ki++; + jiov[ki].iov_base = jp_key->jp_value; + jiov[ki].iov_len = (jp_key->jp_ctltype & CTLTYPE) == CTLTYPE_STRING + ? strlen(jp_key->jp_value) + 1 : jp_key->jp_valuelen; + ki++; + *(const void **)&jiov[ki].iov_base = "errmsg"; + jiov[ki].iov_len = sizeof("errmsg"); + ki++; + jiov[ki].iov_base = jail_errmsg; + jiov[ki].iov_len = JAIL_ERRMSGLEN; + ki++; + jail_errmsg[0] = 0; + if (arrays && jail_get(jiov, ki, flags) < 0) { + if (!jail_errmsg[0]) + snprintf(jail_errmsg, sizeof(jail_errmsg), + "jail_get: %s", strerror(errno)); + return (-1); + } + /* Allocate storage for all parameters. */ + for (ai = j = 0, i = ki; j < njp; j++) { + if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { + ai++; + jiov[ai].iov_len += jp[j].jp_elemlen * ARRAY_SLOP; + jiov[ai].iov_base = jp[j].jp_value = + malloc(jiov[ai].iov_len); + if (jiov[ai].iov_base == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (-1); + } + memset(jiov[ai].iov_base, 0, jiov[ai].iov_len); + ai++; + } else if (jp + j != jp_key) { + jiov[i].iov_base = jp[j].jp_name; + jiov[i].iov_len = strlen(jp[j].jp_name) + 1; + i++; + jiov[i].iov_base = jp[j].jp_value = + malloc(jp[j].jp_valuelen); + if (jiov[i].iov_base == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (-1); + } + jiov[i].iov_len = jp[j].jp_valuelen; + memset(jiov[i].iov_base, 0, jiov[i].iov_len); + i++; + } + } + /* + * Get the prison. If there are array elements, retry a few times + * in case their sizes changed from under us. + */ + for (sanity = 0;; sanity++) { + jid = jail_get(jiov, i, flags); + if (jid >= 0 || !arrays || sanity == ARRAY_SANITY || + errno != EINVAL || jail_errmsg[0]) + break; + for (ai = j = 0; j < njp; j++) { + if (jp[j].jp_elemlen && + !(jp[j].jp_flags & JP_RAWVALUE)) { + ai++; + free(jiov[ai].iov_base); + jiov[ai].iov_base = jp[j].jp_value = NULL; + jiov[ai].iov_len = 0; + ai++; + } + } + if (jail_get(jiov, ki, flags) < 0) + break; + for (ai = j = 0; j < njp; j++) { + if (jp[j].jp_elemlen && + !(jp[j].jp_flags & JP_RAWVALUE)) { + ai++; + jiov[ai].iov_len += + jp[j].jp_elemlen * ARRAY_SLOP; + jiov[ai].iov_base = jp[j].jp_value = + malloc(jiov[ai].iov_len); + if (jiov[ai].iov_base == NULL) { + strerror_r(errno, jail_errmsg, + JAIL_ERRMSGLEN); + return (-1); + } + memset(jiov[ai].iov_base, 0, jiov[ai].iov_len); + ai++; + } + } + } + if (jid < 0 && !jail_errmsg[0]) + snprintf(jail_errmsg, sizeof(jail_errmsg), + "jail_get: %s", strerror(errno)); + for (ai = j = 0, i = ki; j < njp; j++) { + if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { + ai++; + jp[j].jp_valuelen = jiov[ai].iov_len; + ai++; + } else if (jp + j != jp_key) { + i++; + jp[j].jp_valuelen = jiov[i].iov_len; + i++; + } + } + return (jid); +} + +/* + * Convert a jail parameter's value to external form. + */ +char * +jailparam_export(struct jailparam *jp) +{ + char *value, *tvalue, **values; + size_t valuelen; + int i, nval; + char valbuf[INET6_ADDRSTRLEN]; + + if (!jp->jp_ctltype && jailparam_type(jp) < 0) + return (NULL); + if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) { + value = strdup(jp->jp_value); + if (value == NULL) + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (value); + } + nval = jp->jp_elemlen ? jp->jp_valuelen / jp->jp_elemlen : 1; + if (nval == 0) { + value = strdup(""); + if (value == NULL) + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (value); + } + values = alloca(nval * sizeof(char *)); + valuelen = 0; + for (i = 0; i < nval; i++) { + switch (jp->jp_ctltype & CTLTYPE) { + case CTLTYPE_INT: + if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) { + strlcpy(valbuf, + ((int *)jp->jp_value)[i] ? "true" : "false", + sizeof(valbuf)); + break; + } + snprintf(valbuf, sizeof(valbuf), "%d", + ((int *)jp->jp_value)[i]); + break; + case CTLTYPE_UINT: + snprintf(valbuf, sizeof(valbuf), "%u", + ((unsigned *)jp->jp_value)[i]); + break; + case CTLTYPE_LONG: + snprintf(valbuf, sizeof(valbuf), "%ld", + ((long *)jp->jp_value)[i]); + break; + case CTLTYPE_ULONG: + snprintf(valbuf, sizeof(valbuf), "%lu", + ((unsigned long *)jp->jp_value)[i]); + break; + case CTLTYPE_QUAD: + snprintf(valbuf, sizeof(valbuf), "%jd", + (intmax_t)((int64_t *)jp->jp_value)[i]); + break; + case CTLTYPE_STRUCT: + switch (jp->jp_structtype) { + case JPS_IN_ADDR: + if (inet_ntop(AF_INET, + &((struct in_addr *)jp->jp_value)[i], + valbuf, sizeof(valbuf)) == NULL) { + strerror_r(errno, jail_errmsg, + JAIL_ERRMSGLEN); + + return (NULL); + } + break; + case JPS_IN6_ADDR: + if (inet_ntop(AF_INET6, + &((struct in6_addr *)jp->jp_value)[i], + valbuf, sizeof(valbuf)) == NULL) { + strerror_r(errno, jail_errmsg, + JAIL_ERRMSGLEN); + + return (NULL); + } + break; + default: + goto unknown_type; + } + break; + default: + unknown_type: + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "unknown type for %s", jp->jp_name); + errno = ENOENT; + return (NULL); + } + valuelen += strlen(valbuf) + 1; + values[i] = alloca(valuelen); + strcpy(values[i], valbuf); + } + value = malloc(valuelen + 1); + if (value == NULL) + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + else { + tvalue = value; + for (i = 0; i < nval; i++) { + strcpy(tvalue, values[i]); + if (i < nval - 1) { + tvalue += strlen(values[i]); + *tvalue++ = ','; + } + } + } + return (value); +} + +/* + * Free the contents of a jail parameter list (but not thst list itself). + */ +void +jailparam_free(struct jailparam *jp, unsigned njp) +{ + unsigned j; + + for (j = 0; j < njp; j++) { + free(jp[j].jp_name); + if (!(jp[j].jp_flags & JP_RAWVALUE)) + free(jp[j].jp_value); + } +} + +/* + * Create and import an array of jail parameters, given a list of name and + * value strings, terminated by a null name. + */ +static int +jailparam_vlist(struct jailparam **jpp, va_list ap) +{ + va_list tap; + struct jailparam *jp; + char *name, *value; + int njp; + + va_copy(tap, ap); + for (njp = 0; va_arg(tap, char *) != NULL; njp++) + (void)va_arg(tap, char *); + va_end(tap); + jp = calloc(njp, sizeof(struct jailparam)); + if (jp == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (-1); + } + + for (njp = 0; (name = va_arg(ap, char *)) != NULL; njp++) { + value = va_arg(ap, char *); + if (jailparam_init(jp + njp, name) < 0 || + jailparam_import(jp + njp, value) < 0) { + jailparam_free(jp, njp); + free(jp); + return (-1); + } + } + *jpp = jp; + return (njp); +} + +/* + * Find a parameter's type and size from its MIB. + */ +static int +jailparam_type(struct jailparam *jp) +{ + char *p, *nname; + size_t miblen, desclen; + int isarray; + struct { + int i; + char s[MAXPATHLEN]; + } desc; + int mib[CTL_MAXNAME]; + + /* The "lastjid" parameter isn't real. */ + if (!strcmp(jp->jp_name, "lastjid")) { + jp->jp_valuelen = sizeof(int); + jp->jp_ctltype = CTLTYPE_INT | CTLFLAG_WR; + return (0); + } + + /* Find the sysctl that describes the parameter. */ + mib[0] = 0; + mib[1] = 3; + snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", jp->jp_name); + miblen = sizeof(mib) - 2 * sizeof(int); + if (sysctl(mib, 2, mib + 2, &miblen, desc.s, strlen(desc.s)) < 0) { + if (errno != ENOENT) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(0.3.%s): %s", jp->jp_name, strerror(errno)); + return (-1); + } + /* + * The parameter probably doesn't exist. But it might be + * the "no" counterpart to a boolean. + */ + nname = nononame(jp->jp_name); + if (nname != NULL) { + snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname); + free(nname); + miblen = sizeof(mib) - 2 * sizeof(int); + if (sysctl(mib, 2, mib + 2, &miblen, desc.s, + strlen(desc.s)) >= 0) { + mib[1] = 4; + desclen = sizeof(desc); + if (sysctl(mib, (miblen / sizeof(int)) + 2, + &desc, &desclen, NULL, 0) < 0) { + snprintf(jail_errmsg, + JAIL_ERRMSGLEN, + "sysctl(0.4.%s): %s", desc.s, + strerror(errno)); + return (-1); + } + if ((desc.i & CTLTYPE) == CTLTYPE_INT && + desc.s[0] == 'B') { + jp->jp_ctltype = desc.i; + jp->jp_flags |= JP_NOBOOL; + jp->jp_valuelen = sizeof(int); + return (0); + } + } + } + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "unknown parameter: %s", jp->jp_name); + errno = ENOENT; + return (-1); + } + mib[1] = 4; + desclen = sizeof(desc); + if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen, + NULL, 0) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(0.4.%s): %s", jp->jp_name, strerror(errno)); + return (-1); + } + /* See if this is an array type. */ + p = strchr(desc.s, '\0'); + isarray = 0; + if (p - 2 < desc.s || strcmp(p - 2, ",a")) + isarray = 0; + else { + isarray = 1; + p[-2] = 0; + } + /* Look for types we understand */ + jp->jp_ctltype = desc.i; + switch (desc.i & CTLTYPE) { + case CTLTYPE_INT: + if (desc.s[0] == 'B') + jp->jp_flags |= + (desc.s[1] == 'N') ? JP_NOBOOL : JP_BOOL; + case CTLTYPE_UINT: + jp->jp_valuelen = sizeof(int); + break; + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + jp->jp_valuelen = sizeof(long); + break; + case CTLTYPE_QUAD: + jp->jp_valuelen = sizeof(int64_t); + break; + case CTLTYPE_STRING: + desc.s[0] = 0; + desclen = sizeof(desc.s); + if (sysctl(mib + 2, miblen / sizeof(int), desc.s, &desclen, + NULL, 0) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(" SJPARAM ".%s): %s", jp->jp_name, + strerror(errno)); + return (-1); + } + jp->jp_valuelen = strtoul(desc.s, NULL, 10); + break; + case CTLTYPE_STRUCT: + if (!strcmp(desc.s, "S,in_addr")) { + jp->jp_structtype = JPS_IN_ADDR; + jp->jp_valuelen = sizeof(struct in_addr); + } else if (!strcmp(desc.s, "S,in6_addr")) { + jp->jp_structtype = JPS_IN6_ADDR; + jp->jp_valuelen = sizeof(struct in6_addr); + } else { + desclen = 0; + if (sysctl(mib + 2, miblen / sizeof(int), + NULL, &jp->jp_valuelen, NULL, 0) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(" SJPARAM ".%s): %s", jp->jp_name, + strerror(errno)); + return (-1); + } + } + break; + case CTLTYPE_NODE: + /* + * A node isn't normally a parameter, but may be a boolean + * if its "no" counterpart exists. + */ + nname = noname(jp->jp_name); + if (nname == NULL) + return (-1); + mib[1] = 3; + snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", nname); + free(nname); + miblen = sizeof(mib) - 2 * sizeof(int); + if (sysctl(mib, 2, mib + 2, &miblen, desc.s, + strlen(desc.s)) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "unknown parameter: %s", jp->jp_name); + return (-1); + } + mib[1] = 4; + desclen = sizeof(desc); + if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen, + NULL, 0) < 0) { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "sysctl(0.4.%s): %s", desc.s, strerror(errno)); + return (-1); + } + if ((desc.i & CTLTYPE) != CTLTYPE_INT || desc.s[0] != 'B') { + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "unknown parameter: %s", jp->jp_name); + errno = ENOENT; + return (-1); + } + jp->jp_valuelen = sizeof(int); + jp->jp_ctltype = desc.i; + jp->jp_flags |= JP_BOOL; + break; + default: + snprintf(jail_errmsg, JAIL_ERRMSGLEN, + "unknown type for %s", jp->jp_name); + errno = ENOENT; + return (-1); + } + if (isarray) { + jp->jp_elemlen = jp->jp_valuelen; + jp->jp_valuelen = 0; + } + return (0); +} + +/* + * Change a boolean parameter name into its "no" counterpart or vice versa. + */ +static char * +noname(const char *name) +{ + char *nname, *p; + + nname = malloc(strlen(name) + 3); + if (nname == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (NULL); + } + p = strrchr(name, '.'); + if (p != NULL) + sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); + else + sprintf(nname, "no%s", name); + return (nname); +} + +static char * +nononame(const char *name) +{ + char *p, *nname; + + p = strrchr(name, '.'); + if (strncmp(p ? p + 1 : name, "no", 2)) { + snprintf(jail_errmsg, sizeof(jail_errmsg), + "mismatched boolean: %s", name); + errno = EINVAL; + return (NULL); + } + nname = malloc(strlen(name) - 1); + if (nname == NULL) { + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return (NULL); + } + if (p != NULL) + sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); + else + strcpy(nname, name + 2); + return (nname); +} diff --git a/lib/libjail/jail.h b/lib/libjail/jail.h new file mode 100644 index 000000000000..4b3800152e36 --- /dev/null +++ b/lib/libjail/jail.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2009 James Gritton. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _JAIL_H +#define _JAIL_H + +#define JP_RAWVALUE 0x01 +#define JP_BOOL 0x02 +#define JP_NOBOOL 0x04 + +#define JAIL_ERRMSGLEN 1024 + +extern char jail_errmsg[]; + +struct jailparam { + char *jp_name; + void *jp_value; + size_t jp_valuelen; + size_t jp_elemlen; + int jp_ctltype; + int jp_structtype; + unsigned jp_flags; +}; + +__BEGIN_DECLS +extern int jail_getid(const char *name); +extern char *jail_getname(int jid); +extern int jail_setv(int flags, ...); +extern int jail_getv(int flags, ...); +extern int jailparam_all(struct jailparam **jpp); +extern int jailparam_init(struct jailparam *jp, const char *name); +extern int jailparam_import(struct jailparam *jp, const char *value); +extern int jailparam_import_raw(struct jailparam *jp, void *value, + size_t valuelen); +extern int jailparam_set(struct jailparam *jp, unsigned njp, int flags); +extern int jailparam_get(struct jailparam *jp, unsigned njp, int flags); +extern char *jailparam_export(struct jailparam *jp); +extern void jailparam_free(struct jailparam *jp, unsigned njp); +__END_DECLS + +#endif /* _JAIL_H */ + + diff --git a/lib/libjail/jail_getid.c b/lib/libjail/jail_getid.c new file mode 100644 index 000000000000..e7601d51b8ba --- /dev/null +++ b/lib/libjail/jail_getid.c @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 2009 James Gritton. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jail.h" + + +/* + * Return the JID corresponding to a jail name. + */ +int +jail_getid(const char *name) +{ + char *ep; + int jid; + struct iovec jiov[4]; + + jid = strtoul(name, &ep, 10); + if (*name && !*ep) + return jid; + *(const void **)&jiov[0].iov_base = "name"; + jiov[0].iov_len = sizeof("name"); + jiov[1].iov_len = strlen(name) + 1; + jiov[1].iov_base = alloca(jiov[1].iov_len); + strcpy(jiov[1].iov_base, name); + *(const void **)&jiov[2].iov_base = "errmsg"; + jiov[2].iov_len = sizeof("errmsg"); + jiov[3].iov_base = jail_errmsg; + jiov[3].iov_len = JAIL_ERRMSGLEN; + jail_errmsg[0] = 0; + jid = jail_get(jiov, 4, 0); + if (jid < 0 && !jail_errmsg[0]) + snprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", + strerror(errno)); + return jid; +} + +/* + * Return the name corresponding to a JID. + */ +char * +jail_getname(int jid) +{ + struct iovec jiov[6]; + char *name; + char namebuf[MAXHOSTNAMELEN]; + + *(const void **)&jiov[0].iov_base = "jid"; + jiov[0].iov_len = sizeof("jid"); + jiov[1].iov_base = &jid; + jiov[1].iov_len = sizeof(jid); + *(const void **)&jiov[2].iov_base = "name"; + jiov[2].iov_len = sizeof("name"); + jiov[3].iov_base = namebuf; + jiov[3].iov_len = sizeof(namebuf); + *(const void **)&jiov[4].iov_base = "errmsg"; + jiov[4].iov_len = sizeof("errmsg"); + jiov[5].iov_base = jail_errmsg; + jiov[5].iov_len = JAIL_ERRMSGLEN; + jail_errmsg[0] = 0; + jid = jail_get(jiov, 6, 0); + if (jid < 0 && !jail_errmsg[0]) + snprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", + strerror(errno)); + name = strdup(namebuf); + if (name == NULL) + strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); + return name; +} diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index 0a364af50f39..3a3fd8646255 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -69,6 +69,7 @@ LIBISC?= ${DESTDIR}${LIBDIR}/libisc.a LIBISCCC?= ${DESTDIR}${LIBDIR}/libisccc.a LIBISCCFG?= ${DESTDIR}${LIBDIR}/libisccfg.a .endif +LIBJAIL?= ${DESTDIR}${LIBDIR}/libjail.a LIBKADM5CLNT?= ${DESTDIR}${LIBDIR}/libkadm5clnt.a LIBKADM5SRV?= ${DESTDIR}${LIBDIR}/libkadm5srv.a LIBKAFS5?= ${DESTDIR}${LIBDIR}/libkafs5.a diff --git a/usr.bin/killall/Makefile b/usr.bin/killall/Makefile index ff772a800aec..de58c78dcb6a 100644 --- a/usr.bin/killall/Makefile +++ b/usr.bin/killall/Makefile @@ -1,5 +1,7 @@ # $FreeBSD$ PROG= killall +DPADD= ${LIBJAIL} +LDADD= -ljail .include diff --git a/usr.bin/killall/killall.c b/usr.bin/killall/killall.c index ffb6ee485270..0a779fdcbc3d 100644 --- a/usr.bin/killall/killall.c +++ b/usr.bin/killall/killall.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -101,7 +102,6 @@ nosig(char *name) int main(int ac, char **av) { - struct iovec jparams[2]; struct kinfo_proc *procs = NULL, *newprocs; struct stat sb; struct passwd *pw; @@ -162,18 +162,9 @@ main(int ac, char **av) jflag++; if (*av == NULL) errx(1, "must specify jail"); - jid = strtoul(*av, &ep, 10); - if (!**av || *ep) { - *(const void **)&jparams[0].iov_base = - "name"; - jparams[0].iov_len = sizeof("name"); - jparams[1].iov_base = *av; - jparams[1].iov_len = strlen(*av) + 1; - jid = jail_get(jparams, 2, 0); - if (jid < 0) - errx(1, "unknown jail: %s", - *av); - } + jid = jail_getid(*av); + if (jid < 0) + errx(1, "%s", jail_errmsg); if (jail_attach(jid) == -1) err(1, "jail_attach(%d)", jid); break; diff --git a/usr.sbin/jail/Makefile b/usr.sbin/jail/Makefile index e92ced013a3a..74fd6c47b1e1 100644 --- a/usr.sbin/jail/Makefile +++ b/usr.sbin/jail/Makefile @@ -4,8 +4,8 @@ PROG= jail MAN= jail.8 -DPADD= ${LIBUTIL} -LDADD= -lutil +DPADD= ${LIBJAIL} ${LIBUTIL} +LDADD= -ljail -lutil WARNS?= 6 diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c index 9d9b97961814..666d034a965c 100644 --- a/usr.sbin/jail/jail.c +++ b/usr.sbin/jail/jail.c @@ -32,7 +32,6 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include @@ -41,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -50,15 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include -#define SJPARAM "security.jail.param" -#define ERRMSG_SIZE 256 - -struct param { - struct iovec name; - struct iovec value; -}; - -static struct param *params; +static struct jailparam *params; static char **param_values; static int nparams; @@ -113,7 +105,6 @@ int main(int argc, char **argv) { login_cap_t *lcap = NULL; - struct iovec rparams[2]; struct passwd *pwd = NULL; gid_t *groups; size_t sysvallen; @@ -121,8 +112,8 @@ main(int argc, char **argv) int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag; long ngroups_max; unsigned pi; - char *ep, *jailname, *securelevel, *username, *JidFile; - char errmsg[ERRMSG_SIZE], enforce_statfs[4]; + char *jailname, *securelevel, *username, *JidFile; + char enforce_statfs[4]; static char *cleanenv; const char *shell, *p = NULL; FILE *fp; @@ -176,16 +167,9 @@ main(int argc, char **argv) jail_set_flags |= JAIL_UPDATE; break; case 'r': - jid = strtoul(optarg, &ep, 10); - if (!*optarg || *ep) { - *(const void **)&rparams[0].iov_base = "name"; - rparams[0].iov_len = sizeof("name"); - rparams[1].iov_base = optarg; - rparams[1].iov_len = strlen(optarg) + 1; - jid = jail_get(rparams, 2, 0); - if (jid < 0) - errx(1, "unknown jail: %s", optarg); - } + jid = jail_getid(optarg); + if (jid < 0) + errx(1, "%s", jail_errmsg); rflag = 1; break; default: @@ -280,21 +264,16 @@ main(int argc, char **argv) if (ip6_addr != NULL) set_param("ip6.addr", ip6_addr); #endif - errmsg[0] = 0; - set_param("errmsg", errmsg); if (Jflag) { fp = fopen(JidFile, "w"); if (fp == NULL) errx(1, "Could not create JidFile: %s", JidFile); } - jid = jail_set(¶ms->name, 2 * nparams, + jid = jailparam_set(params, nparams, jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH); - if (jid < 0) { - if (errmsg[0] != '\0') - errx(1, "%s", errmsg); - err(1, "jail_set"); - } + if (jid < 0) + errx(1, "%s", jail_errmsg); if (iflag) { printf("%d\n", jid); fflush(stdout); @@ -303,10 +282,9 @@ main(int argc, char **argv) if (jail_set_flags) { fprintf(fp, "jid=%d", jid); for (i = 0; i < nparams; i++) - if (strcmp(params[i].name.iov_base, "jid") && - strcmp(params[i].name.iov_base, "errmsg")) { + if (strcmp(params[i].jp_name, "jid")) { fprintf(fp, " %s", - (char *)params[i].name.iov_base); + (char *)params[i].jp_name); if (param_values[i]) { putc('=', fp); quoted_print(fp, @@ -316,19 +294,19 @@ main(int argc, char **argv) fprintf(fp, "\n"); } else { for (i = 0; i < nparams; i++) - if (!strcmp(params[i].name.iov_base, "path")) + if (!strcmp(params[i].jp_name, "path")) break; #ifdef INET6 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n", jid, i < nparams - ? (char *)params[i].value.iov_base : argv[0], + ? (char *)params[i].jp_value : argv[0], argv[1], ip4_addr ? ip4_addr : "", ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0] ? "," : "", ip6_addr ? ip6_addr : "", argv[3]); #else fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", jid, i < nparams - ? (char *)params[i].value.iov_base : argv[0], + ? (char *)params[i].jp_value : argv[0], argv[1], ip4_addr ? ip4_addr : "", argv[3]); #endif } @@ -497,14 +475,8 @@ quoted_print(FILE *fp, char *str) static void set_param(const char *name, char *value) { - struct param *param; - char *ep, *p; - size_t buflen, mlen; - int i, nval, mib[CTL_MAXNAME]; - struct { - int i; - char s[MAXPATHLEN]; - } buf; + struct jailparam *param; + int i; static int paramlistsize; @@ -517,9 +489,10 @@ set_param(const char *name, char *value) /* Check for repeat parameters */ for (i = 0; i < nparams; i++) - if (!strcmp(name, params[i].name.iov_base)) { + if (!strcmp(name, params[i].jp_name)) { + jailparam_free(params + i, 1); memcpy(params + i, params + i + 1, - (--nparams - i) * sizeof(struct param)); + (--nparams - i) * sizeof(struct jailparam)); break; } @@ -542,141 +515,9 @@ set_param(const char *name, char *value) /* Look up the paramter. */ param_values[nparams] = value; param = params + nparams++; - *(const void **)¶m->name.iov_base = name; - param->name.iov_len = strlen(name) + 1; - /* Trivial values - no value or errmsg. */ - if (value == NULL) { - param->value.iov_base = NULL; - param->value.iov_len = 0; - return; - } - if (!strcmp(name, "errmsg")) { - param->value.iov_base = value; - param->value.iov_len = ERRMSG_SIZE; - return; - } - mib[0] = 0; - mib[1] = 3; - snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name); - mlen = sizeof(mib) - 2 * sizeof(int); - if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0) - errx(1, "unknown parameter: %s", name); - mib[1] = 4; - buflen = sizeof(buf); - if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0) - err(1, "sysctl(0.4.%s)", name); - /* - * See if this is an array type. - * Treat non-arrays as an array of one. - */ - p = strchr(buf.s, '\0'); - nval = 1; - if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) { - if (value[0] == '\0' || - (value[0] == '-' && value[1] == '\0')) { - param->value.iov_base = value; - param->value.iov_len = 0; - return; - } - p[-2] = 0; - for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) { - *p = '\0'; - nval++; - } - } - - /* Set the values according to the parameter type. */ - switch (buf.i & CTLTYPE) { - case CTLTYPE_INT: - case CTLTYPE_UINT: - param->value.iov_len = nval * sizeof(int); - break; - case CTLTYPE_LONG: - case CTLTYPE_ULONG: - param->value.iov_len = nval * sizeof(long); - break; - case CTLTYPE_STRUCT: - if (!strcmp(buf.s, "S,in_addr")) - param->value.iov_len = nval * sizeof(struct in_addr); -#ifdef INET6 - else if (!strcmp(buf.s, "S,in6_addr")) - param->value.iov_len = nval * sizeof(struct in6_addr); -#endif - else - errx(1, "%s: unknown parameter structure (%s)", - name, buf.s); - break; - case CTLTYPE_STRING: - if (!strcmp(name, "path")) { - param->value.iov_base = malloc(MAXPATHLEN); - if (param->value.iov_base == NULL) - err(1, "malloc"); - if (realpath(value, param->value.iov_base) == NULL) - err(1, "%s: realpath(%s)", name, value); - if (chdir(param->value.iov_base) != 0) - err(1, "chdir: %s", - (char *)param->value.iov_base); - } else - param->value.iov_base = value; - param->value.iov_len = strlen(param->value.iov_base) + 1; - return; - default: - errx(1, "%s: unknown parameter type %d (%s)", - name, buf.i, buf.s); - } - param->value.iov_base = malloc(param->value.iov_len); - for (i = 0; i < nval; i++) { - switch (buf.i & CTLTYPE) { - case CTLTYPE_INT: - ((int *)param->value.iov_base)[i] = - strtol(value, &ep, 10); - if (ep[0] != '\0') - errx(1, "%s: non-integer value \"%s\"", - name, value); - break; - case CTLTYPE_UINT: - ((unsigned *)param->value.iov_base)[i] = - strtoul(value, &ep, 10); - if (ep[0] != '\0') - errx(1, "%s: non-integer value \"%s\"", - name, value); - break; - case CTLTYPE_LONG: - ((long *)param->value.iov_base)[i] = - strtol(value, &ep, 10); - if (ep[0] != '\0') - errx(1, "%s: non-integer value \"%s\"", - name, value); - break; - case CTLTYPE_ULONG: - ((unsigned long *)param->value.iov_base)[i] = - strtoul(value, &ep, 10); - if (ep[0] != '\0') - errx(1, "%s: non-integer value \"%s\"", - name, value); - break; - case CTLTYPE_STRUCT: - if (!strcmp(buf.s, "S,in_addr")) { - if (inet_pton(AF_INET, value, - &((struct in_addr *) - param->value.iov_base)[i]) != 1) - errx(1, "%s: not an IPv4 address: %s", - name, value); - } -#ifdef INET6 - else if (!strcmp(buf.s, "S,in6_addr")) { - if (inet_pton(AF_INET6, value, - &((struct in6_addr *) - param->value.iov_base)[i]) != 1) - errx(1, "%s: not an IPv6 address: %s", - name, value); - } -#endif - } - if (i > 0) - value[-1] = ','; - value = strchr(value, '\0') + 1; - } + if (jailparam_init(param, name) < 0 || + jailparam_import(param, value) < 0) + errx(1, "%s", jail_errmsg); } static void diff --git a/usr.sbin/jexec/Makefile b/usr.sbin/jexec/Makefile index 2bf817cb84b6..d55ea0fc1170 100644 --- a/usr.sbin/jexec/Makefile +++ b/usr.sbin/jexec/Makefile @@ -2,8 +2,8 @@ PROG= jexec MAN= jexec.8 -DPADD= ${LIBUTIL} -LDADD= -lutil +DPADD= ${LIBJAIL} ${LIBUTIL} +LDADD= -ljail -lutil WARNS?= 6 .include diff --git a/usr.sbin/jexec/jexec.c b/usr.sbin/jexec/jexec.c index 0d82e28a1234..8c15ebe98591 100644 --- a/usr.sbin/jexec/jexec.c +++ b/usr.sbin/jexec/jexec.c @@ -31,13 +31,13 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -67,14 +67,13 @@ static void usage(void); int main(int argc, char *argv[]) { - struct iovec params[2]; int jid; login_cap_t *lcap = NULL; struct passwd *pwd = NULL; gid_t *groups = NULL; int ch, ngroups, uflag, Uflag; long ngroups_max; - char *ep, *username; + char *username; ch = uflag = Uflag = 0; username = NULL; @@ -107,18 +106,11 @@ main(int argc, char *argv[]) usage(); if (uflag) GET_USER_INFO; - jid = strtoul(argv[0], &ep, 10); - if (!*argv[0] || *ep) { - *(const void **)¶ms[0].iov_base = "name"; - params[0].iov_len = sizeof("name"); - params[1].iov_base = argv[0]; - params[1].iov_len = strlen(argv[0]) + 1; - jid = jail_get(params, 2, 0); - if (jid < 0) - errx(1, "Unknown jail: %s", argv[0]); - } + jid = jail_getid(argv[0]); + if (jid < 0) + errx(1, "%s", jail_errmsg); if (jail_attach(jid) == -1) - err(1, "jail_attach(): %d", jid); + err(1, "jail_attach(%d)", jid); if (chdir("/") == -1) err(1, "chdir(): /"); if (username != NULL) { diff --git a/usr.sbin/jls/Makefile b/usr.sbin/jls/Makefile index 3968946ce642..2d02c560ac4e 100644 --- a/usr.sbin/jls/Makefile +++ b/usr.sbin/jls/Makefile @@ -2,6 +2,9 @@ PROG= jls MAN= jls.8 +DPADD= ${LIBJAIL} +LDADD= -ljail + WARNS?= 6 .include diff --git a/usr.sbin/jls/jls.c b/usr.sbin/jls/jls.c index d5e70d3b15af..443eb8e8ea35 100644 --- a/usr.sbin/jls/jls.c +++ b/usr.sbin/jls/jls.c @@ -33,32 +33,21 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include #include #include +#include #include #include #include #include #include -#define SJPARAM "security.jail.param" -#define ARRAY_SLOP 5 - -#define CTLTYPE_BOOL (CTLTYPE + 1) -#define CTLTYPE_NOBOOL (CTLTYPE + 2) -#define CTLTYPE_IPADDR (CTLTYPE + 3) -#define CTLTYPE_IP6ADDR (CTLTYPE + 4) - -#define PARAM_KEY 0x01 -#define PARAM_USER 0x02 -#define PARAM_ARRAY 0x04 -#define PARAM_OPT 0x08 -#define PARAM_WR 0x10 +#define JP_USER 0x01000000 +#define JP_OPT 0x02000000 #define PRINT_DEFAULT 0x01 #define PRINT_HEADER 0x02 @@ -67,31 +56,17 @@ __FBSDID("$FreeBSD$"); #define PRINT_SKIP 0x10 #define PRINT_VERBOSE 0x20 -struct param { - char *name; - void *value; - size_t size; - int type; - unsigned flags; - int noparent; -}; - -struct iovec2 { - struct iovec name; - struct iovec value; -}; - -static struct param *params; +static struct jailparam *params; +static int *param_noparent; static int nparams; -static char errmsg[256]; -static int add_param(const char *name, void *value, unsigned flags); -static int get_param(const char *name, struct param *param); +static int add_param(const char *name, void *value, size_t valuelen, + struct jailparam *source, unsigned flags); static int sort_param(const void *a, const void *b); static char *noname(const char *name); static char *nononame(const char *name); static int print_jail(int pflags, int jflags); -static void quoted_print(char *str, int len); +static void quoted_print(char *str); int main(int argc, char **argv) @@ -136,50 +111,54 @@ main(int argc, char **argv) /* Add the parameters to print. */ if (optind == argc) { if (pflags & PRINT_VERBOSE) { - add_param("jid", NULL, PARAM_USER); - add_param("host.hostname", NULL, PARAM_USER); - add_param("path", NULL, PARAM_USER); - add_param("name", NULL, PARAM_USER); - add_param("dying", NULL, PARAM_USER); - add_param("cpuset.id", NULL, PARAM_USER); - add_param("ip4.addr", NULL, PARAM_USER); - add_param("ip6.addr", NULL, PARAM_USER | PARAM_OPT); + add_param("jid", NULL, (size_t)0, NULL, JP_USER); + add_param("host.hostname", NULL, (size_t)0, NULL, + JP_USER); + add_param("path", NULL, (size_t)0, NULL, JP_USER); + add_param("name", NULL, (size_t)0, NULL, JP_USER); + add_param("dying", NULL, (size_t)0, NULL, JP_USER); + add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); + add_param("ip4.addr", NULL, (size_t)0, NULL, JP_USER); + add_param("ip6.addr", NULL, (size_t)0, NULL, + JP_USER | JP_OPT); } else { pflags = (pflags & ~(PRINT_NAMEVAL | PRINT_SKIP | PRINT_VERBOSE)) | PRINT_DEFAULT; - add_param("jid", NULL, PARAM_USER); - add_param("ip4.addr", NULL, PARAM_USER); - add_param("host.hostname", NULL, PARAM_USER); - add_param("path", NULL, PARAM_USER); + add_param("jid", NULL, (size_t)0, NULL, JP_USER); + add_param("ip4.addr", NULL, (size_t)0, NULL, JP_USER); + add_param("host.hostname", NULL, (size_t)0, NULL, + JP_USER); + add_param("path", NULL, (size_t)0, NULL, JP_USER); } } else while (optind < argc) - add_param(argv[optind++], NULL, PARAM_USER); + add_param(argv[optind++], NULL, (size_t)0, NULL, + JP_USER); if (pflags & PRINT_SKIP) { /* Check for parameters with boolean parents. */ for (i = 0; i < nparams; i++) { - if ((params[i].flags & PARAM_USER) && - (dot = strchr(params[i].name, '.'))) { + if ((params[i].jp_flags & JP_USER) && + (dot = strchr(params[i].jp_name, '.'))) { *dot = 0; - nname = noname(params[i].name); + nname = noname(params[i].jp_name); *dot = '.'; - params[i].noparent = - add_param(nname, NULL, PARAM_OPT); + param_noparent[i] = + add_param(nname, NULL, (size_t)0, NULL, + JP_OPT); free(nname); } } } - /* Add the index key and errmsg parameters. */ + /* Add the index key parameters. */ if (jid != 0) - add_param("jid", &jid, PARAM_KEY); + add_param("jid", &jid, sizeof(jid), NULL, 0); else if (jname != NULL) - add_param("name", jname, PARAM_KEY); + add_param("name", jname, strlen(jname), NULL, 0); else - add_param("lastjid", &lastjid, PARAM_KEY); - add_param("errmsg", errmsg, PARAM_KEY); + add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); /* Print a header line if requested. */ if (pflags & PRINT_VERBOSE) @@ -192,105 +171,63 @@ main(int argc, char **argv) "Hostname Path\n"); else if (pflags & PRINT_HEADER) { for (i = spc = 0; i < nparams; i++) - if (params[i].flags & PARAM_USER) { + if (params[i].jp_flags & JP_USER) { if (spc) putchar(' '); else spc = 1; - fputs(params[i].name, stdout); + fputs(params[i].jp_name, stdout); } putchar('\n'); } /* Fetch the jail(s) and print the paramters. */ if (jid != 0 || jname != NULL) { - if (print_jail(pflags, jflags) < 0) { - if (errmsg[0]) - errx(1, "%s", errmsg); - err(1, "jail_get"); - } + if (print_jail(pflags, jflags) < 0) + errx(1, "%s", jail_errmsg); } else { for (lastjid = 0; (lastjid = print_jail(pflags, jflags)) >= 0; ) ; - if (errno != 0 && errno != ENOENT) { - if (errmsg[0]) - errx(1, "%s", errmsg); - err(1, "jail_get"); - } + if (errno != 0 && errno != ENOENT) + errx(1, "%s", jail_errmsg); } return (0); } static int -add_param(const char *name, void *value, unsigned flags) +add_param(const char *name, void *value, size_t valuelen, + struct jailparam *source, unsigned flags) { - struct param *param; - char *nname; - size_t mlen1, mlen2, buflen; - int mib1[CTL_MAXNAME], mib2[CTL_MAXNAME - 2]; + struct jailparam *param, *tparams; int i, tnparams; - char buf[MAXPATHLEN]; static int paramlistsize; /* The pseudo-parameter "all" scans the list of available parameters. */ if (!strcmp(name, "all")) { - tnparams = nparams; - mib1[0] = 0; - mib1[1] = 2; - mlen1 = CTL_MAXNAME - 2; - if (sysctlnametomib(SJPARAM, mib1 + 2, &mlen1) < 0) - err(1, "sysctlnametomib(" SJPARAM ")"); - for (;;) { - /* Get the next parameter. */ - mlen2 = sizeof(mib2); - if (sysctl(mib1, mlen1 + 2, mib2, &mlen2, NULL, 0) < 0) - err(1, "sysctl(0.2)"); - if (mib2[0] != mib1[2] || mib2[1] != mib1[3] || - mib2[2] != mib1[4]) - break; - /* Convert it to an ascii name. */ - memcpy(mib1 + 2, mib2, mlen2); - mlen1 = mlen2 / sizeof(int); - mib1[1] = 1; - buflen = sizeof(buf); - if (sysctl(mib1, mlen1 + 2, buf, &buflen, NULL, 0) < 0) - err(1, "sysctl(0.1)"); - add_param(buf + sizeof(SJPARAM), NULL, flags); - /* - * Convert nobool parameters to bool if their - * counterpart is a node, ortherwise discard them. - */ - param = ¶ms[nparams - 1]; - if (param->type == CTLTYPE_NOBOOL) { - nname = nononame(param->name); - if (get_param(nname, param) >= 0 && - param->type != CTLTYPE_NODE) { - free(nname); - nparams--; - } else { - free(param->name); - param->name = nname; - param->type = CTLTYPE_BOOL; - param->size = sizeof(int); - param->value = NULL; - } - } - mib1[1] = 2; - } - - qsort(params + tnparams, (size_t)(nparams - tnparams), - sizeof(struct param), sort_param); + tnparams = jailparam_all(&tparams); + if (tnparams < 0) + errx(1, "%s", jail_errmsg); + qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), + sort_param); + for (i = 0; i < tnparams; i++) + add_param(tparams[i].jp_name, NULL, (size_t)0, + tparams + i, flags); + free(tparams); return -1; } /* Check for repeat parameters. */ for (i = 0; i < nparams; i++) - if (!strcmp(name, params[i].name)) { - params[i].value = value; - params[i].flags |= flags; + if (!strcmp(name, params[i].jp_name)) { + if (value != NULL && jailparam_import_raw(params + i, + value, valuelen) < 0) + errx(1, "%s", jail_errmsg); + params[i].jp_flags |= flags; + if (source != NULL) + jailparam_free(source, 1); return i; } @@ -298,151 +235,57 @@ add_param(const char *name, void *value, unsigned flags) if (!nparams) { paramlistsize = 32; params = malloc(paramlistsize * sizeof(*params)); - if (params == NULL) + param_noparent = + malloc(paramlistsize * sizeof(*param_noparent)); + if (params == NULL || param_noparent == NULL) err(1, "malloc"); } else if (nparams >= paramlistsize) { paramlistsize *= 2; params = realloc(params, paramlistsize * sizeof(*params)); - if (params == NULL) + param_noparent = realloc(param_noparent, + paramlistsize * sizeof(*param_noparent)); + if (params == NULL || param_noparent == NULL) err(1, "realloc"); } /* Look up the parameter. */ + param_noparent[nparams] = -1; param = params + nparams++; - memset(param, 0, sizeof *param); - param->name = strdup(name); - if (param->name == NULL) - err(1, "strdup"); - param->flags = flags; - param->noparent = -1; - /* We have to know about pseudo-parameters without asking. */ - if (!strcmp(param->name, "lastjid")) { - param->type = CTLTYPE_INT; - param->size = sizeof(int); - goto got_type; + if (source != NULL) { + *param = *source; + param->jp_flags |= flags; + return param - params; } - if (!strcmp(param->name, "errmsg")) { - param->type = CTLTYPE_STRING; - param->size = sizeof(errmsg); - goto got_type; - } - if (get_param(name, param) < 0) { - if (errno != ENOENT) - err(1, "sysctl(0.3.%s)", name); - /* See if this the "no" part of an existing boolean. */ - if ((nname = nononame(name))) { - i = get_param(nname, param); - free(nname); - if (i >= 0 && param->type == CTLTYPE_BOOL) { - param->type = CTLTYPE_NOBOOL; - goto got_type; - } - } - if (flags & PARAM_OPT) { + if (jailparam_init(param, name) < 0) + errx(1, "%s", jail_errmsg); + param->jp_flags = flags; + if ((value != NULL ? jailparam_import_raw(param, value, valuelen) + : jailparam_import(param, value)) < 0) { + if (flags & JP_OPT) { nparams--; - return -1; + return (-1); } - errx(1, "unknown parameter: %s", name); + errx(1, "%s", jail_errmsg); } - if (param->type == CTLTYPE_NODE) { - /* - * A node isn't normally a parameter, but may be a boolean - * if its "no" counterpart exists. - */ - nname = noname(name); - i = get_param(nname, param); - free(nname); - if (i >= 0 && param->type == CTLTYPE_NOBOOL) { - param->type = CTLTYPE_BOOL; - goto got_type; - } - errx(1, "unknown parameter: %s", name); - } - - got_type: - param->value = value; return param - params; } -static int -get_param(const char *name, struct param *param) -{ - char *p; - size_t buflen, mlen; - int mib[CTL_MAXNAME]; - struct { - int i; - char s[MAXPATHLEN]; - } buf; - - /* Look up the MIB. */ - mib[0] = 0; - mib[1] = 3; - snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name); - mlen = sizeof(mib) - 2 * sizeof(int); - if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0) - return (-1); - /* Get the type and size. */ - mib[1] = 4; - buflen = sizeof(buf); - if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0) - err(1, "sysctl(0.4.%s)", name); - param->type = buf.i & CTLTYPE; - if (buf.i & (CTLFLAG_WR | CTLFLAG_TUN)) - param->flags |= PARAM_WR; - p = strchr(buf.s, '\0'); - if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) { - p[-2] = 0; - param->flags |= PARAM_ARRAY; - } - switch (param->type) { - case CTLTYPE_INT: - /* An integer parameter might be a boolean. */ - if (buf.s[0] == 'B') - param->type = buf.s[1] == 'N' - ? CTLTYPE_NOBOOL : CTLTYPE_BOOL; - case CTLTYPE_UINT: - param->size = sizeof(int); - break; - case CTLTYPE_LONG: - case CTLTYPE_ULONG: - param->size = sizeof(long); - break; - case CTLTYPE_STRUCT: - if (!strcmp(buf.s, "S,in_addr")) { - param->type = CTLTYPE_IPADDR; - param->size = sizeof(struct in_addr); - } else if (!strcmp(buf.s, "S,in6_addr")) { - param->type = CTLTYPE_IP6ADDR; - param->size = sizeof(struct in6_addr); - } - break; - case CTLTYPE_STRING: - buf.s[0] = 0; - sysctl(mib + 2, mlen / sizeof(int), buf.s, &buflen, NULL, 0); - param->size = strtoul(buf.s, NULL, 10); - if (param->size == 0) - param->size = BUFSIZ; - } - return (0); -} - static int sort_param(const void *a, const void *b) { - const struct param *parama, *paramb; + const struct jailparam *parama, *paramb; char *ap, *bp; /* Put top-level parameters first. */ parama = a; paramb = b; - ap = strchr(parama->name, '.'); - bp = strchr(paramb->name, '.'); + ap = strchr(parama->jp_name, '.'); + bp = strchr(paramb->jp_name, '.'); if (ap && !bp) return (1); if (bp && !ap) return (-1); - return (strcmp(parama->name, paramb->name)); + return (strcmp(parama->jp_name, paramb->jp_name)); } static char * @@ -483,126 +326,38 @@ static int print_jail(int pflags, int jflags) { char *nname; - int i, ai, jid, count, sanity, spc; + char **param_values; + int i, ai, jid, count, spc; char ipbuf[INET6_ADDRSTRLEN]; - static struct iovec2 *iov, *aiov; - static int narray, nkey; - - /* Set up the parameter list(s) the first time around. */ - if (iov == NULL) { - iov = malloc(nparams * sizeof(struct iovec2)); - if (iov == NULL) - err(1, "malloc"); - for (i = narray = 0; i < nparams; i++) { - iov[i].name.iov_base = params[i].name; - iov[i].name.iov_len = strlen(params[i].name) + 1; - iov[i].value.iov_base = params[i].value; - iov[i].value.iov_len = - params[i].type == CTLTYPE_STRING && - params[i].value != NULL && - ((char *)params[i].value)[0] != '\0' - ? strlen(params[i].value) + 1 : params[i].size; - if (params[i].flags & (PARAM_KEY | PARAM_ARRAY)) { - narray++; - if (params[i].flags & PARAM_KEY) - nkey++; - } - } - if (narray > nkey) { - aiov = malloc(narray * sizeof(struct iovec2)); - if (aiov == NULL) - err(1, "malloc"); - for (i = ai = 0; i < nparams; i++) - if (params[i].flags & - (PARAM_KEY | PARAM_ARRAY)) - aiov[ai++] = iov[i]; - } - } - /* If there are array parameters, find their sizes. */ - if (aiov != NULL) { - for (ai = 0; ai < narray; ai++) - if (aiov[ai].value.iov_base == NULL) - aiov[ai].value.iov_len = 0; - if (jail_get((struct iovec *)aiov, 2 * narray, jflags) < 0) - return (-1); - } - /* Allocate storage for all parameters. */ - for (i = ai = 0; i < nparams; i++) { - if (params[i].flags & (PARAM_KEY | PARAM_ARRAY)) { - if (params[i].flags & PARAM_ARRAY) { - iov[i].value.iov_len = aiov[ai].value.iov_len + - ARRAY_SLOP * params[i].size; - iov[i].value.iov_base = - malloc(iov[i].value.iov_len); - } - ai++; - } else - iov[i].value.iov_base = malloc(params[i].size); - if (iov[i].value.iov_base == NULL) - err(1, "malloc"); - if (params[i].value == NULL) - memset(iov[i].value.iov_base, 0, iov[i].value.iov_len); - } - /* - * Get the actual prison. If there are array elements, retry a few - * times in case the size changed from under us. - */ - if ((jid = jail_get((struct iovec *)iov, 2 * nparams, jflags)) < 0) { - if (errno != EINVAL || aiov == NULL || errmsg[0]) - return (-1); - for (sanity = 0;; sanity++) { - if (sanity == 10) - return (-1); - for (ai = 0; ai < narray; ai++) - if (params[i].flags & PARAM_ARRAY) - aiov[ai].value.iov_len = 0; - if (jail_get((struct iovec *)iov, 2 * narray, jflags) < - 0) - return (-1); - for (i = ai = 0; i < nparams; i++) { - if (!(params[i].flags & - (PARAM_KEY | PARAM_ARRAY))) - continue; - if (params[i].flags & PARAM_ARRAY) { - iov[i].value.iov_len = - aiov[ai].value.iov_len + - ARRAY_SLOP * params[i].size; - iov[i].value.iov_base = - realloc(iov[i].value.iov_base, - iov[i].value.iov_len); - if (iov[i].value.iov_base == NULL) - err(1, "malloc"); - } - ai++; - } - } - } + jid = jailparam_get(params, nparams, jflags); + if (jid < 0) + return jid; if (pflags & PRINT_VERBOSE) { printf("%6d %-29.29s %.74s\n" "%6s %-29.29s %.74s\n" "%6s %-6d\n", - *(int *)iov[0].value.iov_base, - (char *)iov[1].value.iov_base, - (char *)iov[2].value.iov_base, + *(int *)params[0].jp_value, + (char *)params[1].jp_value, + (char *)params[2].jp_value, "", - (char *)iov[3].value.iov_base, - *(int *)iov[4].value.iov_base ? "DYING" : "ACTIVE", + (char *)params[3].jp_value, + *(int *)params[4].jp_value ? "DYING" : "ACTIVE", "", - *(int *)iov[5].value.iov_base); - count = iov[6].value.iov_len / sizeof(struct in_addr); + *(int *)params[5].jp_value); + count = params[6].jp_valuelen / sizeof(struct in_addr); for (ai = 0; ai < count; ai++) if (inet_ntop(AF_INET, - &((struct in_addr *)iov[6].value.iov_base)[ai], + &((struct in_addr *)params[6].jp_value)[ai], ipbuf, sizeof(ipbuf)) == NULL) err(1, "inet_ntop"); else printf("%6s %-15.15s\n", "", ipbuf); - if (!strcmp(params[7].name, "ip6.addr")) { - count = iov[7].value.iov_len / sizeof(struct in6_addr); + if (!strcmp(params[7].jp_name, "ip6.addr")) { + count = params[7].jp_valuelen / sizeof(struct in6_addr); for (ai = 0; ai < count; ai++) - if (inet_ntop(AF_INET6, &((struct in_addr *) - iov[7].value.iov_base)[ai], + if (inet_ntop(AF_INET6, + &((struct in_addr *)params[7].jp_value)[ai], ipbuf, sizeof(ipbuf)) == NULL) err(1, "inet_ntop"); else @@ -610,19 +365,28 @@ print_jail(int pflags, int jflags) } } else if (pflags & PRINT_DEFAULT) printf("%6d %-15.15s %-29.29s %.74s\n", - *(int *)iov[0].value.iov_base, - iov[1].value.iov_len == 0 ? "-" - : inet_ntoa(*(struct in_addr *)iov[1].value.iov_base), - (char *)iov[2].value.iov_base, - (char *)iov[3].value.iov_base); + *(int *)params[0].jp_value, + params[1].jp_valuelen == 0 ? "-" + : inet_ntoa(*(struct in_addr *)params[1].jp_value), + (char *)params[2].jp_value, + (char *)params[3].jp_value); else { + param_values = alloca(nparams * sizeof(*param_values)); + for (i = 0; i < nparams; i++) { + if (!(params[i].jp_flags & JP_USER)) + continue; + param_values[i] = jailparam_export(params + i); + if (param_values[i] == NULL) + errx(1, "%s", jail_errmsg); + } for (i = spc = 0; i < nparams; i++) { - if (!(params[i].flags & PARAM_USER)) + if (!(params[i].jp_flags & JP_USER)) continue; if ((pflags & PRINT_SKIP) && - ((!(params[i].flags & PARAM_WR)) || - (params[i].noparent >= 0 && - *(int *)iov[params[i].noparent].value.iov_base))) + ((!(params[i].jp_ctltype & + (CTLFLAG_WR | CTLFLAG_TUN))) || + (param_noparent[i] >= 0 && + *(int *)params[param_noparent[i]].jp_value))) continue; if (spc) putchar(' '); @@ -633,109 +397,48 @@ print_jail(int pflags, int jflags) * Generally "name=value", but for booleans * either "name" or "noname". */ - switch (params[i].type) { - case CTLTYPE_BOOL: - if (*(int *)iov[i].value.iov_base) - printf("%s", params[i].name); + if (params[i].jp_flags & + (JP_BOOL | JP_NOBOOL)) { + if (*(int *)params[i].jp_value) + printf("%s", params[i].jp_name); else { - nname = noname(params[i].name); + nname = (params[i].jp_flags & + JP_NOBOOL) ? + nononame(params[i].jp_name) + : noname(params[i].jp_name); printf("%s", nname); free(nname); } - break; - case CTLTYPE_NOBOOL: - if (*(int *)iov[i].value.iov_base) - printf("%s", params[i].name); - else { - nname = - nononame(params[i].name); - printf("%s", nname); - free(nname); - } - break; - default: - printf("%s=", params[i].name); + continue; } + printf("%s=", params[i].jp_name); } - count = params[i].flags & PARAM_ARRAY - ? iov[i].value.iov_len / params[i].size : 1; - if (count == 0) { + if (params[i].jp_valuelen == 0) { if (pflags & PRINT_QUOTED) printf("\"\""); else if (!(pflags & PRINT_NAMEVAL)) putchar('-'); - } - for (ai = 0; ai < count; ai++) { - if (ai > 0) - putchar(','); - switch (params[i].type) { - case CTLTYPE_INT: - printf("%d", ((int *) - iov[i].value.iov_base)[ai]); - break; - case CTLTYPE_UINT: - printf("%u", ((int *) - iov[i].value.iov_base)[ai]); - break; - case CTLTYPE_IPADDR: - if (inet_ntop(AF_INET, - &((struct in_addr *) - iov[i].value.iov_base)[ai], - ipbuf, sizeof(ipbuf)) == NULL) - err(1, "inet_ntop"); - else - printf("%s", ipbuf); - break; - case CTLTYPE_IP6ADDR: - if (inet_ntop(AF_INET6, - &((struct in6_addr *) - iov[i].value.iov_base)[ai], - ipbuf, sizeof(ipbuf)) == NULL) - err(1, "inet_ntop"); - else - printf("%s", ipbuf); - break; - case CTLTYPE_LONG: - printf("%ld", ((long *) - iov[i].value.iov_base)[ai]); - case CTLTYPE_ULONG: - printf("%lu", ((long *) - iov[i].value.iov_base)[ai]); - break; - case CTLTYPE_STRING: - if (pflags & PRINT_QUOTED) - quoted_print((char *) - iov[i].value.iov_base, - params[i].size); - else - printf("%.*s", - (int)params[i].size, - (char *) - iov[i].value.iov_base); - break; - case CTLTYPE_BOOL: - case CTLTYPE_NOBOOL: - if (!(pflags & PRINT_NAMEVAL)) - printf(((int *) - iov[i].value.iov_base)[ai] - ? "true" : "false"); - } - } + } else + quoted_print(param_values[i]); } putchar('\n'); + for (i = 0; i < nparams; i++) + if (params[i].jp_flags & JP_USER) + free(param_values[i]); } for (i = 0; i < nparams; i++) - if (params[i].value == NULL) - free(iov[i].value.iov_base); + if (!(params[i].jp_flags & JP_RAWVALUE)) { + free(params[i].jp_value); + params[i].jp_value = NULL; + } return (jid); } static void -quoted_print(char *str, int len) +quoted_print(char *str) { int c, qc; char *p = str; - char *ep = str + len; /* An empty string needs quoting. */ if (!*p) { @@ -753,7 +456,7 @@ quoted_print(char *str, int len) : 0; if (qc) putchar(qc); - while (p < ep && (c = *p++)) { + while ((c = *p++)) { if (c == '\\' || c == qc) putchar('\\'); putchar(c);