Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [RFC PATCH v1] fscrypt: support encrypted and trusted keys
From: Ahmad Fatoum @ 2021-07-28  8:50 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, Jarkko Sakkinen, James Morris,
	Serge E. Hallyn, James Bottomley, Mimi Zohar, Sumit Garg,
	David Howells, linux-fscrypt, linux-crypto, linux-integrity,
	linux-security-module, keyrings, linux-kernel, git, Omar Sandoval,
	Pengutronix Kernel Team
In-Reply-To: <YQA2fHPwH6EsH9BR@sol.localdomain>

Hello Eric,

On 27.07.21 18:38, Eric Biggers wrote:
> On Tue, Jul 27, 2021 at 04:43:49PM +0200, Ahmad Fatoum wrote:
>> For both v1 and v2 key setup mechanisms, userspace supplies the raw key
>> material to the kernel after which it is never again disclosed to
>> userspace.
>>
>> Use of encrypted and trusted keys offers stronger guarantees:
>> The key material is generated within the kernel and is never disclosed to
>> userspace in clear text and, in the case of trusted keys, can be
>> directly rooted to a trust source like a TPM chip.
> 
> Please include a proper justification for this feature

I've patches pending for extending trusted keys to wrap the key sealing
functionality of the CAAM IP on NXP SoCs[1]. I want the kernel to
generate key material in the factory, have the CAAM encrypt it using its
undisclosed unique key and pass it to userspace as encrypted blob that is
persisted to an unencrypted volume. The intention is to thwart offline
decryption of an encrypted file system in an embedded system, where a
passphrase can't be supplied by an end user.

Employing TPM and TEE trusted keys with this is already possible with
dm-crypt, but I'd like this to be possible out-of-the-box with
ubifs + fscrypt as well.

> and update the relevant
> sections of Documentation/filesystems/fscrypt.rst to explain why someone would
> want to use this feature and what it accomplishes.

How about:

-  type "fscrypt-provisioning" whose payload is
+  type "fscrypt-provisioning" or "trusted":
+  "fscrypt-provisioning" keys have a payload of
   struct fscrypt_provisioning_key_payload whose ``raw`` field contains
   the raw key and whose ``type`` field matches ``key_spec.type``.
   Since ``raw`` is variable-length, the total size of this key's
   payload must be ``sizeof(struct fscrypt_provisioning_key_payload)``
-  plus the raw key size.  The process must have Search permission on
-  this key.
+  plus the raw key size.
+  For "trusted" keys, the payload is directly taken as the raw key.

+  The process must have Search permission on this key.

-  Most users should leave this 0 and specify the raw key directly.

+  Most users leave this 0 and specify the raw key directly.
-  The support for specifying a Linux keyring key is intended mainly to

-  allow re-adding keys after a filesystem is unmounted and re-mounted,
+  "trusted" keys are useful to leverage kernel support for sealing and
+  unsealing key material. Sealed keys can be persisted to unencrypted
+  storage and later used to decrypt the file system without requiring
+  userspace to know the raw key material.
+  "fscrypt-provisioning" key support is intended mainly to allow
+  re-adding keys after a filesystem is unmounted and re-mounted,

> As-is, this feature doesn't seem to have a very strong justification.  Please
> also see previous threads where this feature was discussed/requested:
> https://lkml.kernel.org/linux-fscrypt/20180110124418.24385-1-git@andred.net/T/#u,
> https://lkml.kernel.org/linux-fscrypt/20180118131359.8365-1-git@andred.net/T/#u,
> https://lkml.kernel.org/linux-fscrypt/20200116193228.GA266386@vader/T/#u

Thanks. I wasn't aware of the last one. I (re-)read them now. I hope
this mail manages to address the concerns.

(Also added original authors of these mail threads to CC)

> Note that there are several design flaws with the encrypted and trusted key
> types:
> 
> - By default, trusted keys are generated using the TPM's RNG rather than the
>   kernel's RNG, which places all trust in an unauditable black box.

Patch to fix that awaits feedback on linux-integrity[2].

> - trusted and encrypted keys aren't restricted to specific uses in the kernel
>   (like the fscrypt-provisioning key type is) but rather are general-purpose.
>   Hence, it may be possible to leak their contents to userspace by requesting
>   their use for certain algorithms/features, e.g. to encrypt a dm-crypt target
>   using a weak cipher that is vulnerable to key recovery attacks.

The footgun is already there by allowing users to specify their own

raw key. Users can already use $keyid for dm-crypt and then do

  $ keyctl pipe $keyid | fscryptctl add_key /mnt

The responsibility to not reuse key material already lies with the users,
regardless if they handle the raw key material directly or indirectly via
a trusted key description/ID.

> - "encrypted" keys that use a master key of type "user" are supported, despite
>   these being easily obtainable in the clear by userspace providing their own
>   master key.  This violates one of the main design goals of "encrypted" keys.

I care for trusted keys foremost, so I've no problems dropping the encrypted
key support.

> Also, using the "trusted" key type isn't necessary to achieve TPM-bound
> encryption, as TPM binding can be handled in userspace instead.

Trusted keys support TEE and hopefully CAAM soon as well. I don't want my
userspace directly poking a DMA master.
> So I really would like to see a proper justification for this feature, and have
> it be properly documented.

In light of the extended justification above, do you want me to respin with
the proposed changes?

> One comment on the UAPI below.

> Why not just allow the key_id field to specify a "trusted" or "encrypted" key?
> Why is it necessary for FS_IOC_ADD_ENCRYPTION_KEY to support two different ways
> of looking up keyring keys -- by ID and by description?  Looking up by ID works
> fine for "fscrypt-provisioning" keys; why are "trusted" and "encrypted" keys
> different in this regard?

Mixture of reading emails predating key_id and misunderstanding the API.
key_id would be much cleaner indeed. I can change this for v2.

Thanks for your review.

[1]: https://lore.kernel.org/linux-integrity/655aab117f922320e2123815afb5bf3daeb7b8b3.1626885907.git-series.a.fatoum@pengutronix.de/
[2]: https://lore.kernel.org/linux-integrity/cover.9fc9298fd9d63553491871d043a18affc2dbc8a8.1626885907.git-series.a.fatoum@pengutronix.de/T/#meaefcdc9ac091944ddadaebe0410c2325af0032e

Cheers,
Ahmad

> 
> - Eric
> 


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH 1/1] NAX LSM: Add initial support support
From: THOBY Simon @ 2021-07-28 10:19 UTC (permalink / raw)
  To: Igor Zhbanov, linux-integrity, linux-security-module
  Cc: Igor Zhbanov, Mimi Zohar
In-Reply-To: <db1c1de0-3672-4bae-ef45-c554379f36f4@omp.ru>

Hello Igor,

first, a disclaimer: this is the first time I review a kernel patch, so everything I say
must a taken with a grain of salt (a handful, really).

On 7/7/21 3:03 AM, Igor Zhbanov wrote:
> NAX (No Anonymous Execution) is a Linux Security Module that extends DAC
> by making impossible to make anonymous and modified pages executable for
> privileged processes.
> 
> The module intercepts anonymous executable pages created with mmap() and
> mprotect() system calls.
> 
> Depending on the settings, the module can block and log violating system
> calls or log and kill the violating process.
> 

From what I see, there is also a log-only mode (MODE_PERMISSIVE) that users may
use to try this patch before deploying it. You could probably express that.

Something like "This module will log violations, and it can also block the action or kill the offending process,
depending on the enabled settings." maybe?

> Signed-off-by: Igor Zhbanov <i.zhbanov@omp.ru>
> ---
>  Documentation/admin-guide/LSM/NAX.rst   |  48 ++++
>  Documentation/admin-guide/LSM/index.rst |   1 +
>  security/Kconfig                        |  11 +-
>  security/Makefile                       |   2 +
>  security/nax/Kconfig                    |  71 +++++
>  security/nax/Makefile                   |   4 +
>  security/nax/nax-lsm.c                  | 344 ++++++++++++++++++++++++
>  7 files changed, 476 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/admin-guide/LSM/NAX.rst
>  create mode 100644 security/nax/Kconfig
>  create mode 100644 security/nax/Makefile
>  create mode 100644 security/nax/nax-lsm.c
> 
> diff --git a/Documentation/admin-guide/LSM/NAX.rst b/Documentation/admin-guide/LSM/NAX.rst
> new file mode 100644
> index 000000000000..b742f881f3d7
> --- /dev/null
> +++ b/Documentation/admin-guide/LSM/NAX.rst
> @@ -0,0 +1,48 @@
> +=======
> +NAX LSM
> +=======
> +
> +:Author: Igor Zhbanov
> +
> +NAX (No Anonymous Execution) is a Linux Security Module that extends DAC
> +by making impossible to make anonymous and modified pages executable for
> +privileged processes. The module intercepts anonymous executable pages
> +created with mmap() and mprotect() system calls.
> +
> +To select it at boot time, specify ``security=nax`` (though this will
> +disable any other LSM).
> +
> +The privileged process is a process for which any of the following is true:
> +
> +- ``uid   == 0``
> +- ``euid  == 0``
> +- ``suid  == 0``
> +- ``fsuid == 0``
> +- ``cap_effective`` has any capability except of ``kernel.nax.allowed_caps``
> +- ``cap_permitted`` has any capability except of ``kernel.nax.allowed_caps``
> +

Maybe "except those explicitly allowed in" or "except for the ones allowed in"?

> +The following sysctl parameters are available:
> +
> +* ``kernel.nax.allowed_caps``:
> +
> + Hexadecimal number representing allowed capabilities set for the privileged
> + processes.
> +

Maybe "representing the set of capabilities a non-root process can possess without being considered "privileged" by NAX"?

> +* ``kernel.nax.enforcing``:
> +
> + - 0: Only log errors (when enabled by ``kernel.nax.quiet``)
> + - 1: Forbid unsafe pages mappings (default)
> +
> +* ``kernel.nax.locked``:
> +
> + - 0: Changing of the module's sysctl parameters is allowed
> + - 1: Further changing of the module's sysctl parameters is forbidden
> +
> + Setting this parameter to ``1`` after initial setup during the system boot
> + will prevent the module disabling at the later time.
> +
> +* ``kernel.nax.quiet``:
> +
> + - 0: Log violations (default)
> + - 1: Be quiet
> + - 2: Kill the violating process and log

"quiet" probably not the right word to express that fact that it controls the NAX execution mode.
Something like "mode" or "level" would probably be clearer.
But while reading the patch below I believe this doc is simply a little out of date, because quiet can
only take two values: 0 or 1 (otherwise it would be redundant with the 'enforcing' sysctl).
So you should consider updating this document.

> diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
> index a6ba95fbaa9f..e9df7fc9a461 100644
> --- a/Documentation/admin-guide/LSM/index.rst
> +++ b/Documentation/admin-guide/LSM/index.rst
> @@ -42,6 +42,7 @@ subdirectories.
>  
>     apparmor
>     LoadPin
> +   NAX
>     SELinux
>     Smack
>     tomoyo
> diff --git a/security/Kconfig b/security/Kconfig
> index 0ced7fd33e4d..771419647ae1 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -239,6 +239,7 @@ source "security/yama/Kconfig"
>  source "security/safesetid/Kconfig"
>  source "security/lockdown/Kconfig"
>  source "security/landlock/Kconfig"
> +source "security/nax/Kconfig"
>  
>  source "security/integrity/Kconfig"
>  
> @@ -278,11 +279,11 @@ endchoice
>  
>  config LSM
>  	string "Ordered list of enabled LSMs"
> -	default "landlock,lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
> -	default "landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
> -	default "landlock,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
> -	default "landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
> -	default "landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
> +	default "nax,landlock,lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
> +	default "nax,landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
> +	default "nax,landlock,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
> +	default "nax,landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
> +	default "nax,landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"

I don't know the policy with regard to new LSMs, but enabling this one by default is maybe a bit violent?
That said, considering the default mode is SECURITY_NAX_MODE_LOG, this would just log kernel messages and
not break existing systems, so this shouldn't be a problem.

>  	help
>  	  A comma-separated list of LSMs, in initialization order.
>  	  Any LSMs left off this list will be ignored. This can be
> diff --git a/security/Makefile b/security/Makefile
> index 47e432900e24..5c261bbf4659 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -14,6 +14,7 @@ subdir-$(CONFIG_SECURITY_SAFESETID)    += safesetid
>  subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown
>  subdir-$(CONFIG_BPF_LSM)		+= bpf
>  subdir-$(CONFIG_SECURITY_LANDLOCK)	+= landlock
> +subdir-$(CONFIG_SECURITY_NAX)		+= nax
>  
>  # always enable default capabilities
>  obj-y					+= commoncap.o
> @@ -34,6 +35,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown/
>  obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
>  obj-$(CONFIG_BPF_LSM)			+= bpf/
>  obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
> +obj-$(CONFIG_SECURITY_NAX)		+= nax/
>  
>  # Object integrity file lists
>  subdir-$(CONFIG_INTEGRITY)		+= integrity
> diff --git a/security/nax/Kconfig b/security/nax/Kconfig
> new file mode 100644
> index 000000000000..60ef0964f00a
> --- /dev/null
> +++ b/security/nax/Kconfig
> @@ -0,0 +1,71 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config SECURITY_NAX
> +	bool "NAX support"
> +	depends on SECURITY
> +	default n
> +	help
> +	  This selects NAX (No Anonymous Execution), which extends DAC
> +	  support with additional system-wide security settings beyond
> +	  regular Linux discretionary access controls. Currently available
> +	  is restriction to make anonymous and modified pages executable
> +	  to privileged processes. Like capabilities, this security module
> +	  stacks with other LSMs. Further information can be found in
> +	  Documentation/admin-guide/LSM/NAX.rst.
> +
> +	  If you are unsure how to answer this question, answer N.
> +
> +config SECURITY_NAX_LOCKED
> +	bool "Lock NAX settings"
> +	depends on SECURITY_NAX
> +	help
> +	  If selected, it will not be possible to change enforcing and quiet
> +	  settings via sysctl or the kernel command line. If not selected,
> +	  it can be enabled at boot with the kernel parameter "nax_locked=1"
> +	  or "kernel.nax_locked=1" sysctl (if the settings are not locked).
> +
> +config SECURITY_NAX_QUIET
> +	bool "Silence NAX messages"
> +	depends on SECURITY_NAX
> +	help
> +	  If selected, NAX will not print violations. If not selected, it can
> +	  be enabled at boot with the kernel parameter "nax_quiet=1" or
> +	  "kernel.nax_quiet=1" sysctl (if the settings are not locked).
> +

You probably should document the boot option in the kernel-parameters.txt file.

> +choice
> +	prompt "NAX violation action mode"
> +	default SECURITY_NAX_MODE_LOG
> +	depends on SECURITY_NAX
> +	help
> +	  Select the NAX violation action mode.
> +
> +	  In the default permissive mode the violations are only logged
> +	  (if logging is not suppressed). In the enforcing mode the violations
> +	  are prohibited. And in the kill mode the process is terminated.
> +
> +	  The value can be changed at boot with the kernel parameter
> +	  "nax_mode" (0, 1, 2) or "kernel.nax_mode=" (0, 1, 2) sysctl (if the
> +	  settings are not locked).
> +
> +	config SECURITY_NAX_MODE_LOG
> +		bool "Permissive mode"
> +		help
> +		  In this mode violations are only logged (if logging is not
> +		  suppressed).
> +	config SECURITY_NAX_MODE_ENFORCING
> +		bool "Enforcing mode"
> +		help
> +		  In this mode violations are prohibited and logged (if
> +		  logging is not suppressed).
> +	config SECURITY_NAX_MODE_KILL
> +		bool "Kill mode"
> +		help
> +		  In this mode the voilating process is terminated. The event

"violating"

> +		  is logged (if logging is not suppressed).
> +endchoice
> +
> +config SECURITY_NAX_MODE
> +        int
> +        depends on SECURITY_NAX
> +        default 0 if SECURITY_NAX_MODE_LOG
> +        default 1 if SECURITY_NAX_MODE_ENFORCING
> +        default 2 if SECURITY_NAX_MODE_KILL
> diff --git a/security/nax/Makefile b/security/nax/Makefile
> new file mode 100644
> index 000000000000..9c3372210c77
> --- /dev/null
> +++ b/security/nax/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_SECURITY_NAX) := nax.o
> +
> +nax-y := nax-lsm.o
> diff --git a/security/nax/nax-lsm.c b/security/nax/nax-lsm.c
> new file mode 100644
> index 000000000000..ef99d9b36a9d
> --- /dev/null
> +++ b/security/nax/nax-lsm.c
> @@ -0,0 +1,344 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2016-2021 Open Mobile Platform LLC.
> + *
> + * Written by: Igor Zhbanov <i.zhbanov@omp.ru, izh1979@gmail.com>
> + *
> + * NAX (No Anonymous Execution) Linux Security Module
> + * This module prevents execution of the code in anonymous or modified pages.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation. */


Per checkpatch:
WARNING: Block comments use a trailing */ on a separate line
#328: FILE: security/nax/nax-lsm.c:48:
+	 * or it has any unsafe capability (even in a user namespace) */

Checkpatch also complains about a few places where you could use tabs instead
of spaces, or add a space, and so on.

As a general rule, running 'scripts/checkpatch.pl' prior to sending the patch is
considered a good practice (see 'Documentation/process/submitting-patches.rst').

> +
> +#define pr_fmt(fmt) "NAX: " fmt
> +
> +#include <linux/capability.h>
> +#include <linux/cred.h>
> +#include <linux/ctype.h>
> +#include <linux/lsm_hooks.h>
> +#include <linux/mman.h>
> +#include <linux/sched.h>
> +#include <linux/securebits.h>
> +#include <linux/security.h>
> +#include <linux/sysctl.h>
> +#include <linux/uidgid.h>
> +
> +#define NAX_MODE_PERMISSIVE 0 /* Log only             */
> +#define NAX_MODE_ENFORCING  1 /* Enforce and log      */
> +#define NAX_MODE_KILL       2 /* Kill process and log */
> +
> +static int mode   = CONFIG_SECURITY_NAX_MODE,
> +	   quiet  = IS_ENABLED(CONFIG_SECURITY_NAX_QUIET),
> +	   locked = IS_ENABLED(CONFIG_SECURITY_NAX_LOCKED);
> +
> +#define ALLOWED_CAPS_HEX_LEN (_KERNEL_CAPABILITY_U32S * 8)
> +
> +static char allowed_caps_hex[ALLOWED_CAPS_HEX_LEN + 1];
> +static kernel_cap_t allowed_caps = CAP_EMPTY_SET;
> +
> +static int
> +is_privileged_process(void)
> +{
> +	const struct cred *cred;
> +	kuid_t root_uid;
> +
> +	cred = current_cred();
> +	root_uid = make_kuid(cred->user_ns, 0);
> +	/* We count a process as privileged if it any of its IDs is zero
> +	 * or it has any unsafe capability (even in a user namespace) */
> +	if ((!issecure(SECURE_NOROOT) && (uid_eq(cred->uid,   root_uid) ||
> +					  uid_eq(cred->euid,  root_uid) ||
> +					  uid_eq(cred->suid,  root_uid) ||
> +					  uid_eq(cred->fsuid, root_uid))) ||
> +	    (!cap_issubset(cred->cap_effective, allowed_caps)) ||
> +	    (!cap_issubset(cred->cap_permitted, allowed_caps)))
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static void
> +log_warn(const char *reason)
> +{
> +	if (quiet)
> +		return;
> +
> +	pr_warn_ratelimited("%s: pid=%d, uid=%u, comm=\"%s\"\n",
> +		            reason, current->pid,
> +		            from_kuid(&init_user_ns, current_cred()->uid),
> +	                              current->comm);

Have you considered writing to the audit log instead of the kernel messages directly?
(not saying that this is necessarily better, but is there a reasoning to prefer one or
the other here? Audit logs are often consumed by automated tools and it may be more pratical
for people to detect and treat violations if the messages were pushed to the audit log
- but conversely, that requires defining and maintaining a stable log format for consumers)

> +}
> +
> +static void
> +kill_current_task(void)
> +{
> +	pr_warn("Killing pid=%d, uid=%u, comm=\"%s\"\n",
> +		current->pid, from_kuid(&init_user_ns, current_cred()->uid),
> +		current->comm);

The same question applies here.

> +	force_sig(SIGKILL);
> +}
> +
> +static int
> +nax_mmap_file(struct file *file, unsigned long reqprot,
> +	      unsigned long prot, unsigned long flags)
> +{
> +	int ret = 0;
> +
> +	if (mode == NAX_MODE_PERMISSIVE && quiet)
> +		return 0; /* Skip further checks in this case */
> +
> +	if (!(prot & PROT_EXEC)) /* Not executable memory */
> +		return 0;
> +
> +	if (!is_privileged_process())
> +		return 0; /* Not privileged processes can do anything */
> +
> +	if (!file) { /* Anonymous executable memory */
> +		log_warn("MMAP_ANON_EXEC");
> +		ret = -EACCES;
> +	} else if (prot & PROT_WRITE) { /* Mapping file RWX */
> +		log_warn("MMAP_FILE_WRITE_EXEC");
> +		ret = -EACCES;
> +	}
> +
> +	if (mode == NAX_MODE_KILL)
> +		kill_current_task();
> +
> +	return (mode != NAX_MODE_PERMISSIVE) ? ret : 0;
> +}
> +
> +static int
> +nax_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
> +		  unsigned long prot)
> +{
> +	if (mode == NAX_MODE_PERMISSIVE && quiet)
> +		return 0; /* Skip further checks in this case */
> +
> +	if (!(prot & PROT_EXEC)) /* Not executable memory */
> +		return 0;
> +
> +	if (!is_privileged_process())
> +		return 0; /* Not privileged processes can do anything */
> +
> +	if (!(vma->vm_flags & VM_EXEC)) {
> +		int ret = 0;
> +
> +		if (vma->vm_start >= vma->vm_mm->start_brk &&
> +		    vma->vm_end   <= vma->vm_mm->brk) {
> +			log_warn("MPROTECT_EXEC_HEAP");
> +			ret = -EACCES;
> +		} else if (!vma->vm_file &&
> +			   ((vma->vm_start <= vma->vm_mm->start_stack &&
> +			     vma->vm_end   >= vma->vm_mm->start_stack) ||
> +			    vma_is_stack_for_current(vma))) {
> +			log_warn("MPROTECT_EXEC_STACK");
> +			ret = -EACCES;
> +		} else if (vma->vm_file && vma->anon_vma) {
> +			/* We are making executable a file mapping that has
> +			 * had some COW done. Since pages might have been
> +			 * written, check ability to execute the possibly
> +			 * modified content. This typically should only
> +			 * occur for text relocations. */
> +			log_warn("MPROTECT_EXEC_MODIFIED");
> +			ret = -EACCES;
> +		}
> +
> +		if (ret) {
> +			if (mode == NAX_MODE_KILL)
> +				kill_current_task();
> +
> +			return (mode != NAX_MODE_PERMISSIVE) ? ret : 0;
> +		}
> +	}
> +
> +	return nax_mmap_file(vma->vm_file, reqprot, prot,
> +			     vma->vm_flags & VM_SHARED);

Considering many checks in nax_mmap_file were already done in this function,
wouldn't it be simpler to write the rest the function too (and you could distinguish
MRPOTECT_ANON_EXEC and MMAP_ANON_EXEC that way). What do you think of something like:

> -
> -		if (ret) {
> -			if (mode == NAX_MODE_KILL)
> -				kill_current_task();
> -
> -			return (mode != NAX_MODE_PERMISSIVE) ? ret : 0;
> -		}
> -	}
> -
> -	return nax_mmap_file(vma->vm_file, reqprot, prot,
> -			     vma->vm_flags & VM_SHARED);
> +	} else {
> +		if (!vma->vm_file) { /* Anonymous executable memory */
> +			log_warn("MRPOTECT_ANON_EXEC");
> +			ret = -EACCES;
> +		} else if (prot & PROT_WRITE) { /* remapping the file as RWX */
> +			log_warn("MPROTECT_FILE_WRITE_EXEC");
> +			ret = -EACCES;
> +		}
> +	}
> +
> +	if (ret && mode == NAX_MODE_KILL)
> +		kill_current_task();
> +
> +	return (mode != NAX_MODE_PERMISSIVE) ? ret : 0;


> +}
> +
> +static struct security_hook_list nax_hooks[] __lsm_ro_after_init = {
> +	LSM_HOOK_INIT(mmap_file, nax_mmap_file),
> +	LSM_HOOK_INIT(file_mprotect, nax_file_mprotect),
> +};
> +
> +#ifdef CONFIG_SYSCTL
> +
> +static int
> +nax_dointvec_minmax(struct ctl_table *table, int write,
> +		    void *buffer, size_t *lenp, loff_t *ppos)
> +{
> +	if (write && (!capable(CAP_SYS_ADMIN) || locked))
> +		return -EPERM;
> +
> +	return proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> +}
> +
> +static int
> +nax_dostring(struct ctl_table *table, int write, void *buffer,
> +             size_t *lenp, loff_t *ppos)
> +{
> +	if (write) {
> +		int error;
> +		char *buf = (char *)buffer;
> +		size_t len = *lenp, i;
> +		kernel_cap_t caps = CAP_EMPTY_SET;

The kernel style guide mandates that all variables are only defined at the beggining of the
function, and not at the beggining of any block like C89.

> +
> +		if (!capable(CAP_SYS_ADMIN) || locked)
> +			return -EPERM;
> +
> +		/* Do not allow trailing garbage or excessive length */
> +		if (len == ALLOWED_CAPS_HEX_LEN + 1) {
> +			if (buf[--len] != '\n')
> +				return -EINVAL> +		} else if (len > ALLOWED_CAPS_HEX_LEN || len <= 0)
> +			return -EINVAL;
> +
> +		if ((error = proc_dostring(table, write, buffer, lenp, ppos)))
> +			return error;
> +

Nit: considering that allowed_caps_hex is only used in this function, and that it represents a small amount of
stack space, couldn't you define it directly in the function?

> +		len = strlen(allowed_caps_hex);
> +		for (i = 0; i < len; i++)
> +			if (!isxdigit(allowed_caps_hex[i]))
> +				return -EINVAL;> +
> +		for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) {
> +			unsigned long l;
> +
> +			if (kstrtoul(allowed_caps_hex +
> +			             (len >= 8 ? len - 8 : 0), 16, &l))
> +				return -EINVAL;
> +
> +			caps.cap[i] = l;
> +			if (len < 8)
> +				break;
> +
> +			len -= 8;
> +			allowed_caps_hex[len] = '\0';
> +		}
> +
> +		allowed_caps = cap_intersect(caps, CAP_FULL_SET);

This operation doesn't look atomic to me, and I think other operations couldn run while this
write is ongoing.
While users will probably not write to this often (and a proper system would be locked anyway,
otherwise the attacker would just have to write to the NAX "mode" sysctl to disable it and carry
on his attack), it could break programs (deny the action of kill the process) that perform a
mmap() or a mprotect() and read garbage in allowed_caps because a write to the structure was
running concurrently.

You should maybe consider a way to ensure either the update is atomic or the update is in a
critical section.

> +		return 0;
> +	} else {
> +		unsigned i;
> +
> +		CAP_FOR_EACH_U32(i)
> +			snprintf(allowed_caps_hex + i * 8, 9, "%08x",
> +			         allowed_caps.cap[CAP_LAST_U32 - i]);
> +
> +		return proc_dostring(table, write, buffer, lenp, ppos);
> +	}
> +}
> +
> +struct ctl_path nax_sysctl_path[] = {
> +	{ .procname = "kernel" },
> +	{ .procname = "nax"    },
> +	{ }
> +};
> +
> +static int max_mode = NAX_MODE_KILL;
> +
> +static struct ctl_table nax_sysctl_table[] = {
> +	{
> +		.procname     = "allowed_caps",
> +		.data         = allowed_caps_hex,
> +		.maxlen       = ALLOWED_CAPS_HEX_LEN + 1,
> +		.mode         = 0644,
> +		.proc_handler = nax_dostring,
> +	}, {
> +		.procname     = "locked",
> +		.data         = &locked,
> +		.maxlen       = sizeof(int),
> +		.mode         = 0644,
> +		.proc_handler = nax_dointvec_minmax,
> +		.extra1       = SYSCTL_ZERO,
> +		.extra2       = SYSCTL_ONE,
> +	}, {
> +		.procname     = "mode",
> +		.data         = &mode,
> +		.maxlen       = sizeof(int),
> +		.mode         = 0644,
> +		.proc_handler = nax_dointvec_minmax,
> +		.extra1       = SYSCTL_ZERO,
> +		.extra2       = &max_mode,
> +	}, {
> +		.procname     = "quiet",
> +		.data         = &quiet,
> +		.maxlen       = sizeof(int),
> +		.mode         = 0644,
> +		.proc_handler = nax_dointvec_minmax,
> +		.extra1       = SYSCTL_ZERO,
> +		.extra2       = SYSCTL_ONE,
> +	},
> +	{ }
> +};
> +
> +static void __init
> +nax_init_sysctl(void)
> +{
> +	if (!register_sysctl_paths(nax_sysctl_path, nax_sysctl_table))
> +		panic("NAX: sysctl registration failed.\n");
> +}
> +
> +#else /* !CONFIG_SYSCTL */
> +
> +static inline void
> +nax_init_sysctl(void)
> +{
> +
> +}
> +
> +#endif /* !CONFIG_SYSCTL */
> +
> +static int __init setup_mode(char *str)
> +{
> +	unsigned long val;
> +
> +	if (!locked && !kstrtoul(str, 0, &val)) {
> +		if (val > max_mode){
> +			pr_err("Invalid 'nax_mode' parameter value (%s)\n",
> +			       str);
> +			val = max_mode;
> +		}
> +
> +		mode = val;
> +	}
> +
> +	return 1;
> +}
> +__setup("nax_mode=", setup_mode);
> +
> +static int __init setup_quiet(char *str)
> +{
> +	unsigned long val;
> +
> +	if (!locked && !kstrtoul(str, 0, &val))
> +		quiet = val ? 1 : 0;

The order of the kernel parameters will have an impact on NAX behavior.

nax_mode=1 nax_locked=1 and nax_locked=1 nax_mode=1 will end up with the same behavior.
in the first case the mode is enforced, in the second case it isn't (well unless you changed
 the kernel configuration from the default) and it can't be enabled later either.

Is that desired?

> +
> +	return 1;
> +}
> +__setup("nax_quiet=", setup_quiet);
> +
> +static int __init setup_locked(char *str)
> +{
> +	unsigned long val;
> +
> +	if (!locked && !kstrtoul(str, 0, &val))
> +		locked = val ? 1 : 0;
> +
> +	return 1;
> +}
> +__setup("nax_locked=", setup_locked);
> +
> +static __init int
> +nax_init(void)
> +{
> +	pr_info("Starting.\n");
> +	security_add_hooks(nax_hooks, ARRAY_SIZE(nax_hooks), "nax");
> +	nax_init_sysctl();
> +
> +	return 0;
> +}
> +
> +DEFINE_LSM(nax) = {
> +	.name = "nax",
> +	.init = nax_init,
> +};
> 

Review aside, this patch is certainly interesting for providing a simple way to block anonymous excutable mappins,
so thanks for the submission.
I have to wonder: wouldn't it be interesting to add an option to enable NAX for all processes on the system,
as you mentioned in the cover letter?

Have a nice day,
Simon

^ permalink raw reply

* Re: [RFC][PATCH v2 01/12] diglim: Overview
From: Mauro Carvalho Chehab @ 2021-07-28 11:10 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-2-roberto.sassu@huawei.com>

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

> Add an overview of DIGLIM to Documentation/security/diglim/introduction.rst
> and the architecture to Documentation/security/diglim/architecture.rst
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  .../security/diglim/architecture.rst          |  45 ++
>  Documentation/security/diglim/index.rst       |  11 +
>  .../security/diglim/introduction.rst          | 631 ++++++++++++++++++
>  Documentation/security/index.rst              |   1 +
>  MAINTAINERS                                   |   9 +
>  5 files changed, 697 insertions(+)
>  create mode 100644 Documentation/security/diglim/architecture.rst
>  create mode 100644 Documentation/security/diglim/index.rst
>  create mode 100644 Documentation/security/diglim/introduction.rst
> 
> diff --git a/Documentation/security/diglim/architecture.rst b/Documentation/security/diglim/architecture.rst
> new file mode 100644
> index 000000000000..a54fe2453715
> --- /dev/null
> +++ b/Documentation/security/diglim/architecture.rst
> @@ -0,0 +1,45 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Architecture
> +============
> +
> +This section introduces the high level architecture of DIGLIM.
> +
> +::
> +
> + 5. add/delete from hash table and add refs to digest list
> +        +---------------------------------------------+
> +        |                            +-----+   +-------------+         +--+
> +        |                            | key |-->| digest refs |-->...-->|  |
> +        V                            +-----+   +-------------+         +--+
> + +-------------+                     +-----+   +-------------+
> + | digest list |                     | key |-->| digest refs |
> + |  (compact)  |                     +-----+   +-------------+
> + +-------------+                     +-----+   +-------------+
> +        ^ 4. copy to                 | key |-->| digest refs |
> +        |    kernel memory           +-----+   +-------------+ kernel space
> + --------------------------------------------------------------------------
> +        ^                                          ^             user space
> +        |<----------------+       3b. upload       |
> + +-------------+   +------------+                  | 6. query digest
> + | digest list |   | user space | 2b. convert
> + |  (compact)  |   |   parser   |
> + +-------------+   +------------+
> + 1a. upload               ^       1b. read
> +                          |
> +                   +------------+
> +                   | RPM header |
> +                   +------------+
> +
> +
> +As mentioned before, digest lists can be uploaded directly if they are in

"before"? This is at the beginning of this document ;-)

You should probably add a reference to introduction.rst here, like:

	As mentioned at Documentation/security/diglim/introduction.rst, ...


> +the compact format (step 1a) or can be uploaded indirectly by the user
> +space parser if they are in an alternative format (steps 1b-3b).
> +
> +During upload, the kernel makes a copy of the digest list to the kernel
> +memory (step 4), and creates the necessary structures to index the digests
> +(hash table and a linked list of digest list references to locate the
> +digests in the digest list) (step 5).
> +
> +Finally, digests can be searched from user space through a securityfs file
> +(step 6) or by the kernel itself.

This probably applies to Documentation/security as a hole, but the
best is to split the documents on two separate parts:
	- the kAPI and internals;
	- the admin-guide part.

The audience for the admin-guide is distribution pagagers and
syssadmins.

> diff --git a/Documentation/security/diglim/index.rst b/Documentation/security/diglim/index.rst
> new file mode 100644
> index 000000000000..0fc5ab019bc0
> --- /dev/null
> +++ b/Documentation/security/diglim/index.rst
> @@ -0,0 +1,11 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +======================================
> +Digest Lists Integrity Module (DIGLIM)
> +======================================
> +
> +.. toctree::
> +   :maxdepth: 1
> +
> +   introduction
> +   architecture
> diff --git a/Documentation/security/diglim/introduction.rst b/Documentation/security/diglim/introduction.rst
> new file mode 100644
> index 000000000000..d8d8b2a17222
> --- /dev/null
> +++ b/Documentation/security/diglim/introduction.rst
> @@ -0,0 +1,631 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Introduction
> +============
> +
> +Digest Lists Integrity Module (DIGLIM) is a new component added to the
> +integrity subsystem in the kernel, primarily aiming to aid Integrity

I would replace:

	"is a new component added to" -> "is a component of"

As this is the kind of text that tends to be outdated with time...
Imagine someone reading this paragraph maybe 10 years in the future ;-)

> +Measurement Architecture (IMA) in the process of checking the integrity of
> +file content and metadata. It accomplishes this task by storing reference
> +values coming from software vendors and by reporting whether or not the
> +digest of file content or metadata calculated by IMA (or EVM) is found
> +among those values. In this way, IMA can decide, depending on the result of
> +a query, if a measurement should be taken or access to the file should be
> +granted. The `Security Assumptions`_ section explains more in detail why
> +this component has been placed in the kernel.
> +
> +The main benefits of using IMA in conjunction with DIGLIM are the ability
> +to implement advanced remote attestation schemes based on the usage of a
> +TPM key for establishing a TLS secure channel [1][2], and to reduce the
> +burden on Linux distribution vendors to extend secure boot at OS level to
> +applications.
> +
> +DIGLIM does not have the complexity of feature-rich databases. In fact, its
> +main functionality comes from the hash table primitives already in the
> +kernel. It does not have an ad-hoc storage module, it just indexes data in
> +a fixed format (digest lists, a set of concatenated digests preceded by a
> +header), copied to kernel memory as they are. Lastly, it does not support
> +database-oriented languages such as SQL, but only accepts a digest and its
> +algorithm as a query.
> +
> +The only digest list format supported by DIGLIM is called ``compact``.
> +However, Linux distribution vendors don't have to generate new digest lists
> +in this format for the packages they release, as already available
> +information, such as RPM headers and DEB package metadata, can be already
> +used as a source for reference values (they already include file digests),

	-ETOMANY_already

as "already" available... can be "already" ... "already" include...

I would simplify the above text removing such redundancy.

> +with a user space parser taking care of the conversion to the compact
> +format.
> +
> +Although one might perceive that storing file or metadata digests for a
> +Linux distribution would significantly increase the memory usage, this does
> +not seem to be the case. As an anticipation of the evaluation done in the
> +`Preliminary Performance Evaluation`_ section, protecting binaries and
> +shared libraries of a minimal Fedora 33 installation requires 208K of
> +memory for the digest lists plus 556K for indexing.
> +


> +In exchange for a slightly increased memory usage, DIGLIM improves the
> +performance of the integrity subsystem. In the considered scenario, IMA
> +measurement and appraisal with digest lists requires respectively less than
> +one quarter and less than half the time, compared to the current solution.

I found this paragraph a little bit confusing to understand. Could you
please improve the description?

I mean: 

	what improved by one quarter?
	what improved by "less than half of the time"?

> +
> +DIGLIM also keeps track of whether digest lists have been processed in some
> +way (e.g. measured or appraised by IMA). This is important for example for
> +remote attestation, so that remote verifiers understand what has been
> +uploaded to the kernel.
> +

> +DIGLIM behaves like a transactional database, i.e. it has the ability to
> +roll back to the beginning of the transaction if an error occurred during
> +the addition of a digest list (the deletion operation always succeeds).

I don't think it makes sense to compare it with a transactional database.

I would say, instead, something like:

	The inserts on DIGLIM are atomic: if an error occurs during the addition
	of a digest list, it rolls back the entire insert operation.


> +This capability has been tested with an ad-hoc fault injection mechanism
> +capable of simulating failures during the operations.
> +
> +Finally, DIGLIM exposes to user space, through securityfs, the digest lists
> +currently loaded, the number of digests added, a query interface and an
> +interface to set digest list labels.
> +
> +[1] LSS EU 2019
> +
> +-   slides:
> +    https://static.sched.com/hosted_files/lsseu2019/bd/secure_attested_communication_channels_lss_eu_2019.pdf
> +-   video: https://youtu.be/mffdQgkvDNY
> +
> +[2] FutureTPM EU project, final review meeting demo
> +
> +-   slides:
> +    https://futuretpm.eu/images/07-3-FutureTPM-Final-Review-Slides-WP6-Device-Management-Use-Case-HWDU.pdf
> +-   video: https://vimeo.com/528251864/4c1d55abcd

The above won't generate any cross-references with Sphinx.

For it correct syntax, see:
	https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#citations


> +
> +
> +Binary Integrity
> +----------------
> +
> +Integrity is a fundamental security property in information systems.

> +Integrity could be described as the condition in which a generic
> +component is just after it has been released by the entity that created it.

Sounds a weird description for me. (ISC)2 defines integrity on its
glossary[1] as:

	"Guarding against improper information modification or destruction and
	 includes ensuring information non-repudiation and authenticity."

[1] https://www.isc2.org/Certifications/CISSP/CISSP-Student-Glossary

> +One way to check whether a component is in this condition (called binary
> +integrity) is to calculate its digest and to compare it with a reference
> +value (i.e. the digest calculated in controlled conditions, when the
> +component is released).
> +
> +IMA, a software part of the integrity subsystem, can perform such
> +evaluation and execute different actions:
> +
> +- store the digest in an integrity-protected measurement list, so that it
> +  can be sent to a remote verifier for analysis;
> +- compare the calculated digest with a reference value (usually protected
> +  with a signature) and deny operations if the file is found corrupted;
> +- store the digest in the system log.
> +
> +


> +Contribution
> +------------

I would rename this chapter to "Benefits".

> +
> +DIGLIM further enhances the capabilities offered by IMA-based solutions
> +and, at the same time, makes them more practical to adopt by reusing
> +existing sources as reference values for integrity decisions.
> +
> +Possible sources for digest lists are:
> +
> +- RPM headers;
> +- Debian repository metadata.
> +
> +
> +Benefits for IMA Measurement
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +One of the issues that arises when files are measured by the OS is that,
> +due to parallel execution, the order in which file accesses happen cannot
> +be predicted. Since the TPM Platform Configuration Register (PCR) extend
> +operation, executed after each file measurement, cryptographically binds
> +the current measurement to the previous ones, the PCR value at the end of a
> +workload cannot be predicted too.
> +
> +Thus, even if the usage of a TPM key, bound to a PCR value, should be
> +allowed when only good files were accessed, the TPM could unexpectedly deny
> +an operation on that key if files accesses did not happen as stated by the
> +key policy (which allows only one of the possible sequences).
> +
> +DIGLIM solves this issue by making the PCR value stable over the time and
> +not dependent on file accesses. The following figure depicts the current
> +and the new approaches:
> +
> +::
> +
> + IMA measurement list (current)
> +
> + entry#        1st boot               2nd boot               3rd boot
> +       +----+---------------+ +----+---------------+ +----+---------------+
> + 1:    | 10 | file1 measur. | | 10 | file3 measur. | | 10 | file2 measur. |
> +       +----+---------------+ +----+---------------+ +----+---------------+
> + 2:    | 10 | file2 measur. | | 10 | file2 measur. | | 10 | file3 measur. |
> +       +----+---------------+ +----+---------------+ +----+---------------+
> + 3:    | 10 | file3 measur. | | 10 | file1 measur. | | 10 | file4 measur. |
> +       +----+---------------+ +----+---------------+ +----+---------------+
> +
> + PCR:  Extend              != Extend              != Extend
> +       file1, file2, file3    file3, file2, file1    file2, file3, file4
> +
> +
> + PCR Extend definition:
> +
> +       PCR(new value) = Hash(Hash(meas. entry), PCR(previous value))
> +
> +A new entry in the measurement list is created by IMA for each file access.
> +Assuming that ``file1``, ``file2`` and ``file3`` are files provided by the
> +software vendor, ``file4`` is an unknown file, the first two PCR values
> +above represent a good system state, the third a bad system state. The PCR
> +values are the result of the PCR extend operation performed for each
> +measurement entry with the digest of the measurement entry as an input.
> +
> +::
> +
> + IMA measurement list (with DIGLIM)
> +
> + dlist
> + +--------------+
> + |    header    |
> + +--------------+
> + | file1 digest |
> + | file2 digest |
> + | file3 digest |
> + +--------------+
> +
> +``dlist`` is a digest list containing the digest of ``file1``, ``file2``
> +and ``file3``. In the intended scenario, it is generated by a software
> +vendor at the end of the building process, and retrieved by the
> +administrator of the system where the digest list is loaded.
> +
> +::
> +
> + entry#        1st boot               2nd boot               3rd boot
> +       +----+---------------+ +----+---------------+ +----+---------------+
> + 0:    | 11 | dlist measur. | | 11 | dlist measur. | | 11 | dlist measur. |
> +       +----+---------------+ +----+---------------+ +----+---------------+
> + 1:    < file1 measur. skip > < file3 measur. skip > < file2 measur. skip >
> +
> + 2:    < file2 measur. skip > < file2 measur. skip > < file3 measur. skip >
> +                                                     +----+---------------+
> + 3:    < file3 measur. skip > < file1 measur. skip > | 11 | file4 measur. |
> +                                                     +----+---------------+
> +
> + PCR:  Extend               = Extend              != Extend
> +       dlist                  dlist                  dlist, file4
> +
> +
> +The first entry in the measurement list contains the digest of the digest
> +list uploaded to the kernel at kernel initialization time.
> +
> +When a file is accessed, IMA queries DIGLIM with the calculated file digest
> +and, if it is found, IMA skips the measurement.
> +
> +Thus, the only information sent to remote verifiers are: the list of
> +files that could possibly be accessed (from the digest list), but not if
> +they were accessed and when; the measurement of unknown files.
> +
> +Despite providing less information, this solution has the advantage that
> +the good system state (i.e. when only ``file1``, ``file2`` and ``file3``
> +are accessed) now can be represented with a deterministic PCR value (the
> +PCR is extended only with the measurement of the digest list). Also, the
> +bad system state can still be distinguished from the good state (the PCR is
> +extended also with the measurement of ``file4``).
> +
> +If a TPM key is bound to the good PCR value, the TPM would allow the key to
> +be used if ``file1``, ``file2`` or ``file3`` are accessed, regardless of
> +the sequence in which they are accessed (the PCR value does not change),
> +and would revoke the permission when the unknown ``file4`` is accessed (the
> +PCR value changes). If a system is able to establish a TLS connection with
> +a peer, this implicitly means that the system was in a good state (i.e.
> +``file4`` was not accessed, otherwise the TPM would have denied the usage
> +of the TPM key due to the key policy).
> +
> +
> +Benefits for IMA Appraisal
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Extending secure boot to applications means being able to verify the
> +provenance of files accessed. IMA does it by verifying file signatures with
> +a key that it trusts, which requires Linux distribution vendors to
> +additionally include in the package header a signature for each file that
> +must be verified (there is the dedicated ``RPMTAG_FILESIGNATURES`` section
> +in the RPM header).
> +
> +The proposed approach would be instead to verify data provenance from
> +already available metadata (file digests) in existing packages. IMA would
> +verify the signature of package metadata and search file digests extracted
> +from package metadata and added to the hash table in the kernel.
> +
> +For RPMs, file digests can be found in the ``RPMTAG_FILEDIGESTS`` section
> +of ``RPMTAG_IMMUTABLE``, whose signature is in ``RPMTAG_RSAHEADER``. For
> +DEBs, file digests (unsafe to use due to a weak digest algorithm) can be
> +found in the ``md5sum`` file, which can be indirectly verified from
> +``Release.gpg``.
> +
> +The following figure highlights the differences between the current and the
> +proposed approach.
> +
> +::
> +
> + IMA appraisal (current solution, with file signatures):
> +
> +                                                         appraise
> +                                                      +-----------+
> +                                                      V           |
> + +-------------------------+-----+         +-------+-----+        |
> + | RPM header              |     | ima rpm | file1 | sig |        |
> + | ...                     |     | plugin  +-------+-----+     +-----+
> + | file1 sig [to be added] | sig |-------->      ...           | IMA |
> + | ...                     |     |         +-------+-----+     +-----+
> + | fileN sig [to be added] |     |         | fileN | sig |
> + +-------------------------+-----+         +-------+-----+
> +
> +In this case, file signatures must be added to the RPM header, so that the
> +``ima`` rpm plugin can extract them together with the file content. The RPM
> +header signature is not used.
> +
> +::
> +
> + IMA appraisal (with DIGLIM):
> +
> +                                    kernel hash table
> +                                    with RPM header content
> +                                    +---+    +--------------+
> +                                    |   |--->| file1 digest |
> +                                    +---+    +--------------+
> +                                     ...
> +                                    +---+         appraise (file1)
> +                                    |   |  <--------------+
> + +----------------+-----+           +---+                 |
> + | RPM header     |     |             ^                   |
> + | ...            |     | digest_list |                   |
> + | file1 digest   | sig | rpm plugin  |    +-------+   +-----+
> + | ...            |     |-------------+--->| file1 |   | IMA |
> + | fileN digest   |     |                  +-------+   +-----+
> + +----------------+-----+                                 |
> +                     ^                                    |
> +                     +------------------------------------+
> +                             appraise (RPM header)
> +
> +In this case, the RPM header is used as it is, and its signature is used
> +for IMA appraisal. Then, the ``digest_list`` rpm plugin executes the user
> +space parser to parse the RPM header and add the extracted digests to an
> +hash table in the kernel. IMA appraisal of the files in the RPM package
> +consists in searching their digest in the hash table.
> +
> +Other than reusing available information as digest list, another advantage
> +is the lower computational overhead compared to the solution with file
> +signatures (only one signature verification for many files and digest
> +lookup, instead of per file signature verification, see `Preliminary
> +Performance Evaluation`_ for more details).
> +
> +
> +Lifecycle
> +---------
> +
> +The lifecycle of DIGLIM is represented in the following figure:
> +
> +::

You could just use:

	The lifecycle of DIGLIM is represented in the following figure::

> +
> + Vendor premises (release process with modifications):
> +
> + +------------+   +-----------------------+   +------------------------+
> + | 1. build a |   | 2. generate and sign  |   | 3. publish the package |
> + |    package |-->|    a digest list from |-->|    and digest list in  |
> + |            |   |    packaged files     |   |    a repository        |
> + +------------+   +-----------------------+   +------------------------+
> +                                                                 |
> +                                                                 |
> + User premises:                                                  |
> +                                                                 V
> + +---------------------+   +------------------------+   +-----------------+
> + | 6. use digest lists |   | 5. download the digest |   | 4. download and |
> + |    for measurement  |<--|    list and upload to  |<--|    install the  |
> + |    and/or appraisal |   |    the kernel          |   |    package      |
> + +---------------------+   +------------------------+   +-----------------+
> +
> +The figure above represents all the steps when a digest list is
> +generated separately. However, as mentioned in `Contribution`_, in most
> +cases existing packages can be already used as a source for digest lists,
> +limiting the effort for software vendors.
> +
> +If, for example, RPMs are used as a source for digest lists, the figure
> +above becomes:
> +
> +::

Same here.

> +
> + Vendor premises (release process without modifications):
> +
> + +------------+   +------------------------+
> + | 1. build a |   | 2. publish the package |
> + |    package |-->|    in a repository     |---------------------+
> + |            |   |                        |                     |
> + +------------+   +------------------------+                     |
> +                                                                 |
> +                                                                 |
> + User premises:                                                  |
> +                                                                 V
> + +---------------------+   +------------------------+   +-----------------+
> + | 5. use digest lists |   | 4. extract digest list |   | 3. download and |
> + |    for measurement  |<--|    from the package    |<--|    install the  |
> + |    and/or appraisal |   |    and upload to the   |   |    package      |
> + |                     |   |    kernel              |   |                 |
> + +---------------------+   +------------------------+   +-----------------+
> +
> +Step 4 can be performed with the ``digest_list`` rpm plugin and the user
> +space parser, without changes to rpm itself.
> +
> +
> +Security Assumptions
> +--------------------
> +
> +As mentioned in the `Introduction`_, DIGLIM will be primarily used in
> +conjunction with IMA to enforce a mandatory policy on all user space
> +processes, including those owned by root. Even root, in a system with a
> +locked-down kernel, cannot affect the enforcement of the mandatory policy
> +or, if changes are permitted, it cannot do so without being detected.
> +
> +Given that the target of the enforcement are user space processes, DIGLIM
> +cannot be placed in the target, as a Mandatory Access Control (MAC) design
> +is required to have the components responsible to enforce the mandatory
> +policy separated from the target.
> +
> +While locking-down a system and limiting actions with a mandatory policy is
> +generally perceived by users as an obstacle, it has noteworthy benefits for
> +the users themselves.
> +
> +First, it would timely block attempts by malicious software to steal or
> +misuse user assets. Although users could query the package managers to
> +detect them, detection would happen after the fact, or it wouldn't happen
> +at all if the malicious software tampered with package managers. With a
> +mandatory policy enforced by the kernel, users would still be able to
> +decide which software they want to be executed except that, unlike package
> +managers, the kernel is not affected by user space processes or root.
> +
> +Second, it might make systems more easily verifiable from outside, due to
> +the limited actions the system allows. When users connect to a server, not
> +only they would be able to verify the server identity, which is already
> +possible with communication protocols like TLS, but also if the software
> +running on that server can be trusted to handle their sensitive data.
> +
> +
> +Adoption
> +--------
> +
> +A former version of DIGLIM is used in the following OSes:
> +
> +- openEuler 20.09
> +  https://github.com/openeuler-mirror/kernel/tree/openEuler-20.09
> +
> +- openEuler 21.03
> +  https://github.com/openeuler-mirror/kernel/tree/openEuler-21.03
> +
> +Originally, DIGLIM was part of IMA (known as IMA Digest Lists). In this
> +version, it has been redesigned as a standalone module with an API that
> +makes its functionality accessible by IMA and, eventually, other
> +subsystems.
> +
> +User Space Support
> +------------------
> +
> +Digest lists can be generated and managed with ``digest-list-tools``:
> +
> +https://github.com/openeuler-mirror/digest-list-tools
> +
> +It includes two main applications:
> +
> +- ``gen_digest_lists``: generates digest lists from files in the
> +  filesystem or from the RPM database (more digest list sources can be
> +  supported);
> +- ``manage_digest_lists``: converts and uploads digest lists to the
> +  kernel.
> +
> +Integration with rpm is done with the ``digest_list`` plugin:
> +
> +https://gitee.com/src-openeuler/rpm/blob/master/Add-digest-list-plugin.patch
> +
> +This plugin writes the RPM header and its signature to a file, so that the
> +file is ready to be appraised by IMA, and calls the user space parser to
> +convert and upload the digest list to the kernel.
> +
> +
> +Simple Usage Example (Tested with Fedora 33)
> +--------------------------------------------
> +
> +1. Digest list generation (RPM headers and their signature are copied to
> +   the specified directory):
> +
> +.. code-block:: bash
> +
> + # mkdir /etc/digest_lists
> + # gen_digest_lists -t file -f rpm+db -d /etc/digest_lists -o add
> +
> +2. Digest list upload with the user space parser:
> +
> +.. code-block:: bash
> +
> + # manage_digest_lists -p add-digest -d /etc/digest_lists
> +
> +3. First digest list query:
> +
> +.. code-block:: bash
> +
> + # echo sha256-$(sha256sum /bin/cat) > /sys/kernel/security/integrity/diglim/digest_query
> + # cat /sys/kernel/security/integrity/diglim/digest_query
> +   sha256-[...]-0-file_list-rpm-coreutils-8.32-18.fc33.x86_64 (actions: 0): version: 1, algo: sha256, type: 2, modifiers: 1, count: 106, datalen: 3392
> +
> +4. Second digest list query:
> +
> +.. code-block:: bash
> +
> + # echo sha256-$(sha256sum /bin/zip) > /sys/kernel/security/integrity/diglim/digest_query
> + # cat /sys/kernel/security/integrity/diglim/digest_query
> +   sha256-[...]-0-file_list-rpm-zip-3.0-27.fc33.x86_64 (actions: 0): version: 1, algo: sha256, type: 2, modifiers: 1, count: 4, datalen: 128
> +
> +
> +Preliminary Performance Evaluation
> +----------------------------------
> +
> +This section provides an initial estimation of the overhead introduced by
> +DIGLIM. The estimation has been performed on a Fedora 33 virtual machine
> +with 1447 packages installed. The virtual machine has 16 vCPU (host CPU:
> +AMD Ryzen Threadripper PRO 3955WX 16-Cores) and 2G of RAM (host memory:
> +64G). The virtual machine also has a vTPM with libtpms and swtpm as
> +backend.
> +
> +After writing the RPM headers to files, the size of the directory
> +containing them is 36M.
> +
> +After converting the RPM headers to the compact digest list, the size of
> +the data being uploaded to the kernel is 3.6M.
> +
> +The time to load the entire RPM database is 0.628s.
> +
> +After loading the digest lists to the kernel, the slab usage due to
> +indexing is (obtained with slab_nomerge in the kernel command line):
> +
> +::
> +
> + OBJS   ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
> + 118144 118144 100%    0,03K    923      128      3692K digest_list_item_ref_cache
> + 102400 102400 100%    0,03K    800      128      3200K digest_item_cache
> +   2646   2646 100%    0,09K     63       42       252K digest_list_item_cache
> +
> +The stats, obtained from the ``digests_count`` interface, introduced later,
> +are:
> +
> +::
> +
> + Parser digests: 0
> + File digests: 99100
> + Metadata digests: 0
> + Digest list digests: 1423
> +
> +On this installation, this would be the worst case in which all files are
> +measured and/or appraised, which is currently not recommended without
> +enforcing an integrity policy protecting mutable files. Infoflow LSM is a
> +component to accomplish this task:
> +
> +https://patchwork.kernel.org/project/linux-integrity/cover/20190818235745.1417-1-roberto.sassu@huawei.com/
> +
> +The first manageable goal of IMA with DIGLIM is to use an execution policy,
> +with measurement and/or appraisal of files executed or mapped in memory as
> +executable (in addition to kernel modules and firmware). In this
> +case, the digest list contains the digest only for those files. The numbers
> +above change as follows.
> +
> +After converting the RPM headers to the compact digest list, the size of
> +the data being uploaded to the kernel is 208K.
> +
> +The time to load the digest of binaries and shared libraries is 0.062s.
> +
> +After loading the digest lists to the kernel, the slab usage due to
> +indexing is:
> +
> +::
> +
> + OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
> + 7168   7168 100%    0,03K     56      128       224K digest_list_item_ref_cache
> + 7168   7168 100%    0,03K     56      128       224K digest_item_cache
> + 1134   1134 100%    0,09K     27       42       108K digest_list_item_cache
> +
> +
> +The stats, obtained from the ``digests_count`` interface, are:
> +
> +::
> +
> + Parser digests: 0
> + File digests: 5986
> + Metadata digests: 0
> + Digest list digests: 1104
> +
> +
> +Comparison with IMA
> +~~~~~~~~~~~~~~~~~~~
> +
> +This section compares the performance between the current solution for IMA
> +measurement and appraisal, and IMA with DIGLIM.
> +
> +
> +Workload A (without DIGLIM):
> +
> +#. cat file[0-5985] > /dev/null
> +
> +
> +Workload B (with DIGLIM):
> +
> +#. echo $PWD/0-file_list-compact-file[0-1103] > <securityfs>/integrity/diglim/digest_list_add
> +#. cat file[0-5985] > /dev/null
> +
> +
> +Workload A execution time without IMA policy:
> +
> +::
> +
> + real	0m0,155s
> + user	0m0,008s
> + sys	0m0,066s
> +
> +
> +Measurement
> +...........
> +
> +IMA policy:
> +
> +::
> +
> + measure fowner=2000 func=FILE_CHECK mask=MAY_READ use_diglim=allow pcr=11 ima_template=ima-sig
> +
> +``use_diglim`` is a policy keyword not yet supported by IMA.
> +
> +
> +Workload A execution time with IMA and 5986 files with signature measured:
> +
> +::
> +
> + real	0m8,273s
> + user	0m0,008s
> + sys	0m2,537s
> +
> +
> +Workload B execution time with IMA, 1104 digest lists with signature
> +measured and uploaded to the kernel, and 5986 files with signature accessed
> +but not measured (due to the file digest being found in the hash table):
> +
> +::
> +
> + real	0m1,837s
> + user	0m0,036s
> + sys	0m0,583s
> +
> +
> +Appraisal
> +.........
> +
> +IMA policy:
> +
> +::
> +
> + appraise fowner=2000 func=FILE_CHECK mask=MAY_READ use_diglim=allow
> +
> +``use_diglim`` is a policy keyword not yet supported by IMA.
> +
> +
> +Workload A execution time with IMA and 5986 files with file signature
> +appraised:
> +
> +::
> +
> + real	0m2,197s
> + user	0m0,011s
> + sys	0m2,022s
> +
> +
> +Workload B execution time with IMA, 1104 digest lists with signature
> +appraised and uploaded to the kernel, and with 5986 files with signature
> +not verified (due to the file digest being found in the hash table):
> +
> +::
> +
> + real	0m0,982s
> + user	0m0,020s
> + sys	0m0,865s
> diff --git a/Documentation/security/index.rst b/Documentation/security/index.rst
> index 16335de04e8c..6c3aea41c55b 100644
> --- a/Documentation/security/index.rst
> +++ b/Documentation/security/index.rst
> @@ -17,3 +17,4 @@ Security Documentation
>     tpm/index
>     digsig
>     landlock
> +   diglim/index
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6c8be735cc91..c914dadd7e65 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5452,6 +5452,15 @@ L:	linux-gpio@vger.kernel.org
>  S:	Maintained
>  F:	drivers/gpio/gpio-gpio-mm.c
>  
> +DIGLIM
> +M:	Roberto Sassu <roberto.sassu@huawei.com>
> +L:	linux-integrity@vger.kernel.org
> +S:	Supported
> +T:	git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
> +F:	Documentation/security/diglim/architecture.rst
> +F:	Documentation/security/diglim/index.rst
> +F:	Documentation/security/diglim/introduction.rst
> +
>  DIOLAN U2C-12 I2C DRIVER
>  M:	Guenter Roeck <linux@roeck-us.net>
>  L:	linux-i2c@vger.kernel.org



Thanks,
Mauro

^ permalink raw reply

* Re: [RFC][PATCH v2 02/12] diglim: Basic definitions
From: Mauro Carvalho Chehab @ 2021-07-28 11:31 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-3-roberto.sassu@huawei.com>

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

> Introduce the basic definitions, exported to user space, to use digest
> lists. The definitions, added to include/uapi/linux/diglim.h, are
> documented in Documentation/security/diglim/implementation.rst.
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  .../security/diglim/implementation.rst        | 97 +++++++++++++++++++
>  Documentation/security/diglim/index.rst       |  1 +
>  MAINTAINERS                                   |  2 +
>  include/uapi/linux/diglim.h                   | 51 ++++++++++
>  4 files changed, 151 insertions(+)
>  create mode 100644 Documentation/security/diglim/implementation.rst
>  create mode 100644 include/uapi/linux/diglim.h
> 
> diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst
> new file mode 100644
> index 000000000000..59a180b3bb3f
> --- /dev/null
> +++ b/Documentation/security/diglim/implementation.rst
> @@ -0,0 +1,97 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Implementation
> +==============
> +
> +This section describes the implementation of DIGLIM.
> +
> +
> +Basic Definitions
> +-----------------
> +
> +This section introduces the basic definitions required to use DIGLIM.
> +
> +
> +Compact Digest List Format
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +.. kernel-doc:: include/uapi/linux/diglim.h
> +   :identifiers: compact_list_hdr
> +
> +Compact Types
> +.............
> +
> +Digests can be of different types:
> +
> +- ``COMPACT_PARSER``: digests of executables which are given the ability to
> +  parse digest lists not in the compact format and to upload to the kernel
> +  the digest list converted to the compact format;
> +- ``COMPACT_FILE``: digests of regular files;
> +- ``COMPACT_METADATA``: digests of file metadata (e.g. the digest
> +  calculated by EVM to verify a portable signature);
> +- ``COMPACT_DIGEST_LIST``: digests of digest lists (only used internally by
> +  the kernel).
> +
> +Different users of DIGLIM might query digests with different compact types.
> +For example, IMA would be interested in COMPACT_FILE, as it deals with
> +regular files, while EVM would be interested in COMPACT_METADATA, as it
> +verifies file metadata.
> +
> +
> +Compact Modifiers
> +.................
> +
> +Digests can also have specific attributes called modifiers (bit position):
> +
> +- ``COMPACT_MOD_IMMUTABLE``: file content or metadata should not be
> +  modifiable.
> +
> +IMA might use this information to deny open for writing, or EVM to deny
> +setxattr operations.
> +
> +
> +Actions
> +.......
> +
> +This section defines a set of possible actions that have been executed on
> +the digest lists (bit position):
> +
> +- ``COMPACT_ACTION_IMA_MEASURED``: the digest list has been measured by
> +  IMA;
> +- ``COMPACT_ACTION_IMA_APPRAISED``: the digest list has been successfully
> +  appraised by IMA;
> +- ``COMPACT_ACTION_IMA_APPRAISED_DIGSIG``: the digest list has been
> +  successfully appraised by IMA by verifying a digital signature.
> +
> +This information might help users of DIGLIM to decide whether to use the
> +result of a queried digest.
> +
> +For example, if a digest belongs to a digest list that was not measured
> +before, IMA should ignore the result of the query, as the measurement list
> +sent to remote verifiers would lack which digests have been uploaded to the
> +kernel.
> +
> +
> +Compact Digest List Example
> +...........................
> +
> +::
> +
> + version: 1, type: 2, modifiers: 0 algo: 4, count: 3, datalen: 96
> + <SHA256 digest1><SHA256 digest2><SHA256 digest3>
> + version: 1, type: 3, modifiers: 1 algo: 6, count: 2, datalen: 128
> + <SHA512 digest1><SHA512 digest2>
> +
> +This digest list consists of two blocks. The first block contains three
> +SHA256 digests of regular files. The second block contains two SHA512
> +digests of immutable metadata.
> +
> +
> +Compact Digest List Operations
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Finally, this section defines the possible operations that can be performed
> +with digest lists:
> +
> +- ``DIGEST_LIST_ADD``: the digest list is being added;
> +- ``DIGEST_LIST_DEL``: the digest list is being deleted.
> diff --git a/Documentation/security/diglim/index.rst b/Documentation/security/diglim/index.rst
> index 0fc5ab019bc0..4771134c2f0d 100644
> --- a/Documentation/security/diglim/index.rst
> +++ b/Documentation/security/diglim/index.rst
> @@ -9,3 +9,4 @@ Digest Lists Integrity Module (DIGLIM)
>  
>     introduction
>     architecture
> +   implementation
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c914dadd7e65..f61f5239468a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5458,8 +5458,10 @@ L:	linux-integrity@vger.kernel.org
>  S:	Supported
>  T:	git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
>  F:	Documentation/security/diglim/architecture.rst
> +F:	Documentation/security/diglim/implementation.rst
>  F:	Documentation/security/diglim/index.rst
>  F:	Documentation/security/diglim/introduction.rst
> +F:	include/uapi/linux/diglim.h
>  
>  DIOLAN U2C-12 I2C DRIVER
>  M:	Guenter Roeck <linux@roeck-us.net>
> diff --git a/include/uapi/linux/diglim.h b/include/uapi/linux/diglim.h
> new file mode 100644
> index 000000000000..8a33d1f0fefb
> --- /dev/null
> +++ b/include/uapi/linux/diglim.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> + *
> + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> + *
> + * DIGLIM definitions exported to user space, useful for generating digest
> + * lists.
> + */
> +
> +#ifndef _UAPI__LINUX_DIGLIM_H
> +#define _UAPI__LINUX_DIGLIM_H
> +
> +#include <linux/types.h>
> +#include <linux/hash_info.h>
> +
> +enum compact_types { COMPACT_KEY, COMPACT_PARSER, COMPACT_FILE,
> +		     COMPACT_METADATA, COMPACT_DIGEST_LIST, COMPACT__LAST };
> +
> +enum compact_modifiers { COMPACT_MOD_IMMUTABLE, COMPACT_MOD__LAST };
> +
> +enum compact_actions { COMPACT_ACTION_IMA_MEASURED,
> +		       COMPACT_ACTION_IMA_APPRAISED,
> +		       COMPACT_ACTION_IMA_APPRAISED_DIGSIG,
> +		       COMPACT_ACTION__LAST };
> +
> +enum ops { DIGEST_LIST_ADD, DIGEST_LIST_DEL, DIGEST_LIST_OP__LAST };
> +
> +/**
> + * struct compact_list_hdr - header of the following concatenated digests
> + * @version: version of the digest list
> + * @_reserved: field reserved for future use
> + * @type: type of digest list among enum compact_types
> + * @modifiers: additional attributes among (1 << enum compact_modifiers)
> + * @algo: digest algorithm
> + * @count: number of digests
> + * @datalen: length of concatenated digests
> + *
> + * A digest list is a set of blocks composed by struct compact_list_hdr and
> + * the following concatenated digests.
> + */
> +struct compact_list_hdr {
> +	__u8 version;
> +	__u8 _reserved;
> +	__le16 type;
> +	__le16 modifiers;
> +	__le16 algo;
> +	__le32 count;
> +	__le32 datalen;
> +} __packed;
> +#endif /*_UAPI__LINUX_DIGLIM_H*/

Besides Greg's notes, I'm wondering why to enforce a particular
endness here. I mean, this is uAPI. I would expect it to use the
CPU endianness instead, in order to avoid uneeded conversions.

Thanks,
Mauro

^ permalink raw reply

* Re: [RFC][PATCH v2 03/12] diglim: Objects
From: Mauro Carvalho Chehab @ 2021-07-28 11:38 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-4-roberto.sassu@huawei.com>

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

> Define the objects to manage digest lists:
> 
> - digest_list_item: a digest list loaded into the kernel;
> - digest_list_item_ref: a reference to a digest list;
> - digest_item: a digest of a digest list.
> 
> Also define some helpers for the objects. More information can be found in
> Documentation/security/diglim/implementation.rst.
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  .../security/diglim/implementation.rst        | 105 ++++++++++++++
>  MAINTAINERS                                   |   1 +
>  security/integrity/diglim/diglim.h            | 134 ++++++++++++++++++
>  3 files changed, 240 insertions(+)
>  create mode 100644 security/integrity/diglim/diglim.h
> 
> diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst
> index 59a180b3bb3f..6002049612a1 100644
> --- a/Documentation/security/diglim/implementation.rst
> +++ b/Documentation/security/diglim/implementation.rst
> @@ -95,3 +95,108 @@ with digest lists:
>  
>  - ``DIGEST_LIST_ADD``: the digest list is being added;
>  - ``DIGEST_LIST_DEL``: the digest list is being deleted.
> +
> +
> +Objects
> +-------
> +
> +This section defines the objects to manage digest lists.
> +
> +.. kernel-doc:: security/integrity/diglim/diglim.h
> +
> +They are represented in the following class diagram:
> +
> +::
> +
> + digest_offset,
> + hdr_offset---------------+
> +                          |
> +                          |
> + +------------------+     |     +----------------------+
> + | digest_list_item |--- N:1 ---| digest_list_item_ref |
> + +------------------+           +----------------------+
> +                                           |
> +                                          1:N
> +                                           |
> +                                    +-------------+
> +                                    | digest_item |
> +                                    +-------------+
> +
> +A ``digest_list_item`` is associated to one or multiple
> +``digest_list_item_ref``, one for each digest it contains. However,
> +a ``digest_list_item_ref`` is associated to only one ``digest_list_item``,
> +as it represents a single location within a specific digest list.
> +
> +Given that a ``digest_list_item_ref`` represents a single location, it is
> +associated to only one ``digest_item``. However, a ``digest_item`` can have
> +multiple references (as it might appears multiple times within the same
> +digest list or in different digest lists, if it is duplicated).
> +
> +All digest list references are stored for a given digest, so that a query
> +result can include the OR of the modifiers and actions of each referenced
> +digest list.
> +
> +The relationship between the described objects can be graphically
> +represented as:
> +
> +::

Just merge "::" at the previous line (everywhere).

> +
> + Hash table            +-------------+         +-------------+
> + PARSER      +-----+   | digest_item |         | digest_item |
> + FILE        | key |-->|             |-->...-->|             |
> + METADATA    +-----+   |ref0|...|refN|         |ref0|...|refN|
> +                       +-------------+         +-------------+
> +            ref0:         |                               | refN:
> +            digest_offset | +-----------------------------+ digest_offset
> +            hdr_offset    | |                               hdr_offset
> +                          | |
> +                          V V
> +                     +--------------------+
> +                     |  digest_list_item  |
> +                     |                    |
> +                     | size, buf, actions |
> +                     +--------------------+
> +                          ^
> +                          |
> + Hash table            +-------------+         +-------------+
> + DIGEST_LIST +-----+   |ref0         |         |ref0         |
> +             | key |-->|             |-->...-->|             |
> +             +-----+   | digest_item |         | digest_item |
> +                       +-------------+         +-------------+
> +
> +The reference for the digest of the digest list differs from the references
> +for the other digest types. ``digest_offset`` and ``hdr_offset`` are set to
> +zero, so that the digest of the digest list is retrieved from the
> +``digest_list_item`` structure directly (see ``get_digest()`` below).
> +
> +Finally, this section defines useful helpers to access a digest or the
> +header the digest belongs to. For example:
> +
> +::
> +
> + static inline struct compact_list_hdr *get_hdr(
> +                                      struct digest_list_item *digest_list,
> +                                      loff_t hdr_offset)

I would write this to avoid ending a line with an open parenthesis. You could,
for instance, write it as:

	static inline struct compact_list_hdr *
	get_hdr(struct digest_list_item *digest_list,
		off_t hdr_offset)

if you also want to avoid to have a line bigger than 80 columns.

> + {
> +         return (struct compact_list_hdr *)(digest_list->buf + hdr_offset);
> + }
> +
> +the header can be obtained by summing the address of the digest list buffer
> +in the ``digest_list_item`` structure with ``hdr_offset``.
> +
> +Similarly:
> +
> +::
> +
> + static inline u8 *get_digest(struct digest_list_item *digest_list,
> +                              loff_t digest_offset, loff_t hdr_offset)
> + {
> +         /* Digest list digest is stored in a different place. */
> +         if (!digest_offset)
> +                 return digest_list->digest;
> +         return digest_list->buf + digest_offset;
> + }
> +
> +the digest can be obtained by summing the address of the digest list buffer
> +with ``digest_offset`` (except for the digest lists, where the digest is
> +stored in the ``digest`` field of the ``digest_list_item`` structure).
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f61f5239468a..f7592d41367d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5462,6 +5462,7 @@ F:	Documentation/security/diglim/implementation.rst
>  F:	Documentation/security/diglim/index.rst
>  F:	Documentation/security/diglim/introduction.rst
>  F:	include/uapi/linux/diglim.h
> +F:	security/integrity/diglim/diglim.h
>  
>  DIOLAN U2C-12 I2C DRIVER
>  M:	Guenter Roeck <linux@roeck-us.net>
> diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h
> new file mode 100644
> index 000000000000..578253d7e1d1
> --- /dev/null
> +++ b/security/integrity/diglim/diglim.h
> @@ -0,0 +1,134 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> + *
> + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> + *
> + * Definitions only used inside DIGLIM.
> + */
> +
> +#ifndef __DIGLIM_INTERNAL_H
> +#define __DIGLIM_INTERNAL_H
> +
> +#include <linux/types.h>
> +#include <linux/crypto.h>
> +#include <linux/fs.h>
> +#include <linux/security.h>
> +#include <linux/hash.h>
> +#include <linux/tpm.h>
> +#include <linux/audit.h>
> +#include <crypto/hash_info.h>
> +#include <linux/hash_info.h>
> +#include <uapi/linux/diglim.h>
> +
> +#define MAX_DIGEST_SIZE 64
> +#define HASH_BITS 10
> +#define DIGLIM_HTABLE_SIZE (1 << HASH_BITS)
> +
> +/**
> + * struct digest_list_item - a digest list loaded into the kernel
> + *
> + * @size: size of the digest list buffer
> + * @buf: digest list buffer
> + * @digest: digest of the digest list
> + * @label: label used to identify the digest list (e.g. file name)
> + * @actions: actions performed on the digest list
> + * @algo: digest algorithm
> + */
> +struct digest_list_item {
> +	loff_t size;
> +	u8 *buf;
> +	u8 digest[64];
> +	const char *label;
> +	u8 actions;
> +	enum hash_algo algo;
> +};
> +
> +/**
> + * struct digest_list_item_ref - a reference to a digest list
> + *
> + * @list: linked list pointers
> + * @digest_list: pointer to struct digest_list_item
> + * @digest_offset: offset of the digest in the referenced digest list
> + * @hdr_offset: offset of the header the digest refers to in the digest list
> + */
> +struct digest_list_item_ref {
> +	struct list_head list;
> +	struct digest_list_item *digest_list;
> +	u32 digest_offset;
> +	u32 hdr_offset;
> +};
> +
> +/**
> + * struct digest_item - a digest of a digest list
> + *
> + * @hnext: pointers of the hash table
> + * @refs: linked list of struct digest_list_item_ref
> + */
> +struct digest_item {
> +	struct hlist_node hnext;
> +	struct list_head refs;
> +};
> +


> +struct h_table {
> +	unsigned long len;
> +	struct hlist_head queue[DIGLIM_HTABLE_SIZE];
> +};
> +
> +static inline unsigned int hash_key(u8 *digest)
> +{
> +	return (digest[0] | digest[1] << 8) % DIGLIM_HTABLE_SIZE;
> +}
> +
> +static inline struct compact_list_hdr *get_hdr(
> +					struct digest_list_item *digest_list,
> +					loff_t hdr_offset)

Same here with regards to open parenthesis.

> +{
> +	return (struct compact_list_hdr *)(digest_list->buf + hdr_offset);
> +}
> +
> +static inline enum hash_algo get_algo(struct digest_list_item *digest_list,
> +				      loff_t digest_offset, loff_t hdr_offset)
> +{
> +	/* Digest list digest algorithm is stored in a different place. */
> +	if (!digest_offset)
> +		return digest_list->algo;
> +
> +	return get_hdr(digest_list, hdr_offset)->algo;
> +}
> +
> +static inline u8 *get_digest(struct digest_list_item *digest_list,
> +			     loff_t digest_offset, loff_t hdr_offset)
> +{
> +	/* Digest list digest is stored in a different place. */
> +	if (!digest_offset)
> +		return digest_list->digest;
> +
> +	return digest_list->buf + digest_offset;
> +}
> +
> +static inline struct compact_list_hdr *get_hdr_ref(
> +					struct digest_list_item_ref *ref)
> +{
> +	return get_hdr(ref->digest_list, ref->hdr_offset);
> +}
> +
> +static inline enum hash_algo get_algo_ref(struct digest_list_item_ref *ref)
> +{
> +	/* Digest list digest algorithm is stored in a different place. */
> +	if (!ref->digest_offset)
> +		return ref->digest_list->algo;
> +
> +	return get_hdr_ref(ref)->algo;
> +}
> +
> +static inline u8 *get_digest_ref(struct digest_list_item_ref *ref)
> +{
> +	/* Digest list digest is stored in a different place. */
> +	if (!ref->digest_offset)
> +		return ref->digest_list->digest;
> +
> +	return ref->digest_list->buf + ref->digest_offset;
> +}

I would also document the above static inline functions and 
struct h_table.

> +#endif /*__DIGLIM_INTERNAL_H*/



Thanks,
Mauro

^ permalink raw reply

* RE: [RFC][PATCH v2 01/12] diglim: Overview
From: Roberto Sassu @ 2021-07-28 11:40 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20210728131000.16788919@coco.lan>

> From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> Sent: Wednesday, July 28, 2021 1:10 PM
> Em Mon, 26 Jul 2021 18:36:49 +0200
> Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> 
> > Add an overview of DIGLIM to
> Documentation/security/diglim/introduction.rst
> > and the architecture to Documentation/security/diglim/architecture.rst
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >  .../security/diglim/architecture.rst          |  45 ++
> >  Documentation/security/diglim/index.rst       |  11 +
> >  .../security/diglim/introduction.rst          | 631 ++++++++++++++++++
> >  Documentation/security/index.rst              |   1 +
> >  MAINTAINERS                                   |   9 +
> >  5 files changed, 697 insertions(+)
> >  create mode 100644 Documentation/security/diglim/architecture.rst
> >  create mode 100644 Documentation/security/diglim/index.rst
> >  create mode 100644 Documentation/security/diglim/introduction.rst
> >
> > diff --git a/Documentation/security/diglim/architecture.rst
> b/Documentation/security/diglim/architecture.rst
> > new file mode 100644
> > index 000000000000..a54fe2453715
> > --- /dev/null
> > +++ b/Documentation/security/diglim/architecture.rst
> > @@ -0,0 +1,45 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +Architecture
> > +============
> > +
> > +This section introduces the high level architecture of DIGLIM.
> > +
> > +::
> > +
> > + 5. add/delete from hash table and add refs to digest list
> > +        +---------------------------------------------+
> > +        |                            +-----+   +-------------+         +--+
> > +        |                            | key |-->| digest refs |-->...-->|  |
> > +        V                            +-----+   +-------------+         +--+
> > + +-------------+                     +-----+   +-------------+
> > + | digest list |                     | key |-->| digest refs |
> > + |  (compact)  |                     +-----+   +-------------+
> > + +-------------+                     +-----+   +-------------+
> > +        ^ 4. copy to                 | key |-->| digest refs |
> > +        |    kernel memory           +-----+   +-------------+ kernel space
> > + --------------------------------------------------------------------------
> > +        ^                                          ^             user space
> > +        |<----------------+       3b. upload       |
> > + +-------------+   +------------+                  | 6. query digest
> > + | digest list |   | user space | 2b. convert
> > + |  (compact)  |   |   parser   |
> > + +-------------+   +------------+
> > + 1a. upload               ^       1b. read
> > +                          |
> > +                   +------------+
> > +                   | RPM header |
> > +                   +------------+
> > +
> > +
> > +As mentioned before, digest lists can be uploaded directly if they are in
> 
> "before"? This is at the beginning of this document ;-)
> 
> You should probably add a reference to introduction.rst here, like:
> 
> 	As mentioned at Documentation/security/diglim/introduction.rst, ...

Hi Mauro

ok.

> > +the compact format (step 1a) or can be uploaded indirectly by the user
> > +space parser if they are in an alternative format (steps 1b-3b).
> > +
> > +During upload, the kernel makes a copy of the digest list to the kernel
> > +memory (step 4), and creates the necessary structures to index the digests
> > +(hash table and a linked list of digest list references to locate the
> > +digests in the digest list) (step 5).
> > +
> > +Finally, digests can be searched from user space through a securityfs file
> > +(step 6) or by the kernel itself.
> 
> This probably applies to Documentation/security as a hole, but the
> best is to split the documents on two separate parts:
> 	- the kAPI and internals;
> 	- the admin-guide part.
> 
> The audience for the admin-guide is distribution pagagers and
> syssadmins.

Ok. I will create an admin-guide.
 
> > diff --git a/Documentation/security/diglim/index.rst
> b/Documentation/security/diglim/index.rst
> > new file mode 100644
> > index 000000000000..0fc5ab019bc0
> > --- /dev/null
> > +++ b/Documentation/security/diglim/index.rst
> > @@ -0,0 +1,11 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +======================================
> > +Digest Lists Integrity Module (DIGLIM)
> > +======================================
> > +
> > +.. toctree::
> > +   :maxdepth: 1
> > +
> > +   introduction
> > +   architecture
> > diff --git a/Documentation/security/diglim/introduction.rst
> b/Documentation/security/diglim/introduction.rst
> > new file mode 100644
> > index 000000000000..d8d8b2a17222
> > --- /dev/null
> > +++ b/Documentation/security/diglim/introduction.rst
> > @@ -0,0 +1,631 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +Introduction
> > +============
> > +
> > +Digest Lists Integrity Module (DIGLIM) is a new component added to the
> > +integrity subsystem in the kernel, primarily aiming to aid Integrity
> 
> I would replace:
> 
> 	"is a new component added to" -> "is a component of"
> 
> As this is the kind of text that tends to be outdated with time...
> Imagine someone reading this paragraph maybe 10 years in the future ;-)

Ok.

> > +Measurement Architecture (IMA) in the process of checking the integrity of
> > +file content and metadata. It accomplishes this task by storing reference
> > +values coming from software vendors and by reporting whether or not the
> > +digest of file content or metadata calculated by IMA (or EVM) is found
> > +among those values. In this way, IMA can decide, depending on the result
> of
> > +a query, if a measurement should be taken or access to the file should be
> > +granted. The `Security Assumptions`_ section explains more in detail why
> > +this component has been placed in the kernel.
> > +
> > +The main benefits of using IMA in conjunction with DIGLIM are the ability
> > +to implement advanced remote attestation schemes based on the usage of
> a
> > +TPM key for establishing a TLS secure channel [1][2], and to reduce the
> > +burden on Linux distribution vendors to extend secure boot at OS level to
> > +applications.
> > +
> > +DIGLIM does not have the complexity of feature-rich databases. In fact, its
> > +main functionality comes from the hash table primitives already in the
> > +kernel. It does not have an ad-hoc storage module, it just indexes data in
> > +a fixed format (digest lists, a set of concatenated digests preceded by a
> > +header), copied to kernel memory as they are. Lastly, it does not support
> > +database-oriented languages such as SQL, but only accepts a digest and its
> > +algorithm as a query.
> > +
> > +The only digest list format supported by DIGLIM is called ``compact``.
> > +However, Linux distribution vendors don't have to generate new digest lists
> > +in this format for the packages they release, as already available
> > +information, such as RPM headers and DEB package metadata, can be
> already
> > +used as a source for reference values (they already include file digests),
> 
> 	-ETOMANY_already
> 
> as "already" available... can be "already" ... "already" include...
> 
> I would simplify the above text removing such redundancy.

Ok.

> > +with a user space parser taking care of the conversion to the compact
> > +format.
> > +
> > +Although one might perceive that storing file or metadata digests for a
> > +Linux distribution would significantly increase the memory usage, this does
> > +not seem to be the case. As an anticipation of the evaluation done in the
> > +`Preliminary Performance Evaluation`_ section, protecting binaries and
> > +shared libraries of a minimal Fedora 33 installation requires 208K of
> > +memory for the digest lists plus 556K for indexing.
> > +
> 
> 
> > +In exchange for a slightly increased memory usage, DIGLIM improves the
> > +performance of the integrity subsystem. In the considered scenario, IMA
> > +measurement and appraisal with digest lists requires respectively less than
> > +one quarter and less than half the time, compared to the current solution.
> 
> I found this paragraph a little bit confusing to understand. Could you
> please improve the description?
> 
> I mean:
> 
> 	what improved by one quarter?
> 	what improved by "less than half of the time"?

Ok. I didn't want to make the text too heavy. The tests are described
in the Preliminary Performance Evaluation section.

> > +
> > +DIGLIM also keeps track of whether digest lists have been processed in
> some
> > +way (e.g. measured or appraised by IMA). This is important for example for
> > +remote attestation, so that remote verifiers understand what has been
> > +uploaded to the kernel.
> > +
> 
> > +DIGLIM behaves like a transactional database, i.e. it has the ability to
> > +roll back to the beginning of the transaction if an error occurred during
> > +the addition of a digest list (the deletion operation always succeeds).
> 
> I don't think it makes sense to compare it with a transactional database.
> 
> I would say, instead, something like:
> 
> 	The inserts on DIGLIM are atomic: if an error occurs during the
> addition
> 	of a digest list, it rolls back the entire insert operation.

Ok, better.

> > +This capability has been tested with an ad-hoc fault injection mechanism
> > +capable of simulating failures during the operations.
> > +
> > +Finally, DIGLIM exposes to user space, through securityfs, the digest lists
> > +currently loaded, the number of digests added, a query interface and an
> > +interface to set digest list labels.
> > +
> > +[1] LSS EU 2019
> > +
> > +-   slides:
> > +
> https://static.sched.com/hosted_files/lsseu2019/bd/secure_attested_commu
> nication_channels_lss_eu_2019.pdf
> > +-   video: https://youtu.be/mffdQgkvDNY
> > +
> > +[2] FutureTPM EU project, final review meeting demo
> > +
> > +-   slides:
> > +    https://futuretpm.eu/images/07-3-FutureTPM-Final-Review-Slides-WP6-
> Device-Management-Use-Case-HWDU.pdf
> > +-   video: https://vimeo.com/528251864/4c1d55abcd
> 
> The above won't generate any cross-references with Sphinx.
> 
> For it correct syntax, see:
> 	https://www.sphinx-
> doc.org/en/master/usage/restructuredtext/basics.html#citations

Ok, will fix it.

> > +
> > +
> > +Binary Integrity
> > +----------------
> > +
> > +Integrity is a fundamental security property in information systems.
> 
> > +Integrity could be described as the condition in which a generic
> > +component is just after it has been released by the entity that created it.
> 
> Sounds a weird description for me. (ISC)2 defines integrity on its
> glossary[1] as:
> 
> 	"Guarding against improper information modification or destruction
> and
> 	 includes ensuring information non-repudiation and authenticity."
> 
> [1] https://www.isc2.org/Certifications/CISSP/CISSP-Student-Glossary

Ok, I meant integrity in the context of trusted computing.

https://trustedcomputinggroup.org/wp-content/uploads/IWG_ArchitecturePartII_v1.0.pdf

In general the term "integrity" is used to denote the pristine state of a
component (page 13).

> > +One way to check whether a component is in this condition (called binary
> > +integrity) is to calculate its digest and to compare it with a reference
> > +value (i.e. the digest calculated in controlled conditions, when the
> > +component is released).
> > +
> > +IMA, a software part of the integrity subsystem, can perform such
> > +evaluation and execute different actions:
> > +
> > +- store the digest in an integrity-protected measurement list, so that it
> > +  can be sent to a remote verifier for analysis;
> > +- compare the calculated digest with a reference value (usually protected
> > +  with a signature) and deny operations if the file is found corrupted;
> > +- store the digest in the system log.
> > +
> > +
> 
> 
> > +Contribution
> > +------------
> 
> I would rename this chapter to "Benefits".

Ok.

> > +
> > +DIGLIM further enhances the capabilities offered by IMA-based solutions
> > +and, at the same time, makes them more practical to adopt by reusing
> > +existing sources as reference values for integrity decisions.
> > +
> > +Possible sources for digest lists are:
> > +
> > +- RPM headers;
> > +- Debian repository metadata.
> > +
> > +
> > +Benefits for IMA Measurement
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +One of the issues that arises when files are measured by the OS is that,
> > +due to parallel execution, the order in which file accesses happen cannot
> > +be predicted. Since the TPM Platform Configuration Register (PCR) extend
> > +operation, executed after each file measurement, cryptographically binds
> > +the current measurement to the previous ones, the PCR value at the end of
> a
> > +workload cannot be predicted too.
> > +
> > +Thus, even if the usage of a TPM key, bound to a PCR value, should be
> > +allowed when only good files were accessed, the TPM could unexpectedly
> deny
> > +an operation on that key if files accesses did not happen as stated by the
> > +key policy (which allows only one of the possible sequences).
> > +
> > +DIGLIM solves this issue by making the PCR value stable over the time and
> > +not dependent on file accesses. The following figure depicts the current
> > +and the new approaches:
> > +
> > +::
> > +
> > + IMA measurement list (current)
> > +
> > + entry#        1st boot               2nd boot               3rd boot
> > +       +----+---------------+ +----+---------------+ +----+---------------+
> > + 1:    | 10 | file1 measur. | | 10 | file3 measur. | | 10 | file2 measur. |
> > +       +----+---------------+ +----+---------------+ +----+---------------+
> > + 2:    | 10 | file2 measur. | | 10 | file2 measur. | | 10 | file3 measur. |
> > +       +----+---------------+ +----+---------------+ +----+---------------+
> > + 3:    | 10 | file3 measur. | | 10 | file1 measur. | | 10 | file4 measur. |
> > +       +----+---------------+ +----+---------------+ +----+---------------+
> > +
> > + PCR:  Extend              != Extend              != Extend
> > +       file1, file2, file3    file3, file2, file1    file2, file3, file4
> > +
> > +
> > + PCR Extend definition:
> > +
> > +       PCR(new value) = Hash(Hash(meas. entry), PCR(previous value))
> > +
> > +A new entry in the measurement list is created by IMA for each file access.
> > +Assuming that ``file1``, ``file2`` and ``file3`` are files provided by the
> > +software vendor, ``file4`` is an unknown file, the first two PCR values
> > +above represent a good system state, the third a bad system state. The PCR
> > +values are the result of the PCR extend operation performed for each
> > +measurement entry with the digest of the measurement entry as an input.
> > +
> > +::
> > +
> > + IMA measurement list (with DIGLIM)
> > +
> > + dlist
> > + +--------------+
> > + |    header    |
> > + +--------------+
> > + | file1 digest |
> > + | file2 digest |
> > + | file3 digest |
> > + +--------------+
> > +
> > +``dlist`` is a digest list containing the digest of ``file1``, ``file2``
> > +and ``file3``. In the intended scenario, it is generated by a software
> > +vendor at the end of the building process, and retrieved by the
> > +administrator of the system where the digest list is loaded.
> > +
> > +::
> > +
> > + entry#        1st boot               2nd boot               3rd boot
> > +       +----+---------------+ +----+---------------+ +----+---------------+
> > + 0:    | 11 | dlist measur. | | 11 | dlist measur. | | 11 | dlist measur. |
> > +       +----+---------------+ +----+---------------+ +----+---------------+
> > + 1:    < file1 measur. skip > < file3 measur. skip > < file2 measur. skip >
> > +
> > + 2:    < file2 measur. skip > < file2 measur. skip > < file3 measur. skip >
> > +                                                     +----+---------------+
> > + 3:    < file3 measur. skip > < file1 measur. skip > | 11 | file4 measur. |
> > +                                                     +----+---------------+
> > +
> > + PCR:  Extend               = Extend              != Extend
> > +       dlist                  dlist                  dlist, file4
> > +
> > +
> > +The first entry in the measurement list contains the digest of the digest
> > +list uploaded to the kernel at kernel initialization time.
> > +
> > +When a file is accessed, IMA queries DIGLIM with the calculated file digest
> > +and, if it is found, IMA skips the measurement.
> > +
> > +Thus, the only information sent to remote verifiers are: the list of
> > +files that could possibly be accessed (from the digest list), but not if
> > +they were accessed and when; the measurement of unknown files.
> > +
> > +Despite providing less information, this solution has the advantage that
> > +the good system state (i.e. when only ``file1``, ``file2`` and ``file3``
> > +are accessed) now can be represented with a deterministic PCR value (the
> > +PCR is extended only with the measurement of the digest list). Also, the
> > +bad system state can still be distinguished from the good state (the PCR is
> > +extended also with the measurement of ``file4``).
> > +
> > +If a TPM key is bound to the good PCR value, the TPM would allow the key
> to
> > +be used if ``file1``, ``file2`` or ``file3`` are accessed, regardless of
> > +the sequence in which they are accessed (the PCR value does not change),
> > +and would revoke the permission when the unknown ``file4`` is accessed
> (the
> > +PCR value changes). If a system is able to establish a TLS connection with
> > +a peer, this implicitly means that the system was in a good state (i.e.
> > +``file4`` was not accessed, otherwise the TPM would have denied the usage
> > +of the TPM key due to the key policy).
> > +
> > +
> > +Benefits for IMA Appraisal
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Extending secure boot to applications means being able to verify the
> > +provenance of files accessed. IMA does it by verifying file signatures with
> > +a key that it trusts, which requires Linux distribution vendors to
> > +additionally include in the package header a signature for each file that
> > +must be verified (there is the dedicated ``RPMTAG_FILESIGNATURES``
> section
> > +in the RPM header).
> > +
> > +The proposed approach would be instead to verify data provenance from
> > +already available metadata (file digests) in existing packages. IMA would
> > +verify the signature of package metadata and search file digests extracted
> > +from package metadata and added to the hash table in the kernel.
> > +
> > +For RPMs, file digests can be found in the ``RPMTAG_FILEDIGESTS`` section
> > +of ``RPMTAG_IMMUTABLE``, whose signature is in
> ``RPMTAG_RSAHEADER``. For
> > +DEBs, file digests (unsafe to use due to a weak digest algorithm) can be
> > +found in the ``md5sum`` file, which can be indirectly verified from
> > +``Release.gpg``.
> > +
> > +The following figure highlights the differences between the current and the
> > +proposed approach.
> > +
> > +::
> > +
> > + IMA appraisal (current solution, with file signatures):
> > +
> > +                                                         appraise
> > +                                                      +-----------+
> > +                                                      V           |
> > + +-------------------------+-----+         +-------+-----+        |
> > + | RPM header              |     | ima rpm | file1 | sig |        |
> > + | ...                     |     | plugin  +-------+-----+     +-----+
> > + | file1 sig [to be added] | sig |-------->      ...           | IMA |
> > + | ...                     |     |         +-------+-----+     +-----+
> > + | fileN sig [to be added] |     |         | fileN | sig |
> > + +-------------------------+-----+         +-------+-----+
> > +
> > +In this case, file signatures must be added to the RPM header, so that the
> > +``ima`` rpm plugin can extract them together with the file content. The
> RPM
> > +header signature is not used.
> > +
> > +::
> > +
> > + IMA appraisal (with DIGLIM):
> > +
> > +                                    kernel hash table
> > +                                    with RPM header content
> > +                                    +---+    +--------------+
> > +                                    |   |--->| file1 digest |
> > +                                    +---+    +--------------+
> > +                                     ...
> > +                                    +---+         appraise (file1)
> > +                                    |   |  <--------------+
> > + +----------------+-----+           +---+                 |
> > + | RPM header     |     |             ^                   |
> > + | ...            |     | digest_list |                   |
> > + | file1 digest   | sig | rpm plugin  |    +-------+   +-----+
> > + | ...            |     |-------------+--->| file1 |   | IMA |
> > + | fileN digest   |     |                  +-------+   +-----+
> > + +----------------+-----+                                 |
> > +                     ^                                    |
> > +                     +------------------------------------+
> > +                             appraise (RPM header)
> > +
> > +In this case, the RPM header is used as it is, and its signature is used
> > +for IMA appraisal. Then, the ``digest_list`` rpm plugin executes the user
> > +space parser to parse the RPM header and add the extracted digests to an
> > +hash table in the kernel. IMA appraisal of the files in the RPM package
> > +consists in searching their digest in the hash table.
> > +
> > +Other than reusing available information as digest list, another advantage
> > +is the lower computational overhead compared to the solution with file
> > +signatures (only one signature verification for many files and digest
> > +lookup, instead of per file signature verification, see `Preliminary
> > +Performance Evaluation`_ for more details).
> > +
> > +
> > +Lifecycle
> > +---------
> > +
> > +The lifecycle of DIGLIM is represented in the following figure:
> > +
> > +::
> 
> You could just use:
> 
> 	The lifecycle of DIGLIM is represented in the following figure::
> 
> > +
> > + Vendor premises (release process with modifications):
> > +
> > + +------------+   +-----------------------+   +------------------------+
> > + | 1. build a |   | 2. generate and sign  |   | 3. publish the package |
> > + |    package |-->|    a digest list from |-->|    and digest list in  |
> > + |            |   |    packaged files     |   |    a repository        |
> > + +------------+   +-----------------------+   +------------------------+
> > +                                                                 |
> > +                                                                 |
> > + User premises:                                                  |
> > +                                                                 V
> > + +---------------------+   +------------------------+   +-----------------+
> > + | 6. use digest lists |   | 5. download the digest |   | 4. download and |
> > + |    for measurement  |<--|    list and upload to  |<--|    install the  |
> > + |    and/or appraisal |   |    the kernel          |   |    package      |
> > + +---------------------+   +------------------------+   +-----------------+
> > +
> > +The figure above represents all the steps when a digest list is
> > +generated separately. However, as mentioned in `Contribution`_, in most
> > +cases existing packages can be already used as a source for digest lists,
> > +limiting the effort for software vendors.
> > +
> > +If, for example, RPMs are used as a source for digest lists, the figure
> > +above becomes:
> > +
> > +::
> 
> Same here.

Ok.

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> > +
> > + Vendor premises (release process without modifications):
> > +
> > + +------------+   +------------------------+
> > + | 1. build a |   | 2. publish the package |
> > + |    package |-->|    in a repository     |---------------------+
> > + |            |   |                        |                     |
> > + +------------+   +------------------------+                     |
> > +                                                                 |
> > +                                                                 |
> > + User premises:                                                  |
> > +                                                                 V
> > + +---------------------+   +------------------------+   +-----------------+
> > + | 5. use digest lists |   | 4. extract digest list |   | 3. download and |
> > + |    for measurement  |<--|    from the package    |<--|    install the  |
> > + |    and/or appraisal |   |    and upload to the   |   |    package      |
> > + |                     |   |    kernel              |   |                 |
> > + +---------------------+   +------------------------+   +-----------------+
> > +
> > +Step 4 can be performed with the ``digest_list`` rpm plugin and the user
> > +space parser, without changes to rpm itself.
> > +
> > +
> > +Security Assumptions
> > +--------------------
> > +
> > +As mentioned in the `Introduction`_, DIGLIM will be primarily used in
> > +conjunction with IMA to enforce a mandatory policy on all user space
> > +processes, including those owned by root. Even root, in a system with a
> > +locked-down kernel, cannot affect the enforcement of the mandatory
> policy
> > +or, if changes are permitted, it cannot do so without being detected.
> > +
> > +Given that the target of the enforcement are user space processes, DIGLIM
> > +cannot be placed in the target, as a Mandatory Access Control (MAC)
> design
> > +is required to have the components responsible to enforce the mandatory
> > +policy separated from the target.
> > +
> > +While locking-down a system and limiting actions with a mandatory policy
> is
> > +generally perceived by users as an obstacle, it has noteworthy benefits for
> > +the users themselves.
> > +
> > +First, it would timely block attempts by malicious software to steal or
> > +misuse user assets. Although users could query the package managers to
> > +detect them, detection would happen after the fact, or it wouldn't happen
> > +at all if the malicious software tampered with package managers. With a
> > +mandatory policy enforced by the kernel, users would still be able to
> > +decide which software they want to be executed except that, unlike
> package
> > +managers, the kernel is not affected by user space processes or root.
> > +
> > +Second, it might make systems more easily verifiable from outside, due to
> > +the limited actions the system allows. When users connect to a server, not
> > +only they would be able to verify the server identity, which is already
> > +possible with communication protocols like TLS, but also if the software
> > +running on that server can be trusted to handle their sensitive data.
> > +
> > +
> > +Adoption
> > +--------
> > +
> > +A former version of DIGLIM is used in the following OSes:
> > +
> > +- openEuler 20.09
> > +  https://github.com/openeuler-mirror/kernel/tree/openEuler-20.09
> > +
> > +- openEuler 21.03
> > +  https://github.com/openeuler-mirror/kernel/tree/openEuler-21.03
> > +
> > +Originally, DIGLIM was part of IMA (known as IMA Digest Lists). In this
> > +version, it has been redesigned as a standalone module with an API that
> > +makes its functionality accessible by IMA and, eventually, other
> > +subsystems.
> > +
> > +User Space Support
> > +------------------
> > +
> > +Digest lists can be generated and managed with ``digest-list-tools``:
> > +
> > +https://github.com/openeuler-mirror/digest-list-tools
> > +
> > +It includes two main applications:
> > +
> > +- ``gen_digest_lists``: generates digest lists from files in the
> > +  filesystem or from the RPM database (more digest list sources can be
> > +  supported);
> > +- ``manage_digest_lists``: converts and uploads digest lists to the
> > +  kernel.
> > +
> > +Integration with rpm is done with the ``digest_list`` plugin:
> > +
> > +https://gitee.com/src-openeuler/rpm/blob/master/Add-digest-list-
> plugin.patch
> > +
> > +This plugin writes the RPM header and its signature to a file, so that the
> > +file is ready to be appraised by IMA, and calls the user space parser to
> > +convert and upload the digest list to the kernel.
> > +
> > +
> > +Simple Usage Example (Tested with Fedora 33)
> > +--------------------------------------------
> > +
> > +1. Digest list generation (RPM headers and their signature are copied to
> > +   the specified directory):
> > +
> > +.. code-block:: bash
> > +
> > + # mkdir /etc/digest_lists
> > + # gen_digest_lists -t file -f rpm+db -d /etc/digest_lists -o add
> > +
> > +2. Digest list upload with the user space parser:
> > +
> > +.. code-block:: bash
> > +
> > + # manage_digest_lists -p add-digest -d /etc/digest_lists
> > +
> > +3. First digest list query:
> > +
> > +.. code-block:: bash
> > +
> > + # echo sha256-$(sha256sum /bin/cat) >
> /sys/kernel/security/integrity/diglim/digest_query
> > + # cat /sys/kernel/security/integrity/diglim/digest_query
> > +   sha256-[...]-0-file_list-rpm-coreutils-8.32-18.fc33.x86_64 (actions: 0):
> version: 1, algo: sha256, type: 2, modifiers: 1, count: 106, datalen: 3392
> > +
> > +4. Second digest list query:
> > +
> > +.. code-block:: bash
> > +
> > + # echo sha256-$(sha256sum /bin/zip) >
> /sys/kernel/security/integrity/diglim/digest_query
> > + # cat /sys/kernel/security/integrity/diglim/digest_query
> > +   sha256-[...]-0-file_list-rpm-zip-3.0-27.fc33.x86_64 (actions: 0): version: 1,
> algo: sha256, type: 2, modifiers: 1, count: 4, datalen: 128
> > +
> > +
> > +Preliminary Performance Evaluation
> > +----------------------------------
> > +
> > +This section provides an initial estimation of the overhead introduced by
> > +DIGLIM. The estimation has been performed on a Fedora 33 virtual machine
> > +with 1447 packages installed. The virtual machine has 16 vCPU (host CPU:
> > +AMD Ryzen Threadripper PRO 3955WX 16-Cores) and 2G of RAM (host
> memory:
> > +64G). The virtual machine also has a vTPM with libtpms and swtpm as
> > +backend.
> > +
> > +After writing the RPM headers to files, the size of the directory
> > +containing them is 36M.
> > +
> > +After converting the RPM headers to the compact digest list, the size of
> > +the data being uploaded to the kernel is 3.6M.
> > +
> > +The time to load the entire RPM database is 0.628s.
> > +
> > +After loading the digest lists to the kernel, the slab usage due to
> > +indexing is (obtained with slab_nomerge in the kernel command line):
> > +
> > +::
> > +
> > + OBJS   ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
> > + 118144 118144 100%    0,03K    923      128      3692K
> digest_list_item_ref_cache
> > + 102400 102400 100%    0,03K    800      128      3200K digest_item_cache
> > +   2646   2646 100%    0,09K     63       42       252K digest_list_item_cache
> > +
> > +The stats, obtained from the ``digests_count`` interface, introduced later,
> > +are:
> > +
> > +::
> > +
> > + Parser digests: 0
> > + File digests: 99100
> > + Metadata digests: 0
> > + Digest list digests: 1423
> > +
> > +On this installation, this would be the worst case in which all files are
> > +measured and/or appraised, which is currently not recommended without
> > +enforcing an integrity policy protecting mutable files. Infoflow LSM is a
> > +component to accomplish this task:
> > +
> > +https://patchwork.kernel.org/project/linux-
> integrity/cover/20190818235745.1417-1-roberto.sassu@huawei.com/
> > +
> > +The first manageable goal of IMA with DIGLIM is to use an execution policy,
> > +with measurement and/or appraisal of files executed or mapped in memory
> as
> > +executable (in addition to kernel modules and firmware). In this
> > +case, the digest list contains the digest only for those files. The numbers
> > +above change as follows.
> > +
> > +After converting the RPM headers to the compact digest list, the size of
> > +the data being uploaded to the kernel is 208K.
> > +
> > +The time to load the digest of binaries and shared libraries is 0.062s.
> > +
> > +After loading the digest lists to the kernel, the slab usage due to
> > +indexing is:
> > +
> > +::
> > +
> > + OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
> > + 7168   7168 100%    0,03K     56      128       224K digest_list_item_ref_cache
> > + 7168   7168 100%    0,03K     56      128       224K digest_item_cache
> > + 1134   1134 100%    0,09K     27       42       108K digest_list_item_cache
> > +
> > +
> > +The stats, obtained from the ``digests_count`` interface, are:
> > +
> > +::
> > +
> > + Parser digests: 0
> > + File digests: 5986
> > + Metadata digests: 0
> > + Digest list digests: 1104
> > +
> > +
> > +Comparison with IMA
> > +~~~~~~~~~~~~~~~~~~~
> > +
> > +This section compares the performance between the current solution for
> IMA
> > +measurement and appraisal, and IMA with DIGLIM.
> > +
> > +
> > +Workload A (without DIGLIM):
> > +
> > +#. cat file[0-5985] > /dev/null
> > +
> > +
> > +Workload B (with DIGLIM):
> > +
> > +#. echo $PWD/0-file_list-compact-file[0-1103] >
> <securityfs>/integrity/diglim/digest_list_add
> > +#. cat file[0-5985] > /dev/null
> > +
> > +
> > +Workload A execution time without IMA policy:
> > +
> > +::
> > +
> > + real	0m0,155s
> > + user	0m0,008s
> > + sys	0m0,066s
> > +
> > +
> > +Measurement
> > +...........
> > +
> > +IMA policy:
> > +
> > +::
> > +
> > + measure fowner=2000 func=FILE_CHECK mask=MAY_READ
> use_diglim=allow pcr=11 ima_template=ima-sig
> > +
> > +``use_diglim`` is a policy keyword not yet supported by IMA.
> > +
> > +
> > +Workload A execution time with IMA and 5986 files with signature
> measured:
> > +
> > +::
> > +
> > + real	0m8,273s
> > + user	0m0,008s
> > + sys	0m2,537s
> > +
> > +
> > +Workload B execution time with IMA, 1104 digest lists with signature
> > +measured and uploaded to the kernel, and 5986 files with signature
> accessed
> > +but not measured (due to the file digest being found in the hash table):
> > +
> > +::
> > +
> > + real	0m1,837s
> > + user	0m0,036s
> > + sys	0m0,583s
> > +
> > +
> > +Appraisal
> > +.........
> > +
> > +IMA policy:
> > +
> > +::
> > +
> > + appraise fowner=2000 func=FILE_CHECK mask=MAY_READ
> use_diglim=allow
> > +
> > +``use_diglim`` is a policy keyword not yet supported by IMA.
> > +
> > +
> > +Workload A execution time with IMA and 5986 files with file signature
> > +appraised:
> > +
> > +::
> > +
> > + real	0m2,197s
> > + user	0m0,011s
> > + sys	0m2,022s
> > +
> > +
> > +Workload B execution time with IMA, 1104 digest lists with signature
> > +appraised and uploaded to the kernel, and with 5986 files with signature
> > +not verified (due to the file digest being found in the hash table):
> > +
> > +::
> > +
> > + real	0m0,982s
> > + user	0m0,020s
> > + sys	0m0,865s
> > diff --git a/Documentation/security/index.rst
> b/Documentation/security/index.rst
> > index 16335de04e8c..6c3aea41c55b 100644
> > --- a/Documentation/security/index.rst
> > +++ b/Documentation/security/index.rst
> > @@ -17,3 +17,4 @@ Security Documentation
> >     tpm/index
> >     digsig
> >     landlock
> > +   diglim/index
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 6c8be735cc91..c914dadd7e65 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -5452,6 +5452,15 @@ L:	linux-gpio@vger.kernel.org
> >  S:	Maintained
> >  F:	drivers/gpio/gpio-gpio-mm.c
> >
> > +DIGLIM
> > +M:	Roberto Sassu <roberto.sassu@huawei.com>
> > +L:	linux-integrity@vger.kernel.org
> > +S:	Supported
> > +T:	git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
> > +F:	Documentation/security/diglim/architecture.rst
> > +F:	Documentation/security/diglim/index.rst
> > +F:	Documentation/security/diglim/introduction.rst
> > +
> >  DIOLAN U2C-12 I2C DRIVER
> >  M:	Guenter Roeck <linux@roeck-us.net>
> >  L:	linux-i2c@vger.kernel.org
> 
> 
> 
> Thanks,
> Mauro

^ permalink raw reply

* RE: [RFC][PATCH v2 02/12] diglim: Basic definitions
From: Roberto Sassu @ 2021-07-28 11:45 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20210728133102.339c7b8e@coco.lan>

> From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> Sent: Wednesday, July 28, 2021 1:31 PM
> Em Mon, 26 Jul 2021 18:36:50 +0200
> Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> 
> > Introduce the basic definitions, exported to user space, to use digest
> > lists. The definitions, added to include/uapi/linux/diglim.h, are
> > documented in Documentation/security/diglim/implementation.rst.
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >  .../security/diglim/implementation.rst        | 97 +++++++++++++++++++
> >  Documentation/security/diglim/index.rst       |  1 +
> >  MAINTAINERS                                   |  2 +
> >  include/uapi/linux/diglim.h                   | 51 ++++++++++
> >  4 files changed, 151 insertions(+)
> >  create mode 100644 Documentation/security/diglim/implementation.rst
> >  create mode 100644 include/uapi/linux/diglim.h
> >
> > diff --git a/Documentation/security/diglim/implementation.rst
> b/Documentation/security/diglim/implementation.rst
> > new file mode 100644
> > index 000000000000..59a180b3bb3f
> > --- /dev/null
> > +++ b/Documentation/security/diglim/implementation.rst
> > @@ -0,0 +1,97 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +Implementation
> > +==============
> > +
> > +This section describes the implementation of DIGLIM.
> > +
> > +
> > +Basic Definitions
> > +-----------------
> > +
> > +This section introduces the basic definitions required to use DIGLIM.
> > +
> > +
> > +Compact Digest List Format
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +.. kernel-doc:: include/uapi/linux/diglim.h
> > +   :identifiers: compact_list_hdr
> > +
> > +Compact Types
> > +.............
> > +
> > +Digests can be of different types:
> > +
> > +- ``COMPACT_PARSER``: digests of executables which are given the ability
> to
> > +  parse digest lists not in the compact format and to upload to the kernel
> > +  the digest list converted to the compact format;
> > +- ``COMPACT_FILE``: digests of regular files;
> > +- ``COMPACT_METADATA``: digests of file metadata (e.g. the digest
> > +  calculated by EVM to verify a portable signature);
> > +- ``COMPACT_DIGEST_LIST``: digests of digest lists (only used internally by
> > +  the kernel).
> > +
> > +Different users of DIGLIM might query digests with different compact types.
> > +For example, IMA would be interested in COMPACT_FILE, as it deals with
> > +regular files, while EVM would be interested in COMPACT_METADATA, as it
> > +verifies file metadata.
> > +
> > +
> > +Compact Modifiers
> > +.................
> > +
> > +Digests can also have specific attributes called modifiers (bit position):
> > +
> > +- ``COMPACT_MOD_IMMUTABLE``: file content or metadata should not be
> > +  modifiable.
> > +
> > +IMA might use this information to deny open for writing, or EVM to deny
> > +setxattr operations.
> > +
> > +
> > +Actions
> > +.......
> > +
> > +This section defines a set of possible actions that have been executed on
> > +the digest lists (bit position):
> > +
> > +- ``COMPACT_ACTION_IMA_MEASURED``: the digest list has been
> measured by
> > +  IMA;
> > +- ``COMPACT_ACTION_IMA_APPRAISED``: the digest list has been
> successfully
> > +  appraised by IMA;
> > +- ``COMPACT_ACTION_IMA_APPRAISED_DIGSIG``: the digest list has been
> > +  successfully appraised by IMA by verifying a digital signature.
> > +
> > +This information might help users of DIGLIM to decide whether to use the
> > +result of a queried digest.
> > +
> > +For example, if a digest belongs to a digest list that was not measured
> > +before, IMA should ignore the result of the query, as the measurement list
> > +sent to remote verifiers would lack which digests have been uploaded to
> the
> > +kernel.
> > +
> > +
> > +Compact Digest List Example
> > +...........................
> > +
> > +::
> > +
> > + version: 1, type: 2, modifiers: 0 algo: 4, count: 3, datalen: 96
> > + <SHA256 digest1><SHA256 digest2><SHA256 digest3>
> > + version: 1, type: 3, modifiers: 1 algo: 6, count: 2, datalen: 128
> > + <SHA512 digest1><SHA512 digest2>
> > +
> > +This digest list consists of two blocks. The first block contains three
> > +SHA256 digests of regular files. The second block contains two SHA512
> > +digests of immutable metadata.
> > +
> > +
> > +Compact Digest List Operations
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Finally, this section defines the possible operations that can be performed
> > +with digest lists:
> > +
> > +- ``DIGEST_LIST_ADD``: the digest list is being added;
> > +- ``DIGEST_LIST_DEL``: the digest list is being deleted.
> > diff --git a/Documentation/security/diglim/index.rst
> b/Documentation/security/diglim/index.rst
> > index 0fc5ab019bc0..4771134c2f0d 100644
> > --- a/Documentation/security/diglim/index.rst
> > +++ b/Documentation/security/diglim/index.rst
> > @@ -9,3 +9,4 @@ Digest Lists Integrity Module (DIGLIM)
> >
> >     introduction
> >     architecture
> > +   implementation
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index c914dadd7e65..f61f5239468a 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -5458,8 +5458,10 @@ L:	linux-integrity@vger.kernel.org
> >  S:	Supported
> >  T:	git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
> >  F:	Documentation/security/diglim/architecture.rst
> > +F:	Documentation/security/diglim/implementation.rst
> >  F:	Documentation/security/diglim/index.rst
> >  F:	Documentation/security/diglim/introduction.rst
> > +F:	include/uapi/linux/diglim.h
> >
> >  DIOLAN U2C-12 I2C DRIVER
> >  M:	Guenter Roeck <linux@roeck-us.net>
> > diff --git a/include/uapi/linux/diglim.h b/include/uapi/linux/diglim.h
> > new file mode 100644
> > index 000000000000..8a33d1f0fefb
> > --- /dev/null
> > +++ b/include/uapi/linux/diglim.h
> > @@ -0,0 +1,51 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> > + *
> > + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> > + *
> > + * DIGLIM definitions exported to user space, useful for generating digest
> > + * lists.
> > + */
> > +
> > +#ifndef _UAPI__LINUX_DIGLIM_H
> > +#define _UAPI__LINUX_DIGLIM_H
> > +
> > +#include <linux/types.h>
> > +#include <linux/hash_info.h>
> > +
> > +enum compact_types { COMPACT_KEY, COMPACT_PARSER,
> COMPACT_FILE,
> > +		     COMPACT_METADATA, COMPACT_DIGEST_LIST,
> COMPACT__LAST };
> > +
> > +enum compact_modifiers { COMPACT_MOD_IMMUTABLE,
> COMPACT_MOD__LAST };
> > +
> > +enum compact_actions { COMPACT_ACTION_IMA_MEASURED,
> > +		       COMPACT_ACTION_IMA_APPRAISED,
> > +		       COMPACT_ACTION_IMA_APPRAISED_DIGSIG,
> > +		       COMPACT_ACTION__LAST };
> > +
> > +enum ops { DIGEST_LIST_ADD, DIGEST_LIST_DEL, DIGEST_LIST_OP__LAST };
> > +
> > +/**
> > + * struct compact_list_hdr - header of the following concatenated digests
> > + * @version: version of the digest list
> > + * @_reserved: field reserved for future use
> > + * @type: type of digest list among enum compact_types
> > + * @modifiers: additional attributes among (1 << enum compact_modifiers)
> > + * @algo: digest algorithm
> > + * @count: number of digests
> > + * @datalen: length of concatenated digests
> > + *
> > + * A digest list is a set of blocks composed by struct compact_list_hdr and
> > + * the following concatenated digests.
> > + */
> > +struct compact_list_hdr {
> > +	__u8 version;
> > +	__u8 _reserved;
> > +	__le16 type;
> > +	__le16 modifiers;
> > +	__le16 algo;
> > +	__le32 count;
> > +	__le32 datalen;
> > +} __packed;
> > +#endif /*_UAPI__LINUX_DIGLIM_H*/
> 
> Besides Greg's notes, I'm wondering why to enforce a particular
> endness here. I mean, this is uAPI. I would expect it to use the
> CPU endianness instead, in order to avoid uneeded conversions.

Also Greg had the same concern. I hoped the Lifecycle section clarified
the fact that digest lists are generated by software vendors not the
local system. Should I add something more in the documentation?

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> Thanks,
> Mauro

^ permalink raw reply

* RE: [RFC][PATCH v2 03/12] diglim: Objects
From: Roberto Sassu @ 2021-07-28 11:47 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20210728133835.2e55e0eb@coco.lan>

> From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> Sent: Wednesday, July 28, 2021 1:39 PM
> Em Mon, 26 Jul 2021 18:36:51 +0200
> Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> 
> > Define the objects to manage digest lists:
> >
> > - digest_list_item: a digest list loaded into the kernel;
> > - digest_list_item_ref: a reference to a digest list;
> > - digest_item: a digest of a digest list.
> >
> > Also define some helpers for the objects. More information can be found in
> > Documentation/security/diglim/implementation.rst.
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >  .../security/diglim/implementation.rst        | 105 ++++++++++++++
> >  MAINTAINERS                                   |   1 +
> >  security/integrity/diglim/diglim.h            | 134 ++++++++++++++++++
> >  3 files changed, 240 insertions(+)
> >  create mode 100644 security/integrity/diglim/diglim.h
> >
> > diff --git a/Documentation/security/diglim/implementation.rst
> b/Documentation/security/diglim/implementation.rst
> > index 59a180b3bb3f..6002049612a1 100644
> > --- a/Documentation/security/diglim/implementation.rst
> > +++ b/Documentation/security/diglim/implementation.rst
> > @@ -95,3 +95,108 @@ with digest lists:
> >
> >  - ``DIGEST_LIST_ADD``: the digest list is being added;
> >  - ``DIGEST_LIST_DEL``: the digest list is being deleted.
> > +
> > +
> > +Objects
> > +-------
> > +
> > +This section defines the objects to manage digest lists.
> > +
> > +.. kernel-doc:: security/integrity/diglim/diglim.h
> > +
> > +They are represented in the following class diagram:
> > +
> > +::
> > +
> > + digest_offset,
> > + hdr_offset---------------+
> > +                          |
> > +                          |
> > + +------------------+     |     +----------------------+
> > + | digest_list_item |--- N:1 ---| digest_list_item_ref |
> > + +------------------+           +----------------------+
> > +                                           |
> > +                                          1:N
> > +                                           |
> > +                                    +-------------+
> > +                                    | digest_item |
> > +                                    +-------------+
> > +
> > +A ``digest_list_item`` is associated to one or multiple
> > +``digest_list_item_ref``, one for each digest it contains. However,
> > +a ``digest_list_item_ref`` is associated to only one ``digest_list_item``,
> > +as it represents a single location within a specific digest list.
> > +
> > +Given that a ``digest_list_item_ref`` represents a single location, it is
> > +associated to only one ``digest_item``. However, a ``digest_item`` can have
> > +multiple references (as it might appears multiple times within the same
> > +digest list or in different digest lists, if it is duplicated).
> > +
> > +All digest list references are stored for a given digest, so that a query
> > +result can include the OR of the modifiers and actions of each referenced
> > +digest list.
> > +
> > +The relationship between the described objects can be graphically
> > +represented as:
> > +
> > +::
> 
> Just merge "::" at the previous line (everywhere).

