mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
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: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
2d9da9b188
commit
fa9b63adab
7 changed files with 75 additions and 2 deletions
|
@ -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[] = {
|
||||||
"io_uring",
|
"io_uring",
|
||||||
"module",
|
"module",
|
||||||
"lsm",
|
"lsm",
|
||||||
"unknown",
|
"namespace",
|
||||||
"unknown",
|
"unknown",
|
||||||
"unknown",
|
"unknown",
|
||||||
"unknown",
|
"unknown",
|
||||||
|
|
|
@ -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 {
|
||||||
#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,
|
||||||
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
|
||||||
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 = {
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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[] = {
|
||||||
.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[] = {
|
||||||
.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,
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue