Re: [RFC][PATCH v2 06/12] diglim: Interfaces - digest_list_add, digest_list_del

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Em Mon, 26 Jul 2021 18:36:54 +0200
Roberto Sassu <roberto.sassu@xxxxxxxxxx> escreveu:

> Introduce <securityfs>/integrity/diglim/digest_list_add, which can be used
> to upload a digest list and add the digests to the hash table; passed data
> are interpreted as file path if the first byte is / or as the digest list
> itself otherwise. ima_measure_critical_data() is called to calculate the
> digest of the digest list and eventually, if an appropriate rule is set in
> the IMA policy, to measure it.
> 
> Also introduce <securityfs>/integrity/diglim/digest_list_del, which can be
> used to upload a digest list and delete the digests from the hash table;
> data are interpreted in the same way as described for digest_list_add.

LGTM.

> 
> Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
> ---
>  .../security/diglim/implementation.rst        |   9 +
>  MAINTAINERS                                   |   1 +
>  include/linux/kernel_read_file.h              |   1 +
>  security/integrity/diglim/Makefile            |   2 +-
>  security/integrity/diglim/diglim.h            |   2 +
>  security/integrity/diglim/fs.c                | 239 ++++++++++++++++++
>  security/integrity/integrity.h                |   4 +
>  7 files changed, 257 insertions(+), 1 deletion(-)
>  create mode 100644 security/integrity/diglim/fs.c
> 
> diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst
> index 9d679567a037..fc0cd8810a80 100644
> --- a/Documentation/security/diglim/implementation.rst
> +++ b/Documentation/security/diglim/implementation.rst
> @@ -244,3 +244,12 @@ back when one of the steps fails.
>  
>  #. digest_list_parse() deletes the struct digest_list_item on unsuccessful
>     add or successful delete.
> +
> +
> +Interfaces
> +----------
> +
> +This section introduces the interfaces in
> +``<securityfs>/integrity/diglim`` necessary to interact with DIGLIM.
> +
> +.. kernel-doc:: security/integrity/diglim/fs.c
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 77c3613c600a..0672128fae7f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5464,6 +5464,7 @@ F:	Documentation/security/diglim/introduction.rst
>  F:	include/linux/diglim.h
>  F:	include/uapi/linux/diglim.h
>  F:	security/integrity/diglim/diglim.h
> +F:	security/integrity/diglim/fs.c
>  F:	security/integrity/diglim/methods.c
>  F:	security/integrity/diglim/parser.c
>  
> diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h
> index 575ffa1031d3..636ecdfdc616 100644
> --- a/include/linux/kernel_read_file.h
> +++ b/include/linux/kernel_read_file.h
> @@ -14,6 +14,7 @@
>  	id(KEXEC_INITRAMFS, kexec-initramfs)	\
>  	id(POLICY, security-policy)		\
>  	id(X509_CERTIFICATE, x509-certificate)	\
> +	id(DIGEST_LIST, digest-list)	\
>  	id(MAX_ID, )
>  
>  #define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
> diff --git a/security/integrity/diglim/Makefile b/security/integrity/diglim/Makefile
> index 34e4e154fff3..ac345afdf5dd 100644
> --- a/security/integrity/diglim/Makefile
> +++ b/security/integrity/diglim/Makefile
> @@ -5,4 +5,4 @@
>  
>  obj-$(CONFIG_DIGLIM) += diglim.o
>  
> -diglim-y := methods.o parser.o
> +diglim-y := methods.o parser.o fs.o
> diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h
> index 3adc218a0325..819703175eda 100644
> --- a/security/integrity/diglim/diglim.h
> +++ b/security/integrity/diglim/diglim.h
> @@ -22,6 +22,8 @@
>  #include <linux/hash_info.h>
>  #include <linux/diglim.h>
>  
> +#include "../integrity.h"
> +
>  #define MAX_DIGEST_SIZE 64
>  #define HASH_BITS 10
>  #define DIGLIM_HTABLE_SIZE (1 << HASH_BITS)
> diff --git a/security/integrity/diglim/fs.c b/security/integrity/diglim/fs.c
> new file mode 100644
> index 000000000000..07a0d75a0e33
> --- /dev/null
> +++ b/security/integrity/diglim/fs.c
> @@ -0,0 +1,239 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> + *
> + * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
> + *
> + * Functions for the interfaces exposed in securityfs.
> + */
> +
> +#include <linux/fcntl.h>
> +#include <linux/kernel_read_file.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <linux/seq_file.h>
> +#include <linux/rculist.h>
> +#include <linux/rcupdate.h>
> +#include <linux/parser.h>
> +#include <linux/vmalloc.h>
> +#include <linux/namei.h>
> +#include <linux/ima.h>
> +
> +#include "diglim.h"
> +
> +#define MAX_DIGEST_LIST_SIZE (64 * 1024 * 1024 - 1)
> +
> +static struct dentry *diglim_dir;
> +/**
> + * DOC: digest_list_add
> + *
> + * digest_list_add can be used to upload a digest list and add the digests
> + * to the hash table; passed data are interpreted as file path if the first
> + * byte is ``/`` or as the digest list itself otherwise.
> + *
> + * ima_measure_critical_data() is called to calculate the digest of the
> + * digest list and eventually, if an appropriate rule is set in the IMA
> + * policy, to measure it.
> + */
> +static struct dentry *digest_list_add_dentry;
> +/**
> + * DOC: digest_list_del
> + *
> + * digest_list_del can be used to upload a digest list and delete the
> + * digests from the hash table; data are interpreted in the same way as
> + * described for digest_list_add.
> + */
> +static struct dentry *digest_list_del_dentry;
> +char digest_label[NAME_MAX + 1];
> +
> +/*
> + * digest_list_read: read and parse the digest list from the path
> + */
> +static ssize_t digest_list_read(char *path, enum ops op)
> +{
> +	void *data = NULL;
> +	char *datap;
> +	size_t size;
> +	u8 actions = 0;
> +	struct file *file;
> +	char event_name[NAME_MAX + 9 + 1];
> +	u8 digest[IMA_MAX_DIGEST_SIZE] = { 0 };
> +	enum hash_algo algo;
> +	int rc, pathlen = strlen(path);
> +
> +	/* Remove \n. */
> +	datap = path;
> +	strsep(&datap, "\n");
> +
> +	file = filp_open(path, O_RDONLY, 0);
> +	if (IS_ERR(file)) {
> +		pr_err("unable to open file: %s (%ld)", path, PTR_ERR(file));
> +		return PTR_ERR(file);
> +	}
> +
> +	rc = kernel_read_file(file, 0, &data, INT_MAX, NULL,
> +			      READING_DIGEST_LIST);
> +	if (rc < 0) {
> +		pr_err("unable to read file: %s (%d)", path, rc);
> +		goto out;
> +	}
> +
> +	size = rc;
> +
> +	snprintf(event_name, sizeof(event_name), "%s_file_%s",
> +		 op == DIGEST_LIST_ADD ? "add" : "del",
> +		 file_dentry(file)->d_name.name);
> +
> +	rc = ima_measure_critical_data("diglim", event_name, data, size, false,
> +				       digest, sizeof(digest));
> +	if (rc < 0 && rc != -EEXIST)
> +		goto out_vfree;
> +
> +	algo = ima_get_current_hash_algo();
> +
> +	if (!rc || rc == -EEXIST)
> +		actions |= (1 << COMPACT_ACTION_IMA_MEASURED);
> +
> +	rc = digest_list_parse(size, data, op, actions, digest, algo, "");
> +	if (rc < 0)
> +		pr_err("unable to upload digest list %s (%d)\n", path, rc);
> +out_vfree:
> +	vfree(data);
> +out:
> +	fput(file);
> +
> +	if (rc < 0)
> +		return rc;
> +
> +	return pathlen;
> +}
> +
> +/*
> + * digest_list_write: write the digest list path or the digest list itself
> + */
> +static ssize_t digest_list_write(struct file *file, const char __user *buf,
> +				 size_t datalen, loff_t *ppos)
> +{
> +	char *data;
> +	ssize_t result;
> +	enum ops op = DIGEST_LIST_ADD;
> +	struct dentry *dentry = file_dentry(file);
> +	u8 digest[IMA_MAX_DIGEST_SIZE];
> +	char event_name[NAME_MAX + 11 + 1];
> +	enum hash_algo algo;
> +	u8 actions = 0;
> +
> +	/* No partial writes. */
> +	result = -EINVAL;
> +	if (*ppos != 0)
> +		goto out;
> +
> +	result = -EFBIG;
> +	if (datalen > MAX_DIGEST_LIST_SIZE)
> +		goto out;
> +
> +	data = memdup_user_nul(buf, datalen);
> +	if (IS_ERR(data)) {
> +		result = PTR_ERR(data);
> +		goto out;
> +	}
> +
> +	if (dentry == digest_list_del_dentry)
> +		op = DIGEST_LIST_DEL;
> +
> +	result = -EPERM;
> +
> +	if (data[0] == '/') {
> +		result = digest_list_read(data, op);
> +	} else {
> +		snprintf(event_name, sizeof(event_name), "%s_buffer_%s",
> +			 op == DIGEST_LIST_ADD ? "add" : "del", digest_label);
> +
> +		result = ima_measure_critical_data("diglim", event_name, data,
> +						   datalen, false, digest,
> +						   sizeof(digest));
> +		if (result < 0 && result != -EEXIST) {
> +			pr_err("failed to measure buffer digest (%ld)\n",
> +			       result);
> +			goto out_kfree;
> +		}
> +
> +		algo = ima_get_current_hash_algo();
> +
> +		if (!result || result == -EEXIST)
> +			actions |= (1 << COMPACT_ACTION_IMA_MEASURED);
> +
> +		result = digest_list_parse(datalen, data, op, actions, digest,
> +					   algo, "");
> +		if (result != datalen) {
> +			pr_err("unable to upload generated digest list\n");
> +			result = -EINVAL;
> +		}
> +	}
> +out_kfree:
> +	kfree(data);
> +out:
> +	return result;
> +}
> +
> +static unsigned long flags;
> +
> +/*
> + * digest_list_open: sequentialize access to the add/del files
> + */
> +static int digest_list_open(struct inode *inode, struct file *filp)
> +{
> +	if ((filp->f_flags & O_ACCMODE) != O_WRONLY)
> +		return -EACCES;
> +
> +	if (test_and_set_bit(0, &flags))
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +
> +/*
> + * digest_list_release - release the add/del files
> + */
> +static int digest_list_release(struct inode *inode, struct file *file)
> +{
> +	clear_bit(0, &flags);
> +	return 0;
> +}
> +
> +static const struct file_operations digest_list_upload_ops = {
> +	.open = digest_list_open,
> +	.write = digest_list_write,
> +	.read = seq_read,
> +	.release = digest_list_release,
> +	.llseek = generic_file_llseek,
> +};
> +
> +static int __init diglim_fs_init(void)
> +{
> +	diglim_dir = securityfs_create_dir("diglim", integrity_dir);
> +	if (IS_ERR(diglim_dir))
> +		return -1;
> +
> +	digest_list_add_dentry = securityfs_create_file("digest_list_add", 0200,
> +						diglim_dir, NULL,
> +						&digest_list_upload_ops);
> +	if (IS_ERR(digest_list_add_dentry))
> +		goto out;
> +
> +	digest_list_del_dentry = securityfs_create_file("digest_list_del", 0200,
> +						diglim_dir, NULL,
> +						&digest_list_upload_ops);
> +	if (IS_ERR(digest_list_del_dentry))
> +		goto out;
> +
> +	return 0;
> +out:
> +	securityfs_remove(digest_list_del_dentry);
> +	securityfs_remove(digest_list_add_dentry);
> +	securityfs_remove(diglim_dir);
> +	return -1;
> +}
> +
> +late_initcall(diglim_fs_init);
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 547425c20e11..ac45e1599c2d 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -6,6 +6,9 @@
>   * Mimi Zohar <zohar@xxxxxxxxxx>
>   */
>  
> +#ifndef __INTEGRITY_H
> +#define __INTEGRITY_H
> +
>  #ifdef pr_fmt
>  #undef pr_fmt
>  #endif
> @@ -283,3 +286,4 @@ static inline void __init add_to_platform_keyring(const char *source,
>  {
>  }
>  #endif
> +#endif /*__INTEGRITY_H*/



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux