Re: [PATCH v2] ima: add the ability to query the hash of a given file.

From: Mimi Zohar
Date: Wed Jan 08 2020 - 11:06:10 EST


On Mon, 2020-01-06 at 17:25 +0100, Florent Revest wrote:
> From: Florent Revest <revest@xxxxxxxxxx>
>
> This allows other parts of the kernel (perhaps a stacked LSM allowing
> system monitoring, eg. the proposed KRSI LSM [1]) to retrieve the hash
> of a given file from IMA if it's present in the iint cache.
>
> It's true that the existence of the hash means that it's also in the
> audit logs or in /sys/kernel/security/ima/ascii_runtime_measurements,
> but it can be difficult to pull that information out for every
> subsequent exec. This is especially true if a given host has been up
> for a long time and the file was first measured a long time ago.
>
> This is based on Peter Moody's patch:
> https://sourceforge.net/p/linux-ima/mailman/message/33036180/

FYI, but unlike the audit log/IMA measurement list, the iint cache
entries can be removed. ÂRefer to security_inode_free(). ÂPerhaps
mention of this difference should be included, here, in the patch
description.

>
> [1] https://lkml.org/lkml/2019/9/10/393
>
> Signed-off-by: Florent Revest <revest@xxxxxxxxxx>

Assuming, with the above difference, you're still interested in having
this feature upstreamed and addressing the comments above and below:

Reviewed-by: Mimi Zohar <zohar@xxxxxxxxxxxxx>

> ---
> include/linux/ima.h | 6 ++++
> security/integrity/ima/ima_main.c | 46 +++++++++++++++++++++++++++++++
> 2 files changed, 52 insertions(+)
>
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 6d904754d858..d621c65ba9a5 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -23,6 +23,7 @@ extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
> extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
> enum kernel_read_file_id id);
> extern void ima_post_path_mknod(struct dentry *dentry);
> +extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
> extern void ima_kexec_cmdline(const void *buf, int size);
>
> #ifdef CONFIG_IMA_KEXEC
> @@ -91,6 +92,11 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
> return;
> }
>
> +static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> static inline void ima_kexec_cmdline(const void *buf, int size) {}
> #endif /* CONFIG_IMA */
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index d7e987baf127..3799b6c6c3b8 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -445,6 +445,52 @@ int ima_file_check(struct file *file, int mask)
> }
> EXPORT_SYMBOL_GPL(ima_file_check);
>
> +/**
> + * ima_file_hash - return the stored measurement if a file has been hashed.
> + * @file: pointer to the file
> + * @buf: buffer in which to store the hash
> + * @buf_size: length of the buffer
> + *
> + * On success, return the hash algorithm (as defined in the enum hash_algo).
> + * If buf is not NULL, this function also outputs the hash into buf.

As of Linux 5.4.y, IMA support for appended file signatures was added.
ÂShould we indicate that the file hash returned is based on the entire
file, including the appended signature?

Mimi


> + * If the hash is larger than buf_size, then only buf_size bytes will be copied.
> + * It generally just makes sense to pass a buffer capable of holding the largest
> + * possible hash: IMA_MAX_DIGEST_SIZE
> + *
> + * If IMA is disabled or if no measurement is available, return -EOPNOTSUPP.
> + * If the parameters are incorrect, return -EINVAL.
> + */
> +int ima_file_hash(struct file *file, char *buf, size_t buf_size)
> +{
> + struct inode *inode;
> + struct integrity_iint_cache *iint;
> + int hash_algo;
> +
> + if (!file)
> + return -EINVAL;
> +
> + if (!ima_policy_flag)
> + return -EOPNOTSUPP;
> +
> + inode = file_inode(file);
> + iint = integrity_iint_find(inode);
> + if (!iint)
> + return -EOPNOTSUPP;
> +
> + mutex_lock(&iint->mutex);
> + if (buf) {
> + size_t copied_size;
> +
> + copied_size = min_t(size_t, iint->ima_hash->length, buf_size);
> + memcpy(buf, iint->ima_hash->digest, copied_size);
> + }
> + hash_algo = iint->ima_hash->algo;
> + mutex_unlock(&iint->mutex);
> +
> + return hash_algo;
> +}
> +EXPORT_SYMBOL_GPL(ima_file_hash);
> +
> /**
> * ima_post_create_tmpfile - mark newly created tmpfile as new
> * @file : newly created tmpfile