2023-11-20 17:34:11

by Roberto Sassu

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

From: Roberto Sassu <[email protected]>

~~~~~
Note: this version is EXPERIMENTAL, I quickly tried to overcome outstanding
issues (use disjoint metadata, enforce LSM ordering), to see if it is
possible; tests pass, but a more careful review is still needed.
~~~~~

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.

In short, while this patch set is big, it does not make any functional
change to IMA and EVM. IMA and EVM functions are called by the LSM
infrastructure in the same places as before (except ima_post_path_mknod()),
rather being hardcoded calls.

Since IMA and EVM mostly use disjoint metadata, the existing
integrity_iint_cache structure was split in two. Fields were added to the
new ima_iint_cache and evm_iint_cache. In case of overlap, such as for the
path_post_mknod LSM hook, the implementation was duplicated to have the
same state as before this patch set.

This patch set also enforces the ordering of the newly introduced LSMs
'ima' and 'evm' at LSM infrastructure level, to make the ordering
independent on how the LSMs are compiled.


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.

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

Patches 22-23 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 24 finally removes the 'integrity' LSM, since 'ima' and 'evm' are now
self-contained and independent.

Patch 25 enforces the ordering of the 'ima' and 'evm' LSMs at LSM
infrastructure level.

The patch set applies on top of lsm/dev, commit e246777e2a03 ("MAINTAINERS:
update the LSM entry"). The linux-integrity/next-integrity-testing at
commit 6918df3be02a ("ima: Remove EXPERIMENTAL from Kconfig") was merged.

Changelog:

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
ima: Move to LSM infrastructure
ima: Move IMA-Appraisal to LSM infrastructure
evm: Move to LSM infrastructure
ima: Remove dependency on 'integrity' LSM
evm: Remove dependency on 'integrity' LSM
integrity: Remove LSM
security: Enforce ordering of 'ima' and 'evm' LSMs

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 | 111 +---------
include/linux/fs.h | 2 -
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/digsig_asymmetric.c | 23 --
security/integrity/evm/evm.h | 17 ++
security/integrity/evm/evm_crypto.c | 5 +-
security/integrity/evm/evm_main.c | 193 +++++++++++++----
security/integrity/iint.c | 197 +----------------
security/integrity/ima/ima.h | 68 ++++--
security/integrity/ima/ima_api.c | 15 +-
security/integrity/ima/ima_appraise.c | 72 ++++---
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 222 +++++++++++++------
security/integrity/ima/ima_policy.c | 2 +-
security/integrity/integrity.h | 26 +--
security/keys/key.c | 10 +-
security/security.c | 286 ++++++++++++++++---------
security/selinux/hooks.c | 3 +-
security/smack/smack_lsm.c | 4 +-
30 files changed, 730 insertions(+), 816 deletions(-)

--
2.34.1


2023-11-20 17:34:34

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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

2023-11-20 17:34:39

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 d7b15ea67c3f..c87ba1bbd7dc 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2819,7 +2819,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

2023-11-20 17:35:16

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 ec5c8065ea36..358ec01a5492 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

2023-11-20 17:35:24

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 c87ba1bbd7dc..ec5c8065ea36 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

2023-11-20 17:36:45

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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

2023-11-20 17:36:49

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 cf976d8dbd7a..7c6a74dbc093 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,
@@ -106,7 +106,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 d452d469c503..7fc083d53fdf 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -558,6 +558,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
@@ -567,7 +568,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 358ec01a5492..ae3625198c9f 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

2023-11-20 17:36:49

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 c925a0d26edf..752ed8a4f3c6 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 53793f3cb36a..7935d11d58b5 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 b340425ccfae..7363e0a07867 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 53336d7daa93..3f60cc4b3b82 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

2023-11-20 17:38:18

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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]>
---
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..84a4aa566c02 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)
+ goto out;

- 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 091cddb4e6de..c3199bb69103 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 664df46b22a9..922ea7709bae 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,
@@ -940,6 +941,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 ce3bc7642e18..8aa6e9f316dd 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

2023-11-20 17:38:58

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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.

LSMs could also take some action before the last reference of a file is
released.

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 e2b45fee94e2..175ca00a6b1d 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 c360458920b1..4c3585e3dcb4 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_mmap_file(struct file *file, unsigned long prot,
@@ -1006,6 +1007,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 fe6a160afc35..9aa072ca5a19 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

2023-11-20 17:39:08

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 7c6a74dbc093..437d4076a3b3 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_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *xattr_name);
extern void evm_inode_post_removexattr(struct dentry *dentry,
@@ -55,7 +56,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,
@@ -114,7 +115,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 7fc083d53fdf..ea84a6f835ff 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -730,6 +730,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.
*
@@ -738,7 +739,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 ae3625198c9f..53793f3cb36a 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

2023-11-20 17:39:25

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 15/25] security: Introduce inode_post_create_tmpfile hook

From: Roberto Sassu <[email protected]>

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

As temp files can be made persistent, treat new temp files like other new
files, so that the file hash is calculated and stored in the security
xattr.

LSMs could also take some action after temp files have been created.

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

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

