2024-01-15 18:19:16

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 00/25] security: Move IMA and EVM to the LSM infrastructure

From: Roberto Sassu <[email protected]>

IMA and EVM are not effectively LSMs, especially due to the fact that in
the past they could not provide a security blob while there is another LSM
active.

That changed in the recent years, the LSM stacking feature now makes it
possible to stack together multiple LSMs, and allows them to provide a
security blob for most kernel objects. While the LSM stacking feature has
some limitations being worked out, it is already suitable to make IMA and
EVM as LSMs.

The main purpose of this patch set is to remove IMA and EVM function calls,
hardcoded in the LSM infrastructure and other places in the kernel, and to
register them as LSM hook implementations, so that those functions are
called by the LSM infrastructure like other regular LSMs.

This patch set introduces two new LSMs 'ima' and 'evm', so that functions
can be registered to their respective LSM, and removes the 'integrity' LSM.
integrity_kernel_module_request() was moved to IMA, since deadlock could
occur only there. integrity_inode_free() was replaced with ima_inode_free()
(EVM does not need to free memory).

In order to make 'ima' and 'evm' independent LSMs, it was necessary to
split integrity metadata used by both IMA and EVM, and to let them manage
their own. The special case of the IMA_NEW_FILE flag, managed by IMA and
used by EVM, was handled by introducing a new flag in EVM, EVM_NEW_FILE,
managed by two additional LSM hooks, evm_post_path_mknod() and
evm_file_release(), equivalent to their counterparts ima_post_path_mknod()
and ima_file_free().

In addition to splitting metadata, it was decided to embed the
evm_iint_inode structure into the inode security blob, since it is small
and because anyway it cannot rely on IMA anymore allocating it (since it
uses a different structure).

On the other hand, to avoid memory pressure concerns, only a pointer to the
ima_iint_cache structure is stored in the inode security blob, and the
structure is allocated on demand, like before.

Another follow-up change was removing the iint parameter from
evm_verifyxattr(), that IMA used to pass integrity metadata to EVM. After
splitting metadata, and aligning EVM_NEW_FILE with IMA_NEW_FILE, this
parameter was not necessary anymore.

The last part was to ensure that the order of IMA and EVM functions is
respected after they become LSMs. Since the order of lsm_info structures in
the .lsm_info.init section depends on the order object files containing
those structures are passed to the linker of the kernel image, and since
IMA is before EVM in the Makefile, that is sufficient to assert that IMA
functions are executed before EVM ones.

The patch set is organized as follows.

Patches 1-9 make IMA and EVM functions suitable to be registered to the LSM
infrastructure, by aligning function parameters.

Patches 10-18 add new LSM hooks in the same places where IMA and EVM
functions are called, if there is no LSM hook already.

Patch 19 moves integrity_kernel_module_request() to IMA, as a prerequisite
for removing the 'integrity' LSM.

Patches 20-22 introduce the new standalone LSMs 'ima' and 'evm', and move
hardcoded calls to IMA, EVM and integrity functions to those LSMs.

Patches 23-24 remove the dependency on the 'integrity' LSM by splitting
integrity metadata, so that the 'ima' and 'evm' LSMs can use their own.
They also duplicate iint_lockdep_annotate() in ima_main.c, since the mutex
field was moved from integrity_iint_cache to ima_iint_cache.

Patch 25 finally removes the 'integrity' LSM, since 'ima' and 'evm' are now
self-contained and independent.

The patch set applies on top of lsm/dev, commit f1bb47a31dff ("lsm: new
security_file_ioctl_compat() hook"). The linux-integrity/next-integrity at
commit c00f94b3a5be ("overlay: disable EVM") was merged.

Changelog:

v8:
- Restore dynamic allocation of IMA integrity metadata, and store only the
pointer in the inode security blob
- Select SECURITY_PATH both in IMA and EVM
- Rename evm_file_free() to evm_file_release()
- Unconditionally register evm_post_path_mknod()
- Introduce the new ima_iint.c file for the management of IMA integrity
metadata
- Introduce ima_inode_set_iint()/ima_inode_get_iint() in ima.h to
respectively store/retrieve the IMA integrity metadata pointer
- Replace ima_iint_inode() with ima_inode_get() and ima_iint_find(), with
same behavior of integrity_inode_get() and integrity_iint_find()
- Initialize the ima_iint_cache in ima_iintcache_init() and call it from
init_ima_lsm()
- Move integrity_kernel_module_request() to IMA in a separate patch
(suggested by Mimi)
- Compile ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS
is enabled
- Remove ima_inode_alloc_security() and ima_inode_free_security(), since
the IMA integrity metadata is not fully embedded in the inode security
blob
- Fixed the missed initialization of ima_iint_cache in
process_measurement() and __ima_inode_hash()
- Add a sentence in 'evm: Move to LSM infrastructure' to mention about
moving evm_inode_remove_acl(), evm_inode_post_remove_acl() and
evm_inode_post_set_acl() to evm_main.c
- Add a sentence in 'ima: Move IMA-Appraisal to LSM infrastructure' to
mention about moving ima_inode_remove_acl() to ima_appraise.c

v7:
- Use return instead of goto in __vfs_removexattr_locked() (suggested by
Casey)
- Clarify in security/integrity/Makefile that the order of 'ima' and 'evm'
LSMs depends on the order in which IMA and EVM are compiled
- Move integrity_iint_cache flags to ima.h and evm.h in security/ and
duplicate IMA_NEW_FILE to EVM_NEW_FILE
- Rename evm_inode_get_iint() to evm_iint_inode() and ima_inode_get_iint()
to ima_iint_inode(), check if inode->i_security is NULL, and just return
the pointer from the inode security blob
- Restore the non-NULL checks after ima_iint_inode() and evm_iint_inode()
(suggested by Casey)
- Introduce evm_file_free() to clear EVM_NEW_FILE
- Remove comment about LSM_ORDER_LAST not guaranteeing the order of 'ima'
and 'evm' LSMs
- Lock iint->mutex before reading IMA_COLLECTED flag in __ima_inode_hash()
and restored ima_policy_flag check
- Remove patch about the hardcoded ordering of 'ima' and 'evm' LSMs in
security.c
- Add missing ima_inode_free_security() to free iint->ima_hash
- Add the cases for LSM_ID_IMA and LSM_ID_EVM in lsm_list_modules_test.c
- Mention about the change in IMA and EVM post functions for private
inodes

v6:
- See v7

v5:
- Rename security_file_pre_free() to security_file_release() and the LSM
hook file_pre_free_security to file_release (suggested by Paul)
- Move integrity_kernel_module_request() to ima_main.c (renamed to
ima_kernel_module_request())
- Split the integrity_iint_cache structure into ima_iint_cache and
evm_iint_cache, so that IMA and EVM can use disjoint metadata and
reserve space with the LSM infrastructure
- Reserve space for the entire ima_iint_cache and evm_iint_cache
structures, not just the pointer (suggested by Paul)
- Introduce ima_inode_get_iint() and evm_inode_get_iint() to retrieve
respectively the ima_iint_cache and evm_iint_cache structure from the
security blob
- Remove the various non-NULL checks for the ima_iint_cache and
evm_iint_cache structures, since the LSM infrastructure ensure that they
always exist
- Remove the iint parameter from evm_verifyxattr() since IMA and EVM
use disjoint integrity metaddata
- Introduce the evm_post_path_mknod() to set the IMA_NEW_FILE flag
- Register the inode_alloc_security LSM hook in IMA and EVM to
initialize the respective integrity metadata structures
- Remove the 'integrity' LSM completely and instead make 'ima' and 'evm'
proper standalone LSMs
- Add the inode parameter to ima_get_verity_digest(), since the inode
field is not present in ima_iint_cache
- Move iint_lockdep_annotate() to ima_main.c (renamed to
ima_iint_lockdep_annotate())
- Remove ima_get_lsm_id() and evm_get_lsm_id(), since IMA and EVM directly
register the needed LSM hooks
- Enforce 'ima' and 'evm' LSM ordering at LSM infrastructure level

v4:
- Improve short and long description of
security_inode_post_create_tmpfile(), security_inode_post_set_acl(),
security_inode_post_remove_acl() and security_file_post_open()
(suggested by Mimi)
- Improve commit message of 'ima: Move to LSM infrastructure' (suggested
by Mimi)

v3:
- Drop 'ima: Align ima_post_path_mknod() definition with LSM
infrastructure' and 'ima: Align ima_post_create_tmpfile() definition
with LSM infrastructure', define the new LSM hooks with the same
IMA parameters instead (suggested by Mimi)
- Do IS_PRIVATE() check in security_path_post_mknod() and
security_inode_post_create_tmpfile() on the new inode rather than the
parent directory (in the post method it is available)
- Don't export ima_file_check() (suggested by Stefan)
- Remove redundant check of file mode in ima_post_path_mknod() (suggested
by Mimi)
- Mention that ima_post_path_mknod() is now conditionally invoked when
CONFIG_SECURITY_PATH=y (suggested by Mimi)
- Mention when a LSM hook will be introduced in the IMA/EVM alignment
patches (suggested by Mimi)
- Simplify the commit messages when introducing a new LSM hook
- Still keep the 'extern' in the function declaration, until the
declaration is removed (suggested by Mimi)
- Improve documentation of security_file_pre_free()
- Register 'ima' and 'evm' as standalone LSMs (suggested by Paul)
- Initialize the 'ima' and 'evm' LSMs from 'integrity', to keep the
original ordering of IMA and EVM functions as when they were hardcoded
- Return the IMA and EVM LSM IDs to 'integrity' for registration of the
integrity-specific hooks
- Reserve an xattr slot from the 'evm' LSM instead of 'integrity'
- Pass the LSM ID to init_ima_appraise_lsm()

v2:
- Add description for newly introduced LSM hooks (suggested by Casey)
- Clarify in the description of security_file_pre_free() that actions can
be performed while the file is still open

v1:
- Drop 'evm: Complete description of evm_inode_setattr()', 'fs: Fix
description of vfs_tmpfile()' and 'security: Introduce LSM_ORDER_LAST',
they were sent separately (suggested by Christian Brauner)
- Replace dentry with file descriptor parameter for
security_inode_post_create_tmpfile()
- Introduce mode_stripped and pass it as mode argument to
security_path_mknod() and security_path_post_mknod()
- Use goto in do_mknodat() and __vfs_removexattr_locked() (suggested by
Mimi)
- Replace __lsm_ro_after_init with __ro_after_init
- Modify short description of security_inode_post_create_tmpfile() and
security_inode_post_set_acl() (suggested by Stefan)
- Move security_inode_post_setattr() just after security_inode_setattr()
(suggested by Mimi)
- Modify short description of security_key_post_create_or_update()
(suggested by Mimi)
- Add back exported functions ima_file_check() and
evm_inode_init_security() respectively to ima.h and evm.h (reported by
kernel robot)
- Remove extern from prototype declarations and fix style issues
- Remove unnecessary include of linux/lsm_hooks.h in ima_main.c and
ima_appraise.c

Roberto Sassu (25):
ima: Align ima_inode_post_setattr() definition with LSM infrastructure
ima: Align ima_file_mprotect() definition with LSM infrastructure
ima: Align ima_inode_setxattr() definition with LSM infrastructure
ima: Align ima_inode_removexattr() definition with LSM infrastructure
ima: Align ima_post_read_file() definition with LSM infrastructure
evm: Align evm_inode_post_setattr() definition with LSM infrastructure
evm: Align evm_inode_setxattr() definition with LSM infrastructure
evm: Align evm_inode_post_setxattr() definition with LSM
infrastructure
security: Align inode_setattr hook definition with EVM
security: Introduce inode_post_setattr hook
security: Introduce inode_post_removexattr hook
security: Introduce file_post_open hook
security: Introduce file_release hook
security: Introduce path_post_mknod hook
security: Introduce inode_post_create_tmpfile hook
security: Introduce inode_post_set_acl hook
security: Introduce inode_post_remove_acl hook
security: Introduce key_post_create_or_update hook
integrity: Move integrity_kernel_module_request() to IMA
ima: Move to LSM infrastructure
ima: Move IMA-Appraisal to LSM infrastructure
evm: Move to LSM infrastructure
evm: Make it independent from 'integrity' LSM
ima: Make it independent from 'integrity' LSM
integrity: Remove LSM

fs/attr.c | 5 +-
fs/file_table.c | 3 +-
fs/namei.c | 12 +-
fs/nfsd/vfs.c | 3 +-
fs/open.c | 1 -
fs/posix_acl.c | 5 +-
fs/xattr.c | 9 +-
include/linux/evm.h | 117 +-------
include/linux/ima.h | 142 ----------
include/linux/integrity.h | 27 --
include/linux/lsm_hook_defs.h | 20 +-
include/linux/security.h | 59 ++++
include/uapi/linux/lsm.h | 2 +
security/integrity/Makefile | 1 +
security/integrity/digsig_asymmetric.c | 23 --
security/integrity/evm/Kconfig | 1 +
security/integrity/evm/evm.h | 19 ++
security/integrity/evm/evm_crypto.c | 4 +-
security/integrity/evm/evm_main.c | 195 ++++++++++---
security/integrity/iint.c | 197 +------------
security/integrity/ima/Kconfig | 1 +
security/integrity/ima/Makefile | 2 +-
security/integrity/ima/ima.h | 148 ++++++++--
security/integrity/ima/ima_api.c | 23 +-
security/integrity/ima/ima_appraise.c | 66 +++--
security/integrity/ima/ima_iint.c | 142 ++++++++++
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 144 +++++++---
security/integrity/ima/ima_policy.c | 2 +-
security/integrity/integrity.h | 80 +-----
security/keys/key.c | 10 +-
security/security.c | 263 +++++++++++-------
security/selinux/hooks.c | 3 +-
security/smack/smack_lsm.c | 4 +-
.../selftests/lsm/lsm_list_modules_test.c | 6 +
35 files changed, 902 insertions(+), 839 deletions(-)
create mode 100644 security/integrity/ima/ima_iint.c

--
2.34.1



2024-01-15 18:19:26

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 01/25] ima: Align ima_inode_post_setattr() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change ima_inode_post_setattr() definition, so that it can be registered as
implementation of the inode_post_setattr hook (to be introduced).

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/attr.c | 2 +-
include/linux/ima.h | 4 ++--
security/integrity/ima/ima_appraise.c | 3 ++-
3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index bdf5deb06ea9..9bddc0a6352c 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -502,7 +502,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,

if (!error) {
fsnotify_change(dentry, ia_valid);
- ima_inode_post_setattr(idmap, dentry);
+ ima_inode_post_setattr(idmap, dentry, ia_valid);
evm_inode_post_setattr(dentry, ia_valid);
}

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 86b57757c7b1..910a2f11a906 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -186,7 +186,7 @@ static inline void ima_post_key_create_or_update(struct key *keyring,
#ifdef CONFIG_IMA_APPRAISE
extern bool is_ima_appraise_enabled(void);
extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry);
+ struct dentry *dentry, int ia_valid);
extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len);
extern int ima_inode_set_acl(struct mnt_idmap *idmap,
@@ -206,7 +206,7 @@ static inline bool is_ima_appraise_enabled(void)
}

static inline void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry)
+ struct dentry *dentry, int ia_valid)
{
return;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 870dde67707b..36c2938a5c69 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -629,6 +629,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
* ima_inode_post_setattr - reflect file metadata changes
* @idmap: idmap of the mount the inode was found from
* @dentry: pointer to the affected dentry
+ * @ia_valid: for the UID and GID status
*
* Changes to a dentry's metadata might result in needing to appraise.
*
@@ -636,7 +637,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
* to lock the inode's i_mutex.
*/
void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry)
+ struct dentry *dentry, int ia_valid)
{
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
--
2.34.1


2024-01-15 18:19:51

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 02/25] ima: Align ima_file_mprotect() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change ima_file_mprotect() definition, so that it can be registered
as implementation of the file_mprotect hook.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
include/linux/ima.h | 5 +++--
security/integrity/ima/ima_main.c | 6 ++++--
security/security.c | 2 +-
3 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 910a2f11a906..b66353f679e8 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -23,7 +23,8 @@ extern void ima_post_create_tmpfile(struct mnt_idmap *idmap,
extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags);
-extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot);
+extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot);
extern int ima_load_data(enum kernel_load_data_id id, bool contents);
extern int ima_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id id, char *description);
@@ -84,7 +85,7 @@ static inline int ima_file_mmap(struct file *file, unsigned long reqprot,
}

static inline int ima_file_mprotect(struct vm_area_struct *vma,
- unsigned long prot)
+ unsigned long reqprot, unsigned long prot)
{
return 0;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index cc1217ac2c6f..b3f5e8401056 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -455,7 +455,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
/**
* ima_file_mprotect - based on policy, limit mprotect change
* @vma: vm_area_struct protection is set to
- * @prot: contains the protection that will be applied by the kernel.
+ * @reqprot: protection requested by the application
+ * @prot: protection that will be applied by the kernel
*
* Files can be mmap'ed read/write and later changed to execute to circumvent
* IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore
@@ -465,7 +466,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
*
* On mprotect change success, return 0. On failure, return -EACESS.
*/
-int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
+int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
{
struct ima_template_desc *template = NULL;
struct file *file;
diff --git a/security/security.c b/security/security.c
index 013c847b28d0..0608679478ea 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2837,7 +2837,7 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot);
if (ret)
return ret;
- return ima_file_mprotect(vma, prot);
+ return ima_file_mprotect(vma, reqprot, prot);
}

/**
--
2.34.1


2024-01-15 18:20:11

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 03/25] ima: Align ima_inode_setxattr() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change ima_inode_setxattr() definition, so that it can be registered as
implementation of the inode_setxattr hook.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
---
include/linux/ima.h | 11 +++++++----
security/integrity/ima/ima_appraise.c | 5 +++--
security/security.c | 2 +-
3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index b66353f679e8..077324309c11 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -188,8 +188,9 @@ static inline void ima_post_key_create_or_update(struct key *keyring,
extern bool is_ima_appraise_enabled(void);
extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid);
-extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len);
+extern int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len, int flags);
extern int ima_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl);
@@ -212,10 +213,12 @@ static inline void ima_inode_post_setattr(struct mnt_idmap *idmap,
return;
}

-static inline int ima_inode_setxattr(struct dentry *dentry,
+static inline int ima_inode_setxattr(struct mnt_idmap *idmap,
+ struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
- size_t xattr_value_len)
+ size_t xattr_value_len,
+ int flags)
{
return 0;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 36c2938a5c69..cb2d0d11aa77 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -750,8 +750,9 @@ static int validate_hash_algo(struct dentry *dentry,
return -EACCES;
}

-int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len)
+int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xvalue = xattr_value;
int digsig = 0;
diff --git a/security/security.c b/security/security.c
index 0608679478ea..98531e9b368f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2269,7 +2269,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
ret = cap_inode_setxattr(dentry, name, value, size, flags);
if (ret)
return ret;
- ret = ima_inode_setxattr(dentry, name, value, size);
+ ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags);
if (ret)
return ret;
return evm_inode_setxattr(idmap, dentry, name, value, size);
--
2.34.1


2024-01-15 18:20:31

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 05/25] ima: Align ima_post_read_file() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change ima_post_read_file() definition, by making "void *buf" a
"char *buf", so that it can be registered as implementation of the
post_read_file hook.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
---
include/linux/ima.h | 4 ++--
security/integrity/ima/ima_main.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 678a03fddd7e..31ef6c3c3207 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -30,7 +30,7 @@ extern int ima_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id id, char *description);
extern int ima_read_file(struct file *file, enum kernel_read_file_id id,
bool contents);
-extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
+extern int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id);
extern void ima_post_path_mknod(struct mnt_idmap *idmap,
struct dentry *dentry);
@@ -108,7 +108,7 @@ static inline int ima_read_file(struct file *file, enum kernel_read_file_id id,
return 0;
}

-static inline int ima_post_read_file(struct file *file, void *buf, loff_t size,
+static inline int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id)
{
return 0;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index b3f5e8401056..02021ee467d3 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -803,7 +803,7 @@ const int read_idmap[READING_MAX_ID] = {
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_post_read_file(struct file *file, void *buf, loff_t size,
+int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id read_id)
{
enum ima_hooks func;
--
2.34.1


2024-01-15 18:20:50

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 06/25] evm: Align evm_inode_post_setattr() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change evm_inode_post_setattr() definition, so that it can be registered as
implementation of the inode_post_setattr hook (to be introduced).

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/attr.c | 2 +-
include/linux/evm.h | 6 ++++--
security/integrity/evm/evm_main.c | 4 +++-
3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 9bddc0a6352c..498e673bdf06 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -503,7 +503,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) {
fsnotify_change(dentry, ia_valid);
ima_inode_post_setattr(idmap, dentry, ia_valid);
- evm_inode_post_setattr(dentry, ia_valid);
+ evm_inode_post_setattr(idmap, dentry, ia_valid);
}

return error;
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 36ec884320d9..5cc386312b5a 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -23,7 +23,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
struct integrity_iint_cache *iint);
extern int evm_inode_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr);
-extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
+extern void evm_inode_post_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid);
extern int evm_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
const void *value, size_t size);
@@ -98,7 +99,8 @@ static inline int evm_inode_setattr(struct mnt_idmap *idmap,
return 0;
}

-static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+static inline void evm_inode_post_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid)
{
return;
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index cc7956d7878b..ac34f21122cd 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -870,6 +870,7 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,

/**
* evm_inode_post_setattr - update 'security.evm' after modifying metadata
+ * @idmap: idmap of the idmapped mount
* @dentry: pointer to the affected dentry
* @ia_valid: for the UID and GID status
*
@@ -879,7 +880,8 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
-void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ia_valid)
{
if (!evm_revalidate_status(NULL))
return;
--
2.34.1


2024-01-15 18:21:11

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 07/25] evm: Align evm_inode_setxattr() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change evm_inode_setxattr() definition, so that it can be registered as
implementation of the inode_setxattr hook.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
---
include/linux/evm.h | 4 ++--
security/integrity/evm/evm_main.c | 3 ++-
security/security.c | 2 +-
3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/include/linux/evm.h b/include/linux/evm.h
index 5cc386312b5a..7de24c1ada90 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -27,7 +27,7 @@ extern void evm_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid);
extern int evm_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
- const void *value, size_t size);
+ const void *value, size_t size, int flags);
extern void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
@@ -107,7 +107,7 @@ static inline void evm_inode_post_setattr(struct mnt_idmap *idmap,

static inline int evm_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
- const void *value, size_t size)
+ const void *value, size_t size, int flags)
{
return 0;
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index ac34f21122cd..12ba3207fd31 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -581,6 +581,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
+ * @flags: flags to pass into filesystem operations
*
* Before allowing the 'security.evm' protected xattr to be updated,
* verify the existing value is valid. As only the kernel should have
@@ -590,7 +591,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
*/
int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name, const void *xattr_value,
- size_t xattr_value_len)
+ size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xattr_data = xattr_value;

diff --git a/security/security.c b/security/security.c
index fda78023de48..f0d1f8f0da80 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2272,7 +2272,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags);
if (ret)
return ret;
- return evm_inode_setxattr(idmap, dentry, name, value, size);
+ return evm_inode_setxattr(idmap, dentry, name, value, size, flags);
}

/**
--
2.34.1


2024-01-15 18:22:02

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 08/25] evm: Align evm_inode_post_setxattr() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change evm_inode_post_setxattr() definition, so that it can be registered
as implementation of the inode_post_setxattr hook.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
---
include/linux/evm.h | 8 +++++---
security/integrity/evm/evm_main.c | 4 +++-
security/security.c | 2 +-
3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/include/linux/evm.h b/include/linux/evm.h
index 7de24c1ada90..3faabdd47852 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -31,7 +31,8 @@ extern int evm_inode_setxattr(struct mnt_idmap *idmap,
extern void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
- size_t xattr_value_len);
+ size_t xattr_value_len,
+ int flags);
extern int evm_inode_copy_up_xattr(const char *name);
extern int evm_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *xattr_name);
@@ -56,7 +57,7 @@ static inline void evm_inode_post_set_acl(struct dentry *dentry,
const char *acl_name,
struct posix_acl *kacl)
{
- return evm_inode_post_setxattr(dentry, acl_name, NULL, 0);
+ return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
}

int evm_inode_init_security(struct inode *inode, struct inode *dir,
@@ -115,7 +116,8 @@ static inline int evm_inode_setxattr(struct mnt_idmap *idmap,
static inline void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
- size_t xattr_value_len)
+ size_t xattr_value_len,
+ int flags)
{
return;
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 12ba3207fd31..d35143179699 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -753,6 +753,7 @@ bool evm_revalidate_status(const char *xattr_name)
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
+ * @flags: flags to pass into filesystem operations
*
* Update the HMAC stored in 'security.evm' to reflect the change.
*
@@ -761,7 +762,8 @@ bool evm_revalidate_status(const char *xattr_name)
* i_mutex lock.
*/
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len)
+ const void *xattr_value, size_t xattr_value_len,
+ int flags)
{
if (!evm_revalidate_status(xattr_name))
return;
diff --git a/security/security.c b/security/security.c
index f0d1f8f0da80..ab847af2c74d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2367,7 +2367,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return;
call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
- evm_inode_post_setxattr(dentry, name, value, size);
+ evm_inode_post_setxattr(dentry, name, value, size, flags);
}

/**
--
2.34.1


2024-01-15 18:22:36

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 09/25] security: Align inode_setattr hook definition with EVM

From: Roberto Sassu <[email protected]>

Add the idmap parameter to the definition, so that evm_inode_setattr() can
be registered as this hook implementation.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
include/linux/lsm_hook_defs.h | 3 ++-
security/security.c | 2 +-
security/selinux/hooks.c | 3 ++-
security/smack/smack_lsm.c | 4 +++-
4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 185924c56378..a843bb5f6f4b 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -135,7 +135,8 @@ LSM_HOOK(int, 0, inode_readlink, struct dentry *dentry)
LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode,
bool rcu)
LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask)
-LSM_HOOK(int, 0, inode_setattr, struct dentry *dentry, struct iattr *attr)
+LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr)
LSM_HOOK(int, 0, inode_getattr, const struct path *path)
LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, const void *value,
diff --git a/security/security.c b/security/security.c
index ab847af2c74d..1d332e294704 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2215,7 +2215,7 @@ int security_inode_setattr(struct mnt_idmap *idmap,

if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_setattr, 0, dentry, attr);
+ ret = call_int_hook(inode_setattr, 0, idmap, dentry, attr);
if (ret)
return ret;
return evm_inode_setattr(idmap, dentry, attr);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 179540441115..dae53d7778bb 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3128,7 +3128,8 @@ static int selinux_inode_permission(struct inode *inode, int mask)
return rc;
}

-static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *iattr)
{
const struct cred *cred = current_cred();
struct inode *inode = d_backing_inode(dentry);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index c126f6a16de4..9b75216d4a58 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1232,12 +1232,14 @@ static int smack_inode_permission(struct inode *inode, int mask)

/**
* smack_inode_setattr - Smack check for setting attributes
+ * @idmap: idmap of the mount
* @dentry: the object
* @iattr: for the force flag
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *iattr)
{
struct smk_audit_info ad;
int rc;
--
2.34.1


2024-01-15 18:23:14

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 10/25] security: Introduce inode_post_setattr hook

From: Roberto Sassu <[email protected]>

In preparation for moving IMA and EVM to the LSM infrastructure, introduce
the inode_post_setattr hook.

At inode_setattr hook, EVM verifies the file's existing HMAC value. At
inode_post_setattr, EVM re-calculates the file's HMAC based on the modified
file attributes and other file metadata.

Other LSMs could similarly take some action after successful file attribute
change.

The new hook cannot return an error and cannot cause the operation to be
reverted.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
---
fs/attr.c | 1 +
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 7 +++++++
security/security.c | 16 ++++++++++++++++
4 files changed, 26 insertions(+)

diff --git a/fs/attr.c b/fs/attr.c
index 498e673bdf06..221d2bb0a906 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -502,6 +502,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,

if (!error) {
fsnotify_change(dentry, ia_valid);
+ security_inode_post_setattr(idmap, dentry, ia_valid);
ima_inode_post_setattr(idmap, dentry, ia_valid);
evm_inode_post_setattr(idmap, dentry, ia_valid);
}
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index a843bb5f6f4b..e1731d0b63f8 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -137,6 +137,8 @@ LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode,
LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask)
LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr)
+LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid)
LSM_HOOK(int, 0, inode_getattr, const struct path *path)
LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, const void *value,
diff --git a/include/linux/security.h b/include/linux/security.h
index d0eb20f90b26..56c841aa3994 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -361,6 +361,8 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
int security_inode_permission(struct inode *inode, int mask);
int security_inode_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr);
+void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ia_valid);
int security_inode_getattr(const struct path *path);
int security_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
@@ -879,6 +881,11 @@ static inline int security_inode_setattr(struct mnt_idmap *idmap,
return 0;
}

+static inline void
+security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ia_valid)
+{ }
+
static inline int security_inode_getattr(const struct path *path)
{
return 0;
diff --git a/security/security.c b/security/security.c
index 1d332e294704..06ffe0bcf711 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2222,6 +2222,22 @@ int security_inode_setattr(struct mnt_idmap *idmap,
}
EXPORT_SYMBOL_GPL(security_inode_setattr);

+/**
+ * security_inode_post_setattr() - Update the inode after a setattr operation
+ * @idmap: idmap of the mount
+ * @dentry: file
+ * @ia_valid: file attributes set
+ *
+ * Update inode security field after successful setting file attributes.
+ */
+void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ia_valid)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_setattr, idmap, dentry, ia_valid);
+}
+
/**
* security_inode_getattr() - Check if getting file attributes is allowed
* @path: file
--
2.34.1


2024-01-15 18:23:33

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 11/25] security: Introduce inode_post_removexattr hook

From: Roberto Sassu <[email protected]>

In preparation for moving IMA and EVM to the LSM infrastructure, introduce
the inode_post_removexattr hook.

At inode_removexattr hook, EVM verifies the file's existing HMAC value. At
inode_post_removexattr, EVM re-calculates the file's HMAC with the passed
xattr removed and other file metadata.

Other LSMs could similarly take some action after successful xattr removal.

The new hook cannot return an error and cannot cause the operation to be
reverted.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
---
fs/xattr.c | 9 +++++----
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 5 +++++
security/security.c | 14 ++++++++++++++
4 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 09d927603433..f891c260a971 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -552,11 +552,12 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,
goto out;

error = __vfs_removexattr(idmap, dentry, name);
+ if (error)
+ return error;

- if (!error) {
- fsnotify_xattr(dentry);
- evm_inode_post_removexattr(dentry, name);
- }
+ fsnotify_xattr(dentry);
+ security_inode_post_removexattr(dentry, name);
+ evm_inode_post_removexattr(dentry, name);

out:
return error;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index e1731d0b63f8..fbca96523c18 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -149,6 +149,8 @@ LSM_HOOK(int, 0, inode_getxattr, struct dentry *dentry, const char *name)
LSM_HOOK(int, 0, inode_listxattr, struct dentry *dentry)
LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
+LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
+ const char *name)
LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap,
diff --git a/include/linux/security.h b/include/linux/security.h
index 56c841aa3994..84ae03690340 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -380,6 +380,7 @@ int security_inode_getxattr(struct dentry *dentry, const char *name);
int security_inode_listxattr(struct dentry *dentry);
int security_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name);
+void security_inode_post_removexattr(struct dentry *dentry, const char *name);
int security_inode_need_killpriv(struct dentry *dentry);
int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry);
int security_inode_getsecurity(struct mnt_idmap *idmap,
@@ -942,6 +943,10 @@ static inline int security_inode_removexattr(struct mnt_idmap *idmap,
return cap_inode_removexattr(idmap, dentry, name);
}

+static inline void security_inode_post_removexattr(struct dentry *dentry,
+ const char *name)
+{ }
+
static inline int security_inode_need_killpriv(struct dentry *dentry)
{
return cap_inode_need_killpriv(dentry);
diff --git a/security/security.c b/security/security.c
index 06ffe0bcf711..d9d2636104db 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2452,6 +2452,20 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
return evm_inode_removexattr(idmap, dentry, name);
}

+/**
+ * security_inode_post_removexattr() - Update the inode after a removexattr op
+ * @dentry: file
+ * @name: xattr name
+ *
+ * Update the inode after a successful removexattr operation.
+ */
+void security_inode_post_removexattr(struct dentry *dentry, const char *name)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_removexattr, dentry, name);
+}
+
/**
* security_inode_need_killpriv() - Check if security_inode_killpriv() required
* @dentry: associated dentry
--
2.34.1


2024-01-15 18:23:56

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 12/25] security: Introduce file_post_open hook

From: Roberto Sassu <[email protected]>

In preparation to move IMA and EVM to the LSM infrastructure, introduce the
file_post_open hook. Also, export security_file_post_open() for NFS.

Based on policy, IMA calculates the digest of the file content and
extends the TPM with the digest, verifies the file's integrity based on
the digest, and/or includes the file digest in the audit log.

LSMs could similarly take action depending on the file content and the
access mask requested with open().

The new hook returns a value and can cause the open to be aborted.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/namei.c | 2 ++
fs/nfsd/vfs.c | 6 ++++++
include/linux/lsm_hook_defs.h | 1 +
include/linux/security.h | 6 ++++++
security/security.c | 17 +++++++++++++++++
5 files changed, 32 insertions(+)

diff --git a/fs/namei.c b/fs/namei.c
index 71c13b2990b4..fb93d3e13df6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd,
error = may_open(idmap, &nd->path, acc_mode, open_flag);
if (!error && !(file->f_mode & FMODE_OPENED))
error = vfs_open(&nd->path, file);
+ if (!error)
+ error = security_file_post_open(file, op->acc_mode);
if (!error)
error = ima_file_check(file, op->acc_mode);
if (!error && do_truncate)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index fbbea7498f02..b0c3f07a8bba 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -877,6 +877,12 @@ __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
goto out;
}

+ host_err = security_file_post_open(file, may_flags);
+ if (host_err) {
+ fput(file);
+ goto out;
+ }
+
host_err = ima_file_check(file, may_flags);
if (host_err) {
fput(file);
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index fbca96523c18..c3fecc7dcb0b 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -191,6 +191,7 @@ LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk,
struct fown_struct *fown, int sig)
LSM_HOOK(int, 0, file_receive, struct file *file)
LSM_HOOK(int, 0, file_open, struct file *file)
+LSM_HOOK(int, 0, file_post_open, struct file *file, int mask)
LSM_HOOK(int, 0, file_truncate, struct file *file)
LSM_HOOK(int, 0, task_alloc, struct task_struct *task,
unsigned long clone_flags)
diff --git a/include/linux/security.h b/include/linux/security.h
index 84ae03690340..97f2212c13b6 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -411,6 +411,7 @@ int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig);
int security_file_receive(struct file *file);
int security_file_open(struct file *file);
+int security_file_post_open(struct file *file, int mask);
int security_file_truncate(struct file *file);
int security_task_alloc(struct task_struct *task, unsigned long clone_flags);
void security_task_free(struct task_struct *task);
@@ -1074,6 +1075,11 @@ static inline int security_file_open(struct file *file)
return 0;
}

+static inline int security_file_post_open(struct file *file, int mask)
+{
+ return 0;
+}
+
static inline int security_file_truncate(struct file *file)
{
return 0;
diff --git a/security/security.c b/security/security.c
index d9d2636104db..f3d92bffd02f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2972,6 +2972,23 @@ int security_file_open(struct file *file)
return fsnotify_perm(file, MAY_OPEN);
}

+/**
+ * security_file_post_open() - Evaluate a file after it has been opened
+ * @file: the file
+ * @mask: access mask
+ *
+ * Evaluate an opened file and the access mask requested with open(). The hook
+ * is useful for LSMs that require the file content to be available in order to
+ * make decisions.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_file_post_open(struct file *file, int mask)
+{
+ return call_int_hook(file_post_open, 0, file, mask);
+}
+EXPORT_SYMBOL_GPL(security_file_post_open);
+
/**
* security_file_truncate() - Check if truncating a file is allowed
* @file: file
--
2.34.1


2024-01-15 18:24:16

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 14/25] security: Introduce path_post_mknod hook

From: Roberto Sassu <[email protected]>

In preparation for moving IMA and EVM to the LSM infrastructure, introduce
the path_post_mknod hook.

IMA-appraisal requires all existing files in policy to have a file
hash/signature stored in security.ima. An exception is made for empty files
created by mknod, by tagging them as new files.

LSMs could also take some action after files are created.

The new hook cannot return an error and cannot cause the operation to be
reverted.

Signed-off-by: Roberto Sassu <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/namei.c | 5 +++++
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 5 +++++
security/security.c | 14 ++++++++++++++
4 files changed, 26 insertions(+)

diff --git a/fs/namei.c b/fs/namei.c
index fb93d3e13df6..b7f433720b1e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4047,6 +4047,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
dentry, mode, 0);
break;
}
+
+ if (error)
+ goto out2;
+
+ security_path_post_mknod(idmap, dentry);
out2:
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 229f84ce12ae..e08b9091350d 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -94,6 +94,8 @@ LSM_HOOK(int, 0, path_mkdir, const struct path *dir, struct dentry *dentry,
LSM_HOOK(int, 0, path_rmdir, const struct path *dir, struct dentry *dentry)
LSM_HOOK(int, 0, path_mknod, const struct path *dir, struct dentry *dentry,
umode_t mode, unsigned int dev)
+LSM_HOOK(void, LSM_RET_VOID, path_post_mknod, struct mnt_idmap *idmap,
+ struct dentry *dentry)
LSM_HOOK(int, 0, path_truncate, const struct path *path)
LSM_HOOK(int, 0, path_symlink, const struct path *dir, struct dentry *dentry,
const char *old_name)
diff --git a/include/linux/security.h b/include/linux/security.h
index 2997348afcb7..977dd9f7f51a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1893,6 +1893,7 @@ int security_path_mkdir(const struct path *dir, struct dentry *dentry, umode_t m
int security_path_rmdir(const struct path *dir, struct dentry *dentry);
int security_path_mknod(const struct path *dir, struct dentry *dentry, umode_t mode,
unsigned int dev);
+void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry);
int security_path_truncate(const struct path *path);
int security_path_symlink(const struct path *dir, struct dentry *dentry,
const char *old_name);
@@ -1927,6 +1928,10 @@ static inline int security_path_mknod(const struct path *dir, struct dentry *den
return 0;
}

+static inline void security_path_post_mknod(struct mnt_idmap *idmap,
+ struct dentry *dentry)
+{ }
+
static inline int security_path_truncate(const struct path *path)
{
return 0;
diff --git a/security/security.c b/security/security.c
index 7d10724872f8..750bfe2768d5 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1800,6 +1800,20 @@ int security_path_mknod(const struct path *dir, struct dentry *dentry,
}
EXPORT_SYMBOL(security_path_mknod);

+/**
+ * security_path_post_mknod() - Update inode security field after file creation
+ * @idmap: idmap of the mount
+ * @dentry: new file
+ *
+ * Update inode security field after a file has been created.
+ */
+void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(path_post_mknod, idmap, dentry);
+}
+
/**
* security_path_mkdir() - Check if creating a new directory is allowed
* @dir: parent directory
--
2.34.1


2024-01-15 18:25:00

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 16/25] security: Introduce inode_post_set_acl hook

From: Roberto Sassu <[email protected]>

In preparation for moving IMA and EVM to the LSM infrastructure, introduce
the inode_post_set_acl hook.

At inode_set_acl hook, EVM verifies the file's existing HMAC value. At
inode_post_set_acl, EVM re-calculates the file's HMAC based on the modified
POSIX ACL and other file metadata.

Other LSMs could similarly take some action after successful POSIX ACL
change.

The new hook cannot return an error and cannot cause the operation to be
reverted.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/posix_acl.c | 1 +
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 7 +++++++
security/security.c | 17 +++++++++++++++++
4 files changed, 27 insertions(+)

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index a05fe94970ce..58e3c1e2fbbc 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -1137,6 +1137,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
error = -EIO;
if (!error) {
fsnotify_xattr(dentry);
+ security_inode_post_set_acl(dentry, acl_name, kacl);
evm_inode_post_set_acl(dentry, acl_name, kacl);
}

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 5f90914d23e0..e1a9a4e0760c 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -157,6 +157,8 @@ LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
const char *name)
LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
+LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
LSM_HOOK(int, 0, inode_remove_acl, struct mnt_idmap *idmap,
diff --git a/include/linux/security.h b/include/linux/security.h
index 1cb604282617..c372797e1617 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -372,6 +372,8 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
int security_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl);
+void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl);
int security_inode_get_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name);
int security_inode_remove_acl(struct mnt_idmap *idmap,
@@ -915,6 +917,11 @@ static inline int security_inode_set_acl(struct mnt_idmap *idmap,
return 0;
}

+static inline void security_inode_post_set_acl(struct dentry *dentry,
+ const char *acl_name,
+ struct posix_acl *kacl)
+{ }
+
static inline int security_inode_get_acl(struct mnt_idmap *idmap,
struct dentry *dentry,
const char *acl_name)
diff --git a/security/security.c b/security/security.c
index 5bc7edc22923..82da3667c4ae 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2350,6 +2350,23 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
}

+/**
+ * security_inode_post_set_acl() - Update inode security from posix acls set
+ * @dentry: file
+ * @acl_name: acl name
+ * @kacl: acl struct
+ *
+ * Update inode security data after successfully setting posix acls on @dentry.
+ * The posix acls in @kacl are identified by @acl_name.
+ */
+void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_set_acl, dentry, acl_name, kacl);
+}
+
/**
* security_inode_get_acl() - Check if reading posix acls is allowed
* @idmap: idmap of the mount
--
2.34.1


2024-01-15 18:25:23

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 17/25] security: Introduce inode_post_remove_acl hook

From: Roberto Sassu <[email protected]>

In preparation for moving IMA and EVM to the LSM infrastructure, introduce
the inode_post_remove_acl hook.

At inode_remove_acl hook, EVM verifies the file's existing HMAC value. At
inode_post_remove_acl, EVM re-calculates the file's HMAC with the passed
POSIX ACL removed and other file metadata.

Other LSMs could similarly take some action after successful POSIX ACL
removal.

The new hook cannot return an error and cannot cause the operation to be
reverted.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/posix_acl.c | 1 +
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 8 ++++++++
security/security.c | 17 +++++++++++++++++
4 files changed, 28 insertions(+)

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 58e3c1e2fbbc..e3fbe1a9f3f5 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -1246,6 +1246,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
error = -EIO;
if (!error) {
fsnotify_xattr(dentry);
+ security_inode_post_remove_acl(idmap, dentry, acl_name);
evm_inode_post_remove_acl(idmap, dentry, acl_name);
}

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index e1a9a4e0760c..47dc633090a4 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -163,6 +163,8 @@ LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
LSM_HOOK(int, 0, inode_remove_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
+LSM_HOOK(void, LSM_RET_VOID, inode_post_remove_acl, struct mnt_idmap *idmap,
+ struct dentry *dentry, const char *acl_name)
LSM_HOOK(int, 0, inode_need_killpriv, struct dentry *dentry)
LSM_HOOK(int, 0, inode_killpriv, struct mnt_idmap *idmap,
struct dentry *dentry)
diff --git a/include/linux/security.h b/include/linux/security.h
index c372797e1617..4b03c76b91f1 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -378,6 +378,9 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name);
int security_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name);
+void security_inode_post_remove_acl(struct mnt_idmap *idmap,
+ struct dentry *dentry,
+ const char *acl_name);
void security_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags);
int security_inode_getxattr(struct dentry *dentry, const char *name);
@@ -936,6 +939,11 @@ static inline int security_inode_remove_acl(struct mnt_idmap *idmap,
return 0;
}

+static inline void security_inode_post_remove_acl(struct mnt_idmap *idmap,
+ struct dentry *dentry,
+ const char *acl_name)
+{ }
+
static inline void security_inode_post_setxattr(struct dentry *dentry,
const char *name, const void *value, size_t size, int flags)
{ }
diff --git a/security/security.c b/security/security.c
index 82da3667c4ae..617acfaa44d1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2413,6 +2413,23 @@ int security_inode_remove_acl(struct mnt_idmap *idmap,
return evm_inode_remove_acl(idmap, dentry, acl_name);
}

+/**
+ * security_inode_post_remove_acl() - Update inode security after rm posix acls
+ * @idmap: idmap of the mount
+ * @dentry: file
+ * @acl_name: acl name
+ *
+ * Update inode security data after successfully removing posix acls on
+ * @dentry in @idmap. The posix acls are identified by @acl_name.
+ */
+void security_inode_post_remove_acl(struct mnt_idmap *idmap,
+ struct dentry *dentry, const char *acl_name)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_remove_acl, idmap, dentry, acl_name);
+}
+
/**
* security_inode_post_setxattr() - Update the inode after a setxattr operation
* @dentry: file
--
2.34.1


2024-01-15 18:26:16

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA

From: Roberto Sassu <[email protected]>

In preparation for removing the 'integrity' LSM, move
integrity_kernel_module_request() to IMA, and rename it to
ima_kernel_module_request().

Compile it conditionally if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled,
and call it from security.c (removed afterwards with the move of IMA to the
LSM infrastructure).

Adding this hook cannot be avoided, since IMA has no control on the flags
passed to crypto_alloc_sig() in public_key_verify_signature(), and thus
cannot pass CRYPTO_NOLOAD, which solved the problem for EVM hashing with
commit e2861fa71641 ("evm: Don't deadlock if a crypto algorithm is
unavailable").

EVM alone does not need to implement this hook, first because there is no
mutex to deadlock, and second because even if it had it, there should be a
recursive call. However, since verification from EVM can be initiated only
by setting inode metadata, deadlock would occur if modprobe would do the
same while loading a kernel module (which is unlikely).

Signed-off-by: Roberto Sassu <[email protected]>
---
include/linux/ima.h | 10 +++++++++
include/linux/integrity.h | 13 ------------
security/integrity/digsig_asymmetric.c | 23 --------------------
security/integrity/ima/ima_main.c | 29 ++++++++++++++++++++++++++
security/security.c | 2 +-
5 files changed, 40 insertions(+), 37 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 31ef6c3c3207..0f9af283cbc8 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -256,4 +256,14 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
return false;
}
#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+
+#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS)
+extern int ima_kernel_module_request(char *kmod_name);
+#else
+static inline int ima_kernel_module_request(char *kmod_name)
+{
+ return 0;
+}
+
+#endif
#endif /* _LINUX_IMA_H */
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2ea0f2f65ab6..ef0f63ef5ebc 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -42,17 +42,4 @@ static inline void integrity_load_keys(void)
}
#endif /* CONFIG_INTEGRITY */

-#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
-
-extern int integrity_kernel_module_request(char *kmod_name);
-
-#else
-
-static inline int integrity_kernel_module_request(char *kmod_name)
-{
- return 0;
-}
-
-#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
-
#endif /* _LINUX_INTEGRITY_H */
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 895f4b9ce8c6..de603cf42ac7 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -132,26 +132,3 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
-
-/**
- * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests
- * @kmod_name: kernel module name
- *
- * We have situation, when public_key_verify_signature() in case of RSA
- * algorithm use alg_name to store internal information in order to
- * construct an algorithm on the fly, but crypto_larval_lookup() will try
- * to use alg_name in order to load kernel module with same name.
- * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- * we are safe to fail such module request from crypto_larval_lookup().
- *
- * In this way we prevent modprobe execution during digsig verification
- * and avoid possible deadlock if modprobe and/or it's dependencies
- * also signed with digsig.
- */
-int integrity_kernel_module_request(char *kmod_name)
-{
- if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
- return -EINVAL;
-
- return 0;
-}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 02021ee467d3..908fa026ec58 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1091,6 +1091,35 @@ int ima_measure_critical_data(const char *event_label,
}
EXPORT_SYMBOL_GPL(ima_measure_critical_data);

+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+
+/**
+ * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
+ * @kmod_name: kernel module name
+ *
+ * We have situation, when public_key_verify_signature() in case of RSA
+ * algorithm use alg_name to store internal information in order to
+ * construct an algorithm on the fly, but crypto_larval_lookup() will try
+ * to use alg_name in order to load kernel module with same name.
+ * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
+ * we are safe to fail such module request from crypto_larval_lookup().
+ *
+ * In this way we prevent modprobe execution during digsig verification
+ * and avoid possible deadlock if modprobe and/or it's dependencies
+ * also signed with digsig.
+ *
+ * Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
+ */
+int ima_kernel_module_request(char *kmod_name)
+{
+ if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
+
static int __init init_ima(void)
{
int error;
diff --git a/security/security.c b/security/security.c
index d2a1226e6e69..6c6571a141a1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -3255,7 +3255,7 @@ int security_kernel_module_request(char *kmod_name)
ret = call_int_hook(kernel_module_request, 0, kmod_name);
if (ret)
return ret;
- return integrity_kernel_module_request(kmod_name);
+ return ima_kernel_module_request(kmod_name);
}

/**
--
2.34.1


2024-01-15 18:26:52

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 20/25] ima: Move to LSM infrastructure

From: Roberto Sassu <[email protected]>

Move hardcoded IMA function calls (not appraisal-specific functions) from
various places in the kernel to the LSM infrastructure, by introducing a
new LSM named 'ima' (at the end of the LSM list and always enabled like
'integrity').

Having IMA before EVM in the Makefile is sufficient to preserve the
relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
and thus the order of IMA and EVM function calls as when they were
hardcoded.

Make moved functions as static (except ima_post_key_create_or_update(),
which is not in ima_main.c), and register them as implementation of the
respective hooks in the new function init_ima_lsm().

Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook
path_post_mknod is always available and ima_post_path_mknod() is always
executed to mark files as new, as before the move.

A slight difference is that IMA and EVM functions registered for the
inode_post_setattr, inode_post_removexattr, path_post_mknod,
inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
won't be executed for private inodes. Since those inodes are supposed to be
fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
and kernel-internal tmpfs files.

Conditionally register ima_post_key_create_or_update() if
CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register
ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.

Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.

Signed-off-by: Roberto Sassu <[email protected]>
Acked-by: Chuck Lever <[email protected]>
---
fs/file_table.c | 2 -
fs/namei.c | 6 -
fs/nfsd/vfs.c | 7 --
fs/open.c | 1 -
include/linux/ima.h | 104 ------------------
include/uapi/linux/lsm.h | 1 +
security/integrity/Makefile | 1 +
security/integrity/ima/Kconfig | 1 +
security/integrity/ima/ima.h | 6 +
security/integrity/ima/ima_main.c | 78 +++++++++----
security/integrity/integrity.h | 1 +
security/keys/key.c | 9 +-
security/security.c | 63 ++---------
.../selftests/lsm/lsm_list_modules_test.c | 3 +
14 files changed, 83 insertions(+), 200 deletions(-)

diff --git a/fs/file_table.c b/fs/file_table.c
index c72dc75f2bd3..0401ac98281c 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -26,7 +26,6 @@
#include <linux/percpu_counter.h>
#include <linux/percpu.h>
#include <linux/task_work.h>
-#include <linux/ima.h>
#include <linux/swap.h>
#include <linux/kmemleak.h>

@@ -386,7 +385,6 @@ static void __fput(struct file *file)
locks_remove_file(file);

security_file_release(file);
- ima_file_free(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
diff --git a/fs/namei.c b/fs/namei.c
index adb3ab27951a..37cc0988308f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -27,7 +27,6 @@
#include <linux/fsnotify.h>
#include <linux/personality.h>
#include <linux/security.h>
-#include <linux/ima.h>
#include <linux/syscalls.h>
#include <linux/mount.h>
#include <linux/audit.h>
@@ -3622,8 +3621,6 @@ static int do_open(struct nameidata *nd,
error = vfs_open(&nd->path, file);
if (!error)
error = security_file_post_open(file, op->acc_mode);
- if (!error)
- error = ima_file_check(file, op->acc_mode);
if (!error && do_truncate)
error = handle_truncate(idmap, file);
if (unlikely(error > 0)) {
@@ -3687,7 +3684,6 @@ static int vfs_tmpfile(struct mnt_idmap *idmap,
spin_unlock(&inode->i_lock);
}
security_inode_post_create_tmpfile(idmap, inode);
- ima_post_create_tmpfile(idmap, inode);
return 0;
}

@@ -4036,8 +4032,6 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
case 0: case S_IFREG:
error = vfs_create(idmap, path.dentry->d_inode,
dentry, mode, true);
- if (!error)
- ima_post_path_mknod(idmap, dentry);
break;
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(idmap, path.dentry->d_inode,
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index b0c3f07a8bba..e491392a1243 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -25,7 +25,6 @@
#include <linux/posix_acl_xattr.h>
#include <linux/xattr.h>
#include <linux/jhash.h>
-#include <linux/ima.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -883,12 +882,6 @@ __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
goto out;
}

- host_err = ima_file_check(file, may_flags);
- if (host_err) {
- fput(file);
- goto out;
- }
-
if (may_flags & NFSD_MAY_64BIT_COOKIE)
file->f_mode |= FMODE_64BITHASH;
else
diff --git a/fs/open.c b/fs/open.c
index 02dc608d40d8..c8bb9bd5259f 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -29,7 +29,6 @@
#include <linux/audit.h>
#include <linux/falloc.h>
#include <linux/fs_struct.h>
-#include <linux/ima.h>
#include <linux/dnotify.h>
#include <linux/compat.h>
#include <linux/mnt_idmapping.h>
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 0f9af283cbc8..23ae24b60ecf 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -16,24 +16,6 @@ struct linux_binprm;

#ifdef CONFIG_IMA
extern enum hash_algo ima_get_current_hash_algo(void);
-extern int ima_bprm_check(struct linux_binprm *bprm);
-extern int ima_file_check(struct file *file, int mask);
-extern void ima_post_create_tmpfile(struct mnt_idmap *idmap,
- struct inode *inode);
-extern void ima_file_free(struct file *file);
-extern int ima_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags);
-extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
- unsigned long prot);
-extern int ima_load_data(enum kernel_load_data_id id, bool contents);
-extern int ima_post_load_data(char *buf, loff_t size,
- enum kernel_load_data_id id, char *description);
-extern int ima_read_file(struct file *file, enum kernel_read_file_id id,
- bool contents);
-extern int ima_post_read_file(struct file *file, char *buf, loff_t size,
- enum kernel_read_file_id id);
-extern void ima_post_path_mknod(struct mnt_idmap *idmap,
- struct dentry *dentry);
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size);
extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
@@ -58,68 +40,6 @@ static inline enum hash_algo ima_get_current_hash_algo(void)
return HASH_ALGO__LAST;
}

-static inline int ima_bprm_check(struct linux_binprm *bprm)
-{
- return 0;
-}
-
-static inline int ima_file_check(struct file *file, int mask)
-{
- return 0;
-}
-
-static inline void ima_post_create_tmpfile(struct mnt_idmap *idmap,
- struct inode *inode)
-{
-}
-
-static inline void ima_file_free(struct file *file)
-{
- return;
-}
-
-static inline int ima_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
-{
- return 0;
-}
-
-static inline int ima_file_mprotect(struct vm_area_struct *vma,
- unsigned long reqprot, unsigned long prot)
-{
- return 0;
-}
-
-static inline int ima_load_data(enum kernel_load_data_id id, bool contents)
-{
- return 0;
-}
-
-static inline int ima_post_load_data(char *buf, loff_t size,
- enum kernel_load_data_id id,
- char *description)
-{
- return 0;
-}
-
-static inline int ima_read_file(struct file *file, enum kernel_read_file_id id,
- bool contents)
-{
- return 0;
-}
-
-static inline int ima_post_read_file(struct file *file, char *buf, loff_t size,
- enum kernel_read_file_id id)
-{
- return 0;
-}
-
-static inline void ima_post_path_mknod(struct mnt_idmap *idmap,
- struct dentry *dentry)
-{
- return;
-}
-
static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
{
return -EOPNOTSUPP;
@@ -170,20 +90,6 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
{}
#endif

-#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
-extern void ima_post_key_create_or_update(struct key *keyring,
- struct key *key,
- const void *payload, size_t plen,
- unsigned long flags, bool create);
-#else
-static inline void ima_post_key_create_or_update(struct key *keyring,
- struct key *key,
- const void *payload,
- size_t plen,
- unsigned long flags,
- bool create) {}
-#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
-
#ifdef CONFIG_IMA_APPRAISE
extern bool is_ima_appraise_enabled(void);
extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
@@ -256,14 +162,4 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
return false;
}
#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
-
-#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS)
-extern int ima_kernel_module_request(char *kmod_name);
-#else
-static inline int ima_kernel_module_request(char *kmod_name)
-{
- return 0;
-}
-
-#endif
#endif /* _LINUX_IMA_H */
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index f8aef9ade549..b3b7fd699b63 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -62,6 +62,7 @@ struct lsm_ctx {
#define LSM_ID_LOCKDOWN 108
#define LSM_ID_BPF 109
#define LSM_ID_LANDLOCK 110
+#define LSM_ID_IMA 111

/*
* LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index d0ffe37dc1d6..92b63039c654 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_powerpc.o \
platform_certs/keyring_handler.o
+# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index b98bfe9efd0c..475c32615006 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -8,6 +8,7 @@ config IMA
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
+ select SECURITY_PATH
select TCG_TPM if HAS_IOMEM
select TCG_TIS if TCG_TPM && X86
select TCG_CRB if TCG_TPM && ACPI
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index c29db699c996..c0412100023e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -127,6 +127,12 @@ void ima_load_kexec_buffer(void);
static inline void ima_load_kexec_buffer(void) {}
#endif /* CONFIG_HAVE_IMA_KEXEC */

+#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
+void ima_post_key_create_or_update(struct key *keyring, struct key *key,
+ const void *payload, size_t plen,
+ unsigned long flags, bool create);
+#endif
+
/*
* The default binary_runtime_measurements list format is defined as the
* platform native format. The canonical format is defined as little-endian.
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 908fa026ec58..483ccd24e214 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -189,7 +189,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
*
* Flag files that changed, based on i_version
*/
-void ima_file_free(struct file *file)
+static void ima_file_free(struct file *file)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;
@@ -427,8 +427,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
+static int ima_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
{
u32 secid;
int ret;
@@ -466,8 +466,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
*
* On mprotect change success, return 0. On failure, return -EACESS.
*/
-int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
- unsigned long prot)
+static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
{
struct ima_template_desc *template = NULL;
struct file *file;
@@ -525,7 +525,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_bprm_check(struct linux_binprm *bprm)
+static int ima_bprm_check(struct linux_binprm *bprm)
{
int ret;
u32 secid;
@@ -551,7 +551,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_file_check(struct file *file, int mask)
+static int ima_file_check(struct file *file, int mask)
{
u32 secid;

@@ -560,7 +560,6 @@ int ima_file_check(struct file *file, int mask)
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
MAY_APPEND), FILE_CHECK);
}
-EXPORT_SYMBOL_GPL(ima_file_check);

static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
size_t buf_size)
@@ -685,8 +684,9 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
* Skip calling process_measurement(), but indicate which newly, created
* tmpfiles are in policy.
*/
-void ima_post_create_tmpfile(struct mnt_idmap *idmap,
- struct inode *inode)
+static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
+ struct inode *inode)
+
{
struct integrity_iint_cache *iint;
int must_appraise;
@@ -717,8 +717,7 @@ void ima_post_create_tmpfile(struct mnt_idmap *idmap,
* Mark files created via the mknodat syscall as new, so that the
* file data can be written later.
*/
-void ima_post_path_mknod(struct mnt_idmap *idmap,
- struct dentry *dentry)
+static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
@@ -753,8 +752,8 @@ void ima_post_path_mknod(struct mnt_idmap *idmap,
*
* For permission return 0, otherwise return -EACCES.
*/
-int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
- bool contents)
+static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
+ bool contents)
{
enum ima_hooks func;
u32 secid;
@@ -803,8 +802,8 @@ const int read_idmap[READING_MAX_ID] = {
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_post_read_file(struct file *file, char *buf, loff_t size,
- enum kernel_read_file_id read_id)
+static int ima_post_read_file(struct file *file, char *buf, loff_t size,
+ enum kernel_read_file_id read_id)
{
enum ima_hooks func;
u32 secid;
@@ -837,7 +836,7 @@ int ima_post_read_file(struct file *file, char *buf, loff_t size,
*
* For permission return 0, otherwise return -EACCES.
*/
-int ima_load_data(enum kernel_load_data_id id, bool contents)
+static int ima_load_data(enum kernel_load_data_id id, bool contents)
{
bool ima_enforce, sig_enforce;

@@ -891,9 +890,9 @@ int ima_load_data(enum kernel_load_data_id id, bool contents)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_post_load_data(char *buf, loff_t size,
- enum kernel_load_data_id load_id,
- char *description)
+static int ima_post_load_data(char *buf, loff_t size,
+ enum kernel_load_data_id load_id,
+ char *description)
{
if (load_id == LOADING_FIRMWARE) {
if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
@@ -1110,7 +1109,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data);
*
* Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
*/
-int ima_kernel_module_request(char *kmod_name)
+static int ima_kernel_module_request(char *kmod_name)
{
if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
return -EINVAL;
@@ -1151,4 +1150,41 @@ static int __init init_ima(void)
return error;
}

+static struct security_hook_list ima_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
+ LSM_HOOK_INIT(file_post_open, ima_file_check),
+ LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
+ LSM_HOOK_INIT(file_release, ima_file_free),
+ LSM_HOOK_INIT(mmap_file, ima_file_mmap),
+ LSM_HOOK_INIT(file_mprotect, ima_file_mprotect),
+ LSM_HOOK_INIT(kernel_load_data, ima_load_data),
+ LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data),
+ LSM_HOOK_INIT(kernel_read_file, ima_read_file),
+ LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file),
+ LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
+#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
+ LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update),
+#endif
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+ LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request),
+#endif
+};
+
+static const struct lsm_id ima_lsmid = {
+ .name = "ima",
+ .id = LSM_ID_IMA,
+};
+
+static int __init init_ima_lsm(void)
+{
+ security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
+ return 0;
+}
+
+DEFINE_LSM(ima) = {
+ .name = "ima",
+ .init = init_ima_lsm,
+ .order = LSM_ORDER_LAST,
+};
+
late_initcall(init_ima); /* Start IMA after the TPM is available */
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 9561db7cf6b4..59eaddd84434 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -18,6 +18,7 @@
#include <crypto/hash.h>
#include <linux/key.h>
#include <linux/audit.h>
+#include <linux/lsm_hooks.h>

/* iint action cache flags */
#define IMA_MEASURE 0x00000001
diff --git a/security/keys/key.c b/security/keys/key.c
index f75fe66c2f03..80fc2f203a0c 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -13,7 +13,6 @@
#include <linux/security.h>
#include <linux/workqueue.h>
#include <linux/random.h>
-#include <linux/ima.h>
#include <linux/err.h>
#include "internal.h"

@@ -937,8 +936,6 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,

security_key_post_create_or_update(keyring, key, payload, plen, flags,
true);
- ima_post_key_create_or_update(keyring, key, payload, plen,
- flags, true);

key_ref = make_key_ref(key, is_key_possessed(keyring_ref));

@@ -970,13 +967,9 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,

key_ref = __key_update(key_ref, &prep);

- if (!IS_ERR(key_ref)) {
+ if (!IS_ERR(key_ref))
security_key_post_create_or_update(keyring, key, payload, plen,
flags, false);
- ima_post_key_create_or_update(keyring, key,
- payload, plen,
- flags, false);
- }

goto error_free_prep;
}
diff --git a/security/security.c b/security/security.c
index 6c6571a141a1..aa17b47d44c1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -50,7 +50,8 @@
(IS_ENABLED(CONFIG_SECURITY_SAFESETID) ? 1 : 0) + \
(IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0))
+ (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \
+ (IS_ENABLED(CONFIG_IMA) ? 1 : 0))

