From 06bb02860a07f6dbecd518fa2d4d0f0bf0505521 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 4 Jan 2024 09:00:49 -0800 Subject: [PATCH] UBUNTU: SAUCE: apparmor4.0.0 [86/90]: apparmor: add ability to mediate caps with policy state machine BugLink: http://bugs.launchpad.net/bugs/2028253 Currently the caps encoding is very limited. Allow capabilities to be mediated by the state machine. This will allow us to add conditionals to capabilities that aren't possible with the current encoding. Signed-off-by: John Johansen (cherry picked from commit bb78e87633afdf5e4b7dc0b2c857f61e97369068 https://git.launchpad.net/~apparmor-dev/ubuntu-kernel-next) Signed-off-by: Paolo Pisati --- security/apparmor/capability.c | 56 ++++++++++++++++++++++++++ security/apparmor/include/capability.h | 1 + security/apparmor/lsm.c | 11 +++-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 9934df16c843..7c0f66f1b297 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -26,6 +26,7 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = { AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK), + AA_SFS_FILE_BOOLEAN("extended", 1), { } }; @@ -118,8 +119,31 @@ static int profile_capable(struct aa_profile *profile, int cap, { struct aa_ruleset *rules = list_first_entry(&profile->rules, typeof(*rules), list); + aa_state_t state; int error; + state = RULE_MEDIATES(rules, ad->class); + if (state) { + struct aa_perms perms = { }; + u32 request; + + /* caps broken into 256 x 32 bit permission chunks */ + state = aa_dfa_next(rules->policy->dfa, state, cap >> 5); + request = 1 << (cap & 0x1f); + perms = *aa_lookup_perms(rules->policy, state); + aa_apply_modes_to_perms(profile, &perms); + + if (opts & CAP_OPT_NOAUDIT) { + if (perms.complain & request) + ad->info = "optional: no audit"; + else + ad = NULL; + } + return aa_check_perms(profile, &perms, request, ad, + audit_cb); + } + + /* fallback to old caps mediation that doesn't support conditionals */ if (cap_raised(rules->caps.allow, cap) && !cap_raised(rules->caps.denied, cap)) error = 0; @@ -163,3 +187,35 @@ int aa_capable(const struct cred *subj_cred, struct aa_label *label, return error; } + +kernel_cap_t aa_profile_capget(struct aa_profile *profile) +{ + struct aa_ruleset *rules = list_first_entry(&profile->rules, + typeof(*rules), list); + aa_state_t state; + + state = RULE_MEDIATES(rules, AA_CLASS_CAP); + if (state) { + kernel_cap_t caps = CAP_EMPTY_SET; + int i; + + /* caps broken into up to 256, 32 bit permission chunks */ + for (i = 0; i < (CAP_LAST_CAP >> 5); i++) { + struct aa_perms perms = { }; + aa_state_t tmp; + + tmp = aa_dfa_next(rules->policy->dfa, state, i); + perms = *aa_lookup_perms(rules->policy, tmp); + aa_apply_modes_to_perms(profile, &perms); + caps.val |= ((u64)(perms.allow)) << (i *5); + caps.val |= ((u64)(perms.complain)) << (i *5); + } + return caps; + } + + /* fallback to old caps */ + if (COMPLAIN_MODE(profile)) + return CAP_FULL_SET; + + return rules->caps.allow; +} diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index d6dcc604ec0c..1ddcec2d1160 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -36,6 +36,7 @@ struct aa_caps { extern struct aa_sfs_entry aa_sfs_entry_caps[]; +kernel_cap_t aa_profile_capget(struct aa_profile *profile); int aa_capable(const struct cred *subj_cred, struct aa_label *label, int cap, unsigned int opts); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 38ae09393f85..0fa290efd2b3 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -188,14 +188,13 @@ static int apparmor_capget(const struct task_struct *target, kernel_cap_t *effec label_for_each_confined(i, label, profile) { struct aa_ruleset *rules; - if (COMPLAIN_MODE(profile)) - continue; + kernel_cap_t allowed; + rules = list_first_entry(&profile->rules, typeof(*rules), list); - *effective = cap_intersect(*effective, - rules->caps.allow); - *permitted = cap_intersect(*permitted, - rules->caps.allow); + allowed = aa_profile_capget(profile); + *effective = cap_intersect(*effective, allowed); + *permitted = cap_intersect(*permitted, allowed); } } rcu_read_unlock();