From: Roberto Sassu roberto.sassu@huawei.com
IMA and EVM are not effectively LSMs, especially due to the fact that in the past they could not provide a security blob while there is another LSM active.
That changed in the recent years, the LSM stacking feature now makes it possible to stack together multiple LSMs, and allows them to provide a security blob for most kernel objects. While the LSM stacking feature has some limitations being worked out, it is already suitable to make IMA and EVM as LSMs.
The main purpose of this patch set is to remove IMA and EVM function calls, hardcoded in the LSM infrastructure and other places in the kernel, and to register them as LSM hook implementations, so that those functions are called by the LSM infrastructure like other regular LSMs.
This patch set introduces two new LSMs 'ima' and 'evm', so that functions can be registered to their respective LSM, and removes the 'integrity' LSM. integrity_kernel_module_request() was moved to IMA, since deadlock could occur only there. integrity_inode_free() was replaced with ima_inode_free() (EVM does not need to free memory).
In order to make 'ima' and 'evm' independent LSMs, it was necessary to split integrity metadata used by both IMA and EVM, and to let them manage their own. The special case of the IMA_NEW_FILE flag, managed by IMA and used by EVM, was handled by introducing a new flag in EVM, EVM_NEW_FILE, managed by two additional LSM hooks, evm_post_path_mknod() and evm_file_release(), equivalent to their counterparts ima_post_path_mknod() and ima_file_free().
In addition to splitting metadata, it was decided to embed the evm_iint_inode structure into the inode security blob, since it is small and because anyway it cannot rely on IMA anymore allocating it (since it uses a different structure).
On the other hand, to avoid memory pressure concerns, only a pointer to the ima_iint_cache structure is stored in the inode security blob, and the structure is allocated on demand, like before.
Another follow-up change was removing the iint parameter from evm_verifyxattr(), that IMA used to pass integrity metadata to EVM. After splitting metadata, and aligning EVM_NEW_FILE with IMA_NEW_FILE, this parameter was not necessary anymore.
The last part was to ensure that the order of IMA and EVM functions is respected after they become LSMs. Since the order of lsm_info structures in the .lsm_info.init section depends on the order object files containing those structures are passed to the linker of the kernel image, and since IMA is before EVM in the Makefile, that is sufficient to assert that IMA functions are executed before EVM ones.
The patch set is organized as follows.
Patches 1-9 make IMA and EVM functions suitable to be registered to the LSM infrastructure, by aligning function parameters.
Patches 10-18 add new LSM hooks in the same places where IMA and EVM functions are called, if there is no LSM hook already.
Patch 19 moves integrity_kernel_module_request() to IMA, as a prerequisite for removing the 'integrity' LSM.
Patches 20-22 introduce the new standalone LSMs 'ima' and 'evm', and move hardcoded calls to IMA, EVM and integrity functions to those LSMs.
Patches 23-24 remove the dependency on the 'integrity' LSM by splitting integrity metadata, so that the 'ima' and 'evm' LSMs can use their own. They also duplicate iint_lockdep_annotate() in ima_main.c, since the mutex field was moved from integrity_iint_cache to ima_iint_cache.
Patch 25 finally removes the 'integrity' LSM, since 'ima' and 'evm' are now self-contained and independent.
The patch set applies on top of lsm/dev, commit f1bb47a31dff ("lsm: new security_file_ioctl_compat() hook"). The linux-integrity/next-integrity at commit c00f94b3a5be ("overlay: disable EVM") was merged.
Changelog:
v8: - Restore dynamic allocation of IMA integrity metadata, and store only the pointer in the inode security blob - Select SECURITY_PATH both in IMA and EVM - Rename evm_file_free() to evm_file_release() - Unconditionally register evm_post_path_mknod() - Introduce the new ima_iint.c file for the management of IMA integrity metadata - Introduce ima_inode_set_iint()/ima_inode_get_iint() in ima.h to respectively store/retrieve the IMA integrity metadata pointer - Replace ima_iint_inode() with ima_inode_get() and ima_iint_find(), with same behavior of integrity_inode_get() and integrity_iint_find() - Initialize the ima_iint_cache in ima_iintcache_init() and call it from init_ima_lsm() - Move integrity_kernel_module_request() to IMA in a separate patch (suggested by Mimi) - Compile ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled - Remove ima_inode_alloc_security() and ima_inode_free_security(), since the IMA integrity metadata is not fully embedded in the inode security blob - Fixed the missed initialization of ima_iint_cache in process_measurement() and __ima_inode_hash() - Add a sentence in 'evm: Move to LSM infrastructure' to mention about moving evm_inode_remove_acl(), evm_inode_post_remove_acl() and evm_inode_post_set_acl() to evm_main.c - Add a sentence in 'ima: Move IMA-Appraisal to LSM infrastructure' to mention about moving ima_inode_remove_acl() to ima_appraise.c
v7: - Use return instead of goto in __vfs_removexattr_locked() (suggested by Casey) - Clarify in security/integrity/Makefile that the order of 'ima' and 'evm' LSMs depends on the order in which IMA and EVM are compiled - Move integrity_iint_cache flags to ima.h and evm.h in security/ and duplicate IMA_NEW_FILE to EVM_NEW_FILE - Rename evm_inode_get_iint() to evm_iint_inode() and ima_inode_get_iint() to ima_iint_inode(), check if inode->i_security is NULL, and just return the pointer from the inode security blob - Restore the non-NULL checks after ima_iint_inode() and evm_iint_inode() (suggested by Casey) - Introduce evm_file_free() to clear EVM_NEW_FILE - Remove comment about LSM_ORDER_LAST not guaranteeing the order of 'ima' and 'evm' LSMs - Lock iint->mutex before reading IMA_COLLECTED flag in __ima_inode_hash() and restored ima_policy_flag check - Remove patch about the hardcoded ordering of 'ima' and 'evm' LSMs in security.c - Add missing ima_inode_free_security() to free iint->ima_hash - Add the cases for LSM_ID_IMA and LSM_ID_EVM in lsm_list_modules_test.c - Mention about the change in IMA and EVM post functions for private inodes
v6: - See v7
v5: - Rename security_file_pre_free() to security_file_release() and the LSM hook file_pre_free_security to file_release (suggested by Paul) - Move integrity_kernel_module_request() to ima_main.c (renamed to ima_kernel_module_request()) - Split the integrity_iint_cache structure into ima_iint_cache and evm_iint_cache, so that IMA and EVM can use disjoint metadata and reserve space with the LSM infrastructure - Reserve space for the entire ima_iint_cache and evm_iint_cache structures, not just the pointer (suggested by Paul) - Introduce ima_inode_get_iint() and evm_inode_get_iint() to retrieve respectively the ima_iint_cache and evm_iint_cache structure from the security blob - Remove the various non-NULL checks for the ima_iint_cache and evm_iint_cache structures, since the LSM infrastructure ensure that they always exist - Remove the iint parameter from evm_verifyxattr() since IMA and EVM use disjoint integrity metaddata - Introduce the evm_post_path_mknod() to set the IMA_NEW_FILE flag - Register the inode_alloc_security LSM hook in IMA and EVM to initialize the respective integrity metadata structures - Remove the 'integrity' LSM completely and instead make 'ima' and 'evm' proper standalone LSMs - Add the inode parameter to ima_get_verity_digest(), since the inode field is not present in ima_iint_cache - Move iint_lockdep_annotate() to ima_main.c (renamed to ima_iint_lockdep_annotate()) - Remove ima_get_lsm_id() and evm_get_lsm_id(), since IMA and EVM directly register the needed LSM hooks - Enforce 'ima' and 'evm' LSM ordering at LSM infrastructure level
v4: - Improve short and long description of security_inode_post_create_tmpfile(), security_inode_post_set_acl(), security_inode_post_remove_acl() and security_file_post_open() (suggested by Mimi) - Improve commit message of 'ima: Move to LSM infrastructure' (suggested by Mimi)
v3: - Drop 'ima: Align ima_post_path_mknod() definition with LSM infrastructure' and 'ima: Align ima_post_create_tmpfile() definition with LSM infrastructure', define the new LSM hooks with the same IMA parameters instead (suggested by Mimi) - Do IS_PRIVATE() check in security_path_post_mknod() and security_inode_post_create_tmpfile() on the new inode rather than the parent directory (in the post method it is available) - Don't export ima_file_check() (suggested by Stefan) - Remove redundant check of file mode in ima_post_path_mknod() (suggested by Mimi) - Mention that ima_post_path_mknod() is now conditionally invoked when CONFIG_SECURITY_PATH=y (suggested by Mimi) - Mention when a LSM hook will be introduced in the IMA/EVM alignment patches (suggested by Mimi) - Simplify the commit messages when introducing a new LSM hook - Still keep the 'extern' in the function declaration, until the declaration is removed (suggested by Mimi) - Improve documentation of security_file_pre_free() - Register 'ima' and 'evm' as standalone LSMs (suggested by Paul) - Initialize the 'ima' and 'evm' LSMs from 'integrity', to keep the original ordering of IMA and EVM functions as when they were hardcoded - Return the IMA and EVM LSM IDs to 'integrity' for registration of the integrity-specific hooks - Reserve an xattr slot from the 'evm' LSM instead of 'integrity' - Pass the LSM ID to init_ima_appraise_lsm()
v2: - Add description for newly introduced LSM hooks (suggested by Casey) - Clarify in the description of security_file_pre_free() that actions can be performed while the file is still open
v1: - Drop 'evm: Complete description of evm_inode_setattr()', 'fs: Fix description of vfs_tmpfile()' and 'security: Introduce LSM_ORDER_LAST', they were sent separately (suggested by Christian Brauner) - Replace dentry with file descriptor parameter for security_inode_post_create_tmpfile() - Introduce mode_stripped and pass it as mode argument to security_path_mknod() and security_path_post_mknod() - Use goto in do_mknodat() and __vfs_removexattr_locked() (suggested by Mimi) - Replace __lsm_ro_after_init with __ro_after_init - Modify short description of security_inode_post_create_tmpfile() and security_inode_post_set_acl() (suggested by Stefan) - Move security_inode_post_setattr() just after security_inode_setattr() (suggested by Mimi) - Modify short description of security_key_post_create_or_update() (suggested by Mimi) - Add back exported functions ima_file_check() and evm_inode_init_security() respectively to ima.h and evm.h (reported by kernel robot) - Remove extern from prototype declarations and fix style issues - Remove unnecessary include of linux/lsm_hooks.h in ima_main.c and ima_appraise.c
Roberto Sassu (25): ima: Align ima_inode_post_setattr() definition with LSM infrastructure ima: Align ima_file_mprotect() definition with LSM infrastructure ima: Align ima_inode_setxattr() definition with LSM infrastructure ima: Align ima_inode_removexattr() definition with LSM infrastructure ima: Align ima_post_read_file() definition with LSM infrastructure evm: Align evm_inode_post_setattr() definition with LSM infrastructure evm: Align evm_inode_setxattr() definition with LSM infrastructure evm: Align evm_inode_post_setxattr() definition with LSM infrastructure security: Align inode_setattr hook definition with EVM security: Introduce inode_post_setattr hook security: Introduce inode_post_removexattr hook security: Introduce file_post_open hook security: Introduce file_release hook security: Introduce path_post_mknod hook security: Introduce inode_post_create_tmpfile hook security: Introduce inode_post_set_acl hook security: Introduce inode_post_remove_acl hook security: Introduce key_post_create_or_update hook integrity: Move integrity_kernel_module_request() to IMA ima: Move to LSM infrastructure ima: Move IMA-Appraisal to LSM infrastructure evm: Move to LSM infrastructure evm: Make it independent from 'integrity' LSM ima: Make it independent from 'integrity' LSM integrity: Remove LSM
fs/attr.c | 5 +- fs/file_table.c | 3 +- fs/namei.c | 12 +- fs/nfsd/vfs.c | 3 +- fs/open.c | 1 - fs/posix_acl.c | 5 +- fs/xattr.c | 9 +- include/linux/evm.h | 117 +------- include/linux/ima.h | 142 ---------- include/linux/integrity.h | 27 -- include/linux/lsm_hook_defs.h | 20 +- include/linux/security.h | 59 ++++ include/uapi/linux/lsm.h | 2 + security/integrity/Makefile | 1 + security/integrity/digsig_asymmetric.c | 23 -- security/integrity/evm/Kconfig | 1 + security/integrity/evm/evm.h | 19 ++ security/integrity/evm/evm_crypto.c | 4 +- security/integrity/evm/evm_main.c | 195 ++++++++++--- security/integrity/iint.c | 197 +------------ security/integrity/ima/Kconfig | 1 + security/integrity/ima/Makefile | 2 +- security/integrity/ima/ima.h | 148 ++++++++-- security/integrity/ima/ima_api.c | 23 +- security/integrity/ima/ima_appraise.c | 66 +++-- security/integrity/ima/ima_iint.c | 142 ++++++++++ security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_main.c | 144 +++++++--- security/integrity/ima/ima_policy.c | 2 +- security/integrity/integrity.h | 80 +----- security/keys/key.c | 10 +- security/security.c | 263 +++++++++++------- security/selinux/hooks.c | 3 +- security/smack/smack_lsm.c | 4 +- .../selftests/lsm/lsm_list_modules_test.c | 6 + 35 files changed, 902 insertions(+), 839 deletions(-) create mode 100644 security/integrity/ima/ima_iint.c
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- 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;
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change ima_inode_post_setattr() definition, so that it can be registered as implementation of the inode_post_setattr hook (to be introduced).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/attr.c | 2 +- include/linux/ima.h | 4 ++-- security/integrity/ima/ima_appraise.c | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-)
Reviewed-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
Change ima_file_mprotect() definition, so that it can be registered as implementation of the file_mprotect hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- include/linux/ima.h | 5 +++-- security/integrity/ima/ima_main.c | 6 ++++-- security/security.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 910a2f11a906..b66353f679e8 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -23,7 +23,8 @@ extern void ima_post_create_tmpfile(struct mnt_idmap *idmap, extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags); -extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot); +extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, + unsigned long prot); extern int ima_load_data(enum kernel_load_data_id id, bool contents); extern int ima_post_load_data(char *buf, loff_t size, enum kernel_load_data_id id, char *description); @@ -84,7 +85,7 @@ static inline int ima_file_mmap(struct file *file, unsigned long reqprot, }
static inline int ima_file_mprotect(struct vm_area_struct *vma, - unsigned long prot) + unsigned long reqprot, unsigned long prot) { return 0; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index cc1217ac2c6f..b3f5e8401056 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -455,7 +455,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot, /** * ima_file_mprotect - based on policy, limit mprotect change * @vma: vm_area_struct protection is set to - * @prot: contains the protection that will be applied by the kernel. + * @reqprot: protection requested by the application + * @prot: protection that will be applied by the kernel * * Files can be mmap'ed read/write and later changed to execute to circumvent * IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore @@ -465,7 +466,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot, * * On mprotect change success, return 0. On failure, return -EACESS. */ -int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) +int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, + unsigned long prot) { struct ima_template_desc *template = NULL; struct file *file; diff --git a/security/security.c b/security/security.c index 013c847b28d0..0608679478ea 100644 --- a/security/security.c +++ b/security/security.c @@ -2837,7 +2837,7 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot); if (ret) return ret; - return ima_file_mprotect(vma, prot); + return ima_file_mprotect(vma, reqprot, prot); }
/**
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change ima_file_mprotect() definition, so that it can be registered as implementation of the file_mprotect hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
include/linux/ima.h | 5 +++-- security/integrity/ima/ima_main.c | 6 ++++-- security/security.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
Change ima_inode_setxattr() definition, so that it can be registered as implementation of the inode_setxattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com --- include/linux/ima.h | 11 +++++++---- security/integrity/ima/ima_appraise.c | 5 +++-- security/security.c | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index b66353f679e8..077324309c11 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -188,8 +188,9 @@ static inline void ima_post_key_create_or_update(struct key *keyring, extern bool is_ima_appraise_enabled(void); extern void ima_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, int ia_valid); -extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, - const void *xattr_value, size_t xattr_value_len); +extern int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name, const void *xattr_value, + size_t xattr_value_len, int flags); extern int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl); @@ -212,10 +213,12 @@ static inline void ima_inode_post_setattr(struct mnt_idmap *idmap, return; }
-static inline int ima_inode_setxattr(struct dentry *dentry, +static inline int ima_inode_setxattr(struct mnt_idmap *idmap, + struct dentry *dentry, const char *xattr_name, const void *xattr_value, - size_t xattr_value_len) + size_t xattr_value_len, + int flags) { return 0; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 36c2938a5c69..cb2d0d11aa77 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -750,8 +750,9 @@ static int validate_hash_algo(struct dentry *dentry, return -EACCES; }
-int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, - const void *xattr_value, size_t xattr_value_len) +int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name, const void *xattr_value, + size_t xattr_value_len, int flags) { const struct evm_ima_xattr_data *xvalue = xattr_value; int digsig = 0; diff --git a/security/security.c b/security/security.c index 0608679478ea..98531e9b368f 100644 --- a/security/security.c +++ b/security/security.c @@ -2269,7 +2269,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap, ret = cap_inode_setxattr(dentry, name, value, size, flags); if (ret) return ret; - ret = ima_inode_setxattr(dentry, name, value, size); + ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags); if (ret) return ret; return evm_inode_setxattr(idmap, dentry, name, value, size);
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change ima_inode_setxattr() definition, so that it can be registered as implementation of the inode_setxattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/ima.h | 11 +++++++---- security/integrity/ima/ima_appraise.c | 5 +++-- security/security.c | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
Change ima_inode_removexattr() definition, so that it can be registered as implementation of the inode_removexattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- include/linux/ima.h | 7 +++++-- security/integrity/ima/ima_appraise.c | 3 ++- security/security.c | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 077324309c11..678a03fddd7e 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -200,7 +200,9 @@ static inline int ima_inode_remove_acl(struct mnt_idmap *idmap, { return ima_inode_set_acl(idmap, dentry, acl_name, NULL); } -extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name); + +extern int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name); #else static inline bool is_ima_appraise_enabled(void) { @@ -231,7 +233,8 @@ static inline int ima_inode_set_acl(struct mnt_idmap *idmap, return 0; }
-static inline int ima_inode_removexattr(struct dentry *dentry, +static inline int ima_inode_removexattr(struct mnt_idmap *idmap, + struct dentry *dentry, const char *xattr_name) { return 0; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index cb2d0d11aa77..36abc84ba299 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -790,7 +790,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, return 0; }
-int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) +int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name) { int result;
diff --git a/security/security.c b/security/security.c index 98531e9b368f..fda78023de48 100644 --- a/security/security.c +++ b/security/security.c @@ -2430,7 +2430,7 @@ int security_inode_removexattr(struct mnt_idmap *idmap, ret = cap_inode_removexattr(idmap, dentry, name); if (ret) return ret; - ret = ima_inode_removexattr(dentry, name); + ret = ima_inode_removexattr(idmap, dentry, name); if (ret) return ret; return evm_inode_removexattr(idmap, dentry, name);
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change ima_inode_removexattr() definition, so that it can be registered as implementation of the inode_removexattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
include/linux/ima.h | 7 +++++-- security/integrity/ima/ima_appraise.c | 3 ++- security/security.c | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com --- 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;
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change ima_post_read_file() definition, by making "void *buf" a "char *buf", so that it can be registered as implementation of the post_read_file hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/ima.h | 4 ++-- security/integrity/ima/ima_main.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
Reviewed-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
Change evm_inode_post_setattr() definition, so that it can be registered as implementation of the inode_post_setattr hook (to be introduced).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- fs/attr.c | 2 +- include/linux/evm.h | 6 ++++-- security/integrity/evm/evm_main.c | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c index 9bddc0a6352c..498e673bdf06 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -503,7 +503,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry, if (!error) { fsnotify_change(dentry, ia_valid); ima_inode_post_setattr(idmap, dentry, ia_valid); - evm_inode_post_setattr(dentry, ia_valid); + evm_inode_post_setattr(idmap, dentry, ia_valid); }
return error; diff --git a/include/linux/evm.h b/include/linux/evm.h index 36ec884320d9..5cc386312b5a 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -23,7 +23,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry, struct integrity_iint_cache *iint); extern int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); -extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid); +extern void evm_inode_post_setattr(struct mnt_idmap *idmap, + struct dentry *dentry, int ia_valid); extern int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name, const void *value, size_t size); @@ -98,7 +99,8 @@ static inline int evm_inode_setattr(struct mnt_idmap *idmap, return 0; }
-static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) +static inline void evm_inode_post_setattr(struct mnt_idmap *idmap, + struct dentry *dentry, int ia_valid) { return; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cc7956d7878b..ac34f21122cd 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -870,6 +870,7 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
/** * evm_inode_post_setattr - update 'security.evm' after modifying metadata + * @idmap: idmap of the idmapped mount * @dentry: pointer to the affected dentry * @ia_valid: for the UID and GID status * @@ -879,7 +880,8 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, * This function is called from notify_change(), which expects the caller * to lock the inode's i_mutex. */ -void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) +void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + int ia_valid) { if (!evm_revalidate_status(NULL)) return;
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change evm_inode_post_setattr() definition, so that it can be registered as implementation of the inode_post_setattr hook (to be introduced).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/attr.c | 2 +- include/linux/evm.h | 6 ++++-- security/integrity/evm/evm_main.c | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-)
Reviewed-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
Change evm_inode_setxattr() definition, so that it can be registered as implementation of the inode_setxattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com --- include/linux/evm.h | 4 ++-- security/integrity/evm/evm_main.c | 3 ++- security/security.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/include/linux/evm.h b/include/linux/evm.h index 5cc386312b5a..7de24c1ada90 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -27,7 +27,7 @@ extern void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, int ia_valid); extern int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name, - const void *value, size_t size); + const void *value, size_t size, int flags); extern void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, @@ -107,7 +107,7 @@ static inline void evm_inode_post_setattr(struct mnt_idmap *idmap,
static inline int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name, - const void *value, size_t size) + const void *value, size_t size, int flags) { return 0; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index ac34f21122cd..12ba3207fd31 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -581,6 +581,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap, * @xattr_name: pointer to the affected extended attribute name * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length + * @flags: flags to pass into filesystem operations * * Before allowing the 'security.evm' protected xattr to be updated, * verify the existing value is valid. As only the kernel should have @@ -590,7 +591,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap, */ int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *xattr_name, const void *xattr_value, - size_t xattr_value_len) + size_t xattr_value_len, int flags) { const struct evm_ima_xattr_data *xattr_data = xattr_value;
diff --git a/security/security.c b/security/security.c index fda78023de48..f0d1f8f0da80 100644 --- a/security/security.c +++ b/security/security.c @@ -2272,7 +2272,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap, ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags); if (ret) return ret; - return evm_inode_setxattr(idmap, dentry, name, value, size); + return evm_inode_setxattr(idmap, dentry, name, value, size, flags); }
/**
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change evm_inode_setxattr() definition, so that it can be registered as implementation of the inode_setxattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/evm.h | 4 ++-- security/integrity/evm/evm_main.c | 3 ++- security/security.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com --- include/linux/evm.h | 8 +++++--- security/integrity/evm/evm_main.c | 4 +++- security/security.c | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/include/linux/evm.h b/include/linux/evm.h index 7de24c1ada90..3faabdd47852 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -31,7 +31,8 @@ extern int evm_inode_setxattr(struct mnt_idmap *idmap, extern void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, - size_t xattr_value_len); + size_t xattr_value_len, + int flags); extern int evm_inode_copy_up_xattr(const char *name); extern int evm_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *xattr_name); @@ -56,7 +57,7 @@ static inline void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name, struct posix_acl *kacl) { - return evm_inode_post_setxattr(dentry, acl_name, NULL, 0); + return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0); }
int evm_inode_init_security(struct inode *inode, struct inode *dir, @@ -115,7 +116,8 @@ static inline int evm_inode_setxattr(struct mnt_idmap *idmap, static inline void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, - size_t xattr_value_len) + size_t xattr_value_len, + int flags) { return; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 12ba3207fd31..d35143179699 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -753,6 +753,7 @@ bool evm_revalidate_status(const char *xattr_name) * @xattr_name: pointer to the affected extended attribute name * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length + * @flags: flags to pass into filesystem operations * * Update the HMAC stored in 'security.evm' to reflect the change. * @@ -761,7 +762,8 @@ bool evm_revalidate_status(const char *xattr_name) * i_mutex lock. */ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, - const void *xattr_value, size_t xattr_value_len) + const void *xattr_value, size_t xattr_value_len, + int flags) { if (!evm_revalidate_status(xattr_name)) return; diff --git a/security/security.c b/security/security.c index f0d1f8f0da80..ab847af2c74d 100644 --- a/security/security.c +++ b/security/security.c @@ -2367,7 +2367,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return; call_void_hook(inode_post_setxattr, dentry, name, value, size, flags); - evm_inode_post_setxattr(dentry, name, value, size); + evm_inode_post_setxattr(dentry, name, value, size, flags); }
/**
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Change evm_inode_post_setxattr() definition, so that it can be registered as implementation of the inode_post_setxattr hook.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/evm.h | 8 +++++--- security/integrity/evm/evm_main.c | 4 +++- security/security.c | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
Add the idmap parameter to the definition, so that evm_inode_setattr() can be registered as this hook implementation.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- include/linux/lsm_hook_defs.h | 3 ++- security/security.c | 2 +- security/selinux/hooks.c | 3 ++- security/smack/smack_lsm.c | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 185924c56378..a843bb5f6f4b 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -135,7 +135,8 @@ LSM_HOOK(int, 0, inode_readlink, struct dentry *dentry) LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode, bool rcu) LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask) -LSM_HOOK(int, 0, inode_setattr, struct dentry *dentry, struct iattr *attr) +LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) LSM_HOOK(int, 0, inode_getattr, const struct path *path) LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap, struct dentry *dentry, const char *name, const void *value, diff --git a/security/security.c b/security/security.c index ab847af2c74d..1d332e294704 100644 --- a/security/security.c +++ b/security/security.c @@ -2215,7 +2215,7 @@ int security_inode_setattr(struct mnt_idmap *idmap,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; - ret = call_int_hook(inode_setattr, 0, dentry, attr); + ret = call_int_hook(inode_setattr, 0, idmap, dentry, attr); if (ret) return ret; return evm_inode_setattr(idmap, dentry, attr); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 179540441115..dae53d7778bb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3128,7 +3128,8 @@ static int selinux_inode_permission(struct inode *inode, int mask) return rc; }
-static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) +static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *iattr) { const struct cred *cred = current_cred(); struct inode *inode = d_backing_inode(dentry); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c126f6a16de4..9b75216d4a58 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1232,12 +1232,14 @@ static int smack_inode_permission(struct inode *inode, int mask)
/** * smack_inode_setattr - Smack check for setting attributes + * @idmap: idmap of the mount * @dentry: the object * @iattr: for the force flag * * Returns 0 if access is permitted, an error code otherwise */ -static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) +static int smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *iattr) { struct smk_audit_info ad; int rc;
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Add the idmap parameter to the definition, so that evm_inode_setattr() can be registered as this hook implementation.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
include/linux/lsm_hook_defs.h | 3 ++- security/security.c | 2 +- security/selinux/hooks.c | 3 ++- security/smack/smack_lsm.c | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com --- fs/attr.c | 1 + include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 7 +++++++ security/security.c | 16 ++++++++++++++++ 4 files changed, 26 insertions(+)
diff --git a/fs/attr.c b/fs/attr.c index 498e673bdf06..221d2bb0a906 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -502,6 +502,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) { fsnotify_change(dentry, ia_valid); + security_inode_post_setattr(idmap, dentry, ia_valid); ima_inode_post_setattr(idmap, dentry, ia_valid); evm_inode_post_setattr(idmap, dentry, ia_valid); } diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index a843bb5f6f4b..e1731d0b63f8 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -137,6 +137,8 @@ LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode, LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask) LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) +LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap, + struct dentry *dentry, int ia_valid) LSM_HOOK(int, 0, inode_getattr, const struct path *path) LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap, struct dentry *dentry, const char *name, const void *value, diff --git a/include/linux/security.h b/include/linux/security.h index d0eb20f90b26..56c841aa3994 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -361,6 +361,8 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode, int security_inode_permission(struct inode *inode, int mask); int security_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); +void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + int ia_valid); int security_inode_getattr(const struct path *path); int security_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name, @@ -879,6 +881,11 @@ static inline int security_inode_setattr(struct mnt_idmap *idmap, return 0; }
+static inline void +security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + int ia_valid) +{ } + static inline int security_inode_getattr(const struct path *path) { return 0; diff --git a/security/security.c b/security/security.c index 1d332e294704..06ffe0bcf711 100644 --- a/security/security.c +++ b/security/security.c @@ -2222,6 +2222,22 @@ int security_inode_setattr(struct mnt_idmap *idmap, } EXPORT_SYMBOL_GPL(security_inode_setattr);
+/** + * security_inode_post_setattr() - Update the inode after a setattr operation + * @idmap: idmap of the mount + * @dentry: file + * @ia_valid: file attributes set + * + * Update inode security field after successful setting file attributes. + */ +void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + int ia_valid) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return; + call_void_hook(inode_post_setattr, idmap, dentry, ia_valid); +} + /** * security_inode_getattr() - Check if getting file attributes is allowed * @path: file
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the inode_post_setattr hook.
At inode_setattr hook, EVM verifies the file's existing HMAC value. At inode_post_setattr, EVM re-calculates the file's HMAC based on the modified file attributes and other file metadata.
Other LSMs could similarly take some action after successful file attribute change.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com
fs/attr.c | 1 + include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 7 +++++++ security/security.c | 16 ++++++++++++++++ 4 files changed, 26 insertions(+)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:17:54PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com
fs/attr.c | 1 +
Acked-by: Christian Brauner brauner@kernel.org
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the inode_post_removexattr hook.
At inode_removexattr hook, EVM verifies the file's existing HMAC value. At inode_post_removexattr, EVM re-calculates the file's HMAC with the passed xattr removed and other file metadata.
Other LSMs could similarly take some action after successful xattr removal.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com --- fs/xattr.c | 9 +++++---- include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 5 +++++ security/security.c | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/fs/xattr.c b/fs/xattr.c index 09d927603433..f891c260a971 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -552,11 +552,12 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap, goto out;
error = __vfs_removexattr(idmap, dentry, name); + if (error) + return error;
- if (!error) { - fsnotify_xattr(dentry); - evm_inode_post_removexattr(dentry, name); - } + fsnotify_xattr(dentry); + security_inode_post_removexattr(dentry, name); + evm_inode_post_removexattr(dentry, name);
out: return error; diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index e1731d0b63f8..fbca96523c18 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -149,6 +149,8 @@ LSM_HOOK(int, 0, inode_getxattr, struct dentry *dentry, const char *name) LSM_HOOK(int, 0, inode_listxattr, struct dentry *dentry) LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap, struct dentry *dentry, const char *name) +LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry, + const char *name) LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl) LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap, diff --git a/include/linux/security.h b/include/linux/security.h index 56c841aa3994..84ae03690340 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -380,6 +380,7 @@ int security_inode_getxattr(struct dentry *dentry, const char *name); int security_inode_listxattr(struct dentry *dentry); int security_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name); +void security_inode_post_removexattr(struct dentry *dentry, const char *name); int security_inode_need_killpriv(struct dentry *dentry); int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry); int security_inode_getsecurity(struct mnt_idmap *idmap, @@ -942,6 +943,10 @@ static inline int security_inode_removexattr(struct mnt_idmap *idmap, return cap_inode_removexattr(idmap, dentry, name); }
+static inline void security_inode_post_removexattr(struct dentry *dentry, + const char *name) +{ } + static inline int security_inode_need_killpriv(struct dentry *dentry) { return cap_inode_need_killpriv(dentry); diff --git a/security/security.c b/security/security.c index 06ffe0bcf711..d9d2636104db 100644 --- a/security/security.c +++ b/security/security.c @@ -2452,6 +2452,20 @@ int security_inode_removexattr(struct mnt_idmap *idmap, return evm_inode_removexattr(idmap, dentry, name); }
+/** + * security_inode_post_removexattr() - Update the inode after a removexattr op + * @dentry: file + * @name: xattr name + * + * Update the inode after a successful removexattr operation. + */ +void security_inode_post_removexattr(struct dentry *dentry, const char *name) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return; + call_void_hook(inode_post_removexattr, dentry, name); +} + /** * security_inode_need_killpriv() - Check if security_inode_killpriv() required * @dentry: associated dentry
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the inode_post_removexattr hook.
At inode_removexattr hook, EVM verifies the file's existing HMAC value. At inode_post_removexattr, EVM re-calculates the file's HMAC with the passed xattr removed and other file metadata.
Other LSMs could similarly take some action after successful xattr removal.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
fs/xattr.c | 9 +++++---- include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 5 +++++ security/security.c | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:17:55PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the inode_post_removexattr hook.
At inode_removexattr hook, EVM verifies the file's existing HMAC value. At inode_post_removexattr, EVM re-calculates the file's HMAC with the passed xattr removed and other file metadata.
Other LSMs could similarly take some action after successful xattr removal.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
fs/xattr.c | 9 +++++----
Acked-by: Christian Brauner brauner@kernel.org
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- fs/namei.c | 2 ++ fs/nfsd/vfs.c | 6 ++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 17 +++++++++++++++++ 5 files changed, 32 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index 71c13b2990b4..fb93d3e13df6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd, error = may_open(idmap, &nd->path, acc_mode, open_flag); if (!error && !(file->f_mode & FMODE_OPENED)) error = vfs_open(&nd->path, file); + if (!error) + error = security_file_post_open(file, op->acc_mode); if (!error) error = ima_file_check(file, op->acc_mode); if (!error && do_truncate) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index fbbea7498f02..b0c3f07a8bba 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -877,6 +877,12 @@ __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, goto out; }
+ host_err = security_file_post_open(file, may_flags); + if (host_err) { + fput(file); + goto out; + } + host_err = ima_file_check(file, may_flags); if (host_err) { fput(file); diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index fbca96523c18..c3fecc7dcb0b 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -191,6 +191,7 @@ LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk, struct fown_struct *fown, int sig) LSM_HOOK(int, 0, file_receive, struct file *file) LSM_HOOK(int, 0, file_open, struct file *file) +LSM_HOOK(int, 0, file_post_open, struct file *file, int mask) LSM_HOOK(int, 0, file_truncate, struct file *file) LSM_HOOK(int, 0, task_alloc, struct task_struct *task, unsigned long clone_flags) diff --git a/include/linux/security.h b/include/linux/security.h index 84ae03690340..97f2212c13b6 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -411,6 +411,7 @@ int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig); int security_file_receive(struct file *file); int security_file_open(struct file *file); +int security_file_post_open(struct file *file, int mask); int security_file_truncate(struct file *file); int security_task_alloc(struct task_struct *task, unsigned long clone_flags); void security_task_free(struct task_struct *task); @@ -1074,6 +1075,11 @@ static inline int security_file_open(struct file *file) return 0; }
+static inline int security_file_post_open(struct file *file, int mask) +{ + return 0; +} + static inline int security_file_truncate(struct file *file) { return 0; diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); }
+/** + * security_file_post_open() - Evaluate a file after it has been opened + * @file: the file + * @mask: access mask + * + * Evaluate an opened file and the access mask requested with open(). The hook + * is useful for LSMs that require the file content to be available in order to + * make decisions. + * + * Return: Returns 0 if permission is granted. + */ +int security_file_post_open(struct file *file, int mask) +{ + return call_int_hook(file_post_open, 0, file, mask); +} +EXPORT_SYMBOL_GPL(security_file_post_open); + /** * security_file_truncate() - Check if truncating a file is allowed * @file: file
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation to move IMA and EVM to the LSM infrastructure, introduce the file_post_open hook. Also, export security_file_post_open() for NFS.
Based on policy, IMA calculates the digest of the file content and extends the TPM with the digest, verifies the file's integrity based on the digest, and/or includes the file digest in the audit log.
LSMs could similarly take action depending on the file content and the access mask requested with open().
The new hook returns a value and can cause the open to be aborted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++ fs/nfsd/vfs.c | 6 ++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 17 +++++++++++++++++ 5 files changed, 32 insertions(+)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++
Acked-by: Christian Brauner brauner@kernel.org
On Fri, Feb 09, 2024 at 10:56:33AM +0100, Christian Brauner wrote:
On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++
Acked-by: Christian Brauner brauner@kernel.org
Redacting that. I have a question.
On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++ fs/nfsd/vfs.c | 6 ++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 17 +++++++++++++++++ 5 files changed, 32 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index 71c13b2990b4..fb93d3e13df6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd, error = may_open(idmap, &nd->path, acc_mode, open_flag); if (!error && !(file->f_mode & FMODE_OPENED)) error = vfs_open(&nd->path, file);
- if (!error)
error = security_file_post_open(file, op->acc_mode);
What does it do for O_CREAT? IOW, we managed to create that thing and we managed to open that thing. Can security_file_post_open() and ima_file_check() fail afterwards even for newly created files?
On Fri, 2024-02-09 at 11:12 +0100, Christian Brauner wrote:
On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++ fs/nfsd/vfs.c | 6 ++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 17 +++++++++++++++++ 5 files changed, 32 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index 71c13b2990b4..fb93d3e13df6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd, error = may_open(idmap, &nd->path, acc_mode, open_flag); if (!error && !(file->f_mode & FMODE_OPENED)) error = vfs_open(&nd->path, file);
- if (!error)
error = security_file_post_open(file, op->acc_mode);
What does it do for O_CREAT? IOW, we managed to create that thing and we managed to open that thing. Can security_file_post_open() and ima_file_check() fail afterwards even for newly created files?
$ strace touch test-file ... openat(AT_FDCWD, "test-file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 EPERM (Operation not permitted)
The open fails, but the file is there. I didn't see warnings/errors in the kernel log.
Roberto
On Fri, Feb 09, 2024 at 11:46:16AM +0100, Roberto Sassu wrote:
On Fri, 2024-02-09 at 11:12 +0100, Christian Brauner wrote:
On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++ fs/nfsd/vfs.c | 6 ++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 17 +++++++++++++++++ 5 files changed, 32 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index 71c13b2990b4..fb93d3e13df6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd, error = may_open(idmap, &nd->path, acc_mode, open_flag); if (!error && !(file->f_mode & FMODE_OPENED)) error = vfs_open(&nd->path, file);
- if (!error)
error = security_file_post_open(file, op->acc_mode);
What does it do for O_CREAT? IOW, we managed to create that thing and we managed to open that thing. Can security_file_post_open() and ima_file_check() fail afterwards even for newly created files?
$ strace touch test-file ... openat(AT_FDCWD, "test-file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 EPERM (Operation not permitted)
Ah, meh. I was hoping IMA just wouldn't care about this case.
On Fri, 2024-02-09 at 12:34 +0100, Christian Brauner wrote:
On Fri, Feb 09, 2024 at 11:46:16AM +0100, Roberto Sassu wrote:
On Fri, 2024-02-09 at 11:12 +0100, Christian Brauner wrote:
On Mon, Jan 15, 2024 at 07:17:56PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 2 ++ fs/nfsd/vfs.c | 6 ++++++ include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ security/security.c | 17 +++++++++++++++++ 5 files changed, 32 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index 71c13b2990b4..fb93d3e13df6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3620,6 +3620,8 @@ static int do_open(struct nameidata *nd, error = may_open(idmap, &nd->path, acc_mode, open_flag); if (!error && !(file->f_mode & FMODE_OPENED)) error = vfs_open(&nd->path, file);
- if (!error)
error = security_file_post_open(file, op->acc_mode);
What does it do for O_CREAT? IOW, we managed to create that thing and we managed to open that thing. Can security_file_post_open() and ima_file_check() fail afterwards even for newly created files?
$ strace touch test-file ... openat(AT_FDCWD, "test-file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 EPERM (Operation not permitted)
Ah, meh. I was hoping IMA just wouldn't care about this case.
Actually it doesn't. I added code to artifically create the situation (to see what happens if a new LSM does that).
Roberto
Hi Roberto,
diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
}
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
thanks,
Mimi
On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar zohar@linux.ibm.com wrote:
Hi Roberto,
diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
}
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
I take it this means you want to pull this via the IMA/EVM tree?
On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar zohar@linux.ibm.com wrote:
Hi Roberto,
diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
}
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
I take it this means you want to pull this via the IMA/EVM tree?
Not sure about that, but I have enough changes to do to make a v10.
Roberto
On Tue, Feb 13, 2024 at 7:59 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar zohar@linux.ibm.com wrote:
Hi Roberto,
diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
}
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
I take it this means you want to pull this via the IMA/EVM tree?
Not sure about that, but I have enough changes to do to make a v10.
Sorry, I should have been more clear, the point I was trying to resolve was who was going to take this patchset (eventually). There are other patches destined for the LSM tree that touch the LSM hooks in a way which will cause conflicts with this patchset, and if you/Mimi are going to take this via the IMA/EVM tree - which is fine with me - I need to take that into account when merging things in the LSM tree during this cycle. It's not a big deal either way, it would just be nice to get an answer on that within the next week.
On Tue, 2024-02-13 at 10:33 -0500, Paul Moore wrote:
On Tue, Feb 13, 2024 at 7:59 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar zohar@linux.ibm.com wrote:
Hi Roberto,
diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
}
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
I take it this means you want to pull this via the IMA/EVM tree?
Not sure about that, but I have enough changes to do to make a v10.
@Roberto: please add my "Reviewed-by" to the remaining patches.
Sorry, I should have been more clear, the point I was trying to resolve was who was going to take this patchset (eventually). There are other patches destined for the LSM tree that touch the LSM hooks in a way which will cause conflicts with this patchset, and if you/Mimi are going to take this via the IMA/EVM tree - which is fine with me - I need to take that into account when merging things in the LSM tree during this cycle. It's not a big deal either way, it would just be nice to get an answer on that within the next week.
Similarly there are other changes for IMA and EVM. If you're willing to create a topic branch for just the v10 patch set that can be merged into your tree and into my tree, I'm fine with your upstreaming v10. (I'll wait to send my pull request after yours.) Roberto will add my Ack's to the integrity, IMA, and EVM related patches. However if you're not willing to create a topic branch, I'll upstream the v10 patch set.
thanks,
Mimi
On Wed, Feb 14, 2024 at 3:07 PM Mimi Zohar zohar@linux.ibm.com wrote:
On Tue, 2024-02-13 at 10:33 -0500, Paul Moore wrote:
On Tue, Feb 13, 2024 at 7:59 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar zohar@linux.ibm.com wrote:
Hi Roberto,
diff --git a/security/security.c b/security/security.c index d9d2636104db..f3d92bffd02f 100644 --- a/security/security.c +++ b/security/security.c @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
}
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
I take it this means you want to pull this via the IMA/EVM tree?
Not sure about that, but I have enough changes to do to make a v10.
@Roberto: please add my "Reviewed-by" to the remaining patches.
Sorry, I should have been more clear, the point I was trying to resolve was who was going to take this patchset (eventually). There are other patches destined for the LSM tree that touch the LSM hooks in a way which will cause conflicts with this patchset, and if you/Mimi are going to take this via the IMA/EVM tree - which is fine with me - I need to take that into account when merging things in the LSM tree during this cycle. It's not a big deal either way, it would just be nice to get an answer on that within the next week.
Similarly there are other changes for IMA and EVM. If you're willing to create a topic branch for just the v10 patch set that can be merged into your tree and into my tree, I'm fine with your upstreaming v10. (I'll wait to send my pull request after yours.) Roberto will add my Ack's to the integrity, IMA, and EVM related patches. However if you're not willing to create a topic branch, I'll upstream the v10 patch set.
I'm not a big fan of sharing topic branches across different subsystem trees, I'd much rather just agree that one tree or another takes the patchset and the others plan accordingly. Based on our previous discussions I was under the impression that you wanted me to merge this patchset into lsm/dev, but it looks like that is no longer the case - which is okay by me.
Assuming Roberto gets a v10 out soon, do you expect to merge the v10 patchset and send it up during the upcoming merge window (for v6.9), or are you expecting to wait until after the upcoming merge window closes and target v6.10? Once again, either is fine, I'm just trying to coordinate this with other patches.
On Wed, 2024-02-14 at 16:21 -0500, Paul Moore wrote:
On Wed, Feb 14, 2024 at 3:07 PM Mimi Zohar zohar@linux.ibm.com wrote:
On Tue, 2024-02-13 at 10:33 -0500, Paul Moore wrote:
On Tue, Feb 13, 2024 at 7:59 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Mon, 2024-02-12 at 16:16 -0500, Paul Moore wrote:
On Mon, Feb 12, 2024 at 4:06 PM Mimi Zohar zohar@linux.ibm.com wrote:
Hi Roberto,
> diff --git a/security/security.c b/security/security.c > index d9d2636104db..f3d92bffd02f 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -2972,6 +2972,23 @@ int security_file_open(struct file *file) > return fsnotify_perm(file, MAY_OPEN); <=== Conflict
Replace with "return fsnotify_open_perm(file);"
> } >
The patch set doesn't apply cleaning to 6.8-rcX without this change. Unless there are other issues, I can make the change.
I take it this means you want to pull this via the IMA/EVM tree?
Not sure about that, but I have enough changes to do to make a v10.
@Roberto: please add my "Reviewed-by" to the remaining patches.
Sorry, I should have been more clear, the point I was trying to resolve was who was going to take this patchset (eventually). There are other patches destined for the LSM tree that touch the LSM hooks in a way which will cause conflicts with this patchset, and if you/Mimi are going to take this via the IMA/EVM tree - which is fine with me - I need to take that into account when merging things in the LSM tree during this cycle. It's not a big deal either way, it would just be nice to get an answer on that within the next week.
Similarly there are other changes for IMA and EVM. If you're willing to create a topic branch for just the v10 patch set that can be merged into your tree and into my tree, I'm fine with your upstreaming v10. (I'll wait to send my pull request after yours.) Roberto will add my Ack's to the integrity, IMA, and EVM related patches. However if you're not willing to create a topic branch, I'll upstream the v10 patch set.
I'm not a big fan of sharing topic branches across different subsystem trees, I'd much rather just agree that one tree or another takes the patchset and the others plan accordingly.
Just curious why not?
Based on our previous discussions I was under the impression that you wanted me to merge this patchset into lsm/dev, but it looks like that is no longer the case - which is okay by me.
Paul, I don't recall saying that. Please go ahead and upstream it. Roberto can add my acks accordingly.
Mimi
Assuming Roberto gets a v10 out soon, do you expect to merge the v10 patchset and send it up during the upcoming merge window (for v6.9), or are you expecting to wait until after the upcoming merge window closes and target v6.10? Once again, either is fine, I'm just trying to coordinate this with other patches.
On Thu, Feb 15, 2024 at 3:18 AM Mimi Zohar zohar@linux.ibm.com wrote:
On Wed, 2024-02-14 at 16:21 -0500, Paul Moore wrote:
I'm not a big fan of sharing topic branches across different subsystem trees, I'd much rather just agree that one tree or another takes the patchset and the others plan accordingly.
Just curious why not?
I don't like the idea of cross-tree dependencies, I realize the term "dependency" isn't a great fit for a shared topic branch - no one needs to feel the need to explain how pulls and merges work - but it's the conceptual idea of there being a dependency across different trees that bothers me. I also tend to dislike the idea that a new feature *absolutely* *must* *be* *in* *a* *certain* *release* to the point that we need to subvert our normal processes to make it happen.
Further, I believe that shared topic branches also discourages cooperation and collaboration. With a topic branch, anyone who wants to build on top of it simply merges the topic branch and off they go; without a shared topic branch there needs to be a discussion about which other patches are affected, which trees are involved, who is going to carry the patches, when are they going up to Linus, etc. As someone who feels strongly that we need more collaboration across kernel subsystems, I'm always going to pick the option that involves developers talking with other developers outside their immediate subsystem.
Hopefully that makes sense.
Based on our previous discussions I was under the impression that you wanted me to merge this patchset into lsm/dev, but it looks like that is no longer the case - which is okay by me.
Paul, I don't recall saying that. Please go ahead and upstream it. Roberto can add my acks accordingly.
I believe it was during an off-list chat when we were discussing an earlier revision of the patchset, however, as I said earlier I'm not bothered by who merges the patches, as long as they eventually end up in Linus' tree I'm happy :) I *really* want to stress that last bit, if you and Roberto have stuff queued for the IMA/EVM tree that depends on this patchset, please go ahead and merge it; you've got my ACKs on the patches that need them, and I believe I've reviewed most of the other patches that don't require my ACK. While there are a some LSM related patches that would sit on top of this patchset, there is nothing that is so critical that it must go in now.
If I don't hear anything back from you, I'll go ahead and merge these into lsm/dev later tonight (probably in about ~12 hours from this email as I have some personal commitments early this evening) just so we can get them into linux-next as soon as possible.
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- fs/file_table.c | 1 + include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 4 ++++ security/security.c | 11 +++++++++++ 4 files changed, 17 insertions(+)
diff --git a/fs/file_table.c b/fs/file_table.c index de4a2915bfd4..c72dc75f2bd3 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -385,6 +385,7 @@ static void __fput(struct file *file) eventpoll_release(file); locks_remove_file(file);
+ security_file_release(file); ima_file_free(file); if (unlikely(file->f_flags & FASYNC)) { if (file->f_op->fasync) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index c3fecc7dcb0b..229f84ce12ae 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -173,6 +173,7 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir, struct kernfs_node *kn) LSM_HOOK(int, 0, file_permission, struct file *file, int mask) LSM_HOOK(int, 0, file_alloc_security, struct file *file) +LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file) LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, unsigned long arg) diff --git a/include/linux/security.h b/include/linux/security.h index 97f2212c13b6..2997348afcb7 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -395,6 +395,7 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir, struct kernfs_node *kn); int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); +void security_file_release(struct file *file); void security_file_free(struct file *file); int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int security_file_ioctl_compat(struct file *file, unsigned int cmd, @@ -1008,6 +1009,9 @@ static inline int security_file_alloc(struct file *file) return 0; }
+static inline void security_file_release(struct file *file) +{ } + static inline void security_file_free(struct file *file) { }
diff --git a/security/security.c b/security/security.c index f3d92bffd02f..7d10724872f8 100644 --- a/security/security.c +++ b/security/security.c @@ -2724,6 +2724,17 @@ int security_file_alloc(struct file *file) return rc; }
+/** + * security_file_release() - Perform actions before releasing the file ref + * @file: the file + * + * Perform actions before releasing the last reference to a file. + */ +void security_file_release(struct file *file) +{ + call_void_hook(file_release, file); +} + /** * security_file_free() - Free a file's LSM blob * @file: the file
On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
Elaborate that last part, please.
On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
Elaborate that last part, please.
Apologies, I didn't understand that either. Casey?
Thanks
Roberto
On 1/16/2024 12:47 AM, Roberto Sassu wrote:
On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
Elaborate that last part, please.
Apologies, I didn't understand that either. Casey?
Just a hypothetical notion that if an LSM wanted to implement an exclusive access scheme it might find the proposed hook helpful. I don't have any plan to create such a scheme, nor do I think that a file_release hook would be the only thing you'd need.
Thanks
Roberto
On Tue, Jan 16, 2024 at 08:51:11AM -0800, Casey Schaufler wrote:
On 1/16/2024 12:47 AM, Roberto Sassu wrote:
On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
Elaborate that last part, please.
Apologies, I didn't understand that either. Casey?
Just a hypothetical notion that if an LSM wanted to implement an exclusive access scheme it might find the proposed hook helpful. I don't have any plan to create such a scheme, nor do I think that a file_release hook would be the only thing you'd need.
Exclusive access to what? "No more than one opened file with this inode at a time"? It won't serialize IO operations, obviously... Details, please.
On 1/16/2024 9:33 AM, Al Viro wrote:
On Tue, Jan 16, 2024 at 08:51:11AM -0800, Casey Schaufler wrote:
On 1/16/2024 12:47 AM, Roberto Sassu wrote:
On Mon, 2024-01-15 at 19:15 +0000, Al Viro wrote:
On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
Elaborate that last part, please.
Apologies, I didn't understand that either. Casey?
Just a hypothetical notion that if an LSM wanted to implement an exclusive access scheme it might find the proposed hook helpful. I don't have any plan to create such a scheme, nor do I think that a file_release hook would be the only thing you'd need.
Exclusive access to what? "No more than one opened file with this inode at a time"? It won't serialize IO operations, obviously... Details, please.
Once a file is opened it can't be opened again until it is closed. That's the simple description, which ignores all sorts of cases. I wouldn't want my system to behave that way, but I have heard arguments that multiple concurrent opens can be a security issue. In the context of my review of the code in question I included the comment solely for the purpose of acknowledging the potential for additional uses of the proposed hook. It's entirely possible someone (not me!) would use the hook in this or some other "clever" way.
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
Let's drop the above sentence as it is is a little vague and is causing some concern with the VFS folks. While I want to see the hooks explained and documented in the code, I've never been a big fan of speculating about potential future uses of the hook, that's dangerous IMO.
Otherwise this looks good.
Acked-by: Paul Moore paul@paul-moore.com
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
fs/file_table.c | 1 + include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 4 ++++ security/security.c | 11 +++++++++++ 4 files changed, 17 insertions(+)
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:17:57PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
fs/file_table.c | 1 + include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 4 ++++ security/security.c | 11 +++++++++++ 4 files changed, 17 insertions(+)
diff --git a/fs/file_table.c b/fs/file_table.c index de4a2915bfd4..c72dc75f2bd3 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -385,6 +385,7 @@ static void __fput(struct file *file) eventpoll_release(file); locks_remove_file(file);
- security_file_release(file); ima_file_free(file);
This has always been an extremely dicy hook in here and that's caused us issues before for stacking filesystems so I'm not enthusiastic about exposing this to all LSMs. So reluctantly,
Acked-by: Christian Brauner brauner@kernel.org
On 1/15/24 13:17, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the file_release hook.
IMA calculates at file close the new digest of the file content and writes it to security.ima, so that appraisal at next file access succeeds.
An LSM could implement an exclusive access scheme for files, only allowing access to files that have no references.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
fs/file_table.c | 1 + include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 4 ++++ security/security.c | 11 +++++++++++ 4 files changed, 17 insertions(+)
diff --git a/fs/file_table.c b/fs/file_table.c index de4a2915bfd4..c72dc75f2bd3 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -385,6 +385,7 @@ static void __fput(struct file *file) eventpoll_release(file); locks_remove_file(file);
- security_file_release(file); ima_file_free(file); if (unlikely(file->f_flags & FASYNC)) { if (file->f_op->fasync)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index c3fecc7dcb0b..229f84ce12ae 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -173,6 +173,7 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir, struct kernfs_node *kn) LSM_HOOK(int, 0, file_permission, struct file *file, int mask) LSM_HOOK(int, 0, file_alloc_security, struct file *file) +LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file) LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, unsigned long arg) diff --git a/include/linux/security.h b/include/linux/security.h index 97f2212c13b6..2997348afcb7 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -395,6 +395,7 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir, struct kernfs_node *kn); int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); +void security_file_release(struct file *file); void security_file_free(struct file *file); int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int security_file_ioctl_compat(struct file *file, unsigned int cmd, @@ -1008,6 +1009,9 @@ static inline int security_file_alloc(struct file *file) return 0; } +static inline void security_file_release(struct file *file) +{ }
- static inline void security_file_free(struct file *file) { }
diff --git a/security/security.c b/security/security.c index f3d92bffd02f..7d10724872f8 100644 --- a/security/security.c +++ b/security/security.c @@ -2724,6 +2724,17 @@ int security_file_alloc(struct file *file) return rc; } +/**
- security_file_release() - Perform actions before releasing the file ref
- @file: the file
- Perform actions before releasing the last reference to a file.
- */
+void security_file_release(struct file *file) +{
- call_void_hook(file_release, file);
+}
- /**
- security_file_free() - Free a file's LSM blob
- @file: the file
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- fs/namei.c | 5 +++++ include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 5 +++++ security/security.c | 14 ++++++++++++++ 4 files changed, 26 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index fb93d3e13df6..b7f433720b1e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4047,6 +4047,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, dentry, mode, 0); break; } + + if (error) + goto out2; + + security_path_post_mknod(idmap, dentry); out2: done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 229f84ce12ae..e08b9091350d 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -94,6 +94,8 @@ LSM_HOOK(int, 0, path_mkdir, const struct path *dir, struct dentry *dentry, LSM_HOOK(int, 0, path_rmdir, const struct path *dir, struct dentry *dentry) LSM_HOOK(int, 0, path_mknod, const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev) +LSM_HOOK(void, LSM_RET_VOID, path_post_mknod, struct mnt_idmap *idmap, + struct dentry *dentry) LSM_HOOK(int, 0, path_truncate, const struct path *path) LSM_HOOK(int, 0, path_symlink, const struct path *dir, struct dentry *dentry, const char *old_name) diff --git a/include/linux/security.h b/include/linux/security.h index 2997348afcb7..977dd9f7f51a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1893,6 +1893,7 @@ int security_path_mkdir(const struct path *dir, struct dentry *dentry, umode_t m int security_path_rmdir(const struct path *dir, struct dentry *dentry); int security_path_mknod(const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev); +void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry); int security_path_truncate(const struct path *path); int security_path_symlink(const struct path *dir, struct dentry *dentry, const char *old_name); @@ -1927,6 +1928,10 @@ static inline int security_path_mknod(const struct path *dir, struct dentry *den return 0; }
+static inline void security_path_post_mknod(struct mnt_idmap *idmap, + struct dentry *dentry) +{ } + static inline int security_path_truncate(const struct path *path) { return 0; diff --git a/security/security.c b/security/security.c index 7d10724872f8..750bfe2768d5 100644 --- a/security/security.c +++ b/security/security.c @@ -1800,6 +1800,20 @@ int security_path_mknod(const struct path *dir, struct dentry *dentry, } EXPORT_SYMBOL(security_path_mknod);
+/** + * security_path_post_mknod() - Update inode security field after file creation + * @idmap: idmap of the mount + * @dentry: new file + * + * Update inode security field after a file has been created. + */ +void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return; + call_void_hook(path_post_mknod, idmap, dentry); +} + /** * security_path_mkdir() - Check if creating a new directory is allowed * @dir: parent directory
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the path_post_mknod hook.
IMA-appraisal requires all existing files in policy to have a file hash/signature stored in security.ima. An exception is made for empty files created by mknod, by tagging them as new files.
LSMs could also take some action after files are created.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 5 +++++ include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 5 +++++ security/security.c | 14 ++++++++++++++ 4 files changed, 26 insertions(+)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:17:58PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 5 +++++
Acked-by: Christian Brauner brauner@kernel.org
On 1/15/24 13:17, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
fs/namei.c | 5 +++++ include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 5 +++++ security/security.c | 14 ++++++++++++++ 4 files changed, 26 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c index fb93d3e13df6..b7f433720b1e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4047,6 +4047,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, dentry, mode, 0); break; }
- if (error)
goto out2;
- security_path_post_mknod(idmap, dentry); out2: done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) {
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 229f84ce12ae..e08b9091350d 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -94,6 +94,8 @@ LSM_HOOK(int, 0, path_mkdir, const struct path *dir, struct dentry *dentry, LSM_HOOK(int, 0, path_rmdir, const struct path *dir, struct dentry *dentry) LSM_HOOK(int, 0, path_mknod, const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev) +LSM_HOOK(void, LSM_RET_VOID, path_post_mknod, struct mnt_idmap *idmap,
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)struct dentry *dentry)
diff --git a/include/linux/security.h b/include/linux/security.h index 2997348afcb7..977dd9f7f51a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1893,6 +1893,7 @@ int security_path_mkdir(const struct path *dir, struct dentry *dentry, umode_t m int security_path_rmdir(const struct path *dir, struct dentry *dentry); int security_path_mknod(const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev); +void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry); int security_path_truncate(const struct path *path); int security_path_symlink(const struct path *dir, struct dentry *dentry, const char *old_name); @@ -1927,6 +1928,10 @@ static inline int security_path_mknod(const struct path *dir, struct dentry *den return 0; } +static inline void security_path_post_mknod(struct mnt_idmap *idmap,
struct dentry *dentry)
+{ }
- static inline int security_path_truncate(const struct path *path) { return 0;
diff --git a/security/security.c b/security/security.c index 7d10724872f8..750bfe2768d5 100644 --- a/security/security.c +++ b/security/security.c @@ -1800,6 +1800,20 @@ int security_path_mknod(const struct path *dir, struct dentry *dentry, } EXPORT_SYMBOL(security_path_mknod); +/**
- security_path_post_mknod() - Update inode security field after file creation
- @idmap: idmap of the mount
- @dentry: new file
- Update inode security field after a file has been created.
- */
+void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry) +{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return;
- call_void_hook(path_post_mknod, idmap, dentry);
+}
- /**
- security_path_mkdir() - Check if creating a new directory is allowed
- @dir: parent directory
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- 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 e08b9091350d..5f90914d23e0 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 977dd9f7f51a..1cb604282617 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); @@ -811,6 +813,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 750bfe2768d5..5bc7edc22923 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
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
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(+)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:17:59PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/namei.c | 1 +
Acked-by: Christian Brauner brauner@kernel.org
On 1/15/24 13:17, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
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 e08b9091350d..5f90914d23e0 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,
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)struct inode *inode)
diff --git a/include/linux/security.h b/include/linux/security.h index 977dd9f7f51a..1cb604282617 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,
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);struct inode *inode);
@@ -811,6 +813,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 750bfe2768d5..5bc7edc22923 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
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- fs/posix_acl.c | 1 + include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 7 +++++++ security/security.c | 17 +++++++++++++++++ 4 files changed, 27 insertions(+)
diff --git a/fs/posix_acl.c b/fs/posix_acl.c index a05fe94970ce..58e3c1e2fbbc 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -1137,6 +1137,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, error = -EIO; if (!error) { fsnotify_xattr(dentry); + security_inode_post_set_acl(dentry, acl_name, kacl); evm_inode_post_set_acl(dentry, acl_name, kacl); }
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 5f90914d23e0..e1a9a4e0760c 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -157,6 +157,8 @@ LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry, const char *name) LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl) +LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry, + const char *acl_name, struct posix_acl *kacl) LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name) LSM_HOOK(int, 0, inode_remove_acl, struct mnt_idmap *idmap, diff --git a/include/linux/security.h b/include/linux/security.h index 1cb604282617..c372797e1617 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -372,6 +372,8 @@ int security_inode_setxattr(struct mnt_idmap *idmap, int security_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl); +void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name, + struct posix_acl *kacl); int security_inode_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name); int security_inode_remove_acl(struct mnt_idmap *idmap, @@ -915,6 +917,11 @@ static inline int security_inode_set_acl(struct mnt_idmap *idmap, return 0; }
+static inline void security_inode_post_set_acl(struct dentry *dentry, + const char *acl_name, + struct posix_acl *kacl) +{ } + static inline int security_inode_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name) diff --git a/security/security.c b/security/security.c index 5bc7edc22923..82da3667c4ae 100644 --- a/security/security.c +++ b/security/security.c @@ -2350,6 +2350,23 @@ int security_inode_set_acl(struct mnt_idmap *idmap, return evm_inode_set_acl(idmap, dentry, acl_name, kacl); }
+/** + * security_inode_post_set_acl() - Update inode security from posix acls set + * @dentry: file + * @acl_name: acl name + * @kacl: acl struct + * + * Update inode security data after successfully setting posix acls on @dentry. + * The posix acls in @kacl are identified by @acl_name. + */ +void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name, + struct posix_acl *kacl) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return; + call_void_hook(inode_post_set_acl, dentry, acl_name, kacl); +} + /** * security_inode_get_acl() - Check if reading posix acls is allowed * @idmap: idmap of the mount
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the inode_post_set_acl hook.
At inode_set_acl hook, EVM verifies the file's existing HMAC value. At inode_post_set_acl, EVM re-calculates the file's HMAC based on the modified POSIX ACL and other file metadata.
Other LSMs could similarly take some action after successful POSIX ACL change.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/posix_acl.c | 1 + include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 7 +++++++ security/security.c | 17 +++++++++++++++++ 4 files changed, 27 insertions(+)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:18:00PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/posix_acl.c | 1 +
Acked-by: Christian Brauner brauner@kernel.org
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- fs/posix_acl.c | 1 + include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 8 ++++++++ security/security.c | 17 +++++++++++++++++ 4 files changed, 28 insertions(+)
diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 58e3c1e2fbbc..e3fbe1a9f3f5 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -1246,6 +1246,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, error = -EIO; if (!error) { fsnotify_xattr(dentry); + security_inode_post_remove_acl(idmap, dentry, acl_name); evm_inode_post_remove_acl(idmap, dentry, acl_name); }
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index e1a9a4e0760c..47dc633090a4 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -163,6 +163,8 @@ LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name) LSM_HOOK(int, 0, inode_remove_acl, struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name) +LSM_HOOK(void, LSM_RET_VOID, inode_post_remove_acl, struct mnt_idmap *idmap, + struct dentry *dentry, const char *acl_name) LSM_HOOK(int, 0, inode_need_killpriv, struct dentry *dentry) LSM_HOOK(int, 0, inode_killpriv, struct mnt_idmap *idmap, struct dentry *dentry) diff --git a/include/linux/security.h b/include/linux/security.h index c372797e1617..4b03c76b91f1 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -378,6 +378,9 @@ int security_inode_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name); int security_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name); +void security_inode_post_remove_acl(struct mnt_idmap *idmap, + struct dentry *dentry, + const char *acl_name); void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); int security_inode_getxattr(struct dentry *dentry, const char *name); @@ -936,6 +939,11 @@ static inline int security_inode_remove_acl(struct mnt_idmap *idmap, return 0; }
+static inline void security_inode_post_remove_acl(struct mnt_idmap *idmap, + struct dentry *dentry, + const char *acl_name) +{ } + static inline void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { } diff --git a/security/security.c b/security/security.c index 82da3667c4ae..617acfaa44d1 100644 --- a/security/security.c +++ b/security/security.c @@ -2413,6 +2413,23 @@ int security_inode_remove_acl(struct mnt_idmap *idmap, return evm_inode_remove_acl(idmap, dentry, acl_name); }
+/** + * security_inode_post_remove_acl() - Update inode security after rm posix acls + * @idmap: idmap of the mount + * @dentry: file + * @acl_name: acl name + * + * Update inode security data after successfully removing posix acls on + * @dentry in @idmap. The posix acls are identified by @acl_name. + */ +void security_inode_post_remove_acl(struct mnt_idmap *idmap, + struct dentry *dentry, const char *acl_name) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return; + call_void_hook(inode_post_remove_acl, idmap, dentry, acl_name); +} + /** * security_inode_post_setxattr() - Update the inode after a setxattr operation * @dentry: file
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for moving IMA and EVM to the LSM infrastructure, introduce the inode_post_remove_acl hook.
At inode_remove_acl hook, EVM verifies the file's existing HMAC value. At inode_post_remove_acl, EVM re-calculates the file's HMAC with the passed POSIX ACL removed and other file metadata.
Other LSMs could similarly take some action after successful POSIX ACL removal.
The new hook cannot return an error and cannot cause the operation to be reverted.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/posix_acl.c | 1 + include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 8 ++++++++ security/security.c | 17 +++++++++++++++++ 4 files changed, 28 insertions(+)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:18:01PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
fs/posix_acl.c | 1 +
Acked-by: Christian Brauner brauner@kernel.org
From: Roberto Sassu roberto.sassu@huawei.com
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- 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 47dc633090a4..f8c4ec008263 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -405,6 +405,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 4b03c76b91f1..8436f9abf43d 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2004,6 +2004,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
@@ -2031,6 +2034,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 617acfaa44d1..d2a1226e6e69 100644 --- a/security/security.c +++ b/security/security.c @@ -5424,6 +5424,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
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
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 roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Acked-by: Casey Schaufler casey@schaufler-ca.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
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(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for removing the 'integrity' LSM, move integrity_kernel_module_request() to IMA, and rename it to ima_kernel_module_request().
Compile it conditionally if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled, and call it from security.c (removed afterwards with the move of IMA to the LSM infrastructure).
Adding this hook cannot be avoided, since IMA has no control on the flags passed to crypto_alloc_sig() in public_key_verify_signature(), and thus cannot pass CRYPTO_NOLOAD, which solved the problem for EVM hashing with commit e2861fa71641 ("evm: Don't deadlock if a crypto algorithm is unavailable").
EVM alone does not need to implement this hook, first because there is no mutex to deadlock, and second because even if it had it, there should be a recursive call. However, since verification from EVM can be initiated only by setting inode metadata, deadlock would occur if modprobe would do the same while loading a kernel module (which is unlikely).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/ima.h | 10 +++++++++ include/linux/integrity.h | 13 ------------ security/integrity/digsig_asymmetric.c | 23 -------------------- security/integrity/ima/ima_main.c | 29 ++++++++++++++++++++++++++ security/security.c | 2 +- 5 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 31ef6c3c3207..0f9af283cbc8 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -256,4 +256,14 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func) return false; } #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */ + +#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) +extern int ima_kernel_module_request(char *kmod_name); +#else +static inline int ima_kernel_module_request(char *kmod_name) +{ + return 0; +} + +#endif #endif /* _LINUX_IMA_H */ diff --git a/include/linux/integrity.h b/include/linux/integrity.h index 2ea0f2f65ab6..ef0f63ef5ebc 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -42,17 +42,4 @@ static inline void integrity_load_keys(void) } #endif /* CONFIG_INTEGRITY */
-#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS - -extern int integrity_kernel_module_request(char *kmod_name); - -#else - -static inline int integrity_kernel_module_request(char *kmod_name) -{ - return 0; -} - -#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */ - #endif /* _LINUX_INTEGRITY_H */ diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 895f4b9ce8c6..de603cf42ac7 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -132,26 +132,3 @@ int asymmetric_verify(struct key *keyring, const char *sig, pr_debug("%s() = %d\n", __func__, ret); return ret; } - -/** - * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests - * @kmod_name: kernel module name - * - * We have situation, when public_key_verify_signature() in case of RSA - * algorithm use alg_name to store internal information in order to - * construct an algorithm on the fly, but crypto_larval_lookup() will try - * to use alg_name in order to load kernel module with same name. - * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, - * we are safe to fail such module request from crypto_larval_lookup(). - * - * In this way we prevent modprobe execution during digsig verification - * and avoid possible deadlock if modprobe and/or it's dependencies - * also signed with digsig. - */ -int integrity_kernel_module_request(char *kmod_name) -{ - if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) - return -EINVAL; - - return 0; -} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 02021ee467d3..908fa026ec58 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1091,6 +1091,35 @@ int ima_measure_critical_data(const char *event_label, } EXPORT_SYMBOL_GPL(ima_measure_critical_data);
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS + +/** + * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests + * @kmod_name: kernel module name + * + * We have situation, when public_key_verify_signature() in case of RSA + * algorithm use alg_name to store internal information in order to + * construct an algorithm on the fly, but crypto_larval_lookup() will try + * to use alg_name in order to load kernel module with same name. + * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, + * we are safe to fail such module request from crypto_larval_lookup(). + * + * In this way we prevent modprobe execution during digsig verification + * and avoid possible deadlock if modprobe and/or it's dependencies + * also signed with digsig. + * + * Return: Zero if it is safe to load the kernel module, -EINVAL otherwise. + */ +int ima_kernel_module_request(char *kmod_name) +{ + if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) + return -EINVAL; + + return 0; +} + +#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */ + static int __init init_ima(void) { int error; diff --git a/security/security.c b/security/security.c index d2a1226e6e69..6c6571a141a1 100644 --- a/security/security.c +++ b/security/security.c @@ -3255,7 +3255,7 @@ int security_kernel_module_request(char *kmod_name) ret = call_int_hook(kernel_module_request, 0, kmod_name); if (ret) return ret; - return integrity_kernel_module_request(kmod_name); + return ima_kernel_module_request(kmod_name); }
/**
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
In preparation for removing the 'integrity' LSM, move integrity_kernel_module_request() to IMA, and rename it to ima_kernel_module_request().
Compile it conditionally if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled, and call it from security.c (removed afterwards with the move of IMA to the LSM infrastructure).
Adding this hook cannot be avoided, since IMA has no control on the flags passed to crypto_alloc_sig() in public_key_verify_signature(), and thus cannot pass CRYPTO_NOLOAD, which solved the problem for EVM hashing with commit e2861fa71641 ("evm: Don't deadlock if a crypto algorithm is unavailable").
EVM alone does not need to implement this hook, first because there is no mutex to deadlock, and second because even if it had it, there should be a recursive call. However, since verification from EVM can be initiated only by setting inode metadata, deadlock would occur if modprobe would do the same while loading a kernel module (which is unlikely).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
include/linux/ima.h | 10 +++++++++ include/linux/integrity.h | 13 ------------ security/integrity/digsig_asymmetric.c | 23 -------------------- security/integrity/ima/ima_main.c | 29 ++++++++++++++++++++++++++ security/security.c | 2 +- 5 files changed, 40 insertions(+), 37 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On 1/15/24 13:18, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
In preparation for removing the 'integrity' LSM, move integrity_kernel_module_request() to IMA, and rename it to ima_kernel_module_request().
Compile it conditionally if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled, and call it from security.c (removed afterwards with the move of IMA to the LSM infrastructure).
Adding this hook cannot be avoided, since IMA has no control on the flags passed to crypto_alloc_sig() in public_key_verify_signature(), and thus cannot pass CRYPTO_NOLOAD, which solved the problem for EVM hashing with commit e2861fa71641 ("evm: Don't deadlock if a crypto algorithm is unavailable").
EVM alone does not need to implement this hook, first because there is no mutex to deadlock, and second because even if it had it, there should be a recursive call. However, since verification from EVM can be initiated only by setting inode metadata, deadlock would occur if modprobe would do the same while loading a kernel module (which is unlikely).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
include/linux/ima.h | 10 +++++++++ include/linux/integrity.h | 13 ------------ security/integrity/digsig_asymmetric.c | 23 -------------------- security/integrity/ima/ima_main.c | 29 ++++++++++++++++++++++++++ security/security.c | 2 +- 5 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 31ef6c3c3207..0f9af283cbc8 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -256,4 +256,14 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func) return false; } #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) +extern int ima_kernel_module_request(char *kmod_name); +#else +static inline int ima_kernel_module_request(char *kmod_name) +{
- return 0;
+}
+#endif #endif /* _LINUX_IMA_H */ diff --git a/include/linux/integrity.h b/include/linux/integrity.h index 2ea0f2f65ab6..ef0f63ef5ebc 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -42,17 +42,4 @@ static inline void integrity_load_keys(void) } #endif /* CONFIG_INTEGRITY */ -#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
-extern int integrity_kernel_module_request(char *kmod_name);
-#else
-static inline int integrity_kernel_module_request(char *kmod_name) -{
- return 0;
-}
-#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
- #endif /* _LINUX_INTEGRITY_H */
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 895f4b9ce8c6..de603cf42ac7 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -132,26 +132,3 @@ int asymmetric_verify(struct key *keyring, const char *sig, pr_debug("%s() = %d\n", __func__, ret); return ret; }
-/**
- integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA
- algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
- */
-int integrity_kernel_module_request(char *kmod_name) -{
- if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
return -EINVAL;
- return 0;
-} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 02021ee467d3..908fa026ec58 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1091,6 +1091,35 @@ int ima_measure_critical_data(const char *event_label, } EXPORT_SYMBOL_GPL(ima_measure_critical_data); +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+/**
- ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
This text needs to some reformulation at some point..
- Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
- */
+int ima_kernel_module_request(char *kmod_name) +{
- if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
return -EINVAL;
- return 0;
+}
+#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
- static int __init init_ima(void) { int error;
diff --git a/security/security.c b/security/security.c index d2a1226e6e69..6c6571a141a1 100644 --- a/security/security.c +++ b/security/security.c @@ -3255,7 +3255,7 @@ int security_kernel_module_request(char *kmod_name) ret = call_int_hook(kernel_module_request, 0, kmod_name); if (ret) return ret;
- return integrity_kernel_module_request(kmod_name);
- return ima_kernel_module_request(kmod_name); }
/**
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger stefanb@linux.ibm.com wrote:
On 1/15/24 13:18, Roberto Sassu wrote:
...
+/**
- ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
This text needs to some reformulation at some point..
There is no time like the present. If you have a suggestion I would love to hear it and I'm sure Roberto would too.
On 2/12/24 12:56, Paul Moore wrote:
On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger stefanb@linux.ibm.com wrote:
On 1/15/24 13:18, Roberto Sassu wrote:
...
+/**
- ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
This text needs to some reformulation at some point..
There is no time like the present. If you have a suggestion I would love to hear it and I'm sure Roberto would too.
My interpretation of the issue after possibly lossy decoding of the above sentences:
Avoid a deadlock by rejecting a virtual kernel module with the name "crypto-pkcs1pad(rsa,*)". This module may be requested by crypto_larval_lookup() while trying to verify an RSA signature in public_key_verify_signature(). Since the loading of the RSA module may itself cause the request for an RSA signature verification it will otherwise lead to a deadlock.
On Mon, 2024-02-12 at 15:28 -0500, Stefan Berger wrote:
On 2/12/24 12:56, Paul Moore wrote:
On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger stefanb@linux.ibm.com wrote:
On 1/15/24 13:18, Roberto Sassu wrote:
...
+/**
- ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
This text needs to some reformulation at some point..
There is no time like the present. If you have a suggestion I would love to hear it and I'm sure Roberto would too.
My interpretation of the issue after possibly lossy decoding of the above sentences:
Avoid a deadlock by rejecting a virtual kernel module with the name "crypto-pkcs1pad(rsa,*)". This module may be requested by crypto_larval_lookup() while trying to verify an RSA signature in public_key_verify_signature(). Since the loading of the RSA module may itself cause the request for an RSA signature verification it will otherwise lead to a deadlock.
I can be even more precise I guess (I actually reproduced it).
Avoid a verification loop where verifying the signature of the modprobe binary requires executing modprobe itself. Since the modprobe iint-
mutex is already held when the signature verification is performed, a
deadlock occurs as soon as modprobe is executed within the critical region, since the same lock cannot be taken again.
This happens when public_key_verify_signature(), in case of RSA algorithm, use alg_name to store internal information in order to construct an algorithm on the fly, but crypto_larval_lookup() will try to use alg_name in order to load a kernel module with same name.
Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, we are safe to fail such module request from crypto_larval_lookup(), and avoid the verification loop.
Roberto
On 2/13/24 03:57, Roberto Sassu wrote:
On Mon, 2024-02-12 at 15:28 -0500, Stefan Berger wrote:
On 2/12/24 12:56, Paul Moore wrote:
On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger stefanb@linux.ibm.com wrote:
On 1/15/24 13:18, Roberto Sassu wrote:
...
+/**
- ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
This text needs to some reformulation at some point..
There is no time like the present. If you have a suggestion I would love to hear it and I'm sure Roberto would too.
My interpretation of the issue after possibly lossy decoding of the above sentences:
Avoid a deadlock by rejecting a virtual kernel module with the name "crypto-pkcs1pad(rsa,*)". This module may be requested by crypto_larval_lookup() while trying to verify an RSA signature in public_key_verify_signature(). Since the loading of the RSA module may itself cause the request for an RSA signature verification it will otherwise lead to a deadlock.
I can be even more precise I guess (I actually reproduced it). > Avoid a verification loop where verifying the signature of the modprobe binary requires executing modprobe itself. Since the modprobe iint-
mutex is already held when the signature verification is performed, a
deadlock occurs as soon as modprobe is executed within the critical region, since the same lock cannot be taken again.
When ecdsa is used for signing files it could get stuck as well and would need this patch:
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 45f1a102c599..2e71dc977d43 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1110,7 +1110,9 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); */ static int ima_kernel_module_request(char *kmod_name) { - if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) + if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0 || + strncmp(kmod_name, "crypto-ecdsa-nist-p", 19) == 0 || + strcmp(kmod_name, "cryptomgr") == 0) return -EINVAL;
return 0;
Rejecting cryptomgr seems necessary in the ecdsa case though I am not sure what the side effects of rejecting it all the time could be.
Stefan
This happens when public_key_verify_signature(), in case of RSA algorithm, use alg_name to store internal information in order to construct an algorithm on the fly, but crypto_larval_lookup() will try to use alg_name in order to load a kernel module with same name.
Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, we are safe to fail such module request from crypto_larval_lookup(), and avoid the verification loop.
Roberto
On Tue, 2024-02-13 at 11:31 -0500, Stefan Berger wrote:
On 2/13/24 03:57, Roberto Sassu wrote:
On Mon, 2024-02-12 at 15:28 -0500, Stefan Berger wrote:
On 2/12/24 12:56, Paul Moore wrote:
On Mon, Feb 12, 2024 at 12:48 PM Stefan Berger stefanb@linux.ibm.com wrote:
On 1/15/24 13:18, Roberto Sassu wrote:
...
+/**
- ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests
- @kmod_name: kernel module name
- We have situation, when public_key_verify_signature() in case of RSA > + * algorithm use alg_name to store internal information in order to
- construct an algorithm on the fly, but crypto_larval_lookup() will try
- to use alg_name in order to load kernel module with same name.
- Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- we are safe to fail such module request from crypto_larval_lookup().
- In this way we prevent modprobe execution during digsig verification
- and avoid possible deadlock if modprobe and/or it's dependencies
- also signed with digsig.
This text needs to some reformulation at some point..
There is no time like the present. If you have a suggestion I would love to hear it and I'm sure Roberto would too.
My interpretation of the issue after possibly lossy decoding of the above sentences:
Avoid a deadlock by rejecting a virtual kernel module with the name "crypto-pkcs1pad(rsa,*)". This module may be requested by crypto_larval_lookup() while trying to verify an RSA signature in public_key_verify_signature(). Since the loading of the RSA module may itself cause the request for an RSA signature verification it will otherwise lead to a deadlock.
I can be even more precise I guess (I actually reproduced it). > Avoid a verification loop where verifying the signature of the modprobe binary requires executing modprobe itself. Since the modprobe iint-
mutex is already held when the signature verification is performed, a
deadlock occurs as soon as modprobe is executed within the critical region, since the same lock cannot be taken again.
When ecdsa is used for signing files it could get stuck as well and would need this patch:
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 45f1a102c599..2e71dc977d43 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1110,7 +1110,9 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); */ static int ima_kernel_module_request(char *kmod_name) {
if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0 ||
strncmp(kmod_name, "crypto-ecdsa-nist-p", 19) == 0 ||
strcmp(kmod_name, "cryptomgr") == 0) return -EINVAL; return 0;
Rejecting cryptomgr seems necessary in the ecdsa case though I am not sure what the side effects of rejecting it all the time could be.
Thanks. Ok, let's find a proper way once IMA/EVM are moved to the LSM infrastructure.
Roberto
Stefan
This happens when public_key_verify_signature(), in case of RSA algorithm, use alg_name to store internal information in order to construct an algorithm on the fly, but crypto_larval_lookup() will try to use alg_name in order to load a kernel module with same name.
Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, we are safe to fail such module request from crypto_larval_lookup(), and avoid the verification loop.
Roberto
From: Roberto Sassu roberto.sassu@huawei.com
Move hardcoded IMA function calls (not appraisal-specific functions) from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'ima' (at the end of the LSM list and always enabled like 'integrity').
Having IMA before EVM in the Makefile is sufficient to preserve the relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM, and thus the order of IMA and EVM function calls as when they were hardcoded.
Make moved functions as static (except ima_post_key_create_or_update(), which is not in ima_main.c), and register them as implementation of the respective hooks in the new function init_ima_lsm().
Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook path_post_mknod is always available and ima_post_path_mknod() is always executed to mark files as new, as before the move.
A slight difference is that IMA and EVM functions registered for the inode_post_setattr, inode_post_removexattr, path_post_mknod, inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl won't be executed for private inodes. Since those inodes are supposed to be fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub and kernel-internal tmpfs files.
Conditionally register ima_post_key_create_or_update() if CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Chuck Lever chuck.lever@oracle.com --- fs/file_table.c | 2 - fs/namei.c | 6 - fs/nfsd/vfs.c | 7 -- fs/open.c | 1 - include/linux/ima.h | 104 ------------------ include/uapi/linux/lsm.h | 1 + security/integrity/Makefile | 1 + security/integrity/ima/Kconfig | 1 + security/integrity/ima/ima.h | 6 + security/integrity/ima/ima_main.c | 78 +++++++++---- security/integrity/integrity.h | 1 + security/keys/key.c | 9 +- security/security.c | 63 ++--------- .../selftests/lsm/lsm_list_modules_test.c | 3 + 14 files changed, 83 insertions(+), 200 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c index c72dc75f2bd3..0401ac98281c 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -26,7 +26,6 @@ #include <linux/percpu_counter.h> #include <linux/percpu.h> #include <linux/task_work.h> -#include <linux/ima.h> #include <linux/swap.h> #include <linux/kmemleak.h>
@@ -386,7 +385,6 @@ static void __fput(struct file *file) locks_remove_file(file);
security_file_release(file); - ima_file_free(file); if (unlikely(file->f_flags & FASYNC)) { if (file->f_op->fasync) file->f_op->fasync(-1, file, 0); diff --git a/fs/namei.c b/fs/namei.c index adb3ab27951a..37cc0988308f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -27,7 +27,6 @@ #include <linux/fsnotify.h> #include <linux/personality.h> #include <linux/security.h> -#include <linux/ima.h> #include <linux/syscalls.h> #include <linux/mount.h> #include <linux/audit.h> @@ -3622,8 +3621,6 @@ static int do_open(struct nameidata *nd, error = vfs_open(&nd->path, file); if (!error) error = security_file_post_open(file, op->acc_mode); - if (!error) - error = ima_file_check(file, op->acc_mode); if (!error && do_truncate) error = handle_truncate(idmap, file); if (unlikely(error > 0)) { @@ -3687,7 +3684,6 @@ static int vfs_tmpfile(struct mnt_idmap *idmap, spin_unlock(&inode->i_lock); } security_inode_post_create_tmpfile(idmap, inode); - ima_post_create_tmpfile(idmap, inode); return 0; }
@@ -4036,8 +4032,6 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, case 0: case S_IFREG: error = vfs_create(idmap, path.dentry->d_inode, dentry, mode, true); - if (!error) - ima_post_path_mknod(idmap, dentry); break; case S_IFCHR: case S_IFBLK: error = vfs_mknod(idmap, path.dentry->d_inode, diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index b0c3f07a8bba..e491392a1243 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -25,7 +25,6 @@ #include <linux/posix_acl_xattr.h> #include <linux/xattr.h> #include <linux/jhash.h> -#include <linux/ima.h> #include <linux/pagemap.h> #include <linux/slab.h> #include <linux/uaccess.h> @@ -883,12 +882,6 @@ __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, goto out; }
- host_err = ima_file_check(file, may_flags); - if (host_err) { - fput(file); - goto out; - } - if (may_flags & NFSD_MAY_64BIT_COOKIE) file->f_mode |= FMODE_64BITHASH; else diff --git a/fs/open.c b/fs/open.c index 02dc608d40d8..c8bb9bd5259f 100644 --- a/fs/open.c +++ b/fs/open.c @@ -29,7 +29,6 @@ #include <linux/audit.h> #include <linux/falloc.h> #include <linux/fs_struct.h> -#include <linux/ima.h> #include <linux/dnotify.h> #include <linux/compat.h> #include <linux/mnt_idmapping.h> diff --git a/include/linux/ima.h b/include/linux/ima.h index 0f9af283cbc8..23ae24b60ecf 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -16,24 +16,6 @@ struct linux_binprm;
#ifdef CONFIG_IMA extern enum hash_algo ima_get_current_hash_algo(void); -extern int ima_bprm_check(struct linux_binprm *bprm); -extern int ima_file_check(struct file *file, int mask); -extern void ima_post_create_tmpfile(struct mnt_idmap *idmap, - struct inode *inode); -extern void ima_file_free(struct file *file); -extern int ima_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags); -extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, - unsigned long prot); -extern int ima_load_data(enum kernel_load_data_id id, bool contents); -extern int ima_post_load_data(char *buf, loff_t size, - enum kernel_load_data_id id, char *description); -extern int ima_read_file(struct file *file, enum kernel_read_file_id id, - bool contents); -extern int ima_post_read_file(struct file *file, char *buf, loff_t size, - enum kernel_read_file_id id); -extern void ima_post_path_mknod(struct mnt_idmap *idmap, - struct dentry *dentry); extern int ima_file_hash(struct file *file, char *buf, size_t buf_size); extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size); extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size); @@ -58,68 +40,6 @@ static inline enum hash_algo ima_get_current_hash_algo(void) return HASH_ALGO__LAST; }
-static inline int ima_bprm_check(struct linux_binprm *bprm) -{ - return 0; -} - -static inline int ima_file_check(struct file *file, int mask) -{ - return 0; -} - -static inline void ima_post_create_tmpfile(struct mnt_idmap *idmap, - struct inode *inode) -{ -} - -static inline void ima_file_free(struct file *file) -{ - return; -} - -static inline int ima_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags) -{ - return 0; -} - -static inline int ima_file_mprotect(struct vm_area_struct *vma, - unsigned long reqprot, unsigned long prot) -{ - return 0; -} - -static inline int ima_load_data(enum kernel_load_data_id id, bool contents) -{ - return 0; -} - -static inline int ima_post_load_data(char *buf, loff_t size, - enum kernel_load_data_id id, - char *description) -{ - return 0; -} - -static inline int ima_read_file(struct file *file, enum kernel_read_file_id id, - bool contents) -{ - return 0; -} - -static inline int ima_post_read_file(struct file *file, char *buf, loff_t size, - enum kernel_read_file_id id) -{ - return 0; -} - -static inline void ima_post_path_mknod(struct mnt_idmap *idmap, - struct dentry *dentry) -{ - return; -} - static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size) { return -EOPNOTSUPP; @@ -170,20 +90,6 @@ static inline void ima_add_kexec_buffer(struct kimage *image) {} #endif
-#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS -extern void ima_post_key_create_or_update(struct key *keyring, - struct key *key, - const void *payload, size_t plen, - unsigned long flags, bool create); -#else -static inline void ima_post_key_create_or_update(struct key *keyring, - struct key *key, - const void *payload, - size_t plen, - unsigned long flags, - bool create) {} -#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */ - #ifdef CONFIG_IMA_APPRAISE extern bool is_ima_appraise_enabled(void); extern void ima_inode_post_setattr(struct mnt_idmap *idmap, @@ -256,14 +162,4 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func) return false; } #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */ - -#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) -extern int ima_kernel_module_request(char *kmod_name); -#else -static inline int ima_kernel_module_request(char *kmod_name) -{ - return 0; -} - -#endif #endif /* _LINUX_IMA_H */ diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h index f8aef9ade549..b3b7fd699b63 100644 --- a/include/uapi/linux/lsm.h +++ b/include/uapi/linux/lsm.h @@ -62,6 +62,7 @@ struct lsm_ctx { #define LSM_ID_LOCKDOWN 108 #define LSM_ID_BPF 109 #define LSM_ID_LANDLOCK 110 +#define LSM_ID_IMA 111
/* * LSM_ATTR_XXX definitions identify different LSM attributes diff --git a/security/integrity/Makefile b/security/integrity/Makefile index d0ffe37dc1d6..92b63039c654 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +# The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/ diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b98bfe9efd0c..475c32615006 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -8,6 +8,7 @@ config IMA select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_HASH_INFO + select SECURITY_PATH select TCG_TPM if HAS_IOMEM select TCG_TIS if TCG_TPM && X86 select TCG_CRB if TCG_TPM && ACPI diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c29db699c996..c0412100023e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -127,6 +127,12 @@ void ima_load_kexec_buffer(void); static inline void ima_load_kexec_buffer(void) {} #endif /* CONFIG_HAVE_IMA_KEXEC */
+#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS +void ima_post_key_create_or_update(struct key *keyring, struct key *key, + const void *payload, size_t plen, + unsigned long flags, bool create); +#endif + /* * The default binary_runtime_measurements list format is defined as the * platform native format. The canonical format is defined as little-endian. diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 908fa026ec58..483ccd24e214 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -189,7 +189,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, * * Flag files that changed, based on i_version */ -void ima_file_free(struct file *file) +static void ima_file_free(struct file *file) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint; @@ -427,8 +427,8 @@ static int process_measurement(struct file *file, const struct cred *cred, * On success return 0. On integrity appraisal error, assuming the file * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ -int ima_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags) +static int ima_file_mmap(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) { u32 secid; int ret; @@ -466,8 +466,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot, * * On mprotect change success, return 0. On failure, return -EACESS. */ -int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, - unsigned long prot) +static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, + unsigned long prot) { struct ima_template_desc *template = NULL; struct file *file; @@ -525,7 +525,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, * On success return 0. On integrity appraisal error, assuming the file * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ -int ima_bprm_check(struct linux_binprm *bprm) +static int ima_bprm_check(struct linux_binprm *bprm) { int ret; u32 secid; @@ -551,7 +551,7 @@ int ima_bprm_check(struct linux_binprm *bprm) * On success return 0. On integrity appraisal error, assuming the file * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ -int ima_file_check(struct file *file, int mask) +static int ima_file_check(struct file *file, int mask) { u32 secid;
@@ -560,7 +560,6 @@ int ima_file_check(struct file *file, int mask) mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); } -EXPORT_SYMBOL_GPL(ima_file_check);
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf, size_t buf_size) @@ -685,8 +684,9 @@ EXPORT_SYMBOL_GPL(ima_inode_hash); * Skip calling process_measurement(), but indicate which newly, created * tmpfiles are in policy. */ -void ima_post_create_tmpfile(struct mnt_idmap *idmap, - struct inode *inode) +static void ima_post_create_tmpfile(struct mnt_idmap *idmap, + struct inode *inode) + { struct integrity_iint_cache *iint; int must_appraise; @@ -717,8 +717,7 @@ void ima_post_create_tmpfile(struct mnt_idmap *idmap, * Mark files created via the mknodat syscall as new, so that the * file data can be written later. */ -void ima_post_path_mknod(struct mnt_idmap *idmap, - struct dentry *dentry) +static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) { struct integrity_iint_cache *iint; struct inode *inode = dentry->d_inode; @@ -753,8 +752,8 @@ void ima_post_path_mknod(struct mnt_idmap *idmap, * * For permission return 0, otherwise return -EACCES. */ -int ima_read_file(struct file *file, enum kernel_read_file_id read_id, - bool contents) +static int ima_read_file(struct file *file, enum kernel_read_file_id read_id, + bool contents) { enum ima_hooks func; u32 secid; @@ -803,8 +802,8 @@ const int read_idmap[READING_MAX_ID] = { * On success return 0. On integrity appraisal error, assuming the file * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ -int ima_post_read_file(struct file *file, char *buf, loff_t size, - enum kernel_read_file_id read_id) +static int ima_post_read_file(struct file *file, char *buf, loff_t size, + enum kernel_read_file_id read_id) { enum ima_hooks func; u32 secid; @@ -837,7 +836,7 @@ int ima_post_read_file(struct file *file, char *buf, loff_t size, * * For permission return 0, otherwise return -EACCES. */ -int ima_load_data(enum kernel_load_data_id id, bool contents) +static int ima_load_data(enum kernel_load_data_id id, bool contents) { bool ima_enforce, sig_enforce;
@@ -891,9 +890,9 @@ int ima_load_data(enum kernel_load_data_id id, bool contents) * On success return 0. On integrity appraisal error, assuming the file * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ -int ima_post_load_data(char *buf, loff_t size, - enum kernel_load_data_id load_id, - char *description) +static int ima_post_load_data(char *buf, loff_t size, + enum kernel_load_data_id load_id, + char *description) { if (load_id == LOADING_FIRMWARE) { if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && @@ -1110,7 +1109,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); * * Return: Zero if it is safe to load the kernel module, -EINVAL otherwise. */ -int ima_kernel_module_request(char *kmod_name) +static int ima_kernel_module_request(char *kmod_name) { if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) return -EINVAL; @@ -1151,4 +1150,41 @@ static int __init init_ima(void) return error; }
+static struct security_hook_list ima_hooks[] __ro_after_init = { + LSM_HOOK_INIT(bprm_check_security, ima_bprm_check), + LSM_HOOK_INIT(file_post_open, ima_file_check), + LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile), + LSM_HOOK_INIT(file_release, ima_file_free), + LSM_HOOK_INIT(mmap_file, ima_file_mmap), + LSM_HOOK_INIT(file_mprotect, ima_file_mprotect), + LSM_HOOK_INIT(kernel_load_data, ima_load_data), + LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data), + LSM_HOOK_INIT(kernel_read_file, ima_read_file), + LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file), + LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod), +#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS + LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update), +#endif +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS + LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request), +#endif +}; + +static const struct lsm_id ima_lsmid = { + .name = "ima", + .id = LSM_ID_IMA, +}; + +static int __init init_ima_lsm(void) +{ + security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid); + return 0; +} + +DEFINE_LSM(ima) = { + .name = "ima", + .init = init_ima_lsm, + .order = LSM_ORDER_LAST, +}; + late_initcall(init_ima); /* Start IMA after the TPM is available */ diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 9561db7cf6b4..59eaddd84434 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -18,6 +18,7 @@ #include <crypto/hash.h> #include <linux/key.h> #include <linux/audit.h> +#include <linux/lsm_hooks.h>
/* iint action cache flags */ #define IMA_MEASURE 0x00000001 diff --git a/security/keys/key.c b/security/keys/key.c index f75fe66c2f03..80fc2f203a0c 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -13,7 +13,6 @@ #include <linux/security.h> #include <linux/workqueue.h> #include <linux/random.h> -#include <linux/ima.h> #include <linux/err.h> #include "internal.h"
@@ -937,8 +936,6 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,
security_key_post_create_or_update(keyring, key, payload, plen, flags, true); - ima_post_key_create_or_update(keyring, key, payload, plen, - flags, true);
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
@@ -970,13 +967,9 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,
key_ref = __key_update(key_ref, &prep);
- if (!IS_ERR(key_ref)) { + if (!IS_ERR(key_ref)) security_key_post_create_or_update(keyring, key, payload, plen, flags, false); - ima_post_key_create_or_update(keyring, key, - payload, plen, - flags, false); - }
goto error_free_prep; } diff --git a/security/security.c b/security/security.c index 6c6571a141a1..aa17b47d44c1 100644 --- a/security/security.c +++ b/security/security.c @@ -50,7 +50,8 @@ (IS_ENABLED(CONFIG_SECURITY_SAFESETID) ? 1 : 0) + \ (IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \ (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \ - (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0)) + (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \ + (IS_ENABLED(CONFIG_IMA) ? 1 : 0))
/* * These are descriptions of the reasons that can be passed to the @@ -1182,12 +1183,7 @@ int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file * */ int security_bprm_check(struct linux_binprm *bprm) { - int ret; - - ret = call_int_hook(bprm_check_security, 0, bprm); - if (ret) - return ret; - return ima_bprm_check(bprm); + return call_int_hook(bprm_check_security, 0, bprm); }
/** @@ -2901,13 +2897,8 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot) int security_mmap_file(struct file *file, unsigned long prot, unsigned long flags) { - unsigned long prot_adj = mmap_prot(file, prot); - int ret; - - ret = call_int_hook(mmap_file, 0, file, prot, prot_adj, flags); - if (ret) - return ret; - return ima_file_mmap(file, prot, prot_adj, flags); + return call_int_hook(mmap_file, 0, file, prot, mmap_prot(file, prot), + flags); }
/** @@ -2936,12 +2927,7 @@ int security_mmap_addr(unsigned long addr) int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) { - int ret; - - ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot); - if (ret) - return ret; - return ima_file_mprotect(vma, reqprot, prot); + return call_int_hook(file_mprotect, 0, vma, reqprot, prot); }
/** @@ -3250,12 +3236,7 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) */ int security_kernel_module_request(char *kmod_name) { - int ret; - - ret = call_int_hook(kernel_module_request, 0, kmod_name); - if (ret) - return ret; - return ima_kernel_module_request(kmod_name); + return call_int_hook(kernel_module_request, 0, kmod_name); }
/** @@ -3271,12 +3252,7 @@ int security_kernel_module_request(char *kmod_name) int security_kernel_read_file(struct file *file, enum kernel_read_file_id id, bool contents) { - int ret; - - ret = call_int_hook(kernel_read_file, 0, file, id, contents); - if (ret) - return ret; - return ima_read_file(file, id, contents); + return call_int_hook(kernel_read_file, 0, file, id, contents); } EXPORT_SYMBOL_GPL(security_kernel_read_file);
@@ -3296,12 +3272,7 @@ EXPORT_SYMBOL_GPL(security_kernel_read_file); int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, enum kernel_read_file_id id) { - int ret; - - ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id); - if (ret) - return ret; - return ima_post_read_file(file, buf, size, id); + return call_int_hook(kernel_post_read_file, 0, file, buf, size, id); } EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
@@ -3316,12 +3287,7 @@ EXPORT_SYMBOL_GPL(security_kernel_post_read_file); */ int security_kernel_load_data(enum kernel_load_data_id id, bool contents) { - int ret; - - ret = call_int_hook(kernel_load_data, 0, id, contents); - if (ret) - return ret; - return ima_load_data(id, contents); + return call_int_hook(kernel_load_data, 0, id, contents); } EXPORT_SYMBOL_GPL(security_kernel_load_data);
@@ -3343,13 +3309,8 @@ int security_kernel_post_load_data(char *buf, loff_t size, enum kernel_load_data_id id, char *description) { - int ret; - - ret = call_int_hook(kernel_post_load_data, 0, buf, size, id, - description); - if (ret) - return ret; - return ima_post_load_data(buf, size, id, description); + return call_int_hook(kernel_post_load_data, 0, buf, size, id, + description); } EXPORT_SYMBOL_GPL(security_kernel_post_load_data);
diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c index 9df29b1e3497..17333787cb2f 100644 --- a/tools/testing/selftests/lsm/lsm_list_modules_test.c +++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c @@ -122,6 +122,9 @@ TEST(correct_lsm_list_modules) case LSM_ID_LANDLOCK: name = "landlock"; break; + case LSM_ID_IMA: + name = "ima"; + break; default: name = "INVALID"; break;
On 1/15/2024 10:18 AM, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Move hardcoded IMA function calls (not appraisal-specific functions) from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'ima' (at the end of the LSM list and always enabled like 'integrity').
Having IMA before EVM in the Makefile is sufficient to preserve the relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM, and thus the order of IMA and EVM function calls as when they were hardcoded.
Make moved functions as static (except ima_post_key_create_or_update(), which is not in ima_main.c), and register them as implementation of the respective hooks in the new function init_ima_lsm().
Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook path_post_mknod is always available and ima_post_path_mknod() is always executed to mark files as new, as before the move.
A slight difference is that IMA and EVM functions registered for the inode_post_setattr, inode_post_removexattr, path_post_mknod, inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl won't be executed for private inodes. Since those inodes are supposed to be fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub and kernel-internal tmpfs files.
Conditionally register ima_post_key_create_or_update() if CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Chuck Lever chuck.lever@oracle.com
Acked-by: Casey Schaufler casey@schaufler-ca.com
fs/file_table.c | 2 - fs/namei.c | 6 - fs/nfsd/vfs.c | 7 -- fs/open.c | 1 - include/linux/ima.h | 104 ------------------ include/uapi/linux/lsm.h | 1 + security/integrity/Makefile | 1 + security/integrity/ima/Kconfig | 1 + security/integrity/ima/ima.h | 6 + security/integrity/ima/ima_main.c | 78 +++++++++---- security/integrity/integrity.h | 1 + security/keys/key.c | 9 +- security/security.c | 63 ++--------- .../selftests/lsm/lsm_list_modules_test.c | 3 + 14 files changed, 83 insertions(+), 200 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c index c72dc75f2bd3..0401ac98281c 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -26,7 +26,6 @@ #include <linux/percpu_counter.h> #include <linux/percpu.h> #include <linux/task_work.h> -#include <linux/ima.h> #include <linux/swap.h> #include <linux/kmemleak.h> @@ -386,7 +385,6 @@ static void __fput(struct file *file) locks_remove_file(file); security_file_release(file);
- ima_file_free(file); if (unlikely(file->f_flags & FASYNC)) { if (file->f_op->fasync) file->f_op->fasync(-1, file, 0);
diff --git a/fs/namei.c b/fs/namei.c index adb3ab27951a..37cc0988308f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -27,7 +27,6 @@ #include <linux/fsnotify.h> #include <linux/personality.h> #include <linux/security.h> -#include <linux/ima.h> #include <linux/syscalls.h> #include <linux/mount.h> #include <linux/audit.h> @@ -3622,8 +3621,6 @@ static int do_open(struct nameidata *nd, error = vfs_open(&nd->path, file); if (!error) error = security_file_post_open(file, op->acc_mode);
- if (!error)
if (!error && do_truncate) error = handle_truncate(idmap, file); if (unlikely(error > 0)) {error = ima_file_check(file, op->acc_mode);
@@ -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)
case S_IFCHR: case S_IFBLK: error = vfs_mknod(idmap, path.dentry->d_inode,ima_post_path_mknod(idmap, dentry); break;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index b0c3f07a8bba..e491392a1243 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -25,7 +25,6 @@ #include <linux/posix_acl_xattr.h> #include <linux/xattr.h> #include <linux/jhash.h> -#include <linux/ima.h> #include <linux/pagemap.h> #include <linux/slab.h> #include <linux/uaccess.h> @@ -883,12 +882,6 @@ __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, goto out; }
- host_err = ima_file_check(file, may_flags);
- if (host_err) {
fput(file);
goto out;
- }
- if (may_flags & NFSD_MAY_64BIT_COOKIE) file->f_mode |= FMODE_64BITHASH; else
diff --git a/fs/open.c b/fs/open.c index 02dc608d40d8..c8bb9bd5259f 100644 --- a/fs/open.c +++ b/fs/open.c @@ -29,7 +29,6 @@ #include <linux/audit.h> #include <linux/falloc.h> #include <linux/fs_struct.h> -#include <linux/ima.h> #include <linux/dnotify.h> #include <linux/compat.h> #include <linux/mnt_idmapping.h> diff --git a/include/linux/ima.h b/include/linux/ima.h index 0f9af283cbc8..23ae24b60ecf 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -16,24 +16,6 @@ struct linux_binprm; #ifdef CONFIG_IMA extern enum hash_algo ima_get_current_hash_algo(void); -extern int ima_bprm_check(struct linux_binprm *bprm); -extern int ima_file_check(struct file *file, int mask); -extern void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode);
-extern void ima_file_free(struct file *file); -extern int ima_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags);
-extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot);
-extern int ima_load_data(enum kernel_load_data_id id, bool contents); -extern int ima_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id id, char *description);
-extern int ima_read_file(struct file *file, enum kernel_read_file_id id,
bool contents);
-extern int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id);
-extern void ima_post_path_mknod(struct mnt_idmap *idmap,
struct dentry *dentry);
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size); extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size); extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size); @@ -58,68 +40,6 @@ static inline enum hash_algo ima_get_current_hash_algo(void) return HASH_ALGO__LAST; } -static inline int ima_bprm_check(struct linux_binprm *bprm) -{
- return 0;
-}
-static inline int ima_file_check(struct file *file, int mask) -{
- return 0;
-}
-static inline void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode)
-{ -}
-static inline void ima_file_free(struct file *file) -{
- return;
-}
-static inline int ima_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
-{
- return 0;
-}
-static inline int ima_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
-{
- return 0;
-}
-static inline int ima_load_data(enum kernel_load_data_id id, bool contents) -{
- return 0;
-}
-static inline int ima_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id id,
char *description)
-{
- return 0;
-}
-static inline int ima_read_file(struct file *file, enum kernel_read_file_id id,
bool contents)
-{
- return 0;
-}
-static inline int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id)
-{
- return 0;
-}
-static inline void ima_post_path_mknod(struct mnt_idmap *idmap,
struct dentry *dentry)
-{
- return;
-}
static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size) { return -EOPNOTSUPP; @@ -170,20 +90,6 @@ static inline void ima_add_kexec_buffer(struct kimage *image) {} #endif -#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS -extern void ima_post_key_create_or_update(struct key *keyring,
struct key *key,
const void *payload, size_t plen,
unsigned long flags, bool create);
-#else -static inline void ima_post_key_create_or_update(struct key *keyring,
struct key *key,
const void *payload,
size_t plen,
unsigned long flags,
bool create) {}
-#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
#ifdef CONFIG_IMA_APPRAISE extern bool is_ima_appraise_enabled(void); extern void ima_inode_post_setattr(struct mnt_idmap *idmap, @@ -256,14 +162,4 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func) return false; } #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
-#if defined(CONFIG_IMA) && defined(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) -extern int ima_kernel_module_request(char *kmod_name); -#else -static inline int ima_kernel_module_request(char *kmod_name) -{
- return 0;
-}
-#endif #endif /* _LINUX_IMA_H */ diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h index f8aef9ade549..b3b7fd699b63 100644 --- a/include/uapi/linux/lsm.h +++ b/include/uapi/linux/lsm.h @@ -62,6 +62,7 @@ struct lsm_ctx { #define LSM_ID_LOCKDOWN 108 #define LSM_ID_BPF 109 #define LSM_ID_LANDLOCK 110 +#define LSM_ID_IMA 111 /*
- LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/integrity/Makefile b/security/integrity/Makefile index d0ffe37dc1d6..92b63039c654 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +# The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/ diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b98bfe9efd0c..475c32615006 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -8,6 +8,7 @@ config IMA select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_HASH_INFO
- select SECURITY_PATH select TCG_TPM if HAS_IOMEM select TCG_TIS if TCG_TPM && X86 select TCG_CRB if TCG_TPM && ACPI
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c29db699c996..c0412100023e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -127,6 +127,12 @@ void ima_load_kexec_buffer(void); static inline void ima_load_kexec_buffer(void) {} #endif /* CONFIG_HAVE_IMA_KEXEC */ +#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS +void ima_post_key_create_or_update(struct key *keyring, struct key *key,
const void *payload, size_t plen,
unsigned long flags, bool create);
+#endif
/*
- The default binary_runtime_measurements list format is defined as the
- platform native format. The canonical format is defined as little-endian.
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 908fa026ec58..483ccd24e214 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -189,7 +189,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
- Flag files that changed, based on i_version
*/ -void ima_file_free(struct file *file) +static void ima_file_free(struct file *file) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint; @@ -427,8 +427,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
- On success return 0. On integrity appraisal error, assuming the file
- is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/ -int ima_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
+static int ima_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
{ u32 secid; int ret; @@ -466,8 +466,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
- On mprotect change success, return 0. On failure, return -EACESS.
*/ -int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
+static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
{ struct ima_template_desc *template = NULL; struct file *file; @@ -525,7 +525,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
- On success return 0. On integrity appraisal error, assuming the file
- is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/ -int ima_bprm_check(struct linux_binprm *bprm) +static int ima_bprm_check(struct linux_binprm *bprm) { int ret; u32 secid; @@ -551,7 +551,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
- On success return 0. On integrity appraisal error, assuming the file
- is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/ -int ima_file_check(struct file *file, int mask) +static int ima_file_check(struct file *file, int mask) { u32 secid; @@ -560,7 +560,6 @@ int ima_file_check(struct file *file, int mask) mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); } -EXPORT_SYMBOL_GPL(ima_file_check); static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf, size_t buf_size) @@ -685,8 +684,9 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
- Skip calling process_measurement(), but indicate which newly, created
- tmpfiles are in policy.
*/ -void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode)
+static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode)
{ struct integrity_iint_cache *iint; int must_appraise; @@ -717,8 +717,7 @@ void ima_post_create_tmpfile(struct mnt_idmap *idmap,
- Mark files created via the mknodat syscall as new, so that the
- file data can be written later.
*/ -void ima_post_path_mknod(struct mnt_idmap *idmap,
struct dentry *dentry)
+static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) { struct integrity_iint_cache *iint; struct inode *inode = dentry->d_inode; @@ -753,8 +752,8 @@ void ima_post_path_mknod(struct mnt_idmap *idmap,
- For permission return 0, otherwise return -EACCES.
*/ -int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
bool contents)
+static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
bool contents)
{ enum ima_hooks func; u32 secid; @@ -803,8 +802,8 @@ const int read_idmap[READING_MAX_ID] = {
- On success return 0. On integrity appraisal error, assuming the file
- is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/ -int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id read_id)
+static int ima_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id read_id)
{ enum ima_hooks func; u32 secid; @@ -837,7 +836,7 @@ int ima_post_read_file(struct file *file, char *buf, loff_t size,
- For permission return 0, otherwise return -EACCES.
*/ -int ima_load_data(enum kernel_load_data_id id, bool contents) +static int ima_load_data(enum kernel_load_data_id id, bool contents) { bool ima_enforce, sig_enforce; @@ -891,9 +890,9 @@ int ima_load_data(enum kernel_load_data_id id, bool contents)
- On success return 0. On integrity appraisal error, assuming the file
- is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/ -int ima_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id load_id,
char *description)
+static int ima_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id load_id,
char *description)
{ if (load_id == LOADING_FIRMWARE) { if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && @@ -1110,7 +1109,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data);
- Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
*/ -int ima_kernel_module_request(char *kmod_name) +static int ima_kernel_module_request(char *kmod_name) { if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) return -EINVAL; @@ -1151,4 +1150,41 @@ static int __init init_ima(void) return error; } +static struct security_hook_list ima_hooks[] __ro_after_init = {
- LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
- LSM_HOOK_INIT(file_post_open, ima_file_check),
- LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
- LSM_HOOK_INIT(file_release, ima_file_free),
- LSM_HOOK_INIT(mmap_file, ima_file_mmap),
- LSM_HOOK_INIT(file_mprotect, ima_file_mprotect),
- LSM_HOOK_INIT(kernel_load_data, ima_load_data),
- LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data),
- LSM_HOOK_INIT(kernel_read_file, ima_read_file),
- LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file),
- LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
+#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
- LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update),
+#endif +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
- LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request),
+#endif +};
+static const struct lsm_id ima_lsmid = {
- .name = "ima",
- .id = LSM_ID_IMA,
+};
+static int __init init_ima_lsm(void) +{
- security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
- return 0;
+}
+DEFINE_LSM(ima) = {
- .name = "ima",
- .init = init_ima_lsm,
- .order = LSM_ORDER_LAST,
+};
late_initcall(init_ima); /* Start IMA after the TPM is available */ diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 9561db7cf6b4..59eaddd84434 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -18,6 +18,7 @@ #include <crypto/hash.h> #include <linux/key.h> #include <linux/audit.h> +#include <linux/lsm_hooks.h> /* iint action cache flags */ #define IMA_MEASURE 0x00000001 diff --git a/security/keys/key.c b/security/keys/key.c index f75fe66c2f03..80fc2f203a0c 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -13,7 +13,6 @@ #include <linux/security.h> #include <linux/workqueue.h> #include <linux/random.h> -#include <linux/ima.h> #include <linux/err.h> #include "internal.h" @@ -937,8 +936,6 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref, security_key_post_create_or_update(keyring, key, payload, plen, flags, true);
- ima_post_key_create_or_update(keyring, key, payload, plen,
flags, true);
key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); @@ -970,13 +967,9 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref, key_ref = __key_update(key_ref, &prep);
- if (!IS_ERR(key_ref)) {
- if (!IS_ERR(key_ref)) security_key_post_create_or_update(keyring, key, payload, plen, flags, false);
ima_post_key_create_or_update(keyring, key,
payload, plen,
flags, false);
- }
goto error_free_prep; } diff --git a/security/security.c b/security/security.c index 6c6571a141a1..aa17b47d44c1 100644 --- a/security/security.c +++ b/security/security.c @@ -50,7 +50,8 @@ (IS_ENABLED(CONFIG_SECURITY_SAFESETID) ? 1 : 0) + \ (IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \ (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0))
- (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_IMA) ? 1 : 0))
/*
- These are descriptions of the reasons that can be passed to the
@@ -1182,12 +1183,7 @@ int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file * */ int security_bprm_check(struct linux_binprm *bprm) {
- int ret;
- ret = call_int_hook(bprm_check_security, 0, bprm);
- if (ret)
return ret;
- return ima_bprm_check(bprm);
- return call_int_hook(bprm_check_security, 0, bprm);
} /** @@ -2901,13 +2897,8 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot) int security_mmap_file(struct file *file, unsigned long prot, unsigned long flags) {
- unsigned long prot_adj = mmap_prot(file, prot);
- int ret;
- ret = call_int_hook(mmap_file, 0, file, prot, prot_adj, flags);
- if (ret)
return ret;
- return ima_file_mmap(file, prot, prot_adj, flags);
- return call_int_hook(mmap_file, 0, file, prot, mmap_prot(file, prot),
flags);
} /** @@ -2936,12 +2927,7 @@ int security_mmap_addr(unsigned long addr) int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) {
- int ret;
- ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot);
- if (ret)
return ret;
- return ima_file_mprotect(vma, reqprot, prot);
- return call_int_hook(file_mprotect, 0, vma, reqprot, prot);
} /** @@ -3250,12 +3236,7 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) */ int security_kernel_module_request(char *kmod_name) {
- int ret;
- ret = call_int_hook(kernel_module_request, 0, kmod_name);
- if (ret)
return ret;
- return ima_kernel_module_request(kmod_name);
- return call_int_hook(kernel_module_request, 0, kmod_name);
} /** @@ -3271,12 +3252,7 @@ int security_kernel_module_request(char *kmod_name) int security_kernel_read_file(struct file *file, enum kernel_read_file_id id, bool contents) {
- int ret;
- ret = call_int_hook(kernel_read_file, 0, file, id, contents);
- if (ret)
return ret;
- return ima_read_file(file, id, contents);
- return call_int_hook(kernel_read_file, 0, file, id, contents);
} EXPORT_SYMBOL_GPL(security_kernel_read_file); @@ -3296,12 +3272,7 @@ EXPORT_SYMBOL_GPL(security_kernel_read_file); int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, enum kernel_read_file_id id) {
- int ret;
- ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
- if (ret)
return ret;
- return ima_post_read_file(file, buf, size, id);
- return call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
} EXPORT_SYMBOL_GPL(security_kernel_post_read_file); @@ -3316,12 +3287,7 @@ EXPORT_SYMBOL_GPL(security_kernel_post_read_file); */ int security_kernel_load_data(enum kernel_load_data_id id, bool contents) {
- int ret;
- ret = call_int_hook(kernel_load_data, 0, id, contents);
- if (ret)
return ret;
- return ima_load_data(id, contents);
- return call_int_hook(kernel_load_data, 0, id, contents);
} EXPORT_SYMBOL_GPL(security_kernel_load_data); @@ -3343,13 +3309,8 @@ int security_kernel_post_load_data(char *buf, loff_t size, enum kernel_load_data_id id, char *description) {
- int ret;
- ret = call_int_hook(kernel_post_load_data, 0, buf, size, id,
description);
- if (ret)
return ret;
- return ima_post_load_data(buf, size, id, description);
- return call_int_hook(kernel_post_load_data, 0, buf, size, id,
description);
} EXPORT_SYMBOL_GPL(security_kernel_post_load_data); diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c index 9df29b1e3497..17333787cb2f 100644 --- a/tools/testing/selftests/lsm/lsm_list_modules_test.c +++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c @@ -122,6 +122,9 @@ TEST(correct_lsm_list_modules) case LSM_ID_LANDLOCK: name = "landlock"; break;
case LSM_ID_IMA:
name = "ima";
default: name = "INVALID"; break;break;
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Move hardcoded IMA function calls (not appraisal-specific functions) from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'ima' (at the end of the LSM list and always enabled like 'integrity').
Having IMA before EVM in the Makefile is sufficient to preserve the relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM, and thus the order of IMA and EVM function calls as when they were hardcoded.
Make moved functions as static (except ima_post_key_create_or_update(), which is not in ima_main.c), and register them as implementation of the respective hooks in the new function init_ima_lsm().
Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook path_post_mknod is always available and ima_post_path_mknod() is always executed to mark files as new, as before the move.
A slight difference is that IMA and EVM functions registered for the inode_post_setattr, inode_post_removexattr, path_post_mknod, inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl won't be executed for private inodes. Since those inodes are supposed to be fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub and kernel-internal tmpfs files.
Conditionally register ima_post_key_create_or_update() if CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Chuck Lever chuck.lever@oracle.com Acked-by: Casey Schaufler casey@schaufler-ca.com
fs/file_table.c | 2 - fs/namei.c | 6 - fs/nfsd/vfs.c | 7 -- fs/open.c | 1 - include/linux/ima.h | 104 ------------------ include/uapi/linux/lsm.h | 1 + security/integrity/Makefile | 1 + security/integrity/ima/Kconfig | 1 + security/integrity/ima/ima.h | 6 + security/integrity/ima/ima_main.c | 78 +++++++++---- security/integrity/integrity.h | 1 + security/keys/key.c | 9 +- security/security.c | 63 ++--------- .../selftests/lsm/lsm_list_modules_test.c | 3 + 14 files changed, 83 insertions(+), 200 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:18:04PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Move hardcoded IMA function calls (not appraisal-specific functions) from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'ima' (at the end of the LSM list and always enabled like 'integrity').
Having IMA before EVM in the Makefile is sufficient to preserve the relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM, and thus the order of IMA and EVM function calls as when they were hardcoded.
Make moved functions as static (except ima_post_key_create_or_update(), which is not in ima_main.c), and register them as implementation of the respective hooks in the new function init_ima_lsm().
Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook path_post_mknod is always available and ima_post_path_mknod() is always executed to mark files as new, as before the move.
A slight difference is that IMA and EVM functions registered for the inode_post_setattr, inode_post_removexattr, path_post_mknod, inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl won't be executed for private inodes. Since those inodes are supposed to be fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub and kernel-internal tmpfs files.
Conditionally register ima_post_key_create_or_update() if CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Chuck Lever chuck.lever@oracle.com
fs/file_table.c | 2 - fs/namei.c | 6 - fs/nfsd/vfs.c | 7 -- fs/open.c | 1 -
Acked-by: Christian Brauner brauner@kernel.org
On 1/15/24 13:18, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Move hardcoded IMA function calls (not appraisal-specific functions) from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'ima' (at the end of the LSM list and always enabled like 'integrity').
Having IMA before EVM in the Makefile is sufficient to preserve the relative order of the new 'ima' LSM in respect to the upcoming 'evm' LSM, and thus the order of IMA and EVM function calls as when they were hardcoded.
Make moved functions as static (except ima_post_key_create_or_update(), which is not in ima_main.c), and register them as implementation of the respective hooks in the new function init_ima_lsm().
Select CONFIG_SECURITY_PATH, to ensure that the path-based LSM hook path_post_mknod is always available and ima_post_path_mknod() is always executed to mark files as new, as before the move.
A slight difference is that IMA and EVM functions registered for the inode_post_setattr, inode_post_removexattr, path_post_mknod, inode_post_create_tmpfile, inode_post_set_acl and inode_post_remove_acl won't be executed for private inodes. Since those inodes are supposed to be fs-internal, they should not be of interest of IMA or EVM. The S_PRIVATE
of interest 'to'
flag is used for anonymous inodes, hugetlbfs, reiserfs xattrs, XFS scrub and kernel-internal tmpfs files.
Conditionally register ima_post_key_create_or_update() if CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS is enabled. Also, conditionally register ima_kernel_module_request() if CONFIG_INTEGRITY_ASYMMETRIC_KEYS is enabled.
Finally, add the LSM_ID_IMA case in lsm_list_modules_test.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Chuck Lever chuck.lever@oracle.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
From: Roberto Sassu roberto.sassu@huawei.com
A few additional IMA hooks are needed to reset the cached appraisal status, causing the file's integrity to be re-evaluated on next access. Register these IMA-appraisal only functions separately from the rest of IMA functions, as appraisal is a separate feature not necessarily enabled in the kernel configuration.
Reuse the same approach as for other IMA functions, move hardcoded calls from various places in the kernel to the LSM infrastructure. Declare the functions as static and register them as hook implementations in init_ima_appraise_lsm(), called by init_ima_lsm().
Also move the inline function ima_inode_remove_acl() from the public ima.h header to ima_appraise.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com --- fs/attr.c | 2 - include/linux/ima.h | 55 --------------------------- security/integrity/ima/ima.h | 5 +++ security/integrity/ima/ima_appraise.c | 38 +++++++++++++----- security/integrity/ima/ima_main.c | 1 + security/security.c | 13 ------- 6 files changed, 35 insertions(+), 79 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c index 221d2bb0a906..38841f3ebbcb 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -17,7 +17,6 @@ #include <linux/filelock.h> #include <linux/security.h> #include <linux/evm.h> -#include <linux/ima.h>
#include "internal.h"
@@ -503,7 +502,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry, if (!error) { fsnotify_change(dentry, ia_valid); security_inode_post_setattr(idmap, dentry, ia_valid); - ima_inode_post_setattr(idmap, dentry, ia_valid); evm_inode_post_setattr(idmap, dentry, ia_valid); }
diff --git a/include/linux/ima.h b/include/linux/ima.h index 23ae24b60ecf..0bae61a15b60 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -92,66 +92,11 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
#ifdef CONFIG_IMA_APPRAISE extern bool is_ima_appraise_enabled(void); -extern void ima_inode_post_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, int ia_valid); -extern int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, - const char *xattr_name, const void *xattr_value, - size_t xattr_value_len, int flags); -extern int ima_inode_set_acl(struct mnt_idmap *idmap, - struct dentry *dentry, const char *acl_name, - struct posix_acl *kacl); -static inline int ima_inode_remove_acl(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *acl_name) -{ - return ima_inode_set_acl(idmap, dentry, acl_name, NULL); -} - -extern int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, - const char *xattr_name); #else static inline bool is_ima_appraise_enabled(void) { return 0; } - -static inline void ima_inode_post_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, int ia_valid) -{ - return; -} - -static inline int ima_inode_setxattr(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *xattr_name, - const void *xattr_value, - size_t xattr_value_len, - int flags) -{ - return 0; -} - -static inline int ima_inode_set_acl(struct mnt_idmap *idmap, - struct dentry *dentry, const char *acl_name, - struct posix_acl *kacl) -{ - - return 0; -} - -static inline int ima_inode_removexattr(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *xattr_name) -{ - return 0; -} - -static inline int ima_inode_remove_acl(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *acl_name) -{ - return 0; -} #endif /* CONFIG_IMA_APPRAISE */
#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c0412100023e..a27fc10f84f7 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -334,6 +334,7 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, int xattr_len); int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value, int xattr_len); +void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);
#else static inline int ima_check_blacklist(struct integrity_iint_cache *iint, @@ -385,6 +386,10 @@ static inline int ima_read_xattr(struct dentry *dentry, return 0; }
+static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid) +{ +} + #endif /* CONFIG_IMA_APPRAISE */
#ifdef CONFIG_IMA_APPRAISE_MODSIG diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 36abc84ba299..076451109637 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -636,8 +636,8 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) * This function is called from notify_change(), which expects the caller * to lock the inode's i_mutex. */ -void ima_inode_post_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, int ia_valid) +static void ima_inode_post_setattr(struct mnt_idmap *idmap, + struct dentry *dentry, int ia_valid) { struct inode *inode = d_backing_inode(dentry); struct integrity_iint_cache *iint; @@ -750,9 +750,9 @@ static int validate_hash_algo(struct dentry *dentry, return -EACCES; }
-int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, - const char *xattr_name, const void *xattr_value, - size_t xattr_value_len, int flags) +static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name, const void *xattr_value, + size_t xattr_value_len, int flags) { const struct evm_ima_xattr_data *xvalue = xattr_value; int digsig = 0; @@ -781,8 +781,8 @@ int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, return result; }
-int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, - const char *acl_name, struct posix_acl *kacl) +static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, + const char *acl_name, struct posix_acl *kacl) { if (evm_revalidate_status(acl_name)) ima_reset_appraise_flags(d_backing_inode(dentry), 0); @@ -790,8 +790,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, return 0; }
-int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, - const char *xattr_name) +static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name) { int result;
@@ -803,3 +803,23 @@ int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, } return result; } + +static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, + const char *acl_name) +{ + return ima_inode_set_acl(idmap, dentry, acl_name, NULL); +} + +static struct security_hook_list ima_appraise_hooks[] __ro_after_init = { + LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr), + LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr), + LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl), + LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr), + LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl), +}; + +void __init init_ima_appraise_lsm(const struct lsm_id *lsmid) +{ + security_add_hooks(ima_appraise_hooks, ARRAY_SIZE(ima_appraise_hooks), + lsmid); +} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 483ccd24e214..77d0d4e4b582 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1178,6 +1178,7 @@ static const struct lsm_id ima_lsmid = { static int __init init_ima_lsm(void) { security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid); + init_ima_appraise_lsm(&ima_lsmid); return 0; }
diff --git a/security/security.c b/security/security.c index aa17b47d44c1..c07ad3c5f767 100644 --- a/security/security.c +++ b/security/security.c @@ -20,7 +20,6 @@ #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> #include <linux/integrity.h> -#include <linux/ima.h> #include <linux/evm.h> #include <linux/fsnotify.h> #include <linux/mman.h> @@ -2308,9 +2307,6 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
if (ret == 1) ret = cap_inode_setxattr(dentry, name, value, size, flags); - if (ret) - return ret; - ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags); if (ret) return ret; return evm_inode_setxattr(idmap, dentry, name, value, size, flags); @@ -2338,9 +2334,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap, return 0; ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name, kacl); - if (ret) - return ret; - ret = ima_inode_set_acl(idmap, dentry, acl_name, kacl); if (ret) return ret; return evm_inode_set_acl(idmap, dentry, acl_name, kacl); @@ -2401,9 +2394,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name); - if (ret) - return ret; - ret = ima_inode_remove_acl(idmap, dentry, acl_name); if (ret) return ret; return evm_inode_remove_acl(idmap, dentry, acl_name); @@ -2503,9 +2493,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap, ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name); if (ret == 1) ret = cap_inode_removexattr(idmap, dentry, name); - if (ret) - return ret; - ret = ima_inode_removexattr(idmap, dentry, name); if (ret) return ret; return evm_inode_removexattr(idmap, dentry, name);
On 1/15/2024 10:18 AM, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
A few additional IMA hooks are needed to reset the cached appraisal status, causing the file's integrity to be re-evaluated on next access. Register these IMA-appraisal only functions separately from the rest of IMA functions, as appraisal is a separate feature not necessarily enabled in the kernel configuration.
Reuse the same approach as for other IMA functions, move hardcoded calls from various places in the kernel to the LSM infrastructure. Declare the functions as static and register them as hook implementations in init_ima_appraise_lsm(), called by init_ima_lsm().
Also move the inline function ima_inode_remove_acl() from the public ima.h header to ima_appraise.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
Reviewed-by: Casey Schaufler casey@schaufler-ca.com
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);
evm_inode_post_setattr(idmap, dentry, ia_valid); }ima_inode_post_setattr(idmap, dentry, ia_valid);
diff --git a/include/linux/ima.h b/include/linux/ima.h index 23ae24b60ecf..0bae61a15b60 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -92,66 +92,11 @@ static inline void ima_add_kexec_buffer(struct kimage *image) #ifdef CONFIG_IMA_APPRAISE extern bool is_ima_appraise_enabled(void); -extern void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid);
-extern int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name, const void *xattr_value,
size_t xattr_value_len, int flags);
-extern int ima_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl);
-static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry,
const char *acl_name)
-{
- return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
-}
-extern int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name);
#else static inline bool is_ima_appraise_enabled(void) { return 0; }
-static inline void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid)
-{
- return;
-}
-static inline int ima_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry,
const char *xattr_name,
const void *xattr_value,
size_t xattr_value_len,
int flags)
-{
- return 0;
-}
-static inline int ima_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
-{
- return 0;
-}
-static inline int ima_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry,
const char *xattr_name)
-{
- return 0;
-}
-static inline int ima_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry,
const char *acl_name)
-{
- return 0;
-} #endif /* CONFIG_IMA_APPRAISE */ #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c0412100023e..a27fc10f84f7 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -334,6 +334,7 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, int xattr_len); int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value, int xattr_len); +void __init init_ima_appraise_lsm(const struct lsm_id *lsmid); #else static inline int ima_check_blacklist(struct integrity_iint_cache *iint, @@ -385,6 +386,10 @@ static inline int ima_read_xattr(struct dentry *dentry, return 0; } +static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid) +{ +}
#endif /* CONFIG_IMA_APPRAISE */ #ifdef CONFIG_IMA_APPRAISE_MODSIG diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 36abc84ba299..076451109637 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -636,8 +636,8 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
- This function is called from notify_change(), which expects the caller
- to lock the inode's i_mutex.
*/ -void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid)
+static void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid)
{ struct inode *inode = d_backing_inode(dentry); struct integrity_iint_cache *iint; @@ -750,9 +750,9 @@ static int validate_hash_algo(struct dentry *dentry, return -EACCES; } -int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name, const void *xattr_value,
size_t xattr_value_len, int flags)
+static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name, const void *xattr_value,
size_t xattr_value_len, int flags)
{ const struct evm_ima_xattr_data *xvalue = xattr_value; int digsig = 0; @@ -781,8 +781,8 @@ int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, return result; } -int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name, struct posix_acl *kacl)
+static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name, struct posix_acl *kacl)
{ if (evm_revalidate_status(acl_name)) ima_reset_appraise_flags(d_backing_inode(dentry), 0); @@ -790,8 +790,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, return 0; } -int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name)
+static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name)
{ int result; @@ -803,3 +803,23 @@ int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, } return result; }
+static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name)
+{
- return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
+}
+static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
- LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
- LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr),
- LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
- LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr),
- LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
+};
+void __init init_ima_appraise_lsm(const struct lsm_id *lsmid) +{
- security_add_hooks(ima_appraise_hooks, ARRAY_SIZE(ima_appraise_hooks),
lsmid);
+} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 483ccd24e214..77d0d4e4b582 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1178,6 +1178,7 @@ static const struct lsm_id ima_lsmid = { static int __init init_ima_lsm(void) { security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
- init_ima_appraise_lsm(&ima_lsmid); return 0;
} diff --git a/security/security.c b/security/security.c index aa17b47d44c1..c07ad3c5f767 100644 --- a/security/security.c +++ b/security/security.c @@ -20,7 +20,6 @@ #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> #include <linux/integrity.h> -#include <linux/ima.h> #include <linux/evm.h> #include <linux/fsnotify.h> #include <linux/mman.h> @@ -2308,9 +2307,6 @@ int security_inode_setxattr(struct mnt_idmap *idmap, if (ret == 1) ret = cap_inode_setxattr(dentry, name, value, size, flags);
- if (ret)
return ret;
- ret = ima_inode_setxattr(idmap, dentry, name, value, size, flags); if (ret) return ret; return evm_inode_setxattr(idmap, dentry, name, value, size, flags);
@@ -2338,9 +2334,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap, return 0; ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name, kacl);
- if (ret)
return ret;
- ret = ima_inode_set_acl(idmap, dentry, acl_name, kacl); if (ret) return ret; return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
@@ -2401,9 +2394,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
- if (ret)
return ret;
- ret = ima_inode_remove_acl(idmap, dentry, acl_name); if (ret) return ret; return evm_inode_remove_acl(idmap, dentry, acl_name);
@@ -2503,9 +2493,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap, ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name); if (ret == 1) ret = cap_inode_removexattr(idmap, dentry, name);
- if (ret)
return ret;
- ret = ima_inode_removexattr(idmap, dentry, name); if (ret) return ret; return evm_inode_removexattr(idmap, dentry, name);
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
A few additional IMA hooks are needed to reset the cached appraisal status, causing the file's integrity to be re-evaluated on next access. Register these IMA-appraisal only functions separately from the rest of IMA functions, as appraisal is a separate feature not necessarily enabled in the kernel configuration.
Reuse the same approach as for other IMA functions, move hardcoded calls from various places in the kernel to the LSM infrastructure. Declare the functions as static and register them as hook implementations in init_ima_appraise_lsm(), called by init_ima_lsm().
Also move the inline function ima_inode_remove_acl() from the public ima.h header to ima_appraise.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
fs/attr.c | 2 - include/linux/ima.h | 55 --------------------------- security/integrity/ima/ima.h | 5 +++ security/integrity/ima/ima_appraise.c | 38 +++++++++++++----- security/integrity/ima/ima_main.c | 1 + security/security.c | 13 ------- 6 files changed, 35 insertions(+), 79 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:18:05PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
A few additional IMA hooks are needed to reset the cached appraisal status, causing the file's integrity to be re-evaluated on next access. Register these IMA-appraisal only functions separately from the rest of IMA functions, as appraisal is a separate feature not necessarily enabled in the kernel configuration.
Reuse the same approach as for other IMA functions, move hardcoded calls from various places in the kernel to the LSM infrastructure. Declare the functions as static and register them as hook implementations in init_ima_appraise_lsm(), called by init_ima_lsm().
Also move the inline function ima_inode_remove_acl() from the public ima.h header to ima_appraise.c.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Stefan Berger stefanb@linux.ibm.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com
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);
evm_inode_post_setattr(idmap, dentry, ia_valid); }ima_inode_post_setattr(idmap, dentry, ia_valid);
Acked-by: Christian Brauner brauner@kernel.org
From: Roberto Sassu roberto.sassu@huawei.com
As for IMA, move hardcoded EVM function calls from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'evm' (last and always enabled like 'ima'). The order in the Makefile ensures that 'evm' hooks are executed after 'ima' ones.
Make EVM functions as static (except for evm_inode_init_security(), which is exported), and register them as hook implementations in init_evm_lsm(). Also move the inline functions evm_inode_remove_acl(), evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public evm.h header to evm_main.c.
Unlike before (see commit to move IMA to the LSM infrastructure), evm_inode_post_setattr(), evm_inode_post_set_acl(), evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not executed for private inodes.
Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com --- fs/attr.c | 2 - fs/posix_acl.c | 3 - fs/xattr.c | 2 - include/linux/evm.h | 113 ----------------- include/uapi/linux/lsm.h | 1 + security/integrity/evm/evm_main.c | 118 +++++++++++++++--- security/security.c | 43 ++----- .../selftests/lsm/lsm_list_modules_test.c | 3 + 8 files changed, 116 insertions(+), 169 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c index 38841f3ebbcb..b51bd7c9b4a7 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -16,7 +16,6 @@ #include <linux/fcntl.h> #include <linux/filelock.h> #include <linux/security.h> -#include <linux/evm.h>
#include "internal.h"
@@ -502,7 +501,6 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry, if (!error) { fsnotify_change(dentry, ia_valid); security_inode_post_setattr(idmap, dentry, ia_valid); - evm_inode_post_setattr(idmap, dentry, ia_valid); }
return error; diff --git a/fs/posix_acl.c b/fs/posix_acl.c index e3fbe1a9f3f5..ae67479cd2b6 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -26,7 +26,6 @@ #include <linux/mnt_idmapping.h> #include <linux/iversion.h> #include <linux/security.h> -#include <linux/evm.h> #include <linux/fsnotify.h> #include <linux/filelock.h>
@@ -1138,7 +1137,6 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (!error) { fsnotify_xattr(dentry); security_inode_post_set_acl(dentry, acl_name, kacl); - evm_inode_post_set_acl(dentry, acl_name, kacl); }
out_inode_unlock: @@ -1247,7 +1245,6 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (!error) { fsnotify_xattr(dentry); security_inode_post_remove_acl(idmap, dentry, acl_name); - evm_inode_post_remove_acl(idmap, dentry, acl_name); }
out_inode_unlock: diff --git a/fs/xattr.c b/fs/xattr.c index f891c260a971..f8b643f91a98 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -16,7 +16,6 @@ #include <linux/mount.h> #include <linux/namei.h> #include <linux/security.h> -#include <linux/evm.h> #include <linux/syscalls.h> #include <linux/export.h> #include <linux/fsnotify.h> @@ -557,7 +556,6 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,
fsnotify_xattr(dentry); security_inode_post_removexattr(dentry, name); - evm_inode_post_removexattr(dentry, name);
out: return error; diff --git a/include/linux/evm.h b/include/linux/evm.h index 3faabdd47852..cb481eccc967 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -21,45 +21,6 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry, void *xattr_value, size_t xattr_value_len, struct integrity_iint_cache *iint); -extern int evm_inode_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, struct iattr *attr); -extern void evm_inode_post_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, int ia_valid); -extern int evm_inode_setxattr(struct mnt_idmap *idmap, - struct dentry *dentry, const char *name, - const void *value, size_t size, int flags); -extern void evm_inode_post_setxattr(struct dentry *dentry, - const char *xattr_name, - const void *xattr_value, - size_t xattr_value_len, - int flags); -extern int evm_inode_copy_up_xattr(const char *name); -extern int evm_inode_removexattr(struct mnt_idmap *idmap, - struct dentry *dentry, const char *xattr_name); -extern void evm_inode_post_removexattr(struct dentry *dentry, - const char *xattr_name); -static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *acl_name) -{ - evm_inode_post_removexattr(dentry, acl_name); -} -extern int evm_inode_set_acl(struct mnt_idmap *idmap, - struct dentry *dentry, const char *acl_name, - struct posix_acl *kacl); -static inline int evm_inode_remove_acl(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *acl_name) -{ - return evm_inode_set_acl(idmap, dentry, acl_name, NULL); -} -static inline void evm_inode_post_set_acl(struct dentry *dentry, - const char *acl_name, - struct posix_acl *kacl) -{ - return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0); -} - int evm_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, struct xattr *xattrs, int *xattr_count); @@ -94,80 +55,6 @@ static inline enum integrity_status evm_verifyxattr(struct dentry *dentry, } #endif
-static inline int evm_inode_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, struct iattr *attr) -{ - return 0; -} - -static inline void evm_inode_post_setattr(struct mnt_idmap *idmap, - struct dentry *dentry, int ia_valid) -{ - return; -} - -static inline int evm_inode_setxattr(struct mnt_idmap *idmap, - struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) -{ - return 0; -} - -static inline void evm_inode_post_setxattr(struct dentry *dentry, - const char *xattr_name, - const void *xattr_value, - size_t xattr_value_len, - int flags) -{ - return; -} - -static inline int evm_inode_copy_up_xattr(const char *name) -{ - return 0; -} - -static inline int evm_inode_removexattr(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *xattr_name) -{ - return 0; -} - -static inline void evm_inode_post_removexattr(struct dentry *dentry, - const char *xattr_name) -{ - return; -} - -static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *acl_name) -{ - return; -} - -static inline int evm_inode_set_acl(struct mnt_idmap *idmap, - struct dentry *dentry, const char *acl_name, - struct posix_acl *kacl) -{ - return 0; -} - -static inline int evm_inode_remove_acl(struct mnt_idmap *idmap, - struct dentry *dentry, - const char *acl_name) -{ - return 0; -} - -static inline void evm_inode_post_set_acl(struct dentry *dentry, - const char *acl_name, - struct posix_acl *kacl) -{ - return; -} - static inline int evm_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, struct xattr *xattrs, diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h index b3b7fd699b63..33d8c9f4aa6b 100644 --- a/include/uapi/linux/lsm.h +++ b/include/uapi/linux/lsm.h @@ -63,6 +63,7 @@ struct lsm_ctx { #define LSM_ID_BPF 109 #define LSM_ID_LANDLOCK 110 #define LSM_ID_IMA 111 +#define LSM_ID_EVM 112
/* * LSM_ATTR_XXX definitions identify different LSM attributes diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index d35143179699..f65dbf3e9256 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -589,9 +589,9 @@ static int evm_protect_xattr(struct mnt_idmap *idmap, * userspace from writing HMAC value. Writing 'security.evm' requires * requires CAP_SYS_ADMIN privileges. */ -int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, - const char *xattr_name, const void *xattr_value, - size_t xattr_value_len, int flags) +static int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name, const void *xattr_value, + size_t xattr_value_len, int flags) { const struct evm_ima_xattr_data *xattr_data = xattr_value;
@@ -621,8 +621,8 @@ int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that * the current value is valid. */ -int evm_inode_removexattr(struct mnt_idmap *idmap, - struct dentry *dentry, const char *xattr_name) +static int evm_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, + const char *xattr_name) { /* Policy permits modification of the protected xattrs even though * there's no HMAC key loaded @@ -672,9 +672,11 @@ static inline int evm_inode_set_acl_change(struct mnt_idmap *idmap, * Prevent modifying posix acls causing the EVM HMAC to be re-calculated * and 'security.evm' xattr updated, unless the existing 'security.evm' is * valid. + * + * Return: zero on success, -EPERM on failure. */ -int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, - const char *acl_name, struct posix_acl *kacl) +static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, + const char *acl_name, struct posix_acl *kacl) { enum integrity_status evm_status;
@@ -713,6 +715,24 @@ int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, return -EPERM; }
+/** + * evm_inode_remove_acl - Protect the EVM extended attribute from posix acls + * @idmap: idmap of the mount + * @dentry: pointer to the affected dentry + * @acl_name: name of the posix acl + * + * Prevent removing posix acls causing the EVM HMAC to be re-calculated + * and 'security.evm' xattr updated, unless the existing 'security.evm' is + * valid. + * + * Return: zero on success, -EPERM on failure. + */ +static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, + const char *acl_name) +{ + return evm_inode_set_acl(idmap, dentry, acl_name, NULL); +} + static void evm_reset_status(struct inode *inode) { struct integrity_iint_cache *iint; @@ -761,9 +781,11 @@ bool evm_revalidate_status(const char *xattr_name) * __vfs_setxattr_noperm(). The caller of which has taken the inode's * i_mutex lock. */ -void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, - const void *xattr_value, size_t xattr_value_len, - int flags) +static void evm_inode_post_setxattr(struct dentry *dentry, + const char *xattr_name, + const void *xattr_value, + size_t xattr_value_len, + int flags) { if (!evm_revalidate_status(xattr_name)) return; @@ -782,6 +804,21 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); }
+/** + * evm_inode_post_set_acl - Update the EVM extended attribute from posix acls + * @dentry: pointer to the affected dentry + * @acl_name: name of the posix acl + * @kacl: pointer to the posix acls + * + * Update the 'security.evm' xattr with the EVM HMAC re-calculated after setting + * posix acls. + */ +static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name, + struct posix_acl *kacl) +{ + return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0); +} + /** * evm_inode_post_removexattr - update 'security.evm' after removing the xattr * @dentry: pointer to the affected dentry @@ -792,7 +829,8 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, * No need to take the i_mutex lock here, as this function is called from * vfs_removexattr() which takes the i_mutex. */ -void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) +static void evm_inode_post_removexattr(struct dentry *dentry, + const char *xattr_name) { if (!evm_revalidate_status(xattr_name)) return; @@ -808,6 +846,22 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) evm_update_evmxattr(dentry, xattr_name, NULL, 0); }
+/** + * evm_inode_post_remove_acl - Update the EVM extended attribute from posix acls + * @idmap: idmap of the mount + * @dentry: pointer to the affected dentry + * @acl_name: name of the posix acl + * + * Update the 'security.evm' xattr with the EVM HMAC re-calculated after + * removing posix acls. + */ +static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap, + struct dentry *dentry, + const char *acl_name) +{ + evm_inode_post_removexattr(dentry, acl_name); +} + static int evm_attr_change(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { @@ -831,8 +885,8 @@ static int evm_attr_change(struct mnt_idmap *idmap, * Permit update of file attributes when files have a valid EVM signature, * except in the case of them having an immutable portable signature. */ -int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, - struct iattr *attr) +static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) { unsigned int ia_valid = attr->ia_valid; enum integrity_status evm_status; @@ -883,8 +937,8 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, * This function is called from notify_change(), which expects the caller * to lock the inode's i_mutex. */ -void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, - int ia_valid) +static void evm_inode_post_setattr(struct mnt_idmap *idmap, + struct dentry *dentry, int ia_valid) { if (!evm_revalidate_status(NULL)) return; @@ -901,7 +955,7 @@ void evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, evm_update_evmxattr(dentry, NULL, NULL, 0); }
-int evm_inode_copy_up_xattr(const char *name) +static int evm_inode_copy_up_xattr(const char *name) { if (strcmp(name, XATTR_NAME_EVM) == 0) return 1; /* Discard */ @@ -1004,4 +1058,36 @@ static int __init init_evm(void) return error; }
+static struct security_hook_list evm_hooks[] __ro_after_init = { + LSM_HOOK_INIT(inode_setattr, evm_inode_setattr), + LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr), + LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr), + LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr), + LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl), + LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl), + LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl), + LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl), + LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr), + LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr), + LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr), + LSM_HOOK_INIT(inode_init_security, evm_inode_init_security), +}; + +static const struct lsm_id evm_lsmid = { + .name = "evm", + .id = LSM_ID_EVM, +}; + +static int __init init_evm_lsm(void) +{ + security_add_hooks(evm_hooks, ARRAY_SIZE(evm_hooks), &evm_lsmid); + return 0; +} + +DEFINE_LSM(evm) = { + .name = "evm", + .init = init_evm_lsm, + .order = LSM_ORDER_LAST, +}; + late_initcall(init_evm); diff --git a/security/security.c b/security/security.c index c07ad3c5f767..a44740640a9a 100644 --- a/security/security.c +++ b/security/security.c @@ -20,13 +20,13 @@ #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> #include <linux/integrity.h> -#include <linux/evm.h> #include <linux/fsnotify.h> #include <linux/mman.h> #include <linux/mount.h> #include <linux/personality.h> #include <linux/backing-dev.h> #include <linux/string.h> +#include <linux/xattr.h> #include <linux/msg.h> #include <net/flow.h>
@@ -50,7 +50,8 @@ (IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \ (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \ (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0) + \ - (IS_ENABLED(CONFIG_IMA) ? 1 : 0)) + (IS_ENABLED(CONFIG_IMA) ? 1 : 0) + \ + (IS_ENABLED(CONFIG_EVM) ? 1 : 0))
/* * These are descriptions of the reasons that can be passed to the @@ -1740,10 +1741,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, if (!xattr_count) goto out;
- ret = evm_inode_init_security(inode, dir, qstr, new_xattrs, - &xattr_count); - if (ret) - goto out; ret = initxattrs(inode, new_xattrs, fs_data); out: for (; xattr_count > 0; xattr_count--) @@ -2235,14 +2232,9 @@ int security_inode_permission(struct inode *inode, int mask) int security_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { - int ret; - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; - ret = call_int_hook(inode_setattr, 0, idmap, dentry, attr); - if (ret) - return ret; - return evm_inode_setattr(idmap, dentry, attr); + return call_int_hook(inode_setattr, 0, idmap, dentry, attr); } EXPORT_SYMBOL_GPL(security_inode_setattr);
@@ -2307,9 +2299,7 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
if (ret == 1) ret = cap_inode_setxattr(dentry, name, value, size, flags); - if (ret) - return ret; - return evm_inode_setxattr(idmap, dentry, name, value, size, flags); + return ret; }
/** @@ -2328,15 +2318,10 @@ int security_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl) { - int ret; - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; - ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name, - kacl); - if (ret) - return ret; - return evm_inode_set_acl(idmap, dentry, acl_name, kacl); + return call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name, + kacl); }
/** @@ -2389,14 +2374,9 @@ int security_inode_get_acl(struct mnt_idmap *idmap, int security_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name) { - int ret; - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; - ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name); - if (ret) - return ret; - return evm_inode_remove_acl(idmap, dentry, acl_name); + return call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name); }
/** @@ -2432,7 +2412,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return; call_void_hook(inode_post_setxattr, dentry, name, value, size, flags); - evm_inode_post_setxattr(dentry, name, value, size, flags); }
/** @@ -2493,9 +2472,7 @@ int security_inode_removexattr(struct mnt_idmap *idmap, ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name); if (ret == 1) ret = cap_inode_removexattr(idmap, dentry, name); - if (ret) - return ret; - return evm_inode_removexattr(idmap, dentry, name); + return ret; }
/** @@ -2699,7 +2676,7 @@ int security_inode_copy_up_xattr(const char *name) return rc; }
- return evm_inode_copy_up_xattr(name); + return LSM_RET_DEFAULT(inode_copy_up_xattr); } EXPORT_SYMBOL(security_inode_copy_up_xattr);
diff --git a/tools/testing/selftests/lsm/lsm_list_modules_test.c b/tools/testing/selftests/lsm/lsm_list_modules_test.c index 17333787cb2f..4d5d4cee2586 100644 --- a/tools/testing/selftests/lsm/lsm_list_modules_test.c +++ b/tools/testing/selftests/lsm/lsm_list_modules_test.c @@ -125,6 +125,9 @@ TEST(correct_lsm_list_modules) case LSM_ID_IMA: name = "ima"; break; + case LSM_ID_EVM: + name = "evm"; + break; default: name = "INVALID"; break;
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
As for IMA, move hardcoded EVM function calls from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'evm' (last and always enabled like 'ima'). The order in the Makefile ensures that 'evm' hooks are executed after 'ima' ones.
Let's add a comment to the Makefile about this so everyone knows not to mix up the ordering, otherwise this looks good to me.
At some point I think we may want to introduce the concept of numerical priorities to security_add_hooks() to add some additional granularity beyond the LSM_ORDER_XXX priority, but that is something we can do later.
Acked-by: Paul Moore paul@paul-moore.com
Make EVM functions as static (except for evm_inode_init_security(), which is exported), and register them as hook implementations in init_evm_lsm(). Also move the inline functions evm_inode_remove_acl(), evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public evm.h header to evm_main.c.
Unlike before (see commit to move IMA to the LSM infrastructure), evm_inode_post_setattr(), evm_inode_post_set_acl(), evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not executed for private inodes.
Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
fs/attr.c | 2 - fs/posix_acl.c | 3 - fs/xattr.c | 2 - include/linux/evm.h | 113 ----------------- include/uapi/linux/lsm.h | 1 + security/integrity/evm/evm_main.c | 118 +++++++++++++++--- security/security.c | 43 ++----- .../selftests/lsm/lsm_list_modules_test.c | 3 + 8 files changed, 116 insertions(+), 169 deletions(-)
-- paul-moore.com
On Mon, Jan 15, 2024 at 07:18:06PM +0100, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
As for IMA, move hardcoded EVM function calls from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'evm' (last and always enabled like 'ima'). The order in the Makefile ensures that 'evm' hooks are executed after 'ima' ones.
Make EVM functions as static (except for evm_inode_init_security(), which is exported), and register them as hook implementations in init_evm_lsm(). Also move the inline functions evm_inode_remove_acl(), evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public evm.h header to evm_main.c.
Unlike before (see commit to move IMA to the LSM infrastructure), evm_inode_post_setattr(), evm_inode_post_set_acl(), evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not executed for private inodes.
Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
fs/attr.c | 2 - fs/posix_acl.c | 3 - fs/xattr.c | 2 -
Acked-by: Christian Brauner brauner@kernel.org
On 1/15/24 13:18, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
As for IMA, move hardcoded EVM function calls from various places in the kernel to the LSM infrastructure, by introducing a new LSM named 'evm' (last and always enabled like 'ima'). The order in the Makefile ensures that 'evm' hooks are executed after 'ima' ones.
Make EVM functions as static (except for evm_inode_init_security(), which is exported), and register them as hook implementations in init_evm_lsm(). Also move the inline functions evm_inode_remove_acl(), evm_inode_post_remove_acl(), and evm_inode_post_set_acl() from the public evm.h header to evm_main.c.
Unlike before (see commit to move IMA to the LSM infrastructure), evm_inode_post_setattr(), evm_inode_post_set_acl(), evm_inode_post_remove_acl(), and evm_inode_post_removexattr() are not executed for private inodes.
Finally, add the LSM_ID_EVM case in lsm_list_modules_test.c
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
} +static struct security_hook_list evm_hooks[] __ro_after_init = {
- LSM_HOOK_INIT(inode_setattr, evm_inode_setattr),
- LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr),
- LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
- LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr),
- LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl),
- LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl),
- LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl),
- LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl),
- LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr),
nit: move this one up after inode_setxattr.
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
From: Roberto Sassu roberto.sassu@huawei.com
Define a new structure for EVM-specific metadata, called evm_iint_cache, and embed it in the inode security blob. Introduce evm_iint_inode() to retrieve metadata, and register evm_inode_alloc_security() for the inode_alloc_security LSM hook, to initialize the structure (before splitting metadata, this task was done by iint_init_always()).
Keep the non-NULL checks after calling evm_iint_inode() except in evm_inode_alloc_security(), to take into account inodes for which security_inode_alloc() was not called. When using shared metadata, obtaining a NULL pointer from integrity_iint_find() meant that the file wasn't in the IMA policy. Now, because IMA and EVM use disjoint metadata, the EVM status has to be stored for every inode regardless of the IMA policy.
Given that from now on EVM relies on its own metadata, remove the iint parameter from evm_verifyxattr(). Also, directly retrieve the iint in evm_verify_hmac(), called by both evm_verifyxattr() and evm_verify_current_integrity(), since now there is no performance penalty in retrieving EVM metadata (constant time).
Replicate the management of the IMA_NEW_FILE flag, by introducing evm_post_path_mknod() and evm_file_release() to respectively set and clear the newly introduced flag EVM_NEW_FILE, at the same time IMA does. Like for IMA, select CONFIG_SECURITY_PATH when EVM is enabled, to ensure that files are marked as new.
Unlike ima_post_path_mknod(), evm_post_path_mknod() cannot check if a file must be appraised. Thus, it marks all affected files. Also, it does not clear EVM_NEW_FILE depending on i_version, but that is not a problem because IMA_NEW_FILE is always cleared when set in ima_check_last_writer().
Move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to security/integrity/evm/evm.h, since that definition is now unnecessary in the common integrity layer.
Finally, switch to the LSM reservation mechanism for the EVM xattr, and consequently decrement by one the number of xattrs to allocate in security_inode_init_security().
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/evm.h | 8 +-- security/integrity/evm/Kconfig | 1 + security/integrity/evm/evm.h | 19 +++++++ security/integrity/evm/evm_crypto.c | 4 +- security/integrity/evm/evm_main.c | 76 ++++++++++++++++++++------- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/integrity.h | 1 - security/security.c | 4 +- 8 files changed, 83 insertions(+), 32 deletions(-)
diff --git a/include/linux/evm.h b/include/linux/evm.h index cb481eccc967..d48d6da32315 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -12,15 +12,12 @@ #include <linux/integrity.h> #include <linux/xattr.h>
-struct integrity_iint_cache; - #ifdef CONFIG_EVM extern int evm_set_key(void *key, size_t keylen); extern enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, void *xattr_value, - size_t xattr_value_len, - struct integrity_iint_cache *iint); + size_t xattr_value_len); int evm_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, struct xattr *xattrs, int *xattr_count); @@ -48,8 +45,7 @@ static inline int evm_set_key(void *key, size_t keylen) static inline enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, void *xattr_value, - size_t xattr_value_len, - struct integrity_iint_cache *iint) + size_t xattr_value_len) { return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index fba9ee359bc9..861b3bacab82 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -6,6 +6,7 @@ config EVM select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_HASH_INFO + select SECURITY_PATH default n help EVM protects a file's security extended attributes against diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 53bd7fec93fa..eb1a2c343bd7 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -32,6 +32,25 @@ struct xattr_list { bool enabled; };
+#define EVM_NEW_FILE 0x00000001 +#define EVM_IMMUTABLE_DIGSIG 0x00000002 + +/* EVM integrity metadata associated with an inode */ +struct evm_iint_cache { + unsigned long flags; + enum integrity_status evm_status:4; +}; + +extern struct lsm_blob_sizes evm_blob_sizes; + +static inline struct evm_iint_cache *evm_iint_inode(const struct inode *inode) +{ + if (unlikely(!inode->i_security)) + return NULL; + + return inode->i_security + evm_blob_sizes.lbs_inode; +} + extern int evm_initialized;
#define EVM_ATTR_FSUUID 0x0001 diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index b1ffd4cc0b44..7552d49d0725 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -322,10 +322,10 @@ int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, static int evm_is_immutable(struct dentry *dentry, struct inode *inode) { const struct evm_ima_xattr_data *xattr_data = NULL; - struct integrity_iint_cache *iint; + struct evm_iint_cache *iint; int rc = 0;
- iint = integrity_iint_find(inode); + iint = evm_iint_inode(inode); if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG)) return 1;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index f65dbf3e9256..14c8f38e61d6 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -178,14 +178,14 @@ static int is_unsupported_fs(struct dentry *dentry) static enum integrity_status evm_verify_hmac(struct dentry *dentry, const char *xattr_name, char *xattr_value, - size_t xattr_value_len, - struct integrity_iint_cache *iint) + size_t xattr_value_len) { struct evm_ima_xattr_data *xattr_data = NULL; struct signature_v2_hdr *hdr; enum integrity_status evm_status = INTEGRITY_PASS; struct evm_digest digest; - struct inode *inode; + struct inode *inode = d_backing_inode(dentry); + struct evm_iint_cache *iint = evm_iint_inode(inode); int rc, xattr_len, evm_immutable = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS || @@ -254,8 +254,6 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, (const char *)xattr_data, xattr_len, digest.digest, digest.hdr.length); if (!rc) { - inode = d_backing_inode(dentry); - if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) { if (iint) iint->flags |= EVM_IMMUTABLE_DIGSIG; @@ -403,7 +401,6 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, * @xattr_name: requested xattr * @xattr_value: requested xattr value * @xattr_value_len: requested xattr value length - * @iint: inode integrity metadata * * Calculate the HMAC for the given dentry and verify it against the stored * security.evm xattr. For performance, use the xattr value and length @@ -416,8 +413,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, */ enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, - void *xattr_value, size_t xattr_value_len, - struct integrity_iint_cache *iint) + void *xattr_value, size_t xattr_value_len) { if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) return INTEGRITY_UNKNOWN; @@ -425,13 +421,8 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry, if (is_unsupported_fs(dentry)) return INTEGRITY_UNKNOWN;
- if (!iint) { - iint = integrity_iint_find(d_backing_inode(dentry)); - if (!iint) - return INTEGRITY_UNKNOWN; - } return evm_verify_hmac(dentry, xattr_name, xattr_value, - xattr_value_len, iint); + xattr_value_len); } EXPORT_SYMBOL_GPL(evm_verifyxattr);
@@ -448,7 +439,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode) return INTEGRITY_PASS; - return evm_verify_hmac(dentry, NULL, NULL, 0, NULL); + return evm_verify_hmac(dentry, NULL, NULL, 0); }
/* @@ -526,14 +517,14 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
evm_status = evm_verify_current_integrity(dentry); if (evm_status == INTEGRITY_NOXATTRS) { - struct integrity_iint_cache *iint; + struct evm_iint_cache *iint;
/* Exception if the HMAC is not going to be calculated. */ if (evm_hmac_disabled()) return 0;
- iint = integrity_iint_find(d_backing_inode(dentry)); - if (iint && (iint->flags & IMA_NEW_FILE)) + iint = evm_iint_inode(d_backing_inode(dentry)); + if (iint && (iint->flags & EVM_NEW_FILE)) return 0;
/* exception for pseudo filesystems */ @@ -735,9 +726,9 @@ static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
static void evm_reset_status(struct inode *inode) { - struct integrity_iint_cache *iint; + struct evm_iint_cache *iint;
- iint = integrity_iint_find(inode); + iint = evm_iint_inode(inode); if (iint) iint->evm_status = INTEGRITY_UNKNOWN; } @@ -1019,6 +1010,42 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir, } EXPORT_SYMBOL_GPL(evm_inode_init_security);
+static int evm_inode_alloc_security(struct inode *inode) +{ + struct evm_iint_cache *iint = evm_iint_inode(inode); + + /* Called by security_inode_alloc(), it cannot be NULL. */ + iint->flags = 0UL; + iint->evm_status = INTEGRITY_UNKNOWN; + + return 0; +} + +static void evm_file_release(struct file *file) +{ + struct inode *inode = file_inode(file); + struct evm_iint_cache *iint = evm_iint_inode(inode); + fmode_t mode = file->f_mode; + + if (!S_ISREG(inode->i_mode) || !(mode & FMODE_WRITE)) + return; + + if (iint && atomic_read(&inode->i_writecount) == 1) + iint->flags &= ~EVM_NEW_FILE; +} + +static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) +{ + struct inode *inode = d_backing_inode(dentry); + struct evm_iint_cache *iint = evm_iint_inode(inode); + + if (!S_ISREG(inode->i_mode)) + return; + + if (iint) + iint->flags |= EVM_NEW_FILE; +} + #ifdef CONFIG_EVM_LOAD_X509 void __init evm_load_x509(void) { @@ -1071,6 +1098,9 @@ static struct security_hook_list evm_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr), LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr), LSM_HOOK_INIT(inode_init_security, evm_inode_init_security), + LSM_HOOK_INIT(inode_alloc_security, evm_inode_alloc_security), + LSM_HOOK_INIT(file_release, evm_file_release), + LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod), };
static const struct lsm_id evm_lsmid = { @@ -1084,10 +1114,16 @@ static int __init init_evm_lsm(void) return 0; }
+struct lsm_blob_sizes evm_blob_sizes __ro_after_init = { + .lbs_inode = sizeof(struct evm_iint_cache), + .lbs_xattr_count = 1, +}; + DEFINE_LSM(evm) = { .name = "evm", .init = init_evm_lsm, .order = LSM_ORDER_LAST, + .blobs = &evm_blob_sizes, };
late_initcall(init_evm); diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 076451109637..1dd6ee72a20a 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -520,7 +520,7 @@ int ima_appraise_measurement(enum ima_hooks func, }
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, - rc < 0 ? 0 : rc, iint); + rc < 0 ? 0 : rc); switch (status) { case INTEGRITY_PASS: case INTEGRITY_PASS_IMMUTABLE: diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 59eaddd84434..7a97c269a072 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -37,7 +37,6 @@ #define IMA_DIGSIG_REQUIRED 0x01000000 #define IMA_PERMIT_DIRECTIO 0x02000000 #define IMA_NEW_FILE 0x04000000 -#define EVM_IMMUTABLE_DIGSIG 0x08000000 #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 #define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_CHECK_BLACKLIST 0x40000000 diff --git a/security/security.c b/security/security.c index a44740640a9a..f811cc376a7a 100644 --- a/security/security.c +++ b/security/security.c @@ -1716,8 +1716,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, return 0;
if (initxattrs) { - /* Allocate +1 for EVM and +1 as terminator. */ - new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 2, + /* Allocate +1 as terminator. */ + new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1, sizeof(*new_xattrs), GFP_NOFS); if (!new_xattrs) return -ENOMEM;
On 1/15/2024 10:18 AM, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Define a new structure for EVM-specific metadata, called evm_iint_cache, and embed it in the inode security blob. Introduce evm_iint_inode() to retrieve metadata, and register evm_inode_alloc_security() for the inode_alloc_security LSM hook, to initialize the structure (before splitting metadata, this task was done by iint_init_always()).
Keep the non-NULL checks after calling evm_iint_inode() except in evm_inode_alloc_security(), to take into account inodes for which security_inode_alloc() was not called. When using shared metadata, obtaining a NULL pointer from integrity_iint_find() meant that the file wasn't in the IMA policy. Now, because IMA and EVM use disjoint metadata, the EVM status has to be stored for every inode regardless of the IMA policy.
Given that from now on EVM relies on its own metadata, remove the iint parameter from evm_verifyxattr(). Also, directly retrieve the iint in evm_verify_hmac(), called by both evm_verifyxattr() and evm_verify_current_integrity(), since now there is no performance penalty in retrieving EVM metadata (constant time).
Replicate the management of the IMA_NEW_FILE flag, by introducing evm_post_path_mknod() and evm_file_release() to respectively set and clear the newly introduced flag EVM_NEW_FILE, at the same time IMA does. Like for IMA, select CONFIG_SECURITY_PATH when EVM is enabled, to ensure that files are marked as new.
Unlike ima_post_path_mknod(), evm_post_path_mknod() cannot check if a file must be appraised. Thus, it marks all affected files. Also, it does not clear EVM_NEW_FILE depending on i_version, but that is not a problem because IMA_NEW_FILE is always cleared when set in ima_check_last_writer().
Move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to security/integrity/evm/evm.h, since that definition is now unnecessary in the common integrity layer.
Finally, switch to the LSM reservation mechanism for the EVM xattr, and consequently decrement by one the number of xattrs to allocate in security_inode_init_security().
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/evm.h | 8 +-- security/integrity/evm/Kconfig | 1 + security/integrity/evm/evm.h | 19 +++++++ security/integrity/evm/evm_crypto.c | 4 +- security/integrity/evm/evm_main.c | 76 ++++++++++++++++++++------- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/integrity.h | 1 - security/security.c | 4 +- 8 files changed, 83 insertions(+), 32 deletions(-)
diff --git a/include/linux/evm.h b/include/linux/evm.h index cb481eccc967..d48d6da32315 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -12,15 +12,12 @@ #include <linux/integrity.h> #include <linux/xattr.h> -struct integrity_iint_cache;
#ifdef CONFIG_EVM extern int evm_set_key(void *key, size_t keylen); extern enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, void *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint);
size_t xattr_value_len);
int evm_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, struct xattr *xattrs, int *xattr_count); @@ -48,8 +45,7 @@ static inline int evm_set_key(void *key, size_t keylen) static inline enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, void *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint)
size_t xattr_value_len)
{ return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index fba9ee359bc9..861b3bacab82 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -6,6 +6,7 @@ config EVM select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_HASH_INFO
- select SECURITY_PATH default n help EVM protects a file's security extended attributes against
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 53bd7fec93fa..eb1a2c343bd7 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -32,6 +32,25 @@ struct xattr_list { bool enabled; }; +#define EVM_NEW_FILE 0x00000001 +#define EVM_IMMUTABLE_DIGSIG 0x00000002
+/* EVM integrity metadata associated with an inode */ +struct evm_iint_cache {
- unsigned long flags;
- enum integrity_status evm_status:4;
+};
+extern struct lsm_blob_sizes evm_blob_sizes;
+static inline struct evm_iint_cache *evm_iint_inode(const struct inode *inode) +{
- if (unlikely(!inode->i_security))
return NULL;
- return inode->i_security + evm_blob_sizes.lbs_inode;
+}
extern int evm_initialized; #define EVM_ATTR_FSUUID 0x0001 diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index b1ffd4cc0b44..7552d49d0725 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -322,10 +322,10 @@ int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, static int evm_is_immutable(struct dentry *dentry, struct inode *inode) { const struct evm_ima_xattr_data *xattr_data = NULL;
- struct integrity_iint_cache *iint;
- struct evm_iint_cache *iint; int rc = 0;
- iint = integrity_iint_find(inode);
- iint = evm_iint_inode(inode); if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG)) return 1;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index f65dbf3e9256..14c8f38e61d6 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -178,14 +178,14 @@ static int is_unsupported_fs(struct dentry *dentry) static enum integrity_status evm_verify_hmac(struct dentry *dentry, const char *xattr_name, char *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint)
size_t xattr_value_len)
{ struct evm_ima_xattr_data *xattr_data = NULL; struct signature_v2_hdr *hdr; enum integrity_status evm_status = INTEGRITY_PASS; struct evm_digest digest;
- struct inode *inode;
- struct inode *inode = d_backing_inode(dentry);
- struct evm_iint_cache *iint = evm_iint_inode(inode); int rc, xattr_len, evm_immutable = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS || @@ -254,8 +254,6 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, (const char *)xattr_data, xattr_len, digest.digest, digest.hdr.length); if (!rc) {
inode = d_backing_inode(dentry);
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) { if (iint) iint->flags |= EVM_IMMUTABLE_DIGSIG;
@@ -403,7 +401,6 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
- @xattr_name: requested xattr
- @xattr_value: requested xattr value
- @xattr_value_len: requested xattr value length
- @iint: inode integrity metadata
- Calculate the HMAC for the given dentry and verify it against the stored
- security.evm xattr. For performance, use the xattr value and length
@@ -416,8 +413,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, */ enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name,
void *xattr_value, size_t xattr_value_len,
struct integrity_iint_cache *iint)
void *xattr_value, size_t xattr_value_len)
{ if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) return INTEGRITY_UNKNOWN; @@ -425,13 +421,8 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry, if (is_unsupported_fs(dentry)) return INTEGRITY_UNKNOWN;
- if (!iint) {
iint = integrity_iint_find(d_backing_inode(dentry));
if (!iint)
return INTEGRITY_UNKNOWN;
- } return evm_verify_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, iint);
xattr_value_len);
} EXPORT_SYMBOL_GPL(evm_verifyxattr); @@ -448,7 +439,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode) return INTEGRITY_PASS;
- return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
- return evm_verify_hmac(dentry, NULL, NULL, 0);
} /* @@ -526,14 +517,14 @@ static int evm_protect_xattr(struct mnt_idmap *idmap, evm_status = evm_verify_current_integrity(dentry); if (evm_status == INTEGRITY_NOXATTRS) {
struct integrity_iint_cache *iint;
struct evm_iint_cache *iint;
/* Exception if the HMAC is not going to be calculated. */ if (evm_hmac_disabled()) return 0;
iint = integrity_iint_find(d_backing_inode(dentry));
if (iint && (iint->flags & IMA_NEW_FILE))
iint = evm_iint_inode(d_backing_inode(dentry));
if (iint && (iint->flags & EVM_NEW_FILE)) return 0;
/* exception for pseudo filesystems */ @@ -735,9 +726,9 @@ static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, static void evm_reset_status(struct inode *inode) {
- struct integrity_iint_cache *iint;
- struct evm_iint_cache *iint;
- iint = integrity_iint_find(inode);
- iint = evm_iint_inode(inode); if (iint) iint->evm_status = INTEGRITY_UNKNOWN;
} @@ -1019,6 +1010,42 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir, } EXPORT_SYMBOL_GPL(evm_inode_init_security); +static int evm_inode_alloc_security(struct inode *inode) +{
- struct evm_iint_cache *iint = evm_iint_inode(inode);
- /* Called by security_inode_alloc(), it cannot be NULL. */
- iint->flags = 0UL;
- iint->evm_status = INTEGRITY_UNKNOWN;
- return 0;
+}
+static void evm_file_release(struct file *file) +{
- struct inode *inode = file_inode(file);
- struct evm_iint_cache *iint = evm_iint_inode(inode);
- fmode_t mode = file->f_mode;
- if (!S_ISREG(inode->i_mode) || !(mode & FMODE_WRITE))
return;
- if (iint && atomic_read(&inode->i_writecount) == 1)
iint->flags &= ~EVM_NEW_FILE;
+}
+static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) +{
- struct inode *inode = d_backing_inode(dentry);
- struct evm_iint_cache *iint = evm_iint_inode(inode);
- if (!S_ISREG(inode->i_mode))
return;
- if (iint)
iint->flags |= EVM_NEW_FILE;
+}
#ifdef CONFIG_EVM_LOAD_X509 void __init evm_load_x509(void) { @@ -1071,6 +1098,9 @@ static struct security_hook_list evm_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr), LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr), LSM_HOOK_INIT(inode_init_security, evm_inode_init_security),
- LSM_HOOK_INIT(inode_alloc_security, evm_inode_alloc_security),
- LSM_HOOK_INIT(file_release, evm_file_release),
- LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod),
}; static const struct lsm_id evm_lsmid = { @@ -1084,10 +1114,16 @@ static int __init init_evm_lsm(void) return 0; } +struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
- .lbs_inode = sizeof(struct evm_iint_cache),
- .lbs_xattr_count = 1,
+};
DEFINE_LSM(evm) = { .name = "evm", .init = init_evm_lsm, .order = LSM_ORDER_LAST,
- .blobs = &evm_blob_sizes,
}; late_initcall(init_evm); diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 076451109637..1dd6ee72a20a 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -520,7 +520,7 @@ int ima_appraise_measurement(enum ima_hooks func, } status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
rc < 0 ? 0 : rc, iint);
switch (status) { case INTEGRITY_PASS: case INTEGRITY_PASS_IMMUTABLE:rc < 0 ? 0 : rc);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 59eaddd84434..7a97c269a072 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -37,7 +37,6 @@ #define IMA_DIGSIG_REQUIRED 0x01000000 #define IMA_PERMIT_DIRECTIO 0x02000000 #define IMA_NEW_FILE 0x04000000 -#define EVM_IMMUTABLE_DIGSIG 0x08000000 #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 #define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_CHECK_BLACKLIST 0x40000000 diff --git a/security/security.c b/security/security.c index a44740640a9a..f811cc376a7a 100644 --- a/security/security.c +++ b/security/security.c @@ -1716,8 +1716,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, return 0; if (initxattrs) {
/* Allocate +1 for EVM and +1 as terminator. */
new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 2,
/* Allocate +1 as terminator. */
if (!new_xattrs) return -ENOMEM;new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1, sizeof(*new_xattrs), GFP_NOFS);
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Define a new structure for EVM-specific metadata, called evm_iint_cache, and embed it in the inode security blob. Introduce evm_iint_inode() to retrieve metadata, and register evm_inode_alloc_security() for the inode_alloc_security LSM hook, to initialize the structure (before splitting metadata, this task was done by iint_init_always()).
Keep the non-NULL checks after calling evm_iint_inode() except in evm_inode_alloc_security(), to take into account inodes for which security_inode_alloc() was not called. When using shared metadata, obtaining a NULL pointer from integrity_iint_find() meant that the file wasn't in the IMA policy. Now, because IMA and EVM use disjoint metadata, the EVM status has to be stored for every inode regardless of the IMA policy.
Given that from now on EVM relies on its own metadata, remove the iint parameter from evm_verifyxattr(). Also, directly retrieve the iint in evm_verify_hmac(), called by both evm_verifyxattr() and evm_verify_current_integrity(), since now there is no performance penalty in retrieving EVM metadata (constant time).
Replicate the management of the IMA_NEW_FILE flag, by introducing evm_post_path_mknod() and evm_file_release() to respectively set and clear the newly introduced flag EVM_NEW_FILE, at the same time IMA does. Like for IMA, select CONFIG_SECURITY_PATH when EVM is enabled, to ensure that files are marked as new.
Unlike ima_post_path_mknod(), evm_post_path_mknod() cannot check if a file must be appraised. Thus, it marks all affected files. Also, it does not clear EVM_NEW_FILE depending on i_version, but that is not a problem because IMA_NEW_FILE is always cleared when set in ima_check_last_writer().
Move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to security/integrity/evm/evm.h, since that definition is now unnecessary in the common integrity layer.
Finally, switch to the LSM reservation mechanism for the EVM xattr, and consequently decrement by one the number of xattrs to allocate in security_inode_init_security().
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/evm.h | 8 +-- security/integrity/evm/Kconfig | 1 + security/integrity/evm/evm.h | 19 +++++++ security/integrity/evm/evm_crypto.c | 4 +- security/integrity/evm/evm_main.c | 76 ++++++++++++++++++++------- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/integrity.h | 1 - security/security.c | 4 +- 8 files changed, 83 insertions(+), 32 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On 1/15/24 13:18, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Define a new structure for EVM-specific metadata, called evm_iint_cache, and embed it in the inode security blob. Introduce evm_iint_inode() to retrieve metadata, and register evm_inode_alloc_security() for the inode_alloc_security LSM hook, to initialize the structure (before splitting metadata, this task was done by iint_init_always()).
Keep the non-NULL checks after calling evm_iint_inode() except in evm_inode_alloc_security(), to take into account inodes for which security_inode_alloc() was not called. When using shared metadata, obtaining a NULL pointer from integrity_iint_find() meant that the file wasn't in the IMA policy. Now, because IMA and EVM use disjoint metadata, the EVM status has to be stored for every inode regardless of the IMA policy.
Given that from now on EVM relies on its own metadata, remove the iint parameter from evm_verifyxattr(). Also, directly retrieve the iint in evm_verify_hmac(), called by both evm_verifyxattr() and evm_verify_current_integrity(), since now there is no performance penalty in retrieving EVM metadata (constant time).
Replicate the management of the IMA_NEW_FILE flag, by introducing evm_post_path_mknod() and evm_file_release() to respectively set and clear the newly introduced flag EVM_NEW_FILE, at the same time IMA does. Like for IMA, select CONFIG_SECURITY_PATH when EVM is enabled, to ensure that files are marked as new.
Unlike ima_post_path_mknod(), evm_post_path_mknod() cannot check if a file must be appraised. Thus, it marks all affected files. Also, it does not clear EVM_NEW_FILE depending on i_version, but that is not a problem because IMA_NEW_FILE is always cleared when set in ima_check_last_writer().
Move the EVM-specific flag EVM_IMMUTABLE_DIGSIG to security/integrity/evm/evm.h, since that definition is now unnecessary in the common integrity layer.
Finally, switch to the LSM reservation mechanism for the EVM xattr, and consequently decrement by one the number of xattrs to allocate in security_inode_init_security().
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
From: Roberto Sassu roberto.sassu@huawei.com
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.
Create ima_iint.c and introduce the same integrity metadata management functions found in iint.c (renamed with ima_). However, instead of putting metadata in an rbtree, reserve space from IMA in the inode security blob for a pointer, and introduce the ima_inode_set_iint()/ima_inode_get_iint() primitives to store/retrieve that pointer. This improves search time from logarithm to constant.
Consequently, 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().
Prefer storing the pointer instead of the entire ima_iint_cache structure, to avoid too much memory pressure. Use the same mechanism as before, a cache named ima_iint_cache (renamed from iint_cache), to quickly allocate a new ima_iint_cache structure when requested by the IMA policy.
Create the new ima_iint_cache in ima_iintcache_init(), called by init_ima_lsm(), during the initialization of the 'ima' LSM. And, register ima_inode_free_security() to free the ima_iint_cache structure, if exists.
Replace integrity_iint_cache with ima_iint_cache in various places of the IMA code. Also, replace integrity_inode_get() and integrity_iint_find(), respectively with ima_inode_get() and ima_iint_find().
Finally, move the remaining IMA-specific flags to security/integrity/ima/ima.h, since they are now unnecessary in the common integrity layer.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/Makefile | 2 +- security/integrity/ima/ima.h | 137 +++++++++++++++++++++---- security/integrity/ima/ima_api.c | 23 +++-- security/integrity/ima/ima_appraise.c | 27 +++-- security/integrity/ima/ima_iint.c | 142 ++++++++++++++++++++++++++ security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_main.c | 36 ++++--- security/integrity/ima/ima_policy.c | 2 +- security/integrity/integrity.h | 53 ---------- 9 files changed, 308 insertions(+), 116 deletions(-) create mode 100644 security/integrity/ima/ima_iint.c
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 2499f2485c04..b376d38b4ee6 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -4,7 +4,7 @@ # Measurement Architecture(IMA). #
-obj-$(CONFIG_IMA) += ima.o +obj-$(CONFIG_IMA) += ima.o ima_iint.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a27fc10f84f7..11d7c0332207 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,107 @@ 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_inode_get_iint(const struct inode *inode) +{ + struct ima_iint_cache **iint_sec; + + if (unlikely(!inode->i_security)) + return NULL; + + iint_sec = inode->i_security + ima_blob_sizes.lbs_inode; + return *iint_sec; +} + +static inline void ima_inode_set_iint(const struct inode *inode, + struct ima_iint_cache *iint) +{ + struct ima_iint_cache **iint_sec; + + if (unlikely(!inode->i_security)) + return; + + iint_sec = inode->i_security + ima_blob_sizes.lbs_inode; + *iint_sec = iint; +} + +struct ima_iint_cache *ima_iint_find(struct inode *inode); +struct ima_iint_cache *ima_inode_get(struct inode *inode); +void ima_inode_free(struct inode *inode); +void __init ima_iintcache_init(void); + extern const int read_idmap[];
#ifdef CONFIG_HAVE_IMA_KEXEC @@ -152,8 +253,8 @@ 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, - const char *op, const char *cause); + 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); void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); @@ -267,10 +368,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, - 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, +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 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 +381,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 +419,16 @@ 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, +int ima_appraise_measurement(enum ima_hooks func, 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 +437,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 +461,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..b37d043d5748 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -131,8 +131,8 @@ 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, - const char *op, const char *cause) + struct ima_iint_cache *iint, const char *op, + const char *cause) { struct ima_template_entry *entry; struct inode *inode = file_inode(file); @@ -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,9 +238,9 @@ 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, - struct file *file, void *buf, loff_t size, - enum hash_algo algo, struct modsig *modsig) +int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, + void *buf, loff_t size, enum hash_algo algo, + struct modsig *modsig) { const char *audit_cause = "failed"; struct inode *inode = file_inode(file); @@ -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,8 +339,8 @@ 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, - struct file *file, const unsigned char *filename, +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, struct ima_template_desc *template_desc) @@ -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..3497741caea9 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -84,8 +84,7 @@ int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode, NULL, NULL, NULL); }
-static int ima_fix_xattr(struct dentry *dentry, - struct integrity_iint_cache *iint) +static int ima_fix_xattr(struct dentry *dentry, struct ima_iint_cache *iint) { int rc, offset; u8 algo = iint->ima_hash->algo; @@ -106,7 +105,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 +125,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 +151,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 +274,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 +441,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; @@ -477,8 +475,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, +int ima_appraise_measurement(enum ima_hooks func, 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 +600,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 +637,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 +645,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_find(inode); if (iint) { set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); if (!action) @@ -674,12 +671,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_find(inode); if (!iint) return; iint->measured_pcrs = 0; diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c new file mode 100644 index 000000000000..e7c9c216c1c6 --- /dev/null +++ b/security/integrity/ima/ima_iint.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2008 IBM Corporation + * + * Authors: + * Mimi Zohar zohar@us.ibm.com + * + * File: ima_iint.c + * - implements the IMA hook: ima_inode_free + * - cache integrity information in the inode security blob + */ +#include <linux/slab.h> + +#include "ima.h" + +static struct kmem_cache *ima_iint_cache __ro_after_init; + +/** + * ima_iint_find - Return the iint associated with an inode + * @inode: Pointer to the inode + * + * Return the IMA integrity information (iint) associated with an inode, if the + * inode was processed by IMA. + * + * Return: Found iint or NULL. + */ +struct ima_iint_cache *ima_iint_find(struct inode *inode) +{ + if (!IS_IMA(inode)) + return NULL; + + return ima_inode_get_iint(inode); +} + +#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 *iint, + struct inode *inode) +{ +#ifdef CONFIG_LOCKDEP + static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING]; + + int depth = inode->i_sb->s_stack_depth; + + if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING)) + depth = 0; + + lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]); +#endif +} + +static void ima_iint_init_always(struct ima_iint_cache *iint, + struct inode *inode) +{ + iint->ima_hash = NULL; + iint->version = 0; + iint->flags = 0UL; + iint->atomic_flags = 0UL; + iint->ima_file_status = INTEGRITY_UNKNOWN; + iint->ima_mmap_status = INTEGRITY_UNKNOWN; + iint->ima_bprm_status = INTEGRITY_UNKNOWN; + iint->ima_read_status = INTEGRITY_UNKNOWN; + iint->ima_creds_status = INTEGRITY_UNKNOWN; + iint->measured_pcrs = 0; + mutex_init(&iint->mutex); + ima_iint_lockdep_annotate(iint, inode); +} + +static void ima_iint_free(struct ima_iint_cache *iint) +{ + kfree(iint->ima_hash); + mutex_destroy(&iint->mutex); + kmem_cache_free(ima_iint_cache, iint); +} + +/** + * ima_inode_get - Find or allocate an iint associated with an inode + * @inode: Pointer to the inode + * + * Find an iint associated with an inode, and allocate a new one if not found. + * Caller must lock i_mutex. + * + * Return: An iint on success, NULL on error. + */ +struct ima_iint_cache *ima_inode_get(struct inode *inode) +{ + struct ima_iint_cache *iint; + + iint = ima_iint_find(inode); + if (iint) + return iint; + + iint = kmem_cache_alloc(ima_iint_cache, GFP_NOFS); + if (!iint) + return NULL; + + ima_iint_init_always(iint, inode); + + inode->i_flags |= S_IMA; + ima_inode_set_iint(inode, iint); + + return iint; +} + +/** + * ima_inode_free - Called on inode free + * @inode: Pointer to the inode + * + * Free the iint associated with an inode. + */ +void ima_inode_free(struct inode *inode) +{ + struct ima_iint_cache *iint; + + if (!IS_IMA(inode)) + return; + + iint = ima_iint_find(inode); + ima_inode_set_iint(inode, NULL); + + ima_iint_free(iint); +} + +static void ima_iint_init_once(void *foo) +{ + struct ima_iint_cache *iint = (struct ima_iint_cache *)foo; + + memset(iint, 0, sizeof(*iint)); +} + +void __init ima_iintcache_init(void) +{ + ima_iint_cache = + kmem_cache_create("ima_iint_cache", sizeof(struct ima_iint_cache), + 0, SLAB_PANIC, ima_iint_init_once); +} 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 77d0d4e4b582..45f1a102c599 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, @@ -127,7 +127,7 @@ static void ima_rdwr_violation_check(struct file *file, if (mode & FMODE_WRITE) { if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { if (!iint) - iint = integrity_iint_find(inode); + iint = ima_iint_find(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_find(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 = 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_inode_get(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 = NULL, tmp_iint; int rc, hash_algo;
if (ima_policy_flag) { - iint = integrity_iint_find(inode); + iint = ima_iint_find(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)) @@ -700,7 +699,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap, return;
/* Nothing to do if we can't allocate memory */ - iint = integrity_inode_get(inode); + iint = ima_inode_get(inode); if (!iint) return;
@@ -719,7 +718,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap, */ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) { - struct integrity_iint_cache *iint; + struct ima_iint_cache *iint; struct inode *inode = dentry->d_inode; int must_appraise;
@@ -732,7 +731,7 @@ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) return;
/* Nothing to do if we can't allocate memory */ - iint = integrity_inode_get(inode); + iint = ima_inode_get(inode); if (!iint) return;
@@ -935,7 +934,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, @@ -1168,6 +1167,7 @@ static struct security_hook_list ima_hooks[] __ro_after_init = { #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request), #endif + LSM_HOOK_INIT(inode_free_security, ima_inode_free), };
static const struct lsm_id ima_lsmid = { @@ -1177,15 +1177,21 @@ static const struct lsm_id ima_lsmid = {
static int __init init_ima_lsm(void) { + ima_iintcache_init(); security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid); init_ima_appraise_lsm(&ima_lsmid); 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,
On 1/15/2024 10:18 AM, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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.
Create ima_iint.c and introduce the same integrity metadata management functions found in iint.c (renamed with ima_). However, instead of putting metadata in an rbtree, reserve space from IMA in the inode security blob for a pointer, and introduce the ima_inode_set_iint()/ima_inode_get_iint() primitives to store/retrieve that pointer. This improves search time from logarithm to constant.
Consequently, 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().
Prefer storing the pointer instead of the entire ima_iint_cache structure, to avoid too much memory pressure. Use the same mechanism as before, a cache named ima_iint_cache (renamed from iint_cache), to quickly allocate a new ima_iint_cache structure when requested by the IMA policy.
Create the new ima_iint_cache in ima_iintcache_init(), called by init_ima_lsm(), during the initialization of the 'ima' LSM. And, register ima_inode_free_security() to free the ima_iint_cache structure, if exists.
Replace integrity_iint_cache with ima_iint_cache in various places of the IMA code. Also, replace integrity_inode_get() and integrity_iint_find(), respectively with ima_inode_get() and ima_iint_find().
Finally, move the remaining IMA-specific flags to security/integrity/ima/ima.h, since they are now unnecessary in the common integrity layer.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Casey Schaufler casey@schaufler-ca.com4
security/integrity/ima/Makefile | 2 +- security/integrity/ima/ima.h | 137 +++++++++++++++++++++---- security/integrity/ima/ima_api.c | 23 +++-- security/integrity/ima/ima_appraise.c | 27 +++-- security/integrity/ima/ima_iint.c | 142 ++++++++++++++++++++++++++ security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_main.c | 36 ++++--- security/integrity/ima/ima_policy.c | 2 +- security/integrity/integrity.h | 53 ---------- 9 files changed, 308 insertions(+), 116 deletions(-) create mode 100644 security/integrity/ima/ima_iint.c
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 2499f2485c04..b376d38b4ee6 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -4,7 +4,7 @@ # Measurement Architecture(IMA). # -obj-$(CONFIG_IMA) += ima.o +obj-$(CONFIG_IMA) += ima.o ima_iint.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a27fc10f84f7..11d7c0332207 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,107 @@ 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_inode_get_iint(const struct inode *inode) +{
- struct ima_iint_cache **iint_sec;
- if (unlikely(!inode->i_security))
return NULL;
- iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
- return *iint_sec;
+}
+static inline void ima_inode_set_iint(const struct inode *inode,
struct ima_iint_cache *iint)
+{
- struct ima_iint_cache **iint_sec;
- if (unlikely(!inode->i_security))
return;
- iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
- *iint_sec = iint;
+}
+struct ima_iint_cache *ima_iint_find(struct inode *inode); +struct ima_iint_cache *ima_inode_get(struct inode *inode); +void ima_inode_free(struct inode *inode); +void __init ima_iintcache_init(void);
extern const int read_idmap[]; #ifdef CONFIG_HAVE_IMA_KEXEC @@ -152,8 +253,8 @@ 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,
const char *op, const char *cause);
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); void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); @@ -267,10 +368,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,
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, +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 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 +381,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 +419,16 @@ 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,
+int ima_appraise_measurement(enum ima_hooks func, 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 +437,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 +461,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..b37d043d5748 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -131,8 +131,8 @@ 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,
const char *op, const char *cause)
struct ima_iint_cache *iint, const char *op,
const char *cause)
{ struct ima_template_entry *entry; struct inode *inode = file_inode(file); @@ -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,9 +238,9 @@ 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,
struct file *file, void *buf, loff_t size,
enum hash_algo algo, struct modsig *modsig)
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
void *buf, loff_t size, enum hash_algo algo,
struct modsig *modsig)
{ const char *audit_cause = "failed"; struct inode *inode = file_inode(file); @@ -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,8 +339,8 @@ 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,
struct file *file, const unsigned char *filename,
+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, struct ima_template_desc *template_desc)
@@ -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..3497741caea9 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -84,8 +84,7 @@ int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode, NULL, NULL, NULL); } -static int ima_fix_xattr(struct dentry *dentry,
struct integrity_iint_cache *iint)
+static int ima_fix_xattr(struct dentry *dentry, struct ima_iint_cache *iint) { int rc, offset; u8 algo = iint->ima_hash->algo; @@ -106,7 +105,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 +125,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 +151,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 +274,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 +441,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; @@ -477,8 +475,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,
+int ima_appraise_measurement(enum ima_hooks func, 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 +600,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 +637,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 +645,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_find(inode); if (iint) { set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); if (!action)
@@ -674,12 +671,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_find(inode); if (!iint) return; iint->measured_pcrs = 0;
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c new file mode 100644 index 000000000000..e7c9c216c1c6 --- /dev/null +++ b/security/integrity/ima/ima_iint.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/*
- Copyright (C) 2008 IBM Corporation
- Authors:
- Mimi Zohar zohar@us.ibm.com
- File: ima_iint.c
- implements the IMA hook: ima_inode_free
- cache integrity information in the inode security blob
- */
+#include <linux/slab.h>
+#include "ima.h"
+static struct kmem_cache *ima_iint_cache __ro_after_init;
+/**
- ima_iint_find - Return the iint associated with an inode
- @inode: Pointer to the inode
- Return the IMA integrity information (iint) associated with an inode, if the
- inode was processed by IMA.
- Return: Found iint or NULL.
- */
+struct ima_iint_cache *ima_iint_find(struct inode *inode) +{
- if (!IS_IMA(inode))
return NULL;
- return ima_inode_get_iint(inode);
+}
+#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 *iint,
struct inode *inode)
+{ +#ifdef CONFIG_LOCKDEP
- static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING];
- int depth = inode->i_sb->s_stack_depth;
- if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
depth = 0;
- lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]);
+#endif +}
+static void ima_iint_init_always(struct ima_iint_cache *iint,
struct inode *inode)
+{
- iint->ima_hash = NULL;
- iint->version = 0;
- iint->flags = 0UL;
- iint->atomic_flags = 0UL;
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
- iint->ima_creds_status = INTEGRITY_UNKNOWN;
- iint->measured_pcrs = 0;
- mutex_init(&iint->mutex);
- ima_iint_lockdep_annotate(iint, inode);
+}
+static void ima_iint_free(struct ima_iint_cache *iint) +{
- kfree(iint->ima_hash);
- mutex_destroy(&iint->mutex);
- kmem_cache_free(ima_iint_cache, iint);
+}
+/**
- ima_inode_get - Find or allocate an iint associated with an inode
- @inode: Pointer to the inode
- Find an iint associated with an inode, and allocate a new one if not found.
- Caller must lock i_mutex.
- Return: An iint on success, NULL on error.
- */
+struct ima_iint_cache *ima_inode_get(struct inode *inode) +{
- struct ima_iint_cache *iint;
- iint = ima_iint_find(inode);
- if (iint)
return iint;
- iint = kmem_cache_alloc(ima_iint_cache, GFP_NOFS);
- if (!iint)
return NULL;
- ima_iint_init_always(iint, inode);
- inode->i_flags |= S_IMA;
- ima_inode_set_iint(inode, iint);
- return iint;
+}
+/**
- ima_inode_free - Called on inode free
- @inode: Pointer to the inode
- Free the iint associated with an inode.
- */
+void ima_inode_free(struct inode *inode) +{
- struct ima_iint_cache *iint;
- if (!IS_IMA(inode))
return;
- iint = ima_iint_find(inode);
- ima_inode_set_iint(inode, NULL);
- ima_iint_free(iint);
+}
+static void ima_iint_init_once(void *foo) +{
- struct ima_iint_cache *iint = (struct ima_iint_cache *)foo;
- memset(iint, 0, sizeof(*iint));
+}
+void __init ima_iintcache_init(void) +{
- ima_iint_cache =
kmem_cache_create("ima_iint_cache", sizeof(struct ima_iint_cache),
0, SLAB_PANIC, ima_iint_init_once);
+} 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 77d0d4e4b582..45f1a102c599 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,
@@ -127,7 +127,7 @@ static void ima_rdwr_violation_check(struct file *file, if (mode & FMODE_WRITE) { if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { if (!iint)
iint = integrity_iint_find(inode);
iint = ima_iint_find(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_find(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 = 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);
if (!iint) rc = -ENOMEM; }iint = ima_inode_get(inode);
@@ -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 = NULL, tmp_iint; int rc, hash_algo;
if (ima_policy_flag) {
iint = integrity_iint_find(inode);
if (iint) mutex_lock(&iint->mutex); }iint = ima_iint_find(inode);
@@ -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));
mutex_init(&tmp_iint.mutex);tmp_iint.inode = inode;
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)) @@ -700,7 +699,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap, return; /* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
- iint = ima_inode_get(inode); if (!iint) return;
@@ -719,7 +718,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap, */ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) {
- struct integrity_iint_cache *iint;
- struct ima_iint_cache *iint; struct inode *inode = dentry->d_inode; int must_appraise;
@@ -732,7 +731,7 @@ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry) return; /* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
- iint = ima_inode_get(inode); if (!iint) return;
@@ -935,7 +934,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,
@@ -1168,6 +1167,7 @@ static struct security_hook_list ima_hooks[] __ro_after_init = { #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request), #endif
- LSM_HOOK_INIT(inode_free_security, ima_inode_free),
}; static const struct lsm_id ima_lsmid = { @@ -1177,15 +1177,21 @@ static const struct lsm_id ima_lsmid = { static int __init init_ima_lsm(void) {
- ima_iintcache_init(); security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid); init_ima_appraise_lsm(&ima_lsmid); 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,
On 1/15/24 13:18, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
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.
Create ima_iint.c and introduce the same integrity metadata management functions found in iint.c (renamed with ima_). However, instead of putting metadata in an rbtree, reserve space from IMA in the inode security blob for a pointer, and introduce the ima_inode_set_iint()/ima_inode_get_iint() primitives to store/retrieve that pointer. This improves search time from logarithm to constant.
logarithmic
Consequently, 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().
Prefer storing the pointer instead of the entire ima_iint_cache structure, to avoid too much memory pressure. Use the same mechanism as before, a cache named ima_iint_cache (renamed from iint_cache), to quickly allocate a new ima_iint_cache structure when requested by the IMA policy.
Create the new ima_iint_cache in ima_iintcache_init(), called by init_ima_lsm(), during the initialization of the 'ima' LSM. And, register ima_inode_free_security() to free the ima_iint_cache structure, if exists.
Replace integrity_iint_cache with ima_iint_cache in various places of the IMA code. Also, replace integrity_inode_get() and integrity_iint_find(), respectively with ima_inode_get() and ima_iint_find().
Finally, move the remaining IMA-specific flags to security/integrity/ima/ima.h, since they are now unnecessary in the common integrity layer.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
From: Roberto Sassu roberto.sassu@huawei.com
Since now IMA and EVM use their own integrity metadata, it is safe to remove the 'integrity' LSM, with its management of integrity metadata.
Keep the iint.c file only for loading IMA and EVM keys at boot, and for creating the integrity directory in securityfs (we need to keep it for retrocompatibility reasons).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/integrity.h | 14 --- security/integrity/iint.c | 197 +-------------------------------- security/integrity/integrity.h | 25 ----- security/security.c | 2 - 4 files changed, 2 insertions(+), 236 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h index ef0f63ef5ebc..459b79683783 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -19,24 +19,10 @@ enum integrity_status { INTEGRITY_UNKNOWN, };
-/* List of EVM protected security xattrs */ #ifdef CONFIG_INTEGRITY -extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode); -extern void integrity_inode_free(struct inode *inode); extern void __init integrity_load_keys(void);
#else -static inline struct integrity_iint_cache * - integrity_inode_get(struct inode *inode) -{ - return NULL; -} - -static inline void integrity_inode_free(struct inode *inode) -{ - return; -} - static inline void integrity_load_keys(void) { } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index d4419a2a1e24..068ac6c2ae1e 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -6,207 +6,14 @@ * Mimi Zohar zohar@us.ibm.com * * File: integrity_iint.c - * - implements the integrity hooks: integrity_inode_alloc, - * integrity_inode_free - * - cache integrity information associated with an inode - * using a rbtree tree. + * - initialize the integrity directory in securityfs + * - load IMA and EVM keys */ -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/rbtree.h> -#include <linux/file.h> -#include <linux/uaccess.h> #include <linux/security.h> -#include <linux/lsm_hooks.h> #include "integrity.h"
-static struct rb_root integrity_iint_tree = RB_ROOT; -static DEFINE_RWLOCK(integrity_iint_lock); -static struct kmem_cache *iint_cache __ro_after_init; - struct dentry *integrity_dir;
-/* - * __integrity_iint_find - return the iint associated with an inode - */ -static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) -{ - struct integrity_iint_cache *iint; - struct rb_node *n = integrity_iint_tree.rb_node; - - while (n) { - iint = rb_entry(n, struct integrity_iint_cache, rb_node); - - if (inode < iint->inode) - n = n->rb_left; - else if (inode > iint->inode) - n = n->rb_right; - else - return iint; - } - - return NULL; -} - -/* - * integrity_iint_find - return the iint associated with an inode - */ -struct integrity_iint_cache *integrity_iint_find(struct inode *inode) -{ - struct integrity_iint_cache *iint; - - if (!IS_IMA(inode)) - return NULL; - - read_lock(&integrity_iint_lock); - iint = __integrity_iint_find(inode); - read_unlock(&integrity_iint_lock); - - return iint; -} - -#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1) - -/* - * It is not clear that IMA should be nested at all, but as long is it measures - * files both on overlayfs and on underlying fs, we need to annotate the iint - * mutex to avoid lockdep false positives related to IMA + overlayfs. - * See ovl_lockdep_annotate_inode_mutex_key() for more details. - */ -static inline void iint_lockdep_annotate(struct integrity_iint_cache *iint, - struct inode *inode) -{ -#ifdef CONFIG_LOCKDEP - static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING]; - - int depth = inode->i_sb->s_stack_depth; - - if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING)) - depth = 0; - - lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]); -#endif -} - -static void iint_init_always(struct integrity_iint_cache *iint, - struct inode *inode) -{ - iint->ima_hash = NULL; - iint->version = 0; - iint->flags = 0UL; - iint->atomic_flags = 0UL; - iint->ima_file_status = INTEGRITY_UNKNOWN; - iint->ima_mmap_status = INTEGRITY_UNKNOWN; - iint->ima_bprm_status = INTEGRITY_UNKNOWN; - iint->ima_read_status = INTEGRITY_UNKNOWN; - iint->ima_creds_status = INTEGRITY_UNKNOWN; - iint->evm_status = INTEGRITY_UNKNOWN; - iint->measured_pcrs = 0; - mutex_init(&iint->mutex); - iint_lockdep_annotate(iint, inode); -} - -static void iint_free(struct integrity_iint_cache *iint) -{ - kfree(iint->ima_hash); - mutex_destroy(&iint->mutex); - kmem_cache_free(iint_cache, iint); -} - -/** - * integrity_inode_get - find or allocate an iint associated with an inode - * @inode: pointer to the inode - * @return: allocated iint - * - * Caller must lock i_mutex - */ -struct integrity_iint_cache *integrity_inode_get(struct inode *inode) -{ - struct rb_node **p; - struct rb_node *node, *parent = NULL; - struct integrity_iint_cache *iint, *test_iint; - - iint = integrity_iint_find(inode); - if (iint) - return iint; - - iint = kmem_cache_alloc(iint_cache, GFP_NOFS); - if (!iint) - return NULL; - - iint_init_always(iint, inode); - - write_lock(&integrity_iint_lock); - - p = &integrity_iint_tree.rb_node; - while (*p) { - parent = *p; - test_iint = rb_entry(parent, struct integrity_iint_cache, - rb_node); - if (inode < test_iint->inode) { - p = &(*p)->rb_left; - } else if (inode > test_iint->inode) { - p = &(*p)->rb_right; - } else { - write_unlock(&integrity_iint_lock); - kmem_cache_free(iint_cache, iint); - return test_iint; - } - } - - iint->inode = inode; - node = &iint->rb_node; - inode->i_flags |= S_IMA; - rb_link_node(node, parent, p); - rb_insert_color(node, &integrity_iint_tree); - - write_unlock(&integrity_iint_lock); - return iint; -} - -/** - * integrity_inode_free - called on security_inode_free - * @inode: pointer to the inode - * - * Free the integrity information(iint) associated with an inode. - */ -void integrity_inode_free(struct inode *inode) -{ - struct integrity_iint_cache *iint; - - if (!IS_IMA(inode)) - return; - - write_lock(&integrity_iint_lock); - iint = __integrity_iint_find(inode); - rb_erase(&iint->rb_node, &integrity_iint_tree); - write_unlock(&integrity_iint_lock); - - iint_free(iint); -} - -static void iint_init_once(void *foo) -{ - struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo; - - memset(iint, 0, sizeof(*iint)); -} - -static int __init integrity_iintcache_init(void) -{ - iint_cache = - kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache), - 0, SLAB_PANIC, iint_init_once); - return 0; -} -DEFINE_LSM(integrity) = { - .name = "integrity", - .init = integrity_iintcache_init, - .order = LSM_ORDER_LAST, -}; - - /* * integrity_kernel_read - read data from the file * diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 671fc50255f9..50d6f798e613 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -102,31 +102,6 @@ struct ima_file_id { __u8 hash[HASH_MAX_DIGESTSIZE]; } __packed;
-/* integrity data associated with an inode */ -struct integrity_iint_cache { - struct rb_node rb_node; /* rooted in integrity_iint_tree */ - struct mutex mutex; /* protects: version, flags, digest */ - struct inode *inode; /* back pointer to inode in question */ - u64 version; /* track inode changes */ - unsigned long flags; - unsigned long measured_pcrs; - unsigned long atomic_flags; - unsigned long real_ino; - dev_t real_dev; - enum integrity_status ima_file_status:4; - enum integrity_status ima_mmap_status:4; - enum integrity_status ima_bprm_status:4; - enum integrity_status ima_read_status:4; - enum integrity_status ima_creds_status:4; - enum integrity_status evm_status:4; - struct ima_digest_data *ima_hash; -}; - -/* rbtree tree calls to lookup, insert, delete - * integrity data associated with an inode. - */ -struct integrity_iint_cache *integrity_iint_find(struct inode *inode); - int integrity_kernel_read(struct file *file, loff_t offset, void *addr, unsigned long count);
diff --git a/security/security.c b/security/security.c index f811cc376a7a..df87c0a7eaac 100644 --- a/security/security.c +++ b/security/security.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> -#include <linux/integrity.h> #include <linux/fsnotify.h> #include <linux/mman.h> #include <linux/mount.h> @@ -1597,7 +1596,6 @@ static void inode_free_by_rcu(struct rcu_head *head) */ void security_inode_free(struct inode *inode) { - integrity_inode_free(inode); call_void_hook(inode_free_security, inode); /* * The inode may still be referenced in a path walk and
On 1/15/2024 10:18 AM, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Since now IMA and EVM use their own integrity metadata, it is safe to remove the 'integrity' LSM, with its management of integrity metadata.
Keep the iint.c file only for loading IMA and EVM keys at boot, and for creating the integrity directory in securityfs (we need to keep it for retrocompatibility reasons).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/integrity.h | 14 --- security/integrity/iint.c | 197 +-------------------------------- security/integrity/integrity.h | 25 ----- security/security.c | 2 - 4 files changed, 2 insertions(+), 236 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h index ef0f63ef5ebc..459b79683783 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -19,24 +19,10 @@ enum integrity_status { INTEGRITY_UNKNOWN, }; -/* List of EVM protected security xattrs */ #ifdef CONFIG_INTEGRITY -extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode); -extern void integrity_inode_free(struct inode *inode); extern void __init integrity_load_keys(void); #else -static inline struct integrity_iint_cache *
integrity_inode_get(struct inode *inode)
-{
- return NULL;
-}
-static inline void integrity_inode_free(struct inode *inode) -{
- return;
-}
static inline void integrity_load_keys(void) { } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index d4419a2a1e24..068ac6c2ae1e 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -6,207 +6,14 @@
- Mimi Zohar zohar@us.ibm.com
- File: integrity_iint.c
- implements the integrity hooks: integrity_inode_alloc,
integrity_inode_free
- cache integrity information associated with an inode
using a rbtree tree.
- initialize the integrity directory in securityfs
*/
- load IMA and EVM keys
-#include <linux/slab.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/rbtree.h> -#include <linux/file.h> -#include <linux/uaccess.h> #include <linux/security.h> -#include <linux/lsm_hooks.h> #include "integrity.h" -static struct rb_root integrity_iint_tree = RB_ROOT; -static DEFINE_RWLOCK(integrity_iint_lock); -static struct kmem_cache *iint_cache __ro_after_init;
struct dentry *integrity_dir; -/*
- __integrity_iint_find - return the iint associated with an inode
- */
-static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) -{
- struct integrity_iint_cache *iint;
- struct rb_node *n = integrity_iint_tree.rb_node;
- while (n) {
iint = rb_entry(n, struct integrity_iint_cache, rb_node);
if (inode < iint->inode)
n = n->rb_left;
else if (inode > iint->inode)
n = n->rb_right;
else
return iint;
- }
- return NULL;
-}
-/*
- integrity_iint_find - return the iint associated with an inode
- */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode) -{
- struct integrity_iint_cache *iint;
- if (!IS_IMA(inode))
return NULL;
- read_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- read_unlock(&integrity_iint_lock);
- return iint;
-}
-#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1)
-/*
- It is not clear that IMA should be nested at all, but as long is it measures
- files both on overlayfs and on underlying fs, we need to annotate the iint
- mutex to avoid lockdep false positives related to IMA + overlayfs.
- See ovl_lockdep_annotate_inode_mutex_key() for more details.
- */
-static inline void iint_lockdep_annotate(struct integrity_iint_cache *iint,
struct inode *inode)
-{ -#ifdef CONFIG_LOCKDEP
- static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING];
- int depth = inode->i_sb->s_stack_depth;
- if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
depth = 0;
- lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]);
-#endif -}
-static void iint_init_always(struct integrity_iint_cache *iint,
struct inode *inode)
-{
- iint->ima_hash = NULL;
- iint->version = 0;
- iint->flags = 0UL;
- iint->atomic_flags = 0UL;
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
- iint->ima_creds_status = INTEGRITY_UNKNOWN;
- iint->evm_status = INTEGRITY_UNKNOWN;
- iint->measured_pcrs = 0;
- mutex_init(&iint->mutex);
- iint_lockdep_annotate(iint, inode);
-}
-static void iint_free(struct integrity_iint_cache *iint) -{
- kfree(iint->ima_hash);
- mutex_destroy(&iint->mutex);
- kmem_cache_free(iint_cache, iint);
-}
-/**
- integrity_inode_get - find or allocate an iint associated with an inode
- @inode: pointer to the inode
- @return: allocated iint
- Caller must lock i_mutex
- */
-struct integrity_iint_cache *integrity_inode_get(struct inode *inode) -{
- struct rb_node **p;
- struct rb_node *node, *parent = NULL;
- struct integrity_iint_cache *iint, *test_iint;
- iint = integrity_iint_find(inode);
- if (iint)
return iint;
- iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
- if (!iint)
return NULL;
- iint_init_always(iint, inode);
- write_lock(&integrity_iint_lock);
- p = &integrity_iint_tree.rb_node;
- while (*p) {
parent = *p;
test_iint = rb_entry(parent, struct integrity_iint_cache,
rb_node);
if (inode < test_iint->inode) {
p = &(*p)->rb_left;
} else if (inode > test_iint->inode) {
p = &(*p)->rb_right;
} else {
write_unlock(&integrity_iint_lock);
kmem_cache_free(iint_cache, iint);
return test_iint;
}
- }
- iint->inode = inode;
- node = &iint->rb_node;
- inode->i_flags |= S_IMA;
- rb_link_node(node, parent, p);
- rb_insert_color(node, &integrity_iint_tree);
- write_unlock(&integrity_iint_lock);
- return iint;
-}
-/**
- integrity_inode_free - called on security_inode_free
- @inode: pointer to the inode
- Free the integrity information(iint) associated with an inode.
- */
-void integrity_inode_free(struct inode *inode) -{
- struct integrity_iint_cache *iint;
- if (!IS_IMA(inode))
return;
- write_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- rb_erase(&iint->rb_node, &integrity_iint_tree);
- write_unlock(&integrity_iint_lock);
- iint_free(iint);
-}
-static void iint_init_once(void *foo) -{
- struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo;
- memset(iint, 0, sizeof(*iint));
-}
-static int __init integrity_iintcache_init(void) -{
- iint_cache =
kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
0, SLAB_PANIC, iint_init_once);
- return 0;
-} -DEFINE_LSM(integrity) = {
- .name = "integrity",
- .init = integrity_iintcache_init,
- .order = LSM_ORDER_LAST,
-};
/*
- integrity_kernel_read - read data from the file
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 671fc50255f9..50d6f798e613 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -102,31 +102,6 @@ struct ima_file_id { __u8 hash[HASH_MAX_DIGESTSIZE]; } __packed; -/* integrity data associated with an inode */ -struct integrity_iint_cache {
- struct rb_node rb_node; /* rooted in integrity_iint_tree */
- struct mutex mutex; /* protects: version, flags, digest */
- struct inode *inode; /* back pointer to inode in question */
- u64 version; /* track inode changes */
- unsigned long flags;
- unsigned long measured_pcrs;
- unsigned long atomic_flags;
- unsigned long real_ino;
- dev_t real_dev;
- enum integrity_status ima_file_status:4;
- enum integrity_status ima_mmap_status:4;
- enum integrity_status ima_bprm_status:4;
- enum integrity_status ima_read_status:4;
- enum integrity_status ima_creds_status:4;
- enum integrity_status evm_status:4;
- struct ima_digest_data *ima_hash;
-};
-/* rbtree tree calls to lookup, insert, delete
- integrity data associated with an inode.
- */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
int integrity_kernel_read(struct file *file, loff_t offset, void *addr, unsigned long count); diff --git a/security/security.c b/security/security.c index f811cc376a7a..df87c0a7eaac 100644 --- a/security/security.c +++ b/security/security.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> -#include <linux/integrity.h> #include <linux/fsnotify.h> #include <linux/mman.h> #include <linux/mount.h> @@ -1597,7 +1596,6 @@ static void inode_free_by_rcu(struct rcu_head *head) */ void security_inode_free(struct inode *inode) {
- integrity_inode_free(inode); call_void_hook(inode_free_security, inode); /*
- The inode may still be referenced in a path walk and
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
Since now IMA and EVM use their own integrity metadata, it is safe to remove the 'integrity' LSM, with its management of integrity metadata.
Keep the iint.c file only for loading IMA and EVM keys at boot, and for creating the integrity directory in securityfs (we need to keep it for retrocompatibility reasons).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: Casey Schaufler casey@schaufler-ca.com
include/linux/integrity.h | 14 --- security/integrity/iint.c | 197 +-------------------------------- security/integrity/integrity.h | 25 ----- security/security.c | 2 - 4 files changed, 2 insertions(+), 236 deletions(-)
Acked-by: Paul Moore paul@paul-moore.com
-- paul-moore.com
On 1/15/24 13:18, Roberto Sassu wrote:
From: Roberto Sassu roberto.sassu@huawei.com
Since now IMA and EVM use their own integrity metadata, it is safe to remove the 'integrity' LSM, with its management of integrity metadata.
Keep the iint.c file only for loading IMA and EVM keys at boot, and for creating the integrity directory in securityfs (we need to keep it for retrocompatibility reasons).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Reviewed-by: Stefan Berger stefanb@linux.ibm.com
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
IMA and EVM are not effectively LSMs, especially due to the fact that in the past they could not provide a security blob while there is another LSM active.
That changed in the recent years, the LSM stacking feature now makes it possible to stack together multiple LSMs, and allows them to provide a security blob for most kernel objects. While the LSM stacking feature has some limitations being worked out, it is already suitable to make IMA and EVM as LSMs.
The main purpose of this patch set is to remove IMA and EVM function calls, hardcoded in the LSM infrastructure and other places in the kernel, and to register them as LSM hook implementations, so that those functions are called by the LSM infrastructure like other regular LSMs.
Thanks Roberto, this is looking good. I appreciate all the work you've put into making this happen; when I first mentioned this idea I figured it would be something that would happen much farther into the future, I wasn't expecting to see you pick this up and put in the work to make it happen - thank you.
I had some pretty minor comments but I think the only thing I saw that I think needs a change/addition is a comment in the Makefile regarding the IMA/EVM ordering; take a look and let me know what you think.
There are also a few patches in the patchset that don't have an ACK/review tag from Mimi, although now that you are co-maininting IMA/EVM with Mimi I don't know if that matters. If the two of you can let me know how you want me to handle LSM patches that are IMA/EVM related I would appreciate it (two ACKs, one or other, something else?).
Once you add a Makefile commane and we sort out the IMA/EVM approval process I think we're good to get this into linux-next. A while back Mimi and I had a chat offline and if I recall everything correctly she preferred that I take this patchset via the LSM tree. I don't have a problem with that, and to be honest I would probably prefer that too, but I wanted to check with everyone that is still the case. Just in case, I've added my ACKs/reviews to this patchset in case this needs to be merged via the integrity tree.
-- paul-moore.com
On Wed, 2024-02-07 at 22:18 -0500, Paul Moore wrote:
On Jan 15, 2024 Roberto Sassu roberto.sassu@huaweicloud.com wrote:
IMA and EVM are not effectively LSMs, especially due to the fact that in the past they could not provide a security blob while there is another LSM active.
That changed in the recent years, the LSM stacking feature now makes it possible to stack together multiple LSMs, and allows them to provide a security blob for most kernel objects. While the LSM stacking feature has some limitations being worked out, it is already suitable to make IMA and EVM as LSMs.
The main purpose of this patch set is to remove IMA and EVM function calls, hardcoded in the LSM infrastructure and other places in the kernel, and to register them as LSM hook implementations, so that those functions are called by the LSM infrastructure like other regular LSMs.
Thanks Roberto, this is looking good. I appreciate all the work you've put into making this happen; when I first mentioned this idea I figured it would be something that would happen much farther into the future, I wasn't expecting to see you pick this up and put in the work to make it happen - thank you.
Thanks! I also appreciate a lot your guidance and suggestions.
I had some pretty minor comments but I think the only thing I saw that I think needs a change/addition is a comment in the Makefile regarding the IMA/EVM ordering; take a look and let me know what you think.
Oh, I remember well, it is there but difficult to spot...
--- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +# The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/
There are also a few patches in the patchset that don't have an ACK/review tag from Mimi, although now that you are co-maininting IMA/EVM with Mimi I don't know if that matters. If the two of you can let me know how you want me to handle LSM patches that are IMA/EVM related I would appreciate it (two ACKs, one or other, something else?).
Ok, we will come back to you about this.
Once you add a Makefile commane and we sort out the IMA/EVM approval process I think we're good to get this into linux-next. A while back Mimi and I had a chat offline and if I recall everything correctly she preferred that I take this patchset via the LSM tree. I don't have a problem with that, and to be honest I would probably prefer that too, but I wanted to check with everyone that is still the case. Just in case, I've added my ACKs/reviews to this patchset in case this needs to be merged via the integrity tree.
Ok, given that there is the comment in the Makefile, the last thing to do from your side is to remove the vague comment in the file_release patch.
Other than that, I think Mimi wanted to give a last look. If that is ok, then the patches should be ready for your repo and linux-next.
Thanks
Roberto
On Thu, Feb 8, 2024 at 3:06 AM Roberto Sassu roberto.sassu@huaweicloud.com wrote:
On Wed, 2024-02-07 at 22:18 -0500, Paul Moore wrote:
...
I had some pretty minor comments but I think the only thing I saw that I think needs a change/addition is a comment in the Makefile regarding the IMA/EVM ordering; take a look and let me know what you think.
Oh, I remember well, it is there but difficult to spot...
--- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +# The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/
Great, thanks for that. Not sure how I missed that ... ?
Once you add a Makefile commane and we sort out the IMA/EVM approval process I think we're good to get this into linux-next. A while back Mimi and I had a chat offline and if I recall everything correctly she preferred that I take this patchset via the LSM tree. I don't have a problem with that, and to be honest I would probably prefer that too, but I wanted to check with everyone that is still the case. Just in case, I've added my ACKs/reviews to this patchset in case this needs to be merged via the integrity tree.
Ok, given that there is the comment in the Makefile, the last thing to do from your side is to remove the vague comment in the file_release patch.
Other than that, I think Mimi wanted to give a last look. If that is ok, then the patches should be ready for your repo and linux-next.
If Mimi is okay with the patchset as-is, and both of you would prefer this to in via the LSM tree, don't worry about the file_release comment, I'll just remove that when merging.
linux-kselftest-mirror@lists.linaro.org