diff options
Diffstat (limited to 'security/integrity/ima/ima_policy.c')
| -rw-r--r-- | security/integrity/ima/ima_policy.c | 126 |
1 files changed, 86 insertions, 40 deletions
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index c9b3bd8f1bb9..8fbd8755f5bc 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -38,6 +38,7 @@ #define IMA_GID 0x2000 #define IMA_EGID 0x4000 #define IMA_FGROUP 0x8000 +#define IMA_FS_SUBTYPE 0x10000 #define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -45,11 +46,12 @@ #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ #define DONT_APPRAISE 0x0008 #define AUDIT 0x0040 +#define DONT_AUDIT 0x0080 #define HASH 0x0100 #define DONT_HASH 0x0200 #define INVALID_PCR(a) (((a) < 0) || \ - (a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8)) + (a) >= (sizeof_field(struct ima_iint_cache, measured_pcrs) * 8)) int ima_policy_flag; static int temp_ima_appraise; @@ -68,7 +70,7 @@ enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY }; struct ima_rule_opt_list { size_t count; - char *items[]; + char *items[] __counted_by(count); }; /* @@ -119,6 +121,7 @@ struct ima_rule_entry { int type; /* audit type */ } lsm[MAX_LSM_RULES]; char *fsname; + char *fs_subtype; struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */ struct ima_rule_opt_list *label; /* Measure data grouped under this label */ struct ima_template_desc *template; @@ -148,7 +151,8 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = { {.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, - {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .func = FILE_CHECK, + .flags = IMA_FSMAGIC | IMA_FUNC}, {.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, @@ -240,7 +244,8 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { {.action = APPRAISE, .func = MODULE_CHECK, - .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | + IMA_CHECK_BLACKLIST}, {.action = APPRAISE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, {.action = APPRAISE, .func = KEXEC_KERNEL_CHECK, @@ -342,6 +347,7 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src) kfree(src_copy); return ERR_PTR(-ENOMEM); } + opt_list->count = count; /* * strsep() has already replaced all instances of '|' with '\0', @@ -357,7 +363,6 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src) opt_list->items[i] = cur; cur = strchr(cur, '\0') + 1; } - opt_list->count = count; return opt_list; } @@ -396,12 +401,14 @@ static void ima_free_rule(struct ima_rule_entry *entry) * the defined_templates list and cannot be freed here */ kfree(entry->fsname); + kfree(entry->fs_subtype); ima_free_rule_opt_list(entry->keyrings); ima_lsm_free_rule(entry); kfree(entry); } -static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) +static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry, + gfp_t gfp) { struct ima_rule_entry *nentry; int i; @@ -410,7 +417,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) * Immutable elements are copied over as pointers and data; only * lsm rules can change */ - nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL); + nentry = kmemdup(entry, sizeof(*nentry), gfp); if (!nentry) return NULL; @@ -425,7 +432,8 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) ima_filter_rule_init(nentry->lsm[i].type, Audit_equal, nentry->lsm[i].args_p, - &nentry->lsm[i].rule); + &nentry->lsm[i].rule, + gfp); if (!nentry->lsm[i].rule) pr_warn("rule for LSM \'%s\' is undefined\n", nentry->lsm[i].args_p); @@ -438,7 +446,7 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry) int i; struct ima_rule_entry *nentry; - nentry = ima_lsm_copy_rule(entry); + nentry = ima_lsm_copy_rule(entry, GFP_KERNEL); if (!nentry) return -ENOMEM; @@ -555,7 +563,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 @@ -565,7 +573,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; @@ -598,6 +606,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule, if ((rule->flags & IMA_FSNAME) && strcmp(rule->fsname, inode->i_sb->s_type->name)) return false; + if (rule->flags & IMA_FS_SUBTYPE) { + if (!inode->i_sb->s_subtype) + return false; + if (strcmp(rule->fs_subtype, inode->i_sb->s_subtype)) + return false; + } if ((rule->flags & IMA_FSUUID) && !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid)) return false; @@ -633,7 +647,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) @@ -647,15 +661,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; @@ -664,13 +679,13 @@ retry: } if (rc == -ESTALE && !rule_reinitialized) { - lsm_rule = ima_lsm_copy_rule(rule); + lsm_rule = ima_lsm_copy_rule(rule, GFP_ATOMIC); if (lsm_rule) { rule_reinitialized = true; goto retry; } } - if (!rc) { + if (rc <= 0) { result = false; goto out; } @@ -718,7 +733,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) @@ -735,8 +750,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) { @@ -754,7 +769,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; @@ -1060,10 +1075,10 @@ void ima_update_policy(void) enum policy_opt { Opt_measure, Opt_dont_measure, Opt_appraise, Opt_dont_appraise, - Opt_audit, Opt_hash, Opt_dont_hash, + Opt_audit, Opt_dont_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_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fs_subtype, 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, @@ -1082,6 +1097,7 @@ static const match_table_t policy_tokens = { {Opt_appraise, "appraise"}, {Opt_dont_appraise, "dont_appraise"}, {Opt_audit, "audit"}, + {Opt_dont_audit, "dont_audit"}, {Opt_hash, "hash"}, {Opt_dont_hash, "dont_hash"}, {Opt_obj_user, "obj_user=%s"}, @@ -1094,6 +1110,7 @@ static const match_table_t policy_tokens = { {Opt_mask, "mask=%s"}, {Opt_fsmagic, "fsmagic=%s"}, {Opt_fsname, "fsname=%s"}, + {Opt_fs_subtype, "fs_subtype=%s"}, {Opt_fsuuid, "fsuuid=%s"}, {Opt_uid_eq, "uid=%s"}, {Opt_euid_eq, "euid=%s"}, @@ -1140,7 +1157,8 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, entry->lsm[lsm_rule].type = audit_type; result = ima_filter_rule_init(entry->lsm[lsm_rule].type, Audit_equal, entry->lsm[lsm_rule].args_p, - &entry->lsm[lsm_rule].rule); + &entry->lsm[lsm_rule].rule, + GFP_KERNEL); if (!entry->lsm[lsm_rule].rule) { pr_warn("rule for LSM \'%s\' is undefined\n", entry->lsm[lsm_rule].args_p); @@ -1277,10 +1295,11 @@ 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_GID | IMA_EGID | + IMA_FSNAME | IMA_FS_SUBTYPE | + IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS | - IMA_VERITY_REQUIRED)) + IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED)) return false; break; @@ -1290,7 +1309,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_GID | IMA_EGID | + IMA_FSNAME | IMA_FS_SUBTYPE | + IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) @@ -1303,7 +1323,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_GID | IMA_EGID | + IMA_PCR | IMA_FSNAME | IMA_FS_SUBTYPE | + IMA_GID | IMA_EGID | IMA_FGROUP)) return false; @@ -1355,7 +1376,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) /* Ensure that combinations of flags are compatible with each other */ if (entry->flags & IMA_CHECK_BLACKLIST && - !(entry->flags & IMA_MODSIG_ALLOWED)) + !(entry->flags & IMA_DIGSIG_REQUIRED)) return false; /* @@ -1427,7 +1448,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) int token; unsigned long lnum; - if (result < 0) + if (result < 0 || *p == '#') /* ignore suffixed comment */ break; if ((*p == '\0') || (*p == ' ') || (*p == '\t')) continue; @@ -1473,6 +1494,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->action = AUDIT; break; + case Opt_dont_audit: + ima_log_string(ab, "action", "dont_audit"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = DONT_AUDIT; + break; case Opt_hash: ima_log_string(ab, "action", "hash"); @@ -1582,6 +1611,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = 0; entry->flags |= IMA_FSNAME; break; + case Opt_fs_subtype: + ima_log_string(ab, "fs_subtype", args[0].from); + + if (entry->fs_subtype) { + result = -EINVAL; + break; + } + + entry->fs_subtype = kstrdup(args[0].from, GFP_KERNEL); + if (!entry->fs_subtype) { + result = -ENOMEM; + break; + } + result = 0; + entry->flags |= IMA_FS_SUBTYPE; + break; case Opt_keyrings: ima_log_string(ab, "keyrings", args[0].from); @@ -1803,11 +1848,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) if (entry->flags & IMA_VERITY_REQUIRED) result = -EINVAL; else - entry->flags |= IMA_DIGSIG_REQUIRED; + entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST; } else if (strcmp(args[0].from, "sigv3") == 0) { /* Only fsverity supports sigv3 for now */ if (entry->flags & IMA_VERITY_REQUIRED) - entry->flags |= IMA_DIGSIG_REQUIRED; + entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST; else result = -EINVAL; } else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && @@ -1816,18 +1861,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = -EINVAL; else entry->flags |= IMA_DIGSIG_REQUIRED | - IMA_MODSIG_ALLOWED; + IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST; } else { result = -EINVAL; } break; case Opt_appraise_flag: ima_log_string(ab, "appraise_flag", args[0].from); - if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && - strstr(args[0].from, "blacklist")) - entry->flags |= IMA_CHECK_BLACKLIST; - else - result = -EINVAL; break; case Opt_appraise_algos: ima_log_string(ab, "appraise_algos", args[0].from); @@ -2097,6 +2137,8 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, pt(Opt_dont_appraise)); if (entry->action & AUDIT) seq_puts(m, pt(Opt_audit)); + if (entry->action & DONT_AUDIT) + seq_puts(m, pt(Opt_dont_audit)); if (entry->action & HASH) seq_puts(m, pt(Opt_hash)); if (entry->action & DONT_HASH) @@ -2133,6 +2175,12 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_FS_SUBTYPE) { + snprintf(tbuf, sizeof(tbuf), "%s", entry->fs_subtype); + seq_printf(m, pt(Opt_fs_subtype), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_KEYRINGS) { seq_puts(m, "keyrings="); ima_show_rule_opt_list(m, entry->keyrings); @@ -2271,8 +2319,6 @@ int ima_policy_show(struct seq_file *m, void *v) } if (entry->flags & IMA_VERITY_REQUIRED) seq_puts(m, "digest_type=verity "); - if (entry->flags & IMA_CHECK_BLACKLIST) - seq_puts(m, "appraise_flag=check_blacklist "); if (entry->flags & IMA_PERMIT_DIRECTIO) seq_puts(m, "permit_directio "); rcu_read_unlock(); |
