[RFC][PATCH v2 08/13] ima: Use digest cache for measurement
From: Roberto Sassu
Date: Sat Aug 12 2023 - 06:49:43 EST
From: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
If a measure rule contains 'digest_cache=content', get the digest cache (if
available) associated to the file being measured.
AND the digest list mask from the IMA policy with the digest list mask of
the digest cache (set depending on the actions done on the digest lists),
to determine if the digest cache can be used for the measurement action.
If the digest cache is enabled, lookup the calculated digest of the file
being accessed and if found, pass the ANDed masks to
ima_store_measurement(). Otherwise, reset the mask to zero.
Finally, if the DIGEST_CACHE_MEASURE flag is set in the mask, mark the file
as measured for the supplied PCR (which cannot be the default one).
At the first digest list accessed, iterate over all digest lists in the
same directory, and measure them to make the PCR predictable. However,
don't parse those digest lists except the requested one, to avoid too much
memory pressure.
Skipping the measurement of cached digests causes less information to be
available to remote verifiers. In particular, they would know that a subset
or all files in the measured digest list could have been accessed, but they
won't know if and when.
Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
security/integrity/ima/ima.h | 3 ++-
security/integrity/ima/ima_api.c | 16 +++++++++++++++-
security/integrity/ima/ima_main.c | 28 ++++++++++++++++++++++++++--
3 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index f8f91d5c04a..bb75cc3d2fd 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -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,
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index c591b093bb5..77b1ecff45a 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -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,
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 4fdfc399fa6..54d006fc490 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -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) {
--
2.34.1