Page MenuHomeFreeBSD

D47668.id147930.diff
No OneTemporary

D47668.id147930.diff

diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3805,6 +3805,7 @@
kern/kern_idle.c standard
kern/kern_intr.c standard
kern/kern_jail.c standard
+kern/kern_jailmeta.c standard
kern/kern_kcov.c optional kcov \
compile-with "${NOSAN_C} ${MSAN_CFLAGS}"
kern/kern_khelp.c standard
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -4253,7 +4253,7 @@
/*
* Jail-related sysctls.
*/
-static SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Jails");
#if defined(INET) || defined(INET6)
diff --git a/sys/kern/kern_jailmeta.c b/sys/kern/kern_jailmeta.c
new file mode 100644
--- /dev/null
+++ b/sys/kern/kern_jailmeta.c
@@ -0,0 +1,445 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 SkunkWerks GmbH
+ *
+ * This software was developed by Igor Ostapenko <igoro@FreeBSD.org>
+ * under sponsorship from SkunkWerks GmbH.
+ */
+
+#include <sys/param.h>
+#include <sys/_bitset.h>
+#include <sys/bitset.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/jail.h>
+#include <sys/osd.h>
+#include <sys/proc.h>
+
+/*
+ * Buffer limit
+ *
+ * The hard limit is the actual value used during setting or modification. The
+ * soft limit is used solely by the security.jail.param.meta and .env sysctl. If
+ * the hard limit is decreased, the soft limit may remain higher to ensure that
+ * previously set meta strings can still be correctly interpreted by end-user
+ * interfaces, such as jls(8).
+ */
+
+static uint32_t jm_maxbufsize_hard = 4096;
+static uint32_t jm_maxbufsize_soft = 4096;
+
+static int
+jm_sysctl_meta_maxbufsize(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ uint32_t newmax = 0;
+
+ /* only reading */
+
+ if (req->newptr == NULL) {
+ sx_slock(&allprison_lock);
+ error = SYSCTL_OUT(req, &jm_maxbufsize_hard,
+ sizeof(jm_maxbufsize_hard));
+ sx_sunlock(&allprison_lock);
+
+ return (error);
+ }
+
+ /* reading and writing */
+
+ sx_xlock(&allprison_lock);
+
+ error = SYSCTL_OUT(req, &jm_maxbufsize_hard,
+ sizeof(jm_maxbufsize_hard));
+ if (error != 0)
+ goto end;
+
+ error = SYSCTL_IN(req, &newmax, sizeof(newmax));
+ if (error != 0)
+ goto end;
+
+ jm_maxbufsize_hard = newmax;
+ if (jm_maxbufsize_hard >= jm_maxbufsize_soft)
+ jm_maxbufsize_soft = jm_maxbufsize_hard;
+ else if (TAILQ_EMPTY(&allprison))
+ /*
+ * For now, this is the simplest way to
+ * avoid O(n) iteration over all prisons in
+ * case of a large n.
+ */
+ jm_maxbufsize_soft = jm_maxbufsize_hard;
+
+end:
+ sx_xunlock(&allprison_lock);
+ return (error);
+}
+SYSCTL_PROC(_security_jail, OID_AUTO, meta_maxbufsize,
+ CTLTYPE_U32 | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
+ jm_sysctl_meta_maxbufsize, "IU",
+ "Maximum buffer size of each meta and env");
+
+
+/* Allowed chars */
+
+#define NCHARS 256
+BITSET_DEFINE(charbitset, NCHARS);
+static struct charbitset allowedchars;
+
+static int
+jm_sysctl_meta_allowedchars(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ unsigned char chars[NCHARS];
+ int len = 0;
+ const bool readonly = req->newptr == NULL;
+
+ readonly ? sx_slock(&allprison_lock) : sx_xlock(&allprison_lock);
+
+ if (!BIT_ISFULLSET(NCHARS, &allowedchars))
+ for (size_t i = 1; i < NCHARS; i++) {
+ if (!BIT_ISSET(NCHARS, i, &allowedchars))
+ continue;
+ chars[len] = i;
+ len++;
+ }
+ chars[len] = 0;
+
+ error = sysctl_handle_string(oidp, chars, arg2, req);
+
+ if (!readonly) {
+ if (chars[0] == 0) {
+ BIT_FILL(NCHARS, &allowedchars);
+ } else {
+ BIT_ZERO(NCHARS, &allowedchars);
+ for (size_t i = 0; i < NCHARS; i++) {
+ if (chars[i] == 0)
+ break;
+ BIT_SET(NCHARS, chars[i], &allowedchars);
+ }
+ }
+ }
+
+ readonly ? sx_sunlock(&allprison_lock) : sx_xunlock(&allprison_lock);
+
+ return (error);
+}
+SYSCTL_PROC(_security_jail, OID_AUTO, meta_allowedchars,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, NCHARS,
+ jm_sysctl_meta_allowedchars, "A",
+ "The single-byte chars allowed to be used for meta and env"
+ " (empty string means all chars are allowed)");
+
+
+/* Jail parameter announcement */
+
+static int
+jm_sysctl_param_meta(SYSCTL_HANDLER_ARGS)
+{
+ uint32_t soft;
+
+ sx_slock(&allprison_lock);
+ soft = jm_maxbufsize_soft;
+ sx_sunlock(&allprison_lock);
+
+ return (sysctl_jail_param(oidp, arg1, soft, req));
+}
+SYSCTL_PROC(_security_jail_param, OID_AUTO, meta,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
+ jm_sysctl_param_meta, "A", "Jail meta information hidden from the jail");
+SYSCTL_PROC(_security_jail_param, OID_AUTO, env,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
+ jm_sysctl_param_meta, "A", "Jail meta information readable by the jail");
+
+
+/* OSD -- generic */
+
+struct meta {
+ char *name;
+ u_int osd_slot;
+ osd_method_t methods[PR_MAXMETHOD];
+};
+
+static int
+jm_osd_method_set(void *obj, void *data, struct meta *meta)
+{
+ struct prison *pr = obj;
+ struct vfsoptlist *opts = data;
+ int len = 0;
+ char *osd_addr;
+ char *osd_addr_old;
+ int error;
+
+ /* Check the option presence and its len before buf allocation */
+ error = vfs_getopt(opts, meta->name, (void **)&osd_addr, &len);
+ if (error == ENOENT)
+ return (0);
+ if (error != 0)
+ return (error);
+ if (len < 1)
+ return (EINVAL);
+
+ sx_assert(&allprison_lock, SA_LOCKED);
+
+ /* Check buffer size limit */
+ if (len > 1 && len > jm_maxbufsize_hard) /* len includes '\0' char */
+ return (EFBIG);
+
+ /* Check allowed chars */
+ for (size_t i = 0; i < len; i++) {
+ if (osd_addr[i] == 0)
+ continue;
+ if (!BIT_ISSET(NCHARS, osd_addr[i], &allowedchars))
+ return (EINVAL);
+ }
+
+ /* Prepare a new buf */
+ osd_addr = NULL;
+ if (len > 1) {
+ osd_addr = malloc(len, M_PRISON, M_WAITOK);
+ error = vfs_copyopt(opts, meta->name, osd_addr, len);
+ if (error != 0) {
+ free(osd_addr, M_PRISON);
+ return (error);
+ }
+ }
+
+ /* Swap bufs */
+ mtx_lock(&pr->pr_mtx);
+ osd_addr_old = osd_jail_get(pr, meta->osd_slot);
+ error = osd_jail_set(pr, meta->osd_slot, osd_addr);
+ mtx_unlock(&pr->pr_mtx);
+
+ if (error != 0)
+ osd_addr_old = osd_addr;
+
+ free(osd_addr_old, M_PRISON);
+
+ return (error);
+}
+
+static int
+jm_osd_method_get(void *obj, void *data, struct meta *meta)
+{
+ struct prison *pr = obj;
+ struct vfsoptlist *opts = data;
+ char *osd_addr = NULL;
+ char empty = '\0';
+ int error;
+
+ /* Check the option presence to avoid unnecessary locking */
+ error = vfs_getopt(opts, meta->name, NULL, NULL);
+ if (error == ENOENT)
+ return (0);
+ if (error != 0)
+ return (error);
+
+ mtx_lock(&pr->pr_mtx);
+ osd_addr = osd_jail_get(pr, meta->osd_slot);
+ if (osd_addr == NULL)
+ error = vfs_setopts(opts, meta->name, &empty);
+ else
+ error = vfs_setopts(opts, meta->name, osd_addr);
+ mtx_unlock(&pr->pr_mtx);
+
+ return (error);
+}
+
+static int
+jm_osd_method_check(void *obj __unused, void *data, struct meta *meta)
+{
+ struct vfsoptlist *opts = data;
+ char *value = NULL;
+ int error;
+ int len = 0;
+
+ /* Check the option presence */
+ error = vfs_getopt(opts, meta->name, (void **)&value, &len);
+ if (error == ENOENT)
+ return (0);
+ if (error != 0)
+ return (error);
+
+ if (len < 1)
+ return (EINVAL);
+ if (value == NULL)
+ return (EINVAL);
+
+ return (0);
+}
+
+static void
+jm_osd_destructor(void *osd_addr)
+{
+ free(osd_addr, M_PRISON);
+}
+
+
+/* OSD -- meta */
+
+static struct meta meta;
+
+static inline int
+jm_osd_method_set_meta(void *obj, void *data)
+{
+ return (jm_osd_method_set(obj, data, &meta));
+}
+
+static inline int
+jm_osd_method_get_meta(void *obj, void *data)
+{
+ return (jm_osd_method_get(obj, data, &meta));
+}
+
+static inline int
+jm_osd_method_check_meta(void *obj, void *data)
+{
+ return (jm_osd_method_check(obj, data, &meta));
+}
+
+static struct meta meta = {
+ .name = "meta",
+ .osd_slot = 0,
+ .methods = {
+ [PR_METHOD_SET] = jm_osd_method_set_meta,
+ [PR_METHOD_GET] = jm_osd_method_get_meta,
+ [PR_METHOD_CHECK] = jm_osd_method_check_meta,
+ }
+};
+
+
+/* OSD -- env */
+
+static struct meta env;
+
+static inline int
+jm_osd_method_set_env(void *obj, void *data)
+{
+ return (jm_osd_method_set(obj, data, &env));
+}
+
+static inline int
+jm_osd_method_get_env(void *obj, void *data)
+{
+ return (jm_osd_method_get(obj, data, &env));
+}
+
+static inline int
+jm_osd_method_check_env(void *obj, void *data)
+{
+ return (jm_osd_method_check(obj, data, &env));
+}
+
+static struct meta env = {
+ .name = "env",
+ .osd_slot = 0,
+ .methods = {
+ [PR_METHOD_SET] = jm_osd_method_set_env,
+ [PR_METHOD_GET] = jm_osd_method_get_env,
+ [PR_METHOD_CHECK] = jm_osd_method_check_env,
+ }
+};
+
+
+/* A jail can read its 'env' */
+
+static int
+jm_sysctl_env(SYSCTL_HANDLER_ARGS)
+{
+ struct prison *pr;
+ char empty = '\0';
+ char *tmpbuf;
+ size_t outlen;
+ int error = 0;
+
+ pr = req->td->td_ucred->cr_prison;
+
+ mtx_lock(&pr->pr_mtx);
+ arg1 = osd_jail_get(pr, env.osd_slot);
+ if (arg1 == NULL) {
+ tmpbuf = &empty;
+ outlen = 1;
+ } else {
+ outlen = strlen(arg1) + 1;
+ if (req->oldptr != NULL) {
+ tmpbuf = malloc(outlen, M_PRISON, M_NOWAIT);
+ error = (tmpbuf == NULL) ? ENOMEM : 0;
+ if (error == 0)
+ memcpy(tmpbuf, arg1, outlen);
+ }
+ }
+ mtx_unlock(&pr->pr_mtx);
+
+ if (error != 0)
+ return (error);
+
+ if (req->oldptr == NULL)
+ SYSCTL_OUT(req, NULL, outlen);
+ else {
+ SYSCTL_OUT(req, tmpbuf, outlen);
+ if (tmpbuf != &empty)
+ free(tmpbuf, M_PRISON);
+ }
+
+ return (error);
+}
+SYSCTL_PROC(_security_jail, OID_AUTO, env,
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ 0, 0, jm_sysctl_env, "A", "Meta information provided by parent jail");
+
+
+/* Setup and tear down */
+
+static int
+jm_sysinit(void *arg __unused)
+{
+ /* Default set of allowed chars */
+
+ BIT_ZERO(NCHARS, &allowedchars);
+
+ /* Base64 */
+ for (size_t i = 0x41; i <= 0x5A; i++) /* A-Z */
+ BIT_SET(NCHARS, i, &allowedchars);
+ for (size_t i = 0x61; i <= 0x7A; i++) /* a-z */
+ BIT_SET(NCHARS, i, &allowedchars);
+ for (size_t i = 0x30; i <= 0x39; i++) /* 0-9 */
+ BIT_SET(NCHARS, i, &allowedchars);
+ BIT_SET(NCHARS, 0x2B, &allowedchars); /* + */
+ BIT_SET(NCHARS, 0x2F, &allowedchars); /* / */
+ BIT_SET(NCHARS, 0x3D, &allowedchars); /* = */
+
+ /* key=value\n format */
+ BIT_SET(NCHARS, 0x0A, &allowedchars); /* LF */
+ BIT_SET(NCHARS, 0x0D, &allowedchars); /* CR */
+
+ /* Extra */
+ BIT_SET(NCHARS, 0x09, &allowedchars); /* HT */
+ BIT_SET(NCHARS, 0x20, &allowedchars); /* SP */
+ BIT_SET(NCHARS, 0x2C, &allowedchars); /* , */
+ BIT_SET(NCHARS, 0x2D, &allowedchars); /* - */
+ BIT_SET(NCHARS, 0x2E, &allowedchars); /* . */
+ BIT_SET(NCHARS, 0x3A, &allowedchars); /* : */
+ BIT_SET(NCHARS, 0x40, &allowedchars); /* @ */
+ BIT_SET(NCHARS, 0x5F, &allowedchars); /* _ */
+
+
+ meta.osd_slot = osd_jail_register(jm_osd_destructor, meta.methods);
+ env.osd_slot = osd_jail_register(jm_osd_destructor, env.methods);
+
+ return (0);
+}
+
+static int
+jm_sysuninit(void *arg __unused)
+{
+ osd_jail_deregister(meta.osd_slot);
+ osd_jail_deregister(env.osd_slot);
+
+ return (0);
+}
+
+SYSINIT(jailmeta, SI_SUB_DRIVERS, SI_ORDER_ANY, jm_sysinit, NULL);
+SYSUNINIT(jailmeta, SI_SUB_DRIVERS, SI_ORDER_ANY, jm_sysuninit, NULL);
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -376,6 +376,7 @@
/*
* Sysctls to describe jail parameters.
*/
+SYSCTL_DECL(_security_jail);
SYSCTL_DECL(_security_jail_param);
#define SYSCTL_JAIL_PARAM(module, param, type, fmt, descr) \
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -58,6 +58,8 @@
TEST_METADATA.sigsys+= is_exclusive="true"
ATF_TESTS_SH+= coredump_phnum_test
+ATF_TESTS_SH+= jailmeta
+TEST_METADATA.jailmeta+= is_exclusive="true"
ATF_TESTS_SH+= sonewconn_overflow
TEST_METADATA.sonewconn_overflow+= required_programs="python"
TEST_METADATA.sonewconn_overflow+= required_user="root"
diff --git a/tests/sys/kern/jailmeta.sh b/tests/sys/kern/jailmeta.sh
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/jailmeta.sh
@@ -0,0 +1,475 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 SkunkWerks GmbH
+#
+# This software was developed by Igor Ostapenko <igoro@FreeBSD.org>
+# under sponsorship from SkunkWerks GmbH.
+#
+
+setup()
+{
+ # Check if we have enough buffer space for testing
+ if [ $(sysctl -n security.jail.meta_maxbufsize) -lt 128 ]; then
+ atf_skip "sysctl security.jail.meta_maxbufsize must be 128+ for testing."
+ fi
+
+ # Check if chars required for testing are allowed
+ sysctl -b security.jail.meta_allowedchars \
+ | hexdump -e '1/1 "%02x\n"' > meta_allowedchars.hex
+ # ABCabctv =0-9\t\n
+ for b in 41 42 43 61 62 63 74 76 20 30 31 32 33 34 35 36 37 38 39 09 0a
+ do
+ if ! grep $b meta_allowedchars.hex; then
+ rm meta_allowedchars.hex
+ atf_skip "sysctl security.jail.meta_allowedchars is not wide enough for testing"
+ fi
+ done
+ rm meta_allowedchars.hex
+}
+
+atf_test_case "jail_create" "cleanup"
+jail_create_head()
+{
+ atf_set descr 'Test that metadata can be set upon jail creation with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_create_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist meta="a b c" env="C B A"
+
+ atf_check -s exit:0 -o inline:"a b c\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"C B A\n" \
+ jls -j jail1 env
+}
+jail_create_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "jail_modify" "cleanup"
+jail_modify_head()
+{
+ atf_set descr 'Test that metadata can be modified after jail creation with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_modify_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist meta="a b c" env="CAB"
+
+ atf_check -s exit:0 -o inline:"a b c\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"CAB\n" \
+ jls -j jail1 env
+
+ atf_check -s exit:0 \
+ jail -m name=jail1 meta="t1=A t2=B" env="CAB2"
+
+ atf_check -s exit:0 -o inline:"t1=A t2=B\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"CAB2\n" \
+ jls -j jail1 env
+}
+jail_modify_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "jail_add" "cleanup"
+jail_add_head()
+{
+ atf_set descr 'Test that metadata can be added to an existing jail with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_add_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist host.hostname=jail1
+
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -j jail1 env
+
+ atf_check -s exit:0 \
+ jail -m name=jail1 meta="$(jot 3 1 3)" env="$(jot 2 11 12)"
+
+ atf_check -s exit:0 -o inline:"1\n2\n3\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"11\n12\n" \
+ jls -j jail1 env
+}
+jail_add_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "jail_reset" "cleanup"
+jail_reset_head()
+{
+ atf_set descr 'Test that metadata can be reset to an empty string with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_reset_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist meta="123" env="456"
+
+ atf_check -s exit:0 -o inline:"123\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"456\n" \
+ jls -j jail1 env
+
+ atf_check -s exit:0 \
+ jail -m name=jail1 meta= env=
+
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -j jail1 env
+}
+jail_reset_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "jls_libxo" "cleanup"
+jls_libxo_head()
+{
+ atf_set descr 'Test that metadata can be read with jls(8) using libxo'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jls_libxo_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist meta="a b c" env="1 2 3"
+
+ atf_check -s exit:0 -o inline:'{"__version": "2", "jail-information": {"jail": [{"name":"jail1","meta":"a b c"}]}}\n' \
+ jls -j jail1 --libxo json name meta
+ atf_check -s exit:0 -o inline:'{"__version": "2", "jail-information": {"jail": [{"env":"1 2 3"}]}}\n' \
+ jls -j jail1 --libxo json env
+}
+jls_libxo_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "flua_create" "cleanup"
+flua_create_head()
+{
+ atf_set descr 'Test that metadata can be set upon jail creation with flua'
+ atf_set require.user root
+ atf_set execenv jail
+}
+flua_create_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("jail1", {["meta"]="t1 t2=v2", ["env"]="BAC", ["persist"]="true"}, jail.CREATE)'
+
+ atf_check -s exit:0 -o inline:"t1 t2=v2\n" \
+ /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("jail1", {"meta"}); print(res["meta"])'
+ atf_check -s exit:0 -o inline:"BAC\n" \
+ /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("jail1", {"env"}); print(res["env"])'
+}
+flua_create_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "flua_modify" "cleanup"
+flua_modify_head()
+{
+ atf_set descr 'Test that metadata can be changed with flua after jail creation'
+ atf_set require.user root
+ atf_set execenv jail
+}
+flua_modify_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist meta="ABC" env="123"
+
+ atf_check -s exit:0 -o inline:"ABC\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"123\n" \
+ jls -j jail1 env
+
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("jail1", {["meta"]="t1 t2=v", ["env"]="4"}, jail.UPDATE)'
+
+ atf_check -s exit:0 -o inline:"t1 t2=v\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"4\n" \
+ jls -j jail1 env
+}
+flua_modify_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "env_readable_by_jail" "cleanup"
+env_readable_by_jail_head()
+{
+ atf_set descr 'Test that a jail can read its own env parameter via sysctl(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+env_readable_by_jail_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j jail1
+
+ atf_check -s exit:0 \
+ jail -c name=jail1 persist meta="a b c" env="CBA"
+
+ atf_check -s exit:0 -o inline:"a b c\n" \
+ jls -j jail1 meta
+ atf_check -s exit:0 -o inline:"CBA\n" \
+ jls -j jail1 env
+
+ atf_check -s exit:0 -o inline:"CBA\n" \
+ jexec jail1 sysctl -n security.jail.env
+}
+env_readable_by_jail_cleanup()
+{
+ jail -r jail1
+ return 0
+}
+
+atf_test_case "not_inheritable" "cleanup"
+not_inheritable_head()
+{
+ atf_set descr 'Test that a jail does not inherit metadata from its parent jail'
+ atf_set require.user root
+ atf_set execenv jail
+}
+not_inheritable_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j parent
+
+ atf_check -s exit:0 \
+ jail -c name=parent children.max=1 persist meta="abc" env="cba"
+
+ jexec parent jail -c name=child persist
+
+ atf_check -s exit:0 -o inline:"abc\n" \
+ jls -j parent meta
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -j parent.child meta
+
+ atf_check -s exit:0 -o inline:"cba\n" \
+ jexec parent sysctl -n security.jail.env
+ atf_check -s exit:0 -o inline:"\n" \
+ jexec parent.child sysctl -n security.jail.env
+}
+not_inheritable_cleanup()
+{
+ jail -r parent.child
+ jail -r parent
+ return 0
+}
+
+atf_test_case "maxbufsize" "cleanup"
+maxbufsize_head()
+{
+ atf_set descr 'Test that metadata buffer maximum size can be changed'
+ atf_set require.user root
+}
+maxbufsize_body()
+{
+ setup
+
+ jn=jailmeta_maxbufsize
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j $jn
+
+ # the size counts string length and the trailing \0 char
+ origmax=$(sysctl -n security.jail.meta_maxbufsize)
+
+ # must be fine with current max
+ atf_check -s exit:0 \
+ jail -c name=$jn persist meta="$(printf %$((origmax-1))s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn meta | wc -c
+ #
+ atf_check -s exit:0 \
+ jail -m name=$jn env="$(printf %$((origmax-1))s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn env | wc -c
+
+ # should not allow exceeding current max
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn meta="$(printf %${origmax}s)"
+ #
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn env="$(printf %${origmax}s)"
+
+ # should allow the same size with increased max
+ newmax=$((origmax + 1))
+ sysctl security.jail.meta_maxbufsize=$newmax
+ atf_check -s exit:0 \
+ jail -m name=$jn meta="$(printf %${origmax}s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn meta | wc -c
+ #
+ atf_check -s exit:0 \
+ jail -m name=$jn env="$(printf %${origmax}s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn env | wc -c
+
+ # decrease back to the original max
+ sysctl security.jail.meta_maxbufsize=$origmax
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn meta="$(printf %${origmax}s)"
+ #
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn env="$(printf %${origmax}s)"
+
+ # the previously set long meta is still readable as is
+ # due to the soft limit remains higher than the hard limit
+ atf_check_equal '${newmax}' '$(sysctl -n security.jail.param.meta)'
+ atf_check_equal '${newmax}' '$(sysctl -n security.jail.param.env)'
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn meta | wc -c
+ #
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn env | wc -c
+}
+maxbufsize_cleanup()
+{
+ jail -r jailmeta_maxbufsize
+ return 0
+}
+
+atf_test_case "allowedchars" "cleanup"
+allowedchars_head()
+{
+ atf_set descr 'Test that the set of allowed chars can be changed'
+ atf_set require.user root
+}
+allowedchars_body()
+{
+ setup
+
+ jn=jailmeta_allowedchars
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j $jn
+ atf_check -s exit:0 \
+ jail -c name=$jn persist
+
+ # Save the original value
+ sysctl -b security.jail.meta_allowedchars > meta_allowedchars.bin
+
+ # All chars
+ sysctl security.jail.meta_allowedchars=
+ printf $(jot -w '\%o' -s '' -n 127 1 127) > 7bit.bin
+ atf_check -s exit:0 \
+ jail -m name=$jn meta="$(cat 7bit.bin)" env="$(cat 7bit.bin)"
+ jls -j $jn meta > meta.bin
+ jls -j $jn env > env.bin
+ printf '\n' >> 7bit.bin # jls adds a newline
+ atf_check -s exit:0 diff 7bit.bin meta.bin
+ atf_check -s exit:0 diff 7bit.bin env.bin
+
+ # Limited set
+ sysctl security.jail.meta_allowedchars="$(printf 'AB\1\2_\3\11C')"
+ # should be okay if within the limits
+ atf_check -s exit:0 \
+ jail -m name=$jn meta="$(printf 'C\11A\3')" env="$(printf '\1A\2B\3')"
+ # should error and not change env
+ atf_check -s not-exit:0 -o ignore -e ignore \
+ jail -m name=$jn meta="$(printf 'XC\11A\3')" env="$(printf '_\1A\2B\3')"
+ # should error and not change meta
+ atf_check -s not-exit:0 -o ignore -e ignore \
+ jail -m name=$jn meta="$(printf '_C\11A\3')" env="$(printf '\1A\2B\3x')"
+ # should stay intact after errors
+ atf_check -s exit:0 -o inline:"43094103" \
+ jls -j $jn meta | hexdump -e '1/1 "%02x"'
+ atf_check -s exit:0 -o inline:"0141024303" \
+ jls -j $jn env | hexdump -e '1/1 "%02x"'
+
+}
+allowedchars_cleanup()
+{
+ # Restore the original value
+ test -f meta_allowedchars.bin \
+ && sysctl security.jail.meta_allowedchars="'$(cat meta_allowedchars.bin)'"
+ rm *.bin
+
+ jail -r jailmeta_allowedchars
+ return 0
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "jail_create"
+ atf_add_test_case "jail_modify"
+ atf_add_test_case "jail_add"
+ atf_add_test_case "jail_reset"
+
+ atf_add_test_case "jls_libxo"
+
+ atf_add_test_case "flua_create"
+ atf_add_test_case "flua_modify"
+
+ atf_add_test_case "env_readable_by_jail"
+ atf_add_test_case "not_inheritable"
+
+ atf_add_test_case "maxbufsize"
+ atf_add_test_case "allowedchars"
+}
diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8
--- a/usr.sbin/jail/jail.8
+++ b/usr.sbin/jail/jail.8
@@ -513,6 +513,23 @@
The number for the jail's
.Va kern.osreldate
and uname -K.
+.It Va meta , Va env
+An arbitrary string associated with the jail.
+Its maximum buffer size is controlled by the global
+.Va security.jail.meta_maxbufsize
+sysctl, which can only be adjusted by the non-jailed root user.
+While the
+.Va meta
+is hidden from the jail, the
+.Va env
+is readable through the
+.Va security.jail.env
+sysctl.
+The set of allowed single-byte characters for both buffers is limited by the
+global
+.Va security.jail.meta_allowedchars
+sysctl, which is also tunable by the non-jailed root user.
+All characters are allowed if it is set to an empty string.
.It Va allow.*
Some restrictions of the jail environment may be set on a per-jail
basis.

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 5, 6:48 PM (9 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29295459
Default Alt Text
D47668.id147930.diff (24 KB)

Event Timeline