diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 5c2798534950..e1a04bd3b9e5 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -22,8 +22,9 @@ Description: action: measure | dont_measure | appraise | dont_appraise | audit | hash | dont_hash condition:= base | lsm [option] - base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=] - [euid=] [fowner=] [fsname=]] + base: [[func=] [mask=] [fsmagic=] [fsuuid=] [fsname=] + [uid=] [euid=] [gid=] [egid=] + [fowner=] [fgroup=]] lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] option: [[appraise_type=]] [template=] [permit_directio] @@ -40,7 +41,10 @@ Description: fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6) uid:= decimal value euid:= decimal value + gid:= decimal value + egid:= decimal value fowner:= decimal value + fgroup:= decimal value lsm: are LSM specific option: appraise_type:= [imasig] [imasig|modsig] diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 1c8435dfabee..08f907382c61 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -78,7 +78,7 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames); -static int evm_fixmode; +static int evm_fixmode __ro_after_init; static int __init evm_set_fixmode(char *str) { if (strncmp(str, "fix", 3) == 0) diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 2c6c3a5228b5..a64fb0130b01 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -403,7 +403,7 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) } if (!pathname) { - strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX); + strscpy(namebuf, path->dentry->d_name.name, NAME_MAX); pathname = namebuf; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 87b9b71cb820..320ca80aacab 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -36,6 +36,9 @@ #define IMA_KEYRINGS 0x0400 #define IMA_LABEL 0x0800 #define IMA_VALIDATE_ALGOS 0x1000 +#define IMA_GID 0x2000 +#define IMA_EGID 0x4000 +#define IMA_FGROUP 0x8000 #define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -78,9 +81,13 @@ struct ima_rule_entry { unsigned long fsmagic; uuid_t fsuuid; kuid_t uid; + kgid_t gid; kuid_t fowner; - bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */ - bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */ + kgid_t fgroup; + bool (*uid_op)(kuid_t cred_uid, kuid_t rule_uid); /* Handlers for operators */ + bool (*gid_op)(kgid_t cred_gid, kgid_t rule_gid); + bool (*fowner_op)(kuid_t cred_uid, kuid_t rule_uid); /* uid_eq(), uid_gt(), uid_lt() */ + bool (*fgroup_op)(kgid_t cred_gid, kgid_t rule_gid); /* gid_eq(), gid_gt(), gid_lt() */ int pcr; unsigned int allowed_algos; /* bitfield of allowed hash algorithms */ struct { @@ -104,7 +111,8 @@ static_assert( /* * Without LSM specific knowledge, the default policy can only be - * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner + * written in terms of .action, .func, .mask, .fsmagic, .uid, .gid, + * .fowner, and .fgroup */ /* @@ -228,7 +236,7 @@ static struct ima_rule_entry *arch_policy_entry __ro_after_init; static LIST_HEAD(ima_default_rules); static LIST_HEAD(ima_policy_rules); static LIST_HEAD(ima_temp_rules); -static struct list_head *ima_rules = &ima_default_rules; +static struct list_head __rcu *ima_rules = (struct list_head __rcu *)(&ima_default_rules); static int ima_policy __initdata; @@ -582,10 +590,23 @@ static bool ima_match_rules(struct ima_rule_entry *rule, } else if (!rule->uid_op(cred->euid, rule->uid)) return false; } - + if ((rule->flags & IMA_GID) && !rule->gid_op(cred->gid, rule->gid)) + return false; + if (rule->flags & IMA_EGID) { + if (has_capability_noaudit(current, CAP_SETGID)) { + if (!rule->gid_op(cred->egid, rule->gid) + && !rule->gid_op(cred->sgid, rule->gid) + && !rule->gid_op(cred->gid, rule->gid)) + return false; + } else if (!rule->gid_op(cred->egid, rule->gid)) + return false; + } if ((rule->flags & IMA_FOWNER) && !rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner)) return false; + if ((rule->flags & IMA_FGROUP) && + !rule->fgroup_op(i_gid_into_mnt(mnt_userns, inode), rule->fgroup)) + return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; @@ -675,12 +696,14 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode, { struct ima_rule_entry *entry; int action = 0, actmask = flags | (flags << 1); + struct list_head *ima_rules_tmp; if (template_desc && !*template_desc) *template_desc = ima_template_desc_current(); rcu_read_lock(); - list_for_each_entry_rcu(entry, ima_rules, list) { + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(entry, ima_rules_tmp, list) { if (!(entry->action & actmask)) continue; @@ -741,9 +764,11 @@ void ima_update_policy_flags(void) { struct ima_rule_entry *entry; int new_policy_flag = 0; + struct list_head *ima_rules_tmp; rcu_read_lock(); - list_for_each_entry(entry, ima_rules, list) { + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(entry, ima_rules_tmp, list) { /* * SETXATTR_CHECK rules do not implement a full policy check * because rule checking would probably have an important @@ -845,7 +870,7 @@ static int __init ima_init_arch_policy(void) char rule[255]; int result; - result = strlcpy(rule, *rules, sizeof(rule)); + result = strscpy(rule, *rules, sizeof(rule)); INIT_LIST_HEAD(&arch_policy_entry[i].list); result = ima_parse_rule(rule, &arch_policy_entry[i]); @@ -864,8 +889,7 @@ static int __init ima_init_arch_policy(void) /** * ima_init_policy - initialize the default measure rules. * - * ima_rules points to either the ima_default_rules or the - * the new ima_policy_rules. + * ima_rules points to either the ima_default_rules or the new ima_policy_rules. */ void __init ima_init_policy(void) { @@ -968,10 +992,10 @@ void ima_update_policy(void) list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu); - if (ima_rules != policy) { + if (ima_rules != (struct list_head __rcu *)policy) { ima_policy_flag = 0; - ima_rules = policy; + rcu_assign_pointer(ima_rules, policy); /* * IMA architecture specific policy rules are specified * as strings and converted to an array of ima_entry_rules @@ -987,16 +1011,19 @@ void ima_update_policy(void) } /* Keep the enumeration in sync with the policy_tokens! */ -enum { +enum policy_opt { Opt_measure, Opt_dont_measure, Opt_appraise, Opt_dont_appraise, Opt_audit, Opt_hash, Opt_dont_hash, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, - Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, - Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, - Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, - Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, + Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fsuuid, + Opt_uid_eq, Opt_euid_eq, Opt_gid_eq, Opt_egid_eq, + Opt_fowner_eq, Opt_fgroup_eq, + Opt_uid_gt, Opt_euid_gt, Opt_gid_gt, Opt_egid_gt, + Opt_fowner_gt, Opt_fgroup_gt, + Opt_uid_lt, Opt_euid_lt, Opt_gid_lt, Opt_egid_lt, + Opt_fowner_lt, Opt_fgroup_lt, Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos, Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings, Opt_label, Opt_err @@ -1023,13 +1050,22 @@ static const match_table_t policy_tokens = { {Opt_fsuuid, "fsuuid=%s"}, {Opt_uid_eq, "uid=%s"}, {Opt_euid_eq, "euid=%s"}, + {Opt_gid_eq, "gid=%s"}, + {Opt_egid_eq, "egid=%s"}, {Opt_fowner_eq, "fowner=%s"}, + {Opt_fgroup_eq, "fgroup=%s"}, {Opt_uid_gt, "uid>%s"}, {Opt_euid_gt, "euid>%s"}, + {Opt_gid_gt, "gid>%s"}, + {Opt_egid_gt, "egid>%s"}, {Opt_fowner_gt, "fowner>%s"}, + {Opt_fgroup_gt, "fgroup>%s"}, {Opt_uid_lt, "uid<%s"}, {Opt_euid_lt, "euid<%s"}, + {Opt_gid_lt, "gid<%s"}, + {Opt_egid_lt, "egid<%s"}, {Opt_fowner_lt, "fowner<%s"}, + {Opt_fgroup_lt, "fgroup<%s"}, {Opt_appraise_type, "appraise_type=%s"}, {Opt_appraise_flag, "appraise_flag=%s"}, {Opt_appraise_algos, "appraise_algos=%s"}, @@ -1061,7 +1097,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, pr_warn("rule for LSM \'%s\' is undefined\n", entry->lsm[lsm_rule].args_p); - if (ima_rules == &ima_default_rules) { + if (ima_rules == (struct list_head __rcu *)(&ima_default_rules)) { kfree(entry->lsm[lsm_rule].args_p); entry->lsm[lsm_rule].args_p = NULL; result = -EINVAL; @@ -1073,22 +1109,36 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, } static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, - bool (*rule_operator)(kuid_t, kuid_t)) + enum policy_opt rule_operator) { if (!ab) return; - if (rule_operator == &uid_gt) + switch (rule_operator) { + case Opt_uid_gt: + case Opt_euid_gt: + case Opt_gid_gt: + case Opt_egid_gt: + case Opt_fowner_gt: + case Opt_fgroup_gt: audit_log_format(ab, "%s>", key); - else if (rule_operator == &uid_lt) + break; + case Opt_uid_lt: + case Opt_euid_lt: + case Opt_gid_lt: + case Opt_egid_lt: + case Opt_fowner_lt: + case Opt_fgroup_lt: audit_log_format(ab, "%s<", key); - else + break; + default: audit_log_format(ab, "%s=", key); + } audit_log_format(ab, "%s ", value); } static void ima_log_string(struct audit_buffer *ab, char *key, char *value) { - ima_log_string_op(ab, key, value, NULL); + ima_log_string_op(ab, key, value, Opt_err); } /* @@ -1163,7 +1213,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | - IMA_FSNAME | IMA_DIGSIG_REQUIRED | + IMA_FSNAME | IMA_GID | IMA_EGID | + IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS)) return false; @@ -1174,7 +1225,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | - IMA_FSNAME | IMA_DIGSIG_REQUIRED | + IMA_FSNAME | IMA_GID | IMA_EGID | + IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) return false; @@ -1186,7 +1238,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_EUID | - IMA_PCR | IMA_FSNAME)) + IMA_PCR | IMA_FSNAME | IMA_GID | IMA_EGID | + IMA_FGROUP)) return false; break; @@ -1194,7 +1247,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->action & ~(MEASURE | DONT_MEASURE)) return false; - if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR | + if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_GID | IMA_PCR | IMA_KEYRINGS)) return false; @@ -1206,7 +1259,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->action & ~(MEASURE | DONT_MEASURE)) return false; - if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR | + if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_GID | IMA_PCR | IMA_LABEL)) return false; @@ -1276,7 +1329,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) struct audit_buffer *ab; char *from; char *p; - bool uid_token; + bool eid_token; /* either euid or egid */ struct ima_template_desc *template_desc; int result = 0; @@ -1284,9 +1337,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) AUDIT_INTEGRITY_POLICY_RULE); entry->uid = INVALID_UID; + entry->gid = INVALID_GID; entry->fowner = INVALID_UID; + entry->fgroup = INVALID_GID; entry->uid_op = &uid_eq; + entry->gid_op = &gid_eq; entry->fowner_op = &uid_eq; + entry->fgroup_op = &gid_eq; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -1504,12 +1561,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) fallthrough; case Opt_uid_eq: case Opt_euid_eq: - uid_token = (token == Opt_uid_eq) || - (token == Opt_uid_gt) || - (token == Opt_uid_lt); + eid_token = (token == Opt_euid_eq) || + (token == Opt_euid_gt) || + (token == Opt_euid_lt); - ima_log_string_op(ab, uid_token ? "uid" : "euid", - args[0].from, entry->uid_op); + ima_log_string_op(ab, eid_token ? "euid" : "uid", + args[0].from, token); if (uid_valid(entry->uid)) { result = -EINVAL; @@ -1524,8 +1581,43 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) (uid_t)lnum != lnum) result = -EINVAL; else - entry->flags |= uid_token - ? IMA_UID : IMA_EUID; + entry->flags |= eid_token + ? IMA_EUID : IMA_UID; + } + break; + case Opt_gid_gt: + case Opt_egid_gt: + entry->gid_op = &gid_gt; + fallthrough; + case Opt_gid_lt: + case Opt_egid_lt: + if ((token == Opt_gid_lt) || (token == Opt_egid_lt)) + entry->gid_op = &gid_lt; + fallthrough; + case Opt_gid_eq: + case Opt_egid_eq: + eid_token = (token == Opt_egid_eq) || + (token == Opt_egid_gt) || + (token == Opt_egid_lt); + + ima_log_string_op(ab, eid_token ? "egid" : "gid", + args[0].from, token); + + if (gid_valid(entry->gid)) { + result = -EINVAL; + break; + } + + result = kstrtoul(args[0].from, 10, &lnum); + if (!result) { + entry->gid = make_kgid(current_user_ns(), + (gid_t)lnum); + if (!gid_valid(entry->gid) || + (((gid_t)lnum) != lnum)) + result = -EINVAL; + else + entry->flags |= eid_token + ? IMA_EGID : IMA_GID; } break; case Opt_fowner_gt: @@ -1536,8 +1628,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->fowner_op = &uid_lt; fallthrough; case Opt_fowner_eq: - ima_log_string_op(ab, "fowner", args[0].from, - entry->fowner_op); + ima_log_string_op(ab, "fowner", args[0].from, token); if (uid_valid(entry->fowner)) { result = -EINVAL; @@ -1546,13 +1637,41 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = kstrtoul(args[0].from, 10, &lnum); if (!result) { - entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum); - if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) + entry->fowner = make_kuid(current_user_ns(), + (uid_t)lnum); + if (!uid_valid(entry->fowner) || + (((uid_t)lnum) != lnum)) result = -EINVAL; else entry->flags |= IMA_FOWNER; } break; + case Opt_fgroup_gt: + entry->fgroup_op = &gid_gt; + fallthrough; + case Opt_fgroup_lt: + if (token == Opt_fgroup_lt) + entry->fgroup_op = &gid_lt; + fallthrough; + case Opt_fgroup_eq: + ima_log_string_op(ab, "fgroup", args[0].from, token); + + if (gid_valid(entry->fgroup)) { + result = -EINVAL; + break; + } + + result = kstrtoul(args[0].from, 10, &lnum); + if (!result) { + entry->fgroup = make_kgid(current_user_ns(), + (gid_t)lnum); + if (!gid_valid(entry->fgroup) || + (((gid_t)lnum) != lnum)) + result = -EINVAL; + else + entry->flags |= IMA_FGROUP; + } + break; case Opt_obj_user: ima_log_string(ab, "obj_user", args[0].from); result = ima_lsm_rule_init(entry, args, @@ -1768,9 +1887,11 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos) { loff_t l = *pos; struct ima_rule_entry *entry; + struct list_head *ima_rules_tmp; rcu_read_lock(); - list_for_each_entry_rcu(entry, ima_rules, list) { + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(entry, ima_rules_tmp, list) { if (!l--) { rcu_read_unlock(); return entry; @@ -1789,7 +1910,8 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) rcu_read_unlock(); (*pos)++; - return (&entry->list == ima_rules) ? NULL : entry; + return (&entry->list == &ima_default_rules || + &entry->list == &ima_policy_rules) ? NULL : entry; } void ima_policy_stop(struct seq_file *m, void *v) @@ -1936,6 +2058,28 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_GID) { + snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid)); + if (entry->gid_op == &gid_gt) + seq_printf(m, pt(Opt_gid_gt), tbuf); + else if (entry->gid_op == &gid_lt) + seq_printf(m, pt(Opt_gid_lt), tbuf); + else + seq_printf(m, pt(Opt_gid_eq), tbuf); + seq_puts(m, " "); + } + + if (entry->flags & IMA_EGID) { + snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid)); + if (entry->gid_op == &gid_gt) + seq_printf(m, pt(Opt_egid_gt), tbuf); + else if (entry->gid_op == &gid_lt) + seq_printf(m, pt(Opt_egid_lt), tbuf); + else + seq_printf(m, pt(Opt_egid_eq), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_FOWNER) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); if (entry->fowner_op == &uid_gt) @@ -1947,6 +2091,17 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_FGROUP) { + snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->fgroup)); + if (entry->fgroup_op == &gid_gt) + seq_printf(m, pt(Opt_fgroup_gt), tbuf); + else if (entry->fgroup_op == &gid_lt) + seq_printf(m, pt(Opt_fgroup_lt), tbuf); + else + seq_printf(m, pt(Opt_fgroup_eq), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_VALIDATE_ALGOS) { seq_puts(m, "appraise_algos="); ima_policy_show_appraise_algos(m, entry->allowed_algos); @@ -2014,6 +2169,7 @@ bool ima_appraise_signature(enum kernel_read_file_id id) struct ima_rule_entry *entry; bool found = false; enum ima_hooks func; + struct list_head *ima_rules_tmp; if (id >= READING_MAX_ID) return false; @@ -2021,7 +2177,8 @@ bool ima_appraise_signature(enum kernel_read_file_id id) func = read_idmap[id] ?: FILE_CHECK; rcu_read_lock(); - list_for_each_entry_rcu(entry, ima_rules, list) { + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(entry, ima_rules_tmp, list) { if (entry->action != APPRAISE) continue;