[RFC][PATCH v2 9/9] ima: Register to the digest_cache LSM notifier and process events

From: Roberto Sassu
Date: Mon Apr 15 2024 - 12:15:23 EST


From: Roberto Sassu <roberto.sassu@xxxxxxxxxx>

A digest cache used for measurement/appraisal might change over the time
(due to file modification, directory changes). When that happens, IMA
should invalidate the cached integrity result for affected inodes and
evaluate those inodes again.

Implement ima_digest_cache_change(), to be invoked at every notification by
the digest_cache LSM, and register it as a callback with
digest_cache_register_notifier().

For every notification, and if the type of event is DIGEST_CACHE_RESET,
retrieve the inode integrity metadata (if any), and set the
IMA_CHANGE_XATTR atomic flag, so that IMA fully reevaluates the inode in
process_measurement().

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
security/integrity/ima/ima_digest_cache.c | 31 +++++++++++++++++++++++
security/integrity/ima/ima_digest_cache.h | 6 +++++
security/integrity/ima/ima_main.c | 11 +++++++-
3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima_digest_cache.c b/security/integrity/ima/ima_digest_cache.c
index 013c69f265d8..0ab35575ff7c 100644
--- a/security/integrity/ima/ima_digest_cache.c
+++ b/security/integrity/ima/ima_digest_cache.c
@@ -90,3 +90,34 @@ void ima_digest_cache_update_allowed_usage(struct file *file,
out:
digest_cache_put(digest_cache);
}
+
+static int ima_digest_cache_change(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct ima_iint_cache *iint;
+ struct digest_cache_event_data *event_data = data;
+
+ if (event != DIGEST_CACHE_RESET)
+ return NOTIFY_DONE;
+
+ iint = ima_iint_find(event_data->inode);
+ if (!iint) {
+ pr_debug("Integrity metadata not found for inode %lu\n",
+ event_data->inode->i_ino);
+ return NOTIFY_OK;
+ }
+
+ set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
+ pr_debug("Integrity metadata of inode %lu successfully reset\n",
+ event_data->inode->i_ino);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block digest_cache_notifier = {
+ .notifier_call = ima_digest_cache_change,
+};
+
+int ima_digest_cache_register_notifier(void)
+{
+ return digest_cache_register_notifier(&digest_cache_notifier);
+}
diff --git a/security/integrity/ima/ima_digest_cache.h b/security/integrity/ima/ima_digest_cache.h
index cb47c15e975d..44c188c2fb93 100644
--- a/security/integrity/ima/ima_digest_cache.h
+++ b/security/integrity/ima/ima_digest_cache.h
@@ -15,6 +15,7 @@ void ima_digest_cache_store_allowed_usage(struct file *file,
void ima_digest_cache_update_allowed_usage(struct file *file,
struct ima_iint_cache *iint,
u64 *allowed_usage);
+int ima_digest_cache_register_notifier(void);
#else
static inline void
ima_digest_cache_store_allowed_usage(struct file *file,
@@ -27,4 +28,9 @@ ima_digest_cache_update_allowed_usage(struct file *file,
u64 *allowed_usage)
{ }

+static inline int ima_digest_cache_register_notifier(void)
+{
+ return 0;
+}
+
#endif /* CONFIG_SECURITY_DIGEST_CACHE */
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 7ae2bd888d41..fe826755acd1 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1159,8 +1159,17 @@ static int __init init_ima(void)
return error;

error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier);
- if (error)
+ if (error) {
pr_warn("Couldn't register LSM notifier, error %d\n", error);
+ return error;
+ }
+
+ error = ima_digest_cache_register_notifier();
+ if (error) {
+ pr_warn("Couldn't register digest cache notifier, error %d\n",
+ error);
+ unregister_blocking_lsm_notifier(&ima_lsm_policy_notifier);
+ }

if (!error)
ima_update_policy_flags();
--
2.34.1