summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2025-11-26 23:24:14 -0600
committerKyle Evans <kevans@FreeBSD.org>2026-01-15 18:23:40 -0600
commitdb3b39f063d9f05ee808b9c5a11146ed6810d857 (patch)
treea87d4193b8318641d29bbc22f455f2baf70383e4 /lib
parent1af8d5652a01f79d055436c2ee83219700e7b16c (diff)
libjail: extend struct handlers to included MAC labels
MAC label handling is a little special; to avoid being too disruptive, we allocate a `mac_t *` here for the value so that we can mac_prepare() or mac_from_text() into. As a result, we need: - A custom free() handler to avoid leaking the *jp_value - A custom jailparam_get() handler to mac_prepare() the mac_t and populate the iove properly, so that the kernel doesn't have to do something funky like copyin, dereference, copyin again. - A custom jailparam_set() handler to similarly populate the iovec properly. Reviewed by: jamie Differential Revision: https://reviews.freebsd.org/D53960
Diffstat (limited to 'lib')
-rw-r--r--lib/libjail/jail.c192
1 files changed, 189 insertions, 3 deletions
diff --git a/lib/libjail/jail.c b/lib/libjail/jail.c
index 8fbd486d2c96..75fd411c70c8 100644
--- a/lib/libjail/jail.c
+++ b/lib/libjail/jail.c
@@ -29,6 +29,7 @@
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/linker.h>
+#include <sys/mac.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
@@ -50,20 +51,38 @@
#define JPSDEF_OF(jp) \
((jp)->jp_structtype >= 0 ? &jp_structdefs[(jp)->jp_structtype] : NULL)
+static int jps_get(struct jailparam *, struct iovec *);
+static int jps_set(const struct jailparam *, struct iovec *);
+static void jps_free(struct jailparam *);
+
typedef int (jps_import_t)(const struct jailparam *, int, const char *);
typedef char *(jps_export_t)(const struct jailparam *, int);
+typedef int (jps_get_t)(struct jailparam *, struct iovec *);
+typedef int (jps_set_t)(const struct jailparam *, struct iovec *);
+typedef void (jps_free_t)(struct jailparam *);
static jps_import_t jps_import_in_addr;
static jps_import_t jps_import_in6_addr;
+static jps_import_t jps_import_mac_label;
static jps_export_t jps_export_in_addr;
static jps_export_t jps_export_in6_addr;
+static jps_export_t jps_export_mac_label;
+
+static jps_get_t jps_get_mac_label;
+
+static jps_set_t jps_set_mac_label;
+
+static jps_free_t jps_free_mac_label;
static const struct jp_structdef {
const char *jps_type; /* sysctl type */
size_t jps_valuelen; /* value size */
jps_import_t *jps_import; /* jailparam_import() */
jps_export_t *jps_export; /* jailparam_export() */
+ jps_get_t *jps_get; /* jailparam_get() */
+ jps_set_t *jps_set; /* jailparam_set() */
+ jps_free_t *jps_free; /* jailparam_free() */
} jp_structdefs[] = {
{
.jps_type = "S,in_addr",
@@ -77,6 +96,15 @@ static const struct jp_structdef {
.jps_import = jps_import_in6_addr,
.jps_export = jps_export_in6_addr,
},
+ {
+ .jps_type = "S,mac",
+ .jps_valuelen = sizeof(mac_t *),
+ .jps_import = jps_import_mac_label,
+ .jps_export = jps_export_mac_label,
+ .jps_get = jps_get_mac_label,
+ .jps_set = jps_set_mac_label,
+ .jps_free = jps_free_mac_label,
+ },
};
_Static_assert(nitems(jp_structdefs) <= INT_MAX,
@@ -579,7 +607,7 @@ jailparam_set(struct jailparam *jp, unsigned njp, int flags)
/* No value means key removal. */
jiov[i].iov_base = NULL;
jiov[i].iov_len = 0;
- } else {
+ } else if (jps_set(&jp[j], &jiov[i]) != 0) {
/*
* Try to fill in missing values with an empty string.
*/
@@ -624,7 +652,7 @@ jailparam_get(struct jailparam *jp, unsigned njp, int flags)
{
struct iovec *jiov;
struct jailparam *jp_desc, *jp_lastjid, *jp_jid, *jp_name, *jp_key;
- int i, ai, ki, jid, arrays, sanity;
+ int i, ai, ki, jid, arrays, processed, sanity;
unsigned j;
/*
@@ -726,6 +754,26 @@ jailparam_get(struct jailparam *jp, unsigned njp, int flags)
JAIL_ERRMSGLEN);
return (-1);
}
+
+ /*
+ * Returns -1 on error, or # index populated on
+ * success. 0 is perfectly valid for a type
+ * that may want to simply initialize the value
+ * as needed.
+ */
+ processed = jps_get(&jp[j], &jiov[i]);
+ if (processed == -1) {
+ return (-1);
+ } else if (processed > 0) {
+ /*
+ * The above math for jiov sizing does
+ * not really account for one param
+ * expanding to multiple entries.
+ */
+ assert(processed == 1);
+ i += processed;
+ continue;
+ }
}
jiov[i].iov_base = jp[j].jp_value;
jiov[i].iov_len = jp[j].jp_valuelen;
@@ -924,12 +972,15 @@ jailparam_export(struct jailparam *jp)
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))
+ if (!(jp[j].jp_flags & JP_RAWVALUE)) {
+ jps_free(jp);
free(jp[j].jp_value);
+ }
}
}
@@ -1249,6 +1300,43 @@ kvname(const char *name)
}
static int
+jps_get(struct jailparam *jp, struct iovec *jiov)
+{
+ const struct jp_structdef *jpsdef;
+
+ jpsdef = JPSDEF_OF(jp);
+ if (jpsdef == NULL || jpsdef->jps_get == NULL)
+ return (0); /* Nop, but not an error. */
+
+ return ((jpsdef->jps_get)(jp, jiov));
+}
+
+static int
+jps_set(const struct jailparam *jp, struct iovec *jiov)
+{
+ const struct jp_structdef *jpsdef;
+
+ jpsdef = JPSDEF_OF(jp);
+ if (jpsdef == NULL || jpsdef->jps_set == NULL)
+ return (EINVAL); /* Unhandled */
+
+ return ((jpsdef->jps_set)(jp, jiov));
+}
+
+static void
+jps_free(struct jailparam *jp)
+{
+ const struct jp_structdef *jpsdef;
+
+ jpsdef = JPSDEF_OF(jp);
+ if (jpsdef == NULL)
+ return;
+
+ if (jpsdef->jps_free != NULL)
+ jpsdef->jps_free(jp);
+}
+
+static int
jps_import_in_addr(const struct jailparam *jp, int i, const char *value)
{
struct in_addr *addr;
@@ -1280,6 +1368,24 @@ jps_import_in6_addr(const struct jailparam *jp, int i, const char *value)
return (0);
}
+static int
+jps_import_mac_label(const struct jailparam *jp, int i, const char *value)
+{
+ mac_t *pmac;
+
+ pmac = &((mac_t *)jp->jp_value)[i];
+ if (mac_from_text(pmac, value) != 0) {
+ int serrno = errno;
+
+ snprintf(jail_errmsg, JAIL_ERRMSGLEN, "%s: mac_from_text: %s",
+ jp->jp_name, strerror(errno));
+ errno = serrno;
+ return (-1);
+ }
+
+ return (0);
+}
+
static char *
jps_export_in_addr(const struct jailparam *jp, int i)
{
@@ -1307,3 +1413,83 @@ jps_export_in6_addr(const struct jailparam *jp, int i)
/* Error checked by caller. */
return (strdup(valbuf));
}
+
+static char *
+jps_export_mac_label(const struct jailparam *jp, int i)
+{
+ mac_t *macp;
+ char *labelbuf;
+ int error;
+
+ macp = &((mac_t *)jp->jp_value)[i];
+ error = mac_to_text(*macp, &labelbuf);
+ if (error != 0)
+ return (NULL);
+
+ return (labelbuf);
+}
+
+static int
+jps_get_mac_label(struct jailparam *jp, struct iovec *jiov)
+{
+ mac_t *pmac = jp->jp_value;
+ int error;
+
+ error = mac_prepare_type(pmac, "jail");
+ if (error != 0) {
+ int serrno = errno;
+
+ free(jp->jp_value);
+ jp->jp_value = NULL;
+ if (serrno == ENOENT) {
+ snprintf(jail_errmsg, sizeof(jail_errmsg),
+ "jail_get: no mac.conf(5) jail config");
+ } else {
+ strerror_r(serrno, jail_errmsg, JAIL_ERRMSGLEN);
+ }
+
+ errno = serrno;
+ return (-1);
+ }
+
+ /*
+ * MAC label gets special handling because libjail internally maintains
+ * it as a pointer to a mac_t, but we actually want to pass the mac_t
+ * itself. We don't want the jailparam_get() zeroing behavior, as it's
+ * initialized by us.
+ */
+ jiov->iov_base = *pmac;
+ jiov->iov_len = sizeof(**pmac);
+ return (1);
+}
+
+static int
+jps_set_mac_label(const struct jailparam *jp, struct iovec *jiov)
+{
+ mac_t *pmac;
+
+ /*
+ * MAC label gets special handling because libjail internally
+ * maintains it as a pointer to a mac_t, but we actually want to
+ * pass the mac_t itself.
+ */
+ pmac = jp->jp_value;
+ if (pmac != NULL) {
+ jiov->iov_base = *pmac;
+ jiov->iov_len = sizeof(**pmac);
+ } else {
+ jiov->iov_base = NULL;
+ jiov->iov_len = 0;
+ }
+
+ return (0);
+}
+
+static void
+jps_free_mac_label(struct jailparam *jp)
+{
+ mac_t *pmac = jp->jp_value;
+
+ if (pmac != NULL)
+ mac_free(*pmac);
+}