@@ -270,7 +270,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
- struct ima_template_desc *template_desc);
+ struct ima_template_desc *template_desc,
+ u64 digest_cache_mask);
int process_buffer_measurement(struct mnt_idmap *idmap,
struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
@@ -339,7 +339,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
- struct ima_template_desc *template_desc)
+ struct ima_template_desc *template_desc,
+ u64 digest_cache_mask)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "ENOMEM";
@@ -363,6 +364,19 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
if (iint->measured_pcrs & (0x1 << pcr) && !modsig)
return;
+ /*
+ * If the file digest was found in the digest cache, the digest cache
+ * is enabled for measurement, and the digest list was measured, mark
+ * the file as measured, so that it does not appear in the measurement
+ * list (known digest), and the same action is not repeated at the next
+ * access.
+ */
+ if (digest_cache_mask & DIGEST_CACHE_MEASURE) {
+ iint->flags |= IMA_MEASURED;
+ iint->measured_pcrs |= (0x1 << pcr);
+ return;
+ }
+
result = ima_alloc_init_template(&event_data, &entry, template_desc);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
@@ -221,6 +221,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
bool violation_check;
enum hash_algo hash_algo;
unsigned int allowed_algos = 0;
+ u64 digest_cache_mask = 0;
+ struct digest_cache *digest_cache = NULL;
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return 0;
@@ -231,7 +233,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, NULL);
+ &allowed_algos, &digest_cache_mask);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK ||
func == MMAP_CHECK_REQPROT) &&
(ima_policy_flag & IMA_MEASURE));
@@ -263,6 +265,21 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (!action)
goto out;
+ if (digest_cache_mask) {
+ /*
+ * Prefetch the digest lists to measure them in a deterministic
+ * way, and make the PCR predictable.
+ */
+ if (digest_cache_mask & DIGEST_CACHE_MEASURE)
+ digest_cache_iter_dir(file_dentry(file));
+
+ digest_cache = digest_cache_get(file_dentry(file), iint);
+ if (digest_cache)
+ digest_cache_mask &= digest_cache->mask;
+ else
+ digest_cache_mask = 0;
+ }
+
mutex_lock(&iint->mutex);
if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
@@ -349,10 +366,17 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
+ if (rc == 0 && digest_cache_mask) {
+ if (digest_cache_lookup(digest_cache, iint->ima_hash->digest,
+ iint->ima_hash->algo, pathname))
+ /* Reset the mask, the file digest was not found. */
+ digest_cache_mask = 0;
+ }
+
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len, modsig, pcr,
- template_desc);
+ template_desc, digest_cache_mask);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
rc = ima_check_blacklist(iint, modsig, pcr);
if (rc != -EPERM) {