diff --git a/fs/namei.c b/fs/namei.c
index b7f433720b1e..adb3ab27951a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3686,6 +3686,7 @@ static int vfs_tmpfile(struct mnt_idmap *idmap,
inode->i_state |= I_LINKABLE;
spin_unlock(&inode->i_lock);
}
+ security_inode_post_create_tmpfile(idmap, inode);
ima_post_create_tmpfile(idmap, inode);
return 0;
}
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index ee5ab180a312..4b195996f848 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -121,6 +121,8 @@ LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,
const struct qstr *name, const struct inode *context_inode)
LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,
umode_t mode)
+LSM_HOOK(void, LSM_RET_VOID, inode_post_create_tmpfile, struct mnt_idmap *idmap,
+ struct inode *inode)
LSM_HOOK(int, 0, inode_link, struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
LSM_HOOK(int, 0, inode_unlink, struct inode *dir, struct dentry *dentry)
diff --git a/include/linux/security.h b/include/linux/security.h
index 5f595135c8f2..d77b717b5a45 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -344,6 +344,8 @@ int security_inode_init_security_anon(struct inode *inode,
const struct qstr *name,
const struct inode *context_inode);
int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
+void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
+ struct inode *inode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
@@ -809,6 +811,10 @@ static inline int security_inode_create(struct inode *dir,
return 0;
}

+static inline void
+security_inode_post_create_tmpfile(struct mnt_idmap *idmap, struct inode *inode)
+{ }
+
static inline int security_inode_link(struct dentry *old_dentry,
struct inode *dir,
struct dentry *new_dentry)
diff --git a/security/security.c b/security/security.c
index b0203c488d17..a1bdf4859448 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2013,6 +2013,21 @@ int security_inode_create(struct inode *dir, struct dentry *dentry,
}
EXPORT_SYMBOL_GPL(security_inode_create);

+/**
+ * security_inode_post_create_tmpfile() - Update inode security of new tmpfile
+ * @idmap: idmap of the mount
+ * @inode: inode of the new tmpfile
+ *
+ * Update inode security data after a tmpfile has been created.
+ */
+void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
+ struct inode *inode)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+ call_void_hook(inode_post_create_tmpfile, idmap, inode);
+}
+
/**
* security_inode_link() - Check if creating a hard link is allowed
* @old_dentry: existing file
--
2.34.1

2023-11-20 17:39:42

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 4b195996f848..5133dd88b5fb 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 d77b717b5a45..948aaddf0edd 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,
@@ -913,6 +915,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 a1bdf4859448..5d25bbd18d66 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

2023-11-20 17:40:10

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 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 5133dd88b5fb..bfb10a1e6a44 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 948aaddf0edd..7b753460f09d 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);
@@ -934,6 +937,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 5d25bbd18d66..a722d5db0a2c 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

2023-11-20 17:42:14

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 18/25] security: Introduce key_post_create_or_update hook

From: Roberto Sassu <[email protected]>

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

Depending on policy, IMA measures the key content after creation or update,
so that remote verifiers are aware of the operation.

Other LSMs could similarly take some action after successful key creation
or update.

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]>
---
include/linux/lsm_hook_defs.h | 3 +++
include/linux/security.h | 11 +++++++++++
security/keys/key.c | 7 ++++++-
security/security.c | 19 +++++++++++++++++++
4 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index bfb10a1e6a44..2679905f4260 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -403,6 +403,9 @@ LSM_HOOK(void, LSM_RET_VOID, key_free, struct key *key)
LSM_HOOK(int, 0, key_permission, key_ref_t key_ref, const struct cred *cred,
enum key_need_perm need_perm)
LSM_HOOK(int, 0, key_getsecurity, struct key *key, char **buffer)
+LSM_HOOK(void, LSM_RET_VOID, key_post_create_or_update, struct key *keyring,
+ struct key *key, const void *payload, size_t payload_len,
+ unsigned long flags, bool create)
#endif /* CONFIG_KEYS */

#ifdef CONFIG_AUDIT
diff --git a/include/linux/security.h b/include/linux/security.h
index 7b753460f09d..766eaccc4679 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1995,6 +1995,9 @@ void security_key_free(struct key *key);
int security_key_permission(key_ref_t key_ref, const struct cred *cred,
enum key_need_perm need_perm);
int security_key_getsecurity(struct key *key, char **_buffer);
+void security_key_post_create_or_update(struct key *keyring, struct key *key,
+ const void *payload, size_t payload_len,
+ unsigned long flags, bool create);

#else

@@ -2022,6 +2025,14 @@ static inline int security_key_getsecurity(struct key *key, char **_buffer)
return 0;
}

+static inline void security_key_post_create_or_update(struct key *keyring,
+ struct key *key,
+ const void *payload,
+ size_t payload_len,
+ unsigned long flags,
+ bool create)
+{ }
+
#endif
#endif /* CONFIG_KEYS */

diff --git a/security/keys/key.c b/security/keys/key.c
index 0260a1902922..f75fe66c2f03 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -935,6 +935,8 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,
goto error_link_end;
}

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

@@ -968,10 +970,13 @@ 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 a722d5db0a2c..423d53092604 100644
--- a/security/security.c
+++ b/security/security.c
@@ -5406,6 +5406,25 @@ int security_key_getsecurity(struct key *key, char **buffer)
*buffer = NULL;
return call_int_hook(key_getsecurity, 0, key, buffer);
}
+
+/**
+ * security_key_post_create_or_update() - Notification of key create or update
+ * @keyring: keyring to which the key is linked to
+ * @key: created or updated key
+ * @payload: data used to instantiate or update the key
+ * @payload_len: length of payload
+ * @flags: key flags
+ * @create: flag indicating whether the key was created or updated
+ *
+ * Notify the caller of a key creation or update.
+ */
+void security_key_post_create_or_update(struct key *keyring, struct key *key,
+ const void *payload, size_t payload_len,
+ unsigned long flags, bool create)
+{
+ call_void_hook(key_post_create_or_update, keyring, key, payload,
+ payload_len, flags, create);
+}
#endif /* CONFIG_KEYS */

#ifdef CONFIG_AUDIT
--
2.34.1

2023-11-20 17:42:23

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 19/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').

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(). Conditionally register
ima_post_path_mknod() if CONFIG_SECURITY_PATH is enabled, otherwise the
path_post_mknod hook won't be available.

Move integrity_kernel_module_request() to IMA (renamed to
ima_kernel_module_request()), and conditionally register it as
implementation of the kernel_module_request LSM hook (if
CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled).

Define the 'ima' LSM, and initialize it with init_ima_lsm(). Consequently,
assign the LSM_ID_IMA ID to IMA in include/uapi/linux/lsm.h.

Still rely on the existing 'integrity' subsystem to be enabled and to
manage integrity metadata.

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 | 94 ---------------------
include/linux/integrity.h | 13 ---
include/uapi/linux/lsm.h | 1 +
security/integrity/digsig_asymmetric.c | 22 -----
security/integrity/iint.c | 2 +-
security/integrity/ima/ima.h | 6 ++
security/integrity/ima/ima_main.c | 108 ++++++++++++++++++++-----
security/integrity/integrity.h | 1 +
security/keys/key.c | 9 +--
security/security.c | 63 +++------------
14 files changed, 110 insertions(+), 225 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 31ef6c3c3207..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,
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/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index f0386880a78e..ee7d034255a9 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -61,6 +61,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/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 895f4b9ce8c6..4d11c622fabd 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -133,25 +133,3 @@ int asymmetric_verify(struct key *keyring, const char *sig,
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/iint.c b/security/integrity/iint.c
index d4419a2a1e24..6cbf2aa5540e 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -200,13 +200,13 @@ static int __init integrity_iintcache_init(void)
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/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 02021ee467d3..af213bece9b8 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,8 @@ 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 __maybe_unused
+ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
@@ -753,8 +753,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 +803,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 +837,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 +891,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) &&
@@ -1122,4 +1122,72 @@ static int __init init_ima(void)
return error;
}

+/**
+ * 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.
+ */
+static int __maybe_unused ima_kernel_module_request(char *kmod_name)
+{
+ if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+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),
+#ifdef CONFIG_SECURITY_PATH
+ LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
+#endif
+#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,
+};
+
+/*
+ * Since with the LSM_ORDER_LAST there is no guarantee about the ordering
+ * within the .lsm_info.init section, ensure that IMA hooks are before EVM
+ * ones, by letting the 'integrity' LSM call init_ima_lsm() to initialize the
+ * 'ima' and 'evm' LSMs in this sequence.
+ */
+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 423d53092604..e18953ee4a97 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);
}

/**
@@ -2883,13 +2879,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);
}

/**
@@ -2918,12 +2909,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);
}

/**
@@ -3232,12 +3218,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 integrity_kernel_module_request(kmod_name);
+ return call_int_hook(kernel_module_request, 0, kmod_name);
}

/**
@@ -3253,12 +3234,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);

@@ -3278,12 +3254,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);

@@ -3298,12 +3269,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);

@@ -3325,13 +3291,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);

--
2.34.1

2023-11-20 17:42:32

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 21/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'
(at the end of the LSM list and always enabled, like 'ima' and
'integrity'). Consequently, assign the LSM_ID_EVM ID to the 'evm' LSM in
include/uapi/linux/lsm.h.

Make EVM functions as static (except for evm_inode_init_security(), which
is exported), and register them as hook implementations in init_evm_lsm().

Introduce evm_post_path_mknod() to set the IMA_NEW_FILE flag, in
preparation to when IMA and EVM use disjoint integrity metadata.

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]>
---
fs/attr.c | 2 -
fs/posix_acl.c | 3 -
fs/xattr.c | 2 -
include/linux/evm.h | 107 -----------------------
include/uapi/linux/lsm.h | 1 +
security/integrity/evm/evm_main.c | 141 ++++++++++++++++++++++++++----
security/security.c | 45 +++-------
7 files changed, 138 insertions(+), 163 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 84a4aa566c02..2660bc7effdc 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 437d4076a3b3..cb481eccc967 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -21,44 +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_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);
@@ -93,75 +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_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 ee7d034255a9..825339bcd580 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -62,6 +62,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 ea84a6f835ff..f1ce9d0a490f 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -566,9 +566,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;

@@ -598,8 +598,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
@@ -649,9 +649,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;

@@ -690,6 +692,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;
@@ -738,9 +758,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;
@@ -756,6 +778,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
@@ -766,7 +803,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;
@@ -782,6 +820,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)
{
@@ -805,8 +859,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;
@@ -853,8 +907,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;
@@ -925,6 +979,17 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
}
EXPORT_SYMBOL_GPL(evm_inode_init_security);

+static void __maybe_unused
+evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+ struct integrity_iint_cache *iint;
+
+ iint = integrity_iint_find(d_backing_inode(dentry));
+ if (iint)
+ /* needed for successful verification of empty files */
+ iint->flags |= IMA_NEW_FILE;
+}
+
#ifdef CONFIG_EVM_LOAD_X509
void __init evm_load_x509(void)
{
@@ -964,4 +1029,50 @@ 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_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),
+#ifdef CONFIG_SECURITY_PATH
+ LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod),
+#endif
+
+};
+
+static const struct lsm_id evm_lsmid = {
+ .name = "evm",
+ .id = LSM_ID_EVM,
+};
+
+/*
+ * Since with the LSM_ORDER_LAST there is no guarantee about the ordering
+ * within the .lsm_info.init section, ensure that IMA hooks are before EVM
+ * ones, by letting the 'integrity' LSM call init_evm_lsm() to initialize the
+ * 'ima' and 'evm' LSMs in this sequence.
+ */
+int __init init_evm_lsm(void)
+{
+ security_add_hooks(evm_hooks, ARRAY_SIZE(evm_hooks), &evm_lsmid);
+ return 0;
+}
+
+static struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
+ .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/security.c b/security/security.c
index d4ead59fb91f..7741d2d076c5 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
@@ -1715,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;
@@ -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;
}

/**
--
2.34.1

2023-11-20 17:42:46

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 22/25] ima: Remove dependency on 'integrity' LSM

From: Roberto Sassu <[email protected]>

Remove the dependency on the 'integrity' LSM by introducing IMA own
integrity metadata (ima_iint_cache structure, with IMA-specific fields from
the integrity_iint_cache structure), and by managing it directly from the
'ima' LSM.

First, replace integrity_iint_cache with ima_iint_cache in various places
of the IMA code.

Then, reserve space in the security blob for the entire ima_iint_cache
structure, so that retrieval of integrity metadata always succeeds, as
opposed to when integrity_inode_get() was called, which could fail. Adjust
the IMA code accordingly, call ima_inode_get_iint() to retrieve the
ima_iint_cache structure, and remove the now unnecessary non-NULL checks.

Don't include the inode pointer as field in the ima_iint_cache structure,
since the association with the inode is clear. Since the inode field is
missing in ima_iint_cache, pass the extra inode parameter to
ima_get_verity_digest().

Also, since IMA and EVM at this point are using different structures to
store integrity metadata, pass NULL to evm_verifyxattr(). Unfortunately,
this alone does not work, since EVM is expecting IMA to have allocated
integrity metadata with integrity_inode_get(), for files being appraised.

Since this is not true anymore, the integrity_iint_find() call in EVM
always returns NULL making it impossible for EVM to access integrity
metadata. Temporary fix this issue by forcing the creation of integrity
metadata with integrity_inode_get().

Finally, register ima_inode_alloc_security() for the inode_alloc_security
LSM hook, to initialize the new ima_iint_cache structure (before this task
was done by iint_init_always()). Also, duplicate iint_lockdep_annotate()
for the ima_iint_cache structure, and name it ima_iint_lockdep_annotate().

Signed-off-by: Roberto Sassu <[email protected]>
---
security/integrity/evm/evm_main.c | 4 +-
security/integrity/ima/ima.h | 57 +++++++++----
security/integrity/ima/ima_api.c | 15 ++--
security/integrity/ima/ima_appraise.c | 35 ++++----
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 111 ++++++++++++++------------
security/integrity/ima/ima_policy.c | 2 +-
7 files changed, 129 insertions(+), 97 deletions(-)

diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index f1ce9d0a490f..1e59a985b845 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -409,7 +409,7 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
return INTEGRITY_UNKNOWN;

if (!iint) {
- iint = integrity_iint_find(d_backing_inode(dentry));
+ iint = integrity_inode_get(d_backing_inode(dentry));
if (!iint)
return INTEGRITY_UNKNOWN;
}
@@ -984,7 +984,7 @@ evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
struct integrity_iint_cache *iint;

- iint = integrity_iint_find(d_backing_inode(dentry));
+ iint = integrity_inode_get(d_backing_inode(dentry));
if (iint)
/* needed for successful verification of empty files */
iint->flags |= IMA_NEW_FILE;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index a27fc10f84f7..7c94277f2929 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -60,7 +60,7 @@ extern const char boot_aggregate_name[];