Ok.

> > +
> > + Hash table            +-------------+         +-------------+
> > + PARSER      +-----+   | digest_item |         | digest_item |
> > + FILE        | key |-->|             |-->...-->|             |
> > + METADATA    +-----+   |ref0|...|refN|         |ref0|...|refN|
> > +                       +-------------+         +-------------+
> > +            ref0:         |                               | refN:
> > +            digest_offset | +-----------------------------+ digest_offset
> > +            hdr_offset    | |                               hdr_offset
> > +                          | |
> > +                          V V
> > +                     +--------------------+
> > +                     |  digest_list_item  |
> > +                     |                    |
> > +                     | size, buf, actions |
> > +                     +--------------------+
> > +                          ^
> > +                          |
> > + Hash table            +-------------+         +-------------+
> > + DIGEST_LIST +-----+   |ref0         |         |ref0         |
> > +             | key |-->|             |-->...-->|             |
> > +             +-----+   | digest_item |         | digest_item |
> > +                       +-------------+         +-------------+
> > +
> > +The reference for the digest of the digest list differs from the references
> > +for the other digest types. ``digest_offset`` and ``hdr_offset`` are set to
> > +zero, so that the digest of the digest list is retrieved from the
> > +``digest_list_item`` structure directly (see ``get_digest()`` below).
> > +
> > +Finally, this section defines useful helpers to access a digest or the
> > +header the digest belongs to. For example:
> > +
> > +::
> > +
> > + static inline struct compact_list_hdr *get_hdr(
> > +                                      struct digest_list_item *digest_list,
> > +                                      loff_t hdr_offset)
> 
> I would write this to avoid ending a line with an open parenthesis. You could,
> for instance, write it as:
> 
> 	static inline struct compact_list_hdr *
> 	get_hdr(struct digest_list_item *digest_list,
> 		off_t hdr_offset)
> 
> if you also want to avoid to have a line bigger than 80 columns.

Ok.

> > + {
> > +         return (struct compact_list_hdr *)(digest_list->buf + hdr_offset);
> > + }
> > +
> > +the header can be obtained by summing the address of the digest list buffer
> > +in the ``digest_list_item`` structure with ``hdr_offset``.
> > +
> > +Similarly:
> > +
> > +::
> > +
> > + static inline u8 *get_digest(struct digest_list_item *digest_list,
> > +                              loff_t digest_offset, loff_t hdr_offset)
> > + {
> > +         /* Digest list digest is stored in a different place. */
> > +         if (!digest_offset)
> > +                 return digest_list->digest;
> > +         return digest_list->buf + digest_offset;
> > + }
> > +
> > +the digest can be obtained by summing the address of the digest list buffer
> > +with ``digest_offset`` (except for the digest lists, where the digest is
> > +stored in the ``digest`` field of the ``digest_list_item`` structure).
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index f61f5239468a..f7592d41367d 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -5462,6 +5462,7 @@ F:
> 	Documentation/security/diglim/implementation.rst
> >  F:	Documentation/security/diglim/index.rst
> >  F:	Documentation/security/diglim/introduction.rst
> >  F:	include/uapi/linux/diglim.h
> > +F:	security/integrity/diglim/diglim.h
> >
> >  DIOLAN U2C-12 I2C DRIVER
> >  M:	Guenter Roeck <linux@roeck-us.net>
> > diff --git a/security/integrity/diglim/diglim.h
> b/security/integrity/diglim/diglim.h
> > new file mode 100644
> > index 000000000000..578253d7e1d1
> > --- /dev/null
> > +++ b/security/integrity/diglim/diglim.h
> > @@ -0,0 +1,134 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> > + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> > + *
> > + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> > + *
> > + * Definitions only used inside DIGLIM.
> > + */
> > +
> > +#ifndef __DIGLIM_INTERNAL_H
> > +#define __DIGLIM_INTERNAL_H
> > +
> > +#include <linux/types.h>
> > +#include <linux/crypto.h>
> > +#include <linux/fs.h>
> > +#include <linux/security.h>
> > +#include <linux/hash.h>
> > +#include <linux/tpm.h>
> > +#include <linux/audit.h>
> > +#include <crypto/hash_info.h>
> > +#include <linux/hash_info.h>
> > +#include <uapi/linux/diglim.h>
> > +
> > +#define MAX_DIGEST_SIZE 64
> > +#define HASH_BITS 10
> > +#define DIGLIM_HTABLE_SIZE (1 << HASH_BITS)
> > +
> > +/**
> > + * struct digest_list_item - a digest list loaded into the kernel
> > + *
> > + * @size: size of the digest list buffer
> > + * @buf: digest list buffer
> > + * @digest: digest of the digest list
> > + * @label: label used to identify the digest list (e.g. file name)
> > + * @actions: actions performed on the digest list
> > + * @algo: digest algorithm
> > + */
> > +struct digest_list_item {
> > +	loff_t size;
> > +	u8 *buf;
> > +	u8 digest[64];
> > +	const char *label;
> > +	u8 actions;
> > +	enum hash_algo algo;
> > +};
> > +
> > +/**
> > + * struct digest_list_item_ref - a reference to a digest list
> > + *
> > + * @list: linked list pointers
> > + * @digest_list: pointer to struct digest_list_item
> > + * @digest_offset: offset of the digest in the referenced digest list
> > + * @hdr_offset: offset of the header the digest refers to in the digest list
> > + */
> > +struct digest_list_item_ref {
> > +	struct list_head list;
> > +	struct digest_list_item *digest_list;
> > +	u32 digest_offset;
> > +	u32 hdr_offset;
> > +};
> > +
> > +/**
> > + * struct digest_item - a digest of a digest list
> > + *
> > + * @hnext: pointers of the hash table
> > + * @refs: linked list of struct digest_list_item_ref
> > + */
> > +struct digest_item {
> > +	struct hlist_node hnext;
> > +	struct list_head refs;
> > +};
> > +
> 
> 
> > +struct h_table {
> > +	unsigned long len;
> > +	struct hlist_head queue[DIGLIM_HTABLE_SIZE];
> > +};
> > +
> > +static inline unsigned int hash_key(u8 *digest)
> > +{
> > +	return (digest[0] | digest[1] << 8) % DIGLIM_HTABLE_SIZE;
> > +}
> > +
> > +static inline struct compact_list_hdr *get_hdr(
> > +					struct digest_list_item *digest_list,
> > +					loff_t hdr_offset)
> 
> Same here with regards to open parenthesis.
> 
> > +{
> > +	return (struct compact_list_hdr *)(digest_list->buf + hdr_offset);
> > +}
> > +
> > +static inline enum hash_algo get_algo(struct digest_list_item *digest_list,
> > +				      loff_t digest_offset, loff_t hdr_offset)
> > +{
> > +	/* Digest list digest algorithm is stored in a different place. */
> > +	if (!digest_offset)
> > +		return digest_list->algo;
> > +
> > +	return get_hdr(digest_list, hdr_offset)->algo;
> > +}
> > +
> > +static inline u8 *get_digest(struct digest_list_item *digest_list,
> > +			     loff_t digest_offset, loff_t hdr_offset)
> > +{
> > +	/* Digest list digest is stored in a different place. */
> > +	if (!digest_offset)
> > +		return digest_list->digest;
> > +
> > +	return digest_list->buf + digest_offset;
> > +}
> > +
> > +static inline struct compact_list_hdr *get_hdr_ref(
> > +					struct digest_list_item_ref *ref)
> > +{
> > +	return get_hdr(ref->digest_list, ref->hdr_offset);
> > +}
> > +
> > +static inline enum hash_algo get_algo_ref(struct digest_list_item_ref *ref)
> > +{
> > +	/* Digest list digest algorithm is stored in a different place. */
> > +	if (!ref->digest_offset)
> > +		return ref->digest_list->algo;
> > +
> > +	return get_hdr_ref(ref)->algo;
> > +}
> > +
> > +static inline u8 *get_digest_ref(struct digest_list_item_ref *ref)
> > +{
> > +	/* Digest list digest is stored in a different place. */
> > +	if (!ref->digest_offset)
> > +		return ref->digest_list->digest;
> > +
> > +	return ref->digest_list->buf + ref->digest_offset;
> > +}
> 
> I would also document the above static inline functions and
> struct h_table.

Ok.

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> > +#endif /*__DIGLIM_INTERNAL_H*/
> 
> 
> 
> Thanks,
> Mauro

^ permalink raw reply

* Re: [RFC][PATCH v2 04/12] diglim: Methods
From: Mauro Carvalho Chehab @ 2021-07-28 12:18 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-5-roberto.sassu@huawei.com>

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

> Introduce the methods requires to manage the three objects defined.
> 
> - digest_item methods:
>   - digest_add()
>   - digest_del()
>   - __digest_lookup()
>   - diglim_digest_get_info()
> 
> - digest_list_item_ref methods:
>   - digest_list_ref_add()
>   - digest_list_ref_del()
> 
> - digest_list_item methods:
>   - digest_list_add()
>   - digest_list_del()
> 
> More information about these functions can be found in
> Documentation/security/diglim/implementation.rst.
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  .../security/diglim/implementation.rst        |   9 +
>  MAINTAINERS                                   |   2 +
>  include/linux/diglim.h                        |  28 +
>  security/integrity/Kconfig                    |   1 +
>  security/integrity/Makefile                   |   1 +
>  security/integrity/diglim/Kconfig             |  11 +
>  security/integrity/diglim/Makefile            |   8 +
>  security/integrity/diglim/diglim.h            |  20 +-
>  security/integrity/diglim/methods.c           | 499 ++++++++++++++++++
>  9 files changed, 578 insertions(+), 1 deletion(-)
>  create mode 100644 include/linux/diglim.h
>  create mode 100644 security/integrity/diglim/Kconfig
>  create mode 100644 security/integrity/diglim/Makefile
>  create mode 100644 security/integrity/diglim/methods.c
> 
> diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst
> index 6002049612a1..54af23b2f5f1 100644
> --- a/Documentation/security/diglim/implementation.rst
> +++ b/Documentation/security/diglim/implementation.rst
> @@ -200,3 +200,12 @@ Similarly:
>  the digest can be obtained by summing the address of the digest list buffer
>  with ``digest_offset`` (except for the digest lists, where the digest is
>  stored in the ``digest`` field of the ``digest_list_item`` structure).
> +
> +
> +Methods
> +-------
> +
> +This section introduces the methods requires to manage the three objects
> +defined.
> +
> +.. kernel-doc:: security/integrity/diglim/methods.c
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f7592d41367d..9e085a36654a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5461,8 +5461,10 @@ F:	Documentation/security/diglim/architecture.rst
>  F:	Documentation/security/diglim/implementation.rst
>  F:	Documentation/security/diglim/index.rst
>  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/methods.c
>  
>  DIOLAN U2C-12 I2C DRIVER
>  M:	Guenter Roeck <linux@roeck-us.net>
> diff --git a/include/linux/diglim.h b/include/linux/diglim.h
> new file mode 100644
> index 000000000000..d4b4548a288b
> --- /dev/null
> +++ b/include/linux/diglim.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> + *
> + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> + *
> + * DIGLIM functions available for use by kernel subsystems.
> + */
> +
> +#ifndef __DIGLIM_H
> +#define __DIGLIM_H
> +
> +#include <crypto/hash_info.h>
> +#include <uapi/linux/diglim.h>
> +
> +#ifdef CONFIG_DIGLIM
> +extern int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
> +				  enum compact_types type, u16 *modifiers,
> +				  u8 *actions);
> +#else
> +static inline int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
> +					 enum compact_types type,
> +					 u16 *modifiers, u8 *actions)
> +{
> +	return -ENOENT;
> +}
> +#endif /*CONFIG_DIGLIM*/
> +#endif /*__DIGLIM_H*/
> diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
> index 71f0177e8716..8f94f4dcc052 100644
> --- a/security/integrity/Kconfig
> +++ b/security/integrity/Kconfig
> @@ -98,5 +98,6 @@ config INTEGRITY_AUDIT
>  
>  source "security/integrity/ima/Kconfig"
>  source "security/integrity/evm/Kconfig"
> +source "security/integrity/diglim/Kconfig"
>  
>  endif   # if INTEGRITY
> diff --git a/security/integrity/Makefile b/security/integrity/Makefile
> index 7ee39d66cf16..d6166550a6b8 100644
> --- a/security/integrity/Makefile
> +++ b/security/integrity/Makefile
> @@ -19,3 +19,4 @@ integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
>                                       platform_certs/keyring_handler.o
>  obj-$(CONFIG_IMA)			+= ima/
>  obj-$(CONFIG_EVM)			+= evm/
> +obj-$(CONFIG_DIGLIM)			+= diglim/
> diff --git a/security/integrity/diglim/Kconfig b/security/integrity/diglim/Kconfig
> new file mode 100644
> index 000000000000..436a76a14337
> --- /dev/null
> +++ b/security/integrity/diglim/Kconfig
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +# Digest Lists Integrity Module (DIGLIM)
> +#
> +config DIGLIM
> +	bool "Digest Lists Integrity Module (DIGLIM)"
> +	select SECURITYFS
> +	select CRYPTO
> +	select CRYPTO_HASH_INFO
> +	help
> +	  DIGLIM provides reference values for file content and metadata,
> +	  that can be used for measurement and appraisal with IMA.
> diff --git a/security/integrity/diglim/Makefile b/security/integrity/diglim/Makefile
> new file mode 100644
> index 000000000000..b761ed8cfb3e
> --- /dev/null
> +++ b/security/integrity/diglim/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for building Digest Lists Integrity Module (DIGLIM).
> +#
> +
> +obj-$(CONFIG_DIGLIM) += diglim.o
> +
> +diglim-y := methods.o
> diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h
> index 578253d7e1d1..25851e7d4906 100644
> --- a/security/integrity/diglim/diglim.h
> +++ b/security/integrity/diglim/diglim.h
> @@ -20,7 +20,7 @@
>  #include <linux/audit.h>
>  #include <crypto/hash_info.h>
>  #include <linux/hash_info.h>
> -#include <uapi/linux/diglim.h>
> +#include <linux/diglim.h>
>  
>  #define MAX_DIGEST_SIZE 64
>  #define HASH_BITS 10
> @@ -81,6 +81,8 @@ static inline unsigned int hash_key(u8 *digest)
>  	return (digest[0] | digest[1] << 8) % DIGLIM_HTABLE_SIZE;
>  }
>  
> +extern struct h_table htable[COMPACT__LAST];
> +

it sounds somewhat risky to use just "htable" for a var declared
as external.

>  static inline struct compact_list_hdr *get_hdr(
>  					struct digest_list_item *digest_list,
>  					loff_t hdr_offset)
> @@ -131,4 +133,20 @@ static inline u8 *get_digest_ref(struct digest_list_item_ref *ref)
>  
>  	return ref->digest_list->buf + ref->digest_offset;
>  }
> +
> +struct digest_item *__digest_lookup(u8 *digest, enum hash_algo algo,
> +				    enum compact_types type, u16 *modifiers,
> +				    u8 *actions);
> +struct digest_item *digest_add(u8 *digest, enum hash_algo algo,
> +			       enum compact_types type,
> +			       struct digest_list_item *digest_list,
> +			       loff_t digest_offset, loff_t hdr_offset);
> +void digest_del(u8 *digest, enum hash_algo algo, enum compact_types type,
> +		struct digest_list_item *digest_list, loff_t digest_offset,
> +		loff_t hdr_offset);
> +struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
> +				    loff_t size, u8 *buf, u8 actions,
> +				    const char *label);
> +void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
> +		     struct digest_list_item *digest_list);
>  #endif /*__DIGLIM_INTERNAL_H*/
> diff --git a/security/integrity/diglim/methods.c b/security/integrity/diglim/methods.c
> new file mode 100644
> index 000000000000..7ed61399cfe8
> --- /dev/null
> +++ b/security/integrity/diglim/methods.c
> @@ -0,0 +1,499 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> + *
> + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> + *
> + * Functions to manage digest lists.
> + */
> +
> +#include <linux/vmalloc.h>
> +#include <linux/module.h>
> +#include <linux/fault-inject.h>
> +
> +#include "diglim.h"
> +#include "../integrity.h"
> +
> +/* Define a cache for each object type. */
> +static struct kmem_cache *digest_list_item_cache __read_mostly;
> +static struct kmem_cache *digest_list_item_ref_cache __read_mostly;
> +static struct kmem_cache *digest_item_cache __read_mostly;
> +
> +/* Define a hash table for each digest type. */
> +struct h_table htable[COMPACT__LAST] = {{
> +	.queue[0 ... DIGLIM_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
> +}};


> +
> +#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
> +static DECLARE_FAULT_ATTR(fail_diglim);
> +
> +static int __init fail_diglim_debugfs(void)
> +{
> +	struct dentry *dir = fault_create_debugfs_attr("fail_diglim", NULL,
> +						       &fail_diglim);
> +
> +	return PTR_ERR_OR_ZERO(dir);
> +}
> +
> +static inline bool should_fail_diglim(void)
> +{
> +	return should_fail(&fail_diglim, 1);
> +}
> +
> +late_initcall(fail_diglim_debugfs);
> +#else
> +static inline bool should_fail_diglim(void)
> +{
> +	return false;
> +}
> +#endif


I guess this is a matter of personal preference, but, IMO, it is a lot better
to place the debugfs stuff on a separate source file, avoiding ugly #ifdefs
in the middle of the code.

Ok, the current code is too small to deserve a separate file, but
if later patches would add more stuff, then I would opt to have this on
a separate file.

> +
> +/**
> + * __digest_lookup - lookup digest and return associated modifiers and actions
> + * @digest: digest to lookup
> + * @algo: digest algorithm
> + * @type: type of digest to lookup (e.g. file, metadata)
> + * @modifiers: modifiers (attributes) associated to the found digest
> + * @actions: actions performed by IMA on the digest list containing the digest
> + *
> + * This function searches the given digest in the hash table depending on the
> + * passed type and sets the modifiers and actions associated to the digest, if
> + * the pointers are not NULL.
> + *
> + * This function is not intended for external use, as the returned digest item
> + * could be freed at any time after it has been returned.
> + * diglim_digest_get_info() should be used instead by external callers, as it
> + * only returns the modifiers and the actions associated to the digest at the
> + * time the digest is searched.
> + *
> + * RCU protects both the hash table and the linked list of references to the
> + * digest lists containing the found digest.
> + *
> + * Return: a digest_item structure if the digest is found, NULL otherwise.
> + */
> +struct digest_item *__digest_lookup(u8 *digest, enum hash_algo algo,
> +				    enum compact_types type, u16 *modifiers,
> +				    u8 *actions)
> +{
> +	struct digest_item *d = NULL;
> +	struct digest_list_item_ref *ref;
> +	int digest_len = hash_digest_size[algo];
> +	unsigned int key = hash_key(digest);
> +	bool found = false;
> +
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(d, &htable[type].queue[key], hnext) {
> +		list_for_each_entry_rcu(ref, &d->refs, list) {
> +			if (get_algo_ref(ref) != algo ||
> +			    memcmp(get_digest_ref(ref), digest, digest_len))
> +				break;
> +
> +			found = true;
> +
> +			/* There is no need to scan all digest list refs. */
> +			if (!modifiers || !actions)
> +				break;
> +
> +			/*
> +			 * The resulting modifiers and actions are the OR of the
> +			 * modifiers and actions for each digest list.
> +			 */
> +			*modifiers |= get_hdr_ref(ref)->modifiers;
> +			*actions |= ref->digest_list->actions;
> +		}
> +
> +		if (found)
> +			break;
> +	}
> +
> +	rcu_read_unlock();
> +	return d;
> +}
> +
> +/**
> + * diglim_digest_get_info - lookup digest and return modifiers and actions
> + * @digest: digest to lookup
> + * @algo: digest algorithm
> + * @type: type of digest to lookup (e.g. file, metadata)
> + * @modifiers: modifiers (attributes) associated to the found digest
> + * @actions: actions performed by IMA on the digest lists containing the digest
> + *
> + * This function searches the given digest in the hash table depending on the
> + * passed type and sets the modifiers and actions associated to the digest, if
> + * the pointers are not NULL.
> + *
> + * This function is safe for external use, as it does not return pointers of
> + * objects that can be freed without the caller notices it.
> + *
> + * Return: 0 if the digest is found, -ENOENT otherwise.
> + */
> +int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
> +			   enum compact_types type, u16 *modifiers, u8 *actions)
> +{
> +	struct digest_item *d;
> +
> +	d = __digest_lookup(digest, algo, type, modifiers, actions);
> +	if (!d)
> +		return -ENOENT;
> +
> +	return 0;
> +}
> +
> +/**
> + * digest_list_ref_add - add reference to a digest list
> + * @d: digest a new reference is added to
> + * @digest_list: digest list whose reference is being added
> + * @digest_offset: offset of the digest in the buffer of the digest list
> + * @hdr_offset: offset of the header within the digest list the digest refers to
> + *
> + * This function adds a new reference to an existing digest list for a given
> + * digest. The reference is described by the digest_list_item_ref structure and
> + * consists of a pointer of the digest list, the offset of the digest to the
> + * beginning of the digest list buffer and the offset of the header the digest
> + * refers to (each digest list might be composed of several digest blocks, each
> + * prefixed by a header describing the attributes of those digests).
> + *
> + * Return: 0 if a new digest list reference was successfully added, a negative
> + * value otherwise.
> + */
> +static int digest_list_ref_add(struct digest_item *d,
> +			       struct digest_list_item *digest_list,
> +			       loff_t digest_offset, loff_t hdr_offset)
> +{
> +	struct digest_list_item_ref *new_ref = NULL;
> +	u8 *digest = get_digest(digest_list, digest_offset, hdr_offset);
> +	enum hash_algo algo = get_algo(digest_list, digest_offset, hdr_offset);
> +	int digest_len = hash_digest_size[algo];
> +
> +	/* Allocate a new reference. */
> +	if (!should_fail_diglim())
> +		new_ref = kmem_cache_alloc(digest_list_item_ref_cache,
> +					   GFP_KERNEL);
> +	if (!new_ref) {
> +		print_hex_dump(KERN_ERR, "digest list ref allocation failed: ",
> +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> +			       digest_len, true);
> +		return -ENOMEM;
> +	}
> +
> +	/* Set the new reference. */
> +	new_ref->digest_list = digest_list;
> +	/* Converting loff_t -> u32 is fine as long as the digest list < 4G. */
> +	new_ref->digest_offset = digest_offset;
> +	new_ref->hdr_offset = hdr_offset;
> +
> +	list_add_tail_rcu(&new_ref->list, &d->refs);
> +
> +	print_hex_dump_debug("add digest list ref: ", DUMP_PREFIX_NONE,
> +			     digest_len, 1, digest, digest_len, true);
> +	return 0;
> +}
> +
> +/**
> + * digest_list_ref_del - del reference to a digest list
> + * @d: digest a reference is deleted from
> + * @digest_list: digest list whose reference is being deleted
> + * @digest_offset: offset of the digest in the buffer of the digest list
> + * @hdr_offset: offset of the header within the digest list the digest refers to
> + *
> + * This function searches the reference to an already loaded digest list in the
> + * linked list of references stored for each digest item. If the reference is
> + * found (if not, it is a bug), the function deletes it from the linked list.
> + */
> +static void digest_list_ref_del(struct digest_item *d,
> +				struct digest_list_item *digest_list,
> +				loff_t digest_offset, loff_t hdr_offset)
> +{
> +	struct digest_list_item_ref *ref;
> +	u8 *digest = get_digest(digest_list, digest_offset, hdr_offset);
> +	enum hash_algo algo = get_algo(digest_list, digest_offset, hdr_offset);
> +	int digest_len = hash_digest_size[algo];
> +
> +	/* Search for a digest list reference. */
> +	list_for_each_entry(ref, &d->refs, list)
> +		if (ref->digest_list == digest_list)
> +			break;
> +
> +	if (!ref) {
> +		print_hex_dump(KERN_ERR, "digest list ref not found: ",
> +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> +			       digest_len, true);
> +		return;
> +	}
> +
> +	list_del_rcu(&ref->list);
> +	kmem_cache_free(digest_list_item_ref_cache, ref);
> +
> +	print_hex_dump_debug("del digest list ref: ", DUMP_PREFIX_NONE,
> +			     digest_len, 1, digest, digest_len, true);
> +}
> +
> +/**
> + * digest_add - add a new digest
> + * @digest: digest in binary form
> + * @algo: digest algorithm
> + * @type: digest type
> + * @digest_list: digest list the new digest belongs to
> + * @digest_offset: offset of the digest in the buffer of the digest list
> + * @hdr_offset: offset of the header within the digest list the digest refers to
> + *
> + * This function first searches if the digest is already in the hash table for
> + * the given type. The digest is searched by comparing the passed digest and
> + * algorithm with the digest obtained from the first digest list reference
> + * (buffer + digest_offset), or from the digest field of a digest list item,
> + * for a digest list.
> + *
> + * If the digest exists, only a new reference is added (there might be multiple
> + * references to the same digest list).
> + *
> + * If the digest is not found, a new digest item is allocated and a reference to
> + * the passed digest list is added to that item. The digest item is finally
> + * added to the hash table for the given type.
> + *
> + * Proper locking must be provided by the caller.
> + *
> + * Return: a new or the found digest item on success, an error pointer
> + * otherwise.
> + */
> +struct digest_item *digest_add(u8 *digest, enum hash_algo algo,
> +			       enum compact_types type,
> +			       struct digest_list_item *digest_list,
> +			       loff_t digest_offset, loff_t hdr_offset)
> +{
> +	int digest_len = hash_digest_size[algo];
> +	struct digest_item *d;
> +	int ret;
> +
> +	/* Search the digest. */
> +	d = __digest_lookup(digest, algo, type, NULL, NULL);
> +	if (d) {
> +		/*
> +		 * Add a new digest list reference to the existing digest item.
> +		 */
> +		ret = digest_list_ref_add(d, digest_list, digest_offset,
> +					  hdr_offset);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		print_hex_dump_debug("digest add duplicate: ", DUMP_PREFIX_NONE,
> +				     digest_len, 1, digest, digest_len, true);
> +		return d;
> +	}
> +
> +	/* Allocate a new digest item. */
> +	if (!should_fail_diglim())
> +		d = kmem_cache_alloc(digest_item_cache, GFP_KERNEL);
> +	if (!d) {
> +		print_hex_dump_debug("digest allocation failed: ",
> +				     DUMP_PREFIX_NONE, digest_len, 1, digest,
> +				     digest_len, true);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	INIT_LIST_HEAD(&d->refs);
> +
> +	/* Add a new digest list reference to the new digest item. */
> +	ret = digest_list_ref_add(d, digest_list, digest_offset, hdr_offset);
> +	if (ret < 0) {
> +		kmem_cache_free(digest_item_cache, d);
> +		return ERR_PTR(ret);
> +	}
> +
> +	/* Add the new digest item to the hash table for the given type. */
> +	hlist_add_head_rcu(&d->hnext, &htable[type].queue[hash_key(digest)]);
> +	htable[type].len++;
> +
> +	print_hex_dump_debug("digest add: ", DUMP_PREFIX_NONE, digest_len, 1,
> +			     digest, digest_len, true);
> +	return d;
> +}
> +
> +/**
> + * digest_del - delete a digest with one reference, or just a reference
> + * @digest: digest in binary form
> + * @algo: digest algorithm
> + * @type: digest type
> + * @digest_list: digest list the digest belongs to
> + * @digest_offset: offset of the digest in the buffer of the digest list
> + * @hdr_offset: offset of the header within the digest list the digest refers to
> + *
> + * This function is called when a digest list is being removed. The digest is
> + * first searched in the hash table for the given type. If it is found (if not,
> + * it is a bug, because digest lists can be deleted only if they were added
> + * previously), a reference of the passed digest list is deleted from the linked
> + * list of references of the digest item.
> + *
> + * If the last reference was deleted, the digest item is also deleted and
> + * removed from the hash table.
> + *
> + * Proper locking must be provided by the caller.
> + */
> +void digest_del(u8 *digest, enum hash_algo algo, enum compact_types type,
> +		struct digest_list_item *digest_list, loff_t digest_offset,
> +		loff_t hdr_offset)
> +{
> +	struct digest_item *d;
> +	int digest_len = hash_digest_size[algo];
> +
> +	/* Search the digest. */
> +	d = __digest_lookup(digest, algo, type, NULL, NULL);
> +	if (!d) {
> +		print_hex_dump(KERN_ERR, "digest not found: ", DUMP_PREFIX_NONE,
> +			       digest_len, 1, digest, digest_len, true);
> +		return;
> +	}
> +
> +	/* Delete a reference of the passed digest list. */
> +	digest_list_ref_del(d, digest_list, digest_offset, hdr_offset);
> +
> +	print_hex_dump_debug(!list_empty(&d->refs) ?
> +			     "digest del duplicate: " : "digest del: ",
> +			     DUMP_PREFIX_NONE, digest_len, 1, digest,
> +			     digest_len, true);
> +
> +	/* Return if there are still references. */
> +	if (!list_empty(&d->refs))
> +		return;
> +
> +	/*
> +	 * Remove the digest item from the hash table and free it if there are
> +	 * no more references left.
> +	 */
> +	hlist_del_rcu(&d->hnext);
> +	htable[type].len--;
> +	kmem_cache_free(digest_item_cache, d);
> +}
> +
> +/**
> + * digest_list_add - add a new digest list
> + * @digest: digest of the digest list in binary form
> + * @algo: digest algorithm
> + * @size: digest list size
> + * @buf: digest list buffer
> + * @actions: actions (measure/appraise) performed by IMA on the digest list
> + * @label: label to be used to identify the digest list
> + *
> + * This function allocates a new digest list item, which contains the buffer,
> + * size, actions performed by IMA and a label. Each digest list item is
> + * associated to a digest item representing the digest of the digest list.
> + *
> + * This function prevents the same digest list to be added multiple times by
> + * searching its digest in the hash table for the COMPACT_DIGEST_LIST type.
> + *
> + * The passed buffer is copied in a new memory area, to avoid to reference
> + * memory that could be freed by the caller.
> + *
> + * If allocation of a new digest list and the associated buffer was successful,
> + * its digest is added to the hash table for the COMPACT_DIGEST_LIST type.
> + *
> + * Proper locking must be provided by the caller.
> + *
> + * Return: the digest item associated to the digest list item on success, an
> + * error pointer otherwise.
> + */
> +struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
> +				    loff_t size, u8 *buf, u8 actions,
> +				    const char *label)
> +{
> +	struct digest_item *d;
> +	struct digest_list_item *digest_list = NULL;
> +	int digest_len = hash_digest_size[algo];
> +
> +	/* Search the digest of the digest list. */
> +	d = __digest_lookup(digest, algo, COMPACT_DIGEST_LIST, NULL, NULL);
> +	if (d) {
> +		print_hex_dump(KERN_ERR, "digest list already uploaded: ",
> +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> +			       digest_len, true);
> +		return ERR_PTR(-EEXIST);
> +	}
> +
> +	/* Allocate a new digest list. */
> +	if (!should_fail_diglim())
> +		digest_list = kmem_cache_alloc(digest_list_item_cache,
> +					       GFP_KERNEL);
> +	if (!digest_list) {
> +		print_hex_dump(KERN_ERR, "digest list allocation failed: ",
> +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> +			       digest_len, true);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	digest_list->buf = NULL;
> +	digest_list->size = size;
> +
> +	if (!should_fail_diglim())
> +		digest_list->buf = kmemdup(buf, size, GFP_KERNEL);
> +	if (!digest_list->buf) {
> +		print_hex_dump(KERN_ERR, "digest list allocation failed: ",
> +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> +			       digest_len, true);
> +		kmem_cache_free(digest_list_item_cache, digest_list);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	digest_list->actions = actions;
> +	memcpy(digest_list->digest, digest, hash_digest_size[algo]);
> +	digest_list->algo = algo;
> +	digest_list->label = label;
> +
> +	/* Add the digest of the digest list to the hash table. */
> +	d = digest_add(digest, algo, COMPACT_DIGEST_LIST, digest_list, 0, 0);
> +	if (IS_ERR(d)) {
> +		kfree(digest_list->buf);
> +		kmem_cache_free(digest_list_item_cache, digest_list);
> +	}
> +
> +	return d;
> +}
> +
> +/**
> + * digest_list_del - delete an existing digest list
> + * @digest: digest of the digest list in binary form
> + * @algo: digest algorithm
> + * @actions: actions (measure/appraise) performed by IMA on the digest list
> + * @digest_list: digest list to delete
> + *
> + * This function searches the digest of the digest list in the hash table for
> + * the COMPACT_DIGEST_LIST type. If it is found, this function frees the buffer
> + * and the digest list item allocated in digest_list_add().
> + *
> + * This function will be executed only for digest lists that were previously
> + * added.
> + *
> + * Proper locking must be provided by the caller.
> + */
> +void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
> +		     struct digest_list_item *digest_list)
> +{
> +	/* Delete the digest item associated to the digest list. */
> +	digest_del(digest, algo, COMPACT_DIGEST_LIST, digest_list, 0, 0);
> +
> +	/*
> +	 * Free the buffer and the digest list item allocated when the digest
> +	 * list was added.
> +	 */
> +	kfree(digest_list->buf);
> +	kmem_cache_free(digest_list_item_cache, digest_list);
> +}
> +
> +static int __init digest_list_cache_init(void)
> +{
> +	digest_list_item_cache = kmem_cache_create("digest_list_item_cache",
> +						sizeof(struct digest_list_item),
> +						0, SLAB_PANIC, NULL);
> +
> +	digest_list_item_ref_cache = kmem_cache_create(
> +					"digest_list_item_ref_cache",
> +					sizeof(struct digest_list_item_ref), 0,
> +					SLAB_PANIC, NULL);
> +
> +	digest_item_cache = kmem_cache_create("digest_item_cache",
> +					      sizeof(struct digest_item), 0,
> +					      SLAB_PANIC, NULL);
> +
> +	return 0;
> +}
> +
> +late_initcall(digest_list_cache_init)



Thanks,
Mauro

^ permalink raw reply

* RE: [RFC][PATCH v2 04/12] diglim: Methods
From: Roberto Sassu @ 2021-07-28 12:30 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20210728141842.600aaabe@coco.lan>

> From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> Sent: Wednesday, July 28, 2021 2:19 PM
> Em Mon, 26 Jul 2021 18:36:52 +0200
> Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> 
> > Introduce the methods requires to manage the three objects defined.
> >
> > - digest_item methods:
> >   - digest_add()
> >   - digest_del()
> >   - __digest_lookup()
> >   - diglim_digest_get_info()
> >
> > - digest_list_item_ref methods:
> >   - digest_list_ref_add()
> >   - digest_list_ref_del()
> >
> > - digest_list_item methods:
> >   - digest_list_add()
> >   - digest_list_del()
> >
> > More information about these functions can be found in
> > Documentation/security/diglim/implementation.rst.
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >  .../security/diglim/implementation.rst        |   9 +
> >  MAINTAINERS                                   |   2 +
> >  include/linux/diglim.h                        |  28 +
> >  security/integrity/Kconfig                    |   1 +
> >  security/integrity/Makefile                   |   1 +
> >  security/integrity/diglim/Kconfig             |  11 +
> >  security/integrity/diglim/Makefile            |   8 +
> >  security/integrity/diglim/diglim.h            |  20 +-
> >  security/integrity/diglim/methods.c           | 499 ++++++++++++++++++
> >  9 files changed, 578 insertions(+), 1 deletion(-)
> >  create mode 100644 include/linux/diglim.h
> >  create mode 100644 security/integrity/diglim/Kconfig
> >  create mode 100644 security/integrity/diglim/Makefile
> >  create mode 100644 security/integrity/diglim/methods.c
> >
> > diff --git a/Documentation/security/diglim/implementation.rst
> b/Documentation/security/diglim/implementation.rst
> > index 6002049612a1..54af23b2f5f1 100644
> > --- a/Documentation/security/diglim/implementation.rst
> > +++ b/Documentation/security/diglim/implementation.rst
> > @@ -200,3 +200,12 @@ Similarly:
> >  the digest can be obtained by summing the address of the digest list buffer
> >  with ``digest_offset`` (except for the digest lists, where the digest is
> >  stored in the ``digest`` field of the ``digest_list_item`` structure).
> > +
> > +
> > +Methods
> > +-------
> > +
> > +This section introduces the methods requires to manage the three objects
> > +defined.
> > +
> > +.. kernel-doc:: security/integrity/diglim/methods.c
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index f7592d41367d..9e085a36654a 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -5461,8 +5461,10 @@ F:
> 	Documentation/security/diglim/architecture.rst
> >  F:	Documentation/security/diglim/implementation.rst
> >  F:	Documentation/security/diglim/index.rst
> >  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/methods.c
> >
> >  DIOLAN U2C-12 I2C DRIVER
> >  M:	Guenter Roeck <linux@roeck-us.net>
> > diff --git a/include/linux/diglim.h b/include/linux/diglim.h
> > new file mode 100644
> > index 000000000000..d4b4548a288b
> > --- /dev/null
> > +++ b/include/linux/diglim.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> > + *
> > + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> > + *
> > + * DIGLIM functions available for use by kernel subsystems.
> > + */
> > +
> > +#ifndef __DIGLIM_H
> > +#define __DIGLIM_H
> > +
> > +#include <crypto/hash_info.h>
> > +#include <uapi/linux/diglim.h>
> > +
> > +#ifdef CONFIG_DIGLIM
> > +extern int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
> > +				  enum compact_types type, u16 *modifiers,
> > +				  u8 *actions);
> > +#else
> > +static inline int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
> > +					 enum compact_types type,
> > +					 u16 *modifiers, u8 *actions)
> > +{
> > +	return -ENOENT;
> > +}
> > +#endif /*CONFIG_DIGLIM*/
> > +#endif /*__DIGLIM_H*/
> > diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
> > index 71f0177e8716..8f94f4dcc052 100644
> > --- a/security/integrity/Kconfig
> > +++ b/security/integrity/Kconfig
> > @@ -98,5 +98,6 @@ config INTEGRITY_AUDIT
> >
> >  source "security/integrity/ima/Kconfig"
> >  source "security/integrity/evm/Kconfig"
> > +source "security/integrity/diglim/Kconfig"
> >
> >  endif   # if INTEGRITY
> > diff --git a/security/integrity/Makefile b/security/integrity/Makefile
> > index 7ee39d66cf16..d6166550a6b8 100644
> > --- a/security/integrity/Makefile
> > +++ b/security/integrity/Makefile
> > @@ -19,3 +19,4 @@ integrity-$(CONFIG_LOAD_PPC_KEYS) +=
> platform_certs/efi_parser.o \
> >                                       platform_certs/keyring_handler.o
> >  obj-$(CONFIG_IMA)			+= ima/
> >  obj-$(CONFIG_EVM)			+= evm/
> > +obj-$(CONFIG_DIGLIM)			+= diglim/
> > diff --git a/security/integrity/diglim/Kconfig
> b/security/integrity/diglim/Kconfig
> > new file mode 100644
> > index 000000000000..436a76a14337
> > --- /dev/null
> > +++ b/security/integrity/diglim/Kconfig
> > @@ -0,0 +1,11 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +# Digest Lists Integrity Module (DIGLIM)
> > +#
> > +config DIGLIM
> > +	bool "Digest Lists Integrity Module (DIGLIM)"
> > +	select SECURITYFS
> > +	select CRYPTO
> > +	select CRYPTO_HASH_INFO
> > +	help
> > +	  DIGLIM provides reference values for file content and metadata,
> > +	  that can be used for measurement and appraisal with IMA.
> > diff --git a/security/integrity/diglim/Makefile
> b/security/integrity/diglim/Makefile
> > new file mode 100644
> > index 000000000000..b761ed8cfb3e
> > --- /dev/null
> > +++ b/security/integrity/diglim/Makefile
> > @@ -0,0 +1,8 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +#
> > +# Makefile for building Digest Lists Integrity Module (DIGLIM).
> > +#
> > +
> > +obj-$(CONFIG_DIGLIM) += diglim.o
> > +
> > +diglim-y := methods.o
> > diff --git a/security/integrity/diglim/diglim.h
> b/security/integrity/diglim/diglim.h
> > index 578253d7e1d1..25851e7d4906 100644
> > --- a/security/integrity/diglim/diglim.h
> > +++ b/security/integrity/diglim/diglim.h
> > @@ -20,7 +20,7 @@
> >  #include <linux/audit.h>
> >  #include <crypto/hash_info.h>
> >  #include <linux/hash_info.h>
> > -#include <uapi/linux/diglim.h>
> > +#include <linux/diglim.h>
> >
> >  #define MAX_DIGEST_SIZE 64
> >  #define HASH_BITS 10
> > @@ -81,6 +81,8 @@ static inline unsigned int hash_key(u8 *digest)
> >  	return (digest[0] | digest[1] << 8) % DIGLIM_HTABLE_SIZE;
> >  }
> >
> > +extern struct h_table htable[COMPACT__LAST];
> > +
> 
> it sounds somewhat risky to use just "htable" for a var declared
> as external.

