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 <roberto.sassu@xxxxxxxxxx> --- 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