MAC/do: Executable paths feature (GSoC 2025's final state)
By design, mac_do(4) only authorizes credentials change requests if they
are issued by a process spawned from '/usr/bin/mdo'. The executable
paths feature introduces some flexibility by allowing to change that
path, thus allowing another executable to make requests, and to use
multiple such paths (up to 8 in the current implementation). Its
purpose is to enable thin jails scenarios where mdo(1) may not be at its
canonical path ('/usr/bin/mdo') and to allow experimenting with other
userland programs leveraging setcred(2).
Configuration of executable paths is per-jail and intentionally works
completely similarly with rules. It is accessible from within a jail
through the 'security.mac.do.exec_paths' sysctl knob and from outside
a jail through the 'mac.do.exec_paths' jail parameter.
This commit groups the verbatim changes of the following commits that
Kushagra Srivastava, our GSoC 2025 student, created in his GitHub
repository (https://github.com/thesynthax/freebsd-src), branch
'task/exec-paths-refactor':
mac_do(4): Complete refactor of allowed executable paths feature
mac_do(4): Fixed changing security.mac.do.* knobs in inheritance mode
mac_do(4): Debugging rules and exec_paths leak on destroy
mac_do(4): Deep copy rules
mac_do(4): Fixed leak
mac_do(4): fixed various bugs, structs inlined, leaks remain
mac_do(4): MAC/do working in jail, leaks decreased
mac_do(4): MAC/do fixed, works in host and jails, leaks removed
mac_do(4): style
Frozen log for these commits:
https://github.com/OlCe2/freebsd-src/compare/main...14fdc49fb29265fac5d0daf95a13d0dce325c951.
The corresponding pull request is at:
https://github.com/OlCe2/freebsd-src/pull/2.
The GSoC's final state of this code still has a number of problems that
are fixed in subsequent commits. It is however committed separately to
clearly delineate Kushagra's work.
Reviewed by: olce (amendments to come, see above)
MFC after: 1 month
Relnotes: yes
Sponsored by: Google LLC (GSoC 2025)
Sponsored by: The FreeBSD Foundation (review, commit)
Pull Request: https://ron-dev.freebsd.org/FreeBSD/src/pulls/38
This commit is contained in:
committed by
Olivier Certner
parent
3118f1b99f
commit
9818224174
+414
-151
@@ -46,6 +46,9 @@ SYSCTL_INT(_security_mac_do, OID_AUTO, print_parse_error, CTLFLAG_RWTUN,
|
|||||||
|
|
||||||
static MALLOC_DEFINE(M_MAC_DO, "mac_do", "mac_do(4) security module");
|
static MALLOC_DEFINE(M_MAC_DO, "mac_do", "mac_do(4) security module");
|
||||||
|
|
||||||
|
#define EXEC_PATHS_MAXLEN 2048
|
||||||
|
#define MAX_EXEC_PATHS 8
|
||||||
|
|
||||||
#define MAC_RULE_STRING_LEN 1024
|
#define MAC_RULE_STRING_LEN 1024
|
||||||
|
|
||||||
static unsigned osd_jail_slot;
|
static unsigned osd_jail_slot;
|
||||||
@@ -167,6 +170,17 @@ STAILQ_HEAD(rulehead, rule);
|
|||||||
struct rules {
|
struct rules {
|
||||||
char string[MAC_RULE_STRING_LEN];
|
char string[MAC_RULE_STRING_LEN];
|
||||||
struct rulehead head;
|
struct rulehead head;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct exec_paths {
|
||||||
|
char exec_paths_str[EXEC_PATHS_MAXLEN];
|
||||||
|
char exec_paths[MAX_EXEC_PATHS][PATH_MAX];
|
||||||
|
int exec_path_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct conf {
|
||||||
|
struct rules rules;
|
||||||
|
struct exec_paths exec_paths;
|
||||||
volatile u_int use_count __aligned(CACHE_LINE_SIZE);
|
volatile u_int use_count __aligned(CACHE_LINE_SIZE);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,19 +337,34 @@ toast_rules(struct rules *const rules)
|
|||||||
free(rule->gids, M_MAC_DO);
|
free(rule->gids, M_MAC_DO);
|
||||||
free(rule, M_MAC_DO);
|
free(rule, M_MAC_DO);
|
||||||
}
|
}
|
||||||
free(rules, M_MAC_DO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rules *
|
/* Assuming storage is zeroed already */
|
||||||
alloc_rules(void)
|
static void
|
||||||
|
init_rules(struct rules *const rules)
|
||||||
{
|
{
|
||||||
struct rules *const rules = malloc(sizeof(*rules), M_MAC_DO, M_WAITOK);
|
|
||||||
|
|
||||||
_Static_assert(MAC_RULE_STRING_LEN > 0, "MAC_RULE_STRING_LEN <= 0!");
|
_Static_assert(MAC_RULE_STRING_LEN > 0, "MAC_RULE_STRING_LEN <= 0!");
|
||||||
rules->string[0] = 0;
|
|
||||||
STAILQ_INIT(&rules->head);
|
STAILQ_INIT(&rules->head);
|
||||||
rules->use_count = 0;
|
}
|
||||||
return (rules);
|
|
||||||
|
static void
|
||||||
|
init_exec_paths(struct exec_paths *const exec_paths)
|
||||||
|
{
|
||||||
|
_Static_assert(EXEC_PATHS_MAXLEN > 0, "EXEC_PATHS_MAXLEN <= 0!");
|
||||||
|
bzero(exec_paths, sizeof(*exec_paths));
|
||||||
|
exec_paths->exec_paths_str[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct conf *
|
||||||
|
alloc_conf(void)
|
||||||
|
{
|
||||||
|
struct conf *const conf = malloc(sizeof(*conf), M_MAC_DO, M_WAITOK | M_ZERO);
|
||||||
|
|
||||||
|
init_rules(&conf->rules);
|
||||||
|
init_exec_paths(&conf->exec_paths);
|
||||||
|
conf->use_count = 0;
|
||||||
|
|
||||||
|
return (conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -1007,12 +1036,11 @@ parse_single_rule(char *rule, struct rules *const rules,
|
|||||||
* - "gid=1010>gid=1011,gid=1012,gid=1013"
|
* - "gid=1010>gid=1011,gid=1012,gid=1013"
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
parse_rules(const char *const string, struct rules **const rulesp,
|
parse_rules(const char *const string, struct rules *const rules,
|
||||||
struct parse_error **const parse_error)
|
struct parse_error **const parse_error)
|
||||||
{
|
{
|
||||||
const size_t len = strlen(string);
|
const size_t len = strlen(string);
|
||||||
char *copy, *p, *rule;
|
char *copy, *p, *rule;
|
||||||
struct rules *rules;
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
*parse_error = NULL;
|
*parse_error = NULL;
|
||||||
@@ -1024,7 +1052,6 @@ parse_rules(const char *const string, struct rules **const rulesp,
|
|||||||
return (ENAMETOOLONG);
|
return (ENAMETOOLONG);
|
||||||
}
|
}
|
||||||
|
|
||||||
rules = alloc_rules();
|
|
||||||
bcopy(string, rules->string, len + 1);
|
bcopy(string, rules->string, len + 1);
|
||||||
MPASS(rules->string[len] == '\0'); /* Catch some races. */
|
MPASS(rules->string[len] == '\0'); /* Catch some races. */
|
||||||
|
|
||||||
@@ -1044,31 +1071,90 @@ parse_rules(const char *const string, struct rules **const rulesp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*rulesp = rules;
|
out:
|
||||||
|
free(copy, M_MAC_DO);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_exec_paths(const char *const string, struct exec_paths *const exec_paths,
|
||||||
|
struct parse_error **const parse_error)
|
||||||
|
{
|
||||||
|
const size_t len = strlen(string);
|
||||||
|
char *copy, *p, *path;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
*parse_error = NULL;
|
||||||
|
|
||||||
|
if (len >= EXEC_PATHS_MAXLEN) {
|
||||||
|
make_parse_error(parse_error, 0,
|
||||||
|
"Exec path specification string is too long (%zu, max %u)",
|
||||||
|
len, EXEC_PATHS_MAXLEN - 1);
|
||||||
|
return (ENAMETOOLONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
bcopy(string, exec_paths->exec_paths_str, len + 1);
|
||||||
|
MPASS(exec_paths->exec_paths_str[len] == '\0');
|
||||||
|
|
||||||
|
copy = malloc(len + 1, M_MAC_DO, M_WAITOK);
|
||||||
|
bcopy(string, copy, len + 1);
|
||||||
|
MPASS(copy[len] == '\0');
|
||||||
|
|
||||||
|
p = copy;
|
||||||
|
while ((path = strsep_noblanks(&p, ":")) != NULL) {
|
||||||
|
if (*path == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (exec_paths->exec_path_count >= MAX_EXEC_PATHS) {
|
||||||
|
make_parse_error(parse_error, path - copy,
|
||||||
|
"Too many exec paths specified (max %d)", MAX_EXEC_PATHS);
|
||||||
|
error = EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t path_len = strlen(path);
|
||||||
|
if (path_len >= PATH_MAX) {
|
||||||
|
make_parse_error(parse_error, path - copy,
|
||||||
|
"Exec paths too long (%zu, max %u)",
|
||||||
|
path_len, PATH_MAX - 1);
|
||||||
|
error = ENAMETOOLONG;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(exec_paths->exec_paths[exec_paths->exec_path_count], path, PATH_MAX);
|
||||||
|
exec_paths->exec_path_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exec_paths->exec_path_count == 0) {
|
||||||
|
make_parse_error(parse_error, 0, "No valid exec paths found");
|
||||||
|
error = EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(copy, M_MAC_DO);
|
free(copy, M_MAC_DO);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find rules applicable to the passed prison.
|
* Find conf applicable to the passed prison.
|
||||||
*
|
*
|
||||||
* Returns the applicable rules (and never NULL). 'pr' must be unlocked.
|
* Returns the applicable conf (and never NULL). 'pr' must be unlocked.
|
||||||
* 'aprp' is set to the (ancestor) prison holding these, and it must be unlocked
|
* 'aprp' is set to the (ancestor) prison holding these, and it must be unlocked
|
||||||
* once the caller is done accessing the rules. '*aprp' is equal to 'pr' if and
|
* once the caller is done accessing the conf. '*aprp' is equal to 'pr' if and
|
||||||
* only if the current jail has its own set of rules.
|
* only if the current jail has its own set of conf.
|
||||||
*/
|
*/
|
||||||
static struct rules *
|
static struct conf *
|
||||||
find_rules(struct prison *const pr, struct prison **const aprp)
|
find_conf(struct prison *const pr, struct prison **const aprp)
|
||||||
{
|
{
|
||||||
struct prison *cpr, *ppr;
|
struct prison *cpr, *ppr;
|
||||||
struct rules *rules;
|
struct conf *conf;
|
||||||
|
|
||||||
cpr = pr;
|
cpr = pr;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
prison_lock(cpr);
|
prison_lock(cpr);
|
||||||
rules = osd_jail_get(cpr, osd_jail_slot);
|
conf = osd_jail_get(cpr, osd_jail_slot);
|
||||||
if (rules != NULL)
|
if (conf != NULL)
|
||||||
break;
|
break;
|
||||||
prison_unlock(cpr);
|
prison_unlock(cpr);
|
||||||
|
|
||||||
@@ -1078,34 +1164,36 @@ find_rules(struct prison *const pr, struct prison **const aprp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
*aprp = cpr;
|
*aprp = cpr;
|
||||||
return (rules);
|
return (conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
hold_rules(struct rules *const rules)
|
hold_conf(struct conf *const conf)
|
||||||
{
|
{
|
||||||
refcount_acquire(&rules->use_count);
|
refcount_acquire(&conf->use_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
drop_rules(struct rules *const rules)
|
drop_conf(struct conf *const conf)
|
||||||
{
|
{
|
||||||
if (refcount_release(&rules->use_count))
|
if (refcount_release(&conf->use_count)) {
|
||||||
toast_rules(rules);
|
toast_rules(&conf->rules);
|
||||||
|
free(conf, M_MAC_DO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef INVARIANTS
|
#ifdef INVARIANTS
|
||||||
static void
|
static void
|
||||||
check_rules_use_count(const struct rules *const rules, u_int expected)
|
check_conf_use_count(const struct conf *const conf, u_int expected)
|
||||||
{
|
{
|
||||||
const u_int use_count = refcount_load(&rules->use_count);
|
const u_int use_count = refcount_load(&conf->use_count);
|
||||||
|
|
||||||
if (use_count != expected)
|
if (use_count != expected)
|
||||||
panic("MAC/do: Rules at %p: Use count is %u, expected %u",
|
panic("MAC/do: Conf at %p: Use count is %u, expected %u",
|
||||||
rules, use_count, expected);
|
conf, use_count, expected);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define check_rules_use_count(...)
|
#define check_conf_use_count(...)
|
||||||
#endif /* INVARIANTS */
|
#endif /* INVARIANTS */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1117,7 +1205,7 @@ check_rules_use_count(const struct rules *const rules, u_int expected)
|
|||||||
static void
|
static void
|
||||||
dealloc_jail_osd(void *const value)
|
dealloc_jail_osd(void *const value)
|
||||||
{
|
{
|
||||||
struct rules *const rules = value;
|
struct conf *const conf = value;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If called because the "holding" jail goes down, no one should be
|
* If called because the "holding" jail goes down, no one should be
|
||||||
@@ -1133,8 +1221,8 @@ dealloc_jail_osd(void *const value)
|
|||||||
* we ensure that all thread's slots are freed first in mac_do_destroy()
|
* we ensure that all thread's slots are freed first in mac_do_destroy()
|
||||||
* to be able to check that only one reference remains.
|
* to be able to check that only one reference remains.
|
||||||
*/
|
*/
|
||||||
check_rules_use_count(rules, 1);
|
check_conf_use_count(conf, 1);
|
||||||
toast_rules(rules);
|
drop_conf(conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1146,9 +1234,9 @@ dealloc_jail_osd(void *const value)
|
|||||||
* Destroys the 'osd_jail_slot' slot of the passed jail.
|
* Destroys the 'osd_jail_slot' slot of the passed jail.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
remove_rules(struct prison *const pr)
|
remove_conf(struct prison *const pr)
|
||||||
{
|
{
|
||||||
struct rules *old_rules;
|
struct conf *old_conf;
|
||||||
int error __unused;
|
int error __unused;
|
||||||
|
|
||||||
prison_lock(pr);
|
prison_lock(pr);
|
||||||
@@ -1158,7 +1246,7 @@ remove_rules(struct prison *const pr)
|
|||||||
* decrement their use count, and possibly free them, outside of the
|
* decrement their use count, and possibly free them, outside of the
|
||||||
* prison lock.
|
* prison lock.
|
||||||
*/
|
*/
|
||||||
old_rules = osd_jail_get(pr, osd_jail_slot);
|
old_conf = osd_jail_get(pr, osd_jail_slot);
|
||||||
error = osd_jail_set(pr, osd_jail_slot, NULL);
|
error = osd_jail_set(pr, osd_jail_slot, NULL);
|
||||||
/* osd_set() never allocates memory when 'value' is NULL, nor fails. */
|
/* osd_set() never allocates memory when 'value' is NULL, nor fails. */
|
||||||
MPASS(error == 0);
|
MPASS(error == 0);
|
||||||
@@ -1169,40 +1257,40 @@ remove_rules(struct prison *const pr)
|
|||||||
osd_jail_del(pr, osd_jail_slot);
|
osd_jail_del(pr, osd_jail_slot);
|
||||||
prison_unlock(pr);
|
prison_unlock(pr);
|
||||||
|
|
||||||
if (old_rules != NULL)
|
if (old_conf != NULL)
|
||||||
drop_rules(old_rules);
|
drop_conf(old_conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Assign already built rules to a jail.
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
set_rules(struct prison *const pr, struct rules *const rules)
|
set_conf(struct prison *const pr, struct conf *const conf)
|
||||||
{
|
{
|
||||||
struct rules *old_rules;
|
struct conf *old_conf;
|
||||||
void **rsv;
|
void **rsv;
|
||||||
|
|
||||||
check_rules_use_count(rules, 0);
|
hold_conf(conf);
|
||||||
hold_rules(rules);
|
|
||||||
rsv = osd_reserve(osd_jail_slot);
|
rsv = osd_reserve(osd_jail_slot);
|
||||||
|
|
||||||
prison_lock(pr);
|
prison_lock(pr);
|
||||||
old_rules = osd_jail_get(pr, osd_jail_slot);
|
old_conf = osd_jail_get(pr, osd_jail_slot);
|
||||||
osd_jail_set_reserved(pr, osd_jail_slot, rsv, rules);
|
osd_jail_set_reserved(pr, osd_jail_slot, rsv, conf);
|
||||||
prison_unlock(pr);
|
prison_unlock(pr);
|
||||||
if (old_rules != NULL)
|
if (old_conf != NULL)
|
||||||
drop_rules(old_rules);
|
drop_conf(old_conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assigns empty rules to a jail.
|
* Assigns default conf to a jail.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
set_empty_rules(struct prison *const pr)
|
set_default_conf(struct prison *const pr)
|
||||||
{
|
{
|
||||||
struct rules *const rules = alloc_rules();
|
struct conf *const conf = alloc_conf();
|
||||||
|
|
||||||
set_rules(pr, rules);
|
strlcpy(conf->exec_paths.exec_paths_str, "/usr/bin/mdo", EXEC_PATHS_MAXLEN);
|
||||||
|
strlcpy(conf->exec_paths.exec_paths[0], "/usr/bin/mdo", PATH_MAX);
|
||||||
|
conf->exec_paths.exec_path_count = 1;
|
||||||
|
|
||||||
|
set_conf(pr, conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1210,18 +1298,99 @@ set_empty_rules(struct prison *const pr)
|
|||||||
*
|
*
|
||||||
* Returns the same error code as parse_rules() (which see).
|
* Returns the same error code as parse_rules() (which see).
|
||||||
*/
|
*/
|
||||||
static int
|
|
||||||
parse_and_set_rules(struct prison *const pr, const char *rules_string,
|
|
||||||
struct parse_error **const parse_error)
|
|
||||||
{
|
|
||||||
struct rules *rules;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
error = parse_rules(rules_string, &rules, parse_error);
|
static void
|
||||||
|
clone_rules(struct rules *dst, struct rules *const src)
|
||||||
|
{
|
||||||
|
struct rule *src_rule, *dst_rule;
|
||||||
|
|
||||||
|
bzero(dst, sizeof(*dst));
|
||||||
|
strlcpy(dst->string, src->string, sizeof(dst->string));
|
||||||
|
STAILQ_INIT(&dst->head);
|
||||||
|
|
||||||
|
STAILQ_FOREACH(src_rule, &src->head, r_entries) {
|
||||||
|
dst_rule = malloc(sizeof(*dst_rule), M_MAC_DO, M_WAITOK | M_ZERO);
|
||||||
|
bcopy(src_rule, dst_rule, sizeof(*dst_rule));
|
||||||
|
|
||||||
|
if (src_rule->uids_nb > 0) {
|
||||||
|
dst_rule->uids = malloc(sizeof(*dst_rule->uids) * src_rule->uids_nb,
|
||||||
|
M_MAC_DO, M_WAITOK);
|
||||||
|
bcopy(src_rule->uids, dst_rule->uids,
|
||||||
|
sizeof(*dst_rule->uids) * src_rule->uids_nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_rule->gids_nb > 0) {
|
||||||
|
dst_rule->gids = malloc(sizeof(*dst_rule->gids) * src_rule->gids_nb,
|
||||||
|
M_MAC_DO, M_WAITOK);
|
||||||
|
bcopy(src_rule->gids, dst_rule->gids,
|
||||||
|
sizeof(*dst_rule->gids) * src_rule->gids_nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
STAILQ_INSERT_TAIL(&dst->head, dst_rule, r_entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clone_exec_paths(struct exec_paths *dst, struct exec_paths *const src)
|
||||||
|
{
|
||||||
|
bzero(dst, sizeof(*dst));
|
||||||
|
dst->exec_path_count = src->exec_path_count;
|
||||||
|
for (int i = 0; i < src->exec_path_count; i++) {
|
||||||
|
strlcpy(dst->exec_paths[i], src->exec_paths[i],
|
||||||
|
sizeof(dst->exec_paths[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(dst->exec_paths_str, src->exec_paths_str,
|
||||||
|
sizeof(dst->exec_paths_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_and_set_conf(struct prison *pr, const char *rules_string,
|
||||||
|
const char *exec_paths_string, struct parse_error **parse_error)
|
||||||
|
{
|
||||||
|
struct prison *ppr = NULL;
|
||||||
|
struct conf *applicable_conf = NULL;
|
||||||
|
struct conf *conf;
|
||||||
|
int error = 0;
|
||||||
|
bool need_applicable_conf;
|
||||||
|
|
||||||
|
*parse_error = NULL;
|
||||||
|
|
||||||
|
need_applicable_conf = (rules_string == NULL || rules_string[0] == '\0' ||
|
||||||
|
exec_paths_string == NULL || exec_paths_string[0] == '\0');
|
||||||
|
|
||||||
|
if (need_applicable_conf) {
|
||||||
|
applicable_conf = find_conf(pr, &ppr);
|
||||||
|
hold_conf(applicable_conf);
|
||||||
|
prison_unlock(ppr);
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = alloc_conf();
|
||||||
|
|
||||||
|
if (rules_string != NULL && rules_string[0] != '\0') {
|
||||||
|
error = parse_rules(rules_string, &conf->rules, parse_error);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
else if (applicable_conf != NULL)
|
||||||
|
clone_rules(&conf->rules, &applicable_conf->rules);
|
||||||
|
|
||||||
|
if (exec_paths_string != NULL && exec_paths_string[0] != '\0') {
|
||||||
|
error = parse_exec_paths(exec_paths_string, &conf->exec_paths, parse_error);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
} else if (applicable_conf != NULL)
|
||||||
|
clone_exec_paths(&conf->exec_paths, &applicable_conf->exec_paths);
|
||||||
|
|
||||||
|
set_conf(pr, conf);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (applicable_conf != NULL)
|
||||||
|
drop_conf(applicable_conf);
|
||||||
|
if (error != 0)
|
||||||
|
drop_conf(conf);
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
set_rules(pr, rules);
|
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -1230,12 +1399,12 @@ mac_do_sysctl_rules(SYSCTL_HANDLER_ARGS)
|
|||||||
char *const buf = malloc(MAC_RULE_STRING_LEN, M_MAC_DO, M_WAITOK);
|
char *const buf = malloc(MAC_RULE_STRING_LEN, M_MAC_DO, M_WAITOK);
|
||||||
struct prison *const td_pr = req->td->td_ucred->cr_prison;
|
struct prison *const td_pr = req->td->td_ucred->cr_prison;
|
||||||
struct prison *pr;
|
struct prison *pr;
|
||||||
struct rules *rules;
|
struct conf *conf;
|
||||||
struct parse_error *parse_error;
|
struct parse_error *parse_error;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
rules = find_rules(td_pr, &pr);
|
conf = find_conf(td_pr, &pr);
|
||||||
strlcpy(buf, rules->string, MAC_RULE_STRING_LEN);
|
strlcpy(buf, conf->rules.string, MAC_RULE_STRING_LEN);
|
||||||
prison_unlock(pr);
|
prison_unlock(pr);
|
||||||
|
|
||||||
error = sysctl_handle_string(oidp, buf, MAC_RULE_STRING_LEN, req);
|
error = sysctl_handle_string(oidp, buf, MAC_RULE_STRING_LEN, req);
|
||||||
@@ -1243,13 +1412,14 @@ mac_do_sysctl_rules(SYSCTL_HANDLER_ARGS)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Set our prison's rules, not that of the jail we inherited from. */
|
/* Set our prison's rules, not that of the jail we inherited from. */
|
||||||
error = parse_and_set_rules(td_pr, buf, &parse_error);
|
error = parse_and_set_conf(td_pr, buf, NULL, &parse_error);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
if (print_parse_error)
|
if (print_parse_error)
|
||||||
printf("MAC/do: Parse error at index %zu: %s\n",
|
printf("MAC/do: Parse error at index %zu: %s\n",
|
||||||
parse_error->pos, parse_error->msg);
|
parse_error->pos, parse_error->msg);
|
||||||
free_parse_error(parse_error);
|
free_parse_error(parse_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(buf, M_MAC_DO);
|
free(buf, M_MAC_DO);
|
||||||
return (error);
|
return (error);
|
||||||
@@ -1265,13 +1435,52 @@ SYSCTL_JAIL_PARAM_SYS_SUBNODE(mac, do, CTLFLAG_RW, "Jail MAC/do parameters");
|
|||||||
SYSCTL_JAIL_PARAM_STRING(_mac_do, rules, CTLFLAG_RW, MAC_RULE_STRING_LEN,
|
SYSCTL_JAIL_PARAM_STRING(_mac_do, rules, CTLFLAG_RW, MAC_RULE_STRING_LEN,
|
||||||
"Jail MAC/do rules");
|
"Jail MAC/do rules");
|
||||||
|
|
||||||
|
static int
|
||||||
|
mac_do_sysctl_exec_paths(SYSCTL_HANDLER_ARGS)
|
||||||
|
{
|
||||||
|
char *const buf = malloc(EXEC_PATHS_MAXLEN, M_MAC_DO, M_WAITOK);
|
||||||
|
struct prison *const td_pr = req->td->td_ucred->cr_prison;
|
||||||
|
struct prison *pr;
|
||||||
|
struct conf *conf;
|
||||||
|
struct parse_error *parse_error;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
conf = find_conf(td_pr, &pr);
|
||||||
|
strlcpy(buf, conf->exec_paths.exec_paths_str, EXEC_PATHS_MAXLEN);
|
||||||
|
prison_unlock(pr);
|
||||||
|
|
||||||
|
error = sysctl_handle_string(oidp, buf, EXEC_PATHS_MAXLEN, req);
|
||||||
|
if (error != 0 || req->newptr == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = parse_and_set_conf(td_pr, NULL, buf, &parse_error);
|
||||||
|
if (error != 0) {
|
||||||
|
if (print_parse_error)
|
||||||
|
printf("MAC/do: Parse error at index %zu: %s\n",
|
||||||
|
parse_error->pos, parse_error->msg);
|
||||||
|
free_parse_error(parse_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(buf, M_MAC_DO);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSCTL_PROC(_security_mac_do, OID_AUTO, exec_paths,
|
||||||
|
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
|
||||||
|
0, 0, mac_do_sysctl_exec_paths, "A",
|
||||||
|
"Colon-separated list of allowed executables");
|
||||||
|
|
||||||
|
SYSCTL_JAIL_PARAM_STRING(_mac_do, exec_paths, CTLFLAG_RW, EXEC_PATHS_MAXLEN,
|
||||||
|
"Jail MAC/do executable paths");
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mac_do_jail_create(void *obj, void *data __unused)
|
mac_do_jail_create(void *obj, void *data)
|
||||||
{
|
{
|
||||||
struct prison *const pr = obj;
|
struct prison *const pr = obj;
|
||||||
|
|
||||||
set_empty_rules(pr);
|
set_default_conf(pr);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1280,10 +1489,14 @@ mac_do_jail_get(void *obj, void *data)
|
|||||||
{
|
{
|
||||||
struct prison *ppr, *const pr = obj;
|
struct prison *ppr, *const pr = obj;
|
||||||
struct vfsoptlist *const opts = data;
|
struct vfsoptlist *const opts = data;
|
||||||
|
struct conf *conf;
|
||||||
struct rules *rules;
|
struct rules *rules;
|
||||||
|
struct exec_paths *exec_paths;
|
||||||
int jsys, error;
|
int jsys, error;
|
||||||
|
|
||||||
rules = find_rules(pr, &ppr);
|
conf = find_conf(pr, &ppr);
|
||||||
|
rules = &conf->rules;
|
||||||
|
exec_paths = &conf->exec_paths;
|
||||||
|
|
||||||
jsys = pr == ppr ?
|
jsys = pr == ppr ?
|
||||||
(STAILQ_EMPTY(&rules->head) ? JAIL_SYS_DISABLE : JAIL_SYS_NEW) :
|
(STAILQ_EMPTY(&rules->head) ? JAIL_SYS_DISABLE : JAIL_SYS_NEW) :
|
||||||
@@ -1296,6 +1509,10 @@ mac_do_jail_get(void *obj, void *data)
|
|||||||
if (error != 0 && error != ENOENT)
|
if (error != 0 && error != ENOENT)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
error = vfs_setopts(opts, "mac.do.exec_paths", exec_paths->exec_paths_str);
|
||||||
|
if (error != 0 && error != ENOENT)
|
||||||
|
goto done;
|
||||||
|
|
||||||
error = 0;
|
error = 0;
|
||||||
done:
|
done:
|
||||||
prison_unlock(ppr);
|
prison_unlock(ppr);
|
||||||
@@ -1313,13 +1530,16 @@ _Static_assert(-1 != JAIL_SYS_DISABLE && -1 != JAIL_SYS_NEW &&
|
|||||||
* We perform only cheap checks here, i.e., we do not really parse the rules
|
* We perform only cheap checks here, i.e., we do not really parse the rules
|
||||||
* specification string, if any.
|
* specification string, if any.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mac_do_jail_check(void *obj, void *data)
|
mac_do_jail_check(void *obj, void *data)
|
||||||
{
|
{
|
||||||
struct vfsoptlist *opts = data;
|
struct vfsoptlist *opts = data;
|
||||||
char *rules_string;
|
char *rules_string, *exec_paths_string;
|
||||||
int error, jsys, size;
|
int error, jsys, rules_len = 0, exec_paths_len = 0;
|
||||||
|
bool has_rules, has_exec_paths;
|
||||||
|
|
||||||
|
/* Mark unspecified */
|
||||||
error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
|
error = vfs_copyopt(opts, "mac.do", &jsys, sizeof(jsys));
|
||||||
if (error == ENOENT)
|
if (error == ENOENT)
|
||||||
jsys = -1;
|
jsys = -1;
|
||||||
@@ -1337,60 +1557,82 @@ mac_do_jail_check(void *obj, void *data)
|
|||||||
* jail_set() calls vfs_getopts() itself later (they becoming
|
* jail_set() calls vfs_getopts() itself later (they becoming
|
||||||
* inconsistent wouldn't cause any security problem).
|
* inconsistent wouldn't cause any security problem).
|
||||||
*/
|
*/
|
||||||
error = vfs_getopt(opts, "mac.do.rules", (void**)&rules_string, &size);
|
error = vfs_getopt(opts, "mac.do.rules", (void **)&rules_string, &rules_len);
|
||||||
if (error == ENOENT) {
|
if (error == ENOENT)
|
||||||
/*
|
rules_string = NULL;
|
||||||
* Default (in absence of "mac.do.rules") is to disable (and, in
|
else {
|
||||||
* particular, not inherit).
|
|
||||||
*/
|
|
||||||
if (jsys == -1)
|
|
||||||
jsys = JAIL_SYS_DISABLE;
|
|
||||||
|
|
||||||
if (jsys == JAIL_SYS_NEW) {
|
|
||||||
vfs_opterror(opts, "'mac.do.rules' must be specified "
|
|
||||||
"given 'mac.do''s value");
|
|
||||||
return (EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Absence of "mac.do.rules" at this point is OK. */
|
|
||||||
error = 0;
|
|
||||||
} else {
|
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
if (rules_len == 0 || rules_string[rules_len - 1] != '\0') {
|
||||||
/* Not a proper string. */
|
|
||||||
if (size == 0 || rules_string[size - 1] != '\0') {
|
|
||||||
vfs_opterror(opts, "'mac.do.rules' not a proper string");
|
vfs_opterror(opts, "'mac.do.rules' not a proper string");
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
if (rules_len > MAC_RULE_STRING_LEN) {
|
||||||
if (size > MAC_RULE_STRING_LEN) {
|
vfs_opterror(opts, "'mac.do.rules' too long");
|
||||||
vfs_opterror(opts, "'mdo.rules' too long");
|
|
||||||
return (ENAMETOOLONG);
|
return (ENAMETOOLONG);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (jsys == -1)
|
/* Handle 'exec_paths' input */
|
||||||
/* Default (if "mac.do.rules" is present). */
|
error = vfs_getopt(opts, "mac.do.exec_paths", (void **)&exec_paths_string, &exec_paths_len);
|
||||||
jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
|
if (error == ENOENT)
|
||||||
JAIL_SYS_NEW;
|
exec_paths_string = NULL;
|
||||||
|
else {
|
||||||
|
if (error != 0)
|
||||||
|
return (error);
|
||||||
|
if (exec_paths_len == 0 || exec_paths_string[exec_paths_len - 1] != '\0') {
|
||||||
|
vfs_opterror(opts, "'mac.do.exec_paths' not a proper string");
|
||||||
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
if (exec_paths_len > EXEC_PATHS_MAXLEN) {
|
||||||
|
vfs_opterror(opts, "'mac.do.exec_paths' too long");
|
||||||
|
return (ENAMETOOLONG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Be liberal and accept JAIL_SYS_DISABLE and JAIL_SYS_INHERIT
|
* Be liberal, considering that an empty rule or exec paths specification
|
||||||
* with an explicit empty rules specification.
|
* is equivalent to no specification.
|
||||||
|
* This affects the JAIL_SYS_DISABLE and JAIL_SYS_INHERIT sanity checks below.
|
||||||
*/
|
*/
|
||||||
|
has_rules = rules_string && rules_string[0] != '\0';
|
||||||
|
has_exec_paths = exec_paths_string && exec_paths_string[0] != '\0';
|
||||||
|
|
||||||
|
/* Infer 'jsys' if needed */
|
||||||
|
if (jsys == -1) {
|
||||||
|
if (has_rules || has_exec_paths)
|
||||||
|
jsys = JAIL_SYS_NEW;
|
||||||
|
else
|
||||||
|
jsys = JAIL_SYS_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Final checks based on resolved 'jsys' */
|
||||||
switch (jsys) {
|
switch (jsys) {
|
||||||
case JAIL_SYS_DISABLE:
|
case JAIL_SYS_DISABLE:
|
||||||
case JAIL_SYS_INHERIT:
|
case JAIL_SYS_INHERIT:
|
||||||
if (rules_string[0] != '\0') {
|
if (has_rules) {
|
||||||
vfs_opterror(opts, "'mac.do.rules' specified "
|
vfs_opterror(opts, "'mac.do.rules' specified but should not be when mac.do is disabled or inherited");
|
||||||
"but should not given 'mac.do''s value");
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
if (has_exec_paths) {
|
||||||
|
vfs_opterror(opts, "'mac.do.exec_paths' specified but should not be when mac.do is disabled or inherited");
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case JAIL_SYS_NEW:
|
||||||
|
if (!has_rules && !has_exec_paths) {
|
||||||
|
vfs_opterror(opts, "mac.do set to 'new' but neither rules nor exec_paths specified");
|
||||||
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
/* Allow: rules only, exec_paths only (though exec_paths only is discouraged), or both */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
__assert_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (error);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -1398,9 +1640,10 @@ mac_do_jail_set(void *obj, void *data)
|
|||||||
{
|
{
|
||||||
struct prison *pr = obj;
|
struct prison *pr = obj;
|
||||||
struct vfsoptlist *opts = data;
|
struct vfsoptlist *opts = data;
|
||||||
char *rules_string;
|
char *rules_string, *exec_paths_string;
|
||||||
struct parse_error *parse_error;
|
struct parse_error *parse_error = NULL;
|
||||||
int error, jsys;
|
int error, jsys;
|
||||||
|
bool has_rules, has_exec_paths;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The invariants checks used below correspond to what has already been
|
* The invariants checks used below correspond to what has already been
|
||||||
@@ -1414,49 +1657,50 @@ mac_do_jail_set(void *obj, void *data)
|
|||||||
|
|
||||||
rules_string = vfs_getopts(opts, "mac.do.rules", &error);
|
rules_string = vfs_getopts(opts, "mac.do.rules", &error);
|
||||||
MPASS(error == 0 || error == ENOENT);
|
MPASS(error == 0 || error == ENOENT);
|
||||||
if (error == 0) {
|
exec_paths_string = vfs_getopts(opts, "mac.do.exec_paths", &error);
|
||||||
MPASS(strlen(rules_string) < MAC_RULE_STRING_LEN);
|
MPASS(error == 0 || error == ENOENT);
|
||||||
if (jsys == -1)
|
|
||||||
/* Default (if "mac.do.rules" is present). */
|
has_rules = (rules_string != NULL && rules_string[0] != '\0');
|
||||||
jsys = rules_string[0] == '\0' ? JAIL_SYS_DISABLE :
|
has_exec_paths = (exec_paths_string != NULL && exec_paths_string[0] != '\0');
|
||||||
JAIL_SYS_NEW;
|
|
||||||
|
if (jsys == -1) {
|
||||||
|
if (has_rules || has_exec_paths)
|
||||||
|
jsys = JAIL_SYS_NEW;
|
||||||
else
|
else
|
||||||
MPASS(jsys == JAIL_SYS_NEW ||
|
|
||||||
((jsys == JAIL_SYS_DISABLE ||
|
|
||||||
jsys == JAIL_SYS_INHERIT) &&
|
|
||||||
rules_string[0] == '\0'));
|
|
||||||
} else {
|
|
||||||
MPASS(jsys != JAIL_SYS_NEW);
|
|
||||||
if (jsys == -1)
|
|
||||||
/*
|
|
||||||
* Default (in absence of "mac.do.rules") is to disable
|
|
||||||
* (and, in particular, not inherit).
|
|
||||||
*/
|
|
||||||
jsys = JAIL_SYS_DISABLE;
|
jsys = JAIL_SYS_DISABLE;
|
||||||
/* If disabled, we'll store an empty rule specification. */
|
|
||||||
if (jsys == JAIL_SYS_DISABLE)
|
|
||||||
rules_string = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (jsys) {
|
switch (jsys) {
|
||||||
case JAIL_SYS_INHERIT:
|
case JAIL_SYS_INHERIT:
|
||||||
remove_rules(pr);
|
remove_conf(pr);
|
||||||
error = 0;
|
return (0);
|
||||||
break;
|
|
||||||
case JAIL_SYS_DISABLE:
|
case JAIL_SYS_DISABLE:
|
||||||
|
rules_string = "";
|
||||||
|
has_rules = true;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
case JAIL_SYS_NEW:
|
case JAIL_SYS_NEW:
|
||||||
error = parse_and_set_rules(pr, rules_string, &parse_error);
|
error = parse_and_set_conf(pr,
|
||||||
|
has_rules ? rules_string : NULL,
|
||||||
|
has_exec_paths ? exec_paths_string : NULL,
|
||||||
|
&parse_error);
|
||||||
|
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
vfs_opterror(opts,
|
if (parse_error != NULL) {
|
||||||
"MAC/do: Parse error at index %zu: %s\n",
|
vfs_opterror(opts, "MAC/do: Parse error at index %zu: %s\n",
|
||||||
parse_error->pos, parse_error->msg);
|
parse_error->pos, parse_error->msg);
|
||||||
free_parse_error(parse_error);
|
free_parse_error(parse_error);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
__assert_unreachable();
|
__assert_unreachable();
|
||||||
}
|
}
|
||||||
return (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1493,7 +1737,7 @@ struct mac_do_data_header {
|
|||||||
*/
|
*/
|
||||||
int priv;
|
int priv;
|
||||||
/* Rules to apply. */
|
/* Rules to apply. */
|
||||||
struct rules *rules;
|
struct conf *conf;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1536,7 +1780,7 @@ clear_data(void *const data)
|
|||||||
struct mac_do_data_header *const hdr = data;
|
struct mac_do_data_header *const hdr = data;
|
||||||
|
|
||||||
if (hdr != NULL) {
|
if (hdr != NULL) {
|
||||||
drop_rules(hdr->rules);
|
drop_conf(hdr->conf);
|
||||||
/* We don't deallocate so as to save time on next access. */
|
/* We don't deallocate so as to save time on next access. */
|
||||||
hdr->priv = 0;
|
hdr->priv = 0;
|
||||||
}
|
}
|
||||||
@@ -1558,7 +1802,7 @@ is_data_reusable(const void *const data, const size_t size)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
set_data_header(void *const data, const size_t size, const int priv,
|
set_data_header(void *const data, const size_t size, const int priv,
|
||||||
struct rules *const rules)
|
struct conf *const conf)
|
||||||
{
|
{
|
||||||
struct mac_do_data_header *const hdr = data;
|
struct mac_do_data_header *const hdr = data;
|
||||||
|
|
||||||
@@ -1567,7 +1811,7 @@ set_data_header(void *const data, const size_t size, const int priv,
|
|||||||
MPASS(size <= hdr->allocated_size);
|
MPASS(size <= hdr->allocated_size);
|
||||||
hdr->size = size;
|
hdr->size = size;
|
||||||
hdr->priv = priv;
|
hdr->priv = priv;
|
||||||
hdr->rules = rules;
|
hdr->conf = conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The proc lock (and any other non-sleepable lock) must not be held. */
|
/* The proc lock (and any other non-sleepable lock) must not be held. */
|
||||||
@@ -1933,7 +2177,7 @@ static int
|
|||||||
mac_do_priv_grant(struct ucred *cred, int priv)
|
mac_do_priv_grant(struct ucred *cred, int priv)
|
||||||
{
|
{
|
||||||
struct mac_do_setcred_data *const data = fetch_data();
|
struct mac_do_setcred_data *const data = fetch_data();
|
||||||
const struct rules *rules;
|
struct rules *rules;
|
||||||
const struct ucred *new_cred;
|
const struct ucred *new_cred;
|
||||||
const struct rule *rule;
|
const struct rule *rule;
|
||||||
u_int setcred_flags;
|
u_int setcred_flags;
|
||||||
@@ -1950,7 +2194,7 @@ mac_do_priv_grant(struct ucred *cred, int priv)
|
|||||||
/* No. */
|
/* No. */
|
||||||
return (EPERM);
|
return (EPERM);
|
||||||
|
|
||||||
rules = data->hdr.rules;
|
rules = &data->hdr.conf->rules;
|
||||||
new_cred = data->new_cred;
|
new_cred = data->new_cred;
|
||||||
KASSERT(new_cred != NULL,
|
KASSERT(new_cred != NULL,
|
||||||
("priv_check*() called before mac_cred_check_setcred()"));
|
("priv_check*() called before mac_cred_check_setcred()"));
|
||||||
@@ -1988,7 +2232,7 @@ static int
|
|||||||
check_proc(void)
|
check_proc(void)
|
||||||
{
|
{
|
||||||
char *path, *to_free;
|
char *path, *to_free;
|
||||||
int error;
|
int error = EPERM;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only grant privileges if requested by the right executable.
|
* Only grant privileges if requested by the right executable.
|
||||||
@@ -2010,7 +2254,26 @@ check_proc(void)
|
|||||||
*/
|
*/
|
||||||
if (vn_fullpath_jail(curproc->p_textvp, &path, &to_free) != 0)
|
if (vn_fullpath_jail(curproc->p_textvp, &path, &to_free) != 0)
|
||||||
return (EPERM);
|
return (EPERM);
|
||||||
error = strcmp(path, "/usr/bin/mdo") == 0 ? 0 : EPERM;
|
|
||||||
|
struct conf *conf;
|
||||||
|
struct exec_paths *exec_paths;
|
||||||
|
struct prison *td_pr = curproc->p_ucred->cr_prison;
|
||||||
|
struct prison *pr;
|
||||||
|
conf = find_conf(td_pr, &pr);
|
||||||
|
exec_paths = &conf->exec_paths;
|
||||||
|
|
||||||
|
if (exec_paths->exec_path_count > 0) {
|
||||||
|
for (int i = 0; i < exec_paths->exec_path_count; i++) {
|
||||||
|
if (strcmp(exec_paths->exec_paths[i], path) == 0) {
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
prison_unlock(pr);
|
||||||
|
|
||||||
free(to_free, M_TEMP);
|
free(to_free, M_TEMP);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@@ -2018,9 +2281,9 @@ check_proc(void)
|
|||||||
static void
|
static void
|
||||||
mac_do_setcred_enter(void)
|
mac_do_setcred_enter(void)
|
||||||
{
|
{
|
||||||
struct rules *rules;
|
|
||||||
struct prison *pr;
|
struct prison *pr;
|
||||||
struct mac_do_setcred_data * data;
|
struct mac_do_setcred_data * data;
|
||||||
|
struct conf *conf;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2042,8 +2305,8 @@ mac_do_setcred_enter(void)
|
|||||||
/*
|
/*
|
||||||
* Find the currently applicable rules.
|
* Find the currently applicable rules.
|
||||||
*/
|
*/
|
||||||
rules = find_rules(curproc->p_ucred->cr_prison, &pr);
|
conf = find_conf(curproc->p_ucred->cr_prison, &pr);
|
||||||
hold_rules(rules);
|
hold_conf(conf);
|
||||||
prison_unlock(pr);
|
prison_unlock(pr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2052,7 +2315,7 @@ mac_do_setcred_enter(void)
|
|||||||
data = fetch_data();
|
data = fetch_data();
|
||||||
if (!is_data_reusable(data, sizeof(*data)))
|
if (!is_data_reusable(data, sizeof(*data)))
|
||||||
data = alloc_data(data, sizeof(*data));
|
data = alloc_data(data, sizeof(*data));
|
||||||
set_data_header(data, sizeof(*data), PRIV_CRED_SETCRED, rules);
|
set_data_header(data, sizeof(*data), PRIV_CRED_SETCRED, conf);
|
||||||
/* Not really necessary, but helps to catch programming errors. */
|
/* Not really necessary, but helps to catch programming errors. */
|
||||||
data->new_cred = NULL;
|
data->new_cred = NULL;
|
||||||
data->setcred_flags = 0;
|
data->setcred_flags = 0;
|
||||||
@@ -2102,10 +2365,10 @@ mac_do_init(struct mac_policy_conf *mpc)
|
|||||||
struct prison *pr;
|
struct prison *pr;
|
||||||
|
|
||||||
osd_jail_slot = osd_jail_register(dealloc_jail_osd, osd_methods);
|
osd_jail_slot = osd_jail_register(dealloc_jail_osd, osd_methods);
|
||||||
set_empty_rules(&prison0);
|
set_default_conf(&prison0);
|
||||||
sx_slock(&allprison_lock);
|
sx_slock(&allprison_lock);
|
||||||
TAILQ_FOREACH(pr, &allprison, pr_list)
|
TAILQ_FOREACH(pr, &allprison, pr_list)
|
||||||
set_empty_rules(pr);
|
set_default_conf(pr);
|
||||||
sx_sunlock(&allprison_lock);
|
sx_sunlock(&allprison_lock);
|
||||||
|
|
||||||
osd_thread_slot = osd_thread_register(dealloc_thread_osd);
|
osd_thread_slot = osd_thread_register(dealloc_thread_osd);
|
||||||
|
|||||||
Reference in New Issue
Block a user