/*
* These are descriptions of the reasons that can be passed to the
@@ -1182,12 +1183,7 @@ int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *
*/
int security_bprm_check(struct linux_binprm *bprm)
{
- int ret;
-
- ret = call_int_hook(bprm_check_security, 0, bprm);
- if (ret)
- return ret;
- return ima_bprm_check(bprm);
+ return call_int_hook(bprm_check_security, 0, bprm);
}

/**
@@ -2901,13 +2897,8 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags)
{
- unsigned long prot_adj = mmap_prot(file, prot);
- int ret;
-
- ret = call_int_hook(mmap_file, 0, file, prot, prot_adj, flags);
- if (ret)
- return ret;
- return ima_file_mmap(file, prot, prot_adj, flags);
+ return call_int_hook(mmap_file, 0, file, prot, mmap_prot(file, prot),
+ flags);
}

/**
@@ -2936,12 +2927,7 @@ int security_mmap_addr(unsigned long addr)
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
{
- int ret;
-
- ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot);
- if (ret)
- return ret;
- return ima_file_mprotect(vma, reqprot, prot);
+ return call_int_hook(file_mprotect, 0, vma, reqprot, prot);
}

/**
@@ -3250,12 +3236,7 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
*/
int security_kernel_module_request(char *kmod_name)
{
- int ret;
-
- ret = call_int_hook(kernel_module_request, 0, kmod_name);
- if (ret)
- return ret;
- return ima_kernel_module_request(kmod_name);
+ return call_int_hook(kernel_module_request, 0, kmod_name);
}

/**
@@ -3271,12 +3252,7 @@ int security_kernel_module_request(char *kmod_name)
int security_kernel_read_file(struct file *file, enum kernel_read_file_id id,
bool contents)
{
- int ret;
-
- ret = call_int_hook(kernel_read_file, 0, file, id, contents);
- if (ret)
- return ret;
- return ima_read_file(file, id, contents);
+ return call_int_hook(kernel_read_file, 0, file, id, contents);
}
EXPORT_SYMBOL_GPL(security_kernel_read_file);

@@ -3296,12 +3272,7 @@ EXPORT_SYMBOL_GPL(security_kernel_read_file);
int security_kernel_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id)
{
- int ret;
-
- ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
- if (ret)
- return ret;
- return ima_post_read_file(file, buf, size, id);
+ return call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
}
EXPORT_SYMBOL_GPL(security_kernel_post_read_file);

@@ -3316,12 +3287,7 @@ EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
*/
int security_kernel_load_data(enum kernel_load_data_id id, bool contents)
{
- int ret;
-
- ret = call_int_hook(kernel_load_data, 0, id, contents);
- if (ret)
- return ret;
- return ima_load_data(id, contents);
+ return call_int_hook(kernel_load_data, 0, id, contents);
}
EXPORT_SYMBOL_GPL(security_kernel_load_data);

@@ -3343,13 +3309,8 @@ int security_kernel_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id id,
char *description)
{
- int ret;
-
- ret = call_int_hook(kernel_post_load_data, 0, buf, size, id,
- description);
- if (ret)
- return ret;
- return ima_post_load_data(buf, size, id, description);
+ return call_int_hook(kernel_post_load_data, 0, buf, size, id,
+ description);
}
EXPORT_SYMBOL_GPL(security_kernel_post_load_data);

diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c
index 9df29b1e3497..17333787cb2f 100644
--- a/tools/testing/selftests/lsm/lsm_list_modules_test.c
+++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c
@@ -122,6 +122,9 @@ TEST(correct_lsm_list_modules)
case LSM_ID_LANDLOCK:
name = "landlock";
break;
+ case LSM_ID_IMA:
+ name = "ima";
+ break;
default:
name = "INVALID";
break;
--
2.34.1


2024-01-15 18:27:20

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 21/25] ima: Move IMA-Appraisal to LSM infrastructure

From: Roberto Sassu <[email protected]>

A few additional IMA hooks are needed to reset the cached appraisal
status, causing the file's integrity to be re-evaluated on next access.
Register these IMA-appraisal only functions separately from the rest of IMA
functions, as appraisal is a separate feature not necessarily enabled in
the kernel configuration.

Reuse the same approach as for other IMA functions, move hardcoded calls
from various places in the kernel to the LSM infrastructure. Declare the
functions as static and register them as hook implementations in
init_ima_appraise_lsm(), called by init_ima_lsm().

Also move the inline function ima_inode_remove_acl() from the public ima.h
header to ima_appraise.c.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
fs/attr.c | 2 -
include/linux/ima.h | 55 ---------------------------
security/integrity/ima/ima.h | 5 +++
security/integrity/ima/ima_appraise.c | 38 +++++++++++++-----
security/integrity/ima/ima_main.c | 1 +
security/security.c | 13 -------
6 files changed, 35 insertions(+), 79 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 221d2bb0a906..38841f3ebbcb 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -17,7 +17,6 @@
#include <linux/filelock.h>
#include <linux/security.h>
#include <linux/evm.h>
-#include <linux/ima.h>

#include "internal.h"

@@ -503,7 +502,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) {
fsnotify_change(dentry, ia_valid);
security_inode_post_setattr(idmap, dentry, ia_valid);
- ima_inode_post_setattr(idmap, dentry, ia_valid);
evm_inode_post_setattr(idmap, dentry, ia_valid);
}

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 23ae24b60ecf..0bae61a15b60 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -92,66 +92,11 @@ static inline void ima_add_kexec_buffer(struct kimage *image)

#ifdef CONFIG_IMA_APPRAISE
extern bool is_ima_appraise_enabled(void);
-extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, int ia_valid);
-extern int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *xattr_name, const void *xattr_value,
- size_t xattr_value_len, int flags);
-extern int ima_inode_set_acl(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *acl_name,
- struct posix_acl *kacl);
-static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *acl_name)
-{
- return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
-}
-
-extern int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *xattr_name);
#else
static inline bool is_ima_appraise_enabled(void)
{
return 0;
}
-
-static inline void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, int ia_valid)
-{
- return;
-}
-
-static inline int ima_inode_setxattr(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *xattr_name,
- const void *xattr_value,
- size_t xattr_value_len,
- int flags)
-{
- return 0;
-}
-
-static inline int ima_inode_set_acl(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *acl_name,
- struct posix_acl *kacl)
-{
-
- return 0;
-}
-
-static inline int ima_inode_removexattr(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *xattr_name)
-{
- return 0;
-}
-
-static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *acl_name)
-{
- return 0;
-}
#endif /* CONFIG_IMA_APPRAISE */

#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index c0412100023e..a27fc10f84f7 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -334,6 +334,7 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len);
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value, int xattr_len);
+void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);

#else
static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
@@ -385,6 +386,10 @@ static inline int ima_read_xattr(struct dentry *dentry,
return 0;
}

+static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
+{
+}
+
#endif /* CONFIG_IMA_APPRAISE */

#ifdef CONFIG_IMA_APPRAISE_MODSIG
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 36abc84ba299..076451109637 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -636,8 +636,8 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
-void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, int ia_valid)
+static void ima_inode_post_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid)
{
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
@@ -750,9 +750,9 @@ static int validate_hash_algo(struct dentry *dentry,
return -EACCES;
}

-int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *xattr_name, const void *xattr_value,
- size_t xattr_value_len, int flags)
+static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xvalue = xattr_value;
int digsig = 0;
@@ -781,8 +781,8 @@ int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
return result;
}

-int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *acl_name, struct posix_acl *kacl)
+static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
{
if (evm_revalidate_status(acl_name))
ima_reset_appraise_flags(d_backing_inode(dentry), 0);
@@ -790,8 +790,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
return 0;
}

-int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *xattr_name)
+static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name)
{
int result;

@@ -803,3 +803,23 @@ int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
}
return result;
}
+
+static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name)
+{
+ return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
+}
+
+static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
+ LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr),
+ LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
+ LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr),
+ LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
+};
+
+void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
+{
+ security_add_hooks(ima_appraise_hooks, ARRAY_SIZE(ima_appraise_hooks),
+ lsmid);
+}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 483ccd24e214..77d0d4e4b582 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1178,6 +1178,7 @@ static const struct lsm_id ima_lsmid = {
static int __init init_ima_lsm(void)
{
security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
+ init_ima_appraise_lsm(&ima_lsmid);
return 0;
}

diff --git a/security/security.c b/security/security.c
index aa17b47d44c1..c07ad3c5f767 100644
--- a/security/security.c
+++ b/security/security.c
@@ -20,7 +20,6 @@
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
#include <linux/integrity.h>
-#include <linux/ima.h>
#include <linux/evm.h>
#include <linux/fsnotify.h>
#include <linux/mman.h>
@@ -2308,9 +2307,6 @@ int security_inode_setxattr(struct mnt_idmap *idmap,

if (ret == 1)
ret = cap_inode_setxattr(dentry, name, value, size, flags);
- if (ret)
- return ret;
- ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags);
if (ret)
return ret;
return evm_inode_setxattr(idmap, dentry, name, value, size, flags);
@@ -2338,9 +2334,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
return 0;
ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
kacl);
- if (ret)
- return ret;
- ret = ima_inode_set_acl(idmap, dentry, acl_name, kacl);
if (ret)
return ret;
return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
@@ -2401,9 +2394,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
- if (ret)
- return ret;
- ret = ima_inode_remove_acl(idmap, dentry, acl_name);
if (ret)
return ret;
return evm_inode_remove_acl(idmap, dentry, acl_name);
@@ -2503,9 +2493,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name);
if (ret == 1)
ret = cap_inode_removexattr(idmap, dentry, name);
- if (ret)
- return ret;
- ret = ima_inode_removexattr(idmap, dentry, name);
if (ret)
return ret;
return evm_inode_removexattr(idmap, dentry, name);
--
2.34.1


2024-01-15 18:27:43

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 22/25] evm: Move to LSM infrastructure

From: Roberto Sassu <[email protected]>

As for IMA, move hardcoded EVM function calls from various places in the
kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
(last and always enabled like 'ima'). The order in the Makefile ensures
that 'evm' hooks are executed after 'ima' ones.

Make EVM functions as static (except for evm_inode_init_security(), which
is exported), and register them as hook implementations in init_evm_lsm().
Also move the inline functions evm_inode_remove_acl(),
evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public
evm.h header to evm_main.c.

Unlike before (see commit to move IMA to the LSM infrastructure),
evm_inode_post_setattr(), evm_inode_post_set_acl(),
evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
executed for private inodes.

Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
---
fs/attr.c | 2 -
fs/posix_acl.c | 3 -
fs/xattr.c | 2 -
include/linux/evm.h | 113 -----------------
include/uapi/linux/lsm.h | 1 +
security/integrity/evm/evm_main.c | 118 +++++++++++++++---
security/security.c | 43 ++-----
.../selftests/lsm/lsm_list_modules_test.c | 3 +
8 files changed, 116 insertions(+), 169 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 38841f3ebbcb..b51bd7c9b4a7 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -16,7 +16,6 @@
#include <linux/fcntl.h>
#include <linux/filelock.h>
#include <linux/security.h>
-#include <linux/evm.h>

#include "internal.h"

@@ -502,7 +501,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) {
fsnotify_change(dentry, ia_valid);
security_inode_post_setattr(idmap, dentry, ia_valid);
- evm_inode_post_setattr(idmap, dentry, ia_valid);
}

return error;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index e3fbe1a9f3f5..ae67479cd2b6 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -26,7 +26,6 @@
#include <linux/mnt_idmapping.h>
#include <linux/iversion.h>
#include <linux/security.h>
-#include <linux/evm.h>
#include <linux/fsnotify.h>
#include <linux/filelock.h>

@@ -1138,7 +1137,6 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) {
fsnotify_xattr(dentry);
security_inode_post_set_acl(dentry, acl_name, kacl);
- evm_inode_post_set_acl(dentry, acl_name, kacl);
}

out_inode_unlock:
@@ -1247,7 +1245,6 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) {
fsnotify_xattr(dentry);
security_inode_post_remove_acl(idmap, dentry, acl_name);
- evm_inode_post_remove_acl(idmap, dentry, acl_name);
}

out_inode_unlock:
diff --git a/fs/xattr.c b/fs/xattr.c
index f891c260a971..f8b643f91a98 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -16,7 +16,6 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/security.h>
-#include <linux/evm.h>
#include <linux/syscalls.h>
#include <linux/export.h>
#include <linux/fsnotify.h>
@@ -557,7 +556,6 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,

fsnotify_xattr(dentry);
security_inode_post_removexattr(dentry, name);
- evm_inode_post_removexattr(dentry, name);

out:
return error;
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 3faabdd47852..cb481eccc967 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -21,45 +21,6 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
void *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint);
-extern int evm_inode_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, struct iattr *attr);
-extern void evm_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, int ia_valid);
-extern int evm_inode_setxattr(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
-extern void evm_inode_post_setxattr(struct dentry *dentry,
- const char *xattr_name,
- const void *xattr_value,
- size_t xattr_value_len,
- int flags);
-extern int evm_inode_copy_up_xattr(const char *name);
-extern int evm_inode_removexattr(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *xattr_name);
-extern void evm_inode_post_removexattr(struct dentry *dentry,
- const char *xattr_name);
-static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *acl_name)
-{
- evm_inode_post_removexattr(dentry, acl_name);
-}
-extern int evm_inode_set_acl(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *acl_name,
- struct posix_acl *kacl);
-static inline int evm_inode_remove_acl(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *acl_name)
-{
- return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
-}
-static inline void evm_inode_post_set_acl(struct dentry *dentry,
- const char *acl_name,
- struct posix_acl *kacl)
-{
- return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
-}
-
int evm_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, struct xattr *xattrs,
int *xattr_count);
@@ -94,80 +55,6 @@ static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
}
#endif

-static inline int evm_inode_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, struct iattr *attr)
-{
- return 0;
-}
-
-static inline void evm_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry, int ia_valid)
-{
- return;
-}
-
-static inline int evm_inode_setxattr(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
-{
- return 0;
-}
-
-static inline void evm_inode_post_setxattr(struct dentry *dentry,
- const char *xattr_name,
- const void *xattr_value,
- size_t xattr_value_len,
- int flags)
-{
- return;
-}
-
-static inline int evm_inode_copy_up_xattr(const char *name)
-{
- return 0;
-}
-
-static inline int evm_inode_removexattr(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *xattr_name)
-{
- return 0;
-}
-
-static inline void evm_inode_post_removexattr(struct dentry *dentry,
- const char *xattr_name)
-{
- return;
-}
-
-static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *acl_name)
-{
- return;
-}
-
-static inline int evm_inode_set_acl(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *acl_name,
- struct posix_acl *kacl)
-{
- return 0;
-}
-
-static inline int evm_inode_remove_acl(struct mnt_idmap *idmap,
- struct dentry *dentry,
- const char *acl_name)
-{
- return 0;
-}
-
-static inline void evm_inode_post_set_acl(struct dentry *dentry,
- const char *acl_name,
- struct posix_acl *kacl)
-{
- return;
-}
-
static inline int evm_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
struct xattr *xattrs,
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index b3b7fd699b63..33d8c9f4aa6b 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -63,6 +63,7 @@ struct lsm_ctx {
#define LSM_ID_BPF 109
#define LSM_ID_LANDLOCK 110
#define LSM_ID_IMA 111
+#define LSM_ID_EVM 112

/*
* LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index d35143179699..f65dbf3e9256 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -589,9 +589,9 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
* userspace from writing HMAC value. Writing 'security.evm' requires
* requires CAP_SYS_ADMIN privileges.
*/
-int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *xattr_name, const void *xattr_value,
- size_t xattr_value_len, int flags)
+static int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xattr_data = xattr_value;

@@ -621,8 +621,8 @@ int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
* Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
* the current value is valid.
*/
-int evm_inode_removexattr(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *xattr_name)
+static int evm_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name)
{
/* Policy permits modification of the protected xattrs even though
* there's no HMAC key loaded
@@ -672,9 +672,11 @@ static inline int evm_inode_set_acl_change(struct mnt_idmap *idmap,
* Prevent modifying posix acls causing the EVM HMAC to be re-calculated
* and 'security.evm' xattr updated, unless the existing 'security.evm' is
* valid.
+ *
+ * Return: zero on success, -EPERM on failure.
*/
-int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *acl_name, struct posix_acl *kacl)
+static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
{
enum integrity_status evm_status;

@@ -713,6 +715,24 @@ int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
return -EPERM;
}

+/**
+ * evm_inode_remove_acl - Protect the EVM extended attribute from posix acls
+ * @idmap: idmap of the mount
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ *
+ * Prevent removing posix acls causing the EVM HMAC to be re-calculated
+ * and 'security.evm' xattr updated, unless the existing 'security.evm' is
+ * valid.
+ *
+ * Return: zero on success, -EPERM on failure.
+ */
+static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name)
+{
+ return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
+}
+
static void evm_reset_status(struct inode *inode)
{
struct integrity_iint_cache *iint;
@@ -761,9 +781,11 @@ bool evm_revalidate_status(const char *xattr_name)
* __vfs_setxattr_noperm(). The caller of which has taken the inode's
* i_mutex lock.
*/
-void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len,
- int flags)
+static void evm_inode_post_setxattr(struct dentry *dentry,
+ const char *xattr_name,
+ const void *xattr_value,
+ size_t xattr_value_len,
+ int flags)
{
if (!evm_revalidate_status(xattr_name))
return;
@@ -782,6 +804,21 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
}

+/**
+ * evm_inode_post_set_acl - Update the EVM extended attribute from posix acls
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ * @kacl: pointer to the posix acls
+ *
+ * Update the 'security.evm' xattr with the EVM HMAC re-calculated after setting
+ * posix acls.
+ */
+static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
+}
+
/**
* evm_inode_post_removexattr - update 'security.evm' after removing the xattr
* @dentry: pointer to the affected dentry
@@ -792,7 +829,8 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
* No need to take the i_mutex lock here, as this function is called from
* vfs_removexattr() which takes the i_mutex.
*/
-void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
+static void evm_inode_post_removexattr(struct dentry *dentry,
+ const char *xattr_name)
{
if (!evm_revalidate_status(xattr_name))
return;
@@ -808,6 +846,22 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
}

+/**
+ * evm_inode_post_remove_acl - Update the EVM extended attribute from posix acls
+ * @idmap: idmap of the mount
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ *
+ * Update the 'security.evm' xattr with the EVM HMAC re-calculated after
+ * removing posix acls.
+ */
+static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
+ struct dentry *dentry,
+ const char *acl_name)
+{
+ evm_inode_post_removexattr(dentry, acl_name);
+}
+
static int evm_attr_change(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr)
{
@@ -831,8 +885,8 @@ static int evm_attr_change(struct mnt_idmap *idmap,
* Permit update of file attributes when files have a valid EVM signature,
* except in the case of them having an immutable portable signature.
*/
-int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
- struct iattr *attr)
+static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
@@ -883,8 +937,8 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
-void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
- int ia_valid)
+static void evm_inode_post_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid)
{
if (!evm_revalidate_status(NULL))
return;
@@ -901,7 +955,7 @@ void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
evm_update_evmxattr(dentry, NULL, NULL, 0);
}

-int evm_inode_copy_up_xattr(const char *name)
+static int evm_inode_copy_up_xattr(const char *name)
{
if (strcmp(name, XATTR_NAME_EVM) == 0)
return 1; /* Discard */
@@ -1004,4 +1058,36 @@ static int __init init_evm(void)
return error;
}

+static struct security_hook_list evm_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(inode_setattr, evm_inode_setattr),
+ LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr),
+ LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
+ LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr),
+ LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl),
+ LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl),
+ LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl),
+ LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl),
+ LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr),
+ LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr),
+ LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr),
+ LSM_HOOK_INIT(inode_init_security, evm_inode_init_security),
+};
+
+static const struct lsm_id evm_lsmid = {
+ .name = "evm",
+ .id = LSM_ID_EVM,
+};
+
+static int __init init_evm_lsm(void)
+{
+ security_add_hooks(evm_hooks, ARRAY_SIZE(evm_hooks), &evm_lsmid);
+ return 0;
+}
+
+DEFINE_LSM(evm) = {
+ .name = "evm",
+ .init = init_evm_lsm,
+ .order = LSM_ORDER_LAST,
+};
+
late_initcall(init_evm);
diff --git a/security/security.c b/security/security.c
index c07ad3c5f767..a44740640a9a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -20,13 +20,13 @@
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
#include <linux/integrity.h>
-#include <linux/evm.h>
#include <linux/fsnotify.h>
#include <linux/mman.h>
#include <linux/mount.h>
#include <linux/personality.h>
#include <linux/backing-dev.h>
#include <linux/string.h>
+#include <linux/xattr.h>
#include <linux/msg.h>
#include <net/flow.h>

@@ -50,7 +50,8 @@
(IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \
(IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_IMA) ? 1 : 0))
+ (IS_ENABLED(CONFIG_IMA) ? 1 : 0) + \
+ (IS_ENABLED(CONFIG_EVM) ? 1 : 0))

/*
* These are descriptions of the reasons that can be passed to the
@@ -1740,10 +1741,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
if (!xattr_count)
goto out;

- ret = evm_inode_init_security(inode, dir, qstr, new_xattrs,
- &xattr_count);
- if (ret)
- goto out;
ret = initxattrs(inode, new_xattrs, fs_data);
out:
for (; xattr_count > 0; xattr_count--)
@@ -2235,14 +2232,9 @@ int security_inode_permission(struct inode *inode, int mask)
int security_inode_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr)
{
- int ret;
-
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_setattr, 0, idmap, dentry, attr);
- if (ret)
- return ret;
- return evm_inode_setattr(idmap, dentry, attr);
+ return call_int_hook(inode_setattr, 0, idmap, dentry, attr);
}
EXPORT_SYMBOL_GPL(security_inode_setattr);

@@ -2307,9 +2299,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap,

if (ret == 1)
ret = cap_inode_setxattr(dentry, name, value, size, flags);
- if (ret)
- return ret;
- return evm_inode_setxattr(idmap, dentry, name, value, size, flags);
+ return ret;
}

/**
@@ -2328,15 +2318,10 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
{
- int ret;
-
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
- kacl);
- if (ret)
- return ret;
- return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
+ return call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
+ kacl);
}

/**
@@ -2389,14 +2374,9 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
int security_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
- int ret;
-
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
- if (ret)
- return ret;
- return evm_inode_remove_acl(idmap, dentry, acl_name);
+ return call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
}

/**
@@ -2432,7 +2412,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return;
call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
- evm_inode_post_setxattr(dentry, name, value, size, flags);
}

/**
@@ -2493,9 +2472,7 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name);
if (ret == 1)
ret = cap_inode_removexattr(idmap, dentry, name);
- if (ret)
- return ret;
- return evm_inode_removexattr(idmap, dentry, name);
+ return ret;
}

/**
@@ -2699,7 +2676,7 @@ int security_inode_copy_up_xattr(const char *name)
return rc;
}

- return evm_inode_copy_up_xattr(name);
+ return LSM_RET_DEFAULT(inode_copy_up_xattr);
}
EXPORT_SYMBOL(security_inode_copy_up_xattr);

diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c
index 17333787cb2f..4d5d4cee2586 100644
--- a/tools/testing/selftests/lsm/lsm_list_modules_test.c
+++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c
@@ -125,6 +125,9 @@ TEST(correct_lsm_list_modules)
case LSM_ID_IMA:
name = "ima";
break;
+ case LSM_ID_EVM:
+ name = "evm";
+ break;
default:
name = "INVALID";
break;
--
2.34.1


2024-01-15 18:28:08

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 23/25] evm: Make it independent from 'integrity' LSM

From: Roberto Sassu <[email protected]>

Define a new structure for EVM-specific metadata, called evm_iint_cache,
and embed it in the inode security blob. Introduce evm_iint_inode() to
retrieve metadata, and register evm_inode_alloc_security() for the
inode_alloc_security LSM hook, to initialize the structure (before
splitting metadata, this task was done by iint_init_always()).

Keep the non-NULL checks after calling evm_iint_inode() except in
evm_inode_alloc_security(), to take into account inodes for which
security_inode_alloc() was not called. When using shared metadata,
obtaining a NULL pointer from integrity_iint_find() meant that the file
wasn't in the IMA policy. Now, because IMA and EVM use disjoint metadata,
the EVM status has to be stored for every inode regardless of the IMA
policy.

Given that from now on EVM relies on its own metadata, remove the iint
parameter from evm_verifyxattr(). Also, directly retrieve the iint in
evm_verify_hmac(), called by both evm_verifyxattr() and
evm_verify_current_integrity(), since now there is no performance penalty
in retrieving EVM metadata (constant time).

Replicate the management of the IMA_NEW_FILE flag, by introducing
evm_post_path_mknod() and evm_file_release() to respectively set and clear
the newly introduced flag EVM_NEW_FILE, at the same time IMA does. Like for
IMA, select CONFIG_SECURITY_PATH when EVM is enabled, to ensure that files
are marked as new.

Unlike ima_post_path_mknod(), evm_post_path_mknod() cannot check if a file
must be appraised. Thus, it marks all affected files. Also, it does not
clear EVM_NEW_FILE depending on i_version, but that is not a problem
because IMA_NEW_FILE is always cleared when set in ima_check_last_writer().

Move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to
security/integrity/evm/evm.h, since that definition is now unnecessary in
the common integrity layer.

Finally, switch to the LSM reservation mechanism for the EVM xattr, and
consequently decrement by one the number of xattrs to allocate in
security_inode_init_security().

Signed-off-by: Roberto Sassu <[email protected]>
---
include/linux/evm.h | 8 +--
security/integrity/evm/Kconfig | 1 +
security/integrity/evm/evm.h | 19 +++++++
security/integrity/evm/evm_crypto.c | 4 +-
security/integrity/evm/evm_main.c | 76 ++++++++++++++++++++-------
security/integrity/ima/ima_appraise.c | 2 +-
security/integrity/integrity.h | 1 -
security/security.c | 4 +-
8 files changed, 83 insertions(+), 32 deletions(-)

diff --git a/include/linux/evm.h b/include/linux/evm.h
index cb481eccc967..d48d6da32315 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -12,15 +12,12 @@
#include <linux/integrity.h>
#include <linux/xattr.h>

-struct integrity_iint_cache;
-
#ifdef CONFIG_EVM
extern int evm_set_key(void *key, size_t keylen);
extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value,
- size_t xattr_value_len,
- struct integrity_iint_cache *iint);
+ size_t xattr_value_len);
int evm_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, struct xattr *xattrs,
int *xattr_count);
@@ -48,8 +45,7 @@ static inline int evm_set_key(void *key, size_t keylen)
static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value,
- size_t xattr_value_len,
- struct integrity_iint_cache *iint)
+ size_t xattr_value_len)
{
return INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index fba9ee359bc9..861b3bacab82 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -6,6 +6,7 @@ config EVM
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
+ select SECURITY_PATH
default n
help
EVM protects a file's security extended attributes against
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 53bd7fec93fa..eb1a2c343bd7 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -32,6 +32,25 @@ struct xattr_list {
bool enabled;
};

+#define EVM_NEW_FILE 0x00000001
+#define EVM_IMMUTABLE_DIGSIG 0x00000002
+
+/* EVM integrity metadata associated with an inode */
+struct evm_iint_cache {
+ unsigned long flags;
+ enum integrity_status evm_status:4;
+};
+
+extern struct lsm_blob_sizes evm_blob_sizes;
+
+static inline struct evm_iint_cache *evm_iint_inode(const struct inode *inode)
+{
+ if (unlikely(!inode->i_security))
+ return NULL;
+
+ return inode->i_security + evm_blob_sizes.lbs_inode;
+}
+
extern int evm_initialized;

#define EVM_ATTR_FSUUID 0x0001
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index b1ffd4cc0b44..7552d49d0725 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -322,10 +322,10 @@ int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
{
const struct evm_ima_xattr_data *xattr_data = NULL;
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;
int rc = 0;

- iint = integrity_iint_find(inode);
+ iint = evm_iint_inode(inode);
if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
return 1;

diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index f65dbf3e9256..14c8f38e61d6 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -178,14 +178,14 @@ static int is_unsupported_fs(struct dentry *dentry)
static enum integrity_status evm_verify_hmac(struct dentry *dentry,
const char *xattr_name,
char *xattr_value,
- size_t xattr_value_len,
- struct integrity_iint_cache *iint)
+ size_t xattr_value_len)
{
struct evm_ima_xattr_data *xattr_data = NULL;
struct signature_v2_hdr *hdr;
enum integrity_status evm_status = INTEGRITY_PASS;
struct evm_digest digest;
- struct inode *inode;
+ struct inode *inode = d_backing_inode(dentry);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
int rc, xattr_len, evm_immutable = 0;

if (iint && (iint->evm_status == INTEGRITY_PASS ||
@@ -254,8 +254,6 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
(const char *)xattr_data, xattr_len,
digest.digest, digest.hdr.length);
if (!rc) {
- inode = d_backing_inode(dentry);
-
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
if (iint)
iint->flags |= EVM_IMMUTABLE_DIGSIG;
@@ -403,7 +401,6 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
* @xattr_name: requested xattr
* @xattr_value: requested xattr value
* @xattr_value_len: requested xattr value length
- * @iint: inode integrity metadata
*
* Calculate the HMAC for the given dentry and verify it against the stored
* security.evm xattr. For performance, use the xattr value and length
@@ -416,8 +413,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
*/
enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
- void *xattr_value, size_t xattr_value_len,
- struct integrity_iint_cache *iint)
+ void *xattr_value, size_t xattr_value_len)
{
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
return INTEGRITY_UNKNOWN;
@@ -425,13 +421,8 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
if (is_unsupported_fs(dentry))
return INTEGRITY_UNKNOWN;

- if (!iint) {
- iint = integrity_iint_find(d_backing_inode(dentry));
- if (!iint)
- return INTEGRITY_UNKNOWN;
- }
return evm_verify_hmac(dentry, xattr_name, xattr_value,
- xattr_value_len, iint);
+ xattr_value_len);
}
EXPORT_SYMBOL_GPL(evm_verifyxattr);

@@ -448,7 +439,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)

if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
return INTEGRITY_PASS;
- return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
+ return evm_verify_hmac(dentry, NULL, NULL, 0);
}

/*
@@ -526,14 +517,14 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,

evm_status = evm_verify_current_integrity(dentry);
if (evm_status == INTEGRITY_NOXATTRS) {
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;

/* Exception if the HMAC is not going to be calculated. */
if (evm_hmac_disabled())
return 0;

- iint = integrity_iint_find(d_backing_inode(dentry));
- if (iint && (iint->flags & IMA_NEW_FILE))
+ iint = evm_iint_inode(d_backing_inode(dentry));
+ if (iint && (iint->flags & EVM_NEW_FILE))
return 0;

/* exception for pseudo filesystems */
@@ -735,9 +726,9 @@ static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,

static void evm_reset_status(struct inode *inode)
{
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;

- iint = integrity_iint_find(inode);
+ iint = evm_iint_inode(inode);
if (iint)
iint->evm_status = INTEGRITY_UNKNOWN;
}
@@ -1019,6 +1010,42 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
}
EXPORT_SYMBOL_GPL(evm_inode_init_security);

+static int evm_inode_alloc_security(struct inode *inode)
+{
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+
+ /* Called by security_inode_alloc(), it cannot be NULL. */
+ iint->flags = 0UL;
+ iint->evm_status = INTEGRITY_UNKNOWN;
+
+ return 0;
+}
+
+static void evm_file_release(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+ fmode_t mode = file->f_mode;
+
+ if (!S_ISREG(inode->i_mode) || !(mode & FMODE_WRITE))
+ return;
+
+ if (iint && atomic_read(&inode->i_writecount) == 1)
+ iint->flags &= ~EVM_NEW_FILE;
+}
+
+static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+ struct inode *inode = d_backing_inode(dentry);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+
+ if (!S_ISREG(inode->i_mode))
+ return;
+
+ if (iint)
+ iint->flags |= EVM_NEW_FILE;
+}
+
#ifdef CONFIG_EVM_LOAD_X509
void __init evm_load_x509(void)
{
@@ -1071,6 +1098,9 @@ static struct security_hook_list evm_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr),
LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr),
LSM_HOOK_INIT(inode_init_security, evm_inode_init_security),
+ LSM_HOOK_INIT(inode_alloc_security, evm_inode_alloc_security),
+ LSM_HOOK_INIT(file_release, evm_file_release),
+ LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod),
};

static const struct lsm_id evm_lsmid = {
@@ -1084,10 +1114,16 @@ static int __init init_evm_lsm(void)
return 0;
}

+struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct evm_iint_cache),
+ .lbs_xattr_count = 1,
+};
+
DEFINE_LSM(evm) = {
.name = "evm",
.init = init_evm_lsm,
.order = LSM_ORDER_LAST,
+ .blobs = &evm_blob_sizes,
};

late_initcall(init_evm);
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 076451109637..1dd6ee72a20a 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -520,7 +520,7 @@ int ima_appraise_measurement(enum ima_hooks func,
}

status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
- rc < 0 ? 0 : rc, iint);
+ rc < 0 ? 0 : rc);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 59eaddd84434..7a97c269a072 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -37,7 +37,6 @@
#define IMA_DIGSIG_REQUIRED 0x01000000
#define IMA_PERMIT_DIRECTIO 0x02000000
#define IMA_NEW_FILE 0x04000000
-#define EVM_IMMUTABLE_DIGSIG 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
diff --git a/security/security.c b/security/security.c
index a44740640a9a..f811cc376a7a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1716,8 +1716,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
return 0;

if (initxattrs) {
- /* Allocate +1 for EVM and +1 as terminator. */
- new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 2,
+ /* Allocate +1 as terminator. */
+ new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
sizeof(*new_xattrs), GFP_NOFS);
if (!new_xattrs)
return -ENOMEM;
--
2.34.1


2024-01-15 18:29:16

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 25/25] integrity: Remove LSM

From: Roberto Sassu <[email protected]>

Since now IMA and EVM use their own integrity metadata, it is safe to
remove the 'integrity' LSM, with its management of integrity metadata.

Keep the iint.c file only for loading IMA and EVM keys at boot, and for
creating the integrity directory in securityfs (we need to keep it for
retrocompatibility reasons).

Signed-off-by: Roberto Sassu <[email protected]>
---
include/linux/integrity.h | 14 ---
security/integrity/iint.c | 197 +--------------------------------
security/integrity/integrity.h | 25 -----
security/security.c | 2 -
4 files changed, 2 insertions(+), 236 deletions(-)

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index ef0f63ef5ebc..459b79683783 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -19,24 +19,10 @@ enum integrity_status {
INTEGRITY_UNKNOWN,
};

-/* List of EVM protected security xattrs */
#ifdef CONFIG_INTEGRITY
-extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
-extern void integrity_inode_free(struct inode *inode);
extern void __init integrity_load_keys(void);

#else
-static inline struct integrity_iint_cache *
- integrity_inode_get(struct inode *inode)
-{
- return NULL;
-}
-
-static inline void integrity_inode_free(struct inode *inode)
-{
- return;
-}
-
static inline void integrity_load_keys(void)
{
}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index d4419a2a1e24..068ac6c2ae1e 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -6,207 +6,14 @@
* Mimi Zohar <[email protected]>
*
* File: integrity_iint.c
- * - implements the integrity hooks: integrity_inode_alloc,
- * integrity_inode_free
- * - cache integrity information associated with an inode
- * using a rbtree tree.
+ * - initialize the integrity directory in securityfs
+ * - load IMA and EVM keys
*/
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/rbtree.h>
-#include <linux/file.h>
-#include <linux/uaccess.h>
#include <linux/security.h>
-#include <linux/lsm_hooks.h>
#include "integrity.h"

-static struct rb_root integrity_iint_tree = RB_ROOT;
-static DEFINE_RWLOCK(integrity_iint_lock);
-static struct kmem_cache *iint_cache __ro_after_init;
-
struct dentry *integrity_dir;

-/*
- * __integrity_iint_find - return the iint associated with an inode
- */
-static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
-{
- struct integrity_iint_cache *iint;
- struct rb_node *n = integrity_iint_tree.rb_node;
-
- while (n) {
- iint = rb_entry(n, struct integrity_iint_cache, rb_node);
-
- if (inode < iint->inode)
- n = n->rb_left;
- else if (inode > iint->inode)
- n = n->rb_right;
- else
- return iint;
- }
-
- return NULL;
-}
-
-/*
- * integrity_iint_find - return the iint associated with an inode
- */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
-{
- struct integrity_iint_cache *iint;
-
- if (!IS_IMA(inode))
- return NULL;
-
- read_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- read_unlock(&integrity_iint_lock);
-
- return iint;
-}
-
-#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1)
-
-/*
- * It is not clear that IMA should be nested at all, but as long is it measures
- * files both on overlayfs and on underlying fs, we need to annotate the iint
- * mutex to avoid lockdep false positives related to IMA + overlayfs.
- * See ovl_lockdep_annotate_inode_mutex_key() for more details.
- */
-static inline void iint_lockdep_annotate(struct integrity_iint_cache *iint,
- struct inode *inode)
-{
-#ifdef CONFIG_LOCKDEP
- static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING];
-
- int depth = inode->i_sb->s_stack_depth;
-
- if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
- depth = 0;
-
- lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]);
-#endif
-}
-
-static void iint_init_always(struct integrity_iint_cache *iint,
- struct inode *inode)
-{
- iint->ima_hash = NULL;
- iint->version = 0;
- iint->flags = 0UL;
- iint->atomic_flags = 0UL;
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
- iint->ima_creds_status = INTEGRITY_UNKNOWN;
- iint->evm_status = INTEGRITY_UNKNOWN;
- iint->measured_pcrs = 0;
- mutex_init(&iint->mutex);
- iint_lockdep_annotate(iint, inode);
-}
-
-static void iint_free(struct integrity_iint_cache *iint)
-{
- kfree(iint->ima_hash);
- mutex_destroy(&iint->mutex);
- kmem_cache_free(iint_cache, iint);
-}
-
-/**
- * integrity_inode_get - find or allocate an iint associated with an inode
- * @inode: pointer to the inode
- * @return: allocated iint
- *
- * Caller must lock i_mutex
- */
-struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
-{
- struct rb_node **p;
- struct rb_node *node, *parent = NULL;
- struct integrity_iint_cache *iint, *test_iint;
-
- iint = integrity_iint_find(inode);
- if (iint)
- return iint;
-
- iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
- if (!iint)
- return NULL;
-
- iint_init_always(iint, inode);
-
- write_lock(&integrity_iint_lock);
-
- p = &integrity_iint_tree.rb_node;
- while (*p) {
- parent = *p;
- test_iint = rb_entry(parent, struct integrity_iint_cache,
- rb_node);
- if (inode < test_iint->inode) {
- p = &(*p)->rb_left;
- } else if (inode > test_iint->inode) {
- p = &(*p)->rb_right;
- } else {
- write_unlock(&integrity_iint_lock);
- kmem_cache_free(iint_cache, iint);
- return test_iint;
- }
- }
-
- iint->inode = inode;
- node = &iint->rb_node;
- inode->i_flags |= S_IMA;
- rb_link_node(node, parent, p);
- rb_insert_color(node, &integrity_iint_tree);
-
- write_unlock(&integrity_iint_lock);
- return iint;
-}
-
-/**
- * integrity_inode_free - called on security_inode_free
- * @inode: pointer to the inode
- *
- * Free the integrity information(iint) associated with an inode.
- */
-void integrity_inode_free(struct inode *inode)
-{
- struct integrity_iint_cache *iint;
-
- if (!IS_IMA(inode))
- return;
-
- write_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- rb_erase(&iint->rb_node, &integrity_iint_tree);
- write_unlock(&integrity_iint_lock);
-
- iint_free(iint);
-}
-
-static void iint_init_once(void *foo)
-{
- struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo;
-
- memset(iint, 0, sizeof(*iint));
-}
-
-static int __init integrity_iintcache_init(void)
-{
- iint_cache =
- kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
- 0, SLAB_PANIC, iint_init_once);
- return 0;
-}
-DEFINE_LSM(integrity) = {
- .name = "integrity",
- .init = integrity_iintcache_init,
- .order = LSM_ORDER_LAST,
-};
-
-
/*
* integrity_kernel_read - read data from the file
*
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 671fc50255f9..50d6f798e613 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -102,31 +102,6 @@ struct ima_file_id {
__u8 hash[HASH_MAX_DIGESTSIZE];
} __packed;

-/* integrity data associated with an inode */
-struct integrity_iint_cache {
- struct rb_node rb_node; /* rooted in integrity_iint_tree */
- struct mutex mutex; /* protects: version, flags, digest */
- struct inode *inode; /* back pointer to inode in question */
- u64 version; /* track inode changes */
- unsigned long flags;
- unsigned long measured_pcrs;
- unsigned long atomic_flags;
- unsigned long real_ino;
- dev_t real_dev;
- enum integrity_status ima_file_status:4;
- enum integrity_status ima_mmap_status:4;
- enum integrity_status ima_bprm_status:4;
- enum integrity_status ima_read_status:4;
- enum integrity_status ima_creds_status:4;
- enum integrity_status evm_status:4;
- struct ima_digest_data *ima_hash;
-};
-
-/* rbtree tree calls to lookup, insert, delete
- * integrity data associated with an inode.
- */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
-
int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count);

diff --git a/security/security.c b/security/security.c
index f811cc376a7a..df87c0a7eaac 100644
--- a/security/security.c
+++ b/security/security.c
@@ -19,7 +19,6 @@
#include <linux/kernel.h>
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
-#include <linux/integrity.h>
#include <linux/fsnotify.h>
#include <linux/mman.h>
#include <linux/mount.h>
@@ -1597,7 +1596,6 @@ static void inode_free_by_rcu(struct rcu_head *head)
*/
void security_inode_free(struct inode *inode)
{
- integrity_inode_free(inode);
call_void_hook(inode_free_security, inode);
/*
* The inode may still be referenced in a path walk and
--
2.34.1


2024-01-15 18:37:26

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 04/25] ima: Align ima_inode_removexattr() definition with LSM infrastructure

From: Roberto Sassu <[email protected]>

Change ima_inode_removexattr() definition, so that it can be registered as
implementation of the inode_removexattr hook.

Signed-off-by: Roberto Sassu <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Casey Schaufler <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
---
include/linux/ima.h | 7 +++++--
security/integrity/ima/ima_appraise.c | 3 ++-
security/security.c | 2 +-
3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 077324309c11..678a03fddd7e 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -200,7 +200,9 @@ static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
{
return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
}
-extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
+
+extern int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name);
#else
static inline bool is_ima_appraise_enabled(void)
{
@@ -231,7 +233,8 @@ static inline int ima_inode_set_acl(struct mnt_idmap *idmap,
return 0;
}

-static inline int ima_inode_removexattr(struct dentry *dentry,
+static inline int ima_inode_removexattr(struct mnt_idmap *idmap,
+ struct dentry *dentry,
const char *xattr_name)
{
return 0;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index cb2d0d11aa77..36abc84ba299 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -790,7 +790,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
return 0;
}

-int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name)
{
int result;

diff --git a/security/security.c b/security/security.c
index 98531e9b368f..fda78023de48 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2430,7 +2430,7 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
ret = cap_inode_removexattr(idmap, dentry, name);
if (ret)
return ret;
- ret = ima_inode_removexattr(dentry, name);
+ ret = ima_inode_removexattr(idmap, dentry, name);
if (ret)
return ret;
return evm_inode_removexattr(idmap, dentry, name);
--
2.34.1


2024-01-15 18:41:44

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v9 13/25] security: Introduce file_release hook

From: Roberto Sassu <[email protected]>

In preparation for moving IMA and EVM to the LSM infrastructure, introduce
the file_release hook.

IMA calculates at file close the new digest of the file content and writes
it to security.ima, so that appraisal at next file access succeeds.

An LSM could implement an exclusive access scheme for files, only allowing
access to files that have no references.

The new hook cannot return an error and cannot cause the operation to be
reverted.

Signed-off-by: Roberto Sassu <[email protected]>
---
fs/file_table.c | 1 +
include/linux/lsm_hook_defs.h | 1 +
include/linux/security.h | 4 ++++
security/security.c | 11 +++++++++++
4 files changed, 17 insertions(+)

diff --git a/fs/file_table.c b/fs/file_table.c
index de4a2915bfd4..c72dc75f2bd3 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -385,6 +385,7 @@ static void __fput(struct file *file)
eventpoll_release(file);
locks_remove_file(file);

+ security_file_release(file);
ima_file_free(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index c3fecc7dcb0b..229f84ce12ae 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -173,6 +173,7 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
struct kernfs_node *kn)
LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
+LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
unsigned long arg)
diff --git a/include/linux/security.h b/include/linux/security.h
index 97f2212c13b6..2997348afcb7 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -395,6 +395,7 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
struct kernfs_node *kn);
int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
+void security_file_release(struct file *file);
void security_file_free(struct file *file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
@@ -1008,6 +1009,9 @@ static inline int security_file_alloc(struct file *file)
return 0;
}

+static inline void security_file_release(struct file *file)
+{ }
+
static inline void security_file_free(struct file *file)
{ }

diff --git a/security/security.c b/security/security.c
index f3d92bffd02f..7d10724872f8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2724,6 +2724,17 @@ int security_file_alloc(struct file *file)
return rc;
}

+/**
+ * security_file_release() - Perform actions before releasing the file ref
+ * @file: the file
+ *
+ * Perform actions before releasing the last reference to a file.
+ */
+void security_file_release(struct file *file)
+{
+ call_void_hook(file_release, file);
+}
+
/**
* security_file_free() - Free a file's LSM blob
* @file: the file
--
2.34.1


2024-01-15 19:15:58

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH v9 13/25] security: Introduce file_release hook

On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the file_release hook.
>
> IMA calculates at file close the new digest of the file content and writes
> it to security.ima, so that appraisal at next file access succeeds.
>
> An LSM could implement an exclusive access scheme for files, only allowing
> access to files that have no references.

Elaborate that last part, please.

2024-01-16 08:48:02

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 13/25] security: Introduce file_release hook

On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
> On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
> > From: Roberto Sassu <[email protected]>
> >
> > In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> > the file_release hook.
> >
> > IMA calculates at file close the new digest of the file content and writes
> > it to security.ima, so that appraisal at next file access succeeds.
> >
> > An LSM could implement an exclusive access scheme for files, only allowing
> > access to files that have no references.
>
> Elaborate that last part, please.

Apologies, I didn't understand that either. Casey?

Thanks

Roberto


2024-01-16 16:51:23

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v9 13/25] security: Introduce file_release hook

On 1/16/2024 12:47 AM, Roberto Sassu wrote:
> On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
>> On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
>>> From: Roberto Sassu <[email protected]>
>>>
>>> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
>>> the file_release hook.
>>>
>>> IMA calculates at file close the new digest of the file content and writes
>>> it to security.ima, so that appraisal at next file access succeeds.
>>>
>>> An LSM could implement an exclusive access scheme for files, only allowing
>>> access to files that have no references.
>> Elaborate that last part, please.
> Apologies, I didn't understand that either. Casey?

Just a hypothetical notion that if an LSM wanted to implement an
exclusive access scheme it might find the proposed hook helpful.
I don't have any plan to create such a scheme, nor do I think that
a file_release hook would be the only thing you'd need.

>
> Thanks
>
> Roberto
>

2024-01-16 17:34:01

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH v9 13/25] security: Introduce file_release hook

On Tue, Jan 16, 2024 at 08:51:11AM -0800, Casey Schaufler wrote:
> On 1/16/2024 12:47 AM, Roberto Sassu wrote:
> > On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
> >> On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
> >>> From: Roberto Sassu <[email protected]>
> >>>
> >>> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> >>> the file_release hook.
> >>>
> >>> IMA calculates at file close the new digest of the file content and writes
> >>> it to security.ima, so that appraisal at next file access succeeds.
> >>>
> >>> An LSM could implement an exclusive access scheme for files, only allowing
> >>> access to files that have no references.
> >> Elaborate that last part, please.
> > Apologies, I didn't understand that either. Casey?
>
> Just a hypothetical notion that if an LSM wanted to implement an
> exclusive access scheme it might find the proposed hook helpful.
> I don't have any plan to create such a scheme, nor do I think that
> a file_release hook would be the only thing you'd need.

Exclusive access to what? "No more than one opened file with this
inode at a time"? It won't serialize IO operations, obviously...
Details, please.

2024-01-16 18:57:52

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v9 20/25] ima: Move to LSM infrastructure

On 1/15/2024 10:18 AM, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Move hardcoded IMA function calls (not appraisal-specific functions) from
> various places in the kernel to the LSM infrastructure, by introducing a
> new LSM named 'ima' (at the end of the LSM list and always enabled like
> 'integrity').
>
> Having IMA before EVM in the Makefile is sufficient to preserve the
> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
> and thus the order of IMA and EVM function calls as when they were
> hardcoded.
>
> Make moved functions as static (except ima_post_key_create_or_update(),
> which is not in ima_main.c), and register them as implementation of the
> respective hooks in the new function init_ima_lsm().
>
> Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook
> path_post_mknod is always available and ima_post_path_mknod() is always
> executed to mark files as new, as before the move.
>
> A slight difference is that IMA and EVM functions registered for the
> inode_post_setattr, inode_post_removexattr, path_post_mknod,
> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
> won't be executed for private inodes. Since those inodes are supposed to be
> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
> and kernel-internal tmpfs files.
>
> Conditionally register ima_post_key_create_or_update() if
> CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register
> ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
>
> Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Chuck Lever <[email protected]>

Acked-by: Casey Schaufler <[email protected]>

> ---
> fs/file_table.c | 2 -
> fs/namei.c | 6 -
> fs/nfsd/vfs.c | 7 --
> fs/open.c | 1 -
> include/linux/ima.h | 104 ------------------
> include/uapi/linux/lsm.h | 1 +
> security/integrity/Makefile | 1 +
> security/integrity/ima/Kconfig | 1 +
> security/integrity/ima/ima.h | 6 +
> security/integrity/ima/ima_main.c | 78 +++++++++----
> security/integrity/integrity.h | 1 +
> security/keys/key.c | 9 +-
> security/security.c | 63 ++---------
> .../selftests/lsm/lsm_list_modules_test.c | 3 +
> 14 files changed, 83 insertions(+), 200 deletions(-)
>
> diff --git a/fs/file_table.c b/fs/file_table.c
> index c72dc75f2bd3..0401ac98281c 100644
> --- a/fs/file_table.c
> +++ b/fs/file_table.c
> @@ -26,7 +26,6 @@
> #include <linux/percpu_counter.h>
> #include <linux/percpu.h>
> #include <linux/task_work.h>
> -#include <linux/ima.h>
> #include <linux/swap.h>
> #include <linux/kmemleak.h>
>
> @@ -386,7 +385,6 @@ static void __fput(struct file *file)
> locks_remove_file(file);
>
> security_file_release(file);
> - ima_file_free(file);
> if (unlikely(file->f_flags & FASYNC)) {
> if (file->f_op->fasync)
> file->f_op->fasync(-1, file, 0);
> diff --git a/fs/namei.c b/fs/namei.c
> index adb3ab27951a..37cc0988308f 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -27,7 +27,6 @@
> #include <linux/fsnotify.h>
> #include <linux/personality.h>
> #include <linux/security.h>
> -#include <linux/ima.h>
> #include <linux/syscalls.h>
> #include <linux/mount.h>
> #include <linux/audit.h>
> @@ -3622,8 +3621,6 @@ static int do_open(struct nameidata *nd,
> error = vfs_open(&nd->path, file);
> if (!error)
> error = security_file_post_open(file, op->acc_mode);
> - if (!error)
> - error = ima_file_check(file, op->acc_mode);
> if (!error && do_truncate)
> error = handle_truncate(idmap, file);
> if (unlikely(error > 0)) {
> @@ -3687,7 +3684,6 @@ static int vfs_tmpfile(struct mnt_idmap *idmap,
> spin_unlock(&inode->i_lock);
> }
> security_inode_post_create_tmpfile(idmap, inode);
> - ima_post_create_tmpfile(idmap, inode);
> return 0;
> }
>
> @@ -4036,8 +4032,6 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> case 0: case S_IFREG:
> error = vfs_create(idmap, path.dentry->d_inode,
> dentry, mode, true);
> - if (!error)
> - ima_post_path_mknod(idmap, dentry);
> break;
> case S_IFCHR: case S_IFBLK:
> error = vfs_mknod(idmap, path.dentry->d_inode,
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index b0c3f07a8bba..e491392a1243 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -25,7 +25,6 @@
> #include <linux/posix_acl_xattr.h>
> #include <linux/xattr.h>
> #include <linux/jhash.h>
> -#include <linux/ima.h>
> #include <linux/pagemap.h>
> #include <linux/slab.h>
> #include <linux/uaccess.h>
> @@ -883,12 +882,6 @@ __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
> goto out;
> }
>
> - host_err = ima_file_check(file, may_flags);
> - if (host_err) {
> - fput(file);
> - goto out;
> - }
> -
> if (may_flags & NFSD_MAY_64BIT_COOKIE)
> file->f_mode |= FMODE_64BITHASH;
> else
> diff --git a/fs/open.c b/fs/open.c
> index 02dc608d40d8..c8bb9bd5259f 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -29,7 +29,6 @@
> #include <linux/audit.h>
> #include <linux/falloc.h>
> #include <linux/fs_struct.h>
> -#include <linux/ima.h>
> #include <linux/dnotify.h>
> #include <linux/compat.h>
> #include <linux/mnt_idmapping.h>
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 0f9af283cbc8..23ae24b60ecf 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -16,24 +16,6 @@ struct linux_binprm;
>
> #ifdef CONFIG_IMA
> extern enum hash_algo ima_get_current_hash_algo(void);
> -extern int ima_bprm_check(struct linux_binprm *bprm);
> -extern int ima_file_check(struct file *file, int mask);
> -extern void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> - struct inode *inode);
> -extern void ima_file_free(struct file *file);
> -extern int ima_file_mmap(struct file *file, unsigned long reqprot,
> - unsigned long prot, unsigned long flags);
> -extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
> - unsigned long prot);
> -extern int ima_load_data(enum kernel_load_data_id id, bool contents);
> -extern int ima_post_load_data(char *buf, loff_t size,
> - enum kernel_load_data_id id, char *description);
> -extern int ima_read_file(struct file *file, enum kernel_read_file_id id,
> - bool contents);
> -extern int ima_post_read_file(struct file *file, char *buf, loff_t size,
> - enum kernel_read_file_id id);
> -extern void ima_post_path_mknod(struct mnt_idmap *idmap,
> - struct dentry *dentry);
> extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
> extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size);
> extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
> @@ -58,68 +40,6 @@ static inline enum hash_algo ima_get_current_hash_algo(void)
> return HASH_ALGO__LAST;
> }
>
> -static inline int ima_bprm_check(struct linux_binprm *bprm)
> -{
> - return 0;
> -}
> -
> -static inline int ima_file_check(struct file *file, int mask)
> -{
> - return 0;
> -}
> -
> -static inline void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> - struct inode *inode)
> -{
> -}
> -
> -static inline void ima_file_free(struct file *file)
> -{
> - return;
> -}
> -
> -static inline int ima_file_mmap(struct file *file, unsigned long reqprot,
> - unsigned long prot, unsigned long flags)
> -{
> - return 0;
> -}
> -
> -static inline int ima_file_mprotect(struct vm_area_struct *vma,
> - unsigned long reqprot, unsigned long prot)
> -{
> - return 0;
> -}
> -
> -static inline int ima_load_data(enum kernel_load_data_id id, bool contents)
> -{
> - return 0;
> -}
> -
> -static inline int ima_post_load_data(char *buf, loff_t size,
> - enum kernel_load_data_id id,
> - char *description)
> -{
> - return 0;
> -}
> -
> -static inline int ima_read_file(struct file *file, enum kernel_read_file_id id,
> - bool contents)
> -{
> - return 0;
> -}
> -
> -static inline int ima_post_read_file(struct file *file, char *buf, loff_t size,
> - enum kernel_read_file_id id)
> -{
> - return 0;
> -}
> -
> -static inline void ima_post_path_mknod(struct mnt_idmap *idmap,
> - struct dentry *dentry)
> -{
> - return;
> -}
> -
> static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
> {
> return -EOPNOTSUPP;
> @@ -170,20 +90,6 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
> {}
> #endif
>
> -#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
> -extern void ima_post_key_create_or_update(struct key *keyring,
> - struct key *key,
> - const void *payload, size_t plen,
> - unsigned long flags, bool create);
> -#else
> -static inline void ima_post_key_create_or_update(struct key *keyring,
> - struct key *key,
> - const void *payload,
> - size_t plen,
> - unsigned long flags,
> - bool create) {}
> -#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
> -
> #ifdef CONFIG_IMA_APPRAISE
> extern bool is_ima_appraise_enabled(void);
> extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
> @@ -256,14 +162,4 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
> return false;
> }
> #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
> -
> -#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS)
> -extern int ima_kernel_module_request(char *kmod_name);
> -#else
> -static inline int ima_kernel_module_request(char *kmod_name)
> -{
> - return 0;
> -}
> -
> -#endif
> #endif /* _LINUX_IMA_H */
> diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
> index f8aef9ade549..b3b7fd699b63 100644
> --- a/include/uapi/linux/lsm.h
> +++ b/include/uapi/linux/lsm.h
> @@ -62,6 +62,7 @@ struct lsm_ctx {
> #define LSM_ID_LOCKDOWN 108
> #define LSM_ID_BPF 109
> #define LSM_ID_LANDLOCK 110
> +#define LSM_ID_IMA 111
>
> /*
> * LSM_ATTR_XXX definitions identify different LSM attributes
> diff --git a/security/integrity/Makefile b/security/integrity/Makefile
> index d0ffe37dc1d6..92b63039c654 100644
> --- a/security/integrity/Makefile
> +++ b/security/integrity/Makefile
> @@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
> integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
> platform_certs/load_powerpc.o \
> platform_certs/keyring_handler.o
> +# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
> obj-$(CONFIG_IMA) += ima/
> obj-$(CONFIG_EVM) += evm/
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index b98bfe9efd0c..475c32615006 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -8,6 +8,7 @@ config IMA
> select CRYPTO_HMAC
> select CRYPTO_SHA1
> select CRYPTO_HASH_INFO
> + select SECURITY_PATH
> select TCG_TPM if HAS_IOMEM
> select TCG_TIS if TCG_TPM && X86
> select TCG_CRB if TCG_TPM && ACPI
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index c29db699c996..c0412100023e 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -127,6 +127,12 @@ void ima_load_kexec_buffer(void);
> static inline void ima_load_kexec_buffer(void) {}
> #endif /* CONFIG_HAVE_IMA_KEXEC */
>
> +#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
> +void ima_post_key_create_or_update(struct key *keyring, struct key *key,
> + const void *payload, size_t plen,
> + unsigned long flags, bool create);
> +#endif
> +
> /*
> * The default binary_runtime_measurements list format is defined as the
> * platform native format. The canonical format is defined as little-endian.
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 908fa026ec58..483ccd24e214 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -189,7 +189,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
> *
> * Flag files that changed, based on i_version
> */
> -void ima_file_free(struct file *file)
> +static void ima_file_free(struct file *file)
> {
> struct inode *inode = file_inode(file);
> struct integrity_iint_cache *iint;
> @@ -427,8 +427,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
> * On success return 0. On integrity appraisal error, assuming the file
> * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
> */
> -int ima_file_mmap(struct file *file, unsigned long reqprot,
> - unsigned long prot, unsigned long flags)
> +static int ima_file_mmap(struct file *file, unsigned long reqprot,
> + unsigned long prot, unsigned long flags)
> {
> u32 secid;
> int ret;
> @@ -466,8 +466,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
> *
> * On mprotect change success, return 0. On failure, return -EACESS.
> */
> -int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
> - unsigned long prot)
> +static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
> + unsigned long prot)
> {
> struct ima_template_desc *template = NULL;
> struct file *file;
> @@ -525,7 +525,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
> * On success return 0. On integrity appraisal error, assuming the file
> * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
> */
> -int ima_bprm_check(struct linux_binprm *bprm)
> +static int ima_bprm_check(struct linux_binprm *bprm)
> {
> int ret;
> u32 secid;
> @@ -551,7 +551,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
> * On success return 0. On integrity appraisal error, assuming the file
> * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
> */
> -int ima_file_check(struct file *file, int mask)
> +static int ima_file_check(struct file *file, int mask)
> {
> u32 secid;
>
> @@ -560,7 +560,6 @@ int ima_file_check(struct file *file, int mask)
> mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
> MAY_APPEND), FILE_CHECK);
> }
> -EXPORT_SYMBOL_GPL(ima_file_check);
>
> static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
> size_t buf_size)
> @@ -685,8 +684,9 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
> * Skip calling process_measurement(), but indicate which newly, created
> * tmpfiles are in policy.
> */
> -void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> - struct inode *inode)
> +static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> + struct inode *inode)
> +
> {
> struct integrity_iint_cache *iint;
> int must_appraise;
> @@ -717,8 +717,7 @@ void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> * Mark files created via the mknodat syscall as new, so that the
> * file data can be written later.
> */
> -void ima_post_path_mknod(struct mnt_idmap *idmap,
> - struct dentry *dentry)
> +static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> {
> struct integrity_iint_cache *iint;
> struct inode *inode = dentry->d_inode;
> @@ -753,8 +752,8 @@ void ima_post_path_mknod(struct mnt_idmap *idmap,
> *
> * For permission return 0, otherwise return -EACCES.
> */
> -int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
> - bool contents)
> +static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
> + bool contents)
> {
> enum ima_hooks func;
> u32 secid;
> @@ -803,8 +802,8 @@ const int read_idmap[READING_MAX_ID] = {
> * On success return 0. On integrity appraisal error, assuming the file
> * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
> */
> -int ima_post_read_file(struct file *file, char *buf, loff_t size,
> - enum kernel_read_file_id read_id)
> +static int ima_post_read_file(struct file *file, char *buf, loff_t size,
> + enum kernel_read_file_id read_id)
> {
> enum ima_hooks func;
> u32 secid;
> @@ -837,7 +836,7 @@ int ima_post_read_file(struct file *file, char *buf, loff_t size,
> *
> * For permission return 0, otherwise return -EACCES.
> */
> -int ima_load_data(enum kernel_load_data_id id, bool contents)
> +static int ima_load_data(enum kernel_load_data_id id, bool contents)
> {
> bool ima_enforce, sig_enforce;
>
> @@ -891,9 +890,9 @@ int ima_load_data(enum kernel_load_data_id id, bool contents)
> * On success return 0. On integrity appraisal error, assuming the file
> * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
> */
> -int ima_post_load_data(char *buf, loff_t size,
> - enum kernel_load_data_id load_id,
> - char *description)
> +static int ima_post_load_data(char *buf, loff_t size,
> + enum kernel_load_data_id load_id,
> + char *description)
> {
> if (load_id == LOADING_FIRMWARE) {
> if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
> @@ -1110,7 +1109,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data);
> *
> * Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
> */
> -int ima_kernel_module_request(char *kmod_name)
> +static int ima_kernel_module_request(char *kmod_name)
> {
> if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
> return -EINVAL;
> @@ -1151,4 +1150,41 @@ static int __init init_ima(void)
> return error;
> }
>
> +static struct security_hook_list ima_hooks[] __ro_after_init = {
> + LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
> + LSM_HOOK_INIT(file_post_open, ima_file_check),
> + LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
> + LSM_HOOK_INIT(file_release, ima_file_free),
> + LSM_HOOK_INIT(mmap_file, ima_file_mmap),
> + LSM_HOOK_INIT(file_mprotect, ima_file_mprotect),
> + LSM_HOOK_INIT(kernel_load_data, ima_load_data),
> + LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data),
> + LSM_HOOK_INIT(kernel_read_file, ima_read_file),
> + LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file),
> + LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
> +#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
> + LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update),
> +#endif
> +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
> + LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request),
> +#endif
> +};
> +
> +static const struct lsm_id ima_lsmid = {
> + .name = "ima",
> + .id = LSM_ID_IMA,
> +};
> +
> +static int __init init_ima_lsm(void)
> +{
> + security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
> + return 0;
> +}
> +
> +DEFINE_LSM(ima) = {
> + .name = "ima",
> + .init = init_ima_lsm,
> + .order = LSM_ORDER_LAST,
> +};
> +
> late_initcall(init_ima); /* Start IMA after the TPM is available */
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 9561db7cf6b4..59eaddd84434 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -18,6 +18,7 @@
> #include <crypto/hash.h>
> #include <linux/key.h>
> #include <linux/audit.h>
> +#include <linux/lsm_hooks.h>
>
> /* iint action cache flags */
> #define IMA_MEASURE 0x00000001
> diff --git a/security/keys/key.c b/security/keys/key.c
> index f75fe66c2f03..80fc2f203a0c 100644
> --- a/security/keys/key.c
> +++ b/security/keys/key.c
> @@ -13,7 +13,6 @@
> #include <linux/security.h>
> #include <linux/workqueue.h>
> #include <linux/random.h>
> -#include <linux/ima.h>
> #include <linux/err.h>
> #include "internal.h"
>
> @@ -937,8 +936,6 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,
>
> security_key_post_create_or_update(keyring, key, payload, plen, flags,
> true);
> - ima_post_key_create_or_update(keyring, key, payload, plen,
> - flags, true);
>
> key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
>
> @@ -970,13 +967,9 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,
>
> key_ref = __key_update(key_ref, &prep);
>
> - if (!IS_ERR(key_ref)) {
> + if (!IS_ERR(key_ref))
> security_key_post_create_or_update(keyring, key, payload, plen,
> flags, false);
> - ima_post_key_create_or_update(keyring, key,
> - payload, plen,
> - flags, false);
> - }
>
> goto error_free_prep;
> }
> diff --git a/security/security.c b/security/security.c
> index 6c6571a141a1..aa17b47d44c1 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -50,7 +50,8 @@
> (IS_ENABLED(CONFIG_SECURITY_SAFESETID) ? 1 : 0) + \
> (IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \
> (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \
> - (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0))
> + (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \
> + (IS_ENABLED(CONFIG_IMA) ? 1 : 0))
>
> /*
> * These are descriptions of the reasons that can be passed to the
> @@ -1182,12 +1183,7 @@ int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *
> */
> int security_bprm_check(struct linux_binprm *bprm)
> {
> - int ret;
> -
> - ret = call_int_hook(bprm_check_security, 0, bprm);
> - if (ret)
> - return ret;
> - return ima_bprm_check(bprm);
> + return call_int_hook(bprm_check_security, 0, bprm);
> }
>
> /**
> @@ -2901,13 +2897,8 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
> int security_mmap_file(struct file *file, unsigned long prot,
> unsigned long flags)
> {
> - unsigned long prot_adj = mmap_prot(file, prot);
> - int ret;
> -
> - ret = call_int_hook(mmap_file, 0, file, prot, prot_adj, flags);
> - if (ret)
> - return ret;
> - return ima_file_mmap(file, prot, prot_adj, flags);
> + return call_int_hook(mmap_file, 0, file, prot, mmap_prot(file, prot),
> + flags);
> }
>
> /**
> @@ -2936,12 +2927,7 @@ int security_mmap_addr(unsigned long addr)
> int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
> unsigned long prot)
> {
> - int ret;
> -
> - ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot);
> - if (ret)
> - return ret;
> - return ima_file_mprotect(vma, reqprot, prot);
> + return call_int_hook(file_mprotect, 0, vma, reqprot, prot);
> }
>
> /**
> @@ -3250,12 +3236,7 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
> */
> int security_kernel_module_request(char *kmod_name)
> {
> - int ret;
> -
> - ret = call_int_hook(kernel_module_request, 0, kmod_name);
> - if (ret)
> - return ret;
> - return ima_kernel_module_request(kmod_name);
> + return call_int_hook(kernel_module_request, 0, kmod_name);
> }
>
> /**
> @@ -3271,12 +3252,7 @@ int security_kernel_module_request(char *kmod_name)
> int security_kernel_read_file(struct file *file, enum kernel_read_file_id id,
> bool contents)
> {
> - int ret;
> -
> - ret = call_int_hook(kernel_read_file, 0, file, id, contents);
> - if (ret)
> - return ret;
> - return ima_read_file(file, id, contents);
> + return call_int_hook(kernel_read_file, 0, file, id, contents);
> }
> EXPORT_SYMBOL_GPL(security_kernel_read_file);
>
> @@ -3296,12 +3272,7 @@ EXPORT_SYMBOL_GPL(security_kernel_read_file);
> int security_kernel_post_read_file(struct file *file, char *buf, loff_t size,
> enum kernel_read_file_id id)
> {
> - int ret;
> -
> - ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
> - if (ret)
> - return ret;
> - return ima_post_read_file(file, buf, size, id);
> + return call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
> }
> EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
>
> @@ -3316,12 +3287,7 @@ EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
> */
> int security_kernel_load_data(enum kernel_load_data_id id, bool contents)
> {
> - int ret;
> -
> - ret = call_int_hook(kernel_load_data, 0, id, contents);
> - if (ret)
> - return ret;
> - return ima_load_data(id, contents);
> + return call_int_hook(kernel_load_data, 0, id, contents);
> }
> EXPORT_SYMBOL_GPL(security_kernel_load_data);
>
> @@ -3343,13 +3309,8 @@ int security_kernel_post_load_data(char *buf, loff_t size,
> enum kernel_load_data_id id,
> char *description)
> {
> - int ret;
> -
> - ret = call_int_hook(kernel_post_load_data, 0, buf, size, id,
> - description);
> - if (ret)
> - return ret;
> - return ima_post_load_data(buf, size, id, description);
> + return call_int_hook(kernel_post_load_data, 0, buf, size, id,
> + description);
> }
> EXPORT_SYMBOL_GPL(security_kernel_post_load_data);
>
> diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c
> index 9df29b1e3497..17333787cb2f 100644
> --- a/tools/testing/selftests/lsm/lsm_list_modules_test.c
> +++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c
> @@ -122,6 +122,9 @@ TEST(correct_lsm_list_modules)
> case LSM_ID_LANDLOCK:
> name = "landlock";
> break;
> + case LSM_ID_IMA:
> + name = "ima";
> + break;
> default:
> name = "INVALID";
> break;

2024-01-16 19:04:09

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v9 21/25] ima: Move IMA-Appraisal to LSM infrastructure

On 1/15/2024 10:18 AM, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> A few additional IMA hooks are needed to reset the cached appraisal
> status, causing the file's integrity to be re-evaluated on next access.
> Register these IMA-appraisal only functions separately from the rest of IMA
> functions, as appraisal is a separate feature not necessarily enabled in
> the kernel configuration.
>
> Reuse the same approach as for other IMA functions, move hardcoded calls
> from various places in the kernel to the LSM infrastructure. Declare the
> functions as static and register them as hook implementations in
> init_ima_appraise_lsm(), called by init_ima_lsm().
>
> Also move the inline function ima_inode_remove_acl() from the public ima.h
> header to ima_appraise.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>

Reviewed-by: Casey Schaufler <[email protected]>

> ---
> fs/attr.c | 2 -
> include/linux/ima.h | 55 ---------------------------
> security/integrity/ima/ima.h | 5 +++
> security/integrity/ima/ima_appraise.c | 38 +++++++++++++-----
> security/integrity/ima/ima_main.c | 1 +
> security/security.c | 13 -------
> 6 files changed, 35 insertions(+), 79 deletions(-)
>
> diff --git a/fs/attr.c b/fs/attr.c
> index 221d2bb0a906..38841f3ebbcb 100644
> --- a/fs/attr.c
> +++ b/fs/attr.c
> @@ -17,7 +17,6 @@
> #include <linux/filelock.h>
> #include <linux/security.h>
> #include <linux/evm.h>
> -#include <linux/ima.h>
>
> #include "internal.h"
>
> @@ -503,7 +502,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
> if (!error) {
> fsnotify_change(dentry, ia_valid);
> security_inode_post_setattr(idmap, dentry, ia_valid);
> - ima_inode_post_setattr(idmap, dentry, ia_valid);
> evm_inode_post_setattr(idmap, dentry, ia_valid);
> }
>
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 23ae24b60ecf..0bae61a15b60 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -92,66 +92,11 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
>
> #ifdef CONFIG_IMA_APPRAISE
> extern bool is_ima_appraise_enabled(void);
> -extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
> - struct dentry *dentry, int ia_valid);
> -extern int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> - const char *xattr_name, const void *xattr_value,
> - size_t xattr_value_len, int flags);
> -extern int ima_inode_set_acl(struct mnt_idmap *idmap,
> - struct dentry *dentry, const char *acl_name,
> - struct posix_acl *kacl);
> -static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
> - struct dentry *dentry,
> - const char *acl_name)
> -{
> - return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
> -}
> -
> -extern int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
> - const char *xattr_name);
> #else
> static inline bool is_ima_appraise_enabled(void)
> {
> return 0;
> }
> -
> -static inline void ima_inode_post_setattr(struct mnt_idmap *idmap,
> - struct dentry *dentry, int ia_valid)
> -{
> - return;
> -}
> -
> -static inline int ima_inode_setxattr(struct mnt_idmap *idmap,
> - struct dentry *dentry,
> - const char *xattr_name,
> - const void *xattr_value,
> - size_t xattr_value_len,
> - int flags)
> -{
> - return 0;
> -}
> -
> -static inline int ima_inode_set_acl(struct mnt_idmap *idmap,
> - struct dentry *dentry, const char *acl_name,
> - struct posix_acl *kacl)
> -{
> -
> - return 0;
> -}
> -
> -static inline int ima_inode_removexattr(struct mnt_idmap *idmap,
> - struct dentry *dentry,
> - const char *xattr_name)
> -{
> - return 0;
> -}
> -
> -static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
> - struct dentry *dentry,
> - const char *acl_name)
> -{
> - return 0;
> -}
> #endif /* CONFIG_IMA_APPRAISE */
>
> #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index c0412100023e..a27fc10f84f7 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -334,6 +334,7 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
> int xattr_len);
> int ima_read_xattr(struct dentry *dentry,
> struct evm_ima_xattr_data **xattr_value, int xattr_len);
> +void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);
>
> #else
> static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
> @@ -385,6 +386,10 @@ static inline int ima_read_xattr(struct dentry *dentry,
> return 0;
> }
>
> +static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
> +{
> +}
> +
> #endif /* CONFIG_IMA_APPRAISE */
>
> #ifdef CONFIG_IMA_APPRAISE_MODSIG
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> index 36abc84ba299..076451109637 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -636,8 +636,8 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
> * This function is called from notify_change(), which expects the caller
> * to lock the inode's i_mutex.
> */
> -void ima_inode_post_setattr(struct mnt_idmap *idmap,
> - struct dentry *dentry, int ia_valid)
> +static void ima_inode_post_setattr(struct mnt_idmap *idmap,
> + struct dentry *dentry, int ia_valid)
> {
> struct inode *inode = d_backing_inode(dentry);
> struct integrity_iint_cache *iint;
> @@ -750,9 +750,9 @@ static int validate_hash_algo(struct dentry *dentry,
> return -EACCES;
> }
>
> -int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> - const char *xattr_name, const void *xattr_value,
> - size_t xattr_value_len, int flags)
> +static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> + const char *xattr_name, const void *xattr_value,
> + size_t xattr_value_len, int flags)
> {
> const struct evm_ima_xattr_data *xvalue = xattr_value;
> int digsig = 0;
> @@ -781,8 +781,8 @@ int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> return result;
> }
>
> -int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> - const char *acl_name, struct posix_acl *kacl)
> +static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> + const char *acl_name, struct posix_acl *kacl)
> {
> if (evm_revalidate_status(acl_name))
> ima_reset_appraise_flags(d_backing_inode(dentry), 0);
> @@ -790,8 +790,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> return 0;
> }
>
> -int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
> - const char *xattr_name)
> +static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
> + const char *xattr_name)
> {
> int result;
>
> @@ -803,3 +803,23 @@ int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
> }
> return result;
> }
> +
> +static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> + const char *acl_name)
> +{
> + return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
> +}
> +
> +static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
> + LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
> + LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr),
> + LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
> + LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr),
> + LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
> +};
> +
> +void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
> +{
> + security_add_hooks(ima_appraise_hooks, ARRAY_SIZE(ima_appraise_hooks),
> + lsmid);
> +}
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 483ccd24e214..77d0d4e4b582 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -1178,6 +1178,7 @@ static const struct lsm_id ima_lsmid = {
> static int __init init_ima_lsm(void)
> {
> security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
> + init_ima_appraise_lsm(&ima_lsmid);
> return 0;
> }
>
> diff --git a/security/security.c b/security/security.c
> index aa17b47d44c1..c07ad3c5f767 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -20,7 +20,6 @@
> #include <linux/kernel_read_file.h>
> #include <linux/lsm_hooks.h>
> #include <linux/integrity.h>
> -#include <linux/ima.h>
> #include <linux/evm.h>
> #include <linux/fsnotify.h>
> #include <linux/mman.h>
> @@ -2308,9 +2307,6 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
>
> if (ret == 1)
> ret = cap_inode_setxattr(dentry, name, value, size, flags);
> - if (ret)
> - return ret;
> - ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags);
> if (ret)
> return ret;
> return evm_inode_setxattr(idmap, dentry, name, value, size, flags);
> @@ -2338,9 +2334,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
> return 0;
> ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
> kacl);
> - if (ret)
> - return ret;
> - ret = ima_inode_set_acl(idmap, dentry, acl_name, kacl);
> if (ret)
> return ret;
> return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
> @@ -2401,9 +2394,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap,
> if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> return 0;
> ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
> - if (ret)
> - return ret;
> - ret = ima_inode_remove_acl(idmap, dentry, acl_name);
> if (ret)
> return ret;
> return evm_inode_remove_acl(idmap, dentry, acl_name);
> @@ -2503,9 +2493,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
> ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name);
> if (ret == 1)
> ret = cap_inode_removexattr(idmap, dentry, name);
> - if (ret)
> - return ret;
> - ret = ima_inode_removexattr(idmap, dentry, name);
> if (ret)
> return ret;
> return evm_inode_removexattr(idmap, dentry, name);

2024-01-16 19:39:20

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v9 23/25] evm: Make it independent from 'integrity' LSM

On 1/15/2024 10:18 AM, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Define a new structure for EVM-specific metadata, called evm_iint_cache,
> and embed it in the inode security blob. Introduce evm_iint_inode() to
> retrieve metadata, and register evm_inode_alloc_security() for the
> inode_alloc_security LSM hook, to initialize the structure (before
> splitting metadata, this task was done by iint_init_always()).
>
> Keep the non-NULL checks after calling evm_iint_inode() except in
> evm_inode_alloc_security(), to take into account inodes for which
> security_inode_alloc() was not called. When using shared metadata,
> obtaining a NULL pointer from integrity_iint_find() meant that the file
> wasn't in the IMA policy. Now, because IMA and EVM use disjoint metadata,
> the EVM status has to be stored for every inode regardless of the IMA
> policy.
>
> Given that from now on EVM relies on its own metadata, remove the iint
> parameter from evm_verifyxattr(). Also, directly retrieve the iint in
> evm_verify_hmac(), called by both evm_verifyxattr() and
> evm_verify_current_integrity(), since now there is no performance penalty
> in retrieving EVM metadata (constant time).
>
> Replicate the management of the IMA_NEW_FILE flag, by introducing
> evm_post_path_mknod() and evm_file_release() to respectively set and clear
> the newly introduced flag EVM_NEW_FILE, at the same time IMA does. Like for
> IMA, select CONFIG_SECURITY_PATH when EVM is enabled, to ensure that files
> are marked as new.
>
> Unlike ima_post_path_mknod(), evm_post_path_mknod() cannot check if a file
> must be appraised. Thus, it marks all affected files. Also, it does not
> clear EVM_NEW_FILE depending on i_version, but that is not a problem
> because IMA_NEW_FILE is always cleared when set in ima_check_last_writer().
>
> Move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to
> security/integrity/evm/evm.h, since that definition is now unnecessary in
> the common integrity layer.
>
> Finally, switch to the LSM reservation mechanism for the EVM xattr, and
> consequently decrement by one the number of xattrs to allocate in
> security_inode_init_security().
>
> Signed-off-by: Roberto Sassu <[email protected]>

