2023-12-14 17:09:32

by Roberto Sassu

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

From: Roberto Sassu <[email protected]>

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

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

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

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

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

In addition to splitting metadata, it was decided to embed the full
structure into the inode security blob, rather than using a cache of
objects and allocating them on demand. This opens for new possibilities,
such as improving locking in IMA.

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

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

The patch set is organized as follows.

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

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

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.

The patch set applies on top of lsm/dev, commit 80b4ff1d2c9b ("selftests:
remove the LSM_ID_IMA check in lsm/lsm_list_modules_test"). The
linux-integrity/next-integrity-testing at commit f17167bea279 ("ima: Remove
EXPERIMENTAL from Kconfig") was merged.

Changelog:

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

v6:
- See v7

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

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

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

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

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

Roberto Sassu (24):
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
evm: Make it independent from 'integrity' LSM
ima: Make it independent from 'integrity' LSM
integrity: Remove LSM

fs/attr.c | 5 +-
fs/file_table.c | 3 +-
fs/namei.c | 12 +-
fs/nfsd/vfs.c | 3 +-
fs/open.c | 1 -
fs/posix_acl.c | 5 +-
fs/xattr.c | 9 +-
include/linux/evm.h | 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/Makefile | 1 +
security/integrity/digsig_asymmetric.c | 23 --
security/integrity/evm/evm.h | 19 ++
security/integrity/evm/evm_crypto.c | 4 +-
security/integrity/evm/evm_main.c | 195 ++++++++++---
security/integrity/iint.c | 197 +------------
security/integrity/ima/ima.h | 120 +++++++-
security/integrity/ima/ima_api.c | 15 +-
security/integrity/ima/ima_appraise.c | 64 +++--
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 201 +++++++++++---
security/integrity/ima/ima_policy.c | 2 +-
security/integrity/integrity.h | 80 +-----
security/keys/key.c | 10 +-
security/security.c | 261 +++++++++++-------
security/selinux/hooks.c | 3 +-
security/smack/smack_lsm.c | 4 +-
.../selftests/lsm/lsm_list_modules_test.c | 6 +
32 files changed, 783 insertions(+), 825 deletions(-)

--
2.34.1



2023-12-14 17:09:43

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 01/24] 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-12-14 17:10:00

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 02/24] 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-12-14 17:10:41

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 05/24] 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-12-14 17:11:02

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 07/24] 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-12-14 17:11:09

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 04/24] 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-12-14 17:11:20

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 08/24] 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-12-14 17:12:45

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 10/24] security: Introduce inode_post_setattr hook

From: Roberto Sassu <[email protected]>

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

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

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

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

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

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

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

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

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


2023-12-14 17:13:19

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 12/24] security: Introduce file_post_open hook

From: Roberto Sassu <[email protected]>

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

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

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

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

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

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

+ host_err = security_file_post_open(file, may_flags);
+ if (host_err) {
+ fput(file);
+ goto out;
+ }
+
host_err = ima_file_check(file, may_flags);
if (host_err) {
fput(file);
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index c3199bb69103..e2b45fee94e2 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -189,6 +189,7 @@ LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk,
struct fown_struct *fown, int sig)
LSM_HOOK(int, 0, file_receive, struct file *file)
LSM_HOOK(int, 0, file_open, struct file *file)
+LSM_HOOK(int, 0, file_post_open, struct file *file, int mask)
LSM_HOOK(int, 0, file_truncate, struct file *file)
LSM_HOOK(int, 0, task_alloc, struct task_struct *task,
unsigned long clone_flags)
diff --git a/include/linux/security.h b/include/linux/security.h
index 922ea7709bae..c360458920b1 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -409,6 +409,7 @@ int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig);
int security_file_receive(struct file *file);
int security_file_open(struct file *file);
+int security_file_post_open(struct file *file, int mask);
int security_file_truncate(struct file *file);
int security_task_alloc(struct task_struct *task, unsigned long clone_flags);
void security_task_free(struct task_struct *task);
@@ -1065,6 +1066,11 @@ static inline int security_file_open(struct file *file)
return 0;
}

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

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


2023-12-14 17:13:39

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 14/24] security: Introduce path_post_mknod hook

From: Roberto Sassu <[email protected]>

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

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

LSMs could also take some action after files are created.

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

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

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

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

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


2023-12-14 17:13:49

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 15/24] 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-12-14 17:14:04

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 16/24] 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-12-14 17:14:15

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 17/24] 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-12-14 17:14:57

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 19/24] ima: Move to LSM infrastructure

From: Roberto Sassu <[email protected]>

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

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

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

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

Conditionally register ima_post_path_mknod() if CONFIG_SECURITY_PATH is
enabled, otherwise the path_post_mknod hook won't be available.

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

Move integrity_kernel_module_request() to IMA and name it
ima_kernel_module_request(), as only appraisal is affected by the crypto
subsystem trying to load kernel modules. Conditionally register
ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is
enabled.

Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.

Signed-off-by: Roberto Sassu <[email protected]>
Acked-by: Chuck Lever <[email protected]>
---
fs/file_table.c | 2 -
fs/namei.c | 6 --
fs/nfsd/vfs.c | 7 --
fs/open.c | 1 -
include/linux/ima.h | 94 ----------------
include/linux/integrity.h | 13 ---
include/uapi/linux/lsm.h | 1 +
security/integrity/Makefile | 1 +
security/integrity/digsig_asymmetric.c | 23 ----
security/integrity/ima/ima.h | 6 ++
security/integrity/ima/ima_main.c | 102 ++++++++++++++----
security/integrity/integrity.h | 1 +
security/keys/key.c | 9 +-
security/security.c | 63 +++--------
.../selftests/lsm/lsm_list_modules_test.c | 3 +
15 files changed, 107 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/Makefile b/security/integrity/Makefile
index d0ffe37dc1d6..92b63039c654 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_powerpc.o \
platform_certs/keyring_handler.o
+# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 895f4b9ce8c6..de603cf42ac7 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -132,26 +132,3 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
-
-/**
- * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests
- * @kmod_name: kernel module name
- *
- * We have situation, when public_key_verify_signature() in case of RSA
- * algorithm use alg_name to store internal information in order to
- * construct an algorithm on the fly, but crypto_larval_lookup() will try
- * to use alg_name in order to load kernel module with same name.
- * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- * we are safe to fail such module request from crypto_larval_lookup().
- *
- * In this way we prevent modprobe execution during digsig verification
- * and avoid possible deadlock if modprobe and/or it's dependencies
- * also signed with digsig.
- */
-int integrity_kernel_module_request(char *kmod_name)
-{
- if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
- return -EINVAL;
-
- return 0;
-}
diff --git a/security/integrity/ima/ima.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..fa6bfe9155ba 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,66 @@ 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,
+};
+
+static int __init init_ima_lsm(void)
+{
+ security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
+ return 0;
+}
+
+DEFINE_LSM(ima) = {
+ .name = "ima",
+ .init = init_ima_lsm,
+ .order = LSM_ORDER_LAST,
+};
+
late_initcall(init_ima); /* Start IMA after the TPM is available */
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 9561db7cf6b4..59eaddd84434 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -18,6 +18,7 @@
#include <crypto/hash.h>
#include <linux/key.h>
#include <linux/audit.h>
+#include <linux/lsm_hooks.h>

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

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

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

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

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

key_ref = __key_update(key_ref, &prep);

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

goto error_free_prep;
}
diff --git a/security/security.c b/security/security.c
index 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);

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


2023-12-14 17:15:54

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 18/24] 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-12-14 17:16:05

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 20/24] ima: Move IMA-Appraisal to LSM infrastructure

From: Roberto Sassu <[email protected]>

Do the registration of IMA-Appraisal only functions separately from the
rest of IMA functions, as appraisal is a separate feature not necessarily
enabled in the kernel configuration.

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

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

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

#include "internal.h"

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

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

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

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

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

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

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

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

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

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

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

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

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


2023-12-14 17:16:14

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 21/24] evm: Move to LSM infrastructure

From: Roberto Sassu <[email protected]>

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

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

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

Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c

Signed-off-by: Roberto Sassu <[email protected]>
---
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 | 115 +++++++++++++++---
security/security.c | 41 ++-----
.../selftests/lsm/lsm_list_modules_test.c | 3 +
8 files changed, 113 insertions(+), 161 deletions(-)

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

#include "internal.h"

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

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

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

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

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

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

out:
return error;
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 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..0cd014bfc093 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;
@@ -964,4 +1018,35 @@ 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),
+};
+
+static const struct lsm_id evm_lsmid = {
+ .name = "evm",
+ .id = LSM_ID_EVM,
+};
+
+static int __init init_evm_lsm(void)
+{
+ security_add_hooks(evm_hooks, ARRAY_SIZE(evm_hooks), &evm_lsmid);
+ return 0;
+}
+
+DEFINE_LSM(evm) = {
+ .name = "evm",
+ .init = init_evm_lsm,
+ .order = LSM_ORDER_LAST,
+};
+
late_initcall(init_evm);
diff --git a/security/security.c b/security/security.c
index d4ead59fb91f..18a70aa707ad 100644
--- a/security/security.c
+++ b/security/security.c
@@ -20,13 +20,13 @@
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
#include <linux/integrity.h>
-#include <linux/evm.h>
#include <linux/fsnotify.h>
#include <linux/mman.h>
#include <linux/mount.h>
#include <linux/personality.h>
#include <linux/backing-dev.h>
#include <linux/string.h>
+#include <linux/xattr.h>
#include <linux/msg.h>
#include <net/flow.h>

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

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

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

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

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

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

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

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

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

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


2023-12-14 17:17:00

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 22/24] evm: Make it independent from 'integrity' LSM

From: Roberto Sassu <[email protected]>

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

Keep the non-NULL checks after calling evm_iint_inode() except in
evm_inode_alloc_security(), to take into account inodes for which
security_inode_alloc() was not called. When using shared metadata,
obtaining a NULL pointer from integrity_iint_find() meant that the file
wasn't processed by IMA.

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

Replicate the management of the IMA_NEW_FILE flag (now EVM_NEW_FILE), by
introducing evm_post_path_mknod() and evm_file_free() to respectively set
and clear the new flag at the same time IMA does. A noteworthy difference
is that evm_post_path_mknod() cannot check if a file must be appraised.
Also, since IMA_NEW_FILE is always cleared in ima_check_last_writer() if it
is set, it is not necessary to maintain an inode version in EVM to
replicate the IMA logic (the inode version check is in OR).

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

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

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

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

-struct integrity_iint_cache;
-
#ifdef CONFIG_EVM
extern int evm_set_key(void *key, size_t keylen);
extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value,
- size_t xattr_value_len,
- struct integrity_iint_cache *iint);
+ size_t xattr_value_len);
int evm_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, struct xattr *xattrs,
int *xattr_count);
@@ -48,8 +45,7 @@ static inline int evm_set_key(void *key, size_t keylen)
static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
void *xattr_value,
- size_t xattr_value_len,
- struct integrity_iint_cache *iint)
+ size_t xattr_value_len)
{
return INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 53bd7fec93fa..eb1a2c343bd7 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -32,6 +32,25 @@ struct xattr_list {
bool enabled;
};

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

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

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

diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0cd014bfc093..e3a0dd7fae10 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -167,14 +167,14 @@ 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 inode *inode = d_backing_inode(dentry);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
int rc, xattr_len, evm_immutable = 0;

if (iint && (iint->evm_status == INTEGRITY_PASS ||
@@ -240,8 +240,6 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
(const char *)xattr_data, xattr_len,
digest.digest, digest.hdr.length);
if (!rc) {
- inode = d_backing_inode(dentry);
-
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
if (iint)
iint->flags |= EVM_IMMUTABLE_DIGSIG;
@@ -389,7 +387,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 +399,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_iint_find(d_backing_inode(dentry));
- if (!iint)
- return INTEGRITY_UNKNOWN;
- }
return evm_verify_hmac(dentry, xattr_name, xattr_value,
- xattr_value_len, iint);
+ xattr_value_len);
}
EXPORT_SYMBOL_GPL(evm_verifyxattr);

@@ -431,7 +422,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 +494,14 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,

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

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

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

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

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

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

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

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

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

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

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

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


2023-12-14 17:17:41

by Roberto Sassu

[permalink] [raw]
Subject: [PATCH v8 23/24] ima: Make it independent from 'integrity' LSM

From: Roberto Sassu <[email protected]>

Make the 'ima' LSM independent from 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.

Move the remaining IMA-specific flags to security/integrity/ima/ima.h,
since they are now unnecessary in the common integrity layer.

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 it is available for all inodes having the security blob
allocated (those for which security_inode_alloc() was called). Adjust the
IMA code accordingly, call ima_iint_inode() to retrieve the ima_iint_cache
structure. Keep the non-NULL checks since there can be inodes without
security blob.

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().

Finally, register ima_inode_alloc_security/ima_inode_free_security() to
initialize/deinitialize the new ima_iint_cache structure (before this task
was done by iint_init_always() and iint_free()). 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/ima/ima.h | 109 ++++++++++++++++++++++----
security/integrity/ima/ima_api.c | 15 ++--
security/integrity/ima/ima_appraise.c | 25 +++---
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 96 ++++++++++++++++++-----
security/integrity/ima/ima_policy.c | 2 +-
security/integrity/integrity.h | 53 -------------
7 files changed, 194 insertions(+), 108 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index a27fc10f84f7..d1c339a340f8 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,86 @@ struct ima_kexec_hdr {
u64 count;
};

+/* IMA iint action cache flags */
+#define IMA_MEASURE 0x00000001
+#define IMA_MEASURED 0x00000002
+#define IMA_APPRAISE 0x00000004
+#define IMA_APPRAISED 0x00000008
+/*#define IMA_COLLECT 0x00000010 do not use this flag */
+#define IMA_COLLECTED 0x00000020
+#define IMA_AUDIT 0x00000040
+#define IMA_AUDITED 0x00000080
+#define IMA_HASH 0x00000100
+#define IMA_HASHED 0x00000200
+
+/* IMA iint policy rule cache flags */
+#define IMA_NONACTION_FLAGS 0xff000000
+#define IMA_DIGSIG_REQUIRED 0x01000000
+#define IMA_PERMIT_DIRECTIO 0x02000000
+#define IMA_NEW_FILE 0x04000000
+#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
+#define IMA_MODSIG_ALLOWED 0x20000000
+#define IMA_CHECK_BLACKLIST 0x40000000
+#define IMA_VERITY_REQUIRED 0x80000000
+
+#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
+ IMA_HASH | IMA_APPRAISE_SUBMASK)
+#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
+ IMA_HASHED | IMA_COLLECTED | \
+ IMA_APPRAISED_SUBMASK)
+
+/* IMA iint subaction appraise cache flags */
+#define IMA_FILE_APPRAISE 0x00001000
+#define IMA_FILE_APPRAISED 0x00002000
+#define IMA_MMAP_APPRAISE 0x00004000
+#define IMA_MMAP_APPRAISED 0x00008000
+#define IMA_BPRM_APPRAISE 0x00010000
+#define IMA_BPRM_APPRAISED 0x00020000
+#define IMA_READ_APPRAISE 0x00040000
+#define IMA_READ_APPRAISED 0x00080000
+#define IMA_CREDS_APPRAISE 0x00100000
+#define IMA_CREDS_APPRAISED 0x00200000
+#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
+ IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
+ IMA_CREDS_APPRAISE)
+#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
+ IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
+ IMA_CREDS_APPRAISED)
+
+/* IMA iint cache atomic_flags */
+#define IMA_CHANGE_XATTR 0
+#define IMA_UPDATE_XATTR 1
+#define IMA_CHANGE_ATTR 2
+#define IMA_DIGSIG 3
+#define IMA_MUST_MEASURE 4
+
+/* 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_iint_inode(const struct inode *inode)
+{
+ if (unlikely(!inode->i_security))
+ return NULL;
+
+ return inode->i_security + ima_blob_sizes.lbs_inode;
+}
+
extern const int read_idmap[];

#ifdef CONFIG_HAVE_IMA_KEXEC
@@ -152,7 +232,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 +347,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 +360,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 +398,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 +417,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 +441,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 1dd6ee72a20a..d71df7deacb7 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)
@@ -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;
int action;

if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
@@ -648,7 +647,7 @@ 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);
+ iint = ima_iint_inode(inode);
if (iint) {
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
if (!action)
@@ -674,12 +673,12 @@ 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;

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

- iint = integrity_iint_find(inode);
+ iint = ima_iint_inode(inode);
if (!iint)
return;
iint->measured_pcrs = 0;
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 2a9ca5fa4317..b28e49f53ca4 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,9 +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 (atomic_read(&inode->i_readcount)) {
if (!iint)
- iint = integrity_iint_find(inode);
+ iint = ima_iint_inode(inode);
/* IMA_MEASURE is set from reader side */
if (iint && test_bit(IMA_MUST_MEASURE,
&iint->atomic_flags))
@@ -153,7 +153,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,12 +192,12 @@ 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;

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

