aboutsummaryrefslogtreecommitdiff
path: root/security/integrity
diff options
context:
space:
mode:
authorLinus Torvalds2023-11-02 06:53:22 -1000
committerLinus Torvalds2023-11-02 06:53:22 -1000
commitca219be012786654d5c802ee892433aaa0016d10 (patch)
tree027fe9e949f73c9661217adfbf4feaca5b14241c /security/integrity
parent21e80f3841c01aeaf32d7aee7bbc87b3db1aa0c6 (diff)
parentb836c4d29f2744200b2af41e14bf50758dddc818 (diff)
Merge tag 'integrity-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity
Pull integrity updates from Mimi Zohar: "Four integrity changes: two IMA-overlay updates, an integrity Kconfig cleanup, and a secondary keyring update" * tag 'integrity-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: ima: detect changes to the backing overlay file certs: Only allow certs signed by keys on the builtin keyring integrity: fix indentation of config attributes ima: annotate iint mutex to avoid lockdep false positive warnings
Diffstat (limited to 'security/integrity')
-rw-r--r--security/integrity/Kconfig44
-rw-r--r--security/integrity/iint.c48
-rw-r--r--security/integrity/ima/ima_api.c5
-rw-r--r--security/integrity/ima/ima_main.c16
-rw-r--r--security/integrity/integrity.h2
5 files changed, 81 insertions, 34 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index b6e074ac0227..3c45f4f3455f 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -34,10 +34,10 @@ config INTEGRITY_ASYMMETRIC_KEYS
bool "Enable asymmetric keys support"
depends on INTEGRITY_SIGNATURE
default n
- select ASYMMETRIC_KEY_TYPE
- select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
- select CRYPTO_RSA
- select X509_CERTIFICATE_PARSER
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select CRYPTO_RSA
+ select X509_CERTIFICATE_PARSER
help
This option enables digital signature verification using
asymmetric keys.
@@ -53,14 +53,14 @@ config INTEGRITY_TRUSTED_KEYRING
keyring.
config INTEGRITY_PLATFORM_KEYRING
- bool "Provide keyring for platform/firmware trusted keys"
- depends on INTEGRITY_ASYMMETRIC_KEYS
- depends on SYSTEM_BLACKLIST_KEYRING
- help
- Provide a separate, distinct keyring for platform trusted keys, which
- the kernel automatically populates during initialization from values
- provided by the platform for verifying the kexec'ed kerned image
- and, possibly, the initramfs signature.
+ bool "Provide keyring for platform/firmware trusted keys"
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ depends on SYSTEM_BLACKLIST_KEYRING
+ help
+ Provide a separate, distinct keyring for platform trusted keys, which
+ the kernel automatically populates during initialization from values
+ provided by the platform for verifying the kexec'ed kerned image
+ and, possibly, the initramfs signature.
config INTEGRITY_MACHINE_KEYRING
bool "Provide a keyring to which Machine Owner Keys may be added"
@@ -69,10 +69,10 @@ config INTEGRITY_MACHINE_KEYRING
depends on SYSTEM_BLACKLIST_KEYRING
depends on LOAD_UEFI_KEYS || LOAD_PPC_KEYS
help
- If set, provide a keyring to which Machine Owner Keys (MOK) may
- be added. This keyring shall contain just MOK keys. Unlike keys
- in the platform keyring, keys contained in the .machine keyring will
- be trusted within the kernel.
+ If set, provide a keyring to which Machine Owner Keys (MOK) may
+ be added. This keyring shall contain just MOK keys. Unlike keys
+ in the platform keyring, keys contained in the .machine keyring will
+ be trusted within the kernel.
config INTEGRITY_CA_MACHINE_KEYRING
bool "Enforce Machine Keyring CA Restrictions"
@@ -97,14 +97,14 @@ config INTEGRITY_CA_MACHINE_KEYRING_MAX
.platform keyring.
config LOAD_UEFI_KEYS
- depends on INTEGRITY_PLATFORM_KEYRING
- depends on EFI
- def_bool y
+ depends on INTEGRITY_PLATFORM_KEYRING
+ depends on EFI
+ def_bool y
config LOAD_IPL_KEYS
- depends on INTEGRITY_PLATFORM_KEYRING
- depends on S390
- def_bool y
+ depends on INTEGRITY_PLATFORM_KEYRING
+ depends on S390
+ def_bool y
config LOAD_PPC_KEYS
bool "Enable loading of platform and blacklisted keys for POWER"
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index a462df827de2..27ea19fb1f54 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -66,9 +66,32 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
return iint;
}
-static void iint_free(struct integrity_iint_cache *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)
{
- kfree(iint->ima_hash);
iint->ima_hash = NULL;
iint->version = 0;
iint->flags = 0UL;
@@ -80,6 +103,14 @@ static void iint_free(struct integrity_iint_cache *iint)
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);
}
@@ -104,6 +135,8 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
if (!iint)
return NULL;
+ iint_init_always(iint, inode);
+
write_lock(&integrity_iint_lock);
p = &integrity_iint_tree.rb_node;
@@ -153,25 +186,18 @@ void integrity_inode_free(struct inode *inode)
iint_free(iint);
}
-static void init_once(void *foo)
+static void iint_init_once(void *foo)
{
struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo;
memset(iint, 0, sizeof(*iint));
- 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;
- mutex_init(&iint->mutex);
}
static int __init integrity_iintcache_init(void)
{
iint_cache =
kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
- 0, SLAB_PANIC, init_once);
+ 0, SLAB_PANIC, iint_init_once);
return 0;
}
DEFINE_LSM(integrity) = {
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 452e80b541e5..597ea0c4d72f 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -243,6 +243,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
{
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
+ struct inode *real_inode = d_real_inode(file_dentry(file));
const char *filename = file->f_path.dentry->d_name.name;
struct ima_max_digest_data hash;
struct kstat stat;
@@ -302,6 +303,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
iint->version = i_version;
+ if (real_inode != inode) {
+ iint->real_ino = real_inode->i_ino;
+ iint->real_dev = real_inode->i_sb->s_dev;
+ }
/* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 365db0e43d7c..cc1217ac2c6f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -25,6 +25,7 @@
#include <linux/xattr.h>
#include <linux/ima.h>
#include <linux/fs.h>
+#include <linux/iversion.h>
#include "ima.h"
@@ -207,7 +208,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
enum ima_hooks func)
{
- struct inode *inode = file_inode(file);
+ struct inode *backing_inode, *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
@@ -284,6 +285,19 @@ static int process_measurement(struct file *file, const struct cred *cred,
iint->measured_pcrs = 0;
}
+ /* Detect and re-evaluate changes made to the backing file. */
+ backing_inode = d_real_inode(file_dentry(file));
+ if (backing_inode != inode &&
+ (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
+ if (!IS_I_VERSION(backing_inode) ||
+ backing_inode->i_sb->s_dev != iint->real_dev ||
+ backing_inode->i_ino != iint->real_ino ||
+ !inode_eq_iversion(backing_inode, iint->version)) {
+ iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
+ }
+ }
+
/* Determine if already appraised/measured based on bitmask
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
* IMA_AUDIT, IMA_AUDITED)
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index d7553c93f5c0..9561db7cf6b4 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -164,6 +164,8 @@ struct integrity_iint_cache {
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;