diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 64cc3044a42c..daa459a971d4 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -105,6 +105,18 @@ config SECURITY_APPARMOR_PARANOID_LOAD includes policy, and has some form of integrity check. Disabling the check will speed up policy loads. +config SECURITY_APPARMOR_RESTRICT_USERNS + bool "Restrict user namespace creation to confined domains" + depends on SECURITY_APPARMOR && USER_NS + default y + help + This options allows controlling whether apparmor restricts + the creation of new user namespaces to confined tasks by + default. If set unconfined tasks without CAP_SYS_ADMIN + will not be allowed to create new user namespaces. Confined + tasks ability to create new user namespaces will be controlled + by their profile. + config SECURITY_APPARMOR_KUNIT_TEST tristate "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS depends on KUNIT && SECURITY_APPARMOR diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index f7d1644a7423..f48702185ded 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -34,6 +34,7 @@ struct aa_ns; extern int unprivileged_userns_apparmor_policy; +extern int aa_unprivileged_userns_restricted; extern int aa_unprivileged_unconfined_restricted; extern const char *const aa_profile_mode_names[]; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 067793f4d747..e8a695146444 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1050,7 +1050,7 @@ static int apparmor_userns_create(const struct cred *cred) ad.subj_cred = current_cred(); label = begin_current_label_crit_section(); - if (!unconfined(label)) { + if (aa_unprivileged_userns_restricted || !unconfined(label)) { error = fn_for_each(label, profile, aa_profile_ns_perm(profile, &ad, AA_USERNS_CREATE)); @@ -2034,6 +2034,15 @@ static struct ctl_table apparmor_sysctl_table[] = { .mode = 0600, .proc_handler = apparmor_dointvec, }, +#ifdef CONFIG_USER_NS + { + .procname = "apparmor_restrict_unprivileged_userns", + .data = &aa_unprivileged_userns_restricted, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = apparmor_dointvec, + }, +#endif /* CONFIG_USER_NS */ { .procname = "apparmor_restrict_unprivileged_unconfined", .data = &aa_unprivileged_unconfined_restricted, diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 398937fe39e6..aa775746af71 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -88,6 +88,7 @@ #include "include/resource.h" int unprivileged_userns_apparmor_policy = 1; +int aa_unprivileged_userns_restricted = IS_ENABLED(CONFIG_SECURITY_APPARMOR_RESTRICT_USERNS); int aa_unprivileged_unconfined_restricted; const char *const aa_profile_mode_names[] = { diff --git a/security/apparmor/task.c b/security/apparmor/task.c index c87fb9f4ac18..fa10da236e8d 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -318,12 +318,18 @@ int aa_profile_ns_perm(struct aa_profile *profile, u32 request) { struct aa_perms perms = { }; - int error = 0; ad->subj_label = &profile->label; ad->request = request; - if (!profile_unconfined(profile)) { + if (profile_unconfined(profile)) { + if (!aa_unprivileged_userns_restricted || + ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN)) + return 0; + + ad->info = "User namespace creation restricted"; + /* don't just return: allow complain mode to override */ + } else { struct aa_ruleset *rules = list_first_entry(&profile->rules, typeof(*rules), list); @@ -334,10 +340,8 @@ int aa_profile_ns_perm(struct aa_profile *profile, /* 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; + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, ad, audit_ns_cb); }