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
This commit is contained in:
Kyle Evans
2025-11-26 23:24:14 -06:00
parent 1af8d5652a
commit db3b39f063
+189 -3
View File
@@ -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);
}
}
}
@@ -1248,6 +1299,43 @@ kvname(const char *name)
return (kvname);
}
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)
{
@@ -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);
}