apparmor: add io_uring mediation
authorGeorgia Garcia <georgia.garcia@canonical.com>
Mon, 20 Mar 2023 17:43:41 +0000 (14:43 -0300)
committerJohn Johansen <john.johansen@canonical.com>
Wed, 18 Oct 2023 22:58:49 +0000 (15:58 -0700)
For now, the io_uring mediation is limited to sqpoll and
override_creds.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/apparmorfs.c
security/apparmor/audit.c
security/apparmor/include/apparmor.h
security/apparmor/include/audit.h
security/apparmor/include/perms.h
security/apparmor/lsm.c

index 7170349..a608a6b 100644 (file)
@@ -2390,6 +2390,12 @@ static struct aa_sfs_entry aa_sfs_entry_query[] = {
        AA_SFS_DIR("label",                     aa_sfs_entry_query_label),
        { }
 };
+
+static struct aa_sfs_entry aa_sfs_entry_io_uring[] = {
+       AA_SFS_FILE_STRING("mask", "sqpoll override_creds"),
+       { }
+};
+
 static struct aa_sfs_entry aa_sfs_entry_features[] = {
        AA_SFS_DIR("policy",                    aa_sfs_entry_policy),
        AA_SFS_DIR("domain",                    aa_sfs_entry_domain),
@@ -2403,6 +2409,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
        AA_SFS_DIR("ptrace",                    aa_sfs_entry_ptrace),
        AA_SFS_DIR("signal",                    aa_sfs_entry_signal),
        AA_SFS_DIR("query",                     aa_sfs_entry_query),
+       AA_SFS_DIR("io_uring",                  aa_sfs_entry_io_uring),
        { }
 };
 
index 3b24f4a..45beb1c 100644 (file)
@@ -59,7 +59,7 @@ static const char *const aa_class_names[] = {
        "module",
        "lsm",
        "namespace",
-       "unknown",
+       "io_uring",
        "unknown",
        "unknown",
        "unknown",
index e2b759f..f839349 100644 (file)
 #define AA_CLASS_NET           14
 #define AA_CLASS_LABEL         16
 #define AA_CLASS_POSIX_MQUEUE  17
-#define AA_CLASS_IO_URING      18
 #define AA_CLASS_MODULE                19
 #define AA_CLASS_DISPLAY_LSM   20
 #define AA_CLASS_NS            21
+#define AA_CLASS_IO_URING      22
 
 #define AA_CLASS_X             31
 #define AA_CLASS_DBUS          32
index 095707e..acbb03b 100644 (file)
@@ -105,6 +105,9 @@ enum audit_type {
 
 #define OP_USERNS_CREATE "userns_create"
 
+#define OP_URING_OVERRIDE "uring_override"
+#define OP_URING_SQPOLL "uring_sqpoll"
+
 struct apparmor_audit_data {
        int error;
        int type;
@@ -153,6 +156,9 @@ struct apparmor_audit_data {
                        const char *data;
                        unsigned long flags;
                } mnt;
+               struct {
+                       struct aa_label *target;
+               } uring;
        };
 
        struct common_audit_data common;
index 83534df..0f7e913 100644 (file)
@@ -48,6 +48,9 @@
 
 #define AA_LINK_SUBSET         AA_MAY_LOCK     /* overlaid */
 
+#define AA_MAY_CREATE_SQPOLL   AA_MAY_CREATE
+#define AA_MAY_OVERRIDE_CRED   AA_MAY_APPEND
+#define AA_URING_PERM_MASK     (AA_MAY_OVERRIDE_CRED | AA_MAY_CREATE_SQPOLL)
 
 #define PERMS_CHRS_MASK (MAY_READ | MAY_WRITE | AA_MAY_CREATE |                \
                         AA_MAY_DELETE | AA_MAY_LINK | AA_MAY_LOCK |    \
index c61835b..c80c1bd 100644 (file)
@@ -582,6 +582,114 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
                           false);
 }
 
