Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146693307
D47668.id147930.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D47668.id147930.diff
View Options
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 = ∅
+ 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
Details
Attached
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)
Attached To
Mode
D47668: jail: Add meta and env parameters
Attached
Detach File
Event Timeline
Log In to Comment