diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 0799d4de9e2d8f310f23684273c54141c077d995..3fb378017f9dba1ddc32939bd22e87161e312db0 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -438,7 +438,7 @@ int ima_appraise_measurement(enum ima_hooks func, /* Allow access to digest lists without metadata, only if they * are signed or found in a digest list (immutable) */ - if (func == DIGEST_LIST_CHECK) { + if (func == DIGEST_LIST_CHECK || ima_current_is_parser()) { if (xattr_value->type == EVM_IMA_XATTR_DIGSIG) break; if (found_digest && diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index ad0c97815772b65bfada0223b37a89b597522476..9eb1950dea58e7ea1acdb1e547ce65503a513d99 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "ima.h" #include "ima_digest_list.h" @@ -216,6 +218,10 @@ void ima_check_measured_appraised(struct file *file) if (!ima_digest_list_actions) return; + if (file_inode(file)->i_sb->s_magic == SECURITYFS_MAGIC || + S_ISDIR(file_inode(file)->i_mode)) + return; + iint = integrity_iint_find(file_inode(file)); if (!iint) { pr_err("%s not processed, disabling digest lists lookup\n", @@ -342,3 +348,53 @@ void __init ima_load_digest_lists(void) out: path_put(&path); } + +/**************** + * Parser check * + ****************/ +bool ima_check_current_is_parser(void) +{ + struct integrity_iint_cache *parser_iint; + struct file *parser_file; + struct mm_struct *mm; + + mm = get_task_mm(current); + if (!mm) + return false; + + parser_file = get_mm_exe_file(mm); + mmput(mm); + + if (!parser_file) + return false; + + parser_iint = integrity_iint_find(file_inode(parser_file)); + fput(parser_file); + + if (!parser_iint) + return false; + + /* flag cannot be cleared due to write protection of executables */ + if (!(parser_iint->flags & IMA_COLLECTED)) + return false; + + return ima_lookup_digest(parser_iint->ima_hash->digest, + parser_iint->ima_hash->algo, COMPACT_PARSER); +} + +struct task_struct *parser_task; + +void ima_set_parser(void) +{ + parser_task = current; +} + +void ima_unset_parser(void) +{ + parser_task = NULL; +} + +bool ima_current_is_parser(void) +{ + return (current == parser_task); +} diff --git a/security/integrity/ima/ima_digest_list.h b/security/integrity/ima/ima_digest_list.h index 5bd2388ff95eeb9c56fb228f1dc5b011a6d94835..4a8b0e000ad3e1c87a0f18d0a527b0080609fbf0 100644 --- a/security/integrity/ima/ima_digest_list.h +++ b/security/integrity/ima/ima_digest_list.h @@ -24,6 +24,10 @@ extern struct ima_h_table ima_digests_htable; int ima_parse_compact_list(loff_t size, void *buf, int op); void ima_check_measured_appraised(struct file *file); +bool ima_check_current_is_parser(void); +void ima_set_parser(void); +void ima_unset_parser(void); +bool ima_current_is_parser(void); #else static inline int ima_parse_compact_list(loff_t size, void *buf, int op) { @@ -32,5 +36,19 @@ static inline int ima_parse_compact_list(loff_t size, void *buf, int op) static inline void ima_check_measured_appraised(struct file *file) { } +static inline bool ima_check_current_is_parser(void) +{ + return false; +} +static inline void ima_set_parser(void) +{ +} +static inline void ima_unset_parser(void) +{ +} +static inline bool ima_current_is_parser(void) +{ + return false; +} #endif /*CONFIG_IMA_DIGEST_LIST*/ #endif /*LINUX_IMA_DIGEST_LIST_H*/ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 153c7cf5e94f8abf671abbe5684b009297fabe91..3a22b970be8d0257c4e68d2df42f314f1a028df0 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -390,6 +390,20 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, } else { result = ima_parse_add_rule(data); } + } else if (dentry == digest_list_data) { + if (!ima_current_is_parser()) { + result = -EACCES; + } else { + result = ima_parse_compact_list(datalen, data, + DIGEST_LIST_OP_ADD); + } + } else if (dentry == digest_list_data_del) { + if (!ima_current_is_parser()) { + result = -EACCES; + } else { + result = ima_parse_compact_list(datalen, data, + DIGEST_LIST_OP_DEL); + } } else { pr_err("Unknown data type\n"); result = -EINVAL; @@ -461,6 +475,11 @@ static int ima_open_data_upload(struct inode *inode, struct file *filp) } if (test_and_set_bit(flag, &ima_fs_flags)) return -EBUSY; + + if (dentry == digest_list_data || dentry == digest_list_data_del) + if (ima_check_current_is_parser()) + ima_set_parser(); + return 0; } @@ -480,6 +499,9 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file); + if (dentry == digest_list_data || dentry == digest_list_data_del) + ima_unset_parser(); + if (dentry != ima_policy) { clear_bit(flag, &ima_fs_flags); return 0; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index bc62d94e390d4bb6fa7d27e36be8aa6eb7aa83f1..f3242727634aa2a46b00a1785ddadbe4a3df5352 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -565,11 +565,15 @@ int ima_bprm_check(struct linux_binprm *bprm) int ima_file_check(struct file *file, int mask) { u32 secid; + int rc; security_task_getsecid(current, &secid); - return process_measurement(file, current_cred(), secid, NULL, 0, - mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK); + rc = process_measurement(file, current_cred(), secid, NULL, 0, + mask & (MAY_READ | MAY_WRITE | MAY_EXEC | + MAY_APPEND), FILE_CHECK); + if (ima_current_is_parser() && !rc) + ima_check_measured_appraised(file); + return rc; } EXPORT_SYMBOL_GPL(ima_file_check);