]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
apparmor: add user namespace creation mediation
authorJohn Johansen <john.johansen@canonical.com>
Fri, 9 Sep 2022 23:00:09 +0000 (16:00 -0700)
committerJohn Johansen <john.johansen@canonical.com>
Wed, 18 Oct 2023 22:49:02 +0000 (15:49 -0700)
Unprivileged user namespace creation is often used as a first step
in privilege escalation attacks. Instead of disabling it at the
sysrq level, which blocks its legitimate use as for setting up a sandbox,
allow control on a per domain basis.

This allows an admin to quickly lock down a system while also still
allowing legitimate use.

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

index 6d0848f10ff0cf2acb8a41276bfd63adb24e497a..7170349c8af0f96c516afa0d65f58b4d179f7b46 100644 (file)
@@ -2375,6 +2375,7 @@ static struct aa_sfs_entry aa_sfs_entry_mount[] = {
 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"),
        { }
 };
 
index 6933cb2f679b0b816be5151a550a3d668ae1dd65..3b24f4a8c7279a4a2a5ecf057f314f1d12f2ef63 100644 (file)
@@ -58,7 +58,7 @@ static const char *const aa_class_names[] = {
        "io_uring",
        "module",
        "lsm",
-       "unknown",
+       "namespace",
        "unknown",
        "unknown",
        "unknown",
index 8a81557c9d59b1f3f2aa84046fd7d2768a914a49..e2b759f2406415a3a26af01a3b4eacde3fe306ab 100644 (file)
@@ -33,6 +33,7 @@
 #define AA_CLASS_IO_URING      18
 #define AA_CLASS_MODULE                19
 #define AA_CLASS_DISPLAY_LSM   20
+#define AA_CLASS_NS            21
 
 #define AA_CLASS_X             31
 #define AA_CLASS_DBUS          32
index 42d701fec5a6d947e57c15f7f36dacd741fc0df6..095707e05b7068f8c2e4fe5efe5aefc29d92b190 100644 (file)
@@ -103,6 +103,7 @@ enum audit_type {
 #define OP_PROF_LOAD "profile_load"
 #define OP_PROF_RM "profile_remove"
 
+#define OP_USERNS_CREATE "userns_create"
 
 struct apparmor_audit_data {
        int error;
index 13945e2495f0b57c6f69357537bc462d15a65901..b1aaaf60fa8b8477ede2059487d7230e8adebb7c 100644 (file)
@@ -96,4 +96,10 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
                  u32 request);
 
 
+
+#define AA_USERNS_CREATE       8
+
+int aa_profile_ns_perm(struct aa_profile *profile,
+                      struct apparmor_audit_data *ad, u32 request);
+
 #endif /* __AA_TASK_H */
index 518576ae3cfb9661a2d0c45670887941e2864e0e..c61835bd7db92288f4010f2f3687f51212cedbf7 100644 (file)
@@ -836,6 +836,27 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo
        return error;
 }
 
+static int apparmor_userns_create(const struct cred *cred)
+{
+       struct aa_label *label;
+       struct aa_profile *profile;
+       int error = 0;
+       DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_NS,
+                         OP_USERNS_CREATE);
+
+       ad.subj_cred = current_cred();
+
+       label = begin_current_label_crit_section();
+       if (!unconfined(label)) {
+               error = fn_for_each(label, profile,
+                                   aa_profile_ns_perm(profile, &ad,
+                                                      AA_USERNS_CREATE));
+       }
+       end_current_label_crit_section(label);
+
+       return error;
+}
+
 /**
  * apparmor_sk_alloc_security - allocate and attach the sk_security field
  */
@@ -1313,6 +1334,7 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
        LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid_obj),
        LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
        LSM_HOOK_INIT(task_kill, apparmor_task_kill),
+       LSM_HOOK_INIT(userns_create, apparmor_userns_create),
 
 #ifdef CONFIG_AUDIT
        LSM_HOOK_INIT(audit_rule_init, aa_audit_rule_init),
@@ -1784,6 +1806,7 @@ static int apparmor_dointvec(struct ctl_table *table, int write,
 }
 
 static struct ctl_table apparmor_sysctl_table[] = {
+#ifdef CONFIG_USER_NS
        {
                .procname       = "unprivileged_userns_apparmor_policy",
                .data           = &unprivileged_userns_apparmor_policy,
@@ -1791,6 +1814,7 @@ static struct ctl_table apparmor_sysctl_table[] = {
                .mode           = 0600,
                .proc_handler   = apparmor_dointvec,
        },
+#endif /* CONFIG_USER_NS */
        {
                .procname       = "apparmor_display_secid_mode",
                .data           = &apparmor_display_secid_mode,
@@ -1805,7 +1829,6 @@ static struct ctl_table apparmor_sysctl_table[] = {
                .mode           = 0600,
                .proc_handler   = apparmor_dointvec,
        },
-
        { }
 };
 
index 1a7c9d02e31db3f69cd8cc38d42366d90f59ccd1..f29a2e80e6bf68cbc225ef3970fd12ca29526284 100644 (file)
@@ -298,3 +298,44 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
                        profile_tracee_perm(tracee_cred, profile, tracer,
                                            xrequest, &sa));
 }
+
+/* call back to audit ptrace fields */
+static void audit_ns_cb(struct audit_buffer *ab, void *va)
+{
+       struct apparmor_audit_data *ad = aad_of_va(va);
+
+       if (ad->request & AA_USERNS_CREATE)
+               audit_log_format(ab, " requested=\"userns_create\"");
+
+       if (ad->denied & AA_USERNS_CREATE)
+               audit_log_format(ab, " denied=\"userns_create\"");
+}
+
+int aa_profile_ns_perm(struct aa_profile *profile,
+                      struct apparmor_audit_data *ad,
+                      u32 request)
+{
+       struct aa_perms perms = { };
+       int error = 0;
+
+       ad->subj_label = &profile->label;
+       ad->request = request;
+
+       if (!profile_unconfined(profile)) {
+               struct aa_ruleset *rules = list_first_entry(&profile->rules,
+                                                           typeof(*rules),
+                                                           list);
+               aa_state_t state;
+
+               state = RULE_MEDIATES(rules, ad->class);
+               if (!state)
+                       /* TODO: add flag to complain about unmediated */
+                       return 0;
+               perms = *aa_lookup_perms(rules->policy, state);
+               aa_apply_modes_to_perms(profile, &perms);
+               error = aa_check_perms(profile, &perms, request, ad,
+                                      audit_ns_cb);
+       }
+
+       return error;
+}