Ok, adding diglim_ as prefix should be enough.

> >  static inline struct compact_list_hdr *get_hdr(
> >  					struct digest_list_item *digest_list,
> >  					loff_t hdr_offset)
> > @@ -131,4 +133,20 @@ static inline u8 *get_digest_ref(struct
> digest_list_item_ref *ref)
> >
> >  	return ref->digest_list->buf + ref->digest_offset;
> >  }
> > +
> > +struct digest_item *__digest_lookup(u8 *digest, enum hash_algo algo,
> > +				    enum compact_types type, u16 *modifiers,
> > +				    u8 *actions);
> > +struct digest_item *digest_add(u8 *digest, enum hash_algo algo,
> > +			       enum compact_types type,
> > +			       struct digest_list_item *digest_list,
> > +			       loff_t digest_offset, loff_t hdr_offset);
> > +void digest_del(u8 *digest, enum hash_algo algo, enum compact_types
> type,
> > +		struct digest_list_item *digest_list, loff_t digest_offset,
> > +		loff_t hdr_offset);
> > +struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
> > +				    loff_t size, u8 *buf, u8 actions,
> > +				    const char *label);
> > +void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
> > +		     struct digest_list_item *digest_list);
> >  #endif /*__DIGLIM_INTERNAL_H*/
> > diff --git a/security/integrity/diglim/methods.c
> b/security/integrity/diglim/methods.c
> > new file mode 100644
> > index 000000000000..7ed61399cfe8
> > --- /dev/null
> > +++ b/security/integrity/diglim/methods.c
> > @@ -0,0 +1,499 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> > + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> > + *
> > + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> > + *
> > + * Functions to manage digest lists.
> > + */
> > +
> > +#include <linux/vmalloc.h>
> > +#include <linux/module.h>
> > +#include <linux/fault-inject.h>
> > +
> > +#include "diglim.h"
> > +#include "../integrity.h"
> > +
> > +/* Define a cache for each object type. */
> > +static struct kmem_cache *digest_list_item_cache __read_mostly;
> > +static struct kmem_cache *digest_list_item_ref_cache __read_mostly;
> > +static struct kmem_cache *digest_item_cache __read_mostly;
> > +
> > +/* Define a hash table for each digest type. */
> > +struct h_table htable[COMPACT__LAST] = {{
> > +	.queue[0 ... DIGLIM_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
> > +}};
> 
> 
> > +
> > +#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
> > +static DECLARE_FAULT_ATTR(fail_diglim);
> > +
> > +static int __init fail_diglim_debugfs(void)
> > +{
> > +	struct dentry *dir = fault_create_debugfs_attr("fail_diglim", NULL,
> > +						       &fail_diglim);
> > +
> > +	return PTR_ERR_OR_ZERO(dir);
> > +}
> > +
> > +static inline bool should_fail_diglim(void)
> > +{
> > +	return should_fail(&fail_diglim, 1);
> > +}
> > +
> > +late_initcall(fail_diglim_debugfs);
> > +#else
> > +static inline bool should_fail_diglim(void)
> > +{
> > +	return false;
> > +}
> > +#endif
> 
> 
> I guess this is a matter of personal preference, but, IMO, it is a lot better
> to place the debugfs stuff on a separate source file, avoiding ugly #ifdefs
> in the middle of the code.
> 
> Ok, the current code is too small to deserve a separate file, but
> if later patches would add more stuff, then I would opt to have this on
> a separate file.

Ok.

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> > +
> > +/**
> > + * __digest_lookup - lookup digest and return associated modifiers and
> actions
> > + * @digest: digest to lookup
> > + * @algo: digest algorithm
> > + * @type: type of digest to lookup (e.g. file, metadata)
> > + * @modifiers: modifiers (attributes) associated to the found digest
> > + * @actions: actions performed by IMA on the digest list containing the
> digest
> > + *
> > + * This function searches the given digest in the hash table depending on
> the
> > + * passed type and sets the modifiers and actions associated to the digest, if
> > + * the pointers are not NULL.
> > + *
> > + * This function is not intended for external use, as the returned digest item
> > + * could be freed at any time after it has been returned.
> > + * diglim_digest_get_info() should be used instead by external callers, as it
> > + * only returns the modifiers and the actions associated to the digest at the
> > + * time the digest is searched.
> > + *
> > + * RCU protects both the hash table and the linked list of references to the
> > + * digest lists containing the found digest.
> > + *
> > + * Return: a digest_item structure if the digest is found, NULL otherwise.
> > + */
> > +struct digest_item *__digest_lookup(u8 *digest, enum hash_algo algo,
> > +				    enum compact_types type, u16 *modifiers,
> > +				    u8 *actions)
> > +{
> > +	struct digest_item *d = NULL;
> > +	struct digest_list_item_ref *ref;
> > +	int digest_len = hash_digest_size[algo];
> > +	unsigned int key = hash_key(digest);
> > +	bool found = false;
> > +
> > +	rcu_read_lock();
> > +	hlist_for_each_entry_rcu(d, &htable[type].queue[key], hnext) {
> > +		list_for_each_entry_rcu(ref, &d->refs, list) {
> > +			if (get_algo_ref(ref) != algo ||
> > +			    memcmp(get_digest_ref(ref), digest, digest_len))
> > +				break;
> > +
> > +			found = true;
> > +
> > +			/* There is no need to scan all digest list refs. */
> > +			if (!modifiers || !actions)
> > +				break;
> > +
> > +			/*
> > +			 * The resulting modifiers and actions are the OR of
> the
> > +			 * modifiers and actions for each digest list.
> > +			 */
> > +			*modifiers |= get_hdr_ref(ref)->modifiers;
> > +			*actions |= ref->digest_list->actions;
> > +		}
> > +
> > +		if (found)
> > +			break;
> > +	}
> > +
> > +	rcu_read_unlock();
> > +	return d;
> > +}
> > +
> > +/**
> > + * diglim_digest_get_info - lookup digest and return modifiers and actions
> > + * @digest: digest to lookup
> > + * @algo: digest algorithm
> > + * @type: type of digest to lookup (e.g. file, metadata)
> > + * @modifiers: modifiers (attributes) associated to the found digest
> > + * @actions: actions performed by IMA on the digest lists containing the
> digest
> > + *
> > + * This function searches the given digest in the hash table depending on
> the
> > + * passed type and sets the modifiers and actions associated to the digest, if
> > + * the pointers are not NULL.
> > + *
> > + * This function is safe for external use, as it does not return pointers of
> > + * objects that can be freed without the caller notices it.
> > + *
> > + * Return: 0 if the digest is found, -ENOENT otherwise.
> > + */
> > +int diglim_digest_get_info(u8 *digest, enum hash_algo algo,
> > +			   enum compact_types type, u16 *modifiers, u8
> *actions)
> > +{
> > +	struct digest_item *d;
> > +
> > +	d = __digest_lookup(digest, algo, type, modifiers, actions);
> > +	if (!d)
> > +		return -ENOENT;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * digest_list_ref_add - add reference to a digest list
> > + * @d: digest a new reference is added to
> > + * @digest_list: digest list whose reference is being added
> > + * @digest_offset: offset of the digest in the buffer of the digest list
> > + * @hdr_offset: offset of the header within the digest list the digest refers
> to
> > + *
> > + * This function adds a new reference to an existing digest list for a given
> > + * digest. The reference is described by the digest_list_item_ref structure
> and
> > + * consists of a pointer of the digest list, the offset of the digest to the
> > + * beginning of the digest list buffer and the offset of the header the digest
> > + * refers to (each digest list might be composed of several digest blocks,
> each
> > + * prefixed by a header describing the attributes of those digests).
> > + *
> > + * Return: 0 if a new digest list reference was successfully added, a negative
> > + * value otherwise.
> > + */
> > +static int digest_list_ref_add(struct digest_item *d,
> > +			       struct digest_list_item *digest_list,
> > +			       loff_t digest_offset, loff_t hdr_offset)
> > +{
> > +	struct digest_list_item_ref *new_ref = NULL;
> > +	u8 *digest = get_digest(digest_list, digest_offset, hdr_offset);
> > +	enum hash_algo algo = get_algo(digest_list, digest_offset, hdr_offset);
> > +	int digest_len = hash_digest_size[algo];
> > +
> > +	/* Allocate a new reference. */
> > +	if (!should_fail_diglim())
> > +		new_ref = kmem_cache_alloc(digest_list_item_ref_cache,
> > +					   GFP_KERNEL);
> > +	if (!new_ref) {
> > +		print_hex_dump(KERN_ERR, "digest list ref allocation failed: ",
> > +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +			       digest_len, true);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	/* Set the new reference. */
> > +	new_ref->digest_list = digest_list;
> > +	/* Converting loff_t -> u32 is fine as long as the digest list < 4G. */
> > +	new_ref->digest_offset = digest_offset;
> > +	new_ref->hdr_offset = hdr_offset;
> > +
> > +	list_add_tail_rcu(&new_ref->list, &d->refs);
> > +
> > +	print_hex_dump_debug("add digest list ref: ", DUMP_PREFIX_NONE,
> > +			     digest_len, 1, digest, digest_len, true);
> > +	return 0;
> > +}
> > +
> > +/**
> > + * digest_list_ref_del - del reference to a digest list
> > + * @d: digest a reference is deleted from
> > + * @digest_list: digest list whose reference is being deleted
> > + * @digest_offset: offset of the digest in the buffer of the digest list
> > + * @hdr_offset: offset of the header within the digest list the digest refers
> to
> > + *
> > + * This function searches the reference to an already loaded digest list in
> the
> > + * linked list of references stored for each digest item. If the reference is
> > + * found (if not, it is a bug), the function deletes it from the linked list.
> > + */
> > +static void digest_list_ref_del(struct digest_item *d,
> > +				struct digest_list_item *digest_list,
> > +				loff_t digest_offset, loff_t hdr_offset)
> > +{
> > +	struct digest_list_item_ref *ref;
> > +	u8 *digest = get_digest(digest_list, digest_offset, hdr_offset);
> > +	enum hash_algo algo = get_algo(digest_list, digest_offset, hdr_offset);
> > +	int digest_len = hash_digest_size[algo];
> > +
> > +	/* Search for a digest list reference. */
> > +	list_for_each_entry(ref, &d->refs, list)
> > +		if (ref->digest_list == digest_list)
> > +			break;
> > +
> > +	if (!ref) {
> > +		print_hex_dump(KERN_ERR, "digest list ref not found: ",
> > +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +			       digest_len, true);
> > +		return;
> > +	}
> > +
> > +	list_del_rcu(&ref->list);
> > +	kmem_cache_free(digest_list_item_ref_cache, ref);
> > +
> > +	print_hex_dump_debug("del digest list ref: ", DUMP_PREFIX_NONE,
> > +			     digest_len, 1, digest, digest_len, true);
> > +}
> > +
> > +/**
> > + * digest_add - add a new digest
> > + * @digest: digest in binary form
> > + * @algo: digest algorithm
> > + * @type: digest type
> > + * @digest_list: digest list the new digest belongs to
> > + * @digest_offset: offset of the digest in the buffer of the digest list
> > + * @hdr_offset: offset of the header within the digest list the digest refers
> to
> > + *
> > + * This function first searches if the digest is already in the hash table for
> > + * the given type. The digest is searched by comparing the passed digest
> and
> > + * algorithm with the digest obtained from the first digest list reference
> > + * (buffer + digest_offset), or from the digest field of a digest list item,
> > + * for a digest list.
> > + *
> > + * If the digest exists, only a new reference is added (there might be
> multiple
> > + * references to the same digest list).
> > + *
> > + * If the digest is not found, a new digest item is allocated and a reference
> to
> > + * the passed digest list is added to that item. The digest item is finally
> > + * added to the hash table for the given type.
> > + *
> > + * Proper locking must be provided by the caller.
> > + *
> > + * Return: a new or the found digest item on success, an error pointer
> > + * otherwise.
> > + */
> > +struct digest_item *digest_add(u8 *digest, enum hash_algo algo,
> > +			       enum compact_types type,
> > +			       struct digest_list_item *digest_list,
> > +			       loff_t digest_offset, loff_t hdr_offset)
> > +{
> > +	int digest_len = hash_digest_size[algo];
> > +	struct digest_item *d;
> > +	int ret;
> > +
> > +	/* Search the digest. */
> > +	d = __digest_lookup(digest, algo, type, NULL, NULL);
> > +	if (d) {
> > +		/*
> > +		 * Add a new digest list reference to the existing digest item.
> > +		 */
> > +		ret = digest_list_ref_add(d, digest_list, digest_offset,
> > +					  hdr_offset);
> > +		if (ret < 0)
> > +			return ERR_PTR(ret);
> > +
> > +		print_hex_dump_debug("digest add duplicate: ",
> DUMP_PREFIX_NONE,
> > +				     digest_len, 1, digest, digest_len, true);
> > +		return d;
> > +	}
> > +
> > +	/* Allocate a new digest item. */
> > +	if (!should_fail_diglim())
> > +		d = kmem_cache_alloc(digest_item_cache, GFP_KERNEL);
> > +	if (!d) {
> > +		print_hex_dump_debug("digest allocation failed: ",
> > +				     DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +				     digest_len, true);
> > +		return ERR_PTR(-ENOMEM);
> > +	}
> > +
> > +	INIT_LIST_HEAD(&d->refs);
> > +
> > +	/* Add a new digest list reference to the new digest item. */
> > +	ret = digest_list_ref_add(d, digest_list, digest_offset, hdr_offset);
> > +	if (ret < 0) {
> > +		kmem_cache_free(digest_item_cache, d);
> > +		return ERR_PTR(ret);
> > +	}
> > +
> > +	/* Add the new digest item to the hash table for the given type. */
> > +	hlist_add_head_rcu(&d->hnext,
> &htable[type].queue[hash_key(digest)]);
> > +	htable[type].len++;
> > +
> > +	print_hex_dump_debug("digest add: ", DUMP_PREFIX_NONE,
> digest_len, 1,
> > +			     digest, digest_len, true);
> > +	return d;
> > +}
> > +
> > +/**
> > + * digest_del - delete a digest with one reference, or just a reference
> > + * @digest: digest in binary form
> > + * @algo: digest algorithm
> > + * @type: digest type
> > + * @digest_list: digest list the digest belongs to
> > + * @digest_offset: offset of the digest in the buffer of the digest list
> > + * @hdr_offset: offset of the header within the digest list the digest refers
> to
> > + *
> > + * This function is called when a digest list is being removed. The digest is
> > + * first searched in the hash table for the given type. If it is found (if not,
> > + * it is a bug, because digest lists can be deleted only if they were added
> > + * previously), a reference of the passed digest list is deleted from the
> linked
> > + * list of references of the digest item.
> > + *
> > + * If the last reference was deleted, the digest item is also deleted and
> > + * removed from the hash table.
> > + *
> > + * Proper locking must be provided by the caller.
> > + */
> > +void digest_del(u8 *digest, enum hash_algo algo, enum compact_types
> type,
> > +		struct digest_list_item *digest_list, loff_t digest_offset,
> > +		loff_t hdr_offset)
> > +{
> > +	struct digest_item *d;
> > +	int digest_len = hash_digest_size[algo];
> > +
> > +	/* Search the digest. */
> > +	d = __digest_lookup(digest, algo, type, NULL, NULL);
> > +	if (!d) {
> > +		print_hex_dump(KERN_ERR, "digest not found: ",
> DUMP_PREFIX_NONE,
> > +			       digest_len, 1, digest, digest_len, true);
> > +		return;
> > +	}
> > +
> > +	/* Delete a reference of the passed digest list. */
> > +	digest_list_ref_del(d, digest_list, digest_offset, hdr_offset);
> > +
> > +	print_hex_dump_debug(!list_empty(&d->refs) ?
> > +			     "digest del duplicate: " : "digest del: ",
> > +			     DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +			     digest_len, true);
> > +
> > +	/* Return if there are still references. */
> > +	if (!list_empty(&d->refs))
> > +		return;
> > +
> > +	/*
> > +	 * Remove the digest item from the hash table and free it if there are
> > +	 * no more references left.
> > +	 */
> > +	hlist_del_rcu(&d->hnext);
> > +	htable[type].len--;
> > +	kmem_cache_free(digest_item_cache, d);
> > +}
> > +
> > +/**
> > + * digest_list_add - add a new digest list
> > + * @digest: digest of the digest list in binary form
> > + * @algo: digest algorithm
> > + * @size: digest list size
> > + * @buf: digest list buffer
> > + * @actions: actions (measure/appraise) performed by IMA on the digest
> list
> > + * @label: label to be used to identify the digest list
> > + *
> > + * This function allocates a new digest list item, which contains the buffer,
> > + * size, actions performed by IMA and a label. Each digest list item is
> > + * associated to a digest item representing the digest of the digest list.
> > + *
> > + * This function prevents the same digest list to be added multiple times by
> > + * searching its digest in the hash table for the COMPACT_DIGEST_LIST
> type.
> > + *
> > + * The passed buffer is copied in a new memory area, to avoid to reference
> > + * memory that could be freed by the caller.
> > + *
> > + * If allocation of a new digest list and the associated buffer was successful,
> > + * its digest is added to the hash table for the COMPACT_DIGEST_LIST type.
> > + *
> > + * Proper locking must be provided by the caller.
> > + *
> > + * Return: the digest item associated to the digest list item on success, an
> > + * error pointer otherwise.
> > + */
> > +struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
> > +				    loff_t size, u8 *buf, u8 actions,
> > +				    const char *label)
> > +{
> > +	struct digest_item *d;
> > +	struct digest_list_item *digest_list = NULL;
> > +	int digest_len = hash_digest_size[algo];
> > +
> > +	/* Search the digest of the digest list. */
> > +	d = __digest_lookup(digest, algo, COMPACT_DIGEST_LIST, NULL,
> NULL);
> > +	if (d) {
> > +		print_hex_dump(KERN_ERR, "digest list already uploaded: ",
> > +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +			       digest_len, true);
> > +		return ERR_PTR(-EEXIST);
> > +	}
> > +
> > +	/* Allocate a new digest list. */
> > +	if (!should_fail_diglim())
> > +		digest_list = kmem_cache_alloc(digest_list_item_cache,
> > +					       GFP_KERNEL);
> > +	if (!digest_list) {
> > +		print_hex_dump(KERN_ERR, "digest list allocation failed: ",
> > +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +			       digest_len, true);
> > +		return ERR_PTR(-ENOMEM);
> > +	}
> > +
> > +	digest_list->buf = NULL;
> > +	digest_list->size = size;
> > +
> > +	if (!should_fail_diglim())
> > +		digest_list->buf = kmemdup(buf, size, GFP_KERNEL);
> > +	if (!digest_list->buf) {
> > +		print_hex_dump(KERN_ERR, "digest list allocation failed: ",
> > +			       DUMP_PREFIX_NONE, digest_len, 1, digest,
> > +			       digest_len, true);
> > +		kmem_cache_free(digest_list_item_cache, digest_list);
> > +		return ERR_PTR(-ENOMEM);
> > +	}
> > +
> > +	digest_list->actions = actions;
> > +	memcpy(digest_list->digest, digest, hash_digest_size[algo]);
> > +	digest_list->algo = algo;
> > +	digest_list->label = label;
> > +
> > +	/* Add the digest of the digest list to the hash table. */
> > +	d = digest_add(digest, algo, COMPACT_DIGEST_LIST, digest_list, 0, 0);
> > +	if (IS_ERR(d)) {
> > +		kfree(digest_list->buf);
> > +		kmem_cache_free(digest_list_item_cache, digest_list);
> > +	}
> > +
> > +	return d;
> > +}
> > +
> > +/**
> > + * digest_list_del - delete an existing digest list
> > + * @digest: digest of the digest list in binary form
> > + * @algo: digest algorithm
> > + * @actions: actions (measure/appraise) performed by IMA on the digest
> list
> > + * @digest_list: digest list to delete
> > + *
> > + * This function searches the digest of the digest list in the hash table for
> > + * the COMPACT_DIGEST_LIST type. If it is found, this function frees the
> buffer
> > + * and the digest list item allocated in digest_list_add().
> > + *
> > + * This function will be executed only for digest lists that were previously
> > + * added.
> > + *
> > + * Proper locking must be provided by the caller.
> > + */
> > +void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
> > +		     struct digest_list_item *digest_list)
> > +{
> > +	/* Delete the digest item associated to the digest list. */
> > +	digest_del(digest, algo, COMPACT_DIGEST_LIST, digest_list, 0, 0);
> > +
> > +	/*
> > +	 * Free the buffer and the digest list item allocated when the digest
> > +	 * list was added.
> > +	 */
> > +	kfree(digest_list->buf);
> > +	kmem_cache_free(digest_list_item_cache, digest_list);
> > +}
> > +
> > +static int __init digest_list_cache_init(void)
> > +{
> > +	digest_list_item_cache =
> kmem_cache_create("digest_list_item_cache",
> > +						sizeof(struct digest_list_item),
> > +						0, SLAB_PANIC, NULL);
> > +
> > +	digest_list_item_ref_cache = kmem_cache_create(
> > +					"digest_list_item_ref_cache",
> > +					sizeof(struct digest_list_item_ref), 0,
> > +					SLAB_PANIC, NULL);
> > +
> > +	digest_item_cache = kmem_cache_create("digest_item_cache",
> > +					      sizeof(struct digest_item), 0,
> > +					      SLAB_PANIC, NULL);
> > +
> > +	return 0;
> > +}
> > +
> > +late_initcall(digest_list_cache_init)
> 
> 
> 
> Thanks,
> Mauro

^ permalink raw reply

* Re: [RFC][PATCH v2 05/12] diglim: Parser
From: Mauro Carvalho Chehab @ 2021-07-28 12:35 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-6-roberto.sassu@huawei.com>

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

> Introduce the necessary functions to parse a digest list and to execute the
> requested operation.
> 
> The main function is digest_list_parse(), which coordinates the various
> steps required to add or delete a digest list, and has the logic to roll
> back when one of the steps fails.
> 
> A more detailed description about the steps can be found in
> Documentation/security/diglim/implementation.rst

LGTM.

> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  .../security/diglim/implementation.rst        |  35 +++
>  MAINTAINERS                                   |   1 +
>  security/integrity/diglim/Makefile            |   2 +-
>  security/integrity/diglim/diglim.h            |   3 +
>  security/integrity/diglim/parser.c            | 274 ++++++++++++++++++
>  5 files changed, 314 insertions(+), 1 deletion(-)
>  create mode 100644 security/integrity/diglim/parser.c
> 
> diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst
> index 54af23b2f5f1..9d679567a037 100644
> --- a/Documentation/security/diglim/implementation.rst
> +++ b/Documentation/security/diglim/implementation.rst
> @@ -209,3 +209,38 @@ This section introduces the methods requires to manage the three objects
>  defined.
>  
>  .. kernel-doc:: security/integrity/diglim/methods.c
> +
> +
> +Parser
> +------
> +
> +This section introduces the necessary functions to parse a digest list and
> +to execute the requested operation.
> +
> +.. kernel-doc:: security/integrity/diglim/parser.c
> +
> +The main function is digest_list_parse(), which coordinates the various
> +steps required to add or delete a digest list, and has the logic to roll
> +back when one of the steps fails.
> +
> +#. Calls digest_list_validate() to validate the passed buffer containing
> +   the digest list to ensure that the format is correct.
> +
> +#. Calls get_digest_list() to create a new digest_list_item for the add
> +   operation, or to retrieve the existing one for the delete operation.
> +   get_digest_list() refuses to add digest lists that were previously
> +   added and to delete digest lists that weren't previously added. Also,
> +   get_digest_list() refuses to delete digest lists if there are actions
> +   done at addition time that are not currently being performed (it would
> +   guarantee that also deletion is notified to remote verifiers).
> +
> +#. Calls _digest_list_parse() which takes the created/retrieved
> +   struct digest_list_item and adds or delete the digests included in the
> +   digest list.
> +
> +#. If an error occurred, performs a rollback to the previous state, by
> +   calling _digest_list_parse() with the opposite operation and the buffer
> +   size at the time the error occurred.
> +
> +#. digest_list_parse() deletes the struct digest_list_item on unsuccessful
> +   add or successful delete.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9e085a36654a..77c3613c600a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5465,6 +5465,7 @@ F:	include/linux/diglim.h
>  F:	include/uapi/linux/diglim.h
>  F:	security/integrity/diglim/diglim.h
>  F:	security/integrity/diglim/methods.c
> +F:	security/integrity/diglim/parser.c
>  
>  DIOLAN U2C-12 I2C DRIVER
>  M:	Guenter Roeck <linux@roeck-us.net>
> diff --git a/security/integrity/diglim/Makefile b/security/integrity/diglim/Makefile
> index b761ed8cfb3e..34e4e154fff3 100644
> --- a/security/integrity/diglim/Makefile
> +++ b/security/integrity/diglim/Makefile
> @@ -5,4 +5,4 @@
>  
>  obj-$(CONFIG_DIGLIM) += diglim.o
>  
> -diglim-y := methods.o
> +diglim-y := methods.o parser.o
> diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h
> index 25851e7d4906..3adc218a0325 100644
> --- a/security/integrity/diglim/diglim.h
> +++ b/security/integrity/diglim/diglim.h
> @@ -149,4 +149,7 @@ struct digest_item *digest_list_add(u8 *digest, enum hash_algo algo,
>  				    const char *label);
>  void digest_list_del(u8 *digest, enum hash_algo algo, u8 actions,
>  		     struct digest_list_item *digest_list);
> +
> +int digest_list_parse(loff_t size, void *buf, enum ops op, u8 actions,
> +		      u8 *digest, enum hash_algo algo, const char *label);
>  #endif /*__DIGLIM_INTERNAL_H*/
> diff --git a/security/integrity/diglim/parser.c b/security/integrity/diglim/parser.c
> new file mode 100644
> index 000000000000..89a48945b460
> --- /dev/null
> +++ b/security/integrity/diglim/parser.c
> @@ -0,0 +1,274 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
> + *
> + * Author: Roberto Sassu <roberto.sassu@huawei.com>
> + *
> + * Functions to parse digest lists.
> + */
> +
> +#include <linux/vmalloc.h>
> +#include <linux/module.h>
> +
> +#include "diglim.h"
> +#include "../integrity.h"
> +
> +/**
> + * digest_list_validate - validate format of digest list
> + * @size: buffer size
> + * @buf: buffer containing the digest list
> + *
> + * This function validates the format of the passed digest list.
> + *
> + * Return: 0 if the digest list was successfully validated, -EINVAL otherwise.
> + */
> +static int digest_list_validate(loff_t size, void *buf)
> +{
> +	void *bufp = buf, *bufendp = buf + size;
> +	struct compact_list_hdr *hdr;
> +	size_t digest_len;
> +
> +	while (bufp < bufendp) {
> +		if (bufp + sizeof(*hdr) > bufendp) {
> +			pr_err("invalid data\n");
> +			return -EINVAL;
> +		}
> +
> +		hdr = bufp;
> +
> +		if (hdr->version != 1) {
> +			pr_err("unsupported version\n");
> +			return -EINVAL;
> +		}
> +
> +		if (hdr->_reserved != 0) {
> +			pr_err("unexpected value for _reserved field\n");
> +			return -EINVAL;
> +		}
> +
> +		hdr->type = le16_to_cpu(hdr->type);
> +		hdr->modifiers = le16_to_cpu(hdr->modifiers);
> +		hdr->algo = le16_to_cpu(hdr->algo);
> +		hdr->count = le32_to_cpu(hdr->count);
> +		hdr->datalen = le32_to_cpu(hdr->datalen);
> +
> +		if (hdr->algo >= HASH_ALGO__LAST) {
> +			pr_err("invalid hash algorithm\n");
> +			return -EINVAL;
> +		}
> +
> +		digest_len = hash_digest_size[hdr->algo];
> +
> +		if (hdr->type >= COMPACT__LAST ||
> +		    hdr->type == COMPACT_DIGEST_LIST) {
> +			pr_err("invalid type %d\n", hdr->type);
> +			return -EINVAL;
> +		}
> +
> +		bufp += sizeof(*hdr);
> +
> +		if (hdr->datalen != hdr->count * digest_len ||
> +		    bufp + hdr->datalen > bufendp) {
> +			pr_err("invalid data\n");
> +			return -EINVAL;
> +		}
> +
> +		bufp += hdr->count * digest_len;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * _digest_list_parse - parse digest list and add/delete digests
> + * @size: buffer size
> + * @buf: buffer containing the digest list
> + * @op: operation to be performed
> + * @digest_list: digest list digests being added/deleted belong to
> + *
> + * This function parses the digest list and adds or delete the digests in the
> + * found digest blocks.
> + *
> + * Return: the buffer size if all digests were successfully added or deleted,
> + * the size of the already parsed buffer on error.
> + */
> +static int _digest_list_parse(loff_t size, void *buf, enum ops op,
> +			      struct digest_list_item *digest_list)
> +{
> +	void *bufp = buf, *bufendp = buf + size;
> +	struct compact_list_hdr *hdr;
> +	struct digest_item *d = ERR_PTR(-EINVAL);
> +	size_t digest_len;
> +	int i;
> +
> +	while (bufp < bufendp) {
> +		if (bufp + sizeof(*hdr) > bufendp)
> +			break;
> +
> +		hdr = bufp;
> +		bufp += sizeof(*hdr);
> +
> +		digest_len = hash_digest_size[hdr->algo];
> +
> +		for (i = 0; i < hdr->count && bufp + digest_len <= bufendp;
> +		     i++, bufp += digest_len) {
> +			switch (op) {
> +			case DIGEST_LIST_ADD:
> +				d = digest_add(bufp, hdr->algo, hdr->type,
> +					       digest_list, bufp - buf,
> +					       (void *)hdr - buf);
> +				if (IS_ERR(d)) {
> +					pr_err(
> +					    "failed to add a digest from %s\n",
> +					    digest_list->label);
> +					goto out;
> +				}
> +
> +				break;
> +			case DIGEST_LIST_DEL:
> +				digest_del(bufp, hdr->algo, hdr->type,
> +					   digest_list, bufp - buf,
> +					   (void *)hdr - buf);
> +				break;
> +			default:
> +				break;
> +			}
> +		}
> +	}
> +out:
> +	return bufp - buf;
> +}
> +
> +/**
> + * get_digest_list - get the digest list extracted digests will be associated to
> + * @size: buffer size
> + * @buf: buffer containing the digest list
> + * @op: digest list operation
> + * @actions: actions performed on the digest list being processed
> + * @digest: digest of the digest list
> + * @algo: digest algorithm
> + * @label: label to identify the digest list (e.g. file name)
> + *
> + * This function retrieves the digest list item for the passed digest and
> + * algorithm. If it is not found at addition time, this function creates a new
> + * one.
> + *
> + * This function prevents the imbalance of digests (references left after
> + * delete) by ensuring that only digest lists that were previously added can be
> + * deleted.
> + *
> + * This function also ensures that the actions done at the time of addition are
> + * also performed at the time of deletion (it would guarantee that also deletion
> + * is notified to remote verifiers).
> + *
> + * Return: the retrieved/created digest list item on success, an error pointer
> + * otherwise.
> + */
> +static struct digest_list_item *get_digest_list(loff_t size, void *buf,
> +						enum ops op, u8 actions,
> +						u8 *digest, enum hash_algo algo,
> +						const char *label)
> +{
> +	struct digest_item *d;
> +	struct digest_list_item *digest_list;
> +	int digest_len = hash_digest_size[algo];
> +
> +	switch (op) {
> +	case DIGEST_LIST_ADD:
> +		/* Add digest list to be associated to each digest. */
> +		d = digest_list_add(digest, algo, size, buf, actions, label);
> +		if (IS_ERR(d))
> +			return (void *)d;
> +
> +		digest_list = list_first_entry(&d->refs,
> +				struct digest_list_item_ref, list)->digest_list;
> +		break;
> +	case DIGEST_LIST_DEL:
> +		/* Lookup digest list to delete the references. */
> +		d = __digest_lookup(digest, algo, COMPACT_DIGEST_LIST, NULL,
> +				    NULL);
> +		if (!d) {
> +			print_hex_dump(KERN_ERR,
> +				       "digest list digest not found: ",
> +				       DUMP_PREFIX_NONE, digest_len, 1, digest,
> +				       digest_len, true);
> +			return ERR_PTR(-ENOENT);
> +		}
> +
> +		digest_list = list_first_entry(&d->refs,
> +				struct digest_list_item_ref, list)->digest_list;
> +
> +		/*
> +		 * Reject deletion if there are actions done at addition time
> +		 * that are currently not being performed.
> +		 */
> +		if ((digest_list->actions & actions) != digest_list->actions) {
> +			pr_err("missing actions, add: %d, del: %d\n",
> +			       digest_list->actions, actions);
> +			return ERR_PTR(-EPERM);
> +		}
> +
> +		break;
> +	default:
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return digest_list;
> +}
> +
> +/**
> + * digest_list_parse - parse a digest list
> + * @size: buffer size
> + * @buf: buffer containing the digest list
> + * @op: digest list operation
> + * @actions: actions performed on the digest list being processed
> + * @digest: digest of the digest list
> + * @algo: digest algorithm
> + * @label: label to identify the digest list (e.g. file name)
> + *
> + * This function parses the passed digest list and executed the requested
> + * operation. If the operation cannot be successfully executed, this function
> + * performs a rollback to the previous state.
> + *
> + * Return: the buffer size on success, a negative value otherwise.
> + */
> +int digest_list_parse(loff_t size, void *buf, enum ops op, u8 actions,
> +		      u8 *digest, enum hash_algo algo, const char *label)
> +{
> +	struct digest_list_item *digest_list;
> +	enum ops rollback_op = (op == DIGEST_LIST_ADD) ?
> +			       DIGEST_LIST_DEL : DIGEST_LIST_ADD;
> +	int ret, rollback_size;
> +
> +	ret = digest_list_validate(size, buf);
> +	if (ret < 0)
> +		return ret;
> +
> +	digest_list = get_digest_list(size, buf, op, actions, digest, algo,
> +				      label);
> +	if (IS_ERR(digest_list))
> +		return PTR_ERR(digest_list);
> +
> +	ret = _digest_list_parse(size, buf, op, digest_list);
> +	if (ret < 0)
> +		goto out;
> +
> +	if (ret != size) {
> +		rollback_size = ret;
> +
> +		ret = _digest_list_parse(rollback_size, buf, rollback_op,
> +					 digest_list);
> +		if (ret != rollback_size)
> +			pr_err("rollback failed\n");
> +
> +		ret = -EINVAL;
> +	}
> +out:
> +	/* Delete digest list on unsuccessful add or successful delete. */
> +	if ((op == DIGEST_LIST_ADD && ret < 0) ||
> +	    (op == DIGEST_LIST_DEL && ret == size))
> +		digest_list_del(digest, algo, actions, digest_list);
> +
> +	return ret;
> +}

^ permalink raw reply

* Re: [RFC][PATCH v2 06/12] diglim: Interfaces - digest_list_add, digest_list_del
From: Mauro Carvalho Chehab @ 2021-07-28 12:38 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-7-roberto.sassu@huawei.com>

Em Mon, 26 Jul 2021 18:36:54 +0200
Roberto Sassu <roberto.sassu@huawei.com> 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@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*/

^ permalink raw reply

* Re: [RFC][PATCH v2 10/12] diglim: Interfaces - digests_count
From: Mauro Carvalho Chehab @ 2021-07-28 12:45 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel, kernel test robot
In-Reply-To: <20210726163700.2092768-11-roberto.sassu@huawei.com>

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

> Introduce the digests_count interface, which shows the current number of
> digests stored in the hash table by type.
> 
> Reported-by: kernel test robot <lkp@intel.com> (frame size warning)
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>

Patches 7 to 10 also LGTM.

> ---
>  security/integrity/diglim/fs.c | 48 ++++++++++++++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
> 
> diff --git a/security/integrity/diglim/fs.c b/security/integrity/diglim/fs.c
> index f1c1fc56448a..3b1d9616cb62 100644
> --- a/security/integrity/diglim/fs.c
> +++ b/security/integrity/diglim/fs.c
> @@ -23,6 +23,7 @@
>  #include "diglim.h"
>  
>  #define MAX_DIGEST_LIST_SIZE (64 * 1024 * 1024 - 1)
> +#define TMPBUF_SIZE 512
>  
>  static struct dentry *diglim_dir;
>  /**
> @@ -36,6 +37,13 @@ static struct dentry *diglim_dir;
>   * removed.
>   */
>  static struct dentry *digest_lists_loaded_dir;
> +/**
> + * DOC: digests_count
> + *
> + * digests_count shows the current number of digests stored in the hash
> + * table by type.
> + */
> +static struct dentry *digests_count;
>  /**
>   * DOC: digest_label
>   *
> @@ -73,6 +81,39 @@ static struct dentry *digest_list_del_dentry;
>  char digest_query[CRYPTO_MAX_ALG_NAME + 1 + IMA_MAX_DIGEST_SIZE * 2 + 1];
>  char digest_label[NAME_MAX + 1];
>  
> +static char *types_str[COMPACT__LAST] = {
> +	[COMPACT_PARSER] = "Parser",
> +	[COMPACT_FILE] = "File",
> +	[COMPACT_METADATA] = "Metadata",
> +	[COMPACT_DIGEST_LIST] = "Digest list",
> +};
> +
> +static ssize_t diglim_show_htable_len(struct file *filp, char __user *buf,
> +				      size_t count, loff_t *ppos)
> +{
> +	char *tmpbuf;
> +	ssize_t ret, len = 0;
> +	int i;
> +
> +	tmpbuf = kmalloc(TMPBUF_SIZE, GFP_KERNEL);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	for (i = COMPACT_PARSER; i < COMPACT__LAST; i++)
> +		len += scnprintf(tmpbuf + len, TMPBUF_SIZE - len,
> +				 "%s digests: %lu\n", types_str[i],
> +				 htable[i].len);
> +
> +	ret = simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
> +	kfree(tmpbuf);
> +	return ret;
> +}
> +
> +static const struct file_operations htable_len_ops = {
> +	.read = diglim_show_htable_len,
> +	.llseek = generic_file_llseek,
> +};
> +
>  static int parse_digest_list_filename(const char *digest_list_filename,
>  				      u8 *digest, enum hash_algo *algo)
>  {
> @@ -696,6 +737,12 @@ static int __init diglim_fs_init(void)
>  	if (IS_ERR(digest_lists_loaded_dir))
>  		goto out;
>  
> +	digests_count = securityfs_create_file("digests_count", 0440,
> +					       diglim_dir, NULL,
> +					       &htable_len_ops);
> +	if (IS_ERR(digests_count))
> +		goto out;
> +
>  	digest_list_add_dentry = securityfs_create_file("digest_list_add", 0200,
>  						diglim_dir, NULL,
>  						&digest_list_upload_ops);
> @@ -726,6 +773,7 @@ static int __init diglim_fs_init(void)
>  	securityfs_remove(digest_label_dentry);
>  	securityfs_remove(digest_list_del_dentry);
>  	securityfs_remove(digest_list_add_dentry);
> +	securityfs_remove(digests_count);
>  	securityfs_remove(digest_lists_loaded_dir);
>  	securityfs_remove(diglim_dir);
>  	return -1;

^ permalink raw reply

* Re: [RFC][PATCH v2 11/12] diglim: Remote Attestation
From: Mauro Carvalho Chehab @ 2021-07-28 12:47 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar, gregkh, linux-integrity, linux-security-module, linux-doc,
	linux-kselftest, linux-kernel
In-Reply-To: <20210726163700.2092768-12-roberto.sassu@huawei.com>

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

> Add more information about remote attestation with IMA and DIGLIM in
> Documentation/security/diglim/remote_attestation.rst.
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  Documentation/security/diglim/index.rst       |  1 +
>  .../security/diglim/remote_attestation.rst    | 87 +++++++++++++++++++
>  MAINTAINERS                                   |  1 +
>  3 files changed, 89 insertions(+)
>  create mode 100644 Documentation/security/diglim/remote_attestation.rst
> 
> diff --git a/Documentation/security/diglim/index.rst b/Documentation/security/diglim/index.rst
> index 4771134c2f0d..0f28c5ad71c0 100644
> --- a/Documentation/security/diglim/index.rst
> +++ b/Documentation/security/diglim/index.rst
> @@ -10,3 +10,4 @@ Digest Lists Integrity Module (DIGLIM)
>     introduction
>     architecture
>     implementation
> +   remote_attestation
> diff --git a/Documentation/security/diglim/remote_attestation.rst b/Documentation/security/diglim/remote_attestation.rst
> new file mode 100644
> index 000000000000..83fd7581c460
> --- /dev/null
> +++ b/Documentation/security/diglim/remote_attestation.rst
> @@ -0,0 +1,87 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Remote Attestation
> +==================
> +
> +When a digest list is added or deleted through the ``digest_list_add`` or
> +``digest_list_del`` interfaces, its buffer is sent to the IMA function
> +``ima_measure_critical_data()``. The primary reason for it is to calculate
> +the buffer digest, so that the digest list itself is searchable in the hash
> +table.
> +
> +``ima_measure_critical_data()`` can be also used to create a new
> +measurement entry each time this function is called, if there is an
> +appropriate rule in the IMA policy. Given that this function is called
> +during an addition or deletion of a digest list, a remote verifier can
> +infer from the measurement list precise information about what has been
> +uploaded to the kernel.
> +
> +To enable this functionality, the following rule must be added to the IMA
> +policy:
> +
> +::

As commented on other patches at this series, you can merge :: at the
previous text line, e. g.:

	policy::

does the same as:

	policy:

	::

but it is nicer for text-only readers, IMO.

> +
> + measure func=CRITICAL_DATA label=diglim
> +
> +
> +When a file is uploaded, the workflow and the resulting IMA measurement
> +list are:
> +
> +.. code-block:: bash
> +
> + # echo $PWD/0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_list_add
> + # echo $PWD/0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_list_del
> + # cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements
> + ...
> + 10 <template digest> ima-buf sha256:<buffer digest> add_file_0-file_list-compact-cat <buffer>
> + 10 <template digest> ima-buf sha256:<buffer digest> del_file_0-file_list-compact-cat <buffer>
> +
> +When a buffer is uploaded, the workflow and the resulting IMA measurement
> +list are:
> +
> +.. code-block:: bash
> +
> + # echo 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_label
> + # cat 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_list_add
> + # echo 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_label
> + # cat 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_list_del
> + # cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements
> + ...
> + 10 <template digest> ima-buf sha256:<buffer digest> add_buffer_0-file_list-compact-cat <buffer>
> + 10 <template digest> ima-buf sha256:<buffer digest> del_buffer_0-file_list-compact-cat <buffer>
> +
> +In the second case, the digest list label must be set explicitly, as the
> +kernel cannot determine it by itself (in the first case it is derived from
> +the name of the file uploaded).
> +
> +The confirmation that the digest list has been processed by IMA can be
> +obtained by reading the ASCII representation of the digest list:
> +
> +.. code-block:: bash
> +
> + # cat /sys/kernel/security/integrity/diglim/digest_lists_loaded/sha256-<digest list digest>-0-file_list-compact-cat.ascii
> + actions: 1, version: 1, algo: sha256, type: 2, modifiers: 1, count: 1, datalen: 32
> + 87e5bd81850e11eeec2d3bb696b626b2a7f45673241cbbd64769c83580432869
> +
> +In this output, ``actions`` is set to 1 (``COMPACT_ACTION_IMA_MEASURED``
> +bit set).
> +
> +
> +DIGLIM guarantees that the information reported in the IMA measurement list
> +is complete. If digest list loading is not recorded, digest query results
> +are ignored by IMA. If the addition was recorded, deletion can be performed
> +only if also the deletion is recorded. This can be seen in the following
> +sequence of commands:
> +
> +.. code-block:: bash
> +
> + # echo 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_label
> + # cat 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_list_add
> + # echo 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_label
> + # /tmp/cat 0-file_list-compact-cat > /sys/kernel/security/integrity/diglim/digest_list_del
> + diglim: actions mismatch, add: 1, del: 0
> + diglim: unable to upload generated digest list
> + /tmp/cat: write error: Invalid argument
> +
> +Digest list measurement is avoided with the execution of ``/tmp/cat``, for
> +which a dont_measure rule was previously added in the IMA policy.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0672128fae7f..a7c502685109 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5461,6 +5461,7 @@ F:	Documentation/security/diglim/architecture.rst
>  F:	Documentation/security/diglim/implementation.rst
>  F:	Documentation/security/diglim/index.rst
>  F:	Documentation/security/diglim/introduction.rst
> +F:	Documentation/security/diglim/remote_attestation.rst
>  F:	include/linux/diglim.h
>  F:	include/uapi/linux/diglim.h
>  F:	security/integrity/diglim/diglim.h

^ permalink raw reply

* RE: [RFC][PATCH v2 11/12] diglim: Remote Attestation
From: Roberto Sassu @ 2021-07-28 12:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20210728144728.62ace280@sal.lan>

> From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> Sent: Wednesday, July 28, 2021 2:47 PM
> Em Mon, 26 Jul 2021 18:36:59 +0200
> Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> 
> > Add more information about remote attestation with IMA and DIGLIM in
> > Documentation/security/diglim/remote_attestation.rst.
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >  Documentation/security/diglim/index.rst       |  1 +
> >  .../security/diglim/remote_attestation.rst    | 87 +++++++++++++++++++
> >  MAINTAINERS                                   |  1 +
> >  3 files changed, 89 insertions(+)
> >  create mode 100644 Documentation/security/diglim/remote_attestation.rst
> >
> > diff --git a/Documentation/security/diglim/index.rst
> b/Documentation/security/diglim/index.rst
> > index 4771134c2f0d..0f28c5ad71c0 100644
> > --- a/Documentation/security/diglim/index.rst
> > +++ b/Documentation/security/diglim/index.rst
> > @@ -10,3 +10,4 @@ Digest Lists Integrity Module (DIGLIM)
> >     introduction
> >     architecture
> >     implementation
> > +   remote_attestation
> > diff --git a/Documentation/security/diglim/remote_attestation.rst
> b/Documentation/security/diglim/remote_attestation.rst
> > new file mode 100644
> > index 000000000000..83fd7581c460
> > --- /dev/null
> > +++ b/Documentation/security/diglim/remote_attestation.rst
> > @@ -0,0 +1,87 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +Remote Attestation
> > +==================
> > +
> > +When a digest list is added or deleted through the ``digest_list_add`` or
> > +``digest_list_del`` interfaces, its buffer is sent to the IMA function
> > +``ima_measure_critical_data()``. The primary reason for it is to calculate
> > +the buffer digest, so that the digest list itself is searchable in the hash
> > +table.
> > +
> > +``ima_measure_critical_data()`` can be also used to create a new
> > +measurement entry each time this function is called, if there is an
> > +appropriate rule in the IMA policy. Given that this function is called
> > +during an addition or deletion of a digest list, a remote verifier can
> > +infer from the measurement list precise information about what has been
> > +uploaded to the kernel.
> > +
> > +To enable this functionality, the following rule must be added to the IMA
> > +policy:
> > +
> > +::
> 
> As commented on other patches at this series, you can merge :: at the
> previous text line, e. g.:
> 
> 	policy::
> 
> does the same as:
> 
> 	policy:
> 
> 	::
> 
> but it is nicer for text-only readers, IMO.

Ok, will change in the next version of the patch set.

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> > +
> > + measure func=CRITICAL_DATA label=diglim
> > +
> > +
> > +When a file is uploaded, the workflow and the resulting IMA measurement
> > +list are:
> > +
> > +.. code-block:: bash
> > +
> > + # echo $PWD/0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_list_add
> > + # echo $PWD/0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_list_del
> > + # cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements
> > + ...
> > + 10 <template digest> ima-buf sha256:<buffer digest> add_file_0-file_list-
> compact-cat <buffer>
> > + 10 <template digest> ima-buf sha256:<buffer digest> del_file_0-file_list-
> compact-cat <buffer>
> > +
> > +When a buffer is uploaded, the workflow and the resulting IMA
> measurement
> > +list are:
> > +
> > +.. code-block:: bash
> > +
> > + # echo 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_label
> > + # cat 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_list_add
> > + # echo 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_label
> > + # cat 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_list_del
> > + # cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements
> > + ...
> > + 10 <template digest> ima-buf sha256:<buffer digest> add_buffer_0-
> file_list-compact-cat <buffer>
> > + 10 <template digest> ima-buf sha256:<buffer digest> del_buffer_0-file_list-
> compact-cat <buffer>
> > +
> > +In the second case, the digest list label must be set explicitly, as the
> > +kernel cannot determine it by itself (in the first case it is derived from
> > +the name of the file uploaded).
> > +
> > +The confirmation that the digest list has been processed by IMA can be
> > +obtained by reading the ASCII representation of the digest list:
> > +
> > +.. code-block:: bash
> > +
> > + # cat /sys/kernel/security/integrity/diglim/digest_lists_loaded/sha256-
> <digest list digest>-0-file_list-compact-cat.ascii
> > + actions: 1, version: 1, algo: sha256, type: 2, modifiers: 1, count: 1, datalen:
> 32
> > +
> 87e5bd81850e11eeec2d3bb696b626b2a7f45673241cbbd64769c83580432869
> > +
> > +In this output, ``actions`` is set to 1
> (``COMPACT_ACTION_IMA_MEASURED``
> > +bit set).
> > +
> > +
> > +DIGLIM guarantees that the information reported in the IMA measurement
> list
> > +is complete. If digest list loading is not recorded, digest query results
> > +are ignored by IMA. If the addition was recorded, deletion can be
> performed
> > +only if also the deletion is recorded. This can be seen in the following
> > +sequence of commands:
> > +
> > +.. code-block:: bash
> > +
> > + # echo 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_label
> > + # cat 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_list_add
> > + # echo 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_label
> > + # /tmp/cat 0-file_list-compact-cat >
> /sys/kernel/security/integrity/diglim/digest_list_del
> > + diglim: actions mismatch, add: 1, del: 0
> > + diglim: unable to upload generated digest list
> > + /tmp/cat: write error: Invalid argument
> > +
> > +Digest list measurement is avoided with the execution of ``/tmp/cat``, for
> > +which a dont_measure rule was previously added in the IMA policy.
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 0672128fae7f..a7c502685109 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -5461,6 +5461,7 @@ F:
> 	Documentation/security/diglim/architecture.rst
> >  F:	Documentation/security/diglim/implementation.rst
> >  F:	Documentation/security/diglim/index.rst
> >  F:	Documentation/security/diglim/introduction.rst
> > +F:	Documentation/security/diglim/remote_attestation.rst
> >  F:	include/linux/diglim.h
> >  F:	include/uapi/linux/diglim.h
> >  F:	security/integrity/diglim/diglim.h

^ permalink raw reply

* Re: [RFC][PATCH v2 02/12] diglim: Basic definitions
From: Mauro Carvalho Chehab @ 2021-07-28 13:08 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <eb3b025820574f0d901a38a4ad088018@huawei.com>

Em Wed, 28 Jul 2021 11:45:02 +0000
Roberto Sassu <roberto.sassu@huawei.com> escreveu:

> > From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> > Sent: Wednesday, July 28, 2021 1:31 PM
> > Em Mon, 26 Jul 2021 18:36:50 +0200
> > Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> >   

> > > +struct compact_list_hdr {
> > > +	__u8 version;
> > > +	__u8 _reserved;
> > > +	__le16 type;
> > > +	__le16 modifiers;
> > > +	__le16 algo;
> > > +	__le32 count;
> > > +	__le32 datalen;
> > > +} __packed;
> > > +#endif /*_UAPI__LINUX_DIGLIM_H*/  
> > 
> > Besides Greg's notes, I'm wondering why to enforce a particular
> > endness here. I mean, this is uAPI. I would expect it to use the
> > CPU endianness instead, in order to avoid uneeded conversions.  
> 
> Also Greg had the same concern. I hoped the Lifecycle section clarified
> the fact that digest lists are generated by software vendors not the
> local system. Should I add something more in the documentation?

It shouldn't matter what kind of endness software vendors use on
userspace (either CPU or a fixed endiannes - either LE or BE).

I mean, I won't doubt that some package tools use LE while others
would use BE. At some point, this needs to be converted to 
CPU endiannes.

IMO, the best would be to isolate whatever RPM/DEB/... endianness 
is used on userspace from what the Kernel will use internally.

Just my 2 cents.

Regards,
Mauro

^ permalink raw reply

* RE: [RFC][PATCH v2 02/12] diglim: Basic definitions
From: Roberto Sassu @ 2021-07-28 13:47 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: zohar@linux.ibm.com, gregkh@linuxfoundation.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20210728150823.705623ad@sal.lan>

> From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> Sent: Wednesday, July 28, 2021 3:08 PM
> Em Wed, 28 Jul 2021 11:45:02 +0000
> Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> 
> > > From: Mauro Carvalho Chehab [mailto:mchehab+huawei@kernel.org]
> > > Sent: Wednesday, July 28, 2021 1:31 PM
> > > Em Mon, 26 Jul 2021 18:36:50 +0200
> > > Roberto Sassu <roberto.sassu@huawei.com> escreveu:
> > >
> 
> > > > +struct compact_list_hdr {
> > > > +	__u8 version;
> > > > +	__u8 _reserved;
> > > > +	__le16 type;
> > > > +	__le16 modifiers;
> > > > +	__le16 algo;
> > > > +	__le32 count;
> > > > +	__le32 datalen;
> > > > +} __packed;
> > > > +#endif /*_UAPI__LINUX_DIGLIM_H*/
> > >
> > > Besides Greg's notes, I'm wondering why to enforce a particular
> > > endness here. I mean, this is uAPI. I would expect it to use the
> > > CPU endianness instead, in order to avoid uneeded conversions.
> >
> > Also Greg had the same concern. I hoped the Lifecycle section clarified
> > the fact that digest lists are generated by software vendors not the
> > local system. Should I add something more in the documentation?
> 
> It shouldn't matter what kind of endness software vendors use on
> userspace (either CPU or a fixed endiannes - either LE or BE).
> 
> I mean, I won't doubt that some package tools use LE while others
> would use BE. At some point, this needs to be converted to
> CPU endiannes.

If you let digest list generators decide the endianness, probably
it is necessary to also add the endianness information in the
structure. Otherwise, the kernel wouldn't know what to do.

If the kernel knows that the digest list is always in little endian,
it simply calls le32_to_cpu().

> IMO, the best would be to isolate whatever RPM/DEB/... endianness
> is used on userspace from what the Kernel will use internally.

This is a different case. The conversion happens if the digest list
is not in native format. The kernel can also parse an untouched
digest list if it is in native format.

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> Just my 2 cents.
> 
> Regards,
> Mauro

^ permalink raw reply

* Re: [RFC PATCH v1] fscrypt: support encrypted and trusted keys
From: Eric Biggers @ 2021-07-28 16:05 UTC (permalink / raw)
  To: Ahmad Fatoum
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, Jarkko Sakkinen, James Morris,
	Serge E. Hallyn, James Bottomley, Mimi Zohar, Sumit Garg,
	David Howells, linux-fscrypt, linux-crypto, linux-integrity,
	linux-security-module, keyrings, linux-kernel, git, Omar Sandoval,
	Pengutronix Kernel Team
In-Reply-To: <367ea5bb-76cf-6020-cb99-91b5ca82d679@pengutronix.de>

On Wed, Jul 28, 2021 at 10:50:42AM +0200, Ahmad Fatoum wrote:
> Hello Eric,
> 
> On 27.07.21 18:38, Eric Biggers wrote:
> > On Tue, Jul 27, 2021 at 04:43:49PM +0200, Ahmad Fatoum wrote:
> >> For both v1 and v2 key setup mechanisms, userspace supplies the raw key
> >> material to the kernel after which it is never again disclosed to
> >> userspace.
> >>
> >> Use of encrypted and trusted keys offers stronger guarantees:
> >> The key material is generated within the kernel and is never disclosed to
> >> userspace in clear text and, in the case of trusted keys, can be
> >> directly rooted to a trust source like a TPM chip.
> > 
> > Please include a proper justification for this feature
> 
> I've patches pending for extending trusted keys to wrap the key sealing
> functionality of the CAAM IP on NXP SoCs[1]. I want the kernel to
> generate key material in the factory, have the CAAM encrypt it using its
> undisclosed unique key and pass it to userspace as encrypted blob that is
> persisted to an unencrypted volume. The intention is to thwart offline
> decryption of an encrypted file system in an embedded system, where a
> passphrase can't be supplied by an end user.
> 
> Employing TPM and TEE trusted keys with this is already possible with
> dm-crypt, but I'd like this to be possible out-of-the-box with
> ubifs + fscrypt as well.

Why not do the key management in userspace, like tpm-tools
(https://github.com/tpm2-software/tpm2-tools)?  There are a lot of uses for this
type of hardware besides in-kernel crypto.  See
https://wiki.archlinux.org/title/Trusted_Platform_Module for all the things you
can do with the TPM on Linux, including LUKS encryption; this is all with
userspace key management.  Wouldn't the CAAM hardware be useful for similar
purposes and thus need a similar design as well, e.g. with functionality exposed
through some /dev node for userspace to use?  Or are you saying it will only
ever be useful for in-kernel crypto?

> > Note that there are several design flaws with the encrypted and trusted key
> > types:
> > 
> > - By default, trusted keys are generated using the TPM's RNG rather than the
> >   kernel's RNG, which places all trust in an unauditable black box.
> 
> Patch to fix that awaits feedback on linux-integrity[2].

It does *not* fix it, as your patch only provides an option to use the kernel's
RNG whereas the default is still the TPM's RNG.

Most people don't change defaults.

Essentially your same argument was used for Dual_EC_DRBG; people argued it was
okay to standardize because people had the option to choose their own constants
if they felt the default constants were backdoored.  That didn't really matter,
though, since in practice everyone just used the default constants.

> 
> > - trusted and encrypted keys aren't restricted to specific uses in the kernel
> >   (like the fscrypt-provisioning key type is) but rather are general-purpose.
> >   Hence, it may be possible to leak their contents to userspace by requesting
> >   their use for certain algorithms/features, e.g. to encrypt a dm-crypt target
> >   using a weak cipher that is vulnerable to key recovery attacks.
> 
> The footgun is already there by allowing users to specify their own
> 
> raw key. Users can already use $keyid for dm-crypt and then do
> 
>   $ keyctl pipe $keyid | fscryptctl add_key /mnt
> 
> The responsibility to not reuse key material already lies with the users,
> regardless if they handle the raw key material directly or indirectly via
> a trusted key description/ID.

Elsewhere you are claiming that "trusted" keys can never be disclosed to
userspace.  So you can't rely on userspace cooperating, right?

- Eric

^ permalink raw reply

* Re: [PATCH RFC 0/9] sk_buff: optimize layout for GRO
From: Paolo Abeni @ 2021-07-28 16:21 UTC (permalink / raw)
  To: Paul Moore, Casey Schaufler
  Cc: Florian Westphal, netdev, David S. Miller, Jakub Kicinski,
	Eric Dumazet, linux-security-module, selinux
In-Reply-To: <CAHC9VhSTputyDZMrdU0SmO0gg=P3uskT6ejJkfOwn6bFgaFB3A@mail.gmail.com>

On Mon, 2021-07-26 at 22:51 -0400, Paul Moore wrote:
> On Mon, Jul 26, 2021 at 11:13 AM Casey Schaufler <casey@schaufler-ca.com> wrote:
> > On 7/25/2021 3:52 PM, Florian Westphal wrote:
> > > Casey Schaufler <casey@schaufler-ca.com> wrote:
> > > > RedHat and android use SELinux and will want this. Ubuntu doesn't
> > > > yet, but netfilter in in the AppArmor task list. Tizen definitely
> > > > uses it with Smack. The notion that security modules are only used
> > > > in fringe cases is antiquated.
> > > I was not talking about LSM in general, I was referring to the
> > > extended info that Paul mentioned.
> > > 
> > > If thats indeed going to be used on every distro then skb extensions
> > > are not suitable for this, it would result in extr akmalloc for every
> > > skb.
> > 
> > I am explicitly talking about the use of secmarks. All my
> > references are uses of secmarks.
> 
> I'm talking about a void* which would contain LSM specific data; as I
> said earlier, think of inodes.  This LSM specific data would include
> the existing secmark data as well as network peer security information
> which would finally (!!!) allow us to handle forwarded traffic and
> enable a number of other fixes and performance improvements.
> 
> (The details are a bit beyond this discussion but it basically
> revolves around us not having to investigate the import the packet
> headers every time we want to determine the network peer security
> attributes, we could store the resolved LSM information in the
> sk_buff.security blob.)

I've investigated the feasibility of extending the secmark field to
long/void*. I think that performance wise it should be doable on top of
this series: the amount of allocated memory for sk_buff will not
change, nor the amount of memory memseted at skb initialization time.

I stumbled upon some uAPIs issues, as CT/nft expose a secmark related
field via uAPI, changing that size without breaking esisting user-space 
looks hard to me.

Additionally, even patch 7/9 is problematic, as there are some in
kernel users accessing and using the inner_ field regardless skb-
>encapsulation. That works while inner_* field are always
initializared/zeored, but will break with the mentioned patch. The fix
is doable, but large and complex. 

To keep the scope of this series sane, I'll drop in the next iteration
all the problematic patches - that is: no sk_buff layout change at all.

If there is interest for such thing, it could still be added
incrementally.

Cheers,

Paolo




^ permalink raw reply

* Re: [PATCH v10 4/8] PCI/sysfs: Allow userspace to query and set device reset mechanism
From: Bjorn Helgaas @ 2021-07-28 17:09 UTC (permalink / raw)
  To: Amey Narkhede
  Cc: Bjorn Helgaas, alex.williamson, Raphael Norwitz, linux-pci,
	linux-kernel, kw, Shanker Donthineni, Sinan Kaya, Len Brown,
	Rafael J . Wysocki, Serge Hallyn, linux-security-module
In-Reply-To: <20210709123813.8700-5-ameynarkhede03@gmail.com>

[+cc Serge, linux-security-module: should we check CAP_SYS_ADMIN or
similar for changing PCI reset mechanisms for a device?]

On Fri, Jul 09, 2021 at 06:08:09PM +0530, Amey Narkhede wrote:
> Add reset_method sysfs attribute to enable user to query and set user
> preferred device reset methods and their ordering.
> 
> Co-developed-by: Alex Williamson <alex.williamson@redhat.com>
> Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
> Signed-off-by: Amey Narkhede <ameynarkhede03@gmail.com>
> ---
>  Documentation/ABI/testing/sysfs-bus-pci |  19 +++++
>  drivers/pci/pci-sysfs.c                 | 103 ++++++++++++++++++++++++
>  2 files changed, 122 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
> index ef00fada2..43f4e33c7 100644
> --- a/Documentation/ABI/testing/sysfs-bus-pci
> +++ b/Documentation/ABI/testing/sysfs-bus-pci
> @@ -121,6 +121,25 @@ Description:
>  		child buses, and re-discover devices removed earlier
>  		from this part of the device tree.
>  
> +What:		/sys/bus/pci/devices/.../reset_method
> +Date:		March 2021
> +Contact:	Amey Narkhede <ameynarkhede03@gmail.com>
> +Description:
> +		Some devices allow an individual function to be reset
> +		without affecting other functions in the same slot.
> +
> +		For devices that have this support, a file named
> +		reset_method will be present in sysfs. Initially reading
> +		this file will give names of the device supported reset
> +		methods and their ordering. After write, this file will
> +		give names and ordering of currently enabled reset methods.
> +		Writing the name or comma separated list of names of any of
> +		the device supported reset methods to this file will set
> +		the reset methods and their ordering to be used when
> +		resetting the device. Writing empty string to this file
> +		will disable ability to reset the device and writing
> +		"default" will return to the original value.
> +
>  What:		/sys/bus/pci/devices/.../reset
>  Date:		July 2009
>  Contact:	Michael S. Tsirkin <mst@redhat.com>
> diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
> index 316f70c3e..8a740e211 100644
> --- a/drivers/pci/pci-sysfs.c
> +++ b/drivers/pci/pci-sysfs.c
> @@ -1334,6 +1334,108 @@ static const struct attribute_group pci_dev_rom_attr_group = {
>  	.is_bin_visible = pci_dev_rom_attr_is_visible,
>  };
>  
> +static ssize_t reset_method_show(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +	ssize_t len = 0;
> +	int i, idx;
> +
> +	for (i = 0; i < PCI_NUM_RESET_METHODS; i++) {
> +		idx = pdev->reset_methods[i];
> +		if (!idx)
> +			break;
> +
> +		len += sysfs_emit_at(buf, len, "%s%s", len ? "," : "",
> +				     pci_reset_fn_methods[idx].name);
> +	}
> +
> +	if (len)
> +		len += sysfs_emit_at(buf, len, "\n");
> +
> +	return len;
> +}
> +
> +static ssize_t reset_method_store(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +	int n = 0;
> +	char *name, *options = NULL;
> +	u8 reset_methods[PCI_NUM_RESET_METHODS] = { 0 };

Should this check "capable(CAP_SYS_ADMIN)" or similar?  The sysfs file
is mode 0644, so writable only by root.

I do note that Documentation/process/adding-syscalls.rst suggests
"avoid adding new uses of the already overly-general CAP_SYS_ADMIN
capability."  But CAP_SYS_ADMIN is used for all the other checks in
pci-sysfs.c.

> +	if (count >= (PAGE_SIZE - 1))
> +		return -EINVAL;
> +
> +	if (sysfs_streq(buf, "")) {
> +		pci_warn(pdev, "All device reset methods disabled by user");
> +		goto set_reset_methods;
> +	}
> +
> +	if (sysfs_streq(buf, "default")) {
> +		pci_init_reset_methods(pdev);
> +		return count;
> +	}
> +
> +	options = kstrndup(buf, count, GFP_KERNEL);
> +	if (!options)
> +		return -ENOMEM;
> +
> +	while ((name = strsep(&options, ",")) != NULL) {
> +		int i;
> +
> +		if (sysfs_streq(name, ""))
> +			continue;
> +
> +		name = strim(name);
> +
> +		for (i = 1; i < PCI_NUM_RESET_METHODS; i++) {
> +			if (sysfs_streq(name, pci_reset_fn_methods[i].name) &&
> +			    !pci_reset_fn_methods[i].reset_fn(pdev, 1)) {
> +				reset_methods[n++] = i;
> +				break;
> +			}
> +		}
> +
> +		if (i == PCI_NUM_RESET_METHODS) {
> +			kfree(options);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (!pci_reset_fn_methods[1].reset_fn(pdev, 1) && reset_methods[0] != 1)
> +		pci_warn(pdev, "Device specific reset disabled/de-prioritized by user");
> +
> +set_reset_methods:
> +	memcpy(pdev->reset_methods, reset_methods, sizeof(reset_methods));
> +	kfree(options);
> +	return count;
> +}
> +static DEVICE_ATTR_RW(reset_method);
> +
> +static struct attribute *pci_dev_reset_method_attrs[] = {
> +	&dev_attr_reset_method.attr,
> +	NULL,
> +};
> +
> +static umode_t pci_dev_reset_method_attr_is_visible(struct kobject *kobj,
> +						    struct attribute *a, int n)
> +{
> +	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
> +
> +	if (!pci_reset_supported(pdev))
> +		return 0;
> +
> +	return a->mode;
> +}
> +
> +static const struct attribute_group pci_dev_reset_method_attr_group = {
> +	.attrs = pci_dev_reset_method_attrs,
> +	.is_visible = pci_dev_reset_method_attr_is_visible,
> +};
> +
>  static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
>  			   const char *buf, size_t count)
>  {
> @@ -1491,6 +1593,7 @@ const struct attribute_group *pci_dev_groups[] = {
>  	&pci_dev_config_attr_group,
>  	&pci_dev_rom_attr_group,
>  	&pci_dev_reset_attr_group,
> +	&pci_dev_reset_method_attr_group,
>  	&pci_dev_vpd_attr_group,
>  #ifdef CONFIG_DMI
>  	&pci_dev_smbios_attr_group,
> -- 
> 2.32.0
> 

^ permalink raw reply

* Re: [PATCH] tpm: ibmvtpm: Avoid error message when process gets signal while waiting
From: Jarkko Sakkinen @ 2021-07-28 21:50 UTC (permalink / raw)
  To: Stefan Berger
  Cc: Stefan Berger, peterhuewe, jgg, linux-integrity,
	linux-security-module, linux-kernel, Nayna Jain, George Wilson,
	Nageswara R Sastry
In-Reply-To: <ad4011fb-fc1f-4019-9856-7d171db3255c@linux.ibm.com>

On Mon, Jul 26, 2021 at 11:00:51PM -0400, Stefan Berger wrote:
> 
> On 7/26/21 10:42 PM, Jarkko Sakkinen wrote:
> > On Mon, Jul 12, 2021 at 12:25:05PM -0400, Stefan Berger wrote:
> > > From: Stefan Berger <stefanb@linux.ibm.com>
> > > 
> > > When rngd is run as root then lots of these types of message will appear
> > > in the kernel log if the TPM has been configure to provide random bytes:
> > > 
> > > [ 7406.275163] tpm tpm0: tpm_transmit: tpm_recv: error -4
> > > 
> > > The issue is caused by the following call that is interrupted while
> > > waiting for the TPM's response.
> > > 
> > > sig = wait_event_interruptible(ibmvtpm->wq,
> > >                                 !ibmvtpm->tpm_processing_cmd);
> > > 
> > > The solution is to use wait_event() instead.
> > Why?
> 
> So it becomes uninterruptible and these error messages go away.

We do not want to make a process uninterruptible. That would prevent
killing it.

/Jarkko

^ permalink raw reply

* Re: [PATCH v2] KEYS: trusted: fix use as module when CONFIG_TCG_TPM=m
From: Jarkko Sakkinen @ 2021-07-28 21:52 UTC (permalink / raw)
  To: Ahmad Fatoum
  Cc: James Morris, Serge E. Hallyn, James Bottomley, Mimi Zohar,
	Sumit Garg, David Howells, Herbert Xu, David S. Miller, kernel,
	Andreas Rammhold, David Gstir, Richard Weinberger, keyrings,
	linux-crypto, linux-kernel, linux-security-module,
	linux-integrity
In-Reply-To: <fe39a449-88df-766b-a13a-290f4847d43e@pengutronix.de>

On Tue, Jul 27, 2021 at 06:24:49AM +0200, Ahmad Fatoum wrote:
> On 27.07.21 05:04, Jarkko Sakkinen wrote:
> >> Reported-by: Andreas Rammhold <andreas@rammhold.de>
> >> Fixes: 5d0682be3189 ("KEYS: trusted: Add generic trusted keys framework")
> >> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> > 
> > Is it absolutely need to do all this *just* to fix the bug?
> > 
> > For a pure bug fix the most essential thing is to be able the backport
> > it to stable kernels.
> 
> Not much happened in-between, so a backport should be trivial.
> I can provide these if needed.

"not much" is not good enough. It should be "not anything".

/Jarkko

^ permalink raw reply

* Re: [RFC PATCH v1] fscrypt: support encrypted and trusted keys
From: Jarkko Sakkinen @ 2021-07-28 22:22 UTC (permalink / raw)
  To: Ahmad Fatoum
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, James Morris,
	Serge E. Hallyn, James Bottomley, Mimi Zohar, Sumit Garg,
	David Howells, linux-fscrypt, linux-crypto, linux-integrity,
	linux-security-module, keyrings, linux-kernel
In-Reply-To: <20210727144349.11215-1-a.fatoum@pengutronix.de>

On Tue, Jul 27, 2021 at 04:43:49PM +0200, Ahmad Fatoum wrote:
> For both v1 and v2 key setup mechanisms, userspace supplies the raw key
> material to the kernel after which it is never again disclosed to
> userspace.
> 
> Use of encrypted and trusted keys offers stronger guarantees:
> The key material is generated within the kernel and is never disclosed to
> userspace in clear text and, in the case of trusted keys, can be
> directly rooted to a trust source like a TPM chip.
> 
> Add support for trusted and encrypted keys by repurposing
> fscrypt_add_key_arg::raw to hold the key description when the new
> FSCRYPT_KEY_ARG_TYPE_DESC flag is supplied. The location of the flag
> was previously reserved and enforced by ioctl code to be zero, so this
> change won't break backwards compatibility.
> 
> Corresponding userspace patches are available for fscryptctl:
> https://github.com/google/fscryptctl/pull/23
> 
> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
> key_extract_material used by this patch is added in
> <cover.b2fdd70b830d12853b12a12e32ceb0c8162c1346.1626945419.git-series.a.fatoum@pengutronix.de>
> which still awaits feedback.
> 
> Sending this RFC out anyway to get some feedback from the fscrypt
> developers whether this is the correct way to go about it.
> 
> To: "Theodore Y. Ts'o" <tytso@mit.edu>
> To: Jaegeuk Kim <jaegeuk@kernel.org>
> To: Eric Biggers <ebiggers@kernel.org>
> Cc: Jarkko Sakkinen <jarkko@kernel.org>
> Cc: James Morris <jmorris@namei.org>
> Cc: "Serge E. Hallyn" <serge@hallyn.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Mimi Zohar <zohar@linux.ibm.com>
> Cc: Sumit Garg <sumit.garg@linaro.org>
> Cc: David Howells <dhowells@redhat.com>
> Cc: linux-fscrypt@vger.kernel.org
> Cc: linux-crypto@vger.kernel.org
> Cc: linux-integrity@vger.kernel.org
> Cc: linux-security-module@vger.kernel.org
> Cc: keyrings@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
>  Documentation/filesystems/fscrypt.rst | 24 ++++++++---
>  fs/crypto/keyring.c                   | 59 ++++++++++++++++++++++++---
>  include/uapi/linux/fscrypt.h          | 16 +++++++-
>  3 files changed, 87 insertions(+), 12 deletions(-)
> 
> diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
> index 44b67ebd6e40..83738af2afa3 100644
> --- a/Documentation/filesystems/fscrypt.rst
> +++ b/Documentation/filesystems/fscrypt.rst
> @@ -681,11 +681,15 @@ It can be executed on any file or directory on the target filesystem,
>  but using the filesystem's root directory is recommended.  It takes in
>  a pointer to struct fscrypt_add_key_arg, defined as follows::
>  
> +    #define FSCRYPT_KEY_ADD_RAW_ASIS		0
> +    #define FSCRYPT_KEY_ADD_RAW_DESC		1

Would be nice to have these documented.

/Jarkko

^ permalink raw reply

* Re: [RFC PATCH v1] fscrypt: support encrypted and trusted keys
From: Ahmad Fatoum @ 2021-07-28 22:27 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, James Morris,
	Serge E. Hallyn, James Bottomley, Mimi Zohar, Sumit Garg,
	David Howells, linux-fscrypt, linux-crypto, linux-integrity,
	linux-security-module, keyrings, linux-kernel
In-Reply-To: <20210728222243.4wqs64pqngzzii3b@kernel.org>

Hello Jarkko,

On 29.07.21 00:22, Jarkko Sakkinen wrote:
> On Tue, Jul 27, 2021 at 04:43:49PM +0200, Ahmad Fatoum wrote:
>> For both v1 and v2 key setup mechanisms, userspace supplies the raw key
>> material to the kernel after which it is never again disclosed to
>> userspace.
>>
>> Use of encrypted and trusted keys offers stronger guarantees:
>> The key material is generated within the kernel and is never disclosed to
>> userspace in clear text and, in the case of trusted keys, can be
>> directly rooted to a trust source like a TPM chip.
>>
>> Add support for trusted and encrypted keys by repurposing
>> fscrypt_add_key_arg::raw to hold the key description when the new
>> FSCRYPT_KEY_ARG_TYPE_DESC flag is supplied. The location of the flag
>> was previously reserved and enforced by ioctl code to be zero, so this
>> change won't break backwards compatibility.
>>
>> Corresponding userspace patches are available for fscryptctl:
>> https://github.com/google/fscryptctl/pull/23
>>
>> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
>> ---
>> key_extract_material used by this patch is added in
>> <cover.b2fdd70b830d12853b12a12e32ceb0c8162c1346.1626945419.git-series.a.fatoum@pengutronix.de>
>> which still awaits feedback.
>>
>> Sending this RFC out anyway to get some feedback from the fscrypt
>> developers whether this is the correct way to go about it.
>>
>> To: "Theodore Y. Ts'o" <tytso@mit.edu>
>> To: Jaegeuk Kim <jaegeuk@kernel.org>
>> To: Eric Biggers <ebiggers@kernel.org>
>> Cc: Jarkko Sakkinen <jarkko@kernel.org>
>> Cc: James Morris <jmorris@namei.org>
>> Cc: "Serge E. Hallyn" <serge@hallyn.com>
>> Cc: James Bottomley <jejb@linux.ibm.com>
>> Cc: Mimi Zohar <zohar@linux.ibm.com>
>> Cc: Sumit Garg <sumit.garg@linaro.org>
>> Cc: David Howells <dhowells@redhat.com>
>> Cc: linux-fscrypt@vger.kernel.org
>> Cc: linux-crypto@vger.kernel.org
>> Cc: linux-integrity@vger.kernel.org
>> Cc: linux-security-module@vger.kernel.org
>> Cc: keyrings@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org
>> ---
>>  Documentation/filesystems/fscrypt.rst | 24 ++++++++---
>>  fs/crypto/keyring.c                   | 59 ++++++++++++++++++++++++---
>>  include/uapi/linux/fscrypt.h          | 16 +++++++-
>>  3 files changed, 87 insertions(+), 12 deletions(-)
>>
>> diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
>> index 44b67ebd6e40..83738af2afa3 100644
>> --- a/Documentation/filesystems/fscrypt.rst
>> +++ b/Documentation/filesystems/fscrypt.rst
>> @@ -681,11 +681,15 @@ It can be executed on any file or directory on the target filesystem,
>>  but using the filesystem's root directory is recommended.  It takes in
>>  a pointer to struct fscrypt_add_key_arg, defined as follows::
>>  
>> +    #define FSCRYPT_KEY_ADD_RAW_ASIS		0
>> +    #define FSCRYPT_KEY_ADD_RAW_DESC		1
> 
> Would be nice to have these documented.

They have explanatory comments in the uAPI header. The Documentation file
purposefully omits these comments and describes each field separately after
the code block. I am just following suit.

FWIW, I intend to drop these flags for v2 anyway.

Cheers,
Ahmad

> 
> /Jarkko
> 


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH v2] KEYS: trusted: fix use as module when CONFIG_TCG_TPM=m
From: Ahmad Fatoum @ 2021-07-28 22:29 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: James Morris, Serge E. Hallyn, James Bottomley, Mimi Zohar,
	Sumit Garg, David Howells, Herbert Xu, David S. Miller, kernel,
	Andreas Rammhold, David Gstir, Richard Weinberger, keyrings,
	linux-crypto, linux-kernel, linux-security-module,
	linux-integrity
In-Reply-To: <20210728215200.nfvnm5s2b27ang7i@kernel.org>

On 28.07.21 23:52, Jarkko Sakkinen wrote:
> On Tue, Jul 27, 2021 at 06:24:49AM +0200, Ahmad Fatoum wrote:
>> On 27.07.21 05:04, Jarkko Sakkinen wrote:
>>>> Reported-by: Andreas Rammhold <andreas@rammhold.de>
>>>> Fixes: 5d0682be3189 ("KEYS: trusted: Add generic trusted keys framework")
>>>> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
>>>
>>> Is it absolutely need to do all this *just* to fix the bug?
>>>
>>> For a pure bug fix the most essential thing is to be able the backport
>>> it to stable kernels.
>>
>> Not much happened in-between, so a backport should be trivial.
>> I can provide these if needed.
> 
> "not much" is not good enough. It should be "not anything".

"Not much" [code that could conflict was added in-between].

I just checked and it applies cleanly on v5.13. On the off chance
that this patch conflicts with another stable backport by the time
it's backported, I'll get a friendly automated email and send out
a rebased patch.

Cheers,
Ahmad


> 
> /Jarkko
> 


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox