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*/