]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
apparmor: add ability to mediate caps with policy state machine
authorJohn Johansen <john.johansen@canonical.com>
Thu, 4 Jan 2024 17:00:49 +0000 (09:00 -0800)
committerJohn Johansen <john.johansen@canonical.com>
Sat, 18 Jan 2025 14:47:12 +0000 (06:47 -0800)
Currently the caps encoding is very limited and can't be used with
conditionals. 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.

This patch only adds support for using the state machine and retains
the old encoding lookup as part of the runtime mediation code to
support older policy abis. A follow on patch will move backwards
compatibility to a mapping function done at policy load time.

Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/capability.c
security/apparmor/include/capability.h
security/apparmor/lsm.c

index 7ca489ee1054f8bb2c998915431e2c2fb9cf26f3..25b6219cdeb6b06e953165105405621642cf2ade 100644 (file)
@@ -27,6 +27,7 @@
 
 struct aa_sfs_entry aa_sfs_entry_caps[] = {
        AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK),
+       AA_SFS_FILE_BOOLEAN("extended", 1),
        { }
 };
 
@@ -123,8 +124,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;
@@ -168,3 +192,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;
+}
index d6dcc604ec0cc2f044b5c89052cc76d6f57fbe7b..1ddcec2d1160d77050f4581670df01d46885cf38 100644 (file)
@@ -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);
 
index 72c3d1536f690cf5de06df21002e7ad835d23365..479bfea064af018eec092fd05a6964e385f83055 100644 (file)
@@ -177,14 +177,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();