+#ifdef CONFIG_IO_URING
+static const char *audit_uring_mask(u32 mask)
+{
+       if (mask & AA_MAY_CREATE_SQPOLL)
+               return "sqpoll";
+       if (mask & AA_MAY_OVERRIDE_CRED)
+               return "override_creds";
+       return "";
+}
+
+static void audit_uring_cb(struct audit_buffer *ab, void *va)
+{
+       struct apparmor_audit_data *ad = aad_of_va(va);
+
+       if (ad->request & AA_URING_PERM_MASK) {
+               audit_log_format(ab, " requested=\"%s\"",
+                                audit_uring_mask(ad->request));
+               if (ad->denied & AA_URING_PERM_MASK) {
+                       audit_log_format(ab, " denied=\"%s\"",
+                                        audit_uring_mask(ad->denied));
+               }
+       }
+       if (ad->uring.target) {
+               audit_log_format(ab, " tcontext=");
+               aa_label_xaudit(ab, labels_ns(ad->subj_label),
+                               ad->uring.target,
+                               FLAGS_NONE, GFP_ATOMIC);
+       }
+}
+
+static int profile_uring(struct aa_profile *profile, u32 request,
+                        struct aa_label *new, int cap,
+                        struct apparmor_audit_data *ad)
+{
+       unsigned int state;
+       struct aa_ruleset *rules;
+       int error = 0;
+
+       AA_BUG(!profile);
+
+       rules = list_first_entry(&profile->rules, typeof(*rules), list);
+       state = RULE_MEDIATES(rules, AA_CLASS_IO_URING);
+       if (state) {
+               struct aa_perms perms = { };
+
+               if (new) {
+                       aa_label_match(profile, rules, new, state,
+                                      false, request, &perms);
+               } else {
+                       perms = *aa_lookup_perms(rules->policy, state);
+               }
+               aa_apply_modes_to_perms(profile, &perms);
+               error = aa_check_perms(profile, &perms, request, ad,
+                                      audit_uring_cb);
+       }
+
+       return error;
+}
+
+/**
+ * apparmor_uring_override_creds - check the requested cred override
+ * @new: the target creds
+ *
+ * Check to see if the current task is allowed to override it's credentials
+ * to service an io_uring operation.
+ */
+int apparmor_uring_override_creds(const struct cred *new)
+{
+       struct aa_profile *profile;
+       struct aa_label *label;
+       int error;
+       DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING,
+                         OP_URING_OVERRIDE);
+
+       ad.uring.target = cred_label(new);
+       label = __begin_current_label_crit_section();
+       error = fn_for_each(label, profile,
+                       profile_uring(profile, AA_MAY_OVERRIDE_CRED,
+                                     cred_label(new), CAP_SYS_ADMIN, &ad));
+       __end_current_label_crit_section(label);
+
+       return error;
+}
+
+/**
+ * apparmor_uring_sqpoll - check if a io_uring polling thread can be created
+ *
+ * Check to see if the current task is allowed to create a new io_uring
+ * kernel polling thread.
+ */
+int apparmor_uring_sqpoll(void)
+{
+       struct aa_profile *profile;
+       struct aa_label *label;
+       int error;
+       DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING,
+                         OP_URING_SQPOLL);
+
+       label = __begin_current_label_crit_section();
+       error = fn_for_each(label, profile,
+                       profile_uring(profile, AA_MAY_CREATE_SQPOLL,
+                                     NULL, CAP_SYS_ADMIN, &ad));
+       __end_current_label_crit_section(label);
+
+       return error;
+}
+#endif /* CONFIG_IO_URING */
+
 static int apparmor_sb_mount(const char *dev_name, const struct path *path,
                             const char *type, unsigned long flags, void *data)
 {
@@ -1346,6 +1454,11 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
        LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx),
        LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid),
        LSM_HOOK_INIT(release_secctx, apparmor_release_secctx),
+
+#ifdef CONFIG_IO_URING
+       LSM_HOOK_INIT(uring_override_creds, apparmor_uring_override_creds),
+       LSM_HOOK_INIT(uring_sqpoll, apparmor_uring_sqpoll),
+#endif
 };
 
 /*