summaryrefslogtreecommitdiff
path: root/security/apparmor/domain.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor/domain.c')
-rw-r--r--security/apparmor/domain.c97
1 files changed, 68 insertions, 29 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index f02bf770f638..d6958eb00e30 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -12,6 +12,7 @@
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
+#include <linux/mutex.h>
#include <linux/syscalls.h>
#include <linux/personality.h>
#include <linux/xattr.h>
@@ -135,7 +136,7 @@ static int label_compound_match(struct aa_profile *profile,
struct label_it i;
struct path_cond cond = { };
- /* find first subcomponent that is in view and going to be interated with */
+ /* find first subcomponent that is in view and going to be interacted with */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
@@ -863,6 +864,15 @@ audit:
}
/* ensure none ns domain transitions are correctly applied with onexec */
+static struct aa_label *label_merge_wrap(struct aa_label *a, struct aa_label *b,
+ gfp_t gfp)
+{
+ struct aa_label *label = aa_label_merge(a, b, gfp);
+
+ if (!label)
+ return ERR_PTR(-ENOMEM);
+ return label;
+}
static struct aa_label *handle_onexec(const struct cred *subj_cred,
struct aa_label *label,
@@ -890,12 +900,13 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
return ERR_PTR(error);
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
- stack ? aa_label_merge(&profile->label, onexec,
- GFP_KERNEL)
+ stack ? label_merge_wrap(&profile->label, onexec,
+ GFP_KERNEL)
: aa_get_newest_label(onexec),
profile_transition(subj_cred, profile, bprm,
buffer, cond, unsafe));
- if (new)
+ AA_BUG(!new);
+ if (!IS_ERR(new))
return new;
/* TODO: get rid of GLOBAL_ROOT_UID */
@@ -904,7 +915,8 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
OP_CHANGE_ONEXEC,
AA_MAY_ONEXEC, bprm->filename, NULL,
onexec, GLOBAL_ROOT_UID,
- "failed to build target label", -ENOMEM));
+ "failed to build target label",
+ PTR_ERR(new)));
return ERR_PTR(error);
}
@@ -967,14 +979,10 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
profile_transition(subj_cred, profile, bprm,
buffer,
&cond, &unsafe));
-
AA_BUG(!new);
if (IS_ERR(new)) {
error = PTR_ERR(new);
goto done;
- } else if (!new) {
- error = -ENOMEM;
- goto done;
}
/* Policy has specified a domain transitions. If no_new_privs and
@@ -1109,6 +1117,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
int count, int flags)
{
struct aa_profile *profile, *root, *hat = NULL;
+ struct aa_ns *ns, *new_ns;
struct aa_label *new;
struct label_it it;
bool sibling = false;
@@ -1119,6 +1128,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
AA_BUG(!hats);
AA_BUG(count < 1);
+ /*
+ * Acquire the newest label and then hold the lock until we choose a
+ * hat, so that profile replacement doesn't atomically truncate the
+ * list of potential hats. Because we are getting the namespaces from
+ * the profiles and label, we can rely on the namespaces being live
+ * and avoid incrementing their refcounts while grabbing the lock.
+ */
+ label = aa_get_label(label);
+ ns = labels_ns(label);
+
+retry:
+ mutex_lock_nested(&ns->lock, ns->level);
+ if (label_is_stale(label)) {
+ new = aa_get_newest_label(label);
+ new_ns = labels_ns(new);
+ if (new_ns != ns) {
+ aa_put_label(new);
+ mutex_unlock(&ns->lock);
+ ns = new_ns;
+ label = new;
+ goto retry;
+ }
+ aa_put_label(label);
+ label = new;
+ }
+
if (PROFILE_IS_HAT(labels_profile(label)))
sibling = true;
@@ -1127,7 +1162,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
name = hats[i];
label_for_each_in_scope(it, labels_ns(label), label, profile) {
if (sibling && PROFILE_IS_HAT(profile)) {
- root = aa_get_profile_rcu(&profile->parent);
+ root = aa_get_profile(profile->parent);
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
root = aa_get_profile(profile);
} else { /* conflicting change type */
@@ -1187,6 +1222,7 @@ fail:
GLOBAL_ROOT_UID, info, error);
}
}
+ mutex_unlock(&ns->lock);
return ERR_PTR(error);
build:
@@ -1194,11 +1230,9 @@ build:
build_change_hat(subj_cred, profile, name,
sibling),
aa_get_label(&profile->label));
- if (!new) {
- info = "label build failed";
- error = -ENOMEM;
- goto fail;
- } /* else if (IS_ERR) build_change_hat has logged error so return new */
+ mutex_unlock(&ns->lock);
+ AA_BUG(!new);
+ /* return new label or error ptr */
return new;
}
@@ -1527,6 +1561,9 @@ check:
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
aa_get_label(target),
aa_get_label(&profile->label));
+ AA_BUG(!new);
+ if (IS_ERR(new))
+ goto build_fail;
/*
* no new privs prevents domain transitions that would
* reduce restrictions.
@@ -1545,27 +1582,29 @@ check:
/* only transition profiles in the current ns */
if (stack)
new = aa_label_merge(label, target, GFP_KERNEL);
- if (IS_ERR_OR_NULL(new)) {
- info = "failed to build target label";
- if (!new)
- error = -ENOMEM;
- else
- error = PTR_ERR(new);
- new = NULL;
- perms.allow = 0;
- goto audit;
- }
+ if (IS_ERR_OR_NULL(new))
+ goto build_fail;
error = aa_replace_current_label(new);
} else {
- if (new) {
- aa_put_label(new);
- new = NULL;
- }
+ /* new will be recomputed so at exec time. So discard */
+ aa_put_label(new);
+ new = NULL;
/* full transition will be built in exec path */
aa_set_current_onexec(target, stack);
}
+ goto audit;
+
+build_fail:
+ info = "failed to build target label";
+ if (!new)
+ error = -ENOMEM;
+ else
+ error = PTR_ERR(new);
+ new = NULL;
+ perms.allow = 0;
+
audit:
error = fn_for_each_in_scope(label, profile,
aa_audit_file(subj_cred,