/* * These comparators are needed nowhere outside of ima so just define them here. * This pattern should hopefully never be needed outside of ima.
*/ staticinlinebool vfsuid_gt_kuid(vfsuid_t vfsuid, kuid_t kuid)
{ return __vfsuid_val(vfsuid) > __kuid_val(kuid);
}
struct ima_rule_entry { struct list_head list; int action; unsignedint flags; enum ima_hooks func; int mask; unsignedlong fsmagic;
uuid_t fsuuid;
kuid_t uid;
kgid_t gid;
kuid_t fowner;
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)(vfsuid_t vfsuid, kuid_t rule_uid); /* vfsuid_eq_kuid(), vfsuid_gt_kuid(), vfsuid_lt_kuid() */ bool (*fgroup_op)(vfsgid_t vfsgid, kgid_t rule_gid); /* vfsgid_eq_kgid(), vfsgid_gt_kgid(), vfsgid_lt_kgid() */ int pcr; unsignedint allowed_algos; /* bitfield of allowed hash algorithms */ struct { void *rule; /* LSM file metadata specific */ char *args_p; /* audit value */ int type; /* audit type */
} lsm[MAX_LSM_RULES]; char *fsname; 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;
};
/* * sanity check in case the kernels gains more hash algorithms that can * fit in an unsigned int
*/
static_assert(
8 * sizeof(unsignedint) >= HASH_ALGO__LAST, "The bitfield allowed_algos in ima_rule_entry is too small to contain all the supported hash algorithms, consider using a bigger type");
/* * Without LSM specific knowledge, the default policy can only be * written in terms of .action, .func, .mask, .fsmagic, .uid, .gid, * .fowner, and .fgroup
*/
/* * The minimum rule set to allow for full TCB coverage. Measures all files * opened or mmap for exec and everything read by root. Dangerous because * normal users can easily run the machine out of memory simply building * and running executables.
*/ staticstruct 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, .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},
{.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = CGROUP_SUPER_MAGIC,
.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC,
.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC}
};
/* * strsep() has already replaced all instances of '|' with '\0', * leaving a byte sequence of NUL-terminated strings. Reference each * string with the array of items. * * IMPORTANT: Ownership of the allocated buffer is transferred from * src_copy to the first element in the items array. To free the * buffer, kfree() must only be called on the first element of the * array.
*/ for (i = 0, cur = src_copy; i < count; i++) {
opt_list->items[i] = cur;
cur = strchr(cur, '\0') + 1;
}
return opt_list;
}
staticvoid ima_free_rule_opt_list(struct ima_rule_opt_list *opt_list)
{ if (!opt_list) return;
if (opt_list->count) {
kfree(opt_list->items[0]);
opt_list->count = 0;
}
kfree(opt_list);
}
staticvoid ima_lsm_free_rule(struct ima_rule_entry *entry)
{ int i;
for (i = 0; i < MAX_LSM_RULES; i++) {
ima_filter_rule_free(entry->lsm[i].rule);
kfree(entry->lsm[i].args_p);
}
}
staticvoid ima_free_rule(struct ima_rule_entry *entry)
{ if (!entry) return;
/* * entry->template->fields may be allocated in ima_parse_rule() but that * reference is owned by the corresponding ima_template_desc element in * the defined_templates list and cannot be freed here
*/
kfree(entry->fsname);
ima_free_rule_opt_list(entry->keyrings);
ima_lsm_free_rule(entry);
kfree(entry);
}
/* * Immutable elements are copied over as pointers and data; only * lsm rules can change
*/
nentry = kmemdup(entry, sizeof(*nentry), gfp); if (!nentry) return NULL;
ima_filter_rule_init(nentry->lsm[i].type, Audit_equal,
nentry->lsm[i].args_p,
&nentry->lsm[i].rule,
gfp); if (!nentry->lsm[i].rule)
pr_warn("rule for LSM \'%s\' is undefined\n",
nentry->lsm[i].args_p);
} return nentry;
}
staticint ima_lsm_update_rule(struct ima_rule_entry *entry)
{ int i; struct ima_rule_entry *nentry;
nentry = ima_lsm_copy_rule(entry, GFP_KERNEL); if (!nentry) return -ENOMEM;
list_replace_rcu(&entry->list, &nentry->list);
synchronize_rcu(); /* * ima_lsm_copy_rule() shallow copied all references, except for the * LSM references, from entry to nentry so we only want to free the LSM * references and the entry itself. All other memory references will now * be owned by nentry.
*/ for (i = 0; i < MAX_LSM_RULES; i++)
ima_filter_rule_free(entry->lsm[i].rule);
kfree(entry);
return 0;
}
staticbool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry)
{ int i;
for (i = 0; i < MAX_LSM_RULES; i++) if (entry->lsm[i].args_p) returntrue;
returnfalse;
}
/* * The LSM policy can be reloaded, leaving the IMA LSM based rules referring * to the old, stale LSM policy. Update the IMA LSM based rules to reflect * the reloaded LSM policy.
*/ staticvoid ima_lsm_update_rules(void)
{ struct ima_rule_entry *entry, *e; int result;
list_for_each_entry_safe(entry, e, &ima_policy_rules, list) { if (!ima_rule_contains_lsm_cond(entry)) continue;
result = ima_lsm_update_rule(entry); if (result) {
pr_err("lsm rule update error %d\n", result); return;
}
}
}
int ima_lsm_policy_change(struct notifier_block *nb, unsignedlong event, void *lsm_data)
{ if (event != LSM_POLICY_CHANGE) return NOTIFY_DONE;
ima_lsm_update_rules(); return NOTIFY_OK;
}
/** * ima_match_rule_data - determine whether func_data matches the policy rule * @rule: a pointer to a rule * @func_data: data to match against the measure rule data * @cred: a pointer to a credentials structure for user validation * * Returns true if func_data matches one in the rule, false otherwise.
*/ staticbool ima_match_rule_data(struct ima_rule_entry *rule, constchar *func_data, conststruct cred *cred)
{ conststruct ima_rule_opt_list *opt_list = NULL; bool matched = false;
size_t i;
if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) returnfalse;
switch (rule->func) { case KEY_CHECK: if (!rule->keyrings) returntrue;
opt_list = rule->keyrings; break; case CRITICAL_DATA: if (!rule->label) returntrue;
for (i = 0; i < opt_list->count; i++) { if (!strcmp(opt_list->items[i], func_data)) {
matched = true; break;
}
}
return matched;
}
/** * ima_match_rules - determine whether an inode matches the policy rule. * @rule: a pointer to a 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 * @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 * * Returns true on rule match, false on failure.
*/ staticbool ima_match_rules(struct ima_rule_entry *rule, struct mnt_idmap *idmap, struct inode *inode, conststruct cred *cred, struct lsm_prop *prop, enum ima_hooks func, int mask, constchar *func_data)
{ int i; bool result = false; struct ima_rule_entry *lsm_rule = rule; bool rule_reinitialized = false;
switch (func) { case KEY_CHECK: case CRITICAL_DATA: return ((rule->func == func) &&
ima_match_rule_data(rule, func_data, cred)); default: break;
}
if ((rule->flags & IMA_MASK) &&
(rule->mask != mask && func != POST_SETATTR)) returnfalse; if ((rule->flags & IMA_INMASK) &&
(!(rule->mask & mask) && func != POST_SETATTR)) returnfalse; if ((rule->flags & IMA_FSMAGIC)
&& rule->fsmagic != inode->i_sb->s_magic) returnfalse; if ((rule->flags & IMA_FSNAME)
&& strcmp(rule->fsname, inode->i_sb->s_type->name)) returnfalse; if ((rule->flags & IMA_FSUUID) &&
!uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid)) returnfalse; if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) returnfalse; if (rule->flags & IMA_EUID) { if (has_capability_noaudit(current, CAP_SETUID)) { if (!rule->uid_op(cred->euid, rule->uid)
&& !rule->uid_op(cred->suid, rule->uid)
&& !rule->uid_op(cred->uid, rule->uid)) returnfalse;
} elseif (!rule->uid_op(cred->euid, rule->uid)) returnfalse;
} if ((rule->flags & IMA_GID) && !rule->gid_op(cred->gid, rule->gid)) returnfalse; 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)) returnfalse;
} elseif (!rule->gid_op(cred->egid, rule->gid)) returnfalse;
} if ((rule->flags & IMA_FOWNER) &&
!rule->fowner_op(i_uid_into_vfsuid(idmap, inode),
rule->fowner)) returnfalse; if ((rule->flags & IMA_FGROUP) &&
!rule->fgroup_op(i_gid_into_vfsgid(idmap, inode),
rule->fgroup)) returnfalse; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; struct lsm_prop inode_prop = { };
if (!lsm_rule->lsm[i].rule) { if (!lsm_rule->lsm[i].args_p) continue; else returnfalse;
}
retry: switch (i) { case LSM_OBJ_USER: case LSM_OBJ_ROLE: case LSM_OBJ_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(prop, lsm_rule->lsm[i].type,
Audit_equal,
lsm_rule->lsm[i].rule); break; default: break;
}
if (rc == -ESTALE && !rule_reinitialized) {
lsm_rule = ima_lsm_copy_rule(rule, GFP_ATOMIC); if (lsm_rule) {
rule_reinitialized = true; goto retry;
}
} if (!rc) {
result = false; goto out;
}
}
result = true;
out: if (rule_reinitialized) { for (i = 0; i < MAX_LSM_RULES; i++)
ima_filter_rule_free(lsm_rule->lsm[i].rule);
kfree(lsm_rule);
} return result;
}
/* * In addition to knowing that we need to appraise the file in general, * we need to differentiate between calling hooks, for hook specific rules.
*/ staticint get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
{ if (!(rule->flags & IMA_FUNC)) return IMA_FILE_APPRAISE;
switch (func) { case MMAP_CHECK: case MMAP_CHECK_REQPROT: return IMA_MMAP_APPRAISE; case BPRM_CHECK: return IMA_BPRM_APPRAISE; case CREDS_CHECK: return IMA_CREDS_APPRAISE; case FILE_CHECK: case POST_SETATTR: return IMA_FILE_APPRAISE; case MODULE_CHECK ... MAX_CHECK - 1: default: return IMA_READ_APPRAISE;
}
}
/** * ima_match_policy - decision based on LSM and other conditions * @idmap: idmap of the mount the inode was found from * @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 * @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) * @pcr: set the pcr to extend * @template_desc: the template that should be used for this rule * @func_data: func specific data, may be NULL * @allowed_algos: allowlist of hash algorithms for the IMA xattr * * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) * conditions. * * Since the IMA policy may be updated multiple times we need to lock the * list when walking it. Reads are many orders of magnitude more numerous * than writes so ima_match_policy() is classical RCU candidate.
*/ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode, conststruct cred *cred, struct lsm_prop *prop, enum ima_hooks func, int mask, int flags, int *pcr, struct ima_template_desc **template_desc, constchar *func_data, unsignedint *allowed_algos)
{ 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();
if ((pcr) && (entry->flags & IMA_PCR))
*pcr = entry->pcr;
if (template_desc && entry->template)
*template_desc = entry->template;
if (!actmask) break;
}
rcu_read_unlock();
return action;
}
/** * ima_update_policy_flags() - Update global IMA variables * * Update ima_policy_flag and ima_setxattr_allowed_hash_algorithms * based on the currently loaded policy. * * With ima_policy_flag, the decision to short circuit out of a function * or not call the function in the first place can be made earlier. * * With ima_setxattr_allowed_hash_algorithms, the policy can restrict the * set of hash algorithms accepted when updating the security.ima xattr of * a file. * * Context: called after a policy update and at system initialization.
*/ 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();
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 * performance impact on setxattr(). As a consequence, only one * SETXATTR_CHECK can be active at a given time. * Because we want to preserve that property, we set out to use * atomic_cmpxchg. Either: * - the atomic was non-zero: a setxattr hash policy is * already enforced, we do nothing * - the atomic was zero: no setxattr policy was set, enable * the setxattr hash policy
*/ if (entry->func == SETXATTR_CHECK) {
atomic_cmpxchg(&ima_setxattr_allowed_hash_algorithms,
0, entry->allowed_algos); /* SETXATTR_CHECK doesn't impact ima_policy_flag */ continue;
}
if (entry->action & IMA_DO_MASK)
new_policy_flag |= entry->action;
}
rcu_read_unlock();
ima_appraise |= (build_ima_appraise | temp_ima_appraise); if (!ima_appraise)
new_policy_flag &= ~IMA_APPRAISE;
/* Convert each policy string rules to struct ima_rule_entry format */ for (rules = arch_rules, i = 0; *rules != NULL; rules++) { char rule[255]; int result;
/** * ima_init_policy - initialize the default measure rules. * * ima_rules points to either the ima_default_rules or the new ima_policy_rules.
*/ void __init ima_init_policy(void)
{ int build_appraise_entries, arch_entries;
/* if !ima_policy, we load NO default rules */ if (ima_policy)
add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
IMA_DEFAULT_POLICY);
switch (ima_policy) { case ORIGINAL_TCB:
add_rules(original_measurement_rules,
ARRAY_SIZE(original_measurement_rules),
IMA_DEFAULT_POLICY); break; case DEFAULT_TCB:
add_rules(default_measurement_rules,
ARRAY_SIZE(default_measurement_rules),
IMA_DEFAULT_POLICY); break; default: break;
}
/* * Based on runtime secure boot flags, insert arch specific measurement * and appraise rules requiring file signatures for both the initial * and custom policies, prior to other appraise rules. * (Highest priority)
*/
arch_entries = ima_init_arch_policy(); if (!arch_entries)
pr_info("No architecture policies found\n"); else
add_rules(arch_policy_entry, arch_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
/* * Insert the builtin "secure_boot" policy rules requiring file * signatures, prior to other appraise rules.
*/ if (ima_use_secure_boot)
add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
IMA_DEFAULT_POLICY);
/* * Insert the build time appraise rules requiring file signatures * for both the initial and custom policies, prior to other appraise * rules. As the secure boot rules includes all of the build time * rules, include either one or the other set of rules, but not both.
*/
build_appraise_entries = ARRAY_SIZE(build_appraise_rules); if (build_appraise_entries) { if (ima_use_secure_boot)
add_rules(build_appraise_rules, build_appraise_entries,
IMA_CUSTOM_POLICY); else
add_rules(build_appraise_rules, build_appraise_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
}
if (ima_use_appraise_tcb)
add_rules(default_appraise_rules,
ARRAY_SIZE(default_appraise_rules),
IMA_DEFAULT_POLICY);
if (ima_use_critical_data)
add_rules(critical_data_rules,
ARRAY_SIZE(critical_data_rules),
IMA_DEFAULT_POLICY);
/* Make sure we have a valid policy, at least containing some rules. */ int ima_check_policy(void)
{ if (list_empty(&ima_temp_rules)) return -EINVAL; return 0;
}
/** * ima_update_policy - update default_rules with new measure rules * * Called on file .release to update the default rules with a complete new * policy. What we do here is to splice ima_policy_rules and ima_temp_rules so * they make a queue. The policy may be updated multiple times and this is the * RCU updater. * * Policy rules are never deleted so ima_policy_flag gets zeroed only once when * we switch from the default policy to user defined.
*/ void ima_update_policy(void)
{ struct list_head *policy = &ima_policy_rules;
rcu_assign_pointer(ima_rules, policy); /* * IMA architecture specific policy rules are specified * as strings and converted to an array of ima_entry_rules * on boot. After loading a custom policy, free the * architecture specific rules stored as an array.
*/
kfree(arch_policy_entry);
}
ima_update_policy_flags();
/* Custom IMA policy has been loaded */
ima_process_queued_keys();
}
staticint ima_lsm_rule_init(struct ima_rule_entry *entry,
substring_t *args, int lsm_rule, int audit_type)
{ int result;
if (entry->lsm[lsm_rule].rule) return -EINVAL;
entry->lsm[lsm_rule].args_p = match_strdup(args); if (!entry->lsm[lsm_rule].args_p) return -ENOMEM;
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,
GFP_KERNEL); if (!entry->lsm[lsm_rule].rule) {
pr_warn("rule for LSM \'%s\' is undefined\n",
entry->lsm[lsm_rule].args_p);
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;
} else
result = 0;
}
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); 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); break; default:
audit_log_format(ab, "%s=", key);
}
audit_log_format(ab, "%s ", value);
} staticvoid ima_log_string(struct audit_buffer *ab, char *key, char *value)
{
ima_log_string_op(ab, key, value, Opt_err);
}
/* * Validating the appended signature included in the measurement list requires * the file hash calculated without the appended signature (i.e., the 'd-modsig' * field). Therefore, notify the user if they have the 'modsig' field but not * the 'd-modsig' field in the template.
*/ staticvoid check_template_modsig(conststruct ima_template_desc *template)
{ #define MSG "template with 'modsig' field also needs 'd-modsig' field\n" bool has_modsig, has_dmodsig; staticbool checked; int i;
/* We only need to notify the user once. */ if (checked) return;
has_modsig = has_dmodsig = false; for (i = 0; i < template->num_fields; i++) { if (!strcmp(template->fields[i]->field_id, "modsig"))
has_modsig = true; elseif (!strcmp(template->fields[i]->field_id, "d-modsig"))
has_dmodsig = true;
}
if (has_modsig && !has_dmodsig)
pr_notice(MSG);
checked = true; #undef MSG
}
/* * Warn if the template does not contain the given field.
*/ staticvoid check_template_field(conststruct ima_template_desc *template, constchar *field, constchar *msg)
{ int i;
for (i = 0; i < template->num_fields; i++) if (!strcmp(template->fields[i]->field_id, field)) return;
pr_notice_once("%s", msg);
}
staticbool ima_validate_rule(struct ima_rule_entry *entry)
{ /* Ensure that the action is set and is compatible with the flags */ if (entry->action == UNKNOWN) returnfalse;
if (entry->action != MEASURE && entry->flags & IMA_PCR) returnfalse;
/* * The IMA_FUNC bit must be set if and only if there's a valid hook * function specified, and vice versa. Enforcing this property allows * for the NONE case below to validate a rule without an explicit hook * function.
*/ if (((entry->flags & IMA_FUNC) && entry->func == NONE) ||
(!(entry->flags & IMA_FUNC) && entry->func != NONE)) returnfalse;
/* * Ensure that the hook function is compatible with the other * components of the rule
*/ switch (entry->func) { case NONE: case FILE_CHECK: case MMAP_CHECK: case MMAP_CHECK_REQPROT: case BPRM_CHECK: case CREDS_CHECK: case POST_SETATTR: case FIRMWARE_CHECK: case POLICY_CHECK: 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_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED)) returnfalse;
if (ima_rule_contains_lsm_cond(entry)) returnfalse;
break; case SETXATTR_CHECK: /* any action other than APPRAISE is unsupported */ if (entry->action != APPRAISE) returnfalse;
/* SETXATTR_CHECK requires an appraise_algos parameter */ if (!(entry->flags & IMA_VALIDATE_ALGOS)) returnfalse;
/* * full policies are not supported, they would have too * much of a performance impact
*/ if (entry->flags & ~(IMA_FUNC | IMA_VALIDATE_ALGOS)) returnfalse;
break; default: returnfalse;
}
/* Ensure that combinations of flags are compatible with each other */ if (entry->flags & IMA_CHECK_BLACKLIST &&
!(entry->flags & IMA_DIGSIG_REQUIRED)) returnfalse;
/* * Unlike for regular IMA 'appraise' policy rules where security.ima * xattr may contain either a file hash or signature, the security.ima * xattr for fsverity must contain a file signature (sigv3). Ensure * that 'appraise' rules for fsverity require file signatures by * checking the IMA_DIGSIG_REQUIRED flag is set.
*/ if (entry->action == APPRAISE &&
(entry->flags & IMA_VERITY_REQUIRED) &&
!(entry->flags & IMA_DIGSIG_REQUIRED)) returnfalse;
returntrue;
}
staticunsignedint ima_parse_appraise_algos(char *arg)
{ unsignedint res = 0; int idx; char *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:
entry->fowner_op = &vfsuid_gt_kuid;
fallthrough; case Opt_fowner_lt: if (token == Opt_fowner_lt)
entry->fowner_op = &vfsuid_lt_kuid;
fallthrough; case Opt_fowner_eq:
ima_log_string_op(ab, "fowner", args[0].from, token);
if (uid_valid(entry->fowner)) {
result = -EINVAL; break;
}
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))
result = -EINVAL; else
entry->flags |= IMA_FOWNER;
} break; case Opt_fgroup_gt:
entry->fgroup_op = &vfsgid_gt_kgid;
fallthrough; case Opt_fgroup_lt: if (token == Opt_fgroup_lt)
entry->fgroup_op = &vfsgid_lt_kgid;
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,
LSM_OBJ_USER,
AUDIT_OBJ_USER); break; case Opt_obj_role:
ima_log_string(ab, "obj_role", args[0].from);
result = ima_lsm_rule_init(entry, args,
LSM_OBJ_ROLE,
AUDIT_OBJ_ROLE); break; case Opt_obj_type:
ima_log_string(ab, "obj_type", args[0].from);
result = ima_lsm_rule_init(entry, args,
LSM_OBJ_TYPE,
AUDIT_OBJ_TYPE); break; case Opt_subj_user:
ima_log_string(ab, "subj_user", args[0].from);
result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_USER,
AUDIT_SUBJ_USER); break; case Opt_subj_role:
ima_log_string(ab, "subj_role", args[0].from);
result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_ROLE,
AUDIT_SUBJ_ROLE); break; case Opt_subj_type:
ima_log_string(ab, "subj_type", args[0].from);
result = ima_lsm_rule_init(entry, args,
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE); break; case Opt_digest_type:
ima_log_string(ab, "digest_type", args[0].from); if (entry->flags & IMA_DIGSIG_REQUIRED)
result = -EINVAL; elseif ((strcmp(args[0].from, "verity")) == 0)
entry->flags |= IMA_VERITY_REQUIRED; else
result = -EINVAL; break; case Opt_appraise_type:
ima_log_string(ab, "appraise_type", args[0].from);
if ((strcmp(args[0].from, "imasig")) == 0) { if (entry->flags & IMA_VERITY_REQUIRED)
result = -EINVAL; else
entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
} elseif (strcmp(args[0].from, "sigv3") == 0) { /* Only fsverity supports sigv3 for now */ if (entry->flags & IMA_VERITY_REQUIRED)
entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST; else
result = -EINVAL;
} elseif (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
strcmp(args[0].from, "imasig|modsig") == 0) { if (entry->flags & IMA_VERITY_REQUIRED)
result = -EINVAL; else
entry->flags |= IMA_DIGSIG_REQUIRED |
IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST;
} else {
result = -EINVAL;
} break; case Opt_appraise_flag:
ima_log_string(ab, "appraise_flag", args[0].from); break; case Opt_appraise_algos:
ima_log_string(ab, "appraise_algos", args[0].from);
if (entry->allowed_algos) {
result = -EINVAL; break;
}
entry->allowed_algos =
ima_parse_appraise_algos(args[0].from); /* invalid or empty list of algorithms */ if (!entry->allowed_algos) {
result = -EINVAL; break;
}
entry->flags |= IMA_VALIDATE_ALGOS;
break; case Opt_permit_directio:
entry->flags |= IMA_PERMIT_DIRECTIO; break; case Opt_pcr:
ima_log_string(ab, "pcr", args[0].from);
result = kstrtoint(args[0].from, 10, &entry->pcr); if (result || INVALID_PCR(entry->pcr))
result = -EINVAL; else
entry->flags |= IMA_PCR;
break; case Opt_template:
ima_log_string(ab, "template", args[0].from); if (entry->action != MEASURE) {
result = -EINVAL; break;
}
template_desc = lookup_template_desc(args[0].from); if (!template_desc || entry->template) {
result = -EINVAL; break;
}
/* * template_desc_init_fields() does nothing if * the template is already initialised, so * it's safe to do this unconditionally
*/
template_desc_init_fields(template_desc->fmt,
&(template_desc->fields),
&(template_desc->num_fields));
entry->template = template_desc; break; case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL; break;
}
} if (!result && !ima_validate_rule(entry))
result = -EINVAL; elseif (entry->action == APPRAISE)
temp_ima_appraise |= ima_appraise_flag(entry->func);
/** * ima_parse_add_rule - add a rule to ima_policy_rules * @rule: ima measurement policy rule * * Avoid locking by allowing just one writer at a time in ima_write_policy() * Returns the length of the rule parsed, an error code on failure
*/
ssize_t ima_parse_add_rule(char *rule)
{ staticconstchar op[] = "update_policy"; char *p; struct ima_rule_entry *entry;
ssize_t result, len; int audit_info = 0;
p = strsep(&rule, "\n");
len = strlen(p) + 1;
p += strspn(p, " \t");
result = ima_parse_rule(p, entry); if (result) {
ima_free_rule(entry);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
NULL, op, "invalid-policy", result,
audit_info); return result;
}
list_add_tail(&entry->list, &ima_temp_rules);
return len;
}
/** * ima_delete_rules() - called to cleanup invalid in-flight policy. * * We don't need locking as we operate on the temp list, which is * different from the active one. There is also only one user of * ima_delete_rules() at a time.
*/ void ima_delete_rules(void)
{ struct ima_rule_entry *entry, *tmp;
for (idx = 0; idx < HASH_ALGO__LAST; idx++) { if (!(allowed_hashes & (1U << idx))) continue;
/* only add commas if the list contains multiple entries */ if (list_size++)
seq_puts(m, ",");
seq_puts(m, hash_algo_name[idx]);
}
}
int ima_policy_show(struct seq_file *m, void *v)
{ struct ima_rule_entry *entry = v; int i; char tbuf[64] = {0,}; int offset = 0;
rcu_read_lock();
/* Do not print rules with inactive LSM labels */ for (i = 0; i < MAX_LSM_RULES; i++) { if (entry->lsm[i].args_p && !entry->lsm[i].rule) {
rcu_read_unlock(); return 0;
}
}
if (entry->action & MEASURE)
seq_puts(m, pt(Opt_measure)); if (entry->action & DONT_MEASURE)
seq_puts(m, pt(Opt_dont_measure)); if (entry->action & APPRAISE)
seq_puts(m, pt(Opt_appraise)); if (entry->action & DONT_APPRAISE)
seq_puts(m, pt(Opt_dont_appraise)); if (entry->action & AUDIT)
seq_puts(m, pt(Opt_audit)); if (entry->action & HASH)
seq_puts(m, pt(Opt_hash)); if (entry->action & DONT_HASH)
seq_puts(m, pt(Opt_dont_hash));
seq_puts(m, " ");
if (entry->flags & IMA_FUNC)
policy_func_show(m, entry->func);
if ((entry->flags & IMA_MASK) || (entry->flags & IMA_INMASK)) { if (entry->flags & IMA_MASK)
offset = 1; if (entry->mask & MAY_EXEC)
seq_printf(m, pt(Opt_mask), mt(mask_exec) + offset); if (entry->mask & MAY_WRITE)
seq_printf(m, pt(Opt_mask), mt(mask_write) + offset); if (entry->mask & MAY_READ)
seq_printf(m, pt(Opt_mask), mt(mask_read) + offset); if (entry->mask & MAY_APPEND)
seq_printf(m, pt(Opt_mask), mt(mask_append) + offset);
seq_puts(m, " ");
}
for (i = 0; i < MAX_LSM_RULES; i++) { if (entry->lsm[i].rule) { switch (i) { case LSM_OBJ_USER:
seq_printf(m, pt(Opt_obj_user),
entry->lsm[i].args_p); break; case LSM_OBJ_ROLE:
seq_printf(m, pt(Opt_obj_role),
entry->lsm[i].args_p); break; case LSM_OBJ_TYPE:
seq_printf(m, pt(Opt_obj_type),
entry->lsm[i].args_p); break; case LSM_SUBJ_USER:
seq_printf(m, pt(Opt_subj_user),
entry->lsm[i].args_p); break; case LSM_SUBJ_ROLE:
seq_printf(m, pt(Opt_subj_role),
entry->lsm[i].args_p); break; case LSM_SUBJ_TYPE:
seq_printf(m, pt(Opt_subj_type),
entry->lsm[i].args_p); break;
}
seq_puts(m, " ");
}
} if (entry->template)
seq_printf(m, "template=%s ", entry->template->name); if (entry->flags & IMA_DIGSIG_REQUIRED) { if (entry->flags & IMA_VERITY_REQUIRED)
seq_puts(m, "appraise_type=sigv3 "); elseif (entry->flags & IMA_MODSIG_ALLOWED)
seq_puts(m, "appraise_type=imasig|modsig "); else
seq_puts(m, "appraise_type=imasig ");
} if (entry->flags & IMA_VERITY_REQUIRED)
seq_puts(m, "digest_type=verity "); if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio ");
rcu_read_unlock();
seq_puts(m, "\n"); return 0;
} #endif/* CONFIG_IMA_READ_POLICY */
#ifdefined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) /* * ima_appraise_signature: whether IMA will appraise a given function using * an IMA digital signature. This is restricted to cases where the kernel * has a set of built-in trusted keys in order to avoid an attacker simply * loading additional keys.
*/ 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) returnfalse;
if (id == READING_KEXEC_IMAGE && !(ima_appraise & IMA_APPRAISE_ENFORCE)
&& security_locked_down(LOCKDOWN_KEXEC)) returnfalse;
/* * A generic entry will match, but otherwise require that it * match the func we're looking for
*/ if (entry->func && entry->func != func) continue;
/* * We require this to be a digital signature, not a raw IMA * hash.
*/ if (entry->flags & IMA_DIGSIG_REQUIRED)
found = true;
/* * We've found a rule that matches, so break now even if it * didn't require a digital signature - a later rule that does * won't override it, so would be a false positive.
*/ break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.