- iint = integrity_iint_find(inode);
+ iint = ima_iint_inode(inode);
if (!iint)
return;

@@ -209,7 +209,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;
struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
@@ -248,7 +248,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
inode_lock(inode);

if (action) {
- iint = integrity_inode_get(inode);
+ iint = ima_iint_inode(inode);
if (!iint)
rc = -ENOMEM;
}
@@ -564,11 +564,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, tmp_iint;
int rc, hash_algo;

if (ima_policy_flag) {
- iint = integrity_iint_find(inode);
+ iint = ima_iint_inode(inode);
if (iint)
mutex_lock(&iint->mutex);
}
@@ -578,7 +578,6 @@ static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
mutex_unlock(&iint->mutex);

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,
@@ -688,7 +687,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode)

{
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
int must_appraise;

if (!ima_policy_flag || !S_ISREG(inode->i_mode))
@@ -699,8 +698,8 @@ 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);
+ iint = ima_iint_inode(inode);
+ /* iint is NULL if security_inode_alloc() was not called on inode. */
if (!iint)
return;

@@ -720,8 +719,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_iint_inode(inode);
int must_appraise;

if (!ima_policy_flag || !S_ISREG(inode->i_mode))
@@ -732,8 +731,8 @@ 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);
+ iint = ima_iint_inode(inode);
+ /* iint is NULL if security_inode_alloc() was not called on inode. */
if (!iint)
return;

@@ -936,7 +935,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 +1144,60 @@ 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_iint_inode(inode);
+
+ /* Called by security_inode_alloc(), it cannot be NULL. */
+ 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 void ima_inode_free_security(struct inode *inode)
+{
+ struct ima_iint_cache *iint = ima_iint_inode(inode);
+
+ if (!iint)
+ return;
+
+ kfree(iint->ima_hash);
+ mutex_destroy(&iint->mutex);
+}
+
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 +1209,8 @@ 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),
+ LSM_HOOK_INIT(inode_free_security, ima_inode_free_security),
#ifdef CONFIG_SECURITY_PATH
LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
#endif
@@ -1179,10 +1234,15 @@ static 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;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 7a97c269a072..671fc50255f9 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -20,59 +20,6 @@
#include <linux/audit.h>
#include <linux/lsm_hooks.h>

-/* iint action cache flags */
-#define IMA_MEASURE 0x00000001
-#define IMA_MEASURED 0x00000002
-#define IMA_APPRAISE 0x00000004
-#define IMA_APPRAISED 0x00000008
-/*#define IMA_COLLECT 0x00000010 do not use this flag */
-#define IMA_COLLECTED 0x00000020
-#define IMA_AUDIT 0x00000040
-#define IMA_AUDITED 0x00000080
-#define IMA_HASH 0x00000100
-#define IMA_HASHED 0x00000200
-
-/* iint policy rule cache flags */
-#define IMA_NONACTION_FLAGS 0xff000000
-#define IMA_DIGSIG_REQUIRED 0x01000000
-#define IMA_PERMIT_DIRECTIO 0x02000000
-#define IMA_NEW_FILE 0x04000000
-#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
-#define IMA_MODSIG_ALLOWED 0x20000000
-#define IMA_CHECK_BLACKLIST 0x40000000
-#define IMA_VERITY_REQUIRED 0x80000000
-
-#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
- IMA_HASH | IMA_APPRAISE_SUBMASK)
-#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
- IMA_HASHED | IMA_COLLECTED | \
- IMA_APPRAISED_SUBMASK)
-
-/* iint subaction appraise cache flags */
-#define IMA_FILE_APPRAISE 0x00001000
-#define IMA_FILE_APPRAISED 0x00002000
-#define IMA_MMAP_APPRAISE 0x00004000
-#define IMA_MMAP_APPRAISED 0x00008000
-#define IMA_BPRM_APPRAISE 0x00010000
-#define IMA_BPRM_APPRAISED 0x00020000
-#define IMA_READ_APPRAISE 0x00040000
-#define IMA_READ_APPRAISED 0x00080000
-#define IMA_CREDS_APPRAISE 0x00100000
-#define IMA_CREDS_APPRAISED 0x00200000
-#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
- IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
- IMA_CREDS_APPRAISE)
-#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
- IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
- IMA_CREDS_APPRAISED)
-
-/* iint cache atomic_flags */
-#define IMA_CHANGE_XATTR 0
-#define IMA_UPDATE_XATTR 1
-#define IMA_CHANGE_ATTR 2
-#define IMA_DIGSIG 3
-#define IMA_MUST_MEASURE 4
-
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
EVM_XATTR_HMAC,
--
2.34.1


2023-12-15 22:07:26

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v8 21/24] evm: Move to LSM infrastructure

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

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

> ---
> fs/attr.c | 2 -
> fs/posix_acl.c | 3 -
> fs/xattr.c | 2 -
> include/linux/evm.h | 107 ----------------
> include/uapi/linux/lsm.h | 1 +
> security/integrity/evm/evm_main.c | 115 +++++++++++++++---
> security/security.c | 41 ++-----
> .../selftests/lsm/lsm_list_modules_test.c | 3 +
> 8 files changed, 113 insertions(+), 161 deletions(-)
>
> diff --git a/fs/attr.c b/fs/attr.c
> index 38841f3ebbcb..b51bd7c9b4a7 100644
> --- a/fs/attr.c
> +++ b/fs/attr.c
> @@ -16,7 +16,6 @@
> #include <linux/fcntl.h>
> #include <linux/filelock.h>
> #include <linux/security.h>
> -#include <linux/evm.h>
>
> #include "internal.h"
>
> @@ -502,7 +501,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
> if (!error) {
> fsnotify_change(dentry, ia_valid);
> security_inode_post_setattr(idmap, dentry, ia_valid);
> - evm_inode_post_setattr(idmap, dentry, ia_valid);
> }
>
> return error;
> diff --git a/fs/posix_acl.c b/fs/posix_acl.c
> index e3fbe1a9f3f5..ae67479cd2b6 100644
> --- a/fs/posix_acl.c
> +++ b/fs/posix_acl.c
> @@ -26,7 +26,6 @@
> #include <linux/mnt_idmapping.h>
> #include <linux/iversion.h>
> #include <linux/security.h>
> -#include <linux/evm.h>
> #include <linux/fsnotify.h>
> #include <linux/filelock.h>
>
> @@ -1138,7 +1137,6 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> if (!error) {
> fsnotify_xattr(dentry);
> security_inode_post_set_acl(dentry, acl_name, kacl);
> - evm_inode_post_set_acl(dentry, acl_name, kacl);
> }
>
> out_inode_unlock:
> @@ -1247,7 +1245,6 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> if (!error) {
> fsnotify_xattr(dentry);
> security_inode_post_remove_acl(idmap, dentry, acl_name);
> - evm_inode_post_remove_acl(idmap, dentry, acl_name);
> }
>
> out_inode_unlock:
> diff --git a/fs/xattr.c b/fs/xattr.c
> index f891c260a971..f8b643f91a98 100644
> --- a/fs/xattr.c
> +++ b/fs/xattr.c
> @@ -16,7 +16,6 @@
> #include <linux/mount.h>
> #include <linux/namei.h>
> #include <linux/security.h>
> -#include <linux/evm.h>
> #include <linux/syscalls.h>
> #include <linux/export.h>
> #include <linux/fsnotify.h>
> @@ -557,7 +556,6 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,
>
> fsnotify_xattr(dentry);
> security_inode_post_removexattr(dentry, name);
> - evm_inode_post_removexattr(dentry, name);
>
> out:
> return error;
> diff --git a/include/linux/evm.h b/include/linux/evm.h
> index 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..0cd014bfc093 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;
> @@ -964,4 +1018,35 @@ 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),
> +};
> +
> +static const struct lsm_id evm_lsmid = {
> + .name = "evm",
> + .id = LSM_ID_EVM,
> +};
> +
> +static int __init init_evm_lsm(void)
> +{
> + security_add_hooks(evm_hooks, ARRAY_SIZE(evm_hooks), &evm_lsmid);
> + return 0;
> +}
> +
> +DEFINE_LSM(evm) = {
> + .name = "evm",
> + .init = init_evm_lsm,
> + .order = LSM_ORDER_LAST,
> +};
> +
> late_initcall(init_evm);
> diff --git a/security/security.c b/security/security.c
> index d4ead59fb91f..18a70aa707ad 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -20,13 +20,13 @@
> #include <linux/kernel_read_file.h>
> #include <linux/lsm_hooks.h>
> #include <linux/integrity.h>
> -#include <linux/evm.h>
> #include <linux/fsnotify.h>
> #include <linux/mman.h>
> #include <linux/mount.h>
> #include <linux/personality.h>
> #include <linux/backing-dev.h>
> #include <linux/string.h>
> +#include <linux/xattr.h>
> #include <linux/msg.h>
> #include <net/flow.h>
>
> @@ -50,7 +50,8 @@
> (IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \
> (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \
> (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \
> - (IS_ENABLED(CONFIG_IMA) ? 1 : 0))
> + (IS_ENABLED(CONFIG_IMA) ? 1 : 0) + \
> + (IS_ENABLED(CONFIG_EVM) ? 1 : 0))
>
> /*
> * These are descriptions of the reasons that can be passed to the
> @@ -1740,10 +1741,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
> if (!xattr_count)
> goto out;
>
> - ret = evm_inode_init_security(inode, dir, qstr, new_xattrs,
> - &xattr_count);
> - if (ret)
> - goto out;
> ret = initxattrs(inode, new_xattrs, fs_data);
> out:
> for (; xattr_count > 0; xattr_count--)
> @@ -2235,14 +2232,9 @@ int security_inode_permission(struct inode *inode, int mask)
> int security_inode_setattr(struct mnt_idmap *idmap,
> struct dentry *dentry, struct iattr *attr)
> {
> - int ret;
> -
> if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> return 0;
> - ret = call_int_hook(inode_setattr, 0, idmap, dentry, attr);
> - if (ret)
> - return ret;
> - return evm_inode_setattr(idmap, dentry, attr);
> + return call_int_hook(inode_setattr, 0, idmap, dentry, attr);
> }
> EXPORT_SYMBOL_GPL(security_inode_setattr);
>
> @@ -2307,9 +2299,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
>
> if (ret == 1)
> ret = cap_inode_setxattr(dentry, name, value, size, flags);
> - if (ret)
> - return ret;
> - return evm_inode_setxattr(idmap, dentry, name, value, size, flags);
> + return ret;
> }
>
> /**
> @@ -2328,15 +2318,10 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> - int ret;
> -
> if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> return 0;
> - ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
> - kacl);
> - if (ret)
> - return ret;
> - return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
> + return call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
> + kacl);
> }
>
> /**
> @@ -2389,14 +2374,9 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
> int security_inode_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - int ret;
> -
> if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> return 0;
> - ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
> - if (ret)
> - return ret;
> - return evm_inode_remove_acl(idmap, dentry, acl_name);
> + return call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
> }
>
> /**
> @@ -2432,7 +2412,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
> if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> return;
> call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
> - evm_inode_post_setxattr(dentry, name, value, size, flags);
> }
>
> /**
> @@ -2493,9 +2472,7 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
> ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name);
> if (ret == 1)
> ret = cap_inode_removexattr(idmap, dentry, name);
> - if (ret)
> - return ret;
> - return evm_inode_removexattr(idmap, dentry, name);
> + return ret;
> }
>
> /**
> diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c
> index 17333787cb2f..4d5d4cee2586 100644
> --- a/tools/testing/selftests/lsm/lsm_list_modules_test.c
> +++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c
> @@ -125,6 +125,9 @@ TEST(correct_lsm_list_modules)
> case LSM_ID_IMA:
> name = "ima";
> break;
> + case LSM_ID_EVM:
> + name = "evm";
> + break;
> default:
> name = "INVALID";
> break;

2023-12-18 08:40:50

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v8 23/24] ima: Make it independent from 'integrity' LSM

On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Make the 'ima' LSM independent from 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.
>
> Move the remaining IMA-specific flags to security/integrity/ima/ima.h,
> since they are now unnecessary in the common integrity layer.
>
> 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 it is available for all inodes having the security blob
> allocated (those for which security_inode_alloc() was called). Adjust the
> IMA code accordingly, call ima_iint_inode() to retrieve the ima_iint_cache
> structure. Keep the non-NULL checks since there can be inodes without
> security blob.
>
> 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().
>
> Finally, register ima_inode_alloc_security/ima_inode_free_security() to
> initialize/deinitialize the new ima_iint_cache structure (before this task
> was done by iint_init_always() and iint_free()). 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/ima/ima.h | 109 ++++++++++++++++++++++----
> security/integrity/ima/ima_api.c | 15 ++--
> security/integrity/ima/ima_appraise.c | 25 +++---
> security/integrity/ima/ima_init.c | 2 +-
> security/integrity/ima/ima_main.c | 96 ++++++++++++++++++-----
> security/integrity/ima/ima_policy.c | 2 +-
> security/integrity/integrity.h | 53 -------------
> 7 files changed, 194 insertions(+), 108 deletions(-)
>
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index a27fc10f84f7..d1c339a340f8 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,86 @@ struct ima_kexec_hdr {
> u64 count;
> };
>
> +/* IMA iint action cache flags */
> +#define IMA_MEASURE 0x00000001
> +#define IMA_MEASURED 0x00000002
> +#define IMA_APPRAISE 0x00000004
> +#define IMA_APPRAISED 0x00000008
> +/*#define IMA_COLLECT 0x00000010 do not use this flag */
> +#define IMA_COLLECTED 0x00000020
> +#define IMA_AUDIT 0x00000040
> +#define IMA_AUDITED 0x00000080
> +#define IMA_HASH 0x00000100
> +#define IMA_HASHED 0x00000200
> +
> +/* IMA iint policy rule cache flags */
> +#define IMA_NONACTION_FLAGS 0xff000000
> +#define IMA_DIGSIG_REQUIRED 0x01000000
> +#define IMA_PERMIT_DIRECTIO 0x02000000
> +#define IMA_NEW_FILE 0x04000000
> +#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
> +#define IMA_MODSIG_ALLOWED 0x20000000
> +#define IMA_CHECK_BLACKLIST 0x40000000
> +#define IMA_VERITY_REQUIRED 0x80000000
> +
> +#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
> + IMA_HASH | IMA_APPRAISE_SUBMASK)
> +#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
> + IMA_HASHED | IMA_COLLECTED | \
> + IMA_APPRAISED_SUBMASK)
> +
> +/* IMA iint subaction appraise cache flags */
> +#define IMA_FILE_APPRAISE 0x00001000
> +#define IMA_FILE_APPRAISED 0x00002000
> +#define IMA_MMAP_APPRAISE 0x00004000
> +#define IMA_MMAP_APPRAISED 0x00008000
> +#define IMA_BPRM_APPRAISE 0x00010000
> +#define IMA_BPRM_APPRAISED 0x00020000
> +#define IMA_READ_APPRAISE 0x00040000
> +#define IMA_READ_APPRAISED 0x00080000
> +#define IMA_CREDS_APPRAISE 0x00100000
> +#define IMA_CREDS_APPRAISED 0x00200000
> +#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
> + IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
> + IMA_CREDS_APPRAISE)
> +#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
> + IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
> + IMA_CREDS_APPRAISED)
> +
> +/* IMA iint cache atomic_flags */
> +#define IMA_CHANGE_XATTR 0
> +#define IMA_UPDATE_XATTR 1
> +#define IMA_CHANGE_ATTR 2
> +#define IMA_DIGSIG 3
> +#define IMA_MUST_MEASURE 4
> +
> +/* 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_iint_inode(const struct inode *inode)
> +{
> + if (unlikely(!inode->i_security))
> + return NULL;
> +
> + return inode->i_security + ima_blob_sizes.lbs_inode;
> +}
> +
> extern const int read_idmap[];
>
> #ifdef CONFIG_HAVE_IMA_KEXEC
> @@ -152,7 +232,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 +347,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 +360,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 +398,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 +417,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 +441,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 1dd6ee72a20a..d71df7deacb7 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)
> @@ -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;
> int action;
>
> if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
> @@ -648,7 +647,7 @@ 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);
> + iint = ima_iint_inode(inode);
> if (iint) {
> set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
> if (!action)
> @@ -674,12 +673,12 @@ 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;
>
> if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
> return;
>
> - iint = integrity_iint_find(inode);
> + iint = ima_iint_inode(inode);
> if (!iint)
> return;
> iint->measured_pcrs = 0;
> 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 2a9ca5fa4317..b28e49f53ca4 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,9 +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 (atomic_read(&inode->i_readcount)) {
> if (!iint)
> - iint = integrity_iint_find(inode);
> + iint = ima_iint_inode(inode);
> /* IMA_MEASURE is set from reader side */
> if (iint && test_bit(IMA_MUST_MEASURE,
> &iint->atomic_flags))
> @@ -153,7 +153,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,12 +192,12 @@ 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;
>
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> return;
>
> - iint = integrity_iint_find(inode);
> + iint = ima_iint_inode(inode);
> if (!iint)
> return;
>
> @@ -209,7 +209,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;

Argh, really sorry...

Last minute change after restoring the non-NULL checks, it seems I
didn't revert all the changes completely (missing = NULL).

> struct ima_template_desc *template_desc = NULL;
> char *pathbuf = NULL;
> char filename[NAME_MAX];
> @@ -248,7 +248,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
> inode_lock(inode);
>
> if (action) {
> - iint = integrity_inode_get(inode);
> + iint = ima_iint_inode(inode);
> if (!iint)
> rc = -ENOMEM;
> }
> @@ -564,11 +564,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, tmp_iint;

Same here. Will send v9 after I get more reviews.

Thanks

Roberto

> int rc, hash_algo;
>
> if (ima_policy_flag) {
> - iint = integrity_iint_find(inode);
> + iint = ima_iint_inode(inode);
> if (iint)
> mutex_lock(&iint->mutex);
> }
> @@ -578,7 +578,6 @@ static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
> mutex_unlock(&iint->mutex);
>
> 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,
> @@ -688,7 +687,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> struct inode *inode)
>
> {
> - struct integrity_iint_cache *iint;
> + struct ima_iint_cache *iint;
> int must_appraise;
>
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> @@ -699,8 +698,8 @@ 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);
> + iint = ima_iint_inode(inode);
> + /* iint is NULL if security_inode_alloc() was not called on inode. */
> if (!iint)
> return;
>
> @@ -720,8 +719,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_iint_inode(inode);
> int must_appraise;
>
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> @@ -732,8 +731,8 @@ 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);
> + iint = ima_iint_inode(inode);
> + /* iint is NULL if security_inode_alloc() was not called on inode. */
> if (!iint)
> return;
>
> @@ -936,7 +935,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 +1144,60 @@ 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_iint_inode(inode);
> +
> + /* Called by security_inode_alloc(), it cannot be NULL. */
> + 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 void ima_inode_free_security(struct inode *inode)
> +{
> + struct ima_iint_cache *iint = ima_iint_inode(inode);
> +
> + if (!iint)
> + return;
> +
> + kfree(iint->ima_hash);
> + mutex_destroy(&iint->mutex);
> +}
> +
> 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 +1209,8 @@ 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),
> + LSM_HOOK_INIT(inode_free_security, ima_inode_free_security),
> #ifdef CONFIG_SECURITY_PATH
> LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
> #endif
> @@ -1179,10 +1234,15 @@ static 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;
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 7a97c269a072..671fc50255f9 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -20,59 +20,6 @@
> #include <linux/audit.h>
> #include <linux/lsm_hooks.h>
>
> -/* iint action cache flags */
> -#define IMA_MEASURE 0x00000001
> -#define IMA_MEASURED 0x00000002
> -#define IMA_APPRAISE 0x00000004
> -#define IMA_APPRAISED 0x00000008
> -/*#define IMA_COLLECT 0x00000010 do not use this flag */
> -#define IMA_COLLECTED 0x00000020
> -#define IMA_AUDIT 0x00000040
> -#define IMA_AUDITED 0x00000080
> -#define IMA_HASH 0x00000100
> -#define IMA_HASHED 0x00000200
> -
> -/* iint policy rule cache flags */
> -#define IMA_NONACTION_FLAGS 0xff000000
> -#define IMA_DIGSIG_REQUIRED 0x01000000
> -#define IMA_PERMIT_DIRECTIO 0x02000000
> -#define IMA_NEW_FILE 0x04000000
> -#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
> -#define IMA_MODSIG_ALLOWED 0x20000000
> -#define IMA_CHECK_BLACKLIST 0x40000000
> -#define IMA_VERITY_REQUIRED 0x80000000
> -
> -#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
> - IMA_HASH | IMA_APPRAISE_SUBMASK)
> -#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
> - IMA_HASHED | IMA_COLLECTED | \
> - IMA_APPRAISED_SUBMASK)
> -
> -/* iint subaction appraise cache flags */
> -#define IMA_FILE_APPRAISE 0x00001000
> -#define IMA_FILE_APPRAISED 0x00002000
> -#define IMA_MMAP_APPRAISE 0x00004000
> -#define IMA_MMAP_APPRAISED 0x00008000
> -#define IMA_BPRM_APPRAISE 0x00010000
> -#define IMA_BPRM_APPRAISED 0x00020000
> -#define IMA_READ_APPRAISE 0x00040000
> -#define IMA_READ_APPRAISED 0x00080000
> -#define IMA_CREDS_APPRAISE 0x00100000
> -#define IMA_CREDS_APPRAISED 0x00200000
> -#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
> - IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
> - IMA_CREDS_APPRAISE)
> -#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
> - IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
> - IMA_CREDS_APPRAISED)
> -
> -/* iint cache atomic_flags */
> -#define IMA_CHANGE_XATTR 0
> -#define IMA_UPDATE_XATTR 1
> -#define IMA_CHANGE_ATTR 2
> -#define IMA_DIGSIG 3
> -#define IMA_MUST_MEASURE 4
> -
> enum evm_ima_xattr_type {
> IMA_XATTR_DIGEST = 0x01,
> EVM_XATTR_HMAC,


2023-12-26 18:16:06

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 19/24] ima: Move to LSM infrastructure

On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Move hardcoded IMA function calls (not appraisal-specific functions) from
> various places in the kernel to the LSM infrastructure, by introducing a
> new LSM named 'ima' (at the end of the LSM list and always enabled like
> 'integrity').
>
> Having IMA before EVM in the Makefile is sufficient to preserve the
> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
> and thus the order of IMA and EVM function calls as when they were
> hardcoded.
>
> Make moved functions as static (except ima_post_key_create_or_update(),
> which is not in ima_main.c), and register them as implementation of the
> respective hooks in the new function init_ima_lsm().
>
> A slight difference is that IMA and EVM functions registered for the
> inode_post_setattr, inode_post_removexattr, path_post_mknod,
> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
> won't be executed for private inodes. Since those inodes are supposed to be
> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
> and kernel-internal tmpfs files.
>
> Conditionally register ima_post_path_mknod() if CONFIG_SECURITY_PATH is
> enabled, otherwise the path_post_mknod hook won't be available.

Up to this point, enabling CONFIG_SECURITY_PATH was not required. By
making it conditional on CONFIG_SECURITY_PATH, anyone enabling IMA will
also need to enable CONFIG_SECURITY_PATH. Without it, new files will
not be tagged as a "new" file.

Casey, Paul, how common is it today not to enable CONFIG_SECURITY_PATH?
Will enabling it just for IMA be a problem?

>
> Also, conditionally register ima_post_key_create_or_update() if
> CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled.
>
> Move integrity_kernel_module_request() to IMA and name it
> ima_kernel_module_request(), as only appraisal is affected by the crypto
> subsystem trying to load kernel modules. Conditionally register
> ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is
> enabled.

The previous version was so clean.
Moving integrity_kernel_module_request() to IMA should be a separate
patch, probably a prereq. Then like the other functions convert it to
an LSM hook.

Please include a line explaning why the original EVM signature is not
affected.

>
> Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Acked-by: Chuck Lever <[email protected]>

--
thanks,

Mimi


2023-12-26 20:14:27

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v8 19/24] ima: Move to LSM infrastructure

On 12/26/2023 10:14 AM, Mimi Zohar wrote:
> On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
>> From: Roberto Sassu <[email protected]>
>>
>> Move hardcoded IMA function calls (not appraisal-specific functions) from
>> various places in the kernel to the LSM infrastructure, by introducing a
>> new LSM named 'ima' (at the end of the LSM list and always enabled like
>> 'integrity').
>>
>> Having IMA before EVM in the Makefile is sufficient to preserve the
>> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
>> and thus the order of IMA and EVM function calls as when they were
>> hardcoded.
>>
>> Make moved functions as static (except ima_post_key_create_or_update(),
>> which is not in ima_main.c), and register them as implementation of the
>> respective hooks in the new function init_ima_lsm().
>>
>> A slight difference is that IMA and EVM functions registered for the
>> inode_post_setattr, inode_post_removexattr, path_post_mknod,
>> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
>> won't be executed for private inodes. Since those inodes are supposed to be
>> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
>> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
>> and kernel-internal tmpfs files.
>>
>> Conditionally register ima_post_path_mknod() if CONFIG_SECURITY_PATH is
>> enabled, otherwise the path_post_mknod hook won't be available.
> Up to this point, enabling CONFIG_SECURITY_PATH was not required. By
> making it conditional on CONFIG_SECURITY_PATH, anyone enabling IMA will
> also need to enable CONFIG_SECURITY_PATH. Without it, new files will
> not be tagged as a "new" file.
>
> Casey, Paul, how common is it today not to enable CONFIG_SECURITY_PATH?
> Will enabling it just for IMA be a problem?

Landlock, AppArmor and TOMOYO require it. Fedora enables Landlock and Ubuntu
enables AppArmor. I expect that, except for "minimal" distributions, you
won't get any push back. If a distribution is striving for minimal, it's not
going to use IMA.

It makes me wonder if eliminating CONFIG_SECURITY_PATH might not be a
rational alternative.


2023-12-26 22:14:47

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 21/24] evm: Move to LSM infrastructure

On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> As for IMA, move hardcoded EVM function calls from various places in the
> kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
> (last and always enabled like 'ima'). The order in the Makefile ensures
> that 'evm' hooks are executed after 'ima' ones.
>
> Make EVM functions as static (except for evm_inode_init_security(), which
> is exported), and register them as hook implementations in init_evm_lsm().
>
> Unlike before (see commit to move IMA to the LSM infrastructure),
> evm_inode_post_setattr(), evm_inode_post_set_acl(),
> evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
> executed for private inodes.
>

Missing is a comment on moving the inline function definitions -
evm_inode_remove_acl(), evm_inode_post_remove_acl(), and
evm_inode_post_set_acl() - to evm_main.c.

> Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
>
> Signed-off-by: Roberto Sassu <[email protected]>
> ---

[...]
> @@ -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;
> }

Even though capability will be called after EVM, it doesn't make a
difference in this instance.

[...]

> /**
> @@ -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;
> }

'security.capability' is one of the EVM protected xattrs. As
capability isn't an LSM, it will now be called after EVM, which is a
problem.

--
thanks,

Mimi


2023-12-26 22:16:17

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 20/24] ima: Move IMA-Appraisal to LSM infrastructure

On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>

A few additional IMA hooks are needed to reset the cached appraisal
status, causing the file's integrity to be re-evaluated on next access.
Register these IMA-appraisal only functions separately ...

Mimi

> Do the registration of IMA-Appraisal only functions separately from the
> rest of IMA functions, as appraisal is a separate feature not necessarily
> enabled in the kernel configuration.
>
> Reuse the same approach as for other IMA functions, move hardcoded calls
> from various places in the kernel to the LSM infrastructure. Declare the
> functions as static and register them as hook implementations in
> init_ima_appraise_lsm(), called by init_ima_lsm().
>
> Signed-off-by: Roberto Sassu <[email protected]>
> Reviewed-by: Stefan Berger <[email protected]>
> Reviewed-by: Mimi Zohar <[email protected]>


2023-12-27 03:13:03

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 22/24] evm: Make it independent from 'integrity' LSM

On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Define a new structure for EVM-specific metadata, called evm_iint_cache,
> and embed it in the inode security blob. Introduce evm_iint_inode() to
> retrieve metadata, and register evm_inode_alloc_security() for the
> inode_alloc_security LSM hook, to initialize the structure (before
> splitting metadata, this task was done by iint_init_always()).
>
> Keep the non-NULL checks after calling evm_iint_inode() except in
> evm_inode_alloc_security(), to take into account inodes for which
> security_inode_alloc() was not called. When using shared metadata,
> obtaining a NULL pointer from integrity_iint_find() meant that the file
> wasn't processed by IMA.

^wasn't in policy.

Ok. So now regardless of the IMA policy, EVM always allocates and
stores the EVM status. Depending on the IMA policy, the EVM status
could be saved for a lot more inodes.

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

Ok. So the change only negatively impacts memory usage, not
performance.

>
> Replicate the management of the IMA_NEW_FILE flag (now EVM_NEW_FILE), by
> introducing evm_post_path_mknod() and evm_file_free() to respectively set
> and clear the new flag at the same time IMA does.

nit: Instead of "(now EVM_NEW_FILE)", add an additional sentence,
saying "Define EVM_NEW_FILE".

> A noteworthy difference
> is that evm_post_path_mknod() cannot check if a file must be appraised.

This is the result of making EVM independent of IMA's policy.
Somewhere, here or above, this needs to be stated.

> Also, since IMA_NEW_FILE is always cleared in ima_check_last_writer() if it
> is set, it is not necessary to maintain an inode version in EVM to
> replicate the IMA logic (the inode version check is in OR).

IMA checking the i_version is to prevent unnecessarily having to re-
calculate the file data hash, which depending on the file size could
take a while. This is unnecessary for EVM, as re-calculating the EVM
hmac is triggered anytime a trusted xattr is updated. So only the EVM
new file flag needs to cleared on file free.

> Also, move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to
> security/integrity/evm/evm.h, since that definition is now unnecessary in
> the common integrity layer.
>
> Finally, switch to the LSM reservation mechanism for the EVM xattr, and
> consequently decrement by one the number of xattrs to allocate in
> security_inode_init_security().
>
> Signed-off-by: Roberto Sassu <[email protected]>

--
thanks,

Mimi


2023-12-27 13:23:25

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 23/24] ima: Make it independent from 'integrity' LSM

On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <[email protected]>
>
> Make the 'ima' LSM independent from 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.
>
> Move the remaining IMA-specific flags to security/integrity/ima/ima.h,
> since they are now unnecessary in the common integrity layer.
>
> 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 it is available for all inodes having the security blob
> allocated (those for which security_inode_alloc() was called). Adjust the
> IMA code accordingly, call ima_iint_inode() to retrieve the ima_iint_cache
> structure. Keep the non-NULL checks since there can be inodes without
> security blob.

Previously the 'iint' memory was only allocated for regular files in
policy and were tagged S_IMA. This patch totally changes when and how
memory is being allocated. Does it make sense to allocate memory at
security_inode_alloc()? Is this change really necessary for making IMA
a full fledged LSM?

Mimi

>
> 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().
>
> Finally, register ima_inode_alloc_security/ima_inode_free_security() to
> initialize/deinitialize the new ima_iint_cache structure (before this task
> was done by iint_init_always() and iint_free()). 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]>


2023-12-27 16:56:10

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v8 23/24] ima: Make it independent from 'integrity' LSM

On 12/27/2023 2:22 PM, Mimi Zohar wrote:
> On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
>> From: Roberto Sassu <[email protected]>
>>
>> Make the 'ima' LSM independent from 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.
>>
>> Move the remaining IMA-specific flags to security/integrity/ima/ima.h,
>> since they are now unnecessary in the common integrity layer.
>>
>> 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 it is available for all inodes having the security blob
>> allocated (those for which security_inode_alloc() was called). Adjust the
>> IMA code accordingly, call ima_iint_inode() to retrieve the ima_iint_cache
>> structure. Keep the non-NULL checks since there can be inodes without
>> security blob.
>
> Previously the 'iint' memory was only allocated for regular files in
> policy and were tagged S_IMA. This patch totally changes when and how
> memory is being allocated. Does it make sense to allocate memory at
> security_inode_alloc()? Is this change really necessary for making IMA
> a full fledged LSM?

Good question. I think it wouldn't be necessary, we can reuse the same
approach as in the patch 'integrity: Switch from rbtree to LSM-managed
blob for integrity_iint_cache'.

Roberto

> Mimi
>
>>
>> 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().
>>
>> Finally, register ima_inode_alloc_security/ima_inode_free_security() to
>> initialize/deinitialize the new ima_iint_cache structure (before this task
>> was done by iint_init_always() and iint_free()). 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]>


2023-12-27 19:22:20

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 23/24] ima: Make it independent from 'integrity' LSM

On Wed, 2023-12-27 at 17:39 +0100, Roberto Sassu wrote:
> On 12/27/2023 2:22 PM, Mimi Zohar wrote:
> > On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> >> From: Roberto Sassu <[email protected]>
> >>
> >> Make the 'ima' LSM independent from 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.
> >>
> >> Move the remaining IMA-specific flags to security/integrity/ima/ima.h,
> >> since they are now unnecessary in the common integrity layer.
> >>
> >> 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 it is available for all inodes having the security blob
> >> allocated (those for which security_inode_alloc() was called). Adjust the
> >> IMA code accordingly, call ima_iint_inode() to retrieve the ima_iint_cache
> >> structure. Keep the non-NULL checks since there can be inodes without
> >> security blob.
> >
> > Previously the 'iint' memory was only allocated for regular files in
> > policy and were tagged S_IMA. This patch totally changes when and how
> > memory is being allocated. Does it make sense to allocate memory at
> > security_inode_alloc()? Is this change really necessary for making IMA
> > a full fledged LSM?
>
> Good question. I think it wouldn't be necessary, we can reuse the same
> approach as in the patch 'integrity: Switch from rbtree to LSM-managed
> blob for integrity_iint_cache'.

Going forward with the v8 proposed solution would require some real
memory usage analysis for different types of policies.

To me the "integrity: Switch from rbtree to LSM-managed blob for
integrity_iint_cache" makes a lot more sense. Looking back at the
original thread, your reasons back then for not directly allocating the
integrity_iint_cache are still valid for the ima_iint_cache structure.

Mimi

> >
> >>
> >> 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().
> >>
> >> Finally, register ima_inode_alloc_security/ima_inode_free_security() to
> >> initialize/deinitialize the new ima_iint_cache structure (before this task
> >> was done by iint_init_always() and iint_free()). 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]>
>



2023-12-27 19:53:28

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 19/24] ima: Move to LSM infrastructure

On Tue, 2023-12-26 at 12:14 -0800, Casey Schaufler wrote:
> On 12/26/2023 10:14 AM, Mimi Zohar wrote:
> > On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> >> From: Roberto Sassu <[email protected]>
> >>
> >> Move hardcoded IMA function calls (not appraisal-specific functions) from
> >> various places in the kernel to the LSM infrastructure, by introducing a
> >> new LSM named 'ima' (at the end of the LSM list and always enabled like
> >> 'integrity').
> >>
> >> Having IMA before EVM in the Makefile is sufficient to preserve the
> >> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
> >> and thus the order of IMA and EVM function calls as when they were
> >> hardcoded.
> >>
> >> Make moved functions as static (except ima_post_key_create_or_update(),
> >> which is not in ima_main.c), and register them as implementation of the
> >> respective hooks in the new function init_ima_lsm().
> >>
> >> A slight difference is that IMA and EVM functions registered for the
> >> inode_post_setattr, inode_post_removexattr, path_post_mknod,
> >> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
> >> won't be executed for private inodes. Since those inodes are supposed to be
> >> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
> >> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
> >> and kernel-internal tmpfs files.
> >>
> >> Conditionally register ima_post_path_mknod() if CONFIG_SECURITY_PATH is
> >> enabled, otherwise the path_post_mknod hook won't be available.
> > Up to this point, enabling CONFIG_SECURITY_PATH was not required. By
> > making it conditional on CONFIG_SECURITY_PATH, anyone enabling IMA will
> > also need to enable CONFIG_SECURITY_PATH. Without it, new files will
> > not be tagged as a "new" file.
> >
> > Casey, Paul, how common is it today not to enable CONFIG_SECURITY_PATH?
> > Will enabling it just for IMA be a problem?
>
> Landlock, AppArmor and TOMOYO require it. Fedora enables Landlock and Ubuntu
> enables AppArmor. I expect that, except for "minimal" distributions, you
> won't get any push back. If a distribution is striving for minimal, it's not
> going to use IMA.
>
> It makes me wonder if eliminating CONFIG_SECURITY_PATH might not be a
> rational alternative.

Embedded systems were the first to use IMA for file signature
verification, not distros. Could they have enabled
SELinux, lockdown, and IMA?

Mimi


2023-12-27 20:20:18

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH v8 19/24] ima: Move to LSM infrastructure

On 12/27/2023 11:52 AM, Mimi Zohar wrote:
> On Tue, 2023-12-26 at 12:14 -0800, Casey Schaufler wrote:
>> On 12/26/2023 10:14 AM, Mimi Zohar wrote:
>>> On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
>>>> From: Roberto Sassu <[email protected]>
>>>>
>>>> Move hardcoded IMA function calls (not appraisal-specific functions) from
>>>> various places in the kernel to the LSM infrastructure, by introducing a
>>>> new LSM named 'ima' (at the end of the LSM list and always enabled like
>>>> 'integrity').
>>>>
>>>> Having IMA before EVM in the Makefile is sufficient to preserve the
>>>> relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM,
>>>> and thus the order of IMA and EVM function calls as when they were
>>>> hardcoded.
>>>>
>>>> Make moved functions as static (except ima_post_key_create_or_update(),
>>>> which is not in ima_main.c), and register them as implementation of the
>>>> respective hooks in the new function init_ima_lsm().
>>>>
>>>> A slight difference is that IMA and EVM functions registered for the
>>>> inode_post_setattr, inode_post_removexattr, path_post_mknod,
>>>> inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl
>>>> won't be executed for private inodes. Since those inodes are supposed to be
>>>> fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
>>>> flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub
>>>> and kernel-internal tmpfs files.
>>>>
>>>> Conditionally register ima_post_path_mknod() if CONFIG_SECURITY_PATH is
>>>> enabled, otherwise the path_post_mknod hook won't be available.
>>> Up to this point, enabling CONFIG_SECURITY_PATH was not required. By
>>> making it conditional on CONFIG_SECURITY_PATH, anyone enabling IMA will
>>> also need to enable CONFIG_SECURITY_PATH. Without it, new files will
>>> not be tagged as a "new" file.
>>>
>>> Casey, Paul, how common is it today not to enable CONFIG_SECURITY_PATH?
>>> Will enabling it just for IMA be a problem?
>> Landlock, AppArmor and TOMOYO require it. Fedora enables Landlock and Ubuntu
>> enables AppArmor. I expect that, except for "minimal" distributions, you
>> won't get any push back. If a distribution is striving for minimal, it's not
>> going to use IMA.
>>
>> It makes me wonder if eliminating CONFIG_SECURITY_PATH might not be a
>> rational alternative.
> Embedded systems were the first to use IMA for file signature
> verification, not distros. Could they have enabled
> SELinux, lockdown, and IMA?

Yes, they could have. I know some have used Smack and some SELinux.
That's not really relevant, as neither of those use path hooks. My
thought is that CONFIG_SECURITY_PATH adds more aggravation than value,
but I can't quote numbers on either. I don't see a problem with IMA
using path hooks. I also wouldn't see harm in moving the hook(s) you
need for IMA out from that configuration option and into the general
set. With the current rate of new hook additions I can't see moving
an existing hook as a problem.


2024-01-02 10:54:39

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v8 23/24] ima: Make it independent from 'integrity' LSM

On 12/27/2023 8:21 PM, Mimi Zohar wrote:
> On Wed, 2023-12-27 at 17:39 +0100, Roberto Sassu wrote:
>> On 12/27/2023 2:22 PM, Mimi Zohar wrote:
>>> On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
>>>> From: Roberto Sassu <[email protected]>
>>>>
>>>> Make the 'ima' LSM independent from 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.
>>>>
>>>> Move the remaining IMA-specific flags to security/integrity/ima/ima.h,
>>>> since they are now unnecessary in the common integrity layer.
>>>>
>>>> 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 it is available for all inodes having the security blob
>>>> allocated (those for which security_inode_alloc() was called). Adjust the
>>>> IMA code accordingly, call ima_iint_inode() to retrieve the ima_iint_cache
>>>> structure. Keep the non-NULL checks since there can be inodes without
>>>> security blob.
>>>
>>> Previously the 'iint' memory was only allocated for regular files in
>>> policy and were tagged S_IMA. This patch totally changes when and how
>>> memory is being allocated. Does it make sense to allocate memory at
>>> security_inode_alloc()? Is this change really necessary for making IMA
>>> a full fledged LSM?
>>
>> Good question. I think it wouldn't be necessary, we can reuse the same
>> approach as in the patch 'integrity: Switch from rbtree to LSM-managed
>> blob for integrity_iint_cache'.
>
> Going forward with the v8 proposed solution would require some real
> memory usage analysis for different types of policies.
>
> To me the "integrity: Switch from rbtree to LSM-managed blob for
> integrity_iint_cache" makes a lot more sense. Looking back at the
> original thread, your reasons back then for not directly allocating the
> integrity_iint_cache are still valid for the ima_iint_cache structure.

Uhm, ok. It should not be too difficult to restore the old mechanism for
ima_iint_cache. Will do it in v9.

Thanks

Roberto

> Mimi
>
>>>
>>>>
>>>> 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().
>>>>
>>>> Finally, register ima_inode_alloc_security/ima_inode_free_security() to
>>>> initialize/deinitialize the new ima_iint_cache structure (before this task
>>>> was done by iint_init_always() and iint_free()). 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]>
>>
>


2024-01-02 12:15:19

by Roberto Sassu

[permalink] [raw]
Subject: Re: [PATCH v8 21/24] evm: Move to LSM infrastructure

On 12/26/2023 11:13 PM, Mimi Zohar wrote:
> On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
>> From: Roberto Sassu <[email protected]>
>>
>> As for IMA, move hardcoded EVM function calls from various places in the
>> kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
>> (last and always enabled like 'ima'). The order in the Makefile ensures
>> that 'evm' hooks are executed after 'ima' ones.
>>
>> Make EVM functions as static (except for evm_inode_init_security(), which
>> is exported), and register them as hook implementations in init_evm_lsm().
>>
>> Unlike before (see commit to move IMA to the LSM infrastructure),
>> evm_inode_post_setattr(), evm_inode_post_set_acl(),
>> evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
>> executed for private inodes.
>>
>
> Missing is a comment on moving the inline function definitions -
> evm_inode_remove_acl(), evm_inode_post_remove_acl(), and
> evm_inode_post_set_acl() - to evm_main.c.

Ok.

>> Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
>>
>> Signed-off-by: Roberto Sassu <[email protected]>
>> ---
>
> [...]
>> @@ -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;
>> }
>
> Even though capability will be called after EVM, it doesn't make a
> difference in this instance.
>
> [...]
>
>> /**
>> @@ -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;
>> }
>
> 'security.capability' is one of the EVM protected xattrs. As
> capability isn't an LSM, it will now be called after EVM, which is a
> problem.

Uhm, according to this comment in security_inode_removexattr() and
security_inode_setxattr():

/*
* SELinux and Smack integrate the cap call,
* so assume that all LSMs supplying this call do so.
*/

We can add the call to IMA and EVM as well, to be compliant.

However, I'm missing why the two cases are different. It seems
cap_inode_set/removexattr() are doing just checks.

Thanks

Roberto


2024-01-02 17:45:57

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 21/24] evm: Move to LSM infrastructure

On Tue, 2024-01-02 at 12:56 +0100, Roberto Sassu wrote:
> On 12/26/2023 11:13 PM, Mimi Zohar wrote:
> > On Thu, 2023-12-14 at 18:08 +0100, Roberto Sassu wrote:
> >> From: Roberto Sassu <[email protected]>
> >>
> >> As for IMA, move hardcoded EVM function calls from various places in the
> >> kernel to the LSM infrastructure, by introducing a new LSM named 'evm'
> >> (last and always enabled like 'ima'). The order in the Makefile ensures
> >> that 'evm' hooks are executed after 'ima' ones.
> >>
> >> Make EVM functions as static (except for evm_inode_init_security(), which
> >> is exported), and register them as hook implementations in init_evm_lsm().
> >>
> >> Unlike before (see commit to move IMA to the LSM infrastructure),
> >> evm_inode_post_setattr(), evm_inode_post_set_acl(),
> >> evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not
> >> executed for private inodes.
> >>
> >
> > Missing is a comment on moving the inline function definitions -
> > evm_inode_remove_acl(), evm_inode_post_remove_acl(), and
> > evm_inode_post_set_acl() - to evm_main.c.
>
> Ok.
>
> >> Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
> >>
> >> Signed-off-by: Roberto Sassu <[email protected]>
> >> ---
> >
> > [...]
> >> @@ -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;
> >> }
> >
> > Even though capability will be called after EVM, it doesn't make a
> > difference in this instance.
> >
> > [...]
> >
> >> /**
> >> @@ -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;
> >> }
> >
> > 'security.capability' is one of the EVM protected xattrs. As
> > capability isn't an LSM, it will now be called after EVM, which is a
> > problem.
>
> Uhm, according to this comment in security_inode_removexattr() and
> security_inode_setxattr():
>
> /*
> * SELinux and Smack integrate the cap call,
> * so assume that all LSMs supplying this call do so.
> */
>
> We can add the call to IMA and EVM as well, to be compliant.

SELinux and Smack are the only current LSMs that register the
security_inode_removexattr hook. Both enforce mandatory access
control,
so their calling capabilities to enforce DAC kind of makes sense. I'm
not sure it makes sense for IMA and EVM to call capability directly,
just because of the comment.

> However, I'm missing why the two cases are different. It seems
> cap_inode_set/removexattr() are doing just checks.

Both IMA and EVM require CAP_SYS_ADMIN to write/remove security.ima and
security.evm respectively. In addition, EVM must recalculate
security.evm if any protected security xattrs are set or
removed. However, security.evm is updated on
security_inode_post_setxattr, not security_inode_setxattr.

Mimi