diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7170349c8af0..a608a6bd76c5 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -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), { } }; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3b24f4a8c727..45beb1c5f747 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -59,7 +59,7 @@ static const char *const aa_class_names[] = { "module", "lsm", "namespace", - "unknown", + "io_uring", "unknown", "unknown", "unknown", diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index e2b759f24064..f83934913b0f 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -30,10 +30,10 @@ #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 diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 095707e05b70..acbb03b9bd25 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -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; diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 83534df8939f..0f7e913c3fc2 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -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 | \ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c61835bd7db9..c80c1bd3024a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -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 }; /*