/* IMA event related data */
struct ima_event_data {
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
struct file *file;
const unsigned char *filename;
struct evm_ima_xattr_data *xattr_value;
@@ -119,6 +119,34 @@ struct ima_kexec_hdr {
u64 count;
};

+/* IMA integrity metadata associated with an inode */
+struct ima_iint_cache {
+ struct mutex mutex; /* protects: version, flags, digest */
+ 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;
+ struct ima_digest_data *ima_hash;
+};
+
+extern struct lsm_blob_sizes ima_blob_sizes;
+
+static inline struct ima_iint_cache *
+ima_inode_get_iint(const struct inode *inode)
+{
+ struct ima_iint_cache *ima_iint_sec;
+
+ ima_iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
+ return ima_iint_sec;
+}
+
extern const int read_idmap[];

#ifdef CONFIG_HAVE_IMA_KEXEC
@@ -152,7 +180,7 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
struct ima_template_entry *entry);
int ima_calc_boot_aggregate(struct ima_digest_data *hash);
void ima_add_violation(struct file *file, const unsigned char *filename,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
const char *op, const char *cause);
int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
@@ -267,10 +295,10 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
-int ima_collect_measurement(struct integrity_iint_cache *iint,
+int ima_collect_measurement(struct ima_iint_cache *iint,
struct file *file, void *buf, loff_t size,
enum hash_algo algo, struct modsig *modsig);
-void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
@@ -280,7 +308,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
const char *eventname, enum ima_hooks func,
int pcr, const char *func_data,
bool buf_hash, u8 *digest, size_t digest_len);
-void ima_audit_measurement(struct integrity_iint_cache *iint,
+void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry,
@@ -318,17 +346,17 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_KEXEC 0x40

#ifdef CONFIG_IMA_APPRAISE
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr);
int ima_appraise_measurement(enum ima_hooks func,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig);
int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
int mask, enum ima_hooks func);
-void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
-enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+void ima_update_xattr(struct ima_iint_cache *iint, struct file *file);
+enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func);
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len);
@@ -337,14 +365,14 @@ int ima_read_xattr(struct dentry *dentry,
void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);

#else
-static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
+static inline int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
return 0;
}

static inline int ima_appraise_measurement(enum ima_hooks func,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
@@ -361,14 +389,13 @@ static inline int ima_must_appraise(struct mnt_idmap *idmap,
return 0;
}

-static inline void ima_update_xattr(struct integrity_iint_cache *iint,
+static inline void ima_update_xattr(struct ima_iint_cache *iint,
struct file *file)
{
}

-static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
- *iint,
- enum ima_hooks func)
+static inline enum integrity_status
+ima_get_cache_status(struct ima_iint_cache *iint, enum ima_hooks func)
{
return INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 597ea0c4d72f..f3363935804f 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -131,7 +131,7 @@ int ima_store_template(struct ima_template_entry *entry,
* value is invalidated.
*/
void ima_add_violation(struct file *file, const unsigned char *filename,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
const char *op, const char *cause)
{
struct ima_template_entry *entry;
@@ -201,7 +201,8 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
allowed_algos);
}

-static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
+static bool ima_get_verity_digest(struct ima_iint_cache *iint,
+ struct inode *inode,
struct ima_max_digest_data *hash)
{
enum hash_algo alg;
@@ -211,7 +212,7 @@ static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
* On failure, 'measure' policy rules will result in a file data
* hash containing 0's.
*/
- digest_len = fsverity_get_digest(iint->inode, hash->digest, NULL, &alg);
+ digest_len = fsverity_get_digest(inode, hash->digest, NULL, &alg);
if (digest_len == 0)
return false;

@@ -237,7 +238,7 @@ static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
*
* Return 0 on success, error code otherwise
*/
-int ima_collect_measurement(struct integrity_iint_cache *iint,
+int ima_collect_measurement(struct ima_iint_cache *iint,
struct file *file, void *buf, loff_t size,
enum hash_algo algo, struct modsig *modsig)
{
@@ -280,7 +281,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
memset(&hash.digest, 0, sizeof(hash.digest));

if (iint->flags & IMA_VERITY_REQUIRED) {
- if (!ima_get_verity_digest(iint, &hash)) {
+ if (!ima_get_verity_digest(iint, inode, &hash)) {
audit_cause = "no-verity-digest";
result = -ENODATA;
}
@@ -338,7 +339,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
*
* Must be called with iint->mutex held.
*/
-void ima_store_measurement(struct integrity_iint_cache *iint,
+void ima_store_measurement(struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
@@ -382,7 +383,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
ima_free_template_entry(entry);
}

-void ima_audit_measurement(struct integrity_iint_cache *iint,
+void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename)
{
struct audit_buffer *ab;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 076451109637..b0b96c263961 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -85,7 +85,7 @@ int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
}

static int ima_fix_xattr(struct dentry *dentry,
- struct integrity_iint_cache *iint)
+ struct ima_iint_cache *iint)
{
int rc, offset;
u8 algo = iint->ima_hash->algo;
@@ -106,7 +106,7 @@ static int ima_fix_xattr(struct dentry *dentry,
}

/* Return specific func appraised cached result */
-enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func)
{
switch (func) {
@@ -126,7 +126,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
}
}

-static void ima_set_cache_status(struct integrity_iint_cache *iint,
+static void ima_set_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func,
enum integrity_status status)
{
@@ -152,8 +152,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
}
}

-static void ima_cache_flags(struct integrity_iint_cache *iint,
- enum ima_hooks func)
+static void ima_cache_flags(struct ima_iint_cache *iint, enum ima_hooks func)
{
switch (func) {
case MMAP_CHECK:
@@ -276,7 +275,7 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
*
* Return 0 on success, error code otherwise.
*/
-static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
+static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
@@ -443,7 +442,7 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
*
* Returns -EPERM if the hash is blacklisted.
*/
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
enum hash_algo hash_algo;
@@ -478,7 +477,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
* Return 0 on success, error code otherwise
*/
int ima_appraise_measurement(enum ima_hooks func,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig)
@@ -520,7 +519,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, NULL);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
@@ -603,7 +602,7 @@ int ima_appraise_measurement(enum ima_hooks func,
/*
* ima_update_xattr - update 'security.ima' hash value
*/
-void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
+void ima_update_xattr(struct ima_iint_cache *iint, struct file *file)
{
struct dentry *dentry = file_dentry(file);
int rc = 0;
@@ -640,7 +639,7 @@ 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;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode);
int action;

if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
@@ -648,12 +647,9 @@ static void ima_inode_post_setattr(struct mnt_idmap *idmap,
return;

action = ima_must_appraise(idmap, inode, MAY_ACCESS, POST_SETATTR);
- iint = integrity_iint_find(inode);
- if (iint) {
- set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
- if (!action)
- clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
- }
+ set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
+ if (!action)
+ clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
}

/*
@@ -674,14 +670,11 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,

static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode);

if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return;

- iint = integrity_iint_find(inode);
- if (!iint)
- return;
iint->measured_pcrs = 0;
set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
if (digsig)
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 63979aefc95f..393f5c7912d5 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -44,7 +44,7 @@ static int __init ima_add_boot_aggregate(void)
static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
- struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
+ struct ima_iint_cache tmp_iint, *iint = &tmp_iint;
struct ima_event_data event_data = { .iint = iint,
.filename = boot_aggregate_name };
struct ima_max_digest_data hash;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 3b5d53a7f755..737bca82dd5a 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -114,7 +114,7 @@ static int mmap_violation_check(enum ima_hooks func, struct file *file,
*
*/
static void ima_rdwr_violation_check(struct file *file,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
int must_measure,
char **pathbuf,
const char **pathname,
@@ -125,12 +125,9 @@ static void ima_rdwr_violation_check(struct file *file,
bool send_tomtou = false, send_writers = false;

if (mode & FMODE_WRITE) {
- if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
- if (!iint)
- iint = integrity_iint_find(inode);
+ if (atomic_read(&inode->i_readcount)) {
/* IMA_MEASURE is set from reader side */
- if (iint && test_bit(IMA_MUST_MEASURE,
- &iint->atomic_flags))
+ if (test_bit(IMA_MUST_MEASURE, &iint->atomic_flags))
send_tomtou = true;
}
} else {
@@ -153,7 +150,7 @@ static void ima_rdwr_violation_check(struct file *file,
"invalid_pcr", "open_writers");
}

-static void ima_check_last_writer(struct integrity_iint_cache *iint,
+static void ima_check_last_writer(struct ima_iint_cache *iint,
struct inode *inode, struct file *file)
{
fmode_t mode = file->f_mode;
@@ -192,15 +189,11 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
static void ima_file_free(struct file *file)
{
struct inode *inode = file_inode(file);
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode);

if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return;

- iint = integrity_iint_find(inode);
- if (!iint)
- return;
-
ima_check_last_writer(iint, inode, file);
}

@@ -209,7 +202,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
enum ima_hooks func)
{
struct inode *backing_inode, *inode = file_inode(file);
- struct integrity_iint_cache *iint = NULL;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode);
struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
@@ -247,20 +240,12 @@ static int process_measurement(struct file *file, const struct cred *cred,

inode_lock(inode);

- if (action) {
- iint = integrity_inode_get(inode);
- if (!iint)
- rc = -ENOMEM;
- }
-
- if (!rc && violation_check)
+ if (violation_check)
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
&pathbuf, &pathname, filename);

inode_unlock(inode);

- if (rc)
- goto out;
if (!action)
goto out;

@@ -564,21 +549,11 @@ static int ima_file_check(struct file *file, int mask)
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
size_t buf_size)
{
- struct integrity_iint_cache *iint = NULL, tmp_iint;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode), tmp_iint;
int rc, hash_algo;

- if (ima_policy_flag) {
- iint = integrity_iint_find(inode);
- if (iint)
- mutex_lock(&iint->mutex);
- }
-
- if ((!iint || !(iint->flags & IMA_COLLECTED)) && file) {
- if (iint)
- mutex_unlock(&iint->mutex);
-
+ if ((!(iint->flags & IMA_COLLECTED)) && file) {
memset(&tmp_iint, 0, sizeof(tmp_iint));
- tmp_iint.inode = inode;
mutex_init(&tmp_iint.mutex);

rc = ima_collect_measurement(&tmp_iint, file, NULL, 0,
@@ -592,11 +567,9 @@ static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
}

iint = &tmp_iint;
- mutex_lock(&iint->mutex);
}

- if (!iint)
- return -EOPNOTSUPP;
+ mutex_lock(&iint->mutex);

/*
* ima_file_hash can be called when ima_collect_measurement has still
@@ -688,7 +661,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode)

{
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode);
int must_appraise;

if (!ima_policy_flag || !S_ISREG(inode->i_mode))
@@ -699,11 +672,6 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
if (!must_appraise)
return;

- /* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
- if (!iint)
- return;
-
/* needed for writing the security xattrs */
set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
iint->ima_file_status = INTEGRITY_PASS;
@@ -720,8 +688,8 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
static void __maybe_unused
ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
- struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
+ struct ima_iint_cache *iint = ima_inode_get_iint(inode);
int must_appraise;

if (!ima_policy_flag || !S_ISREG(inode->i_mode))
@@ -732,11 +700,6 @@ ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
if (!must_appraise)
return;

- /* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
- if (!iint)
- return;
-
/* needed for re-opening empty files */
iint->flags |= IMA_NEW_FILE;
}
@@ -936,7 +899,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
int ret = 0;
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry = NULL;
- struct integrity_iint_cache iint = {};
+ struct ima_iint_cache iint = {};
struct ima_event_data event_data = {.iint = &iint,
.filename = eventname,
.buf = buf,
@@ -1145,6 +1108,48 @@ static int __maybe_unused ima_kernel_module_request(char *kmod_name)
return 0;
}

+#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 ima_iint_lockdep_annotate(struct ima_iint_cache *ima_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(&ima_iint->mutex, &iint_mutex_key[depth]);
+#endif
+}
+
+static int ima_inode_alloc_security(struct inode *inode)
+{
+ struct ima_iint_cache *iint = ima_inode_get_iint(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->measured_pcrs = 0;
+ mutex_init(&iint->mutex);
+ ima_iint_lockdep_annotate(iint, inode);
+ return 0;
+}
+
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),
@@ -1156,6 +1161,7 @@ static struct security_hook_list ima_hooks[] __ro_after_init = {
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(inode_alloc_security, ima_inode_alloc_security),
#ifdef CONFIG_SECURITY_PATH
LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
#endif
@@ -1185,10 +1191,15 @@ int __init init_ima_lsm(void)
return 0;
}

+struct lsm_blob_sizes ima_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct ima_iint_cache),
+};
+
DEFINE_LSM(ima) = {
.name = "ima",
.init = init_ima_lsm,
.order = LSM_ORDER_LAST,
+ .blobs = &ima_blob_sizes,
};

late_initcall(init_ima); /* Start IMA after the TPM is available */
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index f69062617754..c0556907c2e6 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -49,7 +49,7 @@
#define DONT_HASH 0x0200

#define INVALID_PCR(a) (((a) < 0) || \
- (a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8))
+ (a) >= (sizeof_field(struct ima_iint_cache, measured_pcrs) * 8))

int ima_policy_flag;
static int temp_ima_appraise;
--
2.34.1

2023-11-20 17:43:53

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 23/25] evm: Remove dependency on 'integrity' LSM

From: Roberto Sassu <[email protected]>

Similarly to IMA, introduce EVM own integrity metadata (evm_iint_cache,
with EVM-specific fields from integrity_iint_cache), and reserve them from
the 'evm' LSM.

First, replace the integrity_iint_cache structure with evm_iint_cache in
various places of the EVM code.

Then, reserve space in the security blob for the evm_iint_cache structure,
so that retrieval always succeeds. Replace integrity_inode_get() and
integrity_iint_find() with evm_inode_get_iint(), to retrieve the
evm_iint_cache structure.

Initialize the new evm_iint_cache structure by registering
evm_inode_alloc_security() as implementation of the inode_alloc_security
LSM hook.

Since now IMA and EVM integrity metadata are disjoint, and always
available, remove the iint parameter from evm_verifyxattr() and always
retrieve the evm_iint_cache structure in evm_verify_hmac(), called by
evm_verifyxattr() and evm_verify_current_integrity().

Signed-off-by: Roberto Sassu <[email protected]>
---
include/linux/evm.h | 8 +---
security/integrity/evm/evm.h | 17 ++++++++
security/integrity/evm/evm_crypto.c | 5 +--
security/integrity/evm/evm_main.c | 63 ++++++++++++++-------------
security/integrity/ima/ima_appraise.c | 2 +-
5 files changed, 54 insertions(+), 41 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/evm.h b/security/integrity/evm/evm.h
index 53bd7fec93fa..478b6fbca699 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -32,6 +32,23 @@ struct xattr_list {
bool enabled;
};

+/* 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_inode_get_iint(const struct inode *inode)
+{
+ struct evm_iint_cache *evm_iint_sec;
+
+ evm_iint_sec = inode->i_security + evm_blob_sizes.lbs_inode;
+ return evm_iint_sec;
+}
+
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..c69422cc4a52 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -322,11 +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 = evm_inode_get_iint(inode);
int rc = 0;

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

/* Do this the hard way */
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 1e59a985b845..5aa5207a75e1 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -167,18 +167,20 @@ static int evm_find_protected_xattrs(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 evm_iint_cache *iint;
int rc, xattr_len, evm_immutable = 0;

- if (iint && (iint->evm_status == INTEGRITY_PASS ||
- iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
+ iint = evm_inode_get_iint(d_backing_inode(dentry));
+
+ if ((iint->evm_status == INTEGRITY_PASS ||
+ iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
return iint->evm_status;

/* if status is not PASS, try to check again - against -ENOMEM */
@@ -243,8 +245,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
inode = d_backing_inode(dentry);

if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
- if (iint)
- iint->flags |= EVM_IMMUTABLE_DIGSIG;
+ iint->flags |= EVM_IMMUTABLE_DIGSIG;
evm_status = INTEGRITY_PASS_IMMUTABLE;
} else if (!IS_RDONLY(inode) &&
!(inode->i_sb->s_readonly_remount) &&
@@ -271,8 +272,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
pr_debug("digest: (%d) [%*phN]\n", digest.hdr.length, digest.hdr.length,
digest.digest);
out:
- if (iint)
- iint->evm_status = evm_status;
+ iint->evm_status = evm_status;
kfree(xattr_data);
return evm_status;
}
@@ -389,7 +389,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
@@ -402,19 +401,13 @@ 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;

- if (!iint) {
- iint = integrity_inode_get(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);

@@ -431,7 +424,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);
}

/*
@@ -503,14 +496,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_inode_get_iint(d_backing_inode(dentry));
+ if ((iint->flags & IMA_NEW_FILE))
return 0;

/* exception for pseudo filesystems */
@@ -712,11 +705,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 = evm_inode_get_iint(inode);

- iint = integrity_iint_find(inode);
- if (iint)
- iint->evm_status = INTEGRITY_UNKNOWN;
+ iint->evm_status = INTEGRITY_UNKNOWN;
}

/**
@@ -982,12 +973,11 @@ EXPORT_SYMBOL_GPL(evm_inode_init_security);
static void __maybe_unused
evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;

- iint = integrity_inode_get(d_backing_inode(dentry));
- if (iint)
- /* needed for successful verification of empty files */
- iint->flags |= IMA_NEW_FILE;
+ iint = evm_inode_get_iint(d_backing_inode(dentry));
+ /* needed for successful verification of empty files */
+ iint->flags |= IMA_NEW_FILE;
}

#ifdef CONFIG_EVM_LOAD_X509
@@ -1029,6 +1019,15 @@ static int __init init_evm(void)
return error;
}

+static int evm_inode_alloc_security(struct inode *inode)
+{
+ struct evm_iint_cache *evm_iint = evm_inode_get_iint(inode);
+
+ evm_iint->flags = 0UL;
+ evm_iint->evm_status = INTEGRITY_UNKNOWN;
+ return 0;
+}
+
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),
@@ -1041,6 +1040,7 @@ 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),
#ifdef CONFIG_SECURITY_PATH
LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod),
#endif
@@ -1064,7 +1064,8 @@ int __init init_evm_lsm(void)
return 0;
}

-static struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
+struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct evm_iint_cache),
.lbs_xattr_count = 1,
};

diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index b0b96c263961..89125efb7e06 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -519,7 +519,7 @@ int ima_appraise_measurement(enum ima_hooks func,
}

status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
- rc < 0 ? 0 : rc, NULL);
+ rc < 0 ? 0 : rc);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
--
2.34.1

2023-11-20 17:43:55

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 24/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/fs.h | 2 -
include/linux/integrity.h | 14 --
security/integrity/digsig_asymmetric.c | 1 -
security/integrity/iint.c | 197 +------------------------
security/integrity/integrity.h | 25 ----
security/security.c | 2 -
6 files changed, 2 insertions(+), 239 deletions(-)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 98b7a7a8c42e..745c1faf6c5b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2104,7 +2104,6 @@ struct super_operations {
#define S_NOCMTIME (1 << 7) /* Do not update file c/mtime */
#define S_SWAPFILE (1 << 8) /* Do not truncate: swapon got its bmaps */
#define S_PRIVATE (1 << 9) /* Inode is fs-internal */
-#define S_IMA (1 << 10) /* Inode has an associated IMA struct */
#define S_AUTOMOUNT (1 << 11) /* Automount/referral quasi-directory */
#define S_NOSEC (1 << 12) /* no suid or xattr security attributes */
#ifdef CONFIG_FS_DAX
@@ -2156,7 +2155,6 @@ static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
#define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE)
#define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE)
-#define IS_IMA(inode) ((inode)->i_flags & S_IMA)
#define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT)
#define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC)
#define IS_DAX(inode) ((inode)->i_flags & S_DAX)
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/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 4d11c622fabd..de603cf42ac7 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -132,4 +132,3 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
-
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 6cbf2aa5540e..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 59eaddd84434..1f473fe876eb 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -156,31 +156,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 7741d2d076c5..351a124b771c 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

2023-11-20 17:43:58

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v6 25/25] security: Enforce ordering of 'ima' and 'evm' LSMs

From: Roberto Sassu <[email protected]>

The ordering of LSM_ORDER_LAST LSMs depends on how they are placed in the
.lsm_info.init section of the kernel image.

Without making any assumption on the LSM ordering based on how they are
compiled, enforce that ordering at LSM infrastructure level.

Signed-off-by: Roberto Sassu <[email protected]>
---
security/security.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/security/security.c b/security/security.c
index 351a124b771c..b98db79ca500 100644
--- a/security/security.c
+++ b/security/security.c
@@ -263,6 +263,18 @@ static void __init initialize_lsm(struct lsm_info *lsm)
}
}

+/* Find an LSM with a given name. */
+static struct lsm_info __init *find_lsm(const char *name)
+{
+ struct lsm_info *lsm;
+
+ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++)
+ if (!strcmp(lsm->name, name))
+ return lsm;
+
+ return NULL;
+}
+
/*
* Current index to use while initializing the lsm id list.
*/
@@ -333,10 +345,23 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)

/* LSM_ORDER_LAST is always last. */
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+ /* Do it later, to enforce the expected ordering. */
+ if (!strcmp(lsm->name, "ima") || !strcmp(lsm->name, "evm"))
+ continue;
+
if (lsm->order == LSM_ORDER_LAST)
append_ordered_lsm(lsm, " last");
}

+ /* Ensure that the 'ima' and 'evm' LSMs are last and in this order. */
+ lsm = find_lsm("ima");
+ if (lsm)
+ append_ordered_lsm(lsm, " last");
+
+ lsm = find_lsm("evm");
+ if (lsm)
+ append_ordered_lsm(lsm, " last");
+
/* Disable all LSMs not in the ordered list. */
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
if (exists_ordered_lsm(lsm))
--
2.34.1

2023-11-21 00:51:15

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v6 25/25] security: Enforce ordering of 'ima' and 'evm' LSMs

On 11/20/2023 9:33 AM, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> The ordering of LSM_ORDER_LAST LSMs depends on how they are placed in the
> .lsm_info.init section of the kernel image.
>
> Without making any assumption on the LSM ordering based on how they are
> compiled, enforce that ordering at LSM infrastructure level.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> ---
> security/security.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/security/security.c b/security/security.c
> index 351a124b771c..b98db79ca500 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -263,6 +263,18 @@ static void __init initialize_lsm(struct lsm_info *lsm)
> }
> }
>
> +/* Find an LSM with a given name. */
> +static struct lsm_info __init *find_lsm(const char *name)
> +{
> + struct lsm_info *lsm;
> +
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++)
> + if (!strcmp(lsm->name, name))
> + return lsm;
> +
> + return NULL;
> +}
> +
> /*
> * Current index to use while initializing the lsm id list.
> */
> @@ -333,10 +345,23 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
>
> /* LSM_ORDER_LAST is always last. */
> for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + /* Do it later, to enforce the expected ordering. */
> + if (!strcmp(lsm->name, "ima") || !strcmp(lsm->name, "evm"))
> + continue;
> +

Hard coding the ordering of LSMs is incredibly ugly and unlikely to scale.
Not to mention perplexing the next time someone creates an LSM that "has to be last".

Why isn't LSM_ORDER_LAST sufficient? If it really isn't, how about adding
and using LSM_ORDER_LAST_I_REALLY_MEAN_IT* ?

Alternatively, a declaration of ordering requirements with regard to other
LSMs in lsm_info. You probably don't care where ima is relative to Yama,
but you need to be after SELinux and before evm. lsm_info could have
must_precede and must_follow lists. Maybe a must_not_combine list, too,
although I'm hoping to make that unnecessary.

And you should be using LSM_ID values instead of LSM names.

---
* Naming subject to Paul's sensibilities, of course.

> if (lsm->order == LSM_ORDER_LAST)
> append_ordered_lsm(lsm, " last");
> }
>
> + /* Ensure that the 'ima' and 'evm' LSMs are last and in this order. */
> + lsm = find_lsm("ima");
> + if (lsm)
> + append_ordered_lsm(lsm, " last");
> +
> + lsm = find_lsm("evm");
> + if (lsm)
> + append_ordered_lsm(lsm, " last");
> +
> /* Disable all LSMs not in the ordered list. */
> for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> if (exists_ordered_lsm(lsm))

2023-11-21 07:58:53

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v6 25/25] security: Enforce ordering of 'ima' and 'evm' LSMs

On Mon, 2023-11-20 at 16:50 -0800, Casey Schaufler wrote:
> On 11/20/2023 9:33 AM, Roberto Sassu wrote:
> > From: Roberto Sassu <[email protected]>
> >
> > The ordering of LSM_ORDER_LAST LSMs depends on how they are placed in the
> > .lsm_info.init section of the kernel image.
> >
> > Without making any assumption on the LSM ordering based on how they are
> > compiled, enforce that ordering at LSM infrastructure level.
> >
> > Signed-off-by: Roberto Sassu <[email protected]>
> > ---
> > security/security.c | 25 +++++++++++++++++++++++++
> > 1 file changed, 25 insertions(+)
> >
> > diff --git a/security/security.c b/security/security.c
> > index 351a124b771c..b98db79ca500 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -263,6 +263,18 @@ static void __init initialize_lsm(struct lsm_info *lsm)
> > }
> > }
> >
> > +/* Find an LSM with a given name. */
> > +static struct lsm_info __init *find_lsm(const char *name)
> > +{
> > + struct lsm_info *lsm;
> > +
> > + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++)
> > + if (!strcmp(lsm->name, name))
> > + return lsm;
> > +
> > + return NULL;
> > +}
> > +
> > /*
> > * Current index to use while initializing the lsm id list.
> > */
> > @@ -333,10 +345,23 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> >
> > /* LSM_ORDER_LAST is always last. */
> > for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> > + /* Do it later, to enforce the expected ordering. */
> > + if (!strcmp(lsm->name, "ima") || !strcmp(lsm->name, "evm"))
> > + continue;
> > +
>
> Hard coding the ordering of LSMs is incredibly ugly and unlikely to scale.
> Not to mention perplexing the next time someone creates an LSM that "has to be last".

Uhm, yes, not the best solution.

> Why isn't LSM_ORDER_LAST sufficient? If it really isn't, how about adding
> and using LSM_ORDER_LAST_I_REALLY_MEAN_IT* ?

I don't know if the order at run-time reflects the order in the
Makefile (EVM is compiled after IMA). If it does, there is no need for
this patch.

> Alternatively, a declaration of ordering requirements with regard to other
> LSMs in lsm_info. You probably don't care where ima is relative to Yama,
> but you need to be after SELinux and before evm. lsm_info could have
> must_precede and must_follow lists. Maybe a must_not_combine list, too,
> although I'm hoping to make that unnecessary.

Uhm, I agree. Will think about how to make it more straightforward.

> And you should be using LSM_ID values instead of LSM names.

Ok.

Thanks

Roberto

> ---
> * Naming subject to Paul's sensibilities, of course.
>
> > if (lsm->order == LSM_ORDER_LAST)
> > append_ordered_lsm(lsm, " last");
> > }
> >
> > + /* Ensure that the 'ima' and 'evm' LSMs are last and in this order. */
> > + lsm = find_lsm("ima");
> > + if (lsm)
> > + append_ordered_lsm(lsm, " last");
> > +
> > + lsm = find_lsm("evm");
> > + if (lsm)
> > + append_ordered_lsm(lsm, " last");
> > +
> > /* Disable all LSMs not in the ordered list. */
> > for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> > if (exists_ordered_lsm(lsm))


2023-11-21 08:03:39

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v6 19/25] ima: Move to LSM infrastructure

On Mon, 2023-11-20 at 18:33 +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').
>
> 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(). Conditionally register
> ima_post_path_mknod() if CONFIG_SECURITY_PATH is enabled, otherwise the
> path_post_mknod hook won't be available.
>
> Move integrity_kernel_module_request() to IMA (renamed to
> ima_kernel_module_request()), and conditionally register it as
> implementation of the kernel_module_request LSM hook (if
> CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled).
>
> Define the 'ima' LSM, and initialize it with init_ima_lsm(). Consequently,
> assign the LSM_ID_IMA ID to IMA in include/uapi/linux/lsm.h.
>
> Still rely on the existing 'integrity' subsystem to be enabled and to
> manage integrity metadata.
>
> 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 | 94 ---------------------
> include/linux/integrity.h | 13 ---
> include/uapi/linux/lsm.h | 1 +
> security/integrity/digsig_asymmetric.c | 22 -----
> security/integrity/iint.c | 2 +-
> security/integrity/ima/ima.h | 6 ++
> security/integrity/ima/ima_main.c | 108 ++++++++++++++++++++-----
> security/integrity/integrity.h | 1 +
> security/keys/key.c | 9 +--
> security/security.c | 63 +++------------
> 14 files changed, 110 insertions(+), 225 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 31ef6c3c3207..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,
> 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/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
> index f0386880a78e..ee7d034255a9 100644
> --- a/include/uapi/linux/lsm.h
> +++ b/include/uapi/linux/lsm.h
> @@ -61,6 +61,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/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
> index 895f4b9ce8c6..4d11c622fabd 100644
> --- a/security/integrity/digsig_asymmetric.c
> +++ b/security/integrity/digsig_asymmetric.c
> @@ -133,25 +133,3 @@ int asymmetric_verify(struct key *keyring, const char *sig,
> 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/iint.c b/security/integrity/iint.c
> index d4419a2a1e24..6cbf2aa5540e 100644
> --- a/security/integrity/iint.c
> +++ b/security/integrity/iint.c
> @@ -200,13 +200,13 @@ static int __init integrity_iintcache_init(void)
> 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/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 02021ee467d3..af213bece9b8 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,8 @@ 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 __maybe_unused
> +ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> {
> struct integrity_iint_cache *iint;
> struct inode *inode = dentry->d_inode;
> @@ -753,8 +753,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 +803,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 +837,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 +891,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) &&
> @@ -1122,4 +1122,72 @@ static int __init init_ima(void)
> return error;
> }
>
> +/**
> + * 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.
> + */
> +static int __maybe_unused ima_kernel_module_request(char *kmod_name)
> +{
> + if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +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),
> +#ifdef CONFIG_SECURITY_PATH
> + LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
> +#endif
> +#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,
> +};
> +
> +/*
> + * Since with the LSM_ORDER_LAST there is no guarantee about the ordering
> + * within the .lsm_info.init section, ensure that IMA hooks are before EVM
> + * ones, by letting the 'integrity' LSM call init_ima_lsm() to initialize the
> + * 'ima' and 'evm' LSMs in this sequence.
> + */

Sorry, leftover from the previous version (also for EVM).

> +int __init init_ima_lsm(void)

Will make this as static (reported by kernel robot).

Roberto

> +{
> + 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 423d53092604..e18953ee4a97 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);
> }
>
> /**
> @@ -2883,13 +2879,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);
> }
>
> /**
> @@ -2918,12 +2909,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);
> }
>
> /**
> @@ -3232,12 +3218,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 integrity_kernel_module_request(kmod_name);
> + return call_int_hook(kernel_module_request, 0, kmod_name);
> }
>
> /**
> @@ -3253,12 +3234,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);
>
> @@ -3278,12 +3254,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);
>
> @@ -3298,12 +3269,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);
>
> @@ -3325,13 +3291,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);
>