Reviewed-by: Casey Schaufler <[email protected]>

> ---
> include/linux/evm.h | 8 +--
> security/integrity/evm/Kconfig | 1 +
> security/integrity/evm/evm.h | 19 +++++++
> security/integrity/evm/evm_crypto.c | 4 +-
> security/integrity/evm/evm_main.c | 76 ++++++++++++++++++++-------
> security/integrity/ima/ima_appraise.c | 2 +-
> security/integrity/integrity.h | 1 -
> security/security.c | 4 +-
> 8 files changed, 83 insertions(+), 32 deletions(-)
>
> diff --git a/include/linux/evm.h b/include/linux/evm.h
> index cb481eccc967..d48d6da32315 100644
> --- a/include/linux/evm.h
> +++ b/include/linux/evm.h
> @@ -12,15 +12,12 @@
> #include <linux/integrity.h>
> #include <linux/xattr.h>
>
> -struct integrity_iint_cache;
> -
> #ifdef CONFIG_EVM
> extern int evm_set_key(void *key, size_t keylen);
> extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
> const char *xattr_name,
> void *xattr_value,
> - size_t xattr_value_len,
> - struct integrity_iint_cache *iint);
> + size_t xattr_value_len);
> int evm_inode_init_security(struct inode *inode, struct inode *dir,
> const struct qstr *qstr, struct xattr *xattrs,
> int *xattr_count);
> @@ -48,8 +45,7 @@ static inline int evm_set_key(void *key, size_t keylen)
> static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
> const char *xattr_name,
> void *xattr_value,
> - size_t xattr_value_len,
> - struct integrity_iint_cache *iint)
> + size_t xattr_value_len)
> {
> return INTEGRITY_UNKNOWN;
> }
> diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
> index fba9ee359bc9..861b3bacab82 100644
> --- a/security/integrity/evm/Kconfig
> +++ b/security/integrity/evm/Kconfig
> @@ -6,6 +6,7 @@ config EVM
> select CRYPTO_HMAC
> select CRYPTO_SHA1
> select CRYPTO_HASH_INFO
> + select SECURITY_PATH
> default n
> help
> EVM protects a file's security extended attributes against
> diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
> index 53bd7fec93fa..eb1a2c343bd7 100644
> --- a/security/integrity/evm/evm.h
> +++ b/security/integrity/evm/evm.h
> @@ -32,6 +32,25 @@ struct xattr_list {
> bool enabled;
> };
>
> +#define EVM_NEW_FILE 0x00000001
> +#define EVM_IMMUTABLE_DIGSIG 0x00000002
> +
> +/* EVM integrity metadata associated with an inode */
> +struct evm_iint_cache {
> + unsigned long flags;
> + enum integrity_status evm_status:4;
> +};
> +
> +extern struct lsm_blob_sizes evm_blob_sizes;
> +
> +static inline struct evm_iint_cache *evm_iint_inode(const struct inode *inode)
> +{
> + if (unlikely(!inode->i_security))
> + return NULL;
> +
> + return inode->i_security + evm_blob_sizes.lbs_inode;
> +}
> +
> extern int evm_initialized;
>
> #define EVM_ATTR_FSUUID 0x0001
> diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
> index b1ffd4cc0b44..7552d49d0725 100644
> --- a/security/integrity/evm/evm_crypto.c
> +++ b/security/integrity/evm/evm_crypto.c
> @@ -322,10 +322,10 @@ int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
> static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
> {
> const struct evm_ima_xattr_data *xattr_data = NULL;
> - struct integrity_iint_cache *iint;
> + struct evm_iint_cache *iint;
> int rc = 0;
>
> - iint = integrity_iint_find(inode);
> + iint = evm_iint_inode(inode);
> if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
> return 1;
>
> diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
> index f65dbf3e9256..14c8f38e61d6 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -178,14 +178,14 @@ static int is_unsupported_fs(struct dentry *dentry)
> static enum integrity_status evm_verify_hmac(struct dentry *dentry,
> const char *xattr_name,
> char *xattr_value,
> - size_t xattr_value_len,
> - struct integrity_iint_cache *iint)
> + size_t xattr_value_len)
> {
> struct evm_ima_xattr_data *xattr_data = NULL;
> struct signature_v2_hdr *hdr;
> enum integrity_status evm_status = INTEGRITY_PASS;
> struct evm_digest digest;
> - struct inode *inode;
> + struct inode *inode = d_backing_inode(dentry);
> + struct evm_iint_cache *iint = evm_iint_inode(inode);
> int rc, xattr_len, evm_immutable = 0;
>
> if (iint && (iint->evm_status == INTEGRITY_PASS ||
> @@ -254,8 +254,6 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
> (const char *)xattr_data, xattr_len,
> digest.digest, digest.hdr.length);
> if (!rc) {
> - inode = d_backing_inode(dentry);
> -
> if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
> if (iint)
> iint->flags |= EVM_IMMUTABLE_DIGSIG;
> @@ -403,7 +401,6 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
> * @xattr_name: requested xattr
> * @xattr_value: requested xattr value
> * @xattr_value_len: requested xattr value length
> - * @iint: inode integrity metadata
> *
> * Calculate the HMAC for the given dentry and verify it against the stored
> * security.evm xattr. For performance, use the xattr value and length
> @@ -416,8 +413,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
> */
> enum integrity_status evm_verifyxattr(struct dentry *dentry,
> const char *xattr_name,
> - void *xattr_value, size_t xattr_value_len,
> - struct integrity_iint_cache *iint)
> + void *xattr_value, size_t xattr_value_len)
> {
> if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
> return INTEGRITY_UNKNOWN;
> @@ -425,13 +421,8 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
> if (is_unsupported_fs(dentry))
> return INTEGRITY_UNKNOWN;
>
> - if (!iint) {
> - iint = integrity_iint_find(d_backing_inode(dentry));
> - if (!iint)
> - return INTEGRITY_UNKNOWN;
> - }
> return evm_verify_hmac(dentry, xattr_name, xattr_value,
> - xattr_value_len, iint);
> + xattr_value_len);
> }
> EXPORT_SYMBOL_GPL(evm_verifyxattr);
>
> @@ -448,7 +439,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
>
> if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
> return INTEGRITY_PASS;
> - return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
> + return evm_verify_hmac(dentry, NULL, NULL, 0);
> }
>
> /*
> @@ -526,14 +517,14 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
>
> evm_status = evm_verify_current_integrity(dentry);
> if (evm_status == INTEGRITY_NOXATTRS) {
> - struct integrity_iint_cache *iint;
> + struct evm_iint_cache *iint;
>
> /* Exception if the HMAC is not going to be calculated. */
> if (evm_hmac_disabled())
> return 0;
>
> - iint = integrity_iint_find(d_backing_inode(dentry));
> - if (iint && (iint->flags & IMA_NEW_FILE))
> + iint = evm_iint_inode(d_backing_inode(dentry));
> + if (iint && (iint->flags & EVM_NEW_FILE))
> return 0;
>
> /* exception for pseudo filesystems */
> @@ -735,9 +726,9 @@ static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
>
> static void evm_reset_status(struct inode *inode)
> {
> - struct integrity_iint_cache *iint;
> + struct evm_iint_cache *iint;
>
> - iint = integrity_iint_find(inode);
> + iint = evm_iint_inode(inode);
> if (iint)
> iint->evm_status = INTEGRITY_UNKNOWN;
> }
> @@ -1019,6 +1010,42 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
> }
> EXPORT_SYMBOL_GPL(evm_inode_init_security);
>
> +static int evm_inode_alloc_security(struct inode *inode)
> +{
> + struct evm_iint_cache *iint = evm_iint_inode(inode);
> +
> + /* Called by security_inode_alloc(), it cannot be NULL. */
> + iint->flags = 0UL;
> + iint->evm_status = INTEGRITY_UNKNOWN;
> +
> + return 0;
> +}
> +
> +static void evm_file_release(struct file *file)
> +{
> + struct inode *inode = file_inode(file);
> + struct evm_iint_cache *iint = evm_iint_inode(inode);
> + fmode_t mode = file->f_mode;
> +
> + if (!S_ISREG(inode->i_mode) || !(mode & FMODE_WRITE))
> + return;
> +
> + if (iint && atomic_read(&inode->i_writecount) == 1)
> + iint->flags &= ~EVM_NEW_FILE;
> +}
> +
> +static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> +{
> + struct inode *inode = d_backing_inode(dentry);
> + struct evm_iint_cache *iint = evm_iint_inode(inode);
> +
> + if (!S_ISREG(inode->i_mode))
> + return;
> +
> + if (iint)
> + iint->flags |= EVM_NEW_FILE;
> +}
> +
> #ifdef CONFIG_EVM_LOAD_X509
> void __init evm_load_x509(void)
> {
> @@ -1071,6 +1098,9 @@ static struct security_hook_list evm_hooks[] __ro_after_init = {
> LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr),
> LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr),
> LSM_HOOK_INIT(inode_init_security, evm_inode_init_security),
> + LSM_HOOK_INIT(inode_alloc_security, evm_inode_alloc_security),
> + LSM_HOOK_INIT(file_release, evm_file_release),
> + LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod),
> };
>
> static const struct lsm_id evm_lsmid = {
> @@ -1084,10 +1114,16 @@ static int __init init_evm_lsm(void)
> return 0;
> }
>
> +struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
> + .lbs_inode = sizeof(struct evm_iint_cache),
> + .lbs_xattr_count = 1,
> +};
> +
> DEFINE_LSM(evm) = {
> .name = "evm",
> .init = init_evm_lsm,
> .order = LSM_ORDER_LAST,
> + .blobs = &evm_blob_sizes,
> };
>
> late_initcall(init_evm);
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> index 076451109637..1dd6ee72a20a 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -520,7 +520,7 @@ int ima_appraise_measurement(enum ima_hooks func,
> }
>
> status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
> - rc < 0 ? 0 : rc, iint);
> + rc < 0 ? 0 : rc);
> switch (status) {
> case INTEGRITY_PASS:
> case INTEGRITY_PASS_IMMUTABLE:
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 59eaddd84434..7a97c269a072 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -37,7 +37,6 @@
> #define IMA_DIGSIG_REQUIRED 0x01000000
> #define IMA_PERMIT_DIRECTIO 0x02000000
> #define IMA_NEW_FILE 0x04000000
> -#define EVM_IMMUTABLE_DIGSIG 0x08000000
> #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
> #define IMA_MODSIG_ALLOWED 0x20000000
> #define IMA_CHECK_BLACKLIST 0x40000000
> diff --git a/security/security.c b/security/security.c
> index a44740640a9a..f811cc376a7a 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1716,8 +1716,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
> return 0;
>
> if (initxattrs) {
> - /* Allocate +1 for EVM and +1 as terminator. */
> - new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 2,
> + /* Allocate +1 as terminator. */
> + new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
> sizeof(*new_xattrs), GFP_NOFS);
> if (!new_xattrs)
> return -ENOMEM;

2024-01-16 19:41:24

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v9 25/25] integrity: Remove LSM

On 1/15/2024 10:18 AM, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Since now IMA and EVM use their own integrity metadata, it is safe to
> remove the 'integrity' LSM, with its management of integrity metadata.
>
> Keep the iint.c file only for loading IMA and EVM keys at boot, and for
> creating the integrity directory in securityfs (we need to keep it for
> retrocompatibility reasons).
>
> Signed-off-by: Roberto Sassu <[email protected]>

Reviewed-by: Casey Schaufler <[email protected]>

> ---
> include/linux/integrity.h | 14 ---
> security/integrity/iint.c | 197 +--------------------------------
> security/integrity/integrity.h | 25 -----
> security/security.c | 2 -
> 4 files changed, 2 insertions(+), 236 deletions(-)
>
> diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> index ef0f63ef5ebc..459b79683783 100644
> --- a/include/linux/integrity.h
> +++ b/include/linux/integrity.h
> @@ -19,24 +19,10 @@ enum integrity_status {
> INTEGRITY_UNKNOWN,
> };
>
> -/* List of EVM protected security xattrs */
> #ifdef CONFIG_INTEGRITY
> -extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
> -extern void integrity_inode_free(struct inode *inode);
> extern void __init integrity_load_keys(void);
>
> #else
> -static inline struct integrity_iint_cache *
> - integrity_inode_get(struct inode *inode)
> -{
> - return NULL;
> -}
> -
> -static inline void integrity_inode_free(struct inode *inode)
> -{
> - return;
> -}
> -
> static inline void integrity_load_keys(void)
> {
> }
> diff --git a/security/integrity/iint.c b/security/integrity/iint.c
> index d4419a2a1e24..068ac6c2ae1e 100644
> --- a/security/integrity/iint.c
> +++ b/security/integrity/iint.c
> @@ -6,207 +6,14 @@
> * Mimi Zohar <[email protected]>
> *
> * File: integrity_iint.c
> - * - implements the integrity hooks: integrity_inode_alloc,
> - * integrity_inode_free
> - * - cache integrity information associated with an inode
> - * using a rbtree tree.
> + * - initialize the integrity directory in securityfs
> + * - load IMA and EVM keys
> */
> -#include <linux/slab.h>
> -#include <linux/init.h>
> -#include <linux/spinlock.h>
> -#include <linux/rbtree.h>
> -#include <linux/file.h>
> -#include <linux/uaccess.h>
> #include <linux/security.h>
> -#include <linux/lsm_hooks.h>
> #include "integrity.h"
>
> -static struct rb_root integrity_iint_tree = RB_ROOT;
> -static DEFINE_RWLOCK(integrity_iint_lock);
> -static struct kmem_cache *iint_cache __ro_after_init;
> -
> struct dentry *integrity_dir;
>
> -/*
> - * __integrity_iint_find - return the iint associated with an inode
> - */
> -static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
> -{
> - struct integrity_iint_cache *iint;
> - struct rb_node *n = integrity_iint_tree.rb_node;
> -
> - while (n) {
> - iint = rb_entry(n, struct integrity_iint_cache, rb_node);
> -
> - if (inode < iint->inode)
> - n = n->rb_left;
> - else if (inode > iint->inode)
> - n = n->rb_right;
> - else
> - return iint;
> - }
> -
> - return NULL;
> -}
> -
> -/*
> - * integrity_iint_find - return the iint associated with an inode
> - */
> -struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
> -{
> - struct integrity_iint_cache *iint;
> -
> - if (!IS_IMA(inode))
> - return NULL;
> -
> - read_lock(&integrity_iint_lock);
> - iint = __integrity_iint_find(inode);
> - read_unlock(&integrity_iint_lock);
> -
> - return iint;
> -}
> -
> -#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1)
> -
> -/*
> - * It is not clear that IMA should be nested at all, but as long is it measures
> - * files both on overlayfs and on underlying fs, we need to annotate the iint
> - * mutex to avoid lockdep false positives related to IMA + overlayfs.
> - * See ovl_lockdep_annotate_inode_mutex_key() for more details.
> - */
> -static inline void iint_lockdep_annotate(struct integrity_iint_cache *iint,
> - struct inode *inode)
> -{
> -#ifdef CONFIG_LOCKDEP
> - static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING];
> -
> - int depth = inode->i_sb->s_stack_depth;
> -
> - if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
> - depth = 0;
> -
> - lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]);
> -#endif
> -}
> -
> -static void iint_init_always(struct integrity_iint_cache *iint,
> - struct inode *inode)
> -{
> - iint->ima_hash = NULL;
> - iint->version = 0;
> - iint->flags = 0UL;
> - iint->atomic_flags = 0UL;
> - iint->ima_file_status = INTEGRITY_UNKNOWN;
> - iint->ima_mmap_status = INTEGRITY_UNKNOWN;
> - iint->ima_bprm_status = INTEGRITY_UNKNOWN;
> - iint->ima_read_status = INTEGRITY_UNKNOWN;
> - iint->ima_creds_status = INTEGRITY_UNKNOWN;
> - iint->evm_status = INTEGRITY_UNKNOWN;
> - iint->measured_pcrs = 0;
> - mutex_init(&iint->mutex);
> - iint_lockdep_annotate(iint, inode);
> -}
> -
> -static void iint_free(struct integrity_iint_cache *iint)
> -{
> - kfree(iint->ima_hash);
> - mutex_destroy(&iint->mutex);
> - kmem_cache_free(iint_cache, iint);
> -}
> -
> -/**
> - * integrity_inode_get - find or allocate an iint associated with an inode
> - * @inode: pointer to the inode
> - * @return: allocated iint
> - *
> - * Caller must lock i_mutex
> - */
> -struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
> -{
> - struct rb_node **p;
> - struct rb_node *node, *parent = NULL;
> - struct integrity_iint_cache *iint, *test_iint;
> -
> - iint = integrity_iint_find(inode);
> - if (iint)
> - return iint;
> -
> - iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
> - if (!iint)
> - return NULL;
> -
> - iint_init_always(iint, inode);
> -
> - write_lock(&integrity_iint_lock);
> -
> - p = &integrity_iint_tree.rb_node;
> - while (*p) {
> - parent = *p;
> - test_iint = rb_entry(parent, struct integrity_iint_cache,
> - rb_node);
> - if (inode < test_iint->inode) {
> - p = &(*p)->rb_left;
> - } else if (inode > test_iint->inode) {
> - p = &(*p)->rb_right;
> - } else {
> - write_unlock(&integrity_iint_lock);
> - kmem_cache_free(iint_cache, iint);
> - return test_iint;
> - }
> - }
> -
> - iint->inode = inode;
> - node = &iint->rb_node;
> - inode->i_flags |= S_IMA;
> - rb_link_node(node, parent, p);
> - rb_insert_color(node, &integrity_iint_tree);
> -
> - write_unlock(&integrity_iint_lock);
> - return iint;
> -}
> -
> -/**
> - * integrity_inode_free - called on security_inode_free
> - * @inode: pointer to the inode
> - *
> - * Free the integrity information(iint) associated with an inode.
> - */
> -void integrity_inode_free(struct inode *inode)
> -{
> - struct integrity_iint_cache *iint;
> -
> - if (!IS_IMA(inode))
> - return;
> -
> - write_lock(&integrity_iint_lock);
> - iint = __integrity_iint_find(inode);
> - rb_erase(&iint->rb_node, &integrity_iint_tree);
> - write_unlock(&integrity_iint_lock);
> -
> - iint_free(iint);
> -}
> -
> -static void iint_init_once(void *foo)
> -{
> - struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo;
> -
> - memset(iint, 0, sizeof(*iint));
> -}
> -
> -static int __init integrity_iintcache_init(void)
> -{
> - iint_cache =
> - kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
> - 0, SLAB_PANIC, iint_init_once);
> - return 0;
> -}
> -DEFINE_LSM(integrity) = {
> - .name = "integrity",
> - .init = integrity_iintcache_init,
> - .order = LSM_ORDER_LAST,
> -};
> -
> -
> /*
> * integrity_kernel_read - read data from the file
> *
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 671fc50255f9..50d6f798e613 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -102,31 +102,6 @@ struct ima_file_id {
> __u8 hash[HASH_MAX_DIGESTSIZE];
> } __packed;
>
> -/* integrity data associated with an inode */
> -struct integrity_iint_cache {
> - struct rb_node rb_node; /* rooted in integrity_iint_tree */
> - struct mutex mutex; /* protects: version, flags, digest */
> - struct inode *inode; /* back pointer to inode in question */
> - u64 version; /* track inode changes */
> - unsigned long flags;
> - unsigned long measured_pcrs;
> - unsigned long atomic_flags;
> - unsigned long real_ino;
> - dev_t real_dev;
> - enum integrity_status ima_file_status:4;
> - enum integrity_status ima_mmap_status:4;
> - enum integrity_status ima_bprm_status:4;
> - enum integrity_status ima_read_status:4;
> - enum integrity_status ima_creds_status:4;
> - enum integrity_status evm_status:4;
> - struct ima_digest_data *ima_hash;
> -};
> -
> -/* rbtree tree calls to lookup, insert, delete
> - * integrity data associated with an inode.
> - */
> -struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
> -
> int integrity_kernel_read(struct file *file, loff_t offset,
> void *addr, unsigned long count);
>
> diff --git a/security/security.c b/security/security.c
> index f811cc376a7a..df87c0a7eaac 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -19,7 +19,6 @@
> #include <linux/kernel.h>
> #include <linux/kernel_read_file.h>
> #include <linux/lsm_hooks.h>
> -#include <linux/integrity.h>
> #include <linux/fsnotify.h>
> #include <linux/mman.h>
> #include <linux/mount.h>
> @@ -1597,7 +1596,6 @@ static void inode_free_by_rcu(struct rcu_head *head)
> */
> void security_inode_free(struct inode *inode)
> {
> - integrity_inode_free(inode);
> call_void_hook(inode_free_security, inode);
> /*
> * The inode may still be referenced in a path walk and

2024-02-08 03:18:42

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 0/25] security: Move IMA and EVM to the LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> IMA and EVM are not effectively LSMs, especially due to the fact that in
> the past they could not provide a security blob while there is another LSM
> active.
>
> That changed in the recent years, the LSM stacking feature now makes it
> possible to stack together multiple LSMs, and allows them to provide a
> security blob for most kernel objects. While the LSM stacking feature has
> some limitations being worked out, it is already suitable to make IMA and
> EVM as LSMs.
>
> The main purpose of this patch set is to remove IMA and EVM function calls,
> hardcoded in the LSM infrastructure and other places in the kernel, and to
> register them as LSM hook implementations, so that those functions are
> called by the LSM infrastructure like other regular LSMs.

Thanks Roberto, this is looking good. I appreciate all the work you've
put into making this happen; when I first mentioned this idea I figured
it would be something that would happen much farther into the future, I
wasn't expecting to see you pick this up and put in the work to make it
happen - thank you.

I had some pretty minor comments but I think the only thing I saw that
I think needs a change/addition is a comment in the Makefile regarding
the IMA/EVM ordering; take a look and let me know what you think.

There are also a few patches in the patchset that don't have an
ACK/review tag from Mimi, although now that you are co-maininting IMA/EVM
with Mimi I don't know if that matters. If the two of you can let me
know how you want me to handle LSM patches that are IMA/EVM related I
would appreciate it (two ACKs, one or other, something else?).

Once you add a Makefile commane and we sort out the IMA/EVM approval
process I think we're good to get this into linux-next. A while back
Mimi and I had a chat offline and if I recall everything correctly she
preferred that I take this patchset via the LSM tree. I don't have a
problem with that, and to be honest I would probably prefer
that too, but I wanted to check with everyone that is still the case.
Just in case, I've added my ACKs/reviews to this patchset in case this
needs to be merged via the integrity tree.

--
paul-moore.com

2024-02-08 03:19:01

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 1/25] ima: Align ima_inode_post_setattr() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change ima_inode_post_setattr() definition, so that it can be registered as
> implementation of the inode_post_setattr hook (to be introduced).
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/attr.c | 2 +-
> include/linux/ima.h | 4 ++--
> security/integrity/ima/ima_appraise.c | 3 ++-
> 3 files changed, 5 insertions(+), 4 deletions(-)
>

Reviewed-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:19:28

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 3/25] ima: Align ima_inode_setxattr() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change ima_inode_setxattr() definition, so that it can be registered as
> implementation of the inode_setxattr hook.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> include/linux/ima.h | 11 +++++++----
> security/integrity/ima/ima_appraise.c | 5 +++--
> security/security.c | 2 +-
> 3 files changed, 11 insertions(+), 7 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:19:47

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 2/25] ima: Align ima_file_mprotect() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change ima_file_mprotect() definition, so that it can be registered
> as implementation of the file_mprotect hook.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> include/linux/ima.h | 5 +++--
> security/integrity/ima/ima_main.c | 6 ++++--
> security/security.c | 2 +-
> 3 files changed, 8 insertions(+), 5 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:19:55

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 4/25] ima: Align ima_inode_removexattr() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change ima_inode_removexattr() definition, so that it can be registered as
> implementation of the inode_removexattr hook.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> include/linux/ima.h | 7 +++++--
> security/integrity/ima/ima_appraise.c | 3 ++-
> security/security.c | 2 +-
> 3 files changed, 8 insertions(+), 4 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:20:12

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 5/25] ima: Align ima_post_read_file() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change ima_post_read_file() definition, by making "void *buf" a
> "char *buf", so that it can be registered as implementation of the
> post_read_file hook.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> include/linux/ima.h | 4 ++--
> security/integrity/ima/ima_main.c | 2 +-
> 2 files changed, 3 insertions(+), 3 deletions(-)

Reviewed-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:20:36

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 6/25] evm: Align evm_inode_post_setattr() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change evm_inode_post_setattr() definition, so that it can be registered as
> implementation of the inode_post_setattr hook (to be introduced).
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/attr.c | 2 +-
> include/linux/evm.h | 6 ++++--
> security/integrity/evm/evm_main.c | 4 +++-
> 3 files changed, 8 insertions(+), 4 deletions(-)

Reviewed-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:20:58

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 8/25] evm: Align evm_inode_post_setxattr() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change evm_inode_post_setxattr() definition, so that it can be registered
> as implementation of the inode_post_setxattr hook.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> include/linux/evm.h | 8 +++++---
> security/integrity/evm/evm_main.c | 4 +++-
> security/security.c | 2 +-
> 3 files changed, 9 insertions(+), 5 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:22:00

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 11/25] security: Introduce inode_post_removexattr hook

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_removexattr hook.
>
> At inode_removexattr hook, EVM verifies the file's existing HMAC value. At
> inode_post_removexattr, EVM re-calculates the file's HMAC with the passed
> xattr removed and other file metadata.
>
> Other LSMs could similarly take some action after successful xattr removal.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> fs/xattr.c | 9 +++++----
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 5 +++++
> security/security.c | 14 ++++++++++++++
> 4 files changed, 26 insertions(+), 4 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:22:33

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 14/25] security: Introduce path_post_mknod hook

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the path_post_mknod hook.
>
> IMA-appraisal requires all existing files in policy to have a file
> hash/signature stored in security.ima. An exception is made for empty files
> created by mknod, by tagging them as new files.
>
> LSMs could also take some action after files are created.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/namei.c | 5 +++++
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 5 +++++
> security/security.c | 14 ++++++++++++++
> 4 files changed, 26 insertions(+)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:22:38

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation to move IMA and EVM to the LSM infrastructure, introduce the
> file_post_open hook. Also, export security_file_post_open() for NFS.
>
> Based on policy, IMA calculates the digest of the file content and
> extends the TPM with the digest, verifies the file's integrity based on
> the digest, and/or includes the file digest in the audit log.
>
> LSMs could similarly take action depending on the file content and the
> access mask requested with open().
>
> The new hook returns a value and can cause the open to be aborted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/namei.c | 2 ++
> fs/nfsd/vfs.c | 6 ++++++
> include/linux/lsm_hook_defs.h | 1 +
> include/linux/security.h | 6 ++++++
> security/security.c | 17 +++++++++++++++++
> 5 files changed, 32 insertions(+)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:23:12

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 16/25] security: Introduce inode_post_set_acl hook

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_set_acl hook.
>
> At inode_set_acl hook, EVM verifies the file's existing HMAC value. At
> inode_post_set_acl, EVM re-calculates the file's HMAC based on the modified
> POSIX ACL and other file metadata.
>
> Other LSMs could similarly take some action after successful POSIX ACL
> change.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/posix_acl.c | 1 +
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 7 +++++++
> security/security.c | 17 +++++++++++++++++
> 4 files changed, 27 insertions(+)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:23:14

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 7/25] evm: Align evm_inode_setxattr() definition with LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Change evm_inode_setxattr() definition, so that it can be registered as
> implementation of the inode_setxattr hook.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> include/linux/evm.h | 4 ++--
> security/integrity/evm/evm_main.c | 3 ++-
> security/security.c | 2 +-
> 3 files changed, 5 insertions(+), 4 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:23:28

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 9/25] security: Align inode_setattr hook definition with EVM

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Add the idmap parameter to the definition, so that evm_inode_setattr() can
> be registered as this hook implementation.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> include/linux/lsm_hook_defs.h | 3 ++-
> security/security.c | 2 +-
> security/selinux/hooks.c | 3 ++-
> security/smack/smack_lsm.c | 4 +++-
> 4 files changed, 8 insertions(+), 4 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:23:51

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 10/25] security: Introduce inode_post_setattr hook

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_setattr hook.
>
> At inode_setattr hook, EVM verifies the file's existing HMAC value. At
> inode_post_setattr, EVM re-calculates the file's HMAC based on the modified
> file attributes and other file metadata.
>
> Other LSMs could similarly take some action after successful file attribute
> change.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> ---
> fs/attr.c | 1 +
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 7 +++++++
> security/security.c | 16 ++++++++++++++++
> 4 files changed, 26 insertions(+)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:24:19

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 17/25] security: Introduce inode_post_remove_acl hook

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_remove_acl hook.
>
> At inode_remove_acl hook, EVM verifies the file's existing HMAC value. At
> inode_post_remove_acl, EVM re-calculates the file's HMAC with the passed
> POSIX ACL removed and other file metadata.
>
> Other LSMs could similarly take some action after successful POSIX ACL
> removal.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/posix_acl.c | 1 +
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 8 ++++++++
> security/security.c | 17 +++++++++++++++++
> 4 files changed, 28 insertions(+)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:24:34

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 20/25] ima: Move to LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Move hardcoded IMA function calls (not appraisal-specific functions) from
> various places in the kernel to the LSM infrastructure, by introducing a
> new LSM named 'ima' (at the end of the LSM list and always enabled like
> 'integrity').
>
> Having IMA before EVM in the Makefile is sufficient to preserve the
> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
> and thus the order of IMA and EVM function calls as when they were
> hardcoded.
>
> Make moved functions as static (except ima_post_key_create_or_update(),
> which is not in ima_main.c), and register them as implementation of the
> respective hooks in the new function init_ima_lsm().
>
> Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook
> path_post_mknod is always available and ima_post_path_mknod() is always
> executed to mark files as new, as before the move.
>
> A slight difference is that IMA and EVM functions registered for the
> inode_post_setattr, inode_post_removexattr, path_post_mknod,
> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
> won't be executed for private inodes. Since those inodes are supposed to be
> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
> and kernel-internal tmpfs files.
>
> Conditionally register ima_post_key_create_or_update() if
> CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register
> ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
>
> Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Chuck Lever <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> ---
> fs/file_table.c | 2 -
> fs/namei.c | 6 -
> fs/nfsd/vfs.c | 7 --
> fs/open.c | 1 -
> include/linux/ima.h | 104 ------------------
> include/uapi/linux/lsm.h | 1 +
> security/integrity/Makefile | 1 +
> security/integrity/ima/Kconfig | 1 +
> security/integrity/ima/ima.h | 6 +
> security/integrity/ima/ima_main.c | 78 +++++++++----
> security/integrity/integrity.h | 1 +
> security/keys/key.c | 9 +-
> security/security.c | 63 ++---------
> .../selftests/lsm/lsm_list_modules_test.c | 3 +
> 14 files changed, 83 insertions(+), 200 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:24:47

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 21/25] ima: Move IMA-Appraisal to LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> A few additional IMA hooks are needed to reset the cached appraisal
> status, causing the file's integrity to be re-evaluated on next access.
> Register these IMA-appraisal only functions separately from the rest of IMA
> functions, as appraisal is a separate feature not necessarily enabled in
> the kernel configuration.
>
> Reuse the same approach as for other IMA functions, move hardcoded calls
> from various places in the kernel to the LSM infrastructure. Declare the
> functions as static and register them as hook implementations in
> init_ima_appraise_lsm(), called by init_ima_lsm().
>
> Also move the inline function ima_inode_remove_acl() from the public ima.h
> header to ima_appraise.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> fs/attr.c | 2 -
> include/linux/ima.h | 55 ---------------------------
> security/integrity/ima/ima.h | 5 +++
> security/integrity/ima/ima_appraise.c | 38 +++++++++++++-----
> security/integrity/ima/ima_main.c | 1 +
> security/security.c | 13 -------
> 6 files changed, 35 insertions(+), 79 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:31:12

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> In preparation for removing the 'integrity' LSM, move
> integrity_kernel_module_request() to IMA, and rename it to
> ima_kernel_module_request().
>
> Compile it conditionally if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled,
> and call it from security.c (removed afterwards with the move of IMA to the
> LSM infrastructure).
>
> Adding this hook cannot be avoided, since IMA has no control on the flags
> passed to crypto_alloc_sig() in public_key_verify_signature(), and thus
> cannot pass CRYPTO_NOLOAD, which solved the problem for EVM hashing with
> commit e2861fa71641 ("evm: Don't deadlock if a crypto algorithm is
> unavailable").
>
> EVM alone does not need to implement this hook, first because there is no
> mutex to deadlock, and second because even if it had it, there should be a
> recursive call. However, since verification from EVM can be initiated only
> by setting inode metadata, deadlock would occur if modprobe would do the
> same while loading a kernel module (which is unlikely).
>
> Signed-off-by: Roberto Sassu <[email protected]>
> ---
> include/linux/ima.h | 10 +++++++++
> include/linux/integrity.h | 13 ------------
> security/integrity/digsig_asymmetric.c | 23 --------------------
> security/integrity/ima/ima_main.c | 29 ++++++++++++++++++++++++++
> security/security.c | 2 +-
> 5 files changed, 40 insertions(+), 37 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 03:35:15

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 22/25] evm: Move to LSM infrastructure

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> As for IMA, move hardcoded EVM function calls from various places in the
> kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
> (last and always enabled like 'ima'). The order in the Makefile ensures
> that 'evm' hooks are executed after 'ima' ones.

Let's add a comment to the Makefile about this so everyone knows not
to mix up the ordering, otherwise this looks good to me.

At some point I think we may want to introduce the concept of numerical
priorities to security_add_hooks() to add some additional granularity
beyond the LSM_ORDER_XXX priority, but that is something we can do
later.

Acked-by: Paul Moore <[email protected]>

> Make EVM functions as static (except for evm_inode_init_security(), which
> is exported), and register them as hook implementations in init_evm_lsm().
> Also move the inline functions evm_inode_remove_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public
> evm.h header to evm_main.c.
>
> Unlike before (see commit to move IMA to the LSM infrastructure),
> evm_inode_post_setattr(), evm_inode_post_set_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
> executed for private inodes.
>
> Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> fs/attr.c | 2 -
> fs/posix_acl.c | 3 -
> fs/xattr.c | 2 -
> include/linux/evm.h | 113 -----------------
> include/uapi/linux/lsm.h | 1 +
> security/integrity/evm/evm_main.c | 118 +++++++++++++++---
> security/security.c | 43 ++-----
> .../selftests/lsm/lsm_list_modules_test.c | 3 +
> 8 files changed, 116 insertions(+), 169 deletions(-)

--
paul-moore.com

2024-02-08 03:35:31

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 25/25] integrity: Remove LSM

On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
>
> Since now IMA and EVM use their own integrity metadata, it is safe to
> remove the 'integrity' LSM, with its management of integrity metadata.
>
> Keep the iint.c file only for loading IMA and EVM keys at boot, and for
> creating the integrity directory in securityfs (we need to keep it for
> retrocompatibility reasons).
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> include/linux/integrity.h | 14 ---
> security/integrity/iint.c | 197 +--------------------------------
> security/integrity/integrity.h | 25 -----
> security/security.c | 2 -
> 4 files changed, 2 insertions(+), 236 deletions(-)

Acked-by: Paul Moore <[email protected]>

--
paul-moore.com

2024-02-08 08:06:28

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 0/25] security: Move IMA and EVM to the LSM infrastructure

On Wed, 2024-02-07 at 22:18 -0500, Paul Moore wrote:
> On Jan 15, 2024 Roberto Sassu <[email protected]> wrote:
> >
> > IMA and EVM are not effectively LSMs, especially due to the fact that in
> > the past they could not provide a security blob while there is another LSM
> > active.
> >
> > That changed in the recent years, the LSM stacking feature now makes it
> > possible to stack together multiple LSMs, and allows them to provide a
> > security blob for most kernel objects. While the LSM stacking feature has
> > some limitations being worked out, it is already suitable to make IMA and
> > EVM as LSMs.
> >
> > The main purpose of this patch set is to remove IMA and EVM function calls,
> > hardcoded in the LSM infrastructure and other places in the kernel, and to
> > register them as LSM hook implementations, so that those functions are
> > called by the LSM infrastructure like other regular LSMs.
>
> Thanks Roberto, this is looking good. I appreciate all the work you've
> put into making this happen; when I first mentioned this idea I figured
> it would be something that would happen much farther into the future, I
> wasn't expecting to see you pick this up and put in the work to make it
> happen - thank you.

Thanks! I also appreciate a lot your guidance and suggestions.

> I had some pretty minor comments but I think the only thing I saw that
> I think needs a change/addition is a comment in the Makefile regarding
> the IMA/EVM ordering; take a look and let me know what you think.

Oh, I remember well, it is there but difficult to spot...

--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_powerpc.o \
platform_certs/keyring_handler.o
+# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/

> There are also a few patches in the patchset that don't have an
> ACK/review tag from Mimi, although now that you are co-maininting IMA/EVM
> with Mimi I don't know if that matters. If the two of you can let me
> know how you want me to handle LSM patches that are IMA/EVM related I
> would appreciate it (two ACKs, one or other, something else?).

Ok, we will come back to you about this.

> Once you add a Makefile commane and we sort out the IMA/EVM approval
> process I think we're good to get this into linux-next. A while back
> Mimi and I had a chat offline and if I recall everything correctly she
> preferred that I take this patchset via the LSM tree. I don't have a
> problem with that, and to be honest I would probably prefer
> that too, but I wanted to check with everyone that is still the case.
> Just in case, I've added my ACKs/reviews to this patchset in case this
> needs to be merged via the integrity tree.

Ok, given that there is the comment in the Makefile, the last thing to
do from your side is to remove the vague comment in the file_release
patch.

Other than that, I think Mimi wanted to give a last look. If that is
ok, then the patches should be ready for your repo and linux-next.

Thanks

Roberto


2024-02-08 14:17:10

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 0/25] security: Move IMA and EVM to the LSM infrastructure

On Thu, Feb 8, 2024 at 3:06 AM Roberto Sassu
<[email protected]> wrote:
> On Wed, 2024-02-07 at 22:18 -0500, Paul Moore wrote:

...

> > I had some pretty minor comments but I think the only thing I saw that
> > I think needs a change/addition is a comment in the Makefile regarding
> > the IMA/EVM ordering; take a look and let me know what you think.
>
> Oh, I remember well, it is there but difficult to spot...
>
> --- a/security/integrity/Makefile
> +++ b/security/integrity/Makefile
> @@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
> integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
> platform_certs/load_powerpc.o \
> platform_certs/keyring_handler.o
> +# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
> obj-$(CONFIG_IMA) += ima/
> obj-$(CONFIG_EVM) += evm/

Great, thanks for that. Not sure how I missed that ... ?

> > Once you add a Makefile commane and we sort out the IMA/EVM approval
> > process I think we're good to get this into linux-next. A while back
> > Mimi and I had a chat offline and if I recall everything correctly she
> > preferred that I take this patchset via the LSM tree. I don't have a
> > problem with that, and to be honest I would probably prefer
> > that too, but I wanted to check with everyone that is still the case.
> > Just in case, I've added my ACKs/reviews to this patchset in case this
> > needs to be merged via the integrity tree.
>
> Ok, given that there is the comment in the Makefile, the last thing to
> do from your side is to remove the vague comment in the file_release
> patch.
>
> Other than that, I think Mimi wanted to give a last look. If that is
> ok, then the patches should be ready for your repo and linux-next.

If Mimi is okay with the patchset as-is, and both of you would prefer
this to in via the LSM tree, don't worry about the file_release
comment, I'll just remove that when merging.

--
paul-moore.com

2024-02-09 09:45:52

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 21/25] ima: Move IMA-Appraisal to LSM infrastructure

On Mon, Jan 15, 2024 at 07:18:05PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> A few additional IMA hooks are needed to reset the cached appraisal
> status, causing the file's integrity to be re-evaluated on next access.
> Register these IMA-appraisal only functions separately from the rest of IMA
> functions, as appraisal is a separate feature not necessarily enabled in
> the kernel configuration.
>
> Reuse the same approach as for other IMA functions, move hardcoded calls
> from various places in the kernel to the LSM infrastructure. Declare the
> functions as static and register them as hook implementations in
> init_ima_appraise_lsm(), called by init_ima_lsm().
>
> Also move the inline function ima_inode_remove_acl() from the public ima.h
> header to ima_appraise.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/attr.c | 2 -
> include/linux/ima.h | 55 ---------------------------
> security/integrity/ima/ima.h | 5 +++
> security/integrity/ima/ima_appraise.c | 38 +++++++++++++-----
> security/integrity/ima/ima_main.c | 1 +
> security/security.c | 13 -------
> 6 files changed, 35 insertions(+), 79 deletions(-)
>
> diff --git a/fs/attr.c b/fs/attr.c
> index 221d2bb0a906..38841f3ebbcb 100644
> --- a/fs/attr.c
> +++ b/fs/attr.c
> @@ -17,7 +17,6 @@
> #include <linux/filelock.h>
> #include <linux/security.h>
> #include <linux/evm.h>
> -#include <linux/ima.h>
>
> #include "internal.h"
>
> @@ -503,7 +502,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
> if (!error) {
> fsnotify_change(dentry, ia_valid);
> security_inode_post_setattr(idmap, dentry, ia_valid);
> - ima_inode_post_setattr(idmap, dentry, ia_valid);
> evm_inode_post_setattr(idmap, dentry, ia_valid);
> }

Acked-by: Christian Brauner <[email protected]>

2024-02-09 09:48:21

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 22/25] evm: Move to LSM infrastructure

On Mon, Jan 15, 2024 at 07:18:06PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> As for IMA, move hardcoded EVM function calls from various places in the
> kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
> (last and always enabled like 'ima'). The order in the Makefile ensures
> that 'evm' hooks are executed after 'ima' ones.
>
> Make EVM functions as static (except for evm_inode_init_security(), which
> is exported), and register them as hook implementations in init_evm_lsm().
> Also move the inline functions evm_inode_remove_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public
> evm.h header to evm_main.c.
>
> Unlike before (see commit to move IMA to the LSM infrastructure),
> evm_inode_post_setattr(), evm_inode_post_set_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
> executed for private inodes.
>
> Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> fs/attr.c | 2 -
> fs/posix_acl.c | 3 -
> fs/xattr.c | 2 -

Acked-by: Christian Brauner <[email protected]>

2024-02-09 09:50:21

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 20/25] ima: Move to LSM infrastructure

On Mon, Jan 15, 2024 at 07:18:04PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Move hardcoded IMA function calls (not appraisal-specific functions) from
> various places in the kernel to the LSM infrastructure, by introducing a
> new LSM named 'ima' (at the end of the LSM list and always enabled like
> 'integrity').
>
> Having IMA before EVM in the Makefile is sufficient to preserve the
> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
> and thus the order of IMA and EVM function calls as when they were
> hardcoded.
>
> Make moved functions as static (except ima_post_key_create_or_update(),
> which is not in ima_main.c), and register them as implementation of the
> respective hooks in the new function init_ima_lsm().
>
> Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook
> path_post_mknod is always available and ima_post_path_mknod() is always
> executed to mark files as new, as before the move.
>
> A slight difference is that IMA and EVM functions registered for the
> inode_post_setattr, inode_post_removexattr, path_post_mknod,
> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
> won't be executed for private inodes. Since those inodes are supposed to be
> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
> and kernel-internal tmpfs files.
>
> Conditionally register ima_post_key_create_or_update() if
> CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register
> ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
>
> Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Chuck Lever <[email protected]>
> ---
> fs/file_table.c | 2 -
> fs/namei.c | 6 -
> fs/nfsd/vfs.c | 7 --
> fs/open.c | 1 -

Acked-by: Christian Brauner <[email protected]>

2024-02-09 09:51:54

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 16/25] security: Introduce inode_post_set_acl hook

On Mon, Jan 15, 2024 at 07:18:00PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_set_acl hook.
>
> At inode_set_acl hook, EVM verifies the file's existing HMAC value. At
> inode_post_set_acl, EVM re-calculates the file's HMAC based on the modified
> POSIX ACL and other file metadata.
>
> Other LSMs could similarly take some action after successful POSIX ACL
> change.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/posix_acl.c | 1 +

Acked-by: Christian Brauner <[email protected]>

2024-02-09 09:52:26

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 17/25] security: Introduce inode_post_remove_acl hook

On Mon, Jan 15, 2024 at 07:18:01PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_remove_acl hook.
>
> At inode_remove_acl hook, EVM verifies the file's existing HMAC value. At
> inode_post_remove_acl, EVM re-calculates the file's HMAC with the passed
> POSIX ACL removed and other file metadata.
>
> Other LSMs could similarly take some action after successful POSIX ACL
> removal.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/posix_acl.c | 1 +

Acked-by: Christian Brauner <[email protected]>

2024-02-09 09:54:24

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 14/25] security: Introduce path_post_mknod hook

On Mon, Jan 15, 2024 at 07:17:58PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the path_post_mknod hook.
>
> IMA-appraisal requires all existing files in policy to have a file
> hash/signature stored in security.ima. An exception is made for empty files
> created by mknod, by tagging them as new files.
>
> LSMs could also take some action after files are created.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/namei.c | 5 +++++

Acked-by: Christian Brauner <[email protected]>

2024-02-09 09:59:56

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Fri, Feb 09, 2024 at 10:56:33AM +0100, Christian Brauner wrote:
> On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
> > From: Roberto Sassu <[email protected]>
> >
> > In preparation to move IMA and EVM to the LSM infrastructure, introduce the
> > file_post_open hook. Also, export security_file_post_open() for NFS.
> >
> > Based on policy, IMA calculates the digest of the file content and
> > extends the TPM with the digest, verifies the file's integrity based on
> > the digest, and/or includes the file digest in the audit log.
> >
> > LSMs could similarly take action depending on the file content and the
> > access mask requested with open().
> >
> > The new hook returns a value and can cause the open to be aborted.
> >
> > Signed-off-by: Roberto Sassu <[email protected]>
> > Reviewed-by: Stefan Berger <[email protected]>
> > Acked-by: Casey Schaufler <[email protected]>
> > Reviewed-by: Mimi Zohar <[email protected]>
> > ---
> > fs/namei.c | 2 ++
>
> Acked-by: Christian Brauner <[email protected]>

Redacting that. I have a question.

2024-02-09 10:13:08

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation to move IMA and EVM to the LSM infrastructure, introduce the
> file_post_open hook. Also, export security_file_post_open() for NFS.
>
> Based on policy, IMA calculates the digest of the file content and
> extends the TPM with the digest, verifies the file's integrity based on
> the digest, and/or includes the file digest in the audit log.
>
> LSMs could similarly take action depending on the file content and the
> access mask requested with open().
>
> The new hook returns a value and can cause the open to be aborted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> ---
> fs/namei.c | 2 ++
> fs/nfsd/vfs.c | 6 ++++++
> include/linux/lsm_hook_defs.h | 1 +
> include/linux/security.h | 6 ++++++
> security/security.c | 17 +++++++++++++++++
> 5 files changed, 32 insertions(+)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 71c13b2990b4..fb93d3e13df6 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd,
> error = may_open(idmap, &nd->path, acc_mode, open_flag);
> if (!error && !(file->f_mode & FMODE_OPENED))
> error = vfs_open(&nd->path, file);
> + if (!error)
> + error = security_file_post_open(file, op->acc_mode);

What does it do for O_CREAT? IOW, we managed to create that thing and we
managed to open that thing. Can security_file_post_open() and
ima_file_check() fail afterwards even for newly created files?

2024-02-09 10:17:22

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 11/25] security: Introduce inode_post_removexattr hook

On Mon, Jan 15, 2024 at 07:17:55PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_removexattr hook.
>
> At inode_removexattr hook, EVM verifies the file's existing HMAC value. At
> inode_post_removexattr, EVM re-calculates the file's HMAC with the passed
> xattr removed and other file metadata.
>
> Other LSMs could similarly take some action after successful xattr removal.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>
> ---
> fs/xattr.c | 9 +++++----

Acked-by: Christian Brauner <[email protected]>

2024-02-09 10:17:54

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 10/25] security: Introduce inode_post_setattr hook

On Mon, Jan 15, 2024 at 07:17:54PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the inode_post_setattr hook.
>
> At inode_setattr hook, EVM verifies the file's existing HMAC value. At
> inode_post_setattr, EVM re-calculates the file's HMAC based on the modified
> file attributes and other file metadata.
>
> Other LSMs could similarly take some action after successful file attribute
> change.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> ---
> fs/attr.c | 1 +

Acked-by: Christian Brauner <[email protected]>

2024-02-09 10:25:09

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 13/25] security: Introduce file_release hook

On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the file_release hook.
>
> IMA calculates at file close the new digest of the file content and writes
> it to security.ima, so that appraisal at next file access succeeds.
>
> An LSM could implement an exclusive access scheme for files, only allowing
> access to files that have no references.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> ---
> fs/file_table.c | 1 +
> include/linux/lsm_hook_defs.h | 1 +
> include/linux/security.h | 4 ++++
> security/security.c | 11 +++++++++++
> 4 files changed, 17 insertions(+)
>
> diff --git a/fs/file_table.c b/fs/file_table.c
> index de4a2915bfd4..c72dc75f2bd3 100644
> --- a/fs/file_table.c
> +++ b/fs/file_table.c
> @@ -385,6 +385,7 @@ static void __fput(struct file *file)
> eventpoll_release(file);
> locks_remove_file(file);
>
> + security_file_release(file);
> ima_file_free(file);

This has always been an extremely dicy hook in here and that's caused us
issues before for stacking filesystems so I'm not enthusiastic about
exposing this to all LSMs. So reluctantly,

Acked-by: Christian Brauner <[email protected]>

2024-02-09 10:46:52

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Fri, 2024-02-09 at 11:12 +0100, Christian Brauner wrote:
> On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
> > From: Roberto Sassu <[email protected]>
> >
> > In preparation to move IMA and EVM to the LSM infrastructure, introduce the
> > file_post_open hook. Also, export security_file_post_open() for NFS.
> >
> > Based on policy, IMA calculates the digest of the file content and
> > extends the TPM with the digest, verifies the file's integrity based on
> > the digest, and/or includes the file digest in the audit log.
> >
> > LSMs could similarly take action depending on the file content and the
> > access mask requested with open().
> >
> > The new hook returns a value and can cause the open to be aborted.
> >
> > Signed-off-by: Roberto Sassu <[email protected]>
> > Reviewed-by: Stefan Berger <[email protected]>
> > Acked-by: Casey Schaufler <[email protected]>
> > Reviewed-by: Mimi Zohar <[email protected]>
> > ---
> > fs/namei.c | 2 ++
> > fs/nfsd/vfs.c | 6 ++++++
> > include/linux/lsm_hook_defs.h | 1 +
> > include/linux/security.h | 6 ++++++
> > security/security.c | 17 +++++++++++++++++
> > 5 files changed, 32 insertions(+)
> >
> > diff --git a/fs/namei.c b/fs/namei.c
> > index 71c13b2990b4..fb93d3e13df6 100644
> > --- a/fs/namei.c
> > +++ b/fs/namei.c
> > @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd,
> > error = may_open(idmap, &nd->path, acc_mode, open_flag);
> > if (!error && !(file->f_mode & FMODE_OPENED))
> > error = vfs_open(&nd->path, file);
> > + if (!error)
> > + error = security_file_post_open(file, op->acc_mode);
>
> What does it do for O_CREAT? IOW, we managed to create that thing and we
> managed to open that thing. Can security_file_post_open() and
> ima_file_check() fail afterwards even for newly created files?

$ strace touch test-file
...
openat(AT_FDCWD, "test-file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 EPERM (Operation not permitted)

The open fails, but the file is there. I didn't see warnings/errors in
the kernel log.

Roberto


2024-02-09 11:35:05

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Fri, Feb 09, 2024 at 11:46:16AM +0100, Roberto Sassu wrote:
> On Fri, 2024-02-09 at 11:12 +0100, Christian Brauner wrote:
> > On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
> > > From: Roberto Sassu <[email protected]>
> > >
> > > In preparation to move IMA and EVM to the LSM infrastructure, introduce the
> > > file_post_open hook. Also, export security_file_post_open() for NFS.
> > >
> > > Based on policy, IMA calculates the digest of the file content and
> > > extends the TPM with the digest, verifies the file's integrity based on
> > > the digest, and/or includes the file digest in the audit log.
> > >
> > > LSMs could similarly take action depending on the file content and the
> > > access mask requested with open().
> > >
> > > The new hook returns a value and can cause the open to be aborted.
> > >
> > > Signed-off-by: Roberto Sassu <[email protected]>
> > > Reviewed-by: Stefan Berger <[email protected]>
> > > Acked-by: Casey Schaufler <[email protected]>
> > > Reviewed-by: Mimi Zohar <[email protected]>
> > > ---
> > > fs/namei.c | 2 ++
> > > fs/nfsd/vfs.c | 6 ++++++
> > > include/linux/lsm_hook_defs.h | 1 +
> > > include/linux/security.h | 6 ++++++
> > > security/security.c | 17 +++++++++++++++++
> > > 5 files changed, 32 insertions(+)
> > >
> > > diff --git a/fs/namei.c b/fs/namei.c
> > > index 71c13b2990b4..fb93d3e13df6 100644
> > > --- a/fs/namei.c
> > > +++ b/fs/namei.c
> > > @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd,
> > > error = may_open(idmap, &nd->path, acc_mode, open_flag);
> > > if (!error && !(file->f_mode & FMODE_OPENED))
> > > error = vfs_open(&nd->path, file);
> > > + if (!error)
> > > + error = security_file_post_open(file, op->acc_mode);
> >
> > What does it do for O_CREAT? IOW, we managed to create that thing and we
> > managed to open that thing. Can security_file_post_open() and
> > ima_file_check() fail afterwards even for newly created files?
>
> $ strace touch test-file
> ...
> openat(AT_FDCWD, "test-file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 EPERM (Operation not permitted)

Ah, meh. I was hoping IMA just wouldn't care about this case.

2024-02-09 12:03:37

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Fri, 2024-02-09 at 12:34 +0100, Christian Brauner wrote:
> On Fri, Feb 09, 2024 at 11:46:16AM +0100, Roberto Sassu wrote:
> > On Fri, 2024-02-09 at 11:12 +0100, Christian Brauner wrote:
> > > On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
> > > > From: Roberto Sassu <[email protected]>
> > > >
> > > > In preparation to move IMA and EVM to the LSM infrastructure, introduce the
> > > > file_post_open hook. Also, export security_file_post_open() for NFS.
> > > >
> > > > Based on policy, IMA calculates the digest of the file content and
> > > > extends the TPM with the digest, verifies the file's integrity based on
> > > > the digest, and/or includes the file digest in the audit log.
> > > >
> > > > LSMs could similarly take action depending on the file content and the
> > > > access mask requested with open().
> > > >
> > > > The new hook returns a value and can cause the open to be aborted.
> > > >
> > > > Signed-off-by: Roberto Sassu <[email protected]>
> > > > Reviewed-by: Stefan Berger <[email protected]>
> > > > Acked-by: Casey Schaufler <[email protected]>
> > > > Reviewed-by: Mimi Zohar <[email protected]>
> > > > ---
> > > > fs/namei.c | 2 ++
> > > > fs/nfsd/vfs.c | 6 ++++++
> > > > include/linux/lsm_hook_defs.h | 1 +
> > > > include/linux/security.h | 6 ++++++
> > > > security/security.c | 17 +++++++++++++++++
> > > > 5 files changed, 32 insertions(+)
> > > >
> > > > diff --git a/fs/namei.c b/fs/namei.c
> > > > index 71c13b2990b4..fb93d3e13df6 100644
> > > > --- a/fs/namei.c
> > > > +++ b/fs/namei.c
> > > > @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd,
> > > > error = may_open(idmap, &nd->path, acc_mode, open_flag);
> > > > if (!error && !(file->f_mode & FMODE_OPENED))
> > > > error = vfs_open(&nd->path, file);
> > > > + if (!error)
> > > > + error = security_file_post_open(file, op->acc_mode);
> > >
> > > What does it do for O_CREAT? IOW, we managed to create that thing and we
> > > managed to open that thing. Can security_file_post_open() and
> > > ima_file_check() fail afterwards even for newly created files?
> >
> > $ strace touch test-file
> > ...
> > openat(AT_FDCWD, "test-file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 EPERM (Operation not permitted)
>
> Ah, meh. I was hoping IMA just wouldn't care about this case.

Actually it doesn't. I added code to artifically create the situation
(to see what happens if a new LSM does that).

Roberto


2024-02-12 17:22:22

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 13/25] security: Introduce file_release hook



On 1/15/24 13:17, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the file_release hook.
>
> IMA calculates at file close the new digest of the file content and writes
> it to security.ima, so that appraisal at next file access succeeds.
>
> An LSM could implement an exclusive access scheme for files, only allowing
> access to files that have no references.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>

Reviewed-by: Stefan Berger <[email protected]>

> ---
> fs/file_table.c | 1 +
> include/linux/lsm_hook_defs.h | 1 +
> include/linux/security.h | 4 ++++
> security/security.c | 11 +++++++++++
> 4 files changed, 17 insertions(+)
>
> diff --git a/fs/file_table.c b/fs/file_table.c
> index de4a2915bfd4..c72dc75f2bd3 100644
> --- a/fs/file_table.c
> +++ b/fs/file_table.c
> @@ -385,6 +385,7 @@ static void __fput(struct file *file)
> eventpoll_release(file);
> locks_remove_file(file);
>
> + security_file_release(file);
> ima_file_free(file);
> if (unlikely(file->f_flags & FASYNC)) {
> if (file->f_op->fasync)
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index c3fecc7dcb0b..229f84ce12ae 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -173,6 +173,7 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
> struct kernfs_node *kn)
> LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
> LSM_HOOK(int, 0, file_alloc_security, struct file *file)
> +LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
> LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
> LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
> unsigned long arg)
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 97f2212c13b6..2997348afcb7 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -395,6 +395,7 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
> struct kernfs_node *kn);
> int security_file_permission(struct file *file, int mask);
> int security_file_alloc(struct file *file);
> +void security_file_release(struct file *file);
> void security_file_free(struct file *file);
> int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> int security_file_ioctl_compat(struct file *file, unsigned int cmd,
> @@ -1008,6 +1009,9 @@ static inline int security_file_alloc(struct file *file)
> return 0;
> }
>
> +static inline void security_file_release(struct file *file)
> +{ }
> +
> static inline void security_file_free(struct file *file)
> { }
>
> diff --git a/security/security.c b/security/security.c
> index f3d92bffd02f..7d10724872f8 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -2724,6 +2724,17 @@ int security_file_alloc(struct file *file)
> return rc;
> }
>
> +/**
> + * security_file_release() - Perform actions before releasing the file ref
> + * @file: the file
> + *
> + * Perform actions before releasing the last reference to a file.
> + */
> +void security_file_release(struct file *file)
> +{
> + call_void_hook(file_release, file);
> +}
> +
> /**
> * security_file_free() - Free a file's LSM blob
> * @file: the file

2024-02-12 17:35:21

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 14/25] security: Introduce path_post_mknod hook



On 1/15/24 13:17, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for moving IMA and EVM to the LSM infrastructure, introduce
> the path_post_mknod hook.
>
> IMA-appraisal requires all existing files in policy to have a file
> hash/signature stored in security.ima. An exception is made for empty files
> created by mknod, by tagging them as new files.
>
> LSMs could also take some action after files are created.
>
> The new hook cannot return an error and cannot cause the operation to be
> reverted.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Casey Schaufler <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>

Reviewed-by: Stefan Berger <[email protected]>
> ---
> fs/namei.c | 5 +++++
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 5 +++++
> security/security.c | 14 ++++++++++++++
> 4 files changed, 26 insertions(+)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index fb93d3e13df6..b7f433720b1e 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4047,6 +4047,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode,
> dentry, mode, 0);
> break;
> }
> +
> + if (error)
> + goto out2;
> +
> + security_path_post_mknod(idmap, dentry);
> out2:
> done_path_create(&path, dentry);
> if (retry_estale(error, lookup_flags)) {
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index 229f84ce12ae..e08b9091350d 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -94,6 +94,8 @@ LSM_HOOK(int, 0, path_mkdir, const struct path *dir, struct dentry *dentry,
> LSM_HOOK(int, 0, path_rmdir, const struct path *dir, struct dentry *dentry)
> LSM_HOOK(int, 0, path_mknod, const struct path *dir, struct dentry *dentry,
> umode_t mode, unsigned int dev)
> +LSM_HOOK(void, LSM_RET_VOID, path_post_mknod, struct mnt_idmap *idmap,
> + struct dentry *dentry)
> LSM_HOOK(int, 0, path_truncate, const struct path *path)
> LSM_HOOK(int, 0, path_symlink, const struct path *dir, struct dentry *dentry,
> const char *old_name)
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 2997348afcb7..977dd9f7f51a 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1893,6 +1893,7 @@ int security_path_mkdir(const struct path *dir, struct dentry *dentry, umode_t m
> int security_path_rmdir(const struct path *dir, struct dentry *dentry);
> int security_path_mknod(const struct path *dir, struct dentry *dentry, umode_t mode,
> unsigned int dev);
> +void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry);
> int security_path_truncate(const struct path *path);
> int security_path_symlink(const struct path *dir, struct dentry *dentry,
> const char *old_name);
> @@ -1927,6 +1928,10 @@ static inline int security_path_mknod(const struct path *dir, struct dentry *den
> return 0;
> }
>
> +static inline void security_path_post_mknod(struct mnt_idmap *idmap,
> + struct dentry *dentry)
> +{ }
> +
> static inline int security_path_truncate(const struct path *path)
> {
> return 0;
> diff --git a/security/security.c b/security/security.c
> index 7d10724872f8..750bfe2768d5 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1800,6 +1800,20 @@ int security_path_mknod(const struct path *dir, struct dentry *dentry,
> }
> EXPORT_SYMBOL(security_path_mknod);
>
> +/**
> + * security_path_post_mknod() - Update inode security field after file creation
> + * @idmap: idmap of the mount
> + * @dentry: new file
> + *
> + * Update inode security field after a file has been created.
> + */
> +void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> +{
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> + call_void_hook(path_post_mknod, idmap, dentry);
> +}
> +
> /**
> * security_path_mkdir() - Check if creating a new directory is allowed
> * @dir: parent directory

2024-02-12 17:47:20

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 20/25] ima: Move to LSM infrastructure



On 1/15/24 13:18, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Move hardcoded IMA function calls (not appraisal-specific functions) from
> various places in the kernel to the LSM infrastructure, by introducing a
> new LSM named 'ima' (at the end of the LSM list and always enabled like
> 'integrity').
>
> Having IMA before EVM in the Makefile is sufficient to preserve the
> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
> and thus the order of IMA and EVM function calls as when they were
> hardcoded.
>
> Make moved functions as static (except ima_post_key_create_or_update(),
> which is not in ima_main.c), and register them as implementation of the
> respective hooks in the new function init_ima_lsm().
>
> Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook
> path_post_mknod is always available and ima_post_path_mknod() is always
> executed to mark files as new, as before the move.
>
> A slight difference is that IMA and EVM functions registered for the
> inode_post_setattr, inode_post_removexattr, path_post_mknod,
> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
> won't be executed for private inodes. Since those inodes are supposed to be
> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE

of interest 'to'

> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
> and kernel-internal tmpfs files.
>
> Conditionally register ima_post_key_create_or_update() if
> CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register
> ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
>
> Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Chuck Lever <[email protected]>

Reviewed-by: Stefan Berger <[email protected]>

2024-02-12 17:48:55

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA



On 1/15/24 13:18, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> In preparation for removing the 'integrity' LSM, move
> integrity_kernel_module_request() to IMA, and rename it to
> ima_kernel_module_request().
>
> Compile it conditionally if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled,
> and call it from security.c (removed afterwards with the move of IMA to the
> LSM infrastructure).
>
> Adding this hook cannot be avoided, since IMA has no control on the flags
> passed to crypto_alloc_sig() in public_key_verify_signature(), and thus
> cannot pass CRYPTO_NOLOAD, which solved the problem for EVM hashing with
> commit e2861fa71641 ("evm: Don't deadlock if a crypto algorithm is
> unavailable").
>
> EVM alone does not need to implement this hook, first because there is no
> mutex to deadlock, and second because even if it had it, there should be a
> recursive call. However, since verification from EVM can be initiated only
> by setting inode metadata, deadlock would occur if modprobe would do the
> same while loading a kernel module (which is unlikely).
>
> Signed-off-by: Roberto Sassu <[email protected]>
> ---
> include/linux/ima.h | 10 +++++++++
> include/linux/integrity.h | 13 ------------
> security/integrity/digsig_asymmetric.c | 23 --------------------
> security/integrity/ima/ima_main.c | 29 ++++++++++++++++++++++++++
> security/security.c | 2 +-
> 5 files changed, 40 insertions(+), 37 deletions(-)
>
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 31ef6c3c3207..0f9af283cbc8 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -256,4 +256,14 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
> return false;
> }
> #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
> +
> +#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS)
> +extern int ima_kernel_module_request(char *kmod_name);
> +#else
> +static inline int ima_kernel_module_request(char *kmod_name)
> +{
> + return 0;
> +}
> +
> +#endif
> #endif /* _LINUX_IMA_H */
> diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> index 2ea0f2f65ab6..ef0f63ef5ebc 100644
> --- a/include/linux/integrity.h
> +++ b/include/linux/integrity.h
> @@ -42,17 +42,4 @@ static inline void integrity_load_keys(void)
> }
> #endif /* CONFIG_INTEGRITY */
>
> -#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
> -
> -extern int integrity_kernel_module_request(char *kmod_name);
> -
> -#else
> -
> -static inline int integrity_kernel_module_request(char *kmod_name)
> -{
> - return 0;
> -}
> -
> -#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
> -
> #endif /* _LINUX_INTEGRITY_H */
> diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
> index 895f4b9ce8c6..de603cf42ac7 100644
> --- a/security/integrity/digsig_asymmetric.c
> +++ b/security/integrity/digsig_asymmetric.c
> @@ -132,26 +132,3 @@ int asymmetric_verify(struct key *keyring, const char *sig,
> pr_debug("%s() = %d\n", __func__, ret);
> return ret;
> }
> -
> -/**
> - * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests
> - * @kmod_name: kernel module name
> - *
> - * We have situation, when public_key_verify_signature() in case of RSA
> - * algorithm use alg_name to store internal information in order to
> - * construct an algorithm on the fly, but crypto_larval_lookup() will try
> - * to use alg_name in order to load kernel module with same name.
> - * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
> - * we are safe to fail such module request from crypto_larval_lookup().
> - *
> - * In this way we prevent modprobe execution during digsig verification
> - * and avoid possible deadlock if modprobe and/or it's dependencies
> - * also signed with digsig.
> - */
> -int integrity_kernel_module_request(char *kmod_name)
> -{
> - if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
> - return -EINVAL;
> -
> - return 0;
> -}
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 02021ee467d3..908fa026ec58 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -1091,6 +1091,35 @@ int ima_measure_critical_data(const char *event_label,
> }
> EXPORT_SYMBOL_GPL(ima_measure_critical_data);
>
> +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
> +
> +/**
> + * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
> + * @kmod_name: kernel module name
> + *
> + * We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
> + * construct an algorithm on the fly, but crypto_larval_lookup() will try
> + * to use alg_name in order to load kernel module with same name.
> + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
> + * we are safe to fail such module request from crypto_larval_lookup().
> + *
> + * In this way we prevent modprobe execution during digsig verification
> + * and avoid possible deadlock if modprobe and/or it's dependencies
> + * also signed with digsig.

This text needs to some reformulation at some point..

> + *
> + * Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
> + */
> +int ima_kernel_module_request(char *kmod_name)
> +{
> + if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
> +
> static int __init init_ima(void)
> {
> int error;
> diff --git a/security/security.c b/security/security.c
> index d2a1226e6e69..6c6571a141a1 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -3255,7 +3255,7 @@ int security_kernel_module_request(char *kmod_name)
> ret = call_int_hook(kernel_module_request, 0, kmod_name);
> if (ret)
> return ret;
> - return integrity_kernel_module_request(kmod_name);
> + return ima_kernel_module_request(kmod_name);
> }
>
> /**

Reviewed-by: Stefan Berger <[email protected]>

2024-02-12 18:27:12

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 22/25] evm: Move to LSM infrastructure



On 1/15/24 13:18, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> As for IMA, move hardcoded EVM function calls from various places in the
> kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
> (last and always enabled like 'ima'). The order in the Makefile ensures
> that 'evm' hooks are executed after 'ima' ones.
>
> Make EVM functions as static (except for evm_inode_init_security(), which
> is exported), and register them as hook implementations in init_evm_lsm().
> Also move the inline functions evm_inode_remove_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public
> evm.h header to evm_main.c.
>
> Unlike before (see commit to move IMA to the LSM infrastructure),
> evm_inode_post_setattr(), evm_inode_post_set_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
> executed for private inodes.
>
> Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Casey Schaufler <[email protected]>

> }
>
> +static struct security_hook_list evm_hooks[] __ro_after_init = {
> + LSM_HOOK_INIT(inode_setattr, evm_inode_setattr),
> + LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr),
> + LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
> + LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr),
> + LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl),
> + LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl),
> + LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl),
> + LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl),
> + LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr),

nit: move this one up after inode_setxattr.

Reviewed-by: Stefan Berger <[email protected]>

2024-02-12 19:51:06

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 25/25] integrity: Remove LSM



On 1/15/24 13:18, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Since now IMA and EVM use their own integrity metadata, it is safe to
> remove the 'integrity' LSM, with its management of integrity metadata.
>
> Keep the iint.c file only for loading IMA and EVM keys at boot, and for
> creating the integrity directory in securityfs (we need to keep it for
> retrocompatibility reasons).
>
> Signed-off-by: Roberto Sassu <[email protected]>

Reviewed-by: Stefan Berger <[email protected]>

2024-02-12 20:29:25

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA



On 2/12/24 12:56, Paul Moore wrote:
> On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger <[email protected]> wrote:
>> On 1/15/24 13:18, Roberto Sassu wrote:
>
> ...
>
>>> +/**
>>> + * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
>>> + * @kmod_name: kernel module name
>>> + *
>>> + * We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
>>> + * construct an algorithm on the fly, but crypto_larval_lookup() will try
>>> + * to use alg_name in order to load kernel module with same name.
>>> + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
>>> + * we are safe to fail such module request from crypto_larval_lookup().
>>> + *
>>> + * In this way we prevent modprobe execution during digsig verification
>>> + * and avoid possible deadlock if modprobe and/or it's dependencies
>>> + * also signed with digsig.
>>
>> This text needs to some reformulation at some point..
>
> There is no time like the present. If you have a suggestion I would
> love to hear it and I'm sure Roberto would too.
>

My interpretation of the issue after possibly lossy decoding of the
above sentences:

Avoid a deadlock by rejecting a virtual kernel module with the name
"crypto-pkcs1pad(rsa,*)". This module may be requested by
crypto_larval_lookup() while trying to verify an RSA signature in
public_key_verify_signature(). Since the loading of the RSA module may
itself cause the request for an RSA signature verification it will
otherwise lead to a deadlock.


2024-02-12 21:06:38

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

Hi Roberto,


> diff --git a/security/security.c b/security/security.c
> index d9d2636104db..f3d92bffd02f 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file)
> return fsnotify_perm(file, MAY_OPEN); <=== Conflict

Replace with "return fsnotify_open_perm(file);"

> }
>

The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless
there are other issues, I can make the change.

thanks,

Mimi


2024-02-12 21:18:07

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar <[email protected]> wrote:
>
> Hi Roberto,
>
>
> > diff --git a/security/security.c b/security/security.c
> > index d9d2636104db..f3d92bffd02f 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file)
> > return fsnotify_perm(file, MAY_OPEN); <=== Conflict
>
> Replace with "return fsnotify_open_perm(file);"
>
> > }
> >
>
> The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless
> there are other issues, I can make the change.

I take it this means you want to pull this via the IMA/EVM tree?

--
paul-moore.com

2024-02-13 08:58:18

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA

On Mon, 2024-02-12 at 15:28 -0500, Stefan Berger wrote:
>
> On 2/12/24 12:56, Paul Moore wrote:
> > On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger <[email protected]> wrote:
> > > On 1/15/24 13:18, Roberto Sassu wrote:
> >
> > ...
> >
> > > > +/**
> > > > + * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
> > > > + * @kmod_name: kernel module name
> > > > + *
> > > > + * We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
> > > > + * construct an algorithm on the fly, but crypto_larval_lookup() will try
> > > > + * to use alg_name in order to load kernel module with same name.
> > > > + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
> > > > + * we are safe to fail such module request from crypto_larval_lookup().
> > > > + *
> > > > + * In this way we prevent modprobe execution during digsig verification
> > > > + * and avoid possible deadlock if modprobe and/or it's dependencies
> > > > + * also signed with digsig.
> > >
> > > This text needs to some reformulation at some point..
> >
> > There is no time like the present. If you have a suggestion I would
> > love to hear it and I'm sure Roberto would too.
> >
>
> My interpretation of the issue after possibly lossy decoding of the
> above sentences:
>
> Avoid a deadlock by rejecting a virtual kernel module with the name
> "crypto-pkcs1pad(rsa,*)". This module may be requested by
> crypto_larval_lookup() while trying to verify an RSA signature in
> public_key_verify_signature(). Since the loading of the RSA module may
> itself cause the request for an RSA signature verification it will
> otherwise lead to a deadlock.

I can be even more precise I guess (I actually reproduced it).

Avoid a verification loop where verifying the signature of the modprobe
binary requires executing modprobe itself. Since the modprobe iint-
>mutex is already held when the signature verification is performed, a
deadlock occurs as soon as modprobe is executed within the critical
region, since the same lock cannot be taken again.

This happens when public_key_verify_signature(), in case of RSA
algorithm, use alg_name to store internal information in order to
construct an algorithm on the fly, but crypto_larval_lookup() will try
to use alg_name in order to load a kernel module with same name.

Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
we are safe to fail such module request from crypto_larval_lookup(),
and avoid the verification loop.

Roberto


2024-02-13 12:59:46

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
> On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar <[email protected]> wrote:
> >
> > Hi Roberto,
> >
> >
> > > diff --git a/security/security.c b/security/security.c
> > > index d9d2636104db..f3d92bffd02f 100644
> > > --- a/security/security.c
> > > +++ b/security/security.c
> > > @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file)
> > > return fsnotify_perm(file, MAY_OPEN); <=== Conflict
> >
> > Replace with "return fsnotify_open_perm(file);"
> >
> > > }
> > >
> >
> > The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless
> > there are other issues, I can make the change.
>
> I take it this means you want to pull this via the IMA/EVM tree?

Not sure about that, but I have enough changes to do to make a v10.

Roberto



2024-02-13 16:33:59

by Stefan Berger

[permalink] [raw]
Subject: Re: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA



On 2/13/24 03:57, Roberto Sassu wrote:
> On Mon, 2024-02-12 at 15:28 -0500, Stefan Berger wrote:
>>
>> On 2/12/24 12:56, Paul Moore wrote:
>>> On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger <[email protected]> wrote:
>>>> On 1/15/24 13:18, Roberto Sassu wrote:
>>>
>>> ...
>>>
>>>>> +/**
>>>>> + * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
>>>>> + * @kmod_name: kernel module name
>>>>> + *
>>>>> + * We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
>>>>> + * construct an algorithm on the fly, but crypto_larval_lookup() will try
>>>>> + * to use alg_name in order to load kernel module with same name.
>>>>> + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
>>>>> + * we are safe to fail such module request from crypto_larval_lookup().
>>>>> + *
>>>>> + * In this way we prevent modprobe execution during digsig verification
>>>>> + * and avoid possible deadlock if modprobe and/or it's dependencies
>>>>> + * also signed with digsig.
>>>>
>>>> This text needs to some reformulation at some point..
>>>
>>> There is no time like the present. If you have a suggestion I would
>>> love to hear it and I'm sure Roberto would too.
>>>
>>
>> My interpretation of the issue after possibly lossy decoding of the
>> above sentences:
>>
>> Avoid a deadlock by rejecting a virtual kernel module with the name
>> "crypto-pkcs1pad(rsa,*)". This module may be requested by
>> crypto_larval_lookup() while trying to verify an RSA signature in
>> public_key_verify_signature(). Since the loading of the RSA module may
>> itself cause the request for an RSA signature verification it will
>> otherwise lead to a deadlock.
>
> I can be even more precise I guess (I actually reproduced it). >
> Avoid a verification loop where verifying the signature of the modprobe
> binary requires executing modprobe itself. Since the modprobe iint-
>> mutex is already held when the signature verification is performed, a
> deadlock occurs as soon as modprobe is executed within the critical
> region, since the same lock cannot be taken again.

When ecdsa is used for signing files it could get stuck as well and
would need this patch:

diff --git a/security/integrity/ima/ima_main.c
b/security/integrity/ima/ima_main.c
index 45f1a102c599..2e71dc977d43 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1110,7 +1110,9 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data);
*/
static int ima_kernel_module_request(char *kmod_name)
{
- if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
+ if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0 ||
+ strncmp(kmod_name, "crypto-ecdsa-nist-p", 19) == 0 ||
+ strcmp(kmod_name, "cryptomgr") == 0)
return -EINVAL;

return 0;

Rejecting cryptomgr seems necessary in the ecdsa case though I am not
sure what the side effects of rejecting it all the time could be.

Stefan

>
> This happens when public_key_verify_signature(), in case of RSA
> algorithm, use alg_name to store internal information in order to
> construct an algorithm on the fly, but crypto_larval_lookup() will try
> to use alg_name in order to load a kernel module with same name.
>
> Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
> we are safe to fail such module request from crypto_larval_lookup(),
> and avoid the verification loop.
>
> Roberto
>
>

2024-02-14 20:08:15

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Tue, 2024-02-13 at 10:33 -0500, Paul Moore wrote:
> On Tue, Feb 13, 2024 at 7:59 AM Roberto Sassu
> <[email protected]> wrote:
> > On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
> > > On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar <[email protected]> wrote:
> > > > Hi Roberto,
> > > >
> > > >
> > > > > diff --git a/security/security.c b/security/security.c
> > > > > index d9d2636104db..f3d92bffd02f 100644
> > > > > --- a/security/security.c
> > > > > +++ b/security/security.c
> > > > > @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file)
> > > > > return fsnotify_perm(file, MAY_OPEN); <=== Conflict
> > > >
> > > > Replace with "return fsnotify_open_perm(file);"
> > > >
> > > > > }
> > > > >
> > > >
> > > > The patch set doesn't apply cleaning to 6.8-rcX without this
> > > > change. Unless
> > > > there are other issues, I can make the change.
> > >
> > > I take it this means you want to pull this via the IMA/EVM tree?
> >
> > Not sure about that, but I have enough changes to do to make a v10.

@Roberto: please add my "Reviewed-by" to the remaining patches.

>
> Sorry, I should have been more clear, the point I was trying to
> resolve was who was going to take this patchset (eventually). There
> are other patches destined for the LSM tree that touch the LSM hooks
> in a way which will cause conflicts with this patchset, and if
> you/Mimi are going to take this via the IMA/EVM tree - which is fine
> with me - I need to take that into account when merging things in the
> LSM tree during this cycle. It's not a big deal either way, it would
> just be nice to get an answer on that within the next week.

Similarly there are other changes for IMA and EVM. If you're willing to create
a topic branch for just the v10 patch set that can be merged into your tree and
into my tree, I'm fine with your upstreaming v10. (I'll wait to send my pull
request after yours.) Roberto will add my Ack's to the integrity, IMA, and EVM
related patches. However if you're not willing to create a topic branch, I'll
upstream the v10 patch set.

thanks,

Mimi


2024-02-15 08:20:59

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v9 12/25] security: Introduce file_post_open hook

On Wed, 2024-02-14 at 16:21 -0500, Paul Moore wrote:
> On Wed, Feb 14, 2024 at 3:07 PM Mimi Zohar <[email protected]> wrote:
> > On Tue, 2024-02-13 at 10:33 -0500, Paul Moore wrote:
> > > On Tue, Feb 13, 2024 at 7:59 AM Roberto Sassu
> > > <[email protected]> wrote:
> > > > On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
> > > > > On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar <[email protected]>
> > > > > wrote:
> > > > > > Hi Roberto,
> > > > > >
> > > > > >
> > > > > > > diff --git a/security/security.c b/security/security.c
> > > > > > > index d9d2636104db..f3d92bffd02f 100644
> > > > > > > --- a/security/security.c
> > > > > > > +++ b/security/security.c
> > > > > > > @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file)
> > > > > > > return fsnotify_perm(file, MAY_OPEN); <=== Conflict
> > > > > >
> > > > > > Replace with "return fsnotify_open_perm(file);"
> > > > > >
> > > > > > > }
> > > > > > >
> > > > > >
> > > > > > The patch set doesn't apply cleaning to 6.8-rcX without this
> > > > > > change. Unless
> > > > > > there are other issues, I can make the change.
> > > > >
> > > > > I take it this means you want to pull this via the IMA/EVM tree?
> > > >
> > > > Not sure about that, but I have enough changes to do to make a v10.
> >
> > @Roberto: please add my "Reviewed-by" to the remaining patches.
> >
> > > Sorry, I should have been more clear, the point I was trying to
> > > resolve was who was going to take this patchset (eventually). There
> > > are other patches destined for the LSM tree that touch the LSM hooks
> > > in a way which will cause conflicts with this patchset, and if
> > > you/Mimi are going to take this via the IMA/EVM tree - which is fine
> > > with me - I need to take that into account when merging things in the
> > > LSM tree during this cycle. It's not a big deal either way, it would
> > > just be nice to get an answer on that within the next week.
> >
> > Similarly there are other changes for IMA and EVM. If you're willing to
> > create
> > a topic branch for just the v10 patch set that can be merged into your tree
> > and
> > into my tree, I'm fine with your upstreaming v10. (I'll wait to send my pull
> > request after yours.) Roberto will add my Ack's to the integrity, IMA, and
> > EVM
> > related patches. However if you're not willing to create a topic branch,
> > I'll
> > upstream the v10 patch set.
>
> I'm not a big fan of sharing topic branches across different subsystem
> trees, I'd much rather just agree that one tree or another takes the
> patchset and the others plan accordingly.

Just curious why not?

> Based on our previous
> discussions I was under the impression that you wanted me to merge
> this patchset into lsm/dev, but it looks like that is no longer the
> case - which is okay by me.

Paul, I don't recall saying that. Please go ahead and upstream it. Roberto can
add my acks accordingly.

Mimi

> Assuming Roberto gets a v10 out soon, do you expect to merge the v10
> patchset and send it up during the upcoming merge window (for v6.9),
> or are you expecting to wait until after the upcoming merge window
> closes and target v6.10? Once again, either is fine, I'm just trying
> to coordinate this with other patches.




2024-02-15 09:30:26

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v9 19/25] integrity: Move integrity_kernel_module_request() to IMA

On Tue, 2024-02-13 at 11:31 -0500, Stefan Berger wrote:
>
> On 2/13/24 03:57, Roberto Sassu wrote:
> > On Mon, 2024-02-12 at 15:28 -0500, Stefan Berger wrote:
> > >
> > > On 2/12/24 12:56, Paul Moore wrote:
> > > > On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger <[email protected]> wrote:
> > > > > On 1/15/24 13:18, Roberto Sassu wrote:
> > > >
> > > > ...
> > > >
> > > > > > +/**
> > > > > > + * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
> > > > > > + * @kmod_name: kernel module name
> > > > > > + *
> > > > > > + * We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
> > > > > > + * construct an algorithm on the fly, but crypto_larval_lookup() will try
> > > > > > + * to use alg_name in order to load kernel module with same name.
> > > > > > + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
> > > > > > + * we are safe to fail such module request from crypto_larval_lookup().
> > > > > > + *
> > > > > > + * In this way we prevent modprobe execution during digsig verification
> > > > > > + * and avoid possible deadlock if modprobe and/or it's dependencies
> > > > > > + * also signed with digsig.
> > > > >
> > > > > This text needs to some reformulation at some point..
> > > >
> > > > There is no time like the present. If you have a suggestion I would
> > > > love to hear it and I'm sure Roberto would too.
> > > >
> > >
> > > My interpretation of the issue after possibly lossy decoding of the
> > > above sentences:
> > >
> > > Avoid a deadlock by rejecting a virtual kernel module with the name
> > > "crypto-pkcs1pad(rsa,*)". This module may be requested by
> > > crypto_larval_lookup() while trying to verify an RSA signature in
> > > public_key_verify_signature(). Since the loading of the RSA module may
> > > itself cause the request for an RSA signature verification it will
> > > otherwise lead to a deadlock.
> >
> > I can be even more precise I guess (I actually reproduced it). >
> > Avoid a verification loop where verifying the signature of the modprobe
> > binary requires executing modprobe itself. Since the modprobe iint-
> > > mutex is already held when the signature verification is performed, a
> > deadlock occurs as soon as modprobe is executed within the critical
> > region, since the same lock cannot be taken again.
>
> When ecdsa is used for signing files it could get stuck as well and
> would need this patch:
>
> diff --git a/security/integrity/ima/ima_main.c
> b/security/integrity/ima/ima_main.c
> index 45f1a102c599..2e71dc977d43 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -1110,7 +1110,9 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data);
> */
> static int ima_kernel_module_request(char *kmod_name)
> {
> - if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
> + if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0 ||
> + strncmp(kmod_name, "crypto-ecdsa-nist-p", 19) == 0 ||
> + strcmp(kmod_name, "cryptomgr") == 0)
> return -EINVAL;
>
> return 0;
>
> Rejecting cryptomgr seems necessary in the ecdsa case though I am not
> sure what the side effects of rejecting it all the time could be.

Thanks. Ok, let's find a proper way once IMA/EVM are moved to the LSM
infrastructure.

Roberto

> Stefan
>
> >
> > This happens when public_key_verify_signature(), in case of RSA
> > algorithm, use alg_name to store internal information in order to
> > construct an algorithm on the fly, but crypto_larval_lookup() will try
> > to use alg_name in order to load a kernel module with same name.
> >
> > Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
> > we are safe to fail such module request from crypto_larval_lookup(),
> > and avoid the verification loop.
> >
> > Roberto
> >
> >