@@ -29,7 +29,7 @@ Description:
[obj_user=] [obj_role=] [obj_type=]]
option: [digest_type=] [template=] [permit_directio]
[appraise_type=] [appraise_flag=]
- [appraise_algos=] [keyrings=]
+ [appraise_algos=] [keyrings=] [digest_cache=]
base:
func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
[FIRMWARE_CHECK]
@@ -77,6 +77,9 @@ Description:
For example, "sha256,sha512" to only accept to appraise
files where the security.ima xattr was hashed with one
of these two algorithms.
+ digest_cache:= [content]
+ "content" means that the digest cache is used only
+ for file content measurement and/or appraisal.
default policy:
# PROC_SUPER_MAGIC
@@ -260,7 +260,8 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
const struct cred *cred, u32 secid, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
- const char *func_data, unsigned int *allowed_algos);
+ const char *func_data, unsigned int *allowed_algos,
+ u64 *digest_cache_mask);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
@@ -291,7 +292,8 @@ 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,
struct ima_template_desc **template_desc,
- const char *func_data, unsigned int *allowed_algos);
+ const char *func_data, unsigned int *allowed_algos,
+ u64 *digest_cache_mask);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flags(void);
@@ -173,6 +173,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* @template_desc: pointer filled in if matched measure policy sets template=
* @func_data: func specific data, may be NULL
* @allowed_algos: allowlist of hash algorithms for the IMA xattr
+ * @digest_cache_mask: For which actions and purpose the digest cache is usable
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
@@ -190,7 +191,8 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
const struct cred *cred, u32 secid, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
- const char *func_data, unsigned int *allowed_algos)
+ const char *func_data, unsigned int *allowed_algos,
+ u64 *digest_cache_mask)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
@@ -198,7 +200,7 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
return ima_match_policy(idmap, inode, cred, secid, func, mask,
flags, pcr, template_desc, func_data,
- allowed_algos);
+ allowed_algos, digest_cache_mask);
}
static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
@@ -81,7 +81,7 @@ int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
security_current_getsecid_subj(&secid);
return ima_match_policy(idmap, inode, current_cred(), secid,
func, mask, IMA_APPRAISE | IMA_HASH, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -231,7 +231,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
*/
action = ima_get_action(file_mnt_idmap(file), inode, cred, secid,
mask, func, &pcr, &template_desc, NULL,
- &allowed_algos);
+ &allowed_algos, NULL);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK ||
func == MMAP_CHECK_REQPROT) &&
(ima_policy_flag & IMA_MEASURE));
@@ -473,11 +473,11 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
inode = file_inode(vma->vm_file);
action = ima_get_action(file_mnt_idmap(vma->vm_file), inode,
current_cred(), secid, MAY_EXEC, MMAP_CHECK,
- &pcr, &template, NULL, NULL);
+ &pcr, &template, NULL, NULL, NULL);
action |= ima_get_action(file_mnt_idmap(vma->vm_file), inode,
current_cred(), secid, MAY_EXEC,
MMAP_CHECK_REQPROT, &pcr, &template, NULL,
- NULL);
+ NULL, NULL);
/* Is the mmap'ed file in policy? */
if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
@@ -958,7 +958,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
security_current_getsecid_subj(&secid);
action = ima_get_action(idmap, inode, current_cred(),
secid, 0, func, &pcr, &template,
- func_data, NULL);
+ func_data, NULL, NULL);
if (!(action & IMA_MEASURE) && !digest)
return -ENOENT;
}
@@ -122,6 +122,7 @@ struct ima_rule_entry {
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;
+ u64 digest_cache_mask; /* For which actions and purpose the digest cache is usable */
};
/*
@@ -726,6 +727,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* @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
+ * @digest_cache_mask: For which actions and purpose the digest cache is usable
*
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
@@ -738,7 +740,8 @@ 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,
struct ima_template_desc **template_desc,
- const char *func_data, unsigned int *allowed_algos)
+ const char *func_data, unsigned int *allowed_algos,
+ u64 *digest_cache_mask)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
@@ -783,6 +786,9 @@ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode,
if (template_desc && entry->template)
*template_desc = entry->template;
+ if (digest_cache_mask)
+ *digest_cache_mask |= entry->digest_cache_mask;
+
if (!actmask)
break;
}
@@ -1073,7 +1079,7 @@ enum policy_opt {
Opt_digest_type,
Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos,
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
- Opt_label, Opt_err
+ Opt_label, Opt_digest_cache, Opt_err
};
static const match_table_t policy_tokens = {
@@ -1122,6 +1128,7 @@ static const match_table_t policy_tokens = {
{Opt_template, "template=%s"},
{Opt_keyrings, "keyrings=%s"},
{Opt_label, "label=%s"},
+ {Opt_digest_cache, "digest_cache=%s"},
{Opt_err, NULL}
};
@@ -1886,6 +1893,26 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
&(template_desc->num_fields));
entry->template = template_desc;
break;
+ case Opt_digest_cache:
+ ima_log_string(ab, "template", args[0].from);
+
+ result = -EINVAL;
+
+ if (!strcmp(args[0].from, "content")) {
+ switch (entry->action) {
+ case MEASURE:
+ entry->digest_cache_mask |= DIGEST_CACHE_MEASURE;
+ result = 0;
+ break;
+ case APPRAISE:
+ entry->digest_cache_mask |= DIGEST_CACHE_APPRAISE_CONTENT;
+ result = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -1912,6 +1939,28 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
"verity rules should include d-ngv2");
}
+ /* New-style measurements with digest cache cannot be on default PCR. */
+ if (entry->action == MEASURE &&
+ (entry->digest_cache_mask & DIGEST_CACHE_MEASURE)) {
+ if (!(entry->flags & IMA_PCR) ||
+ entry->pcr == CONFIG_IMA_MEASURE_PCR_IDX)
+ result = -EINVAL;
+ }
+
+ /* Digest cache should not conflict with other appraisal methods. */
+ if (entry->action == APPRAISE &&
+ (entry->digest_cache_mask & DIGEST_CACHE_APPRAISE_CONTENT)) {
+ if ((entry->flags & IMA_DIGSIG_REQUIRED) ||
+ (entry->flags & IMA_VERITY_REQUIRED) ||
+ (entry->flags & IMA_MODSIG_ALLOWED))
+ result = -EINVAL;
+ }
+
+ /* Digest cache cannot be used to measure/appraise digest lists. */
+ if ((entry->flags & IMA_FUNC) && entry->func == DIGEST_LIST_CHECK &&
+ entry->digest_cache_mask)
+ result = -EINVAL;
+
audit_log_format(ab, "res=%d", !result);
audit_log_end(ab);
return result;
@@ -2278,6 +2327,9 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, "appraise_flag=check_blacklist ");
if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio ");
+ if ((entry->digest_cache_mask & DIGEST_CACHE_MEASURE) ||
+ (entry->digest_cache_mask & DIGEST_CACHE_APPRAISE_CONTENT))
+ seq_puts(m, "digest_cache=content ");
rcu_read_unlock();
seq_puts(m, "\n");
return 0;