lkml.org 
[lkml]   [2021]   [Jul]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC][PATCH v2 06/12] diglim: Interfaces - digest_list_add, digest_list_del
    Date
    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.

    Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
    ---
    .../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@huawei.com>
    + *
    + * 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@us.ibm.com>
    */

    +#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*/
    --
    2.25.1
    \
     
     \ /
      Last update: 2021-07-26 18:41    [W:4.985 / U:0.196 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site