apparmor: allow restricting unprivileged change_profile
authorJohn Johansen <john.johansen@canonical.com>
Wed, 9 Aug 2023 07:26:36 +0000 (00:26 -0700)
committerJohn Johansen <john.johansen@canonical.com>
Wed, 18 Oct 2023 22:48:44 +0000 (15:48 -0700)
unprivileged unconfined can use change_profile to alter the confinement
set by the mac admin.

Allow restricting unprivileged unconfined by still allowing change_profile
but stacking the change against unconfined. This allows unconfined to
still apply system policy but allows the task to enter the new confinement.

If unprivileged unconfined is required a sysctl is provided to switch
to the previous behavior.

Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/apparmorfs.c
security/apparmor/domain.c
security/apparmor/include/policy.h
security/apparmor/lsm.c
security/apparmor/policy.c

index b123abbc43d8798967049693207c184450533c03..6d0848f10ff0cf2acb8a41276bfd63adb24e497a 100644 (file)
@@ -2341,6 +2341,11 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = {
        { }
 };
 
+static struct aa_sfs_entry aa_sfs_entry_unconfined[] = {
+       AA_SFS_FILE_BOOLEAN("change_profile", 1),
+       { }
+};
+
 static struct aa_sfs_entry aa_sfs_entry_versions[] = {
        AA_SFS_FILE_BOOLEAN("v5",       1),
        AA_SFS_FILE_BOOLEAN("v6",       1),
@@ -2358,6 +2363,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
        AA_SFS_FILE_U64("outofband",            MAX_OOB_SUPPORTED),
        AA_SFS_FILE_U64("permstable32_version", 1),
        AA_SFS_FILE_STRING("permstable32", PERMS32STR),
+       AA_SFS_DIR("unconfined_restrictions",   aa_sfs_entry_unconfined),
        { }
 };
 
index 87dfa0e403982cd4fecf3cffe9e2a885ca658910..ed4a13d44894ce950bb53646462105d07b9c7111 100644 (file)
@@ -1311,6 +1311,8 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
        return error;
 }
 
+const char *stack_msg = "change_profile unprivileged unconfined converted to stacking";
+
 /**
  * aa_change_profile - perform a one-way profile transition
  * @fqname: name of profile may include namespace (NOT NULL)
@@ -1370,6 +1372,28 @@ int aa_change_profile(const char *fqname, int flags)
                        op = OP_CHANGE_PROFILE;
        }
 
+       /* This should move to a per profile test. Requires pushing build
+        * into callback
+        */
+       if (!stack && unconfined(label) &&
+           label == &labels_ns(label)->unconfined->label &&
+           aa_unprivileged_unconfined_restricted &&
+           /* TODO: refactor so this check is a fn */
+           cap_capable(current_cred(), &init_user_ns, CAP_MAC_OVERRIDE,
+                       CAP_OPT_NOAUDIT)) {
+               /* regardless of the request in this case apparmor
+                * stacks against unconfined so admin set policy can't be
+                * by-passed
+                */
+               stack = true;
+               perms.audit = request;
+               (void) fn_for_each_in_ns(label, profile,
+                               aa_audit_file(subj_cred, profile, &perms, op,
+                                             request, auditname, NULL, target,
+                                             GLOBAL_ROOT_UID, stack_msg, 0));
+               perms.audit = 0;
+       }
+
        if (*fqname == '&') {
                stack = true;
                /* don't have label_parse() do stacking */
index e69c91619a7c24585e92e9ff4f7cf609a0fa7b51..75088cc310b677f7fddf95215965049fa9ee12bf 100644 (file)
@@ -34,6 +34,7 @@
 struct aa_ns;
 
 extern int unprivileged_userns_apparmor_policy;
+extern int aa_unprivileged_unconfined_restricted;
 
 extern const char *const aa_profile_mode_names[];
 #define APPARMOR_MODE_NAMES_MAX_INDEX 4
index bcfe8b9cb4c1c4c32254f07aa5b5a857fe4600ef..518576ae3cfb9661a2d0c45670887941e2864e0e 100644 (file)
@@ -1798,6 +1798,13 @@ static struct ctl_table apparmor_sysctl_table[] = {
                .mode           = 0600,
                .proc_handler   = apparmor_dointvec,
        },
+       {
+               .procname       = "apparmor_restrict_unprivileged_unconfined",
+               .data           = &aa_unprivileged_unconfined_restricted,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = apparmor_dointvec,
+       },
 
        { }
 };
index 0b36bd6a6f337ef7ad784f7535da2ce7f5f23837..a441d96adcbf4f8912a08123aa17614739884052 100644 (file)
@@ -88,6 +88,7 @@
 #include "include/resource.h"
 
 int unprivileged_userns_apparmor_policy = 1;
+int aa_unprivileged_unconfined_restricted;
 
 const char *const aa_profile_mode_names[] = {
        "enforce",