diff options
Diffstat (limited to 'security')
54 files changed, 1027 insertions, 749 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 01b923d97a44..2c0185ebc900 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2366,6 +2366,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), AA_SFS_FILE_U64("permstable32_version", 1), AA_SFS_FILE_STRING("permstable32", PERMS32STR), + AA_SFS_FILE_U64("state32", 1), AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined), { } }; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 6b5181c668b5..73087d76f649 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -264,13 +264,13 @@ int aa_audit_rule_known(struct audit_krule *rule) return 0; } -int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) +int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule) { struct aa_audit_rule *rule = vrule; struct aa_label *label; int found = 0; - label = aa_secid_to_label(sid); + label = prop->apparmor.label; if (!label) return -ENOENT; diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 9934df16c843..7ca489ee1054 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -12,6 +12,7 @@ #include <linux/errno.h> #include <linux/gfp.h> #include <linux/security.h> +#include <linux/timekeeping.h> #include "include/apparmor.h" #include "include/capability.h" @@ -30,8 +31,9 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = { }; struct audit_cache { - struct aa_profile *profile; - kernel_cap_t caps; + const struct cred *ad_subj_cred; + /* Capabilities go from 0 to CAP_LAST_CAP */ + u64 ktime_ns_expiration[CAP_LAST_CAP+1]; }; static DEFINE_PER_CPU(struct audit_cache, audit_cache); @@ -64,6 +66,8 @@ static void audit_cb(struct audit_buffer *ab, void *va) static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile, int cap, int error) { + const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */ + struct aa_ruleset *rules = list_first_entry(&profile->rules, typeof(*rules), list); struct audit_cache *ent; @@ -89,15 +93,16 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile /* Do simple duplicate message elimination */ ent = &get_cpu_var(audit_cache); - if (profile == ent->profile && cap_raised(ent->caps, cap)) { + /* If the capability was never raised the timestamp check would also catch that */ + if (ad->subj_cred == ent->ad_subj_cred && ktime_get_ns() <= ent->ktime_ns_expiration[cap]) { put_cpu_var(audit_cache); if (COMPLAIN_MODE(profile)) return complain_error(error); return error; } else { - aa_put_profile(ent->profile); - ent->profile = aa_get_profile(profile); - cap_raise(ent->caps, cap); + put_cred(ent->ad_subj_cred); + ent->ad_subj_cred = get_cred(ad->subj_cred); + ent->ktime_ns_expiration[cap] = ktime_get_ns() + AUDIT_CACHE_TIMEOUT_NS; } put_cpu_var(audit_cache); @@ -109,7 +114,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile * @profile: profile being enforced (NOT NULL, NOT unconfined) * @cap: capability to test if allowed * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated - * @ad: audit data (MAY BE NULL indicating no auditing) + * @ad: audit data (NOT NULL) * * Returns: 0 if allowed else -EPERM */ diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 571158ec6188..5939bd9a9b9b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -9,7 +9,6 @@ */ #include <linux/errno.h> -#include <linux/fdtable.h> #include <linux/fs.h> #include <linux/file.h> #include <linux/mount.h> @@ -637,6 +636,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, struct aa_ruleset *rules = list_first_entry(&profile->rules, typeof(*rules), list); struct aa_label *new = NULL; + struct aa_profile *new_profile = NULL; const char *info = NULL, *name = NULL, *target = NULL; aa_state_t state = rules->file->start[AA_CLASS_FILE]; struct aa_perms perms = {}; @@ -681,15 +681,18 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, /* hack ix fallback - improve how this is detected */ goto audit; } else if (!new) { - error = -EACCES; info = "profile transition not found"; - /* remove MAY_EXEC to audit as failure */ + /* remove MAY_EXEC to audit as failure or complaint */ perms.allow &= ~MAY_EXEC; + if (COMPLAIN_MODE(profile)) { + /* create null profile instead of failing */ + goto create_learning_profile; + } + error = -EACCES; } } else if (COMPLAIN_MODE(profile)) { +create_learning_profile: /* no exec permission - learning mode */ - struct aa_profile *new_profile = NULL; - new_profile = aa_new_learning_profile(profile, false, name, GFP_KERNEL); if (!new_profile) { @@ -710,8 +713,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, if (!(perms.xindex & AA_X_UNSAFE)) { if (DEBUG_ON) { - dbg_printk("apparmor: scrubbing environment variables" - " for %s profile=", name); + dbg_printk("apparmor: setting AT_SECURE for %s profile=", + name); aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } @@ -790,8 +793,8 @@ static int profile_onexec(const struct cred *subj_cred, if (!(perms.xindex & AA_X_UNSAFE)) { if (DEBUG_ON) { - dbg_printk("apparmor: scrubbing environment " - "variables for %s label=", xname); + dbg_printk("apparmor: setting AT_SECURE for %s label=", + xname); aa_label_printk(onexec, GFP_KERNEL); dbg_printk("\n"); } @@ -822,33 +825,19 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, AA_BUG(!bprm); AA_BUG(!buffer); - if (!stack) { - error = fn_for_each_in_ns(label, profile, - profile_onexec(subj_cred, profile, onexec, stack, - bprm, buffer, cond, unsafe)); - if (error) - return ERR_PTR(error); - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, - aa_get_newest_label(onexec), - profile_transition(subj_cred, profile, bprm, - buffer, - cond, unsafe)); - - } else { - /* TODO: determine how much we want to loosen this */ - error = fn_for_each_in_ns(label, profile, - profile_onexec(subj_cred, profile, onexec, stack, bprm, - buffer, cond, unsafe)); - if (error) - return ERR_PTR(error); - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, - aa_label_merge(&profile->label, onexec, - GFP_KERNEL), - profile_transition(subj_cred, profile, bprm, - buffer, - cond, unsafe)); - } + /* TODO: determine how much we want to loosen this */ + error = fn_for_each_in_ns(label, profile, + profile_onexec(subj_cred, profile, onexec, stack, + bprm, buffer, cond, unsafe)); + if (error) + return ERR_PTR(error); + new = fn_label_build_in_ns(label, profile, GFP_KERNEL, + stack ? aa_label_merge(&profile->label, onexec, + GFP_KERNEL) + : aa_get_newest_label(onexec), + profile_transition(subj_cred, profile, bprm, + buffer, cond, unsafe)); if (new) return new; @@ -961,8 +950,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) if (unsafe) { if (DEBUG_ON) { - dbg_printk("scrubbing environment variables for %s " - "label=", bprm->filename); + dbg_printk("setting AT_SECURE for %s label=", + bprm->filename); aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } @@ -972,8 +961,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) if (label->proxy != new->proxy) { /* when transitioning clear unsafe personality bits */ if (DEBUG_ON) { - dbg_printk("apparmor: clearing unsafe personality " - "bits. %s label=", bprm->filename); + dbg_printk("apparmor: clearing unsafe personality bits. %s label=", + bprm->filename); aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 0c8cc86b417b..e27229349abb 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -202,6 +202,6 @@ static inline int complain_error(int error) void aa_audit_rule_free(void *vrule); int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule, gfp_t gfp); int aa_audit_rule_known(struct audit_krule *rule); -int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule); +int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule); #endif /* __AA_AUDIT_H */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 2a72e6b17d68..93290ae300bb 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -160,31 +160,7 @@ int aa_label_next_confined(struct aa_label *l, int i); #define label_for_each_cont(I, L, P) \ for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i)) -#define next_comb(I, L1, L2) \ -do { \ - (I).j++; \ - if ((I).j >= (L2)->size) { \ - (I).i++; \ - (I).j = 0; \ - } \ -} while (0) - -/* for each combination of P1 in L1, and P2 in L2 */ -#define label_for_each_comb(I, L1, L2, P1, P2) \ -for ((I).i = (I).j = 0; \ - ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \ - (I) = next_comb(I, L1, L2)) - -#define fn_for_each_comb(L1, L2, P1, P2, FN) \ -({ \ - struct label_it i; \ - int __E = 0; \ - label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \ - last_error(__E, (FN)); \ - } \ - __E; \ -}) /* for each profile that is enforcing confinement in a label */ #define label_for_each_confined(I, L, P) \ @@ -291,8 +267,6 @@ bool aa_label_replace(struct aa_label *old, struct aa_label *new); bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old, struct aa_label *new); -struct aa_label *aa_label_find(struct aa_label *l); - struct aa_profile *aa_label_next_in_merge(struct label_it *I, struct aa_label *a, struct aa_label *b); @@ -320,8 +294,6 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, struct aa_label *label, int flags, gfp_t gfp); void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, gfp_t gfp); -void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp); -void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp); void aa_label_printk(struct aa_label *label, gfp_t gfp); struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index d7a894b1031f..f11a0db7f51d 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -59,7 +59,6 @@ extern int apparmor_initialized; /* fn's in lib */ const char *skipn_spaces(const char *str, size_t n); -char *aa_split_fqname(char *args, char **ns_name); const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, size_t *ns_len); void aa_info_message(const char *str); diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 4bb0405c9190..536ce3abd598 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -87,10 +87,12 @@ struct table_header { char td_data[]; }; -#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) +#define TABLE_DATAU16(TABLE) ((u16 *)((TABLE)->td_data)) +#define TABLE_DATAU32(TABLE) ((u32 *)((TABLE)->td_data)) +#define DEFAULT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) #define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) -#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) -#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) +#define NEXT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) +#define CHECK_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) #define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) #define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) #define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 0f7e913c3fc2..bbaa7d39a39a 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -213,9 +213,6 @@ void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend); void aa_profile_match_label(struct aa_profile *profile, struct aa_ruleset *rules, struct aa_label *label, int type, u32 request, struct aa_perms *perms); -int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, - u32 request, int type, u32 *deny, - struct apparmor_audit_data *ad); int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms, u32 request, struct apparmor_audit_data *ad, void (*cb)(struct audit_buffer *, void *)); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 75088cc310b6..757e3c232c57 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -264,7 +264,6 @@ void aa_free_profile(struct aa_profile *profile); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, size_t n); -struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, const char *fqname, size_t n); diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h index a912a5d5d04f..f6a515640950 100644 --- a/security/apparmor/include/secid.h +++ b/security/apparmor/include/secid.h @@ -26,12 +26,13 @@ extern int apparmor_display_secid_mode; struct aa_label *aa_secid_to_label(u32 secid); int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); +int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata, + u32 *seclen); int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); void apparmor_release_secctx(char *secdata, u32 seclen); int aa_alloc_secid(struct aa_label *label, gfp_t gfp); void aa_free_secid(u32 secid); -void aa_secid_update(u32 secid, struct aa_label *label); #endif /* __AA_SECID_H */ diff --git a/security/apparmor/label.c b/security/apparmor/label.c index c71e4615dd46..91483ecacc16 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -899,23 +899,6 @@ struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len, return vec_create_and_insert_label(vec, len, gfp); } -/** - * aa_label_find - find label @label in label set - * @label: label to find (NOT NULL) - * - * Requires: caller to hold a valid ref on l - * - * Returns: refcounted @label if @label is in tree - * refcounted label that is equiv to @label in tree - * else NULL if @label or equiv is not in tree - */ -struct aa_label *aa_label_find(struct aa_label *label) -{ - AA_BUG(!label); - - return vec_find(label->vec, label->size); -} - /** * aa_label_insert - insert label @label into @ls or return existing label @@ -1811,22 +1794,6 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, pr_info("%s", label->hname); } -void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp) -{ - struct aa_ns *ns = aa_get_current_ns(); - - aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp); - aa_put_ns(ns); -} - -void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp) -{ - struct aa_ns *ns = aa_get_current_ns(); - - aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp); - aa_put_ns(ns); -} - void aa_label_printk(struct aa_label *label, gfp_t gfp) { struct aa_ns *ns = aa_get_current_ns(); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index cd569fbbfe36..7db62213e352 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -46,44 +46,6 @@ void aa_free_str_table(struct aa_str_table *t) } /** - * aa_split_fqname - split a fqname into a profile and namespace name - * @fqname: a full qualified name in namespace profile format (NOT NULL) - * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) - * - * Returns: profile name or NULL if one is not specified - * - * Split a namespace name from a profile name (see policy.c for naming - * description). If a portion of the name is missing it returns NULL for - * that portion. - * - * NOTE: may modify the @fqname string. The pointers returned point - * into the @fqname string. - */ -char *aa_split_fqname(char *fqname, char **ns_name) -{ - char *name = strim(fqname); - - *ns_name = NULL; - if (name[0] == ':') { - char *split = strchr(&name[1], ':'); - *ns_name = skip_spaces(&name[1]); - if (split) { - /* overwrite ':' with \0 */ - *split++ = 0; - if (strncmp(split, "//", 2) == 0) - split += 2; - name = skip_spaces(split); - } else - /* a ns name without a following profile is allowed */ - name = NULL; - } - if (name && *name == 0) - name = NULL; - - return name; -} - -/** * skipn_spaces - Removes leading whitespace from @str. * @str: The string to be stripped. * @n: length of str to parse, will stop at \0 if encountered before n @@ -276,33 +238,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, } /** - * aa_audit_perms_cb - generic callback fn for auditing perms - * @ab: audit buffer (NOT NULL) - * @va: audit struct to audit values of (NOT NULL) - */ -static void aa_audit_perms_cb(struct audit_buffer *ab, void *va) -{ - struct common_audit_data *sa = va; - struct apparmor_audit_data *ad = aad(sa); - - if (ad->request) { - audit_log_format(ab, " requested_mask="); - aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs, - PERMS_CHRS_MASK, aa_file_perm_names, - PERMS_NAMES_MASK); - } - if (ad->denied) { - audit_log_format(ab, "denied_mask="); - aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs, - PERMS_CHRS_MASK, aa_file_perm_names, - PERMS_NAMES_MASK); - } - audit_log_format(ab, " peer="); - aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, - FLAGS_NONE, GFP_ATOMIC); -} - -/** * aa_apply_modes_to_perms - apply namespace and profile flags to perms * @profile: that perms where computed from * @perms: perms to apply mode modifiers to @@ -349,25 +284,6 @@ void aa_profile_match_label(struct aa_profile *profile, } -/* currently unused */ -int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, - u32 request, int type, u32 *deny, - struct apparmor_audit_data *ad) -{ - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); - struct aa_perms perms; - - ad->peer = &target->label; - ad->request = request; - - aa_profile_match_label(profile, rules, &target->label, type, request, - &perms); - aa_apply_modes_to_perms(profile, &perms); - *deny |= request & perms.deny; - return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb); -} - /** * aa_check_perms - do audit mode selection based on perms set * @profile: profile being checked diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f5d05297d59e..1edc12862a7d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -982,17 +982,20 @@ static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm) return; } -static void apparmor_current_getsecid_subj(u32 *secid) +static void apparmor_current_getlsmprop_subj(struct lsm_prop *prop) { struct aa_label *label = __begin_current_label_crit_section(); - *secid = label->secid; + + prop->apparmor.label = label; __end_current_label_crit_section(label); } -static void apparmor_task_getsecid_obj(struct task_struct *p, u32 *secid) +static void apparmor_task_getlsmprop_obj(struct task_struct *p, + struct lsm_prop *prop) { struct aa_label *label = aa_get_task_label(p); - *secid = label->secid; + + prop->apparmor.label = label; aa_put_label(label); } @@ -1503,8 +1506,9 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = { LSM_HOOK_INIT(task_free, apparmor_task_free), LSM_HOOK_INIT(task_alloc, apparmor_task_alloc), - LSM_HOOK_INIT(current_getsecid_subj, apparmor_current_getsecid_subj), - LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid_obj), + LSM_HOOK_INIT(current_getlsmprop_subj, + apparmor_current_getlsmprop_subj), + LSM_HOOK_INIT(task_getlsmprop_obj, apparmor_task_getlsmprop_obj), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_kill, apparmor_task_kill), LSM_HOOK_INIT(userns_create, apparmor_userns_create), @@ -1517,6 +1521,7 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = { #endif LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx), + LSM_HOOK_INIT(lsmprop_to_secctx, apparmor_lsmprop_to_secctx), LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid), LSM_HOOK_INIT(release_secctx, apparmor_release_secctx), diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 517d77d3c34c..f2d9c57f8794 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -247,6 +247,42 @@ void aa_dfa_free_kref(struct kref *kref) dfa_free(dfa); } + + +/** + * remap_data16_to_data32 - remap u16 @old table to a u32 based table + * @old: table to remap + * + * Returns: new table with u32 entries instead of u16. + * + * Note: will free @old so caller does not have to + */ +static struct table_header *remap_data16_to_data32(struct table_header *old) +{ + struct table_header *new; + size_t tsize; + u32 i; + + tsize = table_size(old->td_lolen, YYTD_DATA32); + new = kvzalloc(tsize, GFP_KERNEL); + if (!new) { + kvfree(old); + return NULL; + } + new->td_id = old->td_id; + new->td_flags = YYTD_DATA32; + new->td_lolen = old->td_lolen; + + for (i = 0; i < old->td_lolen; i++) + TABLE_DATAU32(new)[i] = (u32) TABLE_DATAU16(old)[i]; + + kvfree(old); + if (is_vmalloc_addr(new)) + vm_unmap_aliases(); + + return new; +} + /** * aa_dfa_unpack - unpack the binary tables of a serialized dfa * @blob: aligned serialized stream of data to unpack (NOT NULL) @@ -326,8 +362,10 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) case YYTD_ID_DEF: case YYTD_ID_NXT: case YYTD_ID_CHK: - if (table->td_flags != YYTD_DATA16) + if (!(table->td_flags == YYTD_DATA16 || + table->td_flags == YYTD_DATA32)) { goto fail; + } break; case YYTD_ID_EC: if (table->td_flags != YYTD_DATA8) @@ -342,6 +380,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) dfa->tables[table->td_id] = table; data += table_size(table->td_lolen, table->td_flags); size -= table_size(table->td_lolen, table->td_flags); + + /* + * this remapping has to be done after incrementing data above + * for now straight remap, later have dfa support both + */ + switch (table->td_id) { + case YYTD_ID_DEF: + case YYTD_ID_NXT: + case YYTD_ID_CHK: + if (table->td_flags == YYTD_DATA16) { + table = remap_data16_to_data32(table); + if (!table) + goto fail; + } + dfa->tables[table->td_id] = table; + break; + } table = NULL; } error = verify_table_headers(dfa->tables, flags); @@ -395,10 +450,10 @@ do { \ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start, const char *str, int len) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); aa_state_t state = start; if (state == DFA_NOMATCH) @@ -434,10 +489,10 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start, */ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); aa_state_t state = start; if (state == DFA_NOMATCH) @@ -472,10 +527,10 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str) */ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); /* current state is <state>, matching character *str */ if (dfa->tables[YYTD_ID_EC]) { @@ -490,10 +545,10 @@ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c) aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); u32 b = (base)[(state)]; if (!(b & MATCH_FLAG_OOB_TRANSITION)) @@ -521,10 +576,10 @@ aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state) aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start, const char *str, const char **retpos) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa); aa_state_t state = start, pos; @@ -582,10 +637,10 @@ aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start, aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start, const char *str, int n, const char **retpos) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa); aa_state_t state = start, pos; @@ -658,10 +713,10 @@ static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start, const char *str, struct match_workbuf *wb, unsigned int *count) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); aa_state_t state = start, pos; AA_BUG(!dfa); diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 45ec994b558d..d6c74c357ffd 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -130,7 +130,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, /* handle error conditions - and still allow a partial path to * be returned. */ - if (!res || IS_ERR(res)) { + if (IS_ERR_OR_NULL(res)) { if (PTR_ERR(res) == -ENAMETOOLONG) { error = -ENAMETOOLONG; *name = buf; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 14df15e35695..d0244fab0653 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -103,8 +103,7 @@ static void aa_free_pdb(struct aa_policydb *pdb) { if (pdb) { aa_put_dfa(pdb->dfa); - if (pdb->perms) - kvfree(pdb->perms); + kvfree(pdb->perms); aa_free_str_table(&pdb->trans); kfree(pdb); } @@ -580,11 +579,6 @@ struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, return profile; } -struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) -{ - return aa_lookupn_profile(ns, hname, strlen(hname)); -} - struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, const char *fqname, size_t n) { @@ -626,6 +620,7 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, /* TODO: ideally we should inherit abi from parent */ profile->label.flags |= FLAG_NULL; + profile->attach.xmatch = aa_get_pdb(nullpdb); rules = list_first_entry(&profile->rules, typeof(*rules), list); rules->file = aa_get_pdb(nullpdb); rules->policy = aa_get_pdb(nullpdb); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 3483c595f999..992b74c50d64 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -645,10 +645,13 @@ fail: static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) { + u32 reserved; + if (version != 1) return false; - return aa_unpack_u32(e, &perm->allow, NULL) && + /* reserved entry is for later expansion, discard for now */ + return aa_unpack_u32(e, &reserved, NULL) && aa_unpack_u32(e, &perm->allow, NULL) && aa_unpack_u32(e, &perm->deny, NULL) && aa_unpack_u32(e, &perm->subtree, NULL) && diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index c64733d6c98f..5b2ba88ae9e2 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -44,7 +44,7 @@ #define TEST_ARRAY_BUF_OFFSET \ (TEST_NAMED_ARRAY_BUF_OFFSET + 3 + strlen(TEST_ARRAY_NAME) + 1) -MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); struct policy_unpack_fixture { struct aa_ext *e; @@ -281,6 +281,8 @@ static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test) ((uintptr_t)puf->e->start <= (uintptr_t)string) && ((uintptr_t)string <= (uintptr_t)puf->e->end)); KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); + + kfree(string); } static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test) @@ -296,6 +298,8 @@ static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test) ((uintptr_t)puf->e->start <= (uintptr_t)string) && ((uintptr_t)string <= (uintptr_t)puf->e->end)); KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); + + kfree(string); } static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test) @@ -313,6 +317,8 @@ static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test) KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_NULL(test, string); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); + + kfree(string); } static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test) diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c index 83d3d1e6d9dc..47dc08fc583e 100644 --- a/security/apparmor/secid.c +++ b/security/apparmor/secid.c @@ -39,20 +39,6 @@ int apparmor_display_secid_mode; * TODO: use secid_update in label replace */ -/** - * aa_secid_update - update a secid mapping to a new label - * @secid: secid to update - * @label: label the secid will now map to - */ -void aa_secid_update(u32 secid, struct aa_label *label) -{ - unsigned long flags; - - xa_lock_irqsave(&aa_secids, flags); - __xa_store(&aa_secids, secid, label, 0); - xa_unlock_irqrestore(&aa_secids, flags); -} - /* * see label for inverse aa_label_to_secid */ @@ -61,10 +47,10 @@ struct aa_label *aa_secid_to_label(u32 secid) return xa_load(&aa_secids, secid); } -int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +static int apparmor_label_to_secctx(struct aa_label *label, char **secdata, + u32 *seclen) { /* TODO: cache secctx and ref count so we don't have to recreate */ - struct aa_label *label = aa_secid_to_label(secid); int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT; int len; @@ -90,6 +76,23 @@ int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) return 0; } +int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ + struct aa_label *label = aa_secid_to_label(secid); + + return apparmor_label_to_secctx(label, secdata, seclen); +} + +int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata, + u32 *seclen) +{ + struct aa_label *label; + + label = prop->apparmor.label; + + return apparmor_label_to_secctx(label, secdata, seclen); +} + int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) { struct aa_label *label; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 6924ed508ebd..377e57e9084f 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -1084,7 +1084,8 @@ static void evm_file_release(struct file *file) if (!S_ISREG(inode->i_mode) || !(mode & FMODE_WRITE)) return; - if (iint && atomic_read(&inode->i_writecount) == 1) + if (iint && iint->flags & EVM_NEW_FILE && + atomic_read(&inode->i_writecount) == 1) iint->flags &= ~EVM_NEW_FILE; } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 3c323ca213d4..c0d3b716d11f 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -369,7 +369,7 @@ static inline void ima_process_queued_keys(void) {} /* LIM API function definitions */ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode, - const struct cred *cred, u32 secid, int mask, + const struct cred *cred, struct lsm_prop *prop, int mask, enum ima_hooks func, int *pcr, struct ima_template_desc **template_desc, const char *func_data, unsigned int *allowed_algos); @@ -400,8 +400,8 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename); /* IMA policy related functions */ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode, - const struct cred *cred, u32 secid, enum ima_hooks func, - int mask, int flags, int *pcr, + const struct cred *cred, struct lsm_prop *prop, + enum ima_hooks func, int mask, int flags, int *pcr, struct ima_template_desc **template_desc, const char *func_data, unsigned int *allowed_algos); void ima_init_policy(void); @@ -555,7 +555,7 @@ static inline void ima_filter_rule_free(void *lsmrule) { } -static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op, +static inline int ima_filter_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *lsmrule) { return -EINVAL; diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 984e861f6e33..c35ea613c9f8 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -165,7 +165,7 @@ err_out: * @idmap: idmap of the mount the inode was found from * @inode: pointer to the inode associated with the object being validated * @cred: pointer to credentials structure to validate - * @secid: secid of the task being validated + * @prop: properties of the task being validated * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, * MAY_APPEND) * @func: caller identifier @@ -187,7 +187,7 @@ err_out: * */ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode, - const struct cred *cred, u32 secid, int mask, + const struct cred *cred, struct lsm_prop *prop, int mask, enum ima_hooks func, int *pcr, struct ima_template_desc **template_desc, const char *func_data, unsigned int *allowed_algos) @@ -196,7 +196,7 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode, flags &= ima_policy_flag; - return ima_match_policy(idmap, inode, cred, secid, func, mask, + return ima_match_policy(idmap, inode, cred, prop, func, mask, flags, pcr, template_desc, func_data, allowed_algos); } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 656c709b974f..884a3533f7af 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -73,13 +73,13 @@ bool is_ima_appraise_enabled(void) int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode, int mask, enum ima_hooks func) { - u32 secid; + struct lsm_prop prop; if (!ima_appraise) return 0; - security_current_getsecid_subj(&secid); - return ima_match_policy(idmap, inode, current_cred(), secid, + security_current_getlsmprop_subj(&prop); + return ima_match_policy(idmap, inode, current_cred(), &prop, func, mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL, NULL); } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 06132cf47016..9b87556b03a7 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -206,8 +206,8 @@ static void ima_file_free(struct file *file) } static int process_measurement(struct file *file, const struct cred *cred, - u32 secid, char *buf, loff_t size, int mask, - enum ima_hooks func) + struct lsm_prop *prop, char *buf, loff_t size, + int mask, enum ima_hooks func) { struct inode *real_inode, *inode = file_inode(file); struct ima_iint_cache *iint = NULL; @@ -232,7 +232,7 @@ static int process_measurement(struct file *file, const struct cred *cred, * bitmask based on the appraise/audit/measurement policy. * Included is the appraise submask. */ - action = ima_get_action(file_mnt_idmap(file), inode, cred, secid, + action = ima_get_action(file_mnt_idmap(file), inode, cred, prop, mask, func, &pcr, &template_desc, NULL, &allowed_algos); violation_check = ((func == FILE_CHECK || func == MMAP_CHECK || @@ -443,23 +443,23 @@ out: static int ima_file_mmap(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) { - u32 secid; + struct lsm_prop prop; int ret; if (!file) return 0; - security_current_getsecid_subj(&secid); + security_current_getlsmprop_subj(&prop); if (reqprot & PROT_EXEC) { - ret = process_measurement(file, current_cred(), secid, NULL, + ret = process_measurement(file, current_cred(), &prop, NULL, 0, MAY_EXEC, MMAP_CHECK_REQPROT); if (ret) return ret; } if (prot & PROT_EXEC) - return process_measurement(file, current_cred(), secid, NULL, + return process_measurement(file, current_cred(), &prop, NULL, 0, MAY_EXEC, MMAP_CHECK); return 0; @@ -488,9 +488,9 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, char *pathbuf = NULL; const char *pathname = NULL; struct inode *inode; + struct lsm_prop prop; int result = 0; int action; - u32 secid; int pcr; /* Is mprotect making an mmap'ed file executable? */ @@ -498,13 +498,13 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) return 0; - security_current_getsecid_subj(&secid); + security_current_getlsmprop_subj(&prop); inode = file_inode(vma->vm_file); action = ima_get_action(file_mnt_idmap(vma->vm_file), inode, - current_cred(), secid, MAY_EXEC, MMAP_CHECK, + current_cred(), &prop, MAY_EXEC, MMAP_CHECK, &pcr, &template, NULL, NULL); action |= ima_get_action(file_mnt_idmap(vma->vm_file), inode, - current_cred(), secid, MAY_EXEC, + current_cred(), &prop, MAY_EXEC, MMAP_CHECK_REQPROT, &pcr, &template, NULL, NULL); @@ -541,16 +541,16 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, static int ima_bprm_check(struct linux_binprm *bprm) { int ret; - u32 secid; + struct lsm_prop prop; - security_current_getsecid_subj(&secid); - ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, - MAY_EXEC, BPRM_CHECK); + security_current_getlsmprop_subj(&prop); + ret = process_measurement(bprm->file, current_cred(), + &prop, NULL, 0, MAY_EXEC, BPRM_CHECK); if (ret) return ret; - security_cred_getsecid(bprm->cred, &secid); - return process_measurement(bprm->file, bprm->cred, secid, NULL, 0, + security_cred_getlsmprop(bprm->cred, &prop); + return process_measurement(bprm->file, bprm->cred, &prop, NULL, 0, MAY_EXEC, CREDS_CHECK); } @@ -566,10 +566,10 @@ static int ima_bprm_check(struct linux_binprm *bprm) */ static int ima_file_check(struct file *file, int mask) { - u32 secid; + struct lsm_prop prop; - security_current_getsecid_subj(&secid); - return process_measurement(file, current_cred(), secid, NULL, 0, + security_current_getlsmprop_subj(&prop); + return process_measurement(file, current_cred(), &prop, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); } @@ -768,7 +768,7 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id, bool contents) { enum ima_hooks func; - u32 secid; + struct lsm_prop prop; /* * Do devices using pre-allocated memory run the risk of the @@ -788,9 +788,9 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id, /* Read entire file for all partial reads. */ func = read_idmap[read_id] ?: FILE_CHECK; - security_current_getsecid_subj(&secid); - return process_measurement(file, current_cred(), secid, NULL, - 0, MAY_READ, func); + security_current_getlsmprop_subj(&prop); + return process_measurement(file, current_cred(), &prop, NULL, 0, + MAY_READ, func); } const int read_idmap[READING_MAX_ID] = { @@ -818,7 +818,7 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size, enum kernel_read_file_id read_id) { enum ima_hooks func; - u32 secid; + struct lsm_prop prop; /* permit signed certs */ if (!file && read_id == READING_X509_CERTIFICATE) @@ -831,8 +831,8 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size, } func = read_idmap[read_id] ?: FILE_CHECK; - security_current_getsecid_subj(&secid); - return process_measurement(file, current_cred(), secid, buf, size, + security_current_getlsmprop_subj(&prop); + return process_measurement(file, current_cred(), &prop, buf, size, MAY_READ, func); } @@ -967,7 +967,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap, int digest_hash_len = hash_digest_size[ima_hash_algo]; int violation = 0; int action = 0; - u32 secid; + struct lsm_prop prop; if (digest && digest_len < digest_hash_len) return -EINVAL; @@ -990,9 +990,9 @@ int process_buffer_measurement(struct mnt_idmap *idmap, * buffer measurements. */ if (func) { - security_current_getsecid_subj(&secid); + security_current_getlsmprop_subj(&prop); action = ima_get_action(idmap, inode, current_cred(), - secid, 0, func, &pcr, &template, + &prop, 0, func, &pcr, &template, func_data, NULL); if (!(action & IMA_MEASURE) && !digest) return -ENOENT; @@ -1062,19 +1062,16 @@ out: */ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) { - struct fd f; - if (!buf || !size) return; - f = fdget(kernel_fd); - if (!fd_file(f)) + CLASS(fd, f)(kernel_fd); + if (fd_empty(f)) return; process_buffer_measurement(file_mnt_idmap(fd_file(f)), file_inode(fd_file(f)), buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0, NULL, false, NULL, 0); - fdput(f); } /** @@ -1114,7 +1111,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS /** - * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests + * ima_kernel_module_request - Prevent crypto-pkcs1(rsa,*) requests * @kmod_name: kernel module name * * Avoid a verification loop where verifying the signature of the modprobe @@ -1128,7 +1125,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); * algorithm on the fly, but crypto_larval_lookup() will try to use alg_name * in order to load a kernel module with same name. * - * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, + * Since we don't have any real "crypto-pkcs1(rsa,*)" kernel modules, * we are safe to fail such module request from crypto_larval_lookup(), and * avoid the verification loop. * @@ -1136,7 +1133,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); */ static int ima_kernel_module_request(char *kmod_name) { - if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) + if (strncmp(kmod_name, "crypto-pkcs1(rsa,", 17) == 0) return -EINVAL; return 0; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 09da8e639239..21a8e54c383f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -557,7 +557,7 @@ static bool ima_match_rule_data(struct ima_rule_entry *rule, * @idmap: idmap of the mount the inode was found from * @inode: a pointer to an inode * @cred: a pointer to a credentials structure for user validation - * @secid: the secid of the task to be validated + * @prop: LSM properties of the task to be validated * @func: LIM hook identifier * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) * @func_data: func specific data, may be NULL @@ -567,7 +567,7 @@ static bool ima_match_rule_data(struct ima_rule_entry *rule, static bool ima_match_rules(struct ima_rule_entry *rule, struct mnt_idmap *idmap, struct inode *inode, const struct cred *cred, - u32 secid, enum ima_hooks func, int mask, + struct lsm_prop *prop, enum ima_hooks func, int mask, const char *func_data) { int i; @@ -635,7 +635,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; - u32 osid; + struct lsm_prop inode_prop = { }; if (!lsm_rule->lsm[i].rule) { if (!lsm_rule->lsm[i].args_p) @@ -649,15 +649,16 @@ retry: case LSM_OBJ_USER: case LSM_OBJ_ROLE: case LSM_OBJ_TYPE: - security_inode_getsecid(inode, &osid); - rc = ima_filter_rule_match(osid, lsm_rule->lsm[i].type, + security_inode_getlsmprop(inode, &inode_prop); + rc = ima_filter_rule_match(&inode_prop, + lsm_rule->lsm[i].type, Audit_equal, lsm_rule->lsm[i].rule); break; case LSM_SUBJ_USER: case LSM_SUBJ_ROLE: case LSM_SUBJ_TYPE: - rc = ima_filter_rule_match(secid, lsm_rule->lsm[i].type, + rc = ima_filter_rule_match(prop, lsm_rule->lsm[i].type, Audit_equal, lsm_rule->lsm[i].rule); break; @@ -720,7 +721,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * @inode: pointer to an inode for which the policy decision is being made * @cred: pointer to a credentials structure for which the policy decision is * being made - * @secid: LSM secid of the task to be validated + * @prop: LSM properties of the task to be validated * @func: IMA hook identifier * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) * @flags: IMA actions to consider (e.g. IMA_MEASURE | IMA_APPRAISE) @@ -737,8 +738,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * than writes so ima_match_policy() is classical RCU candidate. */ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode, - const struct cred *cred, u32 secid, enum ima_hooks func, - int mask, int flags, int *pcr, + const struct cred *cred, struct lsm_prop *prop, + enum ima_hooks func, int mask, int flags, int *pcr, struct ima_template_desc **template_desc, const char *func_data, unsigned int *allowed_algos) { @@ -756,7 +757,7 @@ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode, if (!(entry->action & actmask)) continue; - if (!ima_match_rules(entry, idmap, inode, cred, secid, + if (!ima_match_rules(entry, idmap, inode, cred, prop, func, mask, func_data)) continue; diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 4183956c53af..0e627eac9c33 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -318,15 +318,21 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize, hash_algo_name[hash_algo]); } - if (digest) + if (digest) { memcpy(buffer + offset, digest, digestsize); - else + } else { /* * If digest is NULL, the event being recorded is a violation. * Make room for the digest by increasing the offset by the - * hash algorithm digest size. + * hash algorithm digest size. If the hash algorithm is not + * specified increase the offset by IMA_DIGEST_SIZE which + * fits SHA1 or MD5 */ - offset += hash_digest_size[hash_algo]; + if (hash_algo < HASH_ALGO__LAST) + offset += hash_digest_size[hash_algo]; + else + offset += IMA_DIGEST_SIZE; + } return ima_write_template_field_data(buffer, offset + digestsize, fmt, field_data); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 660f76cb69d3..c2c2da691123 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -37,6 +37,8 @@ struct evm_ima_xattr_data { ); u8 data[]; } __packed; +static_assert(offsetof(struct evm_ima_xattr_data, data) == sizeof(struct evm_ima_xattr_data_hdr), + "struct member likely outside of __struct_group()"); /* Only used in the EVM HMAC code. */ struct evm_xattr { @@ -65,6 +67,8 @@ struct ima_digest_data { ); u8 digest[]; } __packed; +static_assert(offsetof(struct ima_digest_data, digest) == sizeof(struct ima_digest_data_hdr), + "struct member likely outside of __struct_group()"); /* * Instead of wrapping the ima_digest_data struct inside a local structure diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 7d79fc8abe21..e31b97a9f175 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -389,37 +389,21 @@ static bool is_nouser_or_private(const struct dentry *dentry) } static access_mask_t -get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain) -{ - access_mask_t access_dom = 0; - size_t layer_level; - - for (layer_level = 0; layer_level < domain->num_layers; layer_level++) - access_dom |= - landlock_get_raw_fs_access_mask(domain, layer_level); - return access_dom; -} - -static access_mask_t get_handled_fs_accesses(const struct landlock_ruleset *const domain) { /* Handles all initially denied by default access rights. */ - return get_raw_handled_fs_accesses(domain) | + return landlock_union_access_masks(domain).fs | LANDLOCK_ACCESS_FS_INITIALLY_DENIED; } -static const struct landlock_ruleset * -get_fs_domain(const struct landlock_ruleset *const domain) -{ - if (!domain || !get_raw_handled_fs_accesses(domain)) - return NULL; - - return domain; -} +static const struct access_masks any_fs = { + .fs = ~0, +}; static const struct landlock_ruleset *get_current_fs_domain(void) { - return get_fs_domain(landlock_get_current_domain()); + return landlock_get_applicable_domain(landlock_get_current_domain(), + any_fs); } /* @@ -1517,7 +1501,8 @@ static int hook_file_open(struct file *const file) access_mask_t open_access_request, full_access_request, allowed_access, optional_access; const struct landlock_ruleset *const dom = - get_fs_domain(landlock_cred(file->f_cred)->domain); + landlock_get_applicable_domain( + landlock_cred(file->f_cred)->domain, any_fs); if (!dom) return 0; diff --git a/security/landlock/net.c b/security/landlock/net.c index c8bcd29bde09..d5dcc4407a19 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -39,27 +39,9 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset, return err; } -static access_mask_t -get_raw_handled_net_accesses(const struct landlock_ruleset *const domain) -{ - access_mask_t access_dom = 0; - size_t layer_level; - - for (layer_level = 0; layer_level < domain->num_layers; layer_level++) - access_dom |= landlock_get_net_access_mask(domain, layer_level); - return access_dom; -} - -static const struct landlock_ruleset *get_current_net_domain(void) -{ - const struct landlock_ruleset *const dom = - landlock_get_current_domain(); - - if (!dom || !get_raw_handled_net_accesses(dom)) - return NULL; - - return dom; -} +static const struct access_masks any_net = { + .net = ~0, +}; static int current_check_access_socket(struct socket *const sock, struct sockaddr *const address, @@ -72,7 +54,9 @@ static int current_check_access_socket(struct socket *const sock, struct landlock_id id = { .type = LANDLOCK_KEY_NET_PORT, }; - const struct landlock_ruleset *const dom = get_current_net_domain(); + const struct landlock_ruleset *const dom = + landlock_get_applicable_domain(landlock_get_current_domain(), + any_net); if (!dom) return 0; diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 61bdbc550172..631e24d4ffe9 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -11,6 +11,7 @@ #include <linux/bitops.h> #include <linux/build_bug.h> +#include <linux/kernel.h> #include <linux/mutex.h> #include <linux/rbtree.h> #include <linux/refcount.h> @@ -47,6 +48,15 @@ struct access_masks { access_mask_t scope : LANDLOCK_NUM_SCOPE; }; +union access_masks_all { + struct access_masks masks; + u32 all; +}; + +/* Makes sure all fields are covered. */ +static_assert(sizeof(typeof_member(union access_masks_all, masks)) == + sizeof(typeof_member(union access_masks_all, all))); + typedef u16 layer_mask_t; /* Makes sure all layers can be checked. */ static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS); @@ -260,6 +270,61 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset) refcount_inc(&ruleset->usage); } +/** + * landlock_union_access_masks - Return all access rights handled in the + * domain + * + * @domain: Landlock ruleset (used as a domain) + * + * Returns: an access_masks result of the OR of all the domain's access masks. + */ +static inline struct access_masks +landlock_union_access_masks(const struct landlock_ruleset *const domain) +{ + union access_masks_all matches = {}; + size_t layer_level; + + for (layer_level = 0; layer_level < domain->num_layers; layer_level++) { + union access_masks_all layer = { + .masks = domain->access_masks[layer_level], + }; + + matches.all |= layer.all; + } + + return matches.masks; +} + +/** + * landlock_get_applicable_domain - Return @domain if it applies to (handles) + * at least one of the access rights specified + * in @masks + * + * @domain: Landlock ruleset (used as a domain) + * @masks: access masks + * + * Returns: @domain if any access rights specified in @masks is handled, or + * NULL otherwise. + */ +static inline const struct landlock_ruleset * +landlock_get_applicable_domain(const struct landlock_ruleset *const domain, + const struct access_masks masks) +{ + const union access_masks_all masks_all = { + .masks = masks, + }; + union access_masks_all merge = {}; + + if (!domain) + return NULL; + + merge.masks = landlock_union_access_masks(domain); + if (merge.all & masks_all.all) + return domain; + + return NULL; +} + static inline void landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset, const access_mask_t fs_access_mask, @@ -296,18 +361,11 @@ landlock_add_scope_mask(struct landlock_ruleset *const ruleset, } static inline access_mask_t -landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset, - const u16 layer_level) -{ - return ruleset->access_masks[layer_level].fs; -} - -static inline access_mask_t landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset, const u16 layer_level) { /* Handles all initially denied by default access rights. */ - return landlock_get_raw_fs_access_mask(ruleset, layer_level) | + return ruleset->access_masks[layer_level].fs | LANDLOCK_ACCESS_FS_INITIALLY_DENIED; } diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index f5a0e7182ec0..4ed8e70c25ed 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -241,31 +241,21 @@ SYSCALL_DEFINE3(landlock_create_ruleset, static struct landlock_ruleset *get_ruleset_from_fd(const int fd, const fmode_t mode) { - struct fd ruleset_f; + CLASS(fd, ruleset_f)(fd); struct landlock_ruleset *ruleset; - ruleset_f = fdget(fd); - if (!fd_file(ruleset_f)) + if (fd_empty(ruleset_f)) return ERR_PTR(-EBADF); /* Checks FD type and access right. */ - if (fd_file(ruleset_f)->f_op != &ruleset_fops) { - ruleset = ERR_PTR(-EBADFD); - goto out_fdput; - } - if (!(fd_file(ruleset_f)->f_mode & mode)) { - ruleset = ERR_PTR(-EPERM); - goto out_fdput; - } + if (fd_file(ruleset_f)->f_op != &ruleset_fops) + return ERR_PTR(-EBADFD); + if (!(fd_file(ruleset_f)->f_mode & mode)) + return ERR_PTR(-EPERM); ruleset = fd_file(ruleset_f)->private_data; - if (WARN_ON_ONCE(ruleset->num_layers != 1)) { - ruleset = ERR_PTR(-EINVAL); - goto out_fdput; - } + if (WARN_ON_ONCE(ruleset->num_layers != 1)) + return ERR_PTR(-EINVAL); landlock_get_ruleset(ruleset); - -out_fdput: - fdput(ruleset_f); return ruleset; } @@ -276,15 +266,12 @@ out_fdput: */ static int get_path_from_fd(const s32 fd, struct path *const path) { - struct fd f; - int err = 0; + CLASS(fd_raw, f)(fd); BUILD_BUG_ON(!__same_type( fd, ((struct landlock_path_beneath_attr *)NULL)->parent_fd)); - /* Handles O_PATH. */ - f = fdget_raw(fd); - if (!fd_file(f)) + if (fd_empty(f)) return -EBADF; /* * Forbids ruleset FDs, internal filesystems (e.g. nsfs), including @@ -295,16 +282,12 @@ static int get_path_from_fd(const s32 fd, struct path *const path) (fd_file(f)->f_path.mnt->mnt_flags & MNT_INTERNAL) || (fd_file(f)->f_path.dentry->d_sb->s_flags & SB_NOUSER) || d_is_negative(fd_file(f)->f_path.dentry) || - IS_PRIVATE(d_backing_inode(fd_file(f)->f_path.dentry))) { - err = -EBADFD; - goto out_fdput; - } + IS_PRIVATE(d_backing_inode(fd_file(f)->f_path.dentry))) + return -EBADFD; + *path = fd_file(f)->f_path; path_get(path); - -out_fdput: - fdput(f); - return err; + return 0; } static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, @@ -329,7 +312,7 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, return -ENOMSG; /* Checks that allowed_access matches the @ruleset constraints. */ - mask = landlock_get_raw_fs_access_mask(ruleset, 0); + mask = ruleset->access_masks[0].fs; if ((path_beneath_attr.allowed_access | mask) != mask) return -EINVAL; diff --git a/security/landlock/task.c b/security/landlock/task.c index 4acbd7c40eee..dc7dab78392e 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -204,12 +204,17 @@ static bool is_abstract_socket(struct sock *const sock) return false; } +static const struct access_masks unix_scope = { + .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, +}; + static int hook_unix_stream_connect(struct sock *const sock, struct sock *const other, struct sock *const newsk) { const struct landlock_ruleset *const dom = - landlock_get_current_domain(); + landlock_get_applicable_domain(landlock_get_current_domain(), + unix_scope); /* Quick return for non-landlocked tasks. */ if (!dom) @@ -225,7 +230,8 @@ static int hook_unix_may_send(struct socket *const sock, struct socket *const other) { const struct landlock_ruleset *const dom = - landlock_get_current_domain(); + landlock_get_applicable_domain(landlock_get_current_domain(), + unix_scope); if (!dom) return 0; @@ -243,6 +249,10 @@ static int hook_unix_may_send(struct socket *const sock, return 0; } +static const struct access_masks signal_scope = { + .scope = LANDLOCK_SCOPE_SIGNAL, +}; + static int hook_task_kill(struct task_struct *const p, struct kernel_siginfo *const info, const int sig, const struct cred *const cred) @@ -256,6 +266,7 @@ static int hook_task_kill(struct task_struct *const p, } else { dom = landlock_get_current_domain(); } + dom = landlock_get_applicable_domain(dom, signal_scope); /* Quick return for non-landlocked tasks. */ if (!dom) @@ -279,7 +290,8 @@ static int hook_file_send_sigiotask(struct task_struct *tsk, /* Lock already held by send_sigio() and send_sigurg(). */ lockdep_assert_held(&fown->lock); - dom = landlock_file(fown->file)->fown_domain; + dom = landlock_get_applicable_domain( + landlock_file(fown->file)->fown_domain, signal_scope); /* Quick return for unowned socket. */ if (!dom) diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 02144ec39f43..68252452b66c 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -283,7 +283,6 @@ enum loadpin_securityfs_interface_index { static int read_trusted_verity_root_digests(unsigned int fd) { - struct fd f; void *data; int rc; char *p, *d; @@ -295,8 +294,8 @@ static int read_trusted_verity_root_digests(unsigned int fd) if (!list_empty(&dm_verity_loadpin_trusted_root_digests)) return -EPERM; - f = fdget(fd); - if (!fd_file(f)) + CLASS(fd, f)(fd); + if (fd_empty(f)) return -EINVAL; data = kzalloc(SZ_4K, GFP_KERNEL); @@ -359,7 +358,6 @@ static int read_trusted_verity_root_digests(unsigned int fd) } kfree(data); - fdput(f); return 0; @@ -379,8 +377,6 @@ err: /* disallow further attempts after reading a corrupt/invalid file */ deny_reading_verity_digests = true; - fdput(f); - return rc; } diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 849e832719e2..9a8352972086 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -207,7 +207,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current)); - audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); + audit_log_untrustedstring(ab, get_task_comm(comm, current)); switch (a->type) { case LSM_AUDIT_DATA_NONE: @@ -302,7 +302,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, char comm[sizeof(tsk->comm)]; audit_log_format(ab, " opid=%d ocomm=", pid); audit_log_untrustedstring(ab, - memcpy(comm, tsk->comm, sizeof(comm))); + get_task_comm(comm, tsk)); } } break; diff --git a/security/security.c b/security/security.c index c5981e558bc2..09664e09fec9 100644 --- a/security/security.c +++ b/security/security.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> -#include <linux/fsnotify.h> #include <linux/mman.h> #include <linux/mount.h> #include <linux/personality.h> @@ -2726,16 +2725,15 @@ int security_inode_listsecurity(struct inode *inode, EXPORT_SYMBOL(security_inode_listsecurity); /** - * security_inode_getsecid() - Get an inode's secid + * security_inode_getlsmprop() - Get an inode's LSM data * @inode: inode - * @secid: secid to return + * @prop: lsm specific information to return * - * Get the secid associated with the node. In case of failure, @secid will be - * set to zero. + * Get the lsm specific information associated with the node. */ -void security_inode_getsecid(struct inode *inode, u32 *secid) +void security_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop) { - call_void_hook(inode_getsecid, inode, secid); + call_void_hook(inode_getlsmprop, inode, prop); } /** @@ -3104,13 +3102,7 @@ int security_file_receive(struct file *file) */ int security_file_open(struct file *file) { - int ret; - - ret = call_int_hook(file_open, file); - if (ret) - return ret; - - return fsnotify_open_perm(file); + return call_int_hook(file_open, file); } /** @@ -3276,6 +3268,21 @@ void security_cred_getsecid(const struct cred *c, u32 *secid) EXPORT_SYMBOL(security_cred_getsecid); /** + * security_cred_getlsmprop() - Get the LSM data from a set of credentials + * @c: credentials + * @prop: destination for the LSM data + * + * Retrieve the security data of the cred structure @c. In case of + * failure, @prop will be cleared. + */ +void security_cred_getlsmprop(const struct cred *c, struct lsm_prop *prop) +{ + lsmprop_init(prop); + call_void_hook(cred_getlsmprop, c, prop); +} +EXPORT_SYMBOL(security_cred_getlsmprop); + +/** * security_kernel_act_as() - Set the kernel credentials to act as secid * @new: credentials * @secid: secid @@ -3494,33 +3501,33 @@ int security_task_getsid(struct task_struct *p) } /** - * security_current_getsecid_subj() - Get the current task's subjective secid - * @secid: secid value + * security_current_getlsmprop_subj() - Current task's subjective LSM data + * @prop: lsm specific information * * Retrieve the subjective security identifier of the current task and return - * it in @secid. In case of failure, @secid will be set to zero. + * it in @prop. */ -void security_current_getsecid_subj(u32 *secid) +void security_current_getlsmprop_subj(struct lsm_prop *prop) { - *secid = 0; - call_void_hook(current_getsecid_subj, secid); + lsmprop_init(prop); + call_void_hook(current_getlsmprop_subj, prop); } -EXPORT_SYMBOL(security_current_getsecid_subj); +EXPORT_SYMBOL(security_current_getlsmprop_subj); /** - * security_task_getsecid_obj() - Get a task's objective secid + * security_task_getlsmprop_obj() - Get a task's objective LSM data * @p: target task - * @secid: secid value + * @prop: lsm specific information * * Retrieve the objective security identifier of the task_struct in @p and - * return it in @secid. In case of failure, @secid will be set to zero. + * return it in @prop. */ -void security_task_getsecid_obj(struct task_struct *p, u32 *secid) +void security_task_getlsmprop_obj(struct task_struct *p, struct lsm_prop *prop) { - *secid = 0; - call_void_hook(task_getsecid_obj, p, secid); + lsmprop_init(prop); + call_void_hook(task_getlsmprop_obj, p, prop); } -EXPORT_SYMBOL(security_task_getsecid_obj); +EXPORT_SYMBOL(security_task_getlsmprop_obj); /** * security_task_setnice() - Check if setting a task's nice value is allowed @@ -3732,17 +3739,17 @@ int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag) } /** - * security_ipc_getsecid() - Get the sysv ipc object's secid + * security_ipc_getlsmprop() - Get the sysv ipc object LSM data * @ipcp: ipc permission structure - * @secid: secid pointer + * @prop: pointer to lsm information * - * Get the secid associated with the ipc object. In case of failure, @secid - * will be set to zero. + * Get the lsm information associated with the ipc object. */ -void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) + +void security_ipc_getlsmprop(struct kern_ipc_perm *ipcp, struct lsm_prop *prop) { - *secid = 0; - call_void_hook(ipc_getsecid, ipcp, secid); + lsmprop_init(prop); + call_void_hook(ipc_getlsmprop, ipcp, prop); } /** @@ -4314,6 +4321,27 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) EXPORT_SYMBOL(security_secid_to_secctx); /** + * security_lsmprop_to_secctx() - Convert a lsm_prop to a secctx + * @prop: lsm specific information + * @secdata: secctx + * @seclen: secctx length + * + * Convert a @prop entry to security context. If @secdata is NULL the + * length of the result will be returned in @seclen, but no @secdata + * will be returned. This does mean that the length could change between + * calls to check the length and the next call which actually allocates + * and returns the @secdata. + * + * Return: Return 0 on success, error on failure. + */ +int security_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata, + u32 *seclen) +{ + return call_int_hook(lsmprop_to_secctx, prop, secdata, seclen); +} +EXPORT_SYMBOL(security_lsmprop_to_secctx); + +/** * security_secctx_to_secid() - Convert a secctx to a secid * @secdata: secctx * @seclen: length of secctx @@ -5572,7 +5600,7 @@ void security_audit_rule_free(void *lsmrule) /** * security_audit_rule_match() - Check if a label matches an audit rule - * @secid: security label + * @prop: security label * @field: LSM audit field * @op: matching operator * @lsmrule: audit rule @@ -5583,9 +5611,10 @@ void security_audit_rule_free(void *lsmrule) * Return: Returns 1 if secid matches the rule, 0 if it does not, -ERRNO on * failure. */ -int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule) +int security_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, + void *lsmrule) { - return call_int_hook(audit_rule_match, secid, field, op, lsmrule); + return call_int_hook(audit_rule_match, prop, field, op, lsmrule); } #endif /* CONFIG_AUDIT */ diff --git a/security/selinux/.gitignore b/security/selinux/.gitignore index 168fae13ca5a..01c0df8ab009 100644 --- a/security/selinux/.gitignore +++ b/security/selinux/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only av_permissions.h flask.h +/genheaders diff --git a/security/selinux/Makefile b/security/selinux/Makefile index c47519ed8156..86f0575f670d 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -36,7 +36,10 @@ quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs)) # see the note above, replace the $targets and 'flask.h' rule with the lines # below: # targets += $(genhdrs) -# $(addprefix $(obj)/,$(genhdrs)) &: scripts/selinux/... +# $(addprefix $(obj)/,$(genhdrs)) &: $(obj)/genheaders FORCE targets += flask.h -$(obj)/flask.h: scripts/selinux/genheaders/genheaders FORCE +$(obj)/flask.h: $(obj)/genheaders FORCE $(call if_changed,genhdrs) + +hostprogs := genheaders +HOST_EXTRACFLAGS += -I$(srctree)/security/selinux/include diff --git a/security/selinux/avc.c b/security/selinux/avc.c index cc0b0af20296..1f2680bcc43a 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -174,13 +174,15 @@ int avc_get_hash_stats(char *page) * using a linked list for extended_perms_decision lookup because the list is * always small. i.e. less than 5, typically 1 */ -static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver, - struct avc_xperms_node *xp_node) +static struct extended_perms_decision * +avc_xperms_decision_lookup(u8 driver, u8 base_perm, + struct avc_xperms_node *xp_node) { struct avc_xperms_decision_node *xpd_node; list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) { - if (xpd_node->xpd.driver == driver) + if (xpd_node->xpd.driver == driver && + xpd_node->xpd.base_perm == base_perm) return &xpd_node->xpd; } return NULL; @@ -205,11 +207,12 @@ avc_xperms_has_perm(struct extended_perms_decision *xpd, } static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node, - u8 driver, u8 perm) + u8 driver, u8 base_perm, u8 perm) { struct extended_perms_decision *xpd; security_xperm_set(xp_node->xp.drivers.p, driver); - xpd = avc_xperms_decision_lookup(driver, xp_node); + xp_node->xp.base_perms |= base_perm; + xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node); if (xpd && xpd->allowed) security_xperm_set(xpd->allowed->p, perm); } @@ -245,6 +248,7 @@ static void avc_xperms_free(struct avc_xperms_node *xp_node) static void avc_copy_xperms_decision(struct extended_perms_decision *dest, struct extended_perms_decision *src) { + dest->base_perm = src->base_perm; dest->driver = src->driver; dest->used = src->used; if (dest->used & XPERMS_ALLOWED) @@ -272,6 +276,7 @@ static inline void avc_quick_copy_xperms_decision(u8 perm, */ u8 i = perm >> 5; + dest->base_perm = src->base_perm; dest->used = src->used; if (dest->used & XPERMS_ALLOWED) dest->allowed->p[i] = src->allowed->p[i]; @@ -357,6 +362,7 @@ static int avc_xperms_populate(struct avc_node *node, memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p)); dest->xp.len = src->xp.len; + dest->xp.base_perms = src->xp.base_perms; /* for each source xpd allocate a destination xpd and copy */ list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) { @@ -807,6 +813,7 @@ out: * @event : Updating event * @perms : Permission mask bits * @driver: xperm driver information + * @base_perm: the base permission associated with the extended permission * @xperm: xperm permissions * @ssid: AVC entry source sid * @tsid: AVC entry target sid @@ -820,10 +827,9 @@ out: * otherwise, this function updates the AVC entry. The original AVC-entry object * will release later by RCU. */ -static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, - u32 tsid, u16 tclass, u32 seqno, - struct extended_perms_decision *xpd, - u32 flags) +static int avc_update_node(u32 event, u32 perms, u8 driver, u8 base_perm, + u8 xperm, u32 ssid, u32 tsid, u16 tclass, u32 seqno, + struct extended_perms_decision *xpd, u32 flags) { u32 hvalue; int rc = 0; @@ -880,7 +886,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, case AVC_CALLBACK_GRANT: node->ae.avd.allowed |= perms; if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS)) - avc_xperms_allow_perm(node->ae.xp_node, driver, xperm); + avc_xperms_allow_perm(node->ae.xp_node, driver, base_perm, xperm); break; case AVC_CALLBACK_TRY_REVOKE: case AVC_CALLBACK_REVOKE: @@ -987,10 +993,9 @@ static noinline void avc_compute_av(u32 ssid, u32 tsid, u16 tclass, avc_insert(ssid, tsid, tclass, avd, xp_node); } -static noinline int avc_denied(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - u8 driver, u8 xperm, unsigned int flags, - struct av_decision *avd) +static noinline int avc_denied(u32 ssid, u32 tsid, u16 tclass, u32 requested, + u8 driver, u8 base_perm, u8 xperm, + unsigned int flags, struct av_decision *avd) { if (flags & AVC_STRICT) return -EACCES; @@ -999,7 +1004,7 @@ static noinline int avc_denied(u32 ssid, u32 tsid, !(avd->flags & AVD_FLAGS_PERMISSIVE)) return -EACCES; - avc_update_node(AVC_CALLBACK_GRANT, requested, driver, + avc_update_node(AVC_CALLBACK_GRANT, requested, driver, base_perm, xperm, ssid, tsid, tclass, avd->seqno, NULL, flags); return 0; } @@ -1012,7 +1017,8 @@ static noinline int avc_denied(u32 ssid, u32 tsid, * driver field is used to specify which set contains the permission. */ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, - u8 driver, u8 xperm, struct common_audit_data *ad) + u8 driver, u8 base_perm, u8 xperm, + struct common_audit_data *ad) { struct avc_node *node; struct av_decision avd; @@ -1047,22 +1053,23 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, local_xpd.auditallow = &auditallow; local_xpd.dontaudit = &dontaudit; - xpd = avc_xperms_decision_lookup(driver, xp_node); + xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node); if (unlikely(!xpd)) { /* * Compute the extended_perms_decision only if the driver - * is flagged + * is flagged and the base permission is known. */ - if (!security_xperm_test(xp_node->xp.drivers.p, driver)) { + if (!security_xperm_test(xp_node->xp.drivers.p, driver) || + !(xp_node->xp.base_perms & base_perm)) { avd.allowed &= ~requested; goto decision; } rcu_read_unlock(); - security_compute_xperms_decision(ssid, tsid, tclass, - driver, &local_xpd); + security_compute_xperms_decision(ssid, tsid, tclass, driver, + base_perm, &local_xpd); rcu_read_lock(); - avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, - driver, xperm, ssid, tsid, tclass, avd.seqno, + avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, + base_perm, xperm, ssid, tsid, tclass, avd.seqno, &local_xpd, 0); } else { avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd); @@ -1075,8 +1082,8 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, decision: denied = requested & ~(avd.allowed); if (unlikely(denied)) - rc = avc_denied(ssid, tsid, tclass, requested, - driver, xperm, AVC_EXTENDED_PERMS, &avd); + rc = avc_denied(ssid, tsid, tclass, requested, driver, + base_perm, xperm, AVC_EXTENDED_PERMS, &avd); rcu_read_unlock(); @@ -1110,7 +1117,7 @@ static noinline int avc_perm_nonode(u32 ssid, u32 tsid, u16 tclass, avc_compute_av(ssid, tsid, tclass, avd, &xp_node); denied = requested & ~(avd->allowed); if (unlikely(denied)) - return avc_denied(ssid, tsid, tclass, requested, 0, 0, + return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0, flags, avd); return 0; } @@ -1158,7 +1165,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, rcu_read_unlock(); if (unlikely(denied)) - return avc_denied(ssid, tsid, tclass, requested, 0, 0, + return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0, flags, avd); return 0; } diff --git a/security/selinux/genheaders.c b/security/selinux/genheaders.c new file mode 100644 index 000000000000..3834d7eb0af6 --- /dev/null +++ b/security/selinux/genheaders.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +struct security_class_mapping { + const char *name; + const char *perms[sizeof(unsigned) * 8 + 1]; +}; + +#include "classmap.h" +#include "initial_sid_to_string.h" + +const char *progname; + +static void usage(void) +{ + printf("usage: %s flask.h av_permissions.h\n", progname); + exit(1); +} + +static char *stoupperx(const char *s) +{ + char *s2 = strdup(s); + char *p; + + if (!s2) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(3); + } + + for (p = s2; *p; p++) + *p = toupper(*p); + return s2; +} + +int main(int argc, char *argv[]) +{ + int i, j; + int isids_len; + FILE *fout; + + progname = argv[0]; + + if (argc < 3) + usage(); + + fout = fopen(argv[1], "w"); + if (!fout) { + fprintf(stderr, "Could not open %s for writing: %s\n", + argv[1], strerror(errno)); + exit(2); + } + + fprintf(fout, "/* This file is automatically generated. Do not edit. */\n"); + fprintf(fout, "#ifndef _SELINUX_FLASK_H_\n#define _SELINUX_FLASK_H_\n\n"); + + for (i = 0; secclass_map[i].name; i++) { + char *name = stoupperx(secclass_map[i].name); + + fprintf(fout, "#define SECCLASS_%-39s %2d\n", name, i+1); + free(name); + } + + fprintf(fout, "\n"); + + isids_len = sizeof(initial_sid_to_string) / sizeof(char *); + for (i = 1; i < isids_len; i++) { + const char *s = initial_sid_to_string[i]; + if (s) { + char *sidname = stoupperx(s); + + fprintf(fout, "#define SECINITSID_%-39s %2d\n", sidname, i); + free(sidname); + } + } + fprintf(fout, "\n#define SECINITSID_NUM %d\n", i-1); + fprintf(fout, "\nstatic inline bool security_is_socket_class(u16 kern_tclass)\n"); + fprintf(fout, "{\n"); + fprintf(fout, "\tbool sock = false;\n\n"); + fprintf(fout, "\tswitch (kern_tclass) {\n"); + for (i = 0; secclass_map[i].name; i++) { + static char s[] = "SOCKET"; + int len, l; + char *name = stoupperx(secclass_map[i].name); + + len = strlen(name); + l = sizeof(s) - 1; + if (len >= l && memcmp(name + len - l, s, l) == 0) + fprintf(fout, "\tcase SECCLASS_%s:\n", name); + free(name); + } + fprintf(fout, "\t\tsock = true;\n"); + fprintf(fout, "\t\tbreak;\n"); + fprintf(fout, "\tdefault:\n"); + fprintf(fout, "\t\tbreak;\n"); + fprintf(fout, "\t}\n\n"); + fprintf(fout, "\treturn sock;\n"); + fprintf(fout, "}\n"); + + fprintf(fout, "\n#endif\n"); + + if (fclose(fout) != 0) { + fprintf(stderr, "Could not successfully close %s: %s\n", + argv[1], strerror(errno)); + exit(4); + } + + fout = fopen(argv[2], "w"); + if (!fout) { + fprintf(stderr, "Could not open %s for writing: %s\n", + argv[2], strerror(errno)); + exit(5); + } + + fprintf(fout, "/* This file is automatically generated. Do not edit. */\n"); + fprintf(fout, "#ifndef _SELINUX_AV_PERMISSIONS_H_\n#define _SELINUX_AV_PERMISSIONS_H_\n\n"); + + for (i = 0; secclass_map[i].name; i++) { + const struct security_class_mapping *map = &secclass_map[i]; + int len; + char *name = stoupperx(map->name); + + len = strlen(name); + for (j = 0; map->perms[j]; j++) { + char *permname; + + if (j >= 32) { + fprintf(stderr, "Too many permissions to fit into an access vector at (%s, %s).\n", + map->name, map->perms[j]); + exit(5); + } + permname = stoupperx(map->perms[j]); + fprintf(fout, "#define %s__%-*s 0x%08xU\n", name, + 39-len, permname, 1U<<j); + free(permname); + } + free(name); + } + + fprintf(fout, "\n#endif\n"); + + if (fclose(fout) != 0) { + fprintf(stderr, "Could not successfully close %s: %s\n", + argv[2], strerror(errno)); + exit(6); + } + + exit(0); +} diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index fc926d3cac6e..171dd7fceac5 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3503,15 +3503,16 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t return len; } -static void selinux_inode_getsecid(struct inode *inode, u32 *secid) +static void selinux_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop) { struct inode_security_struct *isec = inode_security_novalidate(inode); - *secid = isec->sid; + + prop->selinux.secid = isec->sid; } static int selinux_inode_copy_up(struct dentry *src, struct cred **new) { - u32 sid; + struct lsm_prop prop; struct task_security_struct *tsec; struct cred *new_creds = *new; @@ -3523,8 +3524,8 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new) tsec = selinux_cred(new_creds); /* Get label from overlay inode and set it in create_sid */ - selinux_inode_getsecid(d_inode(src), &sid); - tsec->create_sid = sid; + selinux_inode_getlsmprop(d_inode(src), &prop); + tsec->create_sid = prop.selinux.secid; *new = new_creds; return 0; } @@ -3687,8 +3688,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, return 0; isec = inode_security(inode); - rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, - requested, driver, xperm, &ad); + rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested, + driver, AVC_EXT_IOCTL, xperm, &ad); out: return rc; } @@ -4034,6 +4035,11 @@ static void selinux_cred_getsecid(const struct cred *c, u32 *secid) *secid = cred_sid(c); } +static void selinux_cred_getlsmprop(const struct cred *c, struct lsm_prop *prop) +{ + prop->selinux.secid = cred_sid(c); +} + /* * set the security data for a kernel service * - all the creation contexts are set to unlabelled @@ -4169,14 +4175,15 @@ static int selinux_task_getsid(struct task_struct *p) PROCESS__GETSESSION, NULL); } -static void selinux_current_getsecid_subj(u32 *secid) +static void selinux_current_getlsmprop_subj(struct lsm_prop *prop) { - *secid = current_sid(); + prop->selinux.secid = current_sid(); } -static void selinux_task_getsecid_obj(struct task_struct *p, u32 *secid) +static void selinux_task_getlsmprop_obj(struct task_struct *p, + struct lsm_prop *prop) { - *secid = task_sid_obj(p); + prop->selinux.secid = task_sid_obj(p); } static int selinux_task_setnice(struct task_struct *p, int nice) @@ -4590,14 +4597,10 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec, secclass, NULL, socksid); } -static int sock_has_perm(struct sock *sk, u32 perms) +static bool sock_skip_has_perm(u32 sid) { - struct sk_security_struct *sksec = selinux_sock(sk); - struct common_audit_data ad; - struct lsm_network_audit net; - - if (sksec->sid == SECINITSID_KERNEL) - return 0; + if (sid == SECINITSID_KERNEL) + return true; /* * Before POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT, sockets that @@ -4611,7 +4614,19 @@ static int sock_has_perm(struct sock *sk, u32 perms) * setting. */ if (!selinux_policycap_userspace_initial_context() && - sksec->sid == SECINITSID_INIT) + sid == SECINITSID_INIT) + return true; + return false; +} + + +static int sock_has_perm(struct sock *sk, u32 perms) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net; + + if (sock_skip_has_perm(sksec->sid)) return 0; ad_net_init_from_sk(&ad, &net, sk); @@ -5723,7 +5738,7 @@ static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb, /* we do this in the LOCAL_OUT path and not the POST_ROUTING path * because we want to make sure we apply the necessary labeling * before IPsec is applied so we can leverage AH protection */ - sk = skb->sk; + sk = sk_to_full_sk(skb->sk); if (sk) { struct sk_security_struct *sksec; @@ -5920,6 +5935,26 @@ static unsigned int selinux_ip_postroute(void *priv, } #endif /* CONFIG_NETFILTER */ +static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_type) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net; + u8 driver; + u8 xperm; + + if (sock_skip_has_perm(sksec->sid)) + return 0; + + ad_net_init_from_sk(&ad, &net, sk); + + driver = nlmsg_type >> 8; + xperm = nlmsg_type & 0xff; + + return avc_has_extended_perms(current_sid(), sksec->sid, sksec->sclass, + perms, driver, AVC_EXT_NLMSG, xperm, &ad); +} + static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) { int rc = 0; @@ -5945,7 +5980,12 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm); if (rc == 0) { - rc = sock_has_perm(sk, perm); + if (selinux_policycap_netlink_xperm()) { + rc = nlmsg_sock_has_extended_perms( + sk, perm, nlh->nlmsg_type); + } else { + rc = sock_has_perm(sk, perm); + } if (rc) return rc; } else if (rc == -EINVAL) { @@ -6319,10 +6359,11 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) return ipc_has_perm(ipcp, av); } -static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) +static void selinux_ipc_getlsmprop(struct kern_ipc_perm *ipcp, + struct lsm_prop *prop) { struct ipc_security_struct *isec = selinux_ipc(ipcp); - *secid = isec->sid; + prop->selinux.secid = isec->sid; } static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) @@ -6601,8 +6642,13 @@ static int selinux_ismaclabel(const char *name) static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { - return security_sid_to_context(secid, - secdata, seclen); + return security_sid_to_context(secid, secdata, seclen); +} + +static int selinux_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata, + u32 *seclen) +{ + return selinux_secid_to_secctx(prop->selinux.secid, secdata, seclen); } static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) @@ -7155,7 +7201,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity), LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity), LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity), - LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), + LSM_HOOK_INIT(inode_getlsmprop, selinux_inode_getlsmprop), LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr), LSM_HOOK_INIT(path_notify, selinux_path_notify), @@ -7181,6 +7227,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare), LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer), LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid), + LSM_HOOK_INIT(cred_getlsmprop, selinux_cred_getlsmprop), LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as), LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as), LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), @@ -7189,8 +7236,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), - LSM_HOOK_INIT(current_getsecid_subj, selinux_current_getsecid_subj), - LSM_HOOK_INIT(task_getsecid_obj, selinux_task_getsecid_obj), + LSM_HOOK_INIT(current_getlsmprop_subj, selinux_current_getlsmprop_subj), + LSM_HOOK_INIT(task_getlsmprop_obj, selinux_task_getlsmprop_obj), LSM_HOOK_INIT(task_setnice, selinux_task_setnice), LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio), @@ -7204,7 +7251,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(userns_create, selinux_userns_create), LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), - LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid), + LSM_HOOK_INIT(ipc_getlsmprop, selinux_ipc_getlsmprop), LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate), LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl), @@ -7347,6 +7394,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security), LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security), LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx), + LSM_HOOK_INIT(lsmprop_to_secctx, selinux_lsmprop_to_secctx), LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx), LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security), LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security), diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h index 168d17be7df3..d5b0425055e4 100644 --- a/security/selinux/include/audit.h +++ b/security/selinux/include/audit.h @@ -41,7 +41,7 @@ void selinux_audit_rule_free(void *rule); /** * selinux_audit_rule_match - determine if a context ID matches a rule. - * @sid: the context ID to check + * @prop: includes the context ID to check * @field: the field this rule refers to * @op: the operator the rule uses * @rule: pointer to the audit rule to check against @@ -49,7 +49,8 @@ void selinux_audit_rule_free(void *rule); * Returns 1 if the context id matches the rule, 0 if it does not, and * -errno on failure. */ -int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule); +int selinux_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, + void *rule); /** * selinux_audit_rule_known - check to see if rule contains selinux fields. diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 96a614d47df8..281f40103663 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -136,8 +136,11 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested, int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct common_audit_data *auditdata); +#define AVC_EXT_IOCTL (1 << 0) /* Cache entry for an ioctl extended permission */ +#define AVC_EXT_NLMSG (1 << 1) /* Cache entry for an nlmsg extended permission */ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, - u8 driver, u8 perm, struct common_audit_data *ad); + u8 driver, u8 base_perm, u8 perm, + struct common_audit_data *ad); u32 avc_policy_seqno(void); diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 7229c9bf6c27..2bc20135324a 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -1,8 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#include <linux/capability.h> -#include <linux/socket.h> - #define COMMON_FILE_SOCK_PERMS \ "ioctl", "read", "write", "create", "getattr", "setattr", "lock", \ "relabelfrom", "relabelto", "append", "map" @@ -36,9 +33,13 @@ "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", \ "audit_read", "perfmon", "bpf", "checkpoint_restore" +#ifdef __KERNEL__ /* avoid this check when building host programs */ +#include <linux/capability.h> + #if CAP_LAST_CAP > CAP_CHECKPOINT_RESTORE #error New capability defined, please update COMMON_CAP2_PERMS. #endif +#endif /* * Note: The name for any socket class should be suffixed by "socket", @@ -96,17 +97,17 @@ const struct security_class_mapping secclass_map[] = { { "shm", { COMMON_IPC_PERMS, "lock", NULL } }, { "ipc", { COMMON_IPC_PERMS, NULL } }, { "netlink_route_socket", - { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } }, + { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } }, { "netlink_tcpdiag_socket", - { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } }, + { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } }, { "netlink_nflog_socket", { COMMON_SOCK_PERMS, NULL } }, { "netlink_xfrm_socket", - { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } }, + { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } }, { "netlink_selinux_socket", { COMMON_SOCK_PERMS, NULL } }, { "netlink_iscsi_socket", { COMMON_SOCK_PERMS, NULL } }, { "netlink_audit_socket", { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg_relay", - "nlmsg_readpriv", "nlmsg_tty_audit", NULL } }, + "nlmsg_readpriv", "nlmsg_tty_audit", "nlmsg", NULL } }, { "netlink_fib_lookup_socket", { COMMON_SOCK_PERMS, NULL } }, { "netlink_connector_socket", { COMMON_SOCK_PERMS, NULL } }, { "netlink_netfilter_socket", { COMMON_SOCK_PERMS, NULL } }, @@ -181,6 +182,10 @@ const struct security_class_mapping secclass_map[] = { { NULL } }; +#ifdef __KERNEL__ /* avoid this check when building host programs */ +#include <linux/socket.h> + #if PF_MAX > 46 #error New address family defined, please update secclass_map. #endif +#endif diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h index 99b353b2abb4..d7ba60b62491 100644 --- a/security/selinux/include/initial_sid_to_string.h +++ b/security/selinux/include/initial_sid_to_string.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#ifdef __KERNEL__ #include <linux/stddef.h> +#else +#include <stddef.h> +#endif static const char *const initial_sid_to_string[] = { NULL, /* zero placeholder, not used */ diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h index dc3674eb29c1..079679fe7254 100644 --- a/security/selinux/include/policycap.h +++ b/security/selinux/include/policycap.h @@ -14,6 +14,7 @@ enum { POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS, POLICYDB_CAP_IOCTL_SKIP_CLOEXEC, POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT, + POLICYDB_CAP_NETLINK_XPERM, __POLICYDB_CAP_MAX }; #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1) diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h index 2cffcc1ce851..e080827408c4 100644 --- a/security/selinux/include/policycap_names.h +++ b/security/selinux/include/policycap_names.h @@ -17,6 +17,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = { "genfs_seclabel_symlinks", "ioctl_skip_cloexec", "userspace_initial_context", + "netlink_xperm", }; /* clang-format on */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 289bf9233f71..700bd6c8bb38 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -195,6 +195,12 @@ static inline bool selinux_policycap_userspace_initial_context(void) selinux_state.policycap[POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT]); } +static inline bool selinux_policycap_netlink_xperm(void) +{ + return READ_ONCE( + selinux_state.policycap[POLICYDB_CAP_NETLINK_XPERM]); +} + struct selinux_policy_convert_data; struct selinux_load_state { @@ -233,6 +239,7 @@ struct extended_perms_data { struct extended_perms_decision { u8 used; u8 driver; + u8 base_perm; struct extended_perms_data *allowed; struct extended_perms_data *auditallow; struct extended_perms_data *dontaudit; @@ -240,6 +247,7 @@ struct extended_perms_decision { struct extended_perms { u16 len; /* length associated decision chain */ + u8 base_perms; /* which base permissions are covered */ struct extended_perms_data drivers; /* flag drivers that are used */ }; @@ -251,6 +259,7 @@ void security_compute_av(u32 ssid, u32 tsid, u16 tclass, struct extended_perms *xperms); void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass, u8 driver, + u8 base_perm, struct extended_perms_decision *xpermd); void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass, diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 8ff670cf1ee5..3a95986b134f 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -21,142 +21,142 @@ #include "security.h" struct nlmsg_perm { - u16 nlmsg_type; - u32 perm; + u16 nlmsg_type; + u32 perm; }; static const struct nlmsg_perm nlmsg_route_perms[] = { - { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_NEWNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWNSID, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_SETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_NEWVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETVLAN, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ }, - { RTM_NEWTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_DELTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_NEWNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWNSID, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_SETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_NEWVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETVLAN, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_GETTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, }; static const struct nlmsg_perm nlmsg_tcpdiag_perms[] = { - { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, - { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, - { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, - { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE }, + { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE }, }; static const struct nlmsg_perm nlmsg_xfrm_perms[] = { - { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_REPORT, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_MIGRATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_NEWSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_GETSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_NEWSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_GETSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ }, - { XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, - { XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_REPORT, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_MIGRATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_NEWSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_GETSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_NEWSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, + { XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ }, }; static const struct nlmsg_perm nlmsg_audit_perms[] = { - { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, - { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, - { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, - { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, - { AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, - { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, - { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT }, - { AUDIT_GET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_READ }, - { AUDIT_SET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, + { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, + { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT }, + { AUDIT_GET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_SET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, }; - -static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, size_t tabsize) +static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, + size_t tabsize) { unsigned int i; int err = -EINVAL; - for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++) + for (i = 0; i < tabsize / sizeof(struct nlmsg_perm); i++) if (nlmsg_type == tab[i].nlmsg_type) { *perm = tab[i].perm; err = 0; @@ -168,7 +168,12 @@ static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, s int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) { - int err = 0; + /* While it is possible to add a similar permission to other netlink + * classes, note that the extended permission value is matched against + * the nlmsg_type field. Notably, SECCLASS_NETLINK_GENERIC_SOCKET uses + * dynamic values for this field, which means that it cannot be added + * as-is. + */ switch (sclass) { case SECCLASS_NETLINK_ROUTE_SOCKET: @@ -178,42 +183,52 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) * before updating the BUILD_BUG_ON() macro! */ BUILD_BUG_ON(RTM_MAX != (RTM_NEWTUNNEL + 3)); - err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms, - sizeof(nlmsg_route_perms)); - break; + if (selinux_policycap_netlink_xperm()) { + *perm = NETLINK_ROUTE_SOCKET__NLMSG; + return 0; + } + return nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms, + sizeof(nlmsg_route_perms)); + break; case SECCLASS_NETLINK_TCPDIAG_SOCKET: - err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms, - sizeof(nlmsg_tcpdiag_perms)); + if (selinux_policycap_netlink_xperm()) { + *perm = NETLINK_TCPDIAG_SOCKET__NLMSG; + return 0; + } + return nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms, + sizeof(nlmsg_tcpdiag_perms)); break; - case SECCLASS_NETLINK_XFRM_SOCKET: /* If the BUILD_BUG_ON() below fails you must update the * structures at the top of this file with the new mappings * before updating the BUILD_BUG_ON() macro! */ BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETDEFAULT); - err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms, - sizeof(nlmsg_xfrm_perms)); - break; + if (selinux_policycap_netlink_xperm()) { + *perm = NETLINK_XFRM_SOCKET__NLMSG; + return 0; + } + return nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms, + sizeof(nlmsg_xfrm_perms)); + break; case SECCLASS_NETLINK_AUDIT_SOCKET: - if ((nlmsg_type >= AUDIT_FIRST_USER_MSG && - nlmsg_type <= AUDIT_LAST_USER_MSG) || - (nlmsg_type >= AUDIT_FIRST_USER_MSG2 && - nlmsg_type <= AUDIT_LAST_USER_MSG2)) { + if (selinux_policycap_netlink_xperm()) { + *perm = NETLINK_AUDIT_SOCKET__NLMSG; + return 0; + } else if ((nlmsg_type >= AUDIT_FIRST_USER_MSG && + nlmsg_type <= AUDIT_LAST_USER_MSG) || + (nlmsg_type >= AUDIT_FIRST_USER_MSG2 && + nlmsg_type <= AUDIT_LAST_USER_MSG2)) { *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY; - } else { - err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, - sizeof(nlmsg_audit_perms)); + return 0; } - break; - - /* No messaging from userspace, or class unknown/unhandled */ - default: - err = -ENOENT; + return nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, + sizeof(nlmsg_audit_perms)); break; } - return err; + /* No messaging from userspace, or class unknown/unhandled */ + return -ENOENT; } diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index e172f182b65c..6cd5bb0ba380 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -708,7 +708,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, if (new_value) { char comm[sizeof(current->comm)]; - memcpy(comm, current->comm, sizeof(comm)); + strscpy(comm, current->comm); pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n", comm, current->pid); } @@ -1069,6 +1069,10 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) int rc; u32 i, len, nsids; + pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!" + " This will not be supported in the future; please update your" + " userspace.\n", current->comm, current->pid); + length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_USER, NULL); diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 8e8820484c55..f4407185401c 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -53,8 +53,9 @@ struct avtab_key { */ struct avtab_extended_perms { /* These are not flags. All 256 values may be used */ -#define AVTAB_XPERMS_IOCTLFUNCTION 0x01 -#define AVTAB_XPERMS_IOCTLDRIVER 0x02 +#define AVTAB_XPERMS_IOCTLFUNCTION 0x01 +#define AVTAB_XPERMS_IOCTLDRIVER 0x02 +#define AVTAB_XPERMS_NLMSG 0x03 /* extension of the avtab_key specified */ u8 specified; /* ioctl, netfilter, ... */ /* diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index a9830fbfc5c6..d9f58b5d0f49 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -582,8 +582,7 @@ static void type_attribute_bounds_av(struct policydb *policydb, } /* - * flag which drivers have permissions - * only looking for ioctl based extended permissions + * Flag which drivers have permissions and which base permissions are covered. */ void services_compute_xperms_drivers( struct extended_perms *xperms, @@ -591,14 +590,25 @@ void services_compute_xperms_drivers( { unsigned int i; - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { + switch (node->datum.u.xperms->specified) { + case AVTAB_XPERMS_IOCTLDRIVER: + xperms->base_perms |= AVC_EXT_IOCTL; /* if one or more driver has all permissions allowed */ for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++) xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i]; - } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { + break; + case AVTAB_XPERMS_IOCTLFUNCTION: + xperms->base_perms |= AVC_EXT_IOCTL; + /* if allowing permissions within a driver */ + security_xperm_set(xperms->drivers.p, + node->datum.u.xperms->driver); + break; + case AVTAB_XPERMS_NLMSG: + xperms->base_perms |= AVC_EXT_NLMSG; /* if allowing permissions within a driver */ security_xperm_set(xperms->drivers.p, node->datum.u.xperms->driver); + break; } xperms->len = 1; @@ -628,8 +638,7 @@ static void context_struct_compute_av(struct policydb *policydb, avd->auditallow = 0; avd->auditdeny = 0xffffffff; if (xperms) { - memset(&xperms->drivers, 0, sizeof(xperms->drivers)); - xperms->len = 0; + memset(xperms, 0, sizeof(*xperms)); } if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { @@ -942,57 +951,70 @@ static void avd_init(struct selinux_policy *policy, struct av_decision *avd) avd->flags = 0; } -void services_compute_xperms_decision(struct extended_perms_decision *xpermd, - struct avtab_node *node) +static void update_xperms_extended_data(u8 specified, + struct extended_perms_data *from, + struct extended_perms_data *xp_data) { unsigned int i; - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { - if (xpermd->driver != node->datum.u.xperms->driver) + switch (specified) { + case AVTAB_XPERMS_IOCTLDRIVER: + memset(xp_data->p, 0xff, sizeof(xp_data->p)); + break; + case AVTAB_XPERMS_IOCTLFUNCTION: + case AVTAB_XPERMS_NLMSG: + for (i = 0; i < ARRAY_SIZE(xp_data->p); i++) + xp_data->p[i] |= from->p[i]; + break; + } + +} + +void services_compute_xperms_decision(struct extended_perms_decision *xpermd, + struct avtab_node *node) +{ + switch (node->datum.u.xperms->specified) { + case AVTAB_XPERMS_IOCTLFUNCTION: + if (xpermd->base_perm != AVC_EXT_IOCTL || + xpermd->driver != node->datum.u.xperms->driver) return; - } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { - if (!security_xperm_test(node->datum.u.xperms->perms.p, - xpermd->driver)) + break; + case AVTAB_XPERMS_IOCTLDRIVER: + if (xpermd->base_perm != AVC_EXT_IOCTL || + !security_xperm_test(node->datum.u.xperms->perms.p, + xpermd->driver)) return; - } else { - BUG(); + break; + case AVTAB_XPERMS_NLMSG: + if (xpermd->base_perm != AVC_EXT_NLMSG || + xpermd->driver != node->datum.u.xperms->driver) + return; + break; + default: + pr_warn_once( + "SELinux: unknown extended permission (%u) will be ignored\n", + node->datum.u.xperms->specified); + return; } if (node->key.specified == AVTAB_XPERMS_ALLOWED) { xpermd->used |= XPERMS_ALLOWED; - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { - memset(xpermd->allowed->p, 0xff, - sizeof(xpermd->allowed->p)); - } - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { - for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++) - xpermd->allowed->p[i] |= - node->datum.u.xperms->perms.p[i]; - } + update_xperms_extended_data(node->datum.u.xperms->specified, + &node->datum.u.xperms->perms, + xpermd->allowed); } else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) { xpermd->used |= XPERMS_AUDITALLOW; - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { - memset(xpermd->auditallow->p, 0xff, - sizeof(xpermd->auditallow->p)); - } - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { - for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++) - xpermd->auditallow->p[i] |= - node->datum.u.xperms->perms.p[i]; - } + update_xperms_extended_data(node->datum.u.xperms->specified, + &node->datum.u.xperms->perms, + xpermd->auditallow); } else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) { xpermd->used |= XPERMS_DONTAUDIT; - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { - memset(xpermd->dontaudit->p, 0xff, - sizeof(xpermd->dontaudit->p)); - } - if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { - for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++) - xpermd->dontaudit->p[i] |= - node->datum.u.xperms->perms.p[i]; - } + update_xperms_extended_data(node->datum.u.xperms->specified, + &node->datum.u.xperms->perms, + xpermd->dontaudit); } else { - BUG(); + pr_warn_once("SELinux: unknown specified key (%u)\n", + node->key.specified); } } @@ -1000,6 +1022,7 @@ void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 orig_tclass, u8 driver, + u8 base_perm, struct extended_perms_decision *xpermd) { struct selinux_policy *policy; @@ -1013,6 +1036,7 @@ void security_compute_xperms_decision(u32 ssid, struct ebitmap_node *snode, *tnode; unsigned int i, j; + xpermd->base_perm = base_perm; xpermd->driver = driver; xpermd->used = 0; memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p)); @@ -3635,7 +3659,7 @@ int selinux_audit_rule_known(struct audit_krule *rule) return 0; } -int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) +int selinux_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule) { struct selinux_state *state = &selinux_state; struct selinux_policy *policy; @@ -3661,10 +3685,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) goto out; } - ctxt = sidtab_search(policy->sidtab, sid); + ctxt = sidtab_search(policy->sidtab, prop->selinux.secid); if (unlikely(!ctxt)) { WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", - sid); + prop->selinux.secid); match = -ENOENT; goto out; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 370fd594da12..0c476282e279 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1649,15 +1649,13 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer, } /** - * smack_inode_getsecid - Extract inode's security id + * smack_inode_getlsmprop - Extract inode's security id * @inode: inode to extract the info from - * @secid: where result will be saved + * @prop: where result will be saved */ -static void smack_inode_getsecid(struct inode *inode, u32 *secid) +static void smack_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop) { - struct smack_known *skp = smk_of_inode(inode); - - *secid = skp->smk_secid; + prop->smack.skp = smk_of_inode(inode); } /* @@ -2149,6 +2147,21 @@ static void smack_cred_getsecid(const struct cred *cred, u32 *secid) } /** + * smack_cred_getlsmprop - get the Smack label for a creds structure + * @cred: the object creds + * @prop: where to put the data + * + * Sets the Smack part of the ref + */ +static void smack_cred_getlsmprop(const struct cred *cred, + struct lsm_prop *prop) +{ + rcu_read_lock(); + prop->smack.skp = smk_of_task(smack_cred(cred)); + rcu_read_unlock(); +} + +/** * smack_kernel_act_as - Set the subjective context in a set of credentials * @new: points to the set of credentials to be modified. * @secid: specifies the security ID to be set @@ -2239,30 +2252,27 @@ static int smack_task_getsid(struct task_struct *p) } /** - * smack_current_getsecid_subj - get the subjective secid of the current task - * @secid: where to put the result + * smack_current_getlsmprop_subj - get the subjective secid of the current task + * @prop: where to put the result * * Sets the secid to contain a u32 version of the task's subjective smack label. */ -static void smack_current_getsecid_subj(u32 *secid) +static void smack_current_getlsmprop_subj(struct lsm_prop *prop) { - struct smack_known *skp = smk_of_current(); - - *secid = skp->smk_secid; + prop->smack.skp = smk_of_current(); } /** - * smack_task_getsecid_obj - get the objective secid of the task + * smack_task_getlsmprop_obj - get the objective data of the task * @p: the task - * @secid: where to put the result + * @prop: where to put the result * * Sets the secid to contain a u32 version of the task's objective smack label. */ -static void smack_task_getsecid_obj(struct task_struct *p, u32 *secid) +static void smack_task_getlsmprop_obj(struct task_struct *p, + struct lsm_prop *prop) { - struct smack_known *skp = smk_of_task_struct_obj(p); - - *secid = skp->smk_secid; + prop->smack.skp = smk_of_task_struct_obj(p); } /** @@ -3435,16 +3445,15 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) } /** - * smack_ipc_getsecid - Extract smack security id + * smack_ipc_getlsmprop - Extract smack security data * @ipp: the object permissions - * @secid: where result will be saved + * @prop: where result will be saved */ -static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) +static void smack_ipc_getlsmprop(struct kern_ipc_perm *ipp, struct lsm_prop *prop) { - struct smack_known **blob = smack_ipc(ipp); - struct smack_known *iskp = *blob; + struct smack_known **iskpp = smack_ipc(ipp); - *secid = iskp->smk_secid; + prop->smack.skp = *iskpp; } /** @@ -4757,7 +4766,7 @@ static int smack_audit_rule_known(struct audit_krule *krule) /** * smack_audit_rule_match - Audit given object ? - * @secid: security id for identifying the object to test + * @prop: security id for identifying the object to test * @field: audit rule flags given from user-space * @op: required testing operator * @vrule: smack internal rule presentation @@ -4765,9 +4774,10 @@ static int smack_audit_rule_known(struct audit_krule *krule) * The core Audit hook. It's used to take the decision of * whether to audit or not to audit a given object. */ -static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule) +static int smack_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, + void *vrule) { - struct smack_known *skp; + struct smack_known *skp = prop->smack.skp; char *rule = vrule; if (unlikely(!rule)) { @@ -4778,8 +4788,6 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule) if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) return 0; - skp = smack_from_secid(secid); - /* * No need to do string comparisons. If a match occurs, * both pointers will point to the same smack_known @@ -4809,7 +4817,6 @@ static int smack_ismaclabel(const char *name) return (strcmp(name, XATTR_SMACK_SUFFIX) == 0); } - /** * smack_secid_to_secctx - return the smack label for a secid * @secid: incoming integer @@ -4829,6 +4836,25 @@ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) } /** + * smack_lsmprop_to_secctx - return the smack label + * @prop: includes incoming Smack data + * @secdata: destination + * @seclen: how long it is + * + * Exists for audit code. + */ +static int smack_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata, + u32 *seclen) +{ + struct smack_known *skp = prop->smack.skp; + + if (secdata) + *secdata = skp->smk_known; + *seclen = strlen(skp->smk_known); + return 0; +} + +/** * smack_secctx_to_secid - return the secid for a smack label * @secdata: smack label * @seclen: how long result is @@ -5078,7 +5104,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity), LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity), LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity), - LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid), + LSM_HOOK_INIT(inode_getlsmprop, smack_inode_getlsmprop), LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security), LSM_HOOK_INIT(file_ioctl, smack_file_ioctl), @@ -5098,13 +5124,14 @@ static struct security_hook_list smack_hooks[] __ro_after_init = { LSM_HOOK_INIT(cred_prepare, smack_cred_prepare), LSM_HOOK_INIT(cred_transfer, smack_cred_transfer), LSM_HOOK_INIT(cred_getsecid, smack_cred_getsecid), + LSM_HOOK_INIT(cred_getlsmprop, smack_cred_getlsmprop), LSM_HOOK_INIT(kernel_act_as, smack_kernel_act_as), LSM_HOOK_INIT(kernel_create_files_as, smack_kernel_create_files_as), LSM_HOOK_INIT(task_setpgid, smack_task_setpgid), LSM_HOOK_INIT(task_getpgid, smack_task_getpgid), LSM_HOOK_INIT(task_getsid, smack_task_getsid), - LSM_HOOK_INIT(current_getsecid_subj, smack_current_getsecid_subj), - LSM_HOOK_INIT(task_getsecid_obj, smack_task_getsecid_obj), + LSM_HOOK_INIT(current_getlsmprop_subj, smack_current_getlsmprop_subj), + LSM_HOOK_INIT(task_getlsmprop_obj, smack_task_getlsmprop_obj), LSM_HOOK_INIT(task_setnice, smack_task_setnice), LSM_HOOK_INIT(task_setioprio, smack_task_setioprio), LSM_HOOK_INIT(task_getioprio, smack_task_getioprio), @@ -5115,7 +5142,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = { LSM_HOOK_INIT(task_to_inode, smack_task_to_inode), LSM_HOOK_INIT(ipc_permission, smack_ipc_permission), - LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid), + LSM_HOOK_INIT(ipc_getlsmprop, smack_ipc_getlsmprop), LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security), @@ -5187,6 +5214,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = { LSM_HOOK_INIT(ismaclabel, smack_ismaclabel), LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx), + LSM_HOOK_INIT(lsmprop_to_secctx, smack_lsmprop_to_secctx), LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid), LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx), LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx), diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 5dd1e164f9b1..1401412fd794 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -182,11 +182,9 @@ static inline void smack_catset_bit(unsigned int cat, char *catsetp) */ static void smk_netlabel_audit_set(struct netlbl_audit *nap) { - struct smack_known *skp = smk_of_current(); - nap->loginuid = audit_get_loginuid(current); nap->sessionid = audit_get_sessionid(current); - nap->secid = skp->smk_secid; + nap->prop.smack.skp = smk_of_current(); } /* |