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:
committed by
Paolo Pisati
parent
38fd470ef0
commit
43a6c29532
@@ -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&"),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -153,6 +153,9 @@ struct apparmor_audit_data {
|
||||
kuid_t fsuid;
|
||||
kuid_t ouid;
|
||||
} mq;
|
||||
struct {
|
||||
const char *target;
|
||||
} ns;
|
||||
};
|
||||
};
|
||||
struct {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user