@@ -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:= [data]
+ "data" means that the digest cache is used only
+ for file data measurement and/or appraisal.
default policy:
# PROC_SUPER_MAGIC
@@ -44,6 +44,10 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
#define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0)
+/* Digest cache usage flags. */
+#define IMA_DIGEST_CACHE_MEASURE_DATA 0x0000000000000001
+#define IMA_DIGEST_CACHE_APPRAISE_DATA 0x0000000000000002
+
/* current content of the policy */
extern int ima_policy_flag;
@@ -374,7 +378,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_usage);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
void *buf, loff_t size, enum hash_algo algo,
@@ -405,7 +410,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_usage);
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_usage: Actions and purpose for which digest cache is allowed
*
* 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_usage)
{
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_usage);
}
static bool ima_get_verity_digest(struct ima_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, struct ima_iint_cache *iint)
@@ -234,7 +234,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));
@@ -502,11 +502,11 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
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)))
@@ -994,7 +994,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_usage; /* Actions and purpose for which digest cache is allowed */
};
/*
@@ -714,6 +715,27 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
}
}
+/* Filter IMA hooks that can use digest caches. */
+static bool ima_digest_cache_func_allowed(enum ima_hooks func)
+{
+ switch (func) {
+ case NONE:
+ case FILE_CHECK:
+ case MMAP_CHECK:
+ case MMAP_CHECK_REQPROT:
+ case BPRM_CHECK:
+ case CREDS_CHECK:
+ case FIRMWARE_CHECK:
+ case POLICY_CHECK:
+ case MODULE_CHECK:
+ case KEXEC_KERNEL_CHECK:
+ case KEXEC_INITRAMFS_CHECK:
+ return true;
+ default:
+ return false;
+ }
+}
+
/**
* ima_match_policy - decision based on LSM and other conditions
* @idmap: idmap of the mount the inode was found from
@@ -728,6 +750,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_usage: Actions and purpose for which digest cache is allowed
*
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
@@ -740,7 +763,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_usage)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
@@ -785,6 +809,22 @@ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode,
if (template_desc && entry->template)
*template_desc = entry->template;
+ /*
+ * Since we allow IMA policy rules without func=, check if the
+ * current IMA hook is allowed and, if not, disregard the digest
+ * cache usage from the policy.
+ *
+ * In addition, don't allow digest caches to be used for IMA
+ * policy measurements, so that policies always appear in the
+ * measurement list.
+ */
+ if (digest_cache_usage && ima_digest_cache_func_allowed(func)) {
+ *digest_cache_usage |= entry->digest_cache_usage;
+
+ if (func == POLICY_CHECK)
+ *digest_cache_usage &= ~IMA_DIGEST_CACHE_MEASURE_DATA;
+ }
+
if (!actmask)
break;
}
@@ -1075,7 +1115,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 = {
@@ -1124,6 +1164,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}
};
@@ -1248,6 +1289,19 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->action != MEASURE && entry->flags & IMA_PCR)
return false;
+ /* New-style measurements with digest cache cannot be on default PCR. */
+ if (entry->action == MEASURE &&
+ (entry->digest_cache_usage & IMA_DIGEST_CACHE_MEASURE_DATA)) {
+ if (!(entry->flags & IMA_PCR) ||
+ entry->pcr == CONFIG_IMA_MEASURE_PCR_IDX)
+ return false;
+ }
+
+ /* Digest caches can be used only for a subset of the IMA hooks. */
+ if (entry->digest_cache_usage &&
+ !ima_digest_cache_func_allowed(entry->func))
+ return false;
+
if (entry->action != APPRAISE &&
entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED |
IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
@@ -1884,6 +1938,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, "digest_cache", args[0].from);
+
+ result = -EINVAL;
+
+ if (!strcmp(args[0].from, "data")) {
+ switch (entry->action) {
+ case MEASURE:
+ entry->digest_cache_usage |= IMA_DIGEST_CACHE_MEASURE_DATA;
+ result = 0;
+ break;
+ case APPRAISE:
+ entry->digest_cache_usage |= IMA_DIGEST_CACHE_APPRAISE_DATA;
+ result = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -2274,6 +2348,9 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, "digest_type=verity ");
if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio ");
+ if ((entry->digest_cache_usage & IMA_DIGEST_CACHE_MEASURE_DATA) ||
+ (entry->digest_cache_usage & IMA_DIGEST_CACHE_APPRAISE_DATA))
+ seq_puts(m, "digest_cache=data ");
rcu_read_unlock();
seq_puts(m, "\n");
return 0;