The Digest Lists extension creates a new measurement only when a file is
unknown (i.e. its digest is not found in the uploaded digest lists).
However, if digest lists are not measured, a remote verifier cannot
determine which files could have possibly been accessed. If they are not
appraised, a user would be able to access files that are not signed or
protected with a HMAC.

This patch prevents this issue by monitoring the process that opened
digest_list_data and that can upload digests. If the process opens a file
that is not measured, digest list queries by IMA-Measure will be always
negative (ima_digest_allow() will always return a NULL pointer). The same
happens for IMA-Appraise.

This patch also ensures that the parser can only execute shared libraries
with type COMPACT_PARSER (i.e. libraries adding support for custom digest
list formats).

Signed-off-by: Roberto Sassu <[email protected]>
---
 security/integrity/ima/ima.h             |  2 ++
 security/integrity/ima/ima_digest_list.c | 36 ++++++++++++++++++++++++
 security/integrity/ima/ima_digest_list.h | 15 ++++++++++
 security/integrity/ima/ima_main.c        | 17 ++++++++++-
 4 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index b4a0d2a02ff2..1729ecc4e3e7 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -53,6 +53,8 @@ extern int ima_hash_algo;
 extern int ima_appraise;
 extern struct tpm_chip *ima_tpm_chip;
 
+extern int ima_digest_list_actions;
+
 /* IMA event related data */
 struct ima_event_data {
        struct integrity_iint_cache *iint;
diff --git a/security/integrity/ima/ima_digest_list.c 
b/security/integrity/ima/ima_digest_list.c
index 3c77a6cec29a..3aaa26d6e8e3 100644
--- a/security/integrity/ima/ima_digest_list.c
+++ b/security/integrity/ima/ima_digest_list.c
@@ -163,6 +163,9 @@ bool ima_check_current_is_parser(void)
        struct file *parser_file;
        struct mm_struct *mm;
 
+       if (!(ima_digest_list_actions & ima_policy_flag))
+               return false;
+
        mm = get_task_mm(current);
        if (!mm)
                return false;
@@ -204,3 +207,36 @@ struct task_struct *ima_get_parser(void)
 {
        return current_parser;
 }
+
+/**********************
+ * Digest usage check *
+ **********************/
+void ima_check_parser_action(struct inode *inode, enum ima_hooks hook,
+                            int mask, int action, bool check_digest,
+                            struct ima_digest *digest)
+{
+       int action_mask = (IMA_DO_MASK & ~IMA_APPRAISE_SUBMASK);
+
+       if (current != current_parser)
+               return;
+
+       if (!(mask & (MAY_READ | MAY_EXEC)))
+               return;
+
+       if (hook == MMAP_CHECK && mask == MAY_EXEC && check_digest &&
+           (!digest || digest->type != COMPACT_PARSER))
+               action_mask = 0;
+
+       if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+               action_mask = 0;
+
+       ima_digest_list_actions &= (action & action_mask);
+}
+
+struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action)
+{
+       if (!(ima_digest_list_actions & action))
+               return NULL;
+
+       return digest;
+}
diff --git a/security/integrity/ima/ima_digest_list.h 
b/security/integrity/ima/ima_digest_list.h
index be07a4afd7b6..49798688f9c8 100644
--- a/security/integrity/ima/ima_digest_list.h
+++ b/security/integrity/ima/ima_digest_list.h
@@ -29,6 +29,10 @@ int ima_parse_compact_list(loff_t size, void *buf);
 bool ima_check_current_is_parser(void);
 void ima_set_parser(struct task_struct *parser);
 struct task_struct *ima_get_parser(void);
+void ima_check_parser_action(struct inode *inode, enum ima_hooks hook,
+                            int mask, int action, bool check_digest,
+                            struct ima_digest *digest);
+struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action);
 #else
 static inline struct ima_digest *ima_lookup_digest(u8 *digest,
                                                   enum hash_algo algo)
@@ -50,5 +54,16 @@ static inline struct task_struct *ima_get_parser(void)
 {
        return NULL;
 }
+static inline void ima_check_parser_action(struct inode *inode,
+                                          enum ima_hooks hook, int mask,
+                                          int action, bool check_digest,
+                                          struct ima_digest *digest)
+{
+}
+static inline struct ima_digest *ima_digest_allow(struct ima_digest *digest,
+                                                 int action)
+{
+       return NULL;
+}
 #endif /*CONFIG_IMA_DIGEST_LIST*/
 #endif /*LINUX_IMA_DIGEST_LIST_H*/
diff --git a/security/integrity/ima/ima_main.c 
b/security/integrity/ima/ima_main.c
index dc53ed8b0dd0..15eb00fb6b6d 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -29,6 +29,7 @@
 #include <linux/fs.h>
 
 #include "ima.h"
+#include "ima_digest_list.h"
 
 #ifdef CONFIG_IMA_APPRAISE
 int ima_appraise = IMA_APPRAISE_ENFORCE;
@@ -36,6 +37,9 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
 int ima_appraise;
 #endif
 
+/* Actions (measure/appraisal) for which digest lists can be used */
+int ima_digest_list_actions;
+
 int ima_hash_algo = HASH_ALGO_SHA1;
 static int hash_setup_done;
 
@@ -252,11 +256,15 @@ static int process_measurement(struct file *file, const 
struct cred *cred,
        const char *pathname = NULL;
        int rc = 0, action, must_appraise = 0;
        int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+       struct ima_digest *found_digest = NULL;
        struct evm_ima_xattr_data *xattr_value = NULL;
        int xattr_len = 0;
        bool violation_check;
        enum hash_algo hash_algo;
 
+       ima_check_parser_action(inode, func, mask, ima_policy_flag, false,
+                               NULL);
+
        if (!ima_policy_flag || !S_ISREG(inode->i_mode))
                return 0;
 
@@ -268,6 +276,9 @@ static int process_measurement(struct file *file, const 
struct cred *cred,
                                &template_desc);
        violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
                           (ima_policy_flag & IMA_MEASURE));
+
+       ima_check_parser_action(inode, func, mask, action, false, NULL);
+
        if (!action && !violation_check)
                return 0;
 
@@ -312,7 +323,8 @@ static int process_measurement(struct file *file, const 
struct cred *cred,
        if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags) ||
            ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
             !(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) &&
-            !(action & IMA_FAIL_UNVERIFIABLE_SIGS))) {
+            !(action & IMA_FAIL_UNVERIFIABLE_SIGS)) ||
+           ima_get_parser() == current) {
                iint->flags &= ~IMA_DONE_MASK;
                iint->measured_pcrs = 0;
        }
@@ -366,6 +378,9 @@ 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);
 
+       found_digest = ima_lookup_digest(iint->ima_hash->digest, hash_algo);
+       ima_check_parser_action(inode, func, mask, action, true, found_digest);
+
        if (action & IMA_MEASURE)
                ima_store_measurement(iint, file, pathname,
                                      xattr_value, xattr_len, pcr,
-- 
2.17.1

Reply via email to