Commit fa9b63ad authored by John Johansen's avatar John Johansen

apparmor: add user namespace creation mediation

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: default avatarGeorgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent 2d9da9b1
...@@ -2375,6 +2375,7 @@ static struct aa_sfs_entry aa_sfs_entry_mount[] = { ...@@ -2375,6 +2375,7 @@ static struct aa_sfs_entry aa_sfs_entry_mount[] = {
static struct aa_sfs_entry aa_sfs_entry_ns[] = { static struct aa_sfs_entry aa_sfs_entry_ns[] = {
AA_SFS_FILE_BOOLEAN("profile", 1), AA_SFS_FILE_BOOLEAN("profile", 1),
AA_SFS_FILE_BOOLEAN("pivot_root", 0), AA_SFS_FILE_BOOLEAN("pivot_root", 0),
AA_SFS_FILE_STRING("mask", "userns_create"),
{ } { }
}; };
......
...@@ -58,7 +58,7 @@ static const char *const aa_class_names[] = { ...@@ -58,7 +58,7 @@ static const char *const aa_class_names[] = {
"io_uring", "io_uring",
"module", "module",
"lsm", "lsm",
"unknown", "namespace",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#define AA_CLASS_IO_URING 18 #define AA_CLASS_IO_URING 18
#define AA_CLASS_MODULE 19 #define AA_CLASS_MODULE 19
#define AA_CLASS_DISPLAY_LSM 20 #define AA_CLASS_DISPLAY_LSM 20
#define AA_CLASS_NS 21
#define AA_CLASS_X 31 #define AA_CLASS_X 31
#define AA_CLASS_DBUS 32 #define AA_CLASS_DBUS 32
......
...@@ -103,6 +103,7 @@ enum audit_type { ...@@ -103,6 +103,7 @@ enum audit_type {
#define OP_PROF_LOAD "profile_load" #define OP_PROF_LOAD "profile_load"
#define OP_PROF_RM "profile_remove" #define OP_PROF_RM "profile_remove"
#define OP_USERNS_CREATE "userns_create"
struct apparmor_audit_data { struct apparmor_audit_data {
int error; int error;
......
...@@ -96,4 +96,10 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer, ...@@ -96,4 +96,10 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
u32 request); 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 */ #endif /* __AA_TASK_H */
...@@ -836,6 +836,27 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo ...@@ -836,6 +836,27 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo
return error; 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 * 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 = { ...@@ -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_getsecid_obj, apparmor_task_getsecid_obj),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
LSM_HOOK_INIT(task_kill, apparmor_task_kill), LSM_HOOK_INIT(task_kill, apparmor_task_kill),
LSM_HOOK_INIT(userns_create, apparmor_userns_create),
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
LSM_HOOK_INIT(audit_rule_init, aa_audit_rule_init), LSM_HOOK_INIT(audit_rule_init, aa_audit_rule_init),
...@@ -1784,6 +1806,7 @@ static int apparmor_dointvec(struct ctl_table *table, int write, ...@@ -1784,6 +1806,7 @@ static int apparmor_dointvec(struct ctl_table *table, int write,
} }
static struct ctl_table apparmor_sysctl_table[] = { static struct ctl_table apparmor_sysctl_table[] = {
#ifdef CONFIG_USER_NS
{ {
.procname = "unprivileged_userns_apparmor_policy", .procname = "unprivileged_userns_apparmor_policy",
.data = &unprivileged_userns_apparmor_policy, .data = &unprivileged_userns_apparmor_policy,
...@@ -1791,6 +1814,7 @@ static struct ctl_table apparmor_sysctl_table[] = { ...@@ -1791,6 +1814,7 @@ static struct ctl_table apparmor_sysctl_table[] = {
.mode = 0600, .mode = 0600,
.proc_handler = apparmor_dointvec, .proc_handler = apparmor_dointvec,
}, },
#endif /* CONFIG_USER_NS */
{ {
.procname = "apparmor_display_secid_mode", .procname = "apparmor_display_secid_mode",
.data = &apparmor_display_secid_mode, .data = &apparmor_display_secid_mode,
...@@ -1805,7 +1829,6 @@ static struct ctl_table apparmor_sysctl_table[] = { ...@@ -1805,7 +1829,6 @@ static struct ctl_table apparmor_sysctl_table[] = {
.mode = 0600, .mode = 0600,
.proc_handler = apparmor_dointvec, .proc_handler = apparmor_dointvec,
}, },
{ } { }
}; };
......
...@@ -298,3 +298,44 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer, ...@@ -298,3 +298,44 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
profile_tracee_perm(tracee_cred, profile, tracer, profile_tracee_perm(tracee_cred, profile, tracer,
xrequest, &sa)); 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment