apparmor: fix nnp subset test for unconfined
authorJohn Johansen <john.johansen@canonical.com>
Wed, 25 Sep 2019 15:02:48 +0000 (08:02 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jun 2020 13:32:52 +0000 (15:32 +0200)
[ Upstream commit 3ed4aaa94fc07db3cd0c91be95e3e1b9782a2710 ]

The subset test is not taking into account the unconfined exception
which will cause profile transitions in the stacked confinement
case to fail when no_new_privs is applied.

This fixes a regression introduced in the fix for
https://bugs.launchpad.net/bugs/1839037

BugLink: https://bugs.launchpad.net/bugs/1844186
Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
security/apparmor/domain.c
security/apparmor/include/label.h
security/apparmor/label.c

index b9d5b3459705b1d277c4f1b59a17e74a18d1e810..13b33490e07927cc07921e83fb753cfc370ce6da 100644 (file)
@@ -939,7 +939,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
         * aways results in a further reduction of permissions.
         */
        if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
-           !unconfined(label) && !aa_label_is_subset(new, ctx->nnp)) {
+           !unconfined(label) &&
+           !aa_label_is_unconfined_subset(new, ctx->nnp)) {
                error = -EPERM;
                info = "no new privs";
                goto audit;
@@ -1217,7 +1218,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
                 * reduce restrictions.
                 */
                if (task_no_new_privs(current) && !unconfined(label) &&
-                   !aa_label_is_subset(new, ctx->nnp)) {
+                   !aa_label_is_unconfined_subset(new, ctx->nnp)) {
                        /* not an apparmor denial per se, so don't log it */
                        AA_DEBUG("no_new_privs - change_hat denied");
                        error = -EPERM;
@@ -1238,7 +1239,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
                 * reduce restrictions.
                 */
                if (task_no_new_privs(current) && !unconfined(label) &&
-                   !aa_label_is_subset(previous, ctx->nnp)) {
+                   !aa_label_is_unconfined_subset(previous, ctx->nnp)) {
                        /* not an apparmor denial per se, so don't log it */
                        AA_DEBUG("no_new_privs - change_hat denied");
                        error = -EPERM;
@@ -1433,7 +1434,7 @@ int aa_change_profile(const char *fqname, int flags)
                 * reduce restrictions.
                 */
                if (task_no_new_privs(current) && !unconfined(label) &&
-                   !aa_label_is_subset(new, ctx->nnp)) {
+                   !aa_label_is_unconfined_subset(new, ctx->nnp)) {
                        /* not an apparmor denial per se, so don't log it */
                        AA_DEBUG("no_new_privs - change_hat denied");
                        error = -EPERM;
index 7ce5fe73ae7f5f641c279d03ad79c3b9f4ab8d0d..cecbd3f5429c607fcb0ffad1bd5c1e9c11c4e153 100644 (file)
@@ -285,6 +285,7 @@ bool aa_label_init(struct aa_label *label, int size, gfp_t gfp);
 struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp);
 
 bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub);
+bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub);
 struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
                                             struct aa_label *set,
                                             struct aa_label *sub);
index 6e7aa2ef8ee0fcb748c515815022cc9d8609af0f..6727e6fb69df2230fd45e9344a6afa1a8c2c3d66 100644 (file)
@@ -554,6 +554,39 @@ bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
        return __aa_label_next_not_in_set(&i, set, sub) == NULL;
 }
 
+/**
+ * aa_label_is_unconfined_subset - test if @sub is a subset of @set
+ * @set: label to test against
+ * @sub: label to test if is subset of @set
+ *
+ * This checks for subset but taking into account unconfined. IF
+ * @sub contains an unconfined profile that does not have a matching
+ * unconfined in @set then this will not cause the test to fail.
+ * Conversely we don't care about an unconfined in @set that is not in
+ * @sub
+ *
+ * Returns: true if @sub is special_subset of @set
+ *     else false
+ */
+bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub)
+{
+       struct label_it i = { };
+       struct aa_profile *p;
+
+       AA_BUG(!set);
+       AA_BUG(!sub);
+
+       if (sub == set)
+               return true;
+
+       do {
+               p = __aa_label_next_not_in_set(&i, set, sub);
+               if (p && !profile_unconfined(p))
+                       break;
+       } while (p);
+
+       return p == NULL;
+}
 
 
 /**