UBUNTU: SAUCE: apparmor4.0.0 [85/90]: apparmor: allow profile to be transitioned when a user ns is created

BugLink: http://bugs.launchpad.net/bugs/2028253

User namespaces are used to create sandboxes and often need a different
set of permission than during setup. Allow for a profile state change
on the task creating the namespace.

The transition is encoded the same as exec domain transitions and
only uses a subset of the transitions available at exec. Enivronment
scrubbing etc are not available.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 90f73560fd8dfec350c62a630a9f5719e1da2f23
https://git.launchpad.net/~apparmor-dev/ubuntu-kernel-next)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
This commit is contained in:
John Johansen
2024-01-03 21:56:19 -08:00
committed by Paolo Pisati
parent 38fd470ef0
commit 43a6c29532
5 changed files with 151 additions and 12 deletions
+1
View File
@@ -2627,6 +2627,7 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = {
AA_SFS_FILE_BOOLEAN("profile", 1),
AA_SFS_FILE_BOOLEAN("pivot_root", 0),
AA_SFS_FILE_STRING("mask", "userns_create"),
AA_SFS_FILE_STRING("userns_create", "pciu&"),
{ }
};
+3
View File
@@ -153,6 +153,9 @@ struct apparmor_audit_data {
kuid_t fsuid;
kuid_t ouid;
} mq;
struct {
const char *target;
} ns;
};
};
struct {
+3 -2
View File
@@ -99,7 +99,8 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
#define AA_USERNS_CREATE 8
int aa_profile_ns_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad, u32 request);
struct aa_label *aa_profile_ns_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad,
u32 request);
#endif /* __AA_TASK_H */
+13 -4
View File
@@ -1242,7 +1242,7 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo
return error;
}
static int apparmor_userns_create(const struct cred *cred)
static int apparmor_userns_create(const struct cred *new_cred)
{
struct aa_label *label;
struct aa_profile *profile;
@@ -1252,13 +1252,22 @@ static int apparmor_userns_create(const struct cred *cred)
/* remove unprivileged_userns_restricted check when unconfined is updated */
if (aa_unprivileged_userns_restricted ||
label_mediates(label, AA_CLASS_NS)) {
struct aa_label *new;
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_NS,
OP_USERNS_CREATE);
ad.subj_cred = current_cred();
error = fn_for_each(label, profile,
aa_profile_ns_perm(profile, &ad, AA_USERNS_CREATE));
end_current_label_crit_section(label);
new = fn_label_build(label, profile, GFP_KERNEL,
aa_profile_ns_perm(profile, &ad,
AA_USERNS_CREATE));
if (IS_ERR(new)) {
error = PTR_ERR(new);
} else if (new && cred_label(new_cred) != new) {
aa_put_label(cred_label(new_cred));
set_cred_label(new_cred, new);
} else {
aa_put_label(new);
}
}
end_current_label_crit_section(label);
+131 -6
View File
@@ -308,19 +308,106 @@ static void audit_ns_cb(struct audit_buffer *ab, void *va)
if (ad->denied & AA_USERNS_CREATE)
audit_log_format(ab, " denied=\"userns_create\"");
if (ad->peer) {
audit_log_format(ab, " target=");
aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
FLAG_VIEW_SUBNS, GFP_KERNEL);
} else if (ad->ns.target) {
audit_log_format(ab, " target=");
audit_log_untrustedstring(ab, ad->ns.target);
}
}
int aa_profile_ns_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad,
u32 request)
/*
* Returns: refcounted label to change to, even if no change
* PTR_ERR on failure
*/
static struct aa_label *ns_x_to_label(struct aa_profile *profile,
u32 xindex, const char **lookupname,
const char **info)
{
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new = NULL;
u32 xtype = xindex & AA_X_TYPE_MASK;
struct aa_label *stack = NULL;
/* must be none or table */
switch (xtype) {
case AA_X_NONE:
/* default not failure */
*lookupname = NULL;
return NULL;
break;
case AA_X_TABLE:
/* TODO: fix when perm mapping done at unload */
/* released by caller
* if null for both stack and direct want to try fallback
*/
new = x_table_lookup(profile, xindex, lookupname);
if (!new) {
*info = "failed to find transition profile";
return ERR_PTR(-ENOMEM);
}
if (**lookupname == '&') {
stack = new;
new = NULL;
}
break;
default:
*info = "invalid profile transition type";
return ERR_PTR(-EINVAL);
break;
}
/* stack is true if !new */
if (!new) {
if (xindex & AA_X_UNCONFINED) {
new = aa_get_newest_label(ns_unconfined(profile->ns));
*info = "ux fallback";
} else {
if (xindex & AA_X_INHERIT) {
/* (p|c|n)ix - don't change profile but do
* use the newest version
*/
*info = "ix fallback";
/* no profile && no error */
} /* else, stack is implicitly against current */
new = aa_get_newest_label(&profile->label);
}
}
if (stack) {
/* base the stack on post domain transition */
struct aa_label *base = new;
new = aa_label_merge(base, stack, GFP_KERNEL);
/* null on error */
aa_put_label(base);
aa_put_label(stack);
if (!new)
return ERR_PTR(-ENOMEM);
}
/* released by caller */
return new;
}
struct aa_label *aa_profile_ns_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad,
u32 request)
{
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new;
struct aa_perms perms = { };
aa_state_t state;
ad->subj_label = &profile->label;
ad->request = request;
int error;
/* TODO: rework unconfined profile/dfa to mediate user ns, then
* we can drop the unconfined test
@@ -335,19 +422,57 @@ int aa_profile_ns_perm(struct aa_profile *profile,
if (!aa_unprivileged_userns_restricted ||
ns_capable_noaudit(current_user_ns(),
CAP_SYS_ADMIN))
return 0;
return aa_get_newest_label(&profile->label);
ad->info = "User namespace creation restricted";
/* unconfined unprivileged user */
/* don't just return: allow complain mode to override */
// hardcode unconfined transition for now
new = aa_label_parse(&profile->label,
"unprivileged_userns", GFP_KERNEL,
true, false);
if (IS_ERR(new)) {
ad->info = "Userns create restricted - failed to find unprivileged_userns profile";
ad->error = PTR_ERR(new);
ad->ns.target = "unprivileged_userns";
new = NULL;
perms.deny |= request;
goto hard_coded;
}
ad->info = "Userns create - transitioning profile";
perms.audit = request;
perms.allow = request;
goto hard_coded;
// once we have special unconfined profile, jump to ns_x_to_label()
// end hardcode
} else if (!aa_unprivileged_userns_restricted_force) {
return 0;
return aa_get_newest_label(&profile->label);
}
/* continue to mediation */
}
perms = *aa_lookup_perms(rules->policy, state);
new = ns_x_to_label(profile, perms.xindex, &ad->ns.target, &ad->info);
if (IS_ERR(new)) {
ad->error = PTR_ERR(new);
new = NULL;
perms.deny |= request;
} else if (!new) {
/* no transition - not done in x_to_label so we can track */
new = aa_get_label(&profile->label);
} else {
hard_coded:
ad->peer = new;
}
if (aa_unprivileged_userns_restricted_complain)
perms.complain = ALL_PERMS_MASK;
// TODO: nnp
// TODO: complain mode support for transitions
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, ad, audit_ns_cb);
error = aa_check_perms(profile, &perms, request, ad, audit_ns_cb);
if (error) {
aa_put_label(new);
return ERR_PTR(error);
}
return new;
}