From bd1e2a79d2da09cf6bb697651d0812ee2ba8c534 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 27 Oct 2023 10:31:06 -0700 Subject: [PATCH] UBUNTU: SAUCE: apparmor4.0.0 [80/90]: apparmor: carry mediation check on label BugLink: http://bugs.launchpad.net/bugs/2028253 In order to speed up the mediated check, precompute and store the result as a bit per class type. This not only will allow us to speed up the mediation check but is also a step to removing the unconfined special cases as the unconfined check can be replaced with the generic label_mediates() check. need to compute initial profile mediated bit vector Note: label check does not work for capabilities and resources Signed-off-by: John Johansen (cherry picked from commit d8719977bc2bea2969846a49acfbabf06572ee1f https://git.launchpad.net/~apparmor-dev/ubuntu-kernel-next) Signed-off-by: Paolo Pisati --- security/apparmor/af_unix.c | 2 +- security/apparmor/include/apparmor.h | 1 + security/apparmor/include/label.h | 27 +++++++++++---------------- security/apparmor/include/policy.h | 13 +++++++++++++ security/apparmor/label.c | 24 ++++++++++++++---------- security/apparmor/lsm.c | 2 +- security/apparmor/policy.c | 28 +++++++++++++++++++++++++++- security/apparmor/policy_unpack.c | 2 ++ 8 files changed, 70 insertions(+), 29 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 4ce38a1dfec0..d74d074d86de 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -47,7 +47,7 @@ static inline int unix_fs_perm(const char *op, u32 mask, AA_BUG(!u); AA_BUG(!UNIX_FS(aa_unix_sk(u))); - if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) + if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE)) return 0; mask &= NET_FS_PERMS; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 59d2010bb1a2..6b85f51b6e20 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -38,6 +38,7 @@ #define AA_CLASS_X 31 #define AA_CLASS_DBUS 32 +/* NOTE: if AA_CLASS_LAST > 63 need to update label->mediates */ #define AA_CLASS_LAST AA_CLASS_DBUS /* Control parameters settable through module/boot flags */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index d71ca085125d..e475ee5291e9 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -129,6 +129,7 @@ struct aa_label { long flags; u32 secid; int size; + u64 mediates; struct aa_profile *vec[]; }; @@ -255,23 +256,17 @@ for ((I).i = (I).j = 0; \ #define fn_for_each_not_in_set(L1, L2, P, FN) \ fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set) -#define LABEL_MEDIATES(L, C) \ -({ \ - struct aa_profile *profile; \ - struct label_it i; \ - int ret = 0; \ - label_for_each(i, (L), profile) { \ - struct aa_ruleset *rules = \ - list_first_entry(&profile->rules, typeof(*rules),\ - list); \ - if (RULE_MEDIATES(rules, (C))) { \ - ret = 1; \ - break; \ - } \ - } \ - ret; \ -}) +static inline bool label_mediates(struct aa_label *L, unsigned char C) +{ + return (L)->mediates & (((u64) 1) << (C)); +} +static inline bool label_mediates_safe(struct aa_label *L, unsigned char C) +{ + if (C > AA_CLASS_LAST) + return false; + return label_mediates(L, C); +} int aa_label_cmp(struct aa_label *a, struct aa_label *b); void aa_labelset_destroy(struct aa_labelset *ls); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index f3cae95ee8b4..f5bea4d31441 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -334,6 +334,19 @@ static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head, return RULE_MEDIATES(rule, class); } +void aa_compute_profile_mediates(struct aa_profile *profile); +static inline bool profile_mediates(struct aa_profile *profile, + unsigned char class) +{ + return label_mediates(&profile->label, class); +} + +static inline bool profile_mediates_safe(struct aa_profile *profile, + unsigned char class) +{ + return label_mediates_safe(&profile->label, class); +} + /** * aa_get_profile - increment refcount on profile @p * @p: profile (MAYBE NULL) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 99c09d2b8ca1..ca30e6d0b1f0 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -198,21 +198,25 @@ static bool vec_is_stale(struct aa_profile **vec, int n) return false; } -static long accum_vec_flags(struct aa_profile **vec, int n) +static void accum_label_info(struct aa_label *new) { long u = FLAG_UNCONFINED; int i; - AA_BUG(!vec); + AA_BUG(!new->vec); - for (i = 0; i < n; i++) { - u |= vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 | - FLAG_STALE); - if (!(u & vec[i]->label.flags & FLAG_UNCONFINED)) + /* size == 1 is a profile and flags must be set as part of creation */ + if (new->size == 1) + return; + + for (i = 0; i < new->size; i++) { + u |= new->vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 | + FLAG_STALE); + if (!(u & new->vec[i]->label.flags & FLAG_UNCONFINED)) u &= ~FLAG_UNCONFINED; + new->mediates |= new->vec[i]->label.mediates; } - - return u; + new->flags |= u; } static int sort_cmp(const void *a, const void *b) @@ -645,7 +649,7 @@ static bool __label_replace(struct aa_label *old, struct aa_label *new) rb_replace_node(&old->node, &new->node, &ls->root); old->flags &= ~FLAG_IN_TREE; new->flags |= FLAG_IN_TREE; - new->flags |= accum_vec_flags(new->vec, new->size); + accum_label_info(new); return true; } @@ -706,7 +710,7 @@ static struct aa_label *__label_insert(struct aa_labelset *ls, rb_link_node(&label->node, parent, new); rb_insert_color(&label->node, &ls->root); label->flags |= FLAG_IN_TREE; - label->flags |= accum_vec_flags(label->vec, label->size); + accum_label_info(label); return aa_get_label(label); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 218a53a4d609..1761a44b2069 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1251,7 +1251,7 @@ static int apparmor_userns_create(const struct cred *cred) label = begin_current_label_crit_section(); /* remove unprivileged_userns_restricted check when unconfined is updated */ if (aa_unprivileged_userns_restricted || - LABEL_MEDIATES(label, AA_CLASS_NS)) { + label_mediates(label, AA_CLASS_NS)) { DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_NS, OP_USERNS_CREATE); ad.subj_cred = current_cred(); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f1073561b55a..8147c7c36edf 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -382,6 +382,30 @@ fail: return NULL; } +/* set of rules that are mediated by unconfined */ +static int unconfined_mediates[] = { AA_CLASS_NS, AA_CLASS_IO_URING, 0 }; + +/* must be called after profile rulesets and start information is setup */ +void aa_compute_profile_mediates(struct aa_profile *profile) +{ + int c; + + if (profile_unconfined(profile)) { + int *pos; + + for (pos = unconfined_mediates; *pos; pos++) { + if (ANY_RULE_MEDIATES(&profile->rules, AA_CLASS_NS) != + DFA_NOMATCH) + profile->label.mediates |= ((u64) 1) << AA_CLASS_NS; + } + return; + } + for (c = 0; c <= AA_CLASS_LAST; c++) { + if (ANY_RULE_MEDIATES(&profile->rules, c) != DFA_NOMATCH) + profile->label.mediates |= ((u64) 1) << c; + } +} + /* TODO: profile accounting - setup in remove */ /** @@ -637,10 +661,12 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, rules = list_first_entry(&profile->rules, typeof(*rules), list); rules->file = aa_get_pdb(nullpdb); rules->policy = aa_get_pdb(nullpdb); + aa_compute_profile_mediates(profile); if (parent) { profile->path_flags = parent->path_flags; - + /* override/inherit what is mediated from parent */ + profile->label.mediates = parent->label.mediates; /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); profile->ns = aa_get_ns(parent->ns); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a4af3fc56a65..ac71a54017aa 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1144,6 +1144,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + aa_compute_profile_mediates(profile); + return profile; fail: