Fix execve behavior apparmor for PR_{GET,SET}_NO_NEW_PRIVS
John Johansen [Thu, 12 Apr 2012 21:47:51 +0000 (16:47 -0500)]
Add support for AppArmor to explicitly fail requested domain transitions
if NO_NEW_PRIVS is set and the task is not unconfined.

Transitions from unconfined are still allowed because this always results
in a reduction of privileges.

Acked-by: Eric Paris <eparis@redhat.com>
Signed-off-by: Will Drewry <wad@chromium.org>
Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Andy Lutomirski <luto@amacapital.net>

v18: new acked-by, new description
Signed-off-by: James Morris <james.l.morris@oracle.com>

security/apparmor/domain.c

index 18c88d0..b81ea10 100644 (file)
@@ -360,10 +360,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
        if (bprm->cred_prepared)
                return 0;
 
-       /* XXX: no_new_privs is not usable with AppArmor yet */
-       if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
-               return -EPERM;
-
        cxt = bprm->cred->security;
        BUG_ON(!cxt);
 
@@ -398,6 +394,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
                        new_profile = find_attach(ns, &ns->base.profiles, name);
                if (!new_profile)
                        goto cleanup;
+               /*
+                * NOTE: Domain transitions from unconfined are allowed
+                * even when no_new_privs is set because this aways results
+                * in a further reduction of permissions.
+                */
                goto apply;
        }
 
@@ -459,6 +460,16 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
                /* fail exec */
                error = -EACCES;
 
+       /*
+        * Policy has specified a domain transition, if no_new_privs then
+        * fail the exec.
+        */
+       if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
+               aa_put_profile(new_profile);
+               error = -EPERM;
+               goto cleanup;
+       }
+
        if (!new_profile)
                goto audit;
 
@@ -613,6 +624,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
        const char *target = NULL, *info = NULL;
        int error = 0;
 
+       /*
+        * Fail explicitly requested domain transitions if no_new_privs.
+        * There is no exception for unconfined as change_hat is not
+        * available.
+        */
+       if (current->no_new_privs)
+               return -EPERM;
+
        /* released below */
        cred = get_current_cred();
        cxt = cred->security;
@@ -754,6 +773,18 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
        cxt = cred->security;
        profile = aa_cred_profile(cred);
 
+       /*
+        * Fail explicitly requested domain transitions if no_new_privs
+        * and not unconfined.
+        * Domain transitions from unconfined are allowed even when
+        * no_new_privs is set because this aways results in a reduction
+        * of permissions.
+        */
+       if (current->no_new_privs && !unconfined(profile)) {
+               put_cred(cred);
+               return -EPERM;
+       }
+
        if (ns_name) {
                /* released below */
                ns = aa_find_namespace(profile->ns, ns_name);