aboutsummaryrefslogtreecommitdiffstats
path: root/security/integrity
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity')
-rw-r--r--security/integrity/Kconfig13
-rw-r--r--security/integrity/Makefile1
-rw-r--r--security/integrity/digsig.c18
-rw-r--r--security/integrity/digsig_asymmetric.c15
-rw-r--r--security/integrity/evm/evm.h3
-rw-r--r--security/integrity/evm/evm_crypto.c9
-rw-r--r--security/integrity/evm/evm_main.c85
-rw-r--r--security/integrity/ima/Kconfig14
-rw-r--r--security/integrity/ima/ima_api.c59
-rw-r--r--security/integrity/ima/ima_appraise.c129
-rw-r--r--security/integrity/ima/ima_crypto.c1
-rw-r--r--security/integrity/ima/ima_efi.c2
-rw-r--r--security/integrity/ima/ima_fs.c33
-rw-r--r--security/integrity/ima/ima_init.c5
-rw-r--r--security/integrity/ima/ima_kexec.c2
-rw-r--r--security/integrity/ima/ima_main.c70
-rw-r--r--security/integrity/ima/ima_policy.c99
-rw-r--r--security/integrity/ima/ima_template.c14
-rw-r--r--security/integrity/ima/ima_template_lib.c105
-rw-r--r--security/integrity/ima/ima_template_lib.h4
-rw-r--r--security/integrity/integrity.h58
-rw-r--r--security/integrity/integrity_audit.c2
-rw-r--r--security/integrity/platform_certs/keyring_handler.c48
-rw-r--r--security/integrity/platform_certs/keyring_handler.h13
-rw-r--r--security/integrity/platform_certs/load_uefi.c37
-rw-r--r--security/integrity/platform_certs/machine_keyring.c77
26 files changed, 721 insertions, 195 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index 71f0177e8716..599429f99f99 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -62,6 +62,19 @@ config INTEGRITY_PLATFORM_KEYRING
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"
+ depends on SECONDARY_TRUSTED_KEYRING
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ depends on SYSTEM_BLACKLIST_KEYRING
+ depends on LOAD_UEFI_KEYS
+ depends on !IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
+ 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.
+
config LOAD_UEFI_KEYS
depends on INTEGRITY_PLATFORM_KEYRING
depends on EFI
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index 7ee39d66cf16..d0ffe37dc1d6 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -10,6 +10,7 @@ integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o
+integrity-$(CONFIG_INTEGRITY_MACHINE_KEYRING) += platform_certs/machine_keyring.o
integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_uefi.o \
platform_certs/keyring_handler.o
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 3b06a01bd0fd..8a82a6c7f48a 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -30,6 +30,7 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
".ima",
#endif
".platform",
+ ".machine",
};
#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
@@ -74,7 +75,8 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
/* v1 API expect signature without xattr type */
return digsig_verify(keyring, sig + 1, siglen - 1, digest,
digestlen);
- case 2:
+ case 2: /* regular file data hash based signature */
+ case 3: /* struct ima_file_id data based signature */
return asymmetric_verify(keyring, sig, siglen, digest,
digestlen);
}
@@ -111,6 +113,8 @@ static int __init __integrity_init_keyring(const unsigned int id,
} else {
if (id == INTEGRITY_KEYRING_PLATFORM)
set_platform_trusted_keys(keyring[id]);
+ if (id == INTEGRITY_KEYRING_MACHINE && trust_moklist())
+ set_machine_trusted_keys(keyring[id]);
if (id == INTEGRITY_KEYRING_IMA)
load_module_cert(keyring[id]);
}
@@ -126,7 +130,8 @@ int __init integrity_init_keyring(const unsigned int id)
perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW
| KEY_USR_READ | KEY_USR_SEARCH;
- if (id == INTEGRITY_KEYRING_PLATFORM) {
+ if (id == INTEGRITY_KEYRING_PLATFORM ||
+ id == INTEGRITY_KEYRING_MACHINE) {
restriction = NULL;
goto out;
}
@@ -139,7 +144,14 @@ int __init integrity_init_keyring(const unsigned int id)
return -ENOMEM;
restriction->check = restrict_link_to_ima;
- perm |= KEY_USR_WRITE;
+
+ /*
+ * MOK keys can only be added through a read-only runtime services
+ * UEFI variable during boot. No additional keys shall be allowed to
+ * load into the machine keyring following init from userspace.
+ */
+ if (id != INTEGRITY_KEYRING_MACHINE)
+ perm |= KEY_USR_WRITE;
out:
return __integrity_init_keyring(id, perm, restriction);
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 23240d793b07..895f4b9ce8c6 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -109,22 +109,25 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pk = asymmetric_key_public_key(key);
pks.pkey_algo = pk->pkey_algo;
- if (!strcmp(pk->pkey_algo, "rsa"))
+ if (!strcmp(pk->pkey_algo, "rsa")) {
pks.encoding = "pkcs1";
- else if (!strncmp(pk->pkey_algo, "ecdsa-", 6))
+ } else if (!strncmp(pk->pkey_algo, "ecdsa-", 6)) {
/* edcsa-nist-p192 etc. */
pks.encoding = "x962";
- else if (!strcmp(pk->pkey_algo, "ecrdsa") ||
- !strcmp(pk->pkey_algo, "sm2"))
+ } else if (!strcmp(pk->pkey_algo, "ecrdsa") ||
+ !strcmp(pk->pkey_algo, "sm2")) {
pks.encoding = "raw";
- else
- return -ENOPKG;
+ } else {
+ ret = -ENOPKG;
+ goto out;
+ }
pks.digest = (u8 *)data;
pks.digest_size = datalen;
pks.s = hdr->sig;
pks.s_size = siglen;
ret = verify_signature(key, &pks);
+out:
key_put(key);
pr_debug("%s() = %d\n", __func__, ret);
return ret;
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 0d44f41d16f8..f8b8c5004fc7 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -38,9 +38,6 @@ extern int evm_initialized;
extern int evm_hmac_attrs;
-extern struct crypto_shash *hmac_tfm;
-extern struct crypto_shash *hash_tfm;
-
/* List of EVM protected security xattrs */
extern struct list_head evm_config_xattrnames;
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 0450d79afdc8..708de9656bbd 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -26,7 +26,7 @@
static unsigned char evmkey[MAX_KEY_SIZE];
static const int evmkey_len = MAX_KEY_SIZE;
-struct crypto_shash *hmac_tfm;
+static struct crypto_shash *hmac_tfm;
static struct crypto_shash *evm_tfm[HASH_ALGO__LAST];
static DEFINE_MUTEX(mutex);
@@ -75,7 +75,7 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo)
{
long rc;
const char *algo;
- struct crypto_shash **tfm, *tmp_tfm = NULL;
+ struct crypto_shash **tfm, *tmp_tfm;
struct shash_desc *desc;
if (type == EVM_XATTR_HMAC) {
@@ -120,16 +120,13 @@ unlock:
alloc:
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm),
GFP_KERNEL);
- if (!desc) {
- crypto_free_shash(tmp_tfm);
+ if (!desc)
return ERR_PTR(-ENOMEM);
- }
desc->tfm = *tfm;
rc = crypto_shash_init(desc);
if (rc) {
- crypto_free_shash(tmp_tfm);
kfree(desc);
return ERR_PTR(rc);
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 08f907382c61..23d484e05e6f 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -36,42 +36,36 @@ static const char * const integrity_status_msg[] = {
int evm_hmac_attrs;
static struct xattr_list evm_config_default_xattrnames[] = {
- {.name = XATTR_NAME_SELINUX,
-#ifdef CONFIG_SECURITY_SELINUX
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_SELINUX,
+ .enabled = IS_ENABLED(CONFIG_SECURITY_SELINUX)
},
- {.name = XATTR_NAME_SMACK,
-#ifdef CONFIG_SECURITY_SMACK
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_SMACK,
+ .enabled = IS_ENABLED(CONFIG_SECURITY_SMACK)
},
- {.name = XATTR_NAME_SMACKEXEC,
-#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_SMACKEXEC,
+ .enabled = IS_ENABLED(CONFIG_EVM_EXTRA_SMACK_XATTRS)
},
- {.name = XATTR_NAME_SMACKTRANSMUTE,
-#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_SMACKTRANSMUTE,
+ .enabled = IS_ENABLED(CONFIG_EVM_EXTRA_SMACK_XATTRS)
},
- {.name = XATTR_NAME_SMACKMMAP,
-#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_SMACKMMAP,
+ .enabled = IS_ENABLED(CONFIG_EVM_EXTRA_SMACK_XATTRS)
},
- {.name = XATTR_NAME_APPARMOR,
-#ifdef CONFIG_SECURITY_APPARMOR
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_APPARMOR,
+ .enabled = IS_ENABLED(CONFIG_SECURITY_APPARMOR)
},
- {.name = XATTR_NAME_IMA,
-#ifdef CONFIG_IMA_APPRAISE
- .enabled = true
-#endif
+ {
+ .name = XATTR_NAME_IMA,
+ .enabled = IS_ENABLED(CONFIG_IMA_APPRAISE)
},
- {.name = XATTR_NAME_CAPS,
+ {
+ .name = XATTR_NAME_CAPS,
.enabled = true
},
};
@@ -86,7 +80,7 @@ static int __init evm_set_fixmode(char *str)
else
pr_err("invalid \"%s\" mode", str);
- return 0;
+ return 1;
}
__setup("evm=", evm_set_fixmode);
@@ -436,7 +430,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
struct inode *inode = d_backing_inode(dentry);
if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
- return 0;
+ return INTEGRITY_PASS;
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
}
@@ -463,10 +457,21 @@ static int evm_xattr_acl_change(struct user_namespace *mnt_userns,
int rc;
/*
- * user_ns is not relevant here, ACL_USER/ACL_GROUP don't have impact
- * on the inode mode (see posix_acl_equiv_mode()).
+ * An earlier comment here mentioned that the idmappings for
+ * ACL_{GROUP,USER} don't matter since EVM is only interested in the
+ * mode stored as part of POSIX ACLs. Nonetheless, if it must translate
+ * from the uapi POSIX ACL representation to the VFS internal POSIX ACL
+ * representation it should do so correctly. There's no guarantee that
+ * we won't change POSIX ACLs in a way that ACL_{GROUP,USER} matters
+ * for the mode at some point and it's difficult to keep track of all
+ * the LSM and integrity modules and what they do to POSIX ACLs.
+ *
+ * Frankly, EVM shouldn't try to interpret the uapi struct for POSIX
+ * ACLs it received. It requires knowledge that only the VFS is
+ * guaranteed to have.
*/
- acl = posix_acl_from_xattr(&init_user_ns, xattr_value, xattr_value_len);
+ acl = vfs_set_acl_prepare(mnt_userns, i_user_ns(inode),
+ xattr_value, xattr_value_len);
if (IS_ERR_OR_NULL(acl))
return 1;
@@ -755,13 +760,14 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
}
-static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
+static int evm_attr_change(struct user_namespace *mnt_userns,
+ struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = attr->ia_valid;
- if ((!(ia_valid & ATTR_UID) || uid_eq(attr->ia_uid, inode->i_uid)) &&
- (!(ia_valid & ATTR_GID) || gid_eq(attr->ia_gid, inode->i_gid)) &&
+ if (!i_uid_needs_update(mnt_userns, attr, inode) &&
+ !i_gid_needs_update(mnt_userns, attr, inode) &&
(!(ia_valid & ATTR_MODE) || attr->ia_mode == inode->i_mode))
return 0;
@@ -775,7 +781,8 @@ static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
* 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 dentry *dentry, struct iattr *attr)
+int evm_inode_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
@@ -801,7 +808,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
return 0;
if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
- !evm_attr_change(dentry, attr))
+ !evm_attr_change(mnt_userns, dentry, attr))
return 0;
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index f3a9cc201c8c..7249f16257c7 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -69,10 +69,9 @@ choice
hash, defined as 20 bytes, and a null terminated pathname,
limited to 255 characters. The 'ima-ng' measurement list
template permits both larger hash digests and longer
- pathnames.
+ pathnames. The configured default template can be replaced
+ by specifying "ima_template=" on the boot command line.
- config IMA_TEMPLATE
- bool "ima"
config IMA_NG_TEMPLATE
bool "ima-ng (default)"
config IMA_SIG_TEMPLATE
@@ -82,7 +81,6 @@ endchoice
config IMA_DEFAULT_TEMPLATE
string
depends on IMA
- default "ima" if IMA_TEMPLATE
default "ima-ng" if IMA_NG_TEMPLATE
default "ima-sig" if IMA_SIG_TEMPLATE
@@ -102,19 +100,19 @@ choice
config IMA_DEFAULT_HASH_SHA256
bool "SHA256"
- depends on CRYPTO_SHA256=y && !IMA_TEMPLATE
+ depends on CRYPTO_SHA256=y
config IMA_DEFAULT_HASH_SHA512
bool "SHA512"
- depends on CRYPTO_SHA512=y && !IMA_TEMPLATE
+ depends on CRYPTO_SHA512=y
config IMA_DEFAULT_HASH_WP512
bool "WP512"
- depends on CRYPTO_WP512=y && !IMA_TEMPLATE
+ depends on CRYPTO_WP512=y
config IMA_DEFAULT_HASH_SM3
bool "SM3"
- depends on CRYPTO_SM3=y && !IMA_TEMPLATE
+ depends on CRYPTO_SM3=y
endchoice
config IMA_DEFAULT_HASH
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index a64fb0130b01..c1e76282b5ee 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -14,6 +14,7 @@
#include <linux/xattr.h>
#include <linux/evm.h>
#include <linux/iversion.h>
+#include <linux/fsverity.h>
#include "ima.h"
@@ -200,6 +201,32 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
allowed_algos);
}
+static int ima_get_verity_digest(struct integrity_iint_cache *iint,
+ struct ima_max_digest_data *hash)
+{
+ enum hash_algo verity_alg;
+ int ret;
+
+ /*
+ * On failure, 'measure' policy rules will result in a file data
+ * hash containing 0's.
+ */
+ ret = fsverity_get_digest(iint->inode, hash->digest, &verity_alg);
+ if (ret)
+ return ret;
+
+ /*
+ * Unlike in the case of actually calculating the file hash, in
+ * the fsverity case regardless of the hash algorithm, return
+ * the verity digest to be included in the measurement list. A
+ * mismatch between the verity algorithm and the xattr signature
+ * algorithm, if one exists, will be detected later.
+ */
+ hash->hdr.algo = verity_alg;
+ hash->hdr.length = hash_digest_size[verity_alg];
+ return 0;
+}
+
/*
* ima_collect_measurement - collect file measurement
*
@@ -217,14 +244,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
const char *filename = file->f_path.dentry->d_name.name;
+ struct ima_max_digest_data hash;
int result = 0;
int length;
void *tmpbuf;
u64 i_version;
- struct {
- struct ima_digest_data hdr;
- char digest[IMA_MAX_DIGEST_SIZE];
- } hash;
/*
* Always collect the modsig, because IMA might have already collected
@@ -238,22 +262,37 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
goto out;
/*
- * Dectecting file change is based on i_version. On filesystems
- * which do not support i_version, support is limited to an initial
- * measurement/appraisal/audit.
+ * Detecting file change is based on i_version. On filesystems
+ * which do not support i_version, support was originally limited
+ * to an initial measurement/appraisal/audit, but was modified to
+ * assume the file changed.
*/
i_version = inode_query_iversion(inode);
hash.hdr.algo = algo;
+ hash.hdr.length = hash_digest_size[algo];
/* Initialize hash digest to 0's in case of failure */
memset(&hash.digest, 0, sizeof(hash.digest));
- if (buf)
+ if (iint->flags & IMA_VERITY_REQUIRED) {
+ result = ima_get_verity_digest(iint, &hash);
+ switch (result) {
+ case 0:
+ break;
+ case -ENODATA:
+ audit_cause = "no-verity-digest";
+ break;
+ default:
+ audit_cause = "invalid-verity-digest";
+ break;
+ }
+ } else if (buf) {
result = ima_calc_buffer_hash(buf, size, &hash.hdr);
- else
+ } else {
result = ima_calc_file_hash(file, &hash.hdr);
+ }
- if (result && result != -EBADF && result != -EINVAL)
+ if (result == -ENOMEM)
goto out;
length = sizeof(hash.hdr) + hash.hdr.length;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 17232bbfb9f9..3e0fbbd99534 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -13,7 +13,9 @@
#include <linux/magic.h>
#include <linux/ima.h>
#include <linux/evm.h>
+#include <linux/fsverity.h>
#include <keys/system_keyring.h>
+#include <uapi/linux/fsverity.h>
#include "ima.h"
@@ -183,13 +185,18 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
return ima_hash_algo;
switch (xattr_value->type) {
+ case IMA_VERITY_DIGSIG:
+ sig = (typeof(sig))xattr_value;
+ if (sig->version != 3 || xattr_len <= sizeof(*sig) ||
+ sig->hash_algo >= HASH_ALGO__LAST)
+ return ima_hash_algo;
+ return sig->hash_algo;
case EVM_IMA_XATTR_DIGSIG:
sig = (typeof(sig))xattr_value;
if (sig->version != 2 || xattr_len <= sizeof(*sig)
|| sig->hash_algo >= HASH_ALGO__LAST)
return ima_hash_algo;
return sig->hash_algo;
- break;
case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */
ret = xattr_value->data[0];
@@ -226,6 +233,40 @@ int ima_read_xattr(struct dentry *dentry,
}
/*
+ * calc_file_id_hash - calculate the hash of the ima_file_id struct data
+ * @type: xattr type [enum evm_ima_xattr_type]
+ * @algo: hash algorithm [enum hash_algo]
+ * @digest: pointer to the digest to be hashed
+ * @hash: (out) pointer to the hash
+ *
+ * IMA signature version 3 disambiguates the data that is signed by
+ * indirectly signing the hash of the ima_file_id structure data.
+ *
+ * Signing the ima_file_id struct is currently only supported for
+ * IMA_VERITY_DIGSIG type xattrs.
+ *
+ * Return 0 on success, error code otherwise.
+ */
+static int calc_file_id_hash(enum evm_ima_xattr_type type,
+ enum hash_algo algo, const u8 *digest,
+ struct ima_digest_data *hash)
+{
+ struct ima_file_id file_id = {
+ .hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo};
+ unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo];
+
+ if (type != IMA_VERITY_DIGSIG)
+ return -EINVAL;
+
+ memcpy(file_id.hash, digest, hash_digest_size[algo]);
+
+ hash->algo = algo;
+ hash->length = hash_digest_size[algo];
+
+ return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash);
+}
+
+/*
* xattr_verify - verify xattr digest or signature
*
* Verify whether the hash or signature matches the file contents.
@@ -236,7 +277,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
+ struct ima_max_digest_data hash;
+ struct signature_v2_hdr *sig;
int rc = -EINVAL, hash_start = 0;
+ int mask;
switch (xattr_value->type) {
case IMA_XATTR_DIGEST_NG:
@@ -246,7 +290,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
case IMA_XATTR_DIGEST:
if (*status != INTEGRITY_PASS_IMMUTABLE) {
if (iint->flags & IMA_DIGSIG_REQUIRED) {
- *cause = "IMA-signature-required";
+ if (iint->flags & IMA_VERITY_REQUIRED)
+ *cause = "verity-signature-required";
+ else
+ *cause = "IMA-signature-required";
*status = INTEGRITY_FAIL;
break;
}
@@ -274,6 +321,20 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
break;
case EVM_IMA_XATTR_DIGSIG:
set_bit(IMA_DIGSIG, &iint->atomic_flags);
+
+ mask = IMA_DIGSIG_REQUIRED | IMA_VERITY_REQUIRED;
+ if ((iint->flags & mask) == mask) {
+ *cause = "verity-signature-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ sig = (typeof(sig))xattr_value;
+ if (sig->version >= 3) {
+ *cause = "invalid-signature-version";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
xattr_len,
@@ -297,6 +358,44 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
*status = INTEGRITY_PASS;
}
break;
+ case IMA_VERITY_DIGSIG:
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ if (!(iint->flags & IMA_VERITY_REQUIRED)) {
+ *cause = "IMA-signature-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+ }
+
+ sig = (typeof(sig))xattr_value;
+ if (sig->version != 3) {
+ *cause = "invalid-signature-version";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
+ iint->ima_hash->digest, &hash.hdr);
+ if (rc) {
+ *cause = "sigv3-hashing-error";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
+ (const char *)xattr_value,
+ xattr_len, hash.digest,
+ hash.hdr.length);
+ if (rc) {
+ *cause = "invalid-verity-signature";
+ *status = INTEGRITY_FAIL;
+ } else {
+ *status = INTEGRITY_PASS;
+ }
+
+ break;
default:
*status = INTEGRITY_UNKNOWN;
*cause = "unknown-ima-data";
@@ -396,8 +495,15 @@ int ima_appraise_measurement(enum ima_hooks func,
if (rc && rc != -ENODATA)
goto out;
- cause = iint->flags & IMA_DIGSIG_REQUIRED ?
- "IMA-signature-required" : "missing-hash";
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ if (iint->flags & IMA_VERITY_REQUIRED)
+ cause = "verity-signature-required";
+ else
+ cause = "IMA-signature-required";
+ } else {
+ cause = "missing-hash";
+ }
+
status = INTEGRITY_NOLABEL;
if (file->f_mode & FMODE_CREATED)
iint->flags |= IMA_NEW_FILE;
@@ -408,7 +514,8 @@ int ima_appraise_measurement(enum ima_hooks func,
goto out;
}
- status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
+ status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
+ rc < 0 ? 0 : rc, iint);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
@@ -643,22 +750,26 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const struct evm_ima_xattr_data *xvalue = xattr_value;
int digsig = 0;
int result;
+ int err;
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
+
+ err = validate_hash_algo(dentry, xvalue, xattr_value_len);
+ if (err)
+ return err;
+
digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
} else if (!strcmp(xattr_name, XATTR_NAME_EVM) && xattr_value_len > 0) {
digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
}
if (result == 1 || evm_revalidate_status(xattr_name)) {
- result = validate_hash_algo(dentry, xvalue, xattr_value_len);
- if (result)
- return result;
-
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+ if (result == 1)
+ result = 0;
}
return result;
}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index a7206cc1d7d1..64499056648a 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -205,6 +205,7 @@ out_array:
crypto_free_shash(ima_algo_array[i].tfm);
}
+ kfree(ima_algo_array);
out:
crypto_free_shash(ima_shash_tfm);
return rc;
diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
index 71786d01946f..9db66fe310d4 100644
--- a/security/integrity/ima/ima_efi.c
+++ b/security/integrity/ima/ima_efi.c
@@ -67,6 +67,8 @@ const char * const *arch_get_ima_policy(void)
if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) {
if (IS_ENABLED(CONFIG_MODULE_SIG))
set_module_sig_enforced();
+ if (IS_ENABLED(CONFIG_KEXEC_SIG))
+ set_kexec_sig_enforced();
return sb_arch_rules;
}
return NULL;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3d8e9d5db5aa..cd1683dad3bf 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -452,56 +452,71 @@ static const struct file_operations ima_measure_policy_ops = {
int __init ima_fs_init(void)
{
+ int ret;
+
ima_dir = securityfs_create_dir("ima", integrity_dir);
if (IS_ERR(ima_dir))
- return -1;
+ return PTR_ERR(ima_dir);
ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
NULL);
- if (IS_ERR(ima_symlink))
+ if (IS_ERR(ima_symlink)) {
+ ret = PTR_ERR(ima_symlink);
goto out;
+ }
binary_runtime_measurements =
securityfs_create_file("binary_runtime_measurements",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_measurements_ops);
- if (IS_ERR(binary_runtime_measurements))
+ if (IS_ERR(binary_runtime_measurements)) {
+ ret = PTR_ERR(binary_runtime_measurements);
goto out;
+ }
ascii_runtime_measurements =
securityfs_create_file("ascii_runtime_measurements",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_ascii_measurements_ops);
- if (IS_ERR(ascii_runtime_measurements))
+ if (IS_ERR(ascii_runtime_measurements)) {
+ ret = PTR_ERR(ascii_runtime_measurements);
goto out;
+ }
runtime_measurements_count =
securityfs_create_file("runtime_measurements_count",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_measurements_count_ops);
- if (IS_ERR(runtime_measurements_count))
+ if (IS_ERR(runtime_measurements_count)) {
+ ret = PTR_ERR(runtime_measurements_count);
goto out;
+ }
violations =
securityfs_create_file("violations", S_IRUSR | S_IRGRP,
ima_dir, NULL, &ima_htable_violations_ops);
- if (IS_ERR(violations))
+ if (IS_ERR(violations)) {
+ ret = PTR_ERR(violations);
goto out;
+ }
ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
ima_dir, NULL,
&ima_measure_policy_ops);
- if (IS_ERR(ima_policy))
+ if (IS_ERR(ima_policy)) {
+ ret = PTR_ERR(ima_policy);
goto out;
+ }
return 0;
out:
+ securityfs_remove(ima_policy);
securityfs_remove(violations);
securityfs_remove(runtime_measurements_count);
securityfs_remove(ascii_runtime_measurements);
securityfs_remove(binary_runtime_measurements);
securityfs_remove(ima_symlink);
securityfs_remove(ima_dir);
- securityfs_remove(ima_policy);
- return -1;
+
+ return ret;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b26fa67476b4..63979aefc95f 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -47,12 +47,9 @@ static int __init ima_add_boot_aggregate(void)
struct integrity_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;
int result = -ENOMEM;
int violation = 0;
- struct {
- struct ima_digest_data hdr;
- char digest[TPM_MAX_DIGEST_SIZE];
- } hash;
memset(iint, 0, sizeof(*iint));
memset(&hash, 0, sizeof(hash));
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 13753136f03f..419dc405c831 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -137,7 +137,7 @@ void ima_add_kexec_buffer(struct kimage *image)
/*
* Restore the measurement list from the previous kernel.
*/
-void ima_load_kexec_buffer(void)
+void __init ima_load_kexec_buffer(void)
{
void *kexec_buffer = NULL;
size_t kexec_buffer_size = 0;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 8c6e4514d494..040b03ddc1c7 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -263,7 +263,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
/* reset appraisal flags if ima_inode_post_setattr was called */
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
- IMA_ACTION_FLAGS);
+ IMA_NONACTION_FLAGS);
/*
* Re-evaulate the file if either the xattr has changed or the
@@ -335,7 +335,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
hash_algo = ima_get_hash_algo(xattr_value, xattr_len);
rc = ima_collect_measurement(iint, file, buf, size, hash_algo, modsig);
- if (rc != 0 && rc != -EBADF && rc != -EINVAL)
+ if (rc == -ENOMEM)
goto out_locked;
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
@@ -418,6 +418,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
/**
* 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.
*
* Files can be mmap'ed read/write and later changed to execute to circumvent
@@ -431,7 +432,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
{
struct ima_template_desc *template = NULL;
- struct file *file = vma->vm_file;
+ struct file *file;
char filename[NAME_MAX];
char *pathbuf = NULL;
const char *pathname = NULL;
@@ -519,20 +520,38 @@ int ima_file_check(struct file *file, int mask)
}
EXPORT_SYMBOL_GPL(ima_file_check);
-static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
+static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
+ size_t buf_size)
{
- struct integrity_iint_cache *iint;
- int hash_algo;
+ struct integrity_iint_cache *iint = NULL, tmp_iint;
+ int rc, hash_algo;
- if (!ima_policy_flag)
- return -EOPNOTSUPP;
+ if (ima_policy_flag) {
+ iint = integrity_iint_find(inode);
+ if (iint)
+ mutex_lock(&iint->mutex);
+ }
+
+ if ((!iint || !(iint->flags & IMA_COLLECTED)) && file) {
+ if (iint)
+ mutex_unlock(&iint->mutex);
+
+ 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,
+ ima_hash_algo, NULL);
+ if (rc < 0)
+ return -EOPNOTSUPP;
+
+ iint = &tmp_iint;
+ mutex_lock(&iint->mutex);
+ }
- iint = integrity_iint_find(inode);
if (!iint)
return -EOPNOTSUPP;
- mutex_lock(&iint->mutex);
-
/*
* ima_file_hash can be called when ima_collect_measurement has still
* not been called, we might not always have a hash.
@@ -551,12 +570,14 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
hash_algo = iint->ima_hash->algo;
mutex_unlock(&iint->mutex);
+ if (iint == &tmp_iint)
+ kfree(iint->ima_hash);
+
return hash_algo;
}
/**
- * ima_file_hash - return the stored measurement if a file has been hashed and
- * is in the iint cache.
+ * ima_file_hash - return a measurement of the file
* @file: pointer to the file
* @buf: buffer in which to store the hash
* @buf_size: length of the buffer
@@ -569,7 +590,7 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
* The file hash returned is based on the entire file, including the appended
* signature.
*
- * If IMA is disabled or if no measurement is available, return -EOPNOTSUPP.
+ * If the measurement cannot be performed, return -EOPNOTSUPP.
* If the parameters are incorrect, return -EINVAL.
*/
int ima_file_hash(struct file *file, char *buf, size_t buf_size)
@@ -577,7 +598,7 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
if (!file)
return -EINVAL;
- return __ima_inode_hash(file_inode(file), buf, buf_size);
+ return __ima_inode_hash(file_inode(file), file, buf, buf_size);
}
EXPORT_SYMBOL_GPL(ima_file_hash);
@@ -604,14 +625,14 @@ int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
if (!inode)
return -EINVAL;
- return __ima_inode_hash(inode, buf, buf_size);
+ return __ima_inode_hash(inode, NULL, buf, buf_size);
}
EXPORT_SYMBOL_GPL(ima_inode_hash);
/**
* ima_post_create_tmpfile - mark newly created tmpfile as new
- * @mnt_userns: user namespace of the mount the inode was found from
- * @file : newly created tmpfile
+ * @mnt_userns: user namespace of the mount the inode was found from
+ * @inode: inode of the newly created tmpfile
*
* No measuring, appraising or auditing of newly created tmpfiles is needed.
* Skip calling process_measurement(), but indicate which newly, created
@@ -643,7 +664,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
/**
* ima_post_path_mknod - mark as a new inode
- * @mnt_userns: user namespace of the mount the inode was found from
+ * @mnt_userns: user namespace of the mount the inode was found from
* @dentry: newly created dentry
*
* Mark files created via the mknodat syscall as new, so that the
@@ -764,7 +785,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
* call to ima_post_load_data().
*
* Callers of this LSM hook can not measure, appraise, or audit the
- * data provided by userspace. Enforce policy rules requring a file
+ * data provided by userspace. Enforce policy rules requiring a file
* signature (eg. kexec'ed kernel image).
*
* For permission return 0, otherwise return -EACCES.
@@ -814,8 +835,8 @@ int ima_load_data(enum kernel_load_data_id id, bool contents)
* ima_post_load_data - appraise decision based on policy
* @buf: pointer to in memory file contents
* @size: size of in memory file contents
- * @id: kernel load data caller identifier
- * @description: @id-specific description of contents
+ * @load_id: kernel load data caller identifier
+ * @description: @load_id-specific description of contents
*
* Measure/appraise/audit in memory buffer based on policy. Policy rules
* are written in terms of a policy identifier.
@@ -874,10 +895,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
.buf = buf,
.buf_len = size};
struct ima_template_desc *template;
- struct {
- struct ima_digest_data hdr;
- char digest[IMA_MAX_DIGEST_SIZE];
- } hash = {};
+ struct ima_max_digest_data hash;
char digest_hash[IMA_MAX_DIGEST_SIZE];
int digest_hash_len = hash_digest_size[ima_hash_algo];
int violation = 0;
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 320ca80aacab..a8802b8da946 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -16,7 +16,6 @@
#include <linux/parser.h>
#include <linux/slab.h>
#include <linux/rculist.h>
-#include <linux/genhd.h>
#include <linux/seq_file.h>
#include <linux/ima.h>
@@ -429,7 +428,7 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry)
/*
* ima_lsm_copy_rule() shallow copied all references, except for the
* LSM references, from entry to nentry so we only want to free the LSM
- * references and the entry itself. All other memory refrences will now
+ * references and the entry itself. All other memory references will now
* be owned by nentry.
*/
ima_lsm_free_rule(entry);
@@ -712,7 +711,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
func, mask, func_data))
continue;
- action |= entry->flags & IMA_ACTION_FLAGS;
+ action |= entry->flags & IMA_NONACTION_FLAGS;
action |= entry->action & IMA_DO_MASK;
if (entry->action & IMA_APPRAISE) {
@@ -1024,6 +1023,7 @@ enum policy_opt {
Opt_fowner_gt, Opt_fgroup_gt,
Opt_uid_lt, Opt_euid_lt, Opt_gid_lt, Opt_egid_lt,
Opt_fowner_lt, Opt_fgroup_lt,
+ Opt_digest_type,
Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos,
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
Opt_label, Opt_err
@@ -1066,6 +1066,7 @@ static const match_table_t policy_tokens = {
{Opt_egid_lt, "egid<%s"},
{Opt_fowner_lt, "fowner<%s"},
{Opt_fgroup_lt, "fgroup<%s"},
+ {Opt_digest_type, "digest_type=%s"},
{Opt_appraise_type, "appraise_type=%s"},
{Opt_appraise_flag, "appraise_flag=%s"},
{Opt_appraise_algos, "appraise_algos=%s"},
@@ -1173,6 +1174,21 @@ static void check_template_modsig(const struct ima_template_desc *template)
#undef MSG
}
+/*
+ * Warn if the template does not contain the given field.
+ */
+static void check_template_field(const struct ima_template_desc *template,
+ const char *field, const char *msg)
+{
+ int i;
+
+ for (i = 0; i < template->num_fields; i++)
+ if (!strcmp(template->fields[i]->field_id, field))
+ return;
+
+ pr_notice_once("%s", msg);
+}
+
static bool ima_validate_rule(struct ima_rule_entry *entry)
{
/* Ensure that the action is set and is compatible with the flags */
@@ -1215,7 +1231,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
- IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS))
+ IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
+ IMA_VERITY_REQUIRED))
return false;
break;
@@ -1293,6 +1310,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
!(entry->flags & IMA_MODSIG_ALLOWED))
return false;
+ /*
+ * Unlike for regular IMA 'appraise' policy rules where security.ima
+ * xattr may contain either a file hash or signature, the security.ima
+ * xattr for fsverity must contain a file signature (sigv3). Ensure
+ * that 'appraise' rules for fsverity require file signatures by
+ * checking the IMA_DIGSIG_REQUIRED flag is set.
+ */
+ if (entry->action == APPRAISE &&
+ (entry->flags & IMA_VERITY_REQUIRED) &&
+ !(entry->flags & IMA_DIGSIG_REQUIRED))
+ return false;
+
return true;
}
@@ -1708,16 +1737,39 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE);
break;
+ case Opt_digest_type:
+ ima_log_string(ab, "digest_type", args[0].from);
+ if (entry->flags & IMA_DIGSIG_REQUIRED)
+ result = -EINVAL;
+ else if ((strcmp(args[0].from, "verity")) == 0)
+ entry->flags |= IMA_VERITY_REQUIRED;
+ else
+ result = -EINVAL;
+ break;
case Opt_appraise_type:
ima_log_string(ab, "appraise_type", args[0].from);
- if ((strcmp(args[0].from, "imasig")) == 0)
- entry->flags |= IMA_DIGSIG_REQUIRED;
- else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
- strcmp(args[0].from, "imasig|modsig") == 0)
- entry->flags |= IMA_DIGSIG_REQUIRED |
+
+ if ((strcmp(args[0].from, "imasig")) == 0) {
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_DIGSIG_REQUIRED;
+ } else if (strcmp(args[0].from, "sigv3") == 0) {
+ /* Only fsverity supports sigv3 for now */
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ entry->flags |= IMA_DIGSIG_REQUIRED;
+ else
+ result = -EINVAL;
+ } else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
+ strcmp(args[0].from, "imasig|modsig") == 0) {
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_DIGSIG_REQUIRED |
IMA_MODSIG_ALLOWED;
- else
+ } else {
result = -EINVAL;
+ }
break;
case Opt_appraise_flag:
ima_log_string(ab, "appraise_flag", args[0].from);
@@ -1798,6 +1850,15 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
check_template_modsig(template_desc);
}
+ /* d-ngv2 template field recommended for unsigned fs-verity digests */
+ if (!result && entry->action == MEASURE &&
+ entry->flags & IMA_VERITY_REQUIRED) {
+ template_desc = entry->template ? entry->template :
+ ima_template_desc_current();
+ check_template_field(template_desc, "d-ngv2",
+ "verity rules should include d-ngv2");
+ }
+
audit_log_format(ab, "res=%d", !result);
audit_log_end(ab);
return result;
@@ -1967,6 +2028,14 @@ int ima_policy_show(struct seq_file *m, void *v)
rcu_read_lock();
+ /* Do not print rules with inactive LSM labels */
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ if (entry->lsm[i].args_p && !entry->lsm[i].rule) {
+ rcu_read_unlock();
+ return 0;
+ }
+ }
+
if (entry->action & MEASURE)
seq_puts(m, pt(Opt_measure));
if (entry->action & DONT_MEASURE)
@@ -2142,11 +2211,15 @@ int ima_policy_show(struct seq_file *m, void *v)
if (entry->template)
seq_printf(m, "template=%s ", entry->template->name);
if (entry->flags & IMA_DIGSIG_REQUIRED) {
- if (entry->flags & IMA_MODSIG_ALLOWED)
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ seq_puts(m, "appraise_type=sigv3 ");
+ else if (entry->flags & IMA_MODSIG_ALLOWED)
seq_puts(m, "appraise_type=imasig|modsig ");
else
seq_puts(m, "appraise_type=imasig ");
}
+ if (entry->flags & IMA_VERITY_REQUIRED)
+ seq_puts(m, "digest_type=verity ");
if (entry->flags & IMA_CHECK_BLACKLIST)
seq_puts(m, "appraise_flag=check_blacklist ");
if (entry->flags & IMA_PERMIT_DIRECTIO)
@@ -2174,6 +2247,10 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
if (id >= READING_MAX_ID)
return false;
+ if (id == READING_KEXEC_IMAGE && !(ima_appraise & IMA_APPRAISE_ENFORCE)
+ && security_locked_down(LOCKDOWN_KEXEC))
+ return false;
+
func = read_idmap[id] ?: FILE_CHECK;
rcu_read_lock();
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 694560396be0..c25079faa208 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -20,6 +20,8 @@ static struct ima_template_desc builtin_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
+ {.name = "ima-ngv2", .fmt = "d-ngv2|n-ng"},
+ {.name = "ima-sigv2", .fmt = "d-ngv2|n-ng|sig"},
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
{.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
{.name = "evm-sig",
@@ -29,6 +31,7 @@ static struct ima_template_desc builtin_templates[] = {
static LIST_HEAD(defined_templates);
static DEFINE_SPINLOCK(template_list);
+static int template_setup_done;
static const struct ima_template_field supported_fields[] = {
{.field_id = "d", .field_init = ima_eventdigest_init,
@@ -37,6 +40,8 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_string},
{.field_id = "d-ng", .field_init = ima_eventdigest_ng_init,
.field_show = ima_show_template_digest_ng},
+ {.field_id = "d-ngv2", .field_init = ima_eventdigest_ngv2_init,
+ .field_show = ima_show_template_digest_ngv2},
{.field_id = "n-ng", .field_init = ima_eventname_ng_init,
.field_show = ima_show_template_string},
{.field_id = "sig", .field_init = ima_eventsig_init,
@@ -101,10 +106,11 @@ static int __init ima_template_setup(char *str)
struct ima_template_desc *template_desc;
int template_len = strlen(str);
- if (ima_template)
+ if (template_setup_done)
return 1;
- ima_init_template_list();
+ if (!ima_template)
+ ima_init_template_list();
/*
* Verify that a template with the supplied name exists.
@@ -128,6 +134,7 @@ static int __init ima_template_setup(char *str)
}
ima_template = template_desc;
+ template_setup_done = 1;
return 1;
}
__setup("ima_template=", ima_template_setup);
@@ -136,7 +143,7 @@ static int __init ima_template_fmt_setup(char *str)
{
int num_templates = ARRAY_SIZE(builtin_templates);
- if (ima_template)
+ if (template_setup_done)
return 1;
if (template_desc_init_fields(str, NULL, NULL) < 0) {
@@ -147,6 +154,7 @@ static int __init ima_template_fmt_setup(char *str)
builtin_templates[num_templates - 1].fmt = str;
ima_template = builtin_templates + num_templates - 1;
+ template_setup_done = 1;
return 1;
}
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index ca017cae73eb..7bf9b1507220 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -24,11 +24,24 @@ static bool ima_template_hash_algo_allowed(u8 algo)
enum data_formats {
DATA_FMT_DIGEST = 0,
DATA_FMT_DIGEST_WITH_ALGO,
+ DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO,
DATA_FMT_STRING,
DATA_FMT_HEX,
DATA_FMT_UINT
};
+enum digest_type {
+ DIGEST_TYPE_IMA,
+ DIGEST_TYPE_VERITY,
+ DIGEST_TYPE__LAST
+};
+
+#define DIGEST_TYPE_NAME_LEN_MAX 7 /* including NUL */
+static const char * const digest_type_name[DIGEST_TYPE__LAST] = {
+ [DIGEST_TYPE_IMA] = "ima",
+ [DIGEST_TYPE_VERITY] = "verity"
+};
+
static int ima_write_template_field_data(const void *data, const u32 datalen,
enum data_formats datafmt,
struct ima_field_data *field_data)
@@ -72,8 +85,9 @@ static void ima_show_template_data_ascii(struct seq_file *m,
u32 buflen = field_data->len;
switch (datafmt) {
+ case DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
case DATA_FMT_DIGEST_WITH_ALGO:
- buf_ptr = strnchr(field_data->data, buflen, ':');
+ buf_ptr = strrchr(field_data->data, ':');
if (buf_ptr != field_data->data)
seq_printf(m, "%s", field_data->data);
@@ -178,6 +192,14 @@ void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
field_data);
}
+void ima_show_template_digest_ngv2(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data)
+{
+ ima_show_template_field_data(m, show,
+ DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO,
+ field_data);
+}
+
void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data)
{
@@ -265,26 +287,35 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
}
static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
- u8 hash_algo,
+ u8 digest_type, u8 hash_algo,
struct ima_field_data *field_data)
{
/*
* digest formats:
* - DATA_FMT_DIGEST: digest
- * - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest,
- * where <hash algo> is provided if the hash algoritm is not
- * SHA1 or MD5
+ * - DATA_FMT_DIGEST_WITH_ALGO: <hash algo> + ':' + '\0' + digest,
+ * - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
+ * <digest type> + ':' + <hash algo> + ':' + '\0' + digest,
+ *
+ * where 'DATA_FMT_DIGEST' is the original digest format ('d')
+ * with a hash size limitation of 20 bytes,
+ * where <digest type> is either "ima" or "verity",
+ * where <hash algo> is the hash_algo_name[] string.
*/
- u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 };
+ u8 buffer[DIGEST_TYPE_NAME_LEN_MAX + CRYPTO_MAX_ALG_NAME + 2 +
+ IMA_MAX_DIGEST_SIZE] = { 0 };
enum data_formats fmt = DATA_FMT_DIGEST;
u32 offset = 0;
- if (hash_algo < HASH_ALGO__LAST) {
+ if (digest_type < DIGEST_TYPE__LAST && hash_algo < HASH_ALGO__LAST) {
+ fmt = DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO;
+ offset += 1 + sprintf(buffer, "%s:%s:",
+ digest_type_name[digest_type],
+ hash_algo_name[hash_algo]);
+ } else if (hash_algo < HASH_ALGO__LAST) {
fmt = DATA_FMT_DIGEST_WITH_ALGO;
- offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s",
- hash_algo_name[hash_algo]);
- buffer[offset] = ':';
- offset += 2;
+ offset += 1 + sprintf(buffer, "%s:",
+ hash_algo_name[hash_algo]);
}
if (digest)
@@ -292,10 +323,10 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
else
/*
* If digest is NULL, the event being recorded is a violation.
- * Make room for the digest by increasing the offset of
- * IMA_DIGEST_SIZE.
+ * Make room for the digest by increasing the offset by the
+ * hash algorithm digest size.
*/
- offset += IMA_DIGEST_SIZE;
+ offset += hash_digest_size[hash_algo];
return ima_write_template_field_data(buffer, offset + digestsize,
fmt, field_data);
@@ -307,10 +338,7 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
- struct {
- struct ima_digest_data hdr;
- char digest[IMA_MAX_DIGEST_SIZE];
- } hash;
+ struct ima_max_digest_data hash;
u8 *cur_digest = NULL;
u32 cur_digestsize = 0;
struct inode *inode;
@@ -362,7 +390,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
cur_digestsize = hash.hdr.length;
out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
- HASH_ALGO__LAST, field_data);
+ DIGEST_TYPE__LAST, HASH_ALGO__LAST,
+ field_data);
}
/*
@@ -371,8 +400,32 @@ out:
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
- u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;
+ u8 *cur_digest = NULL, hash_algo = ima_hash_algo;
+ u32 cur_digestsize = 0;
+
+ if (event_data->violation) /* recording a violation. */
+ goto out;
+
+ cur_digest = event_data->iint->ima_hash->digest;
+ cur_digestsize = event_data->iint->ima_hash->length;
+
+ hash_algo = event_data->iint->ima_hash->algo;
+out:
+ return ima_eventdigest_init_common(cur_digest, cur_digestsize,
+ DIGEST_TYPE__LAST, hash_algo,
+ field_data);
+}
+
+/*
+ * This function writes the digest of an event (without size limit),
+ * prefixed with both the digest type and hash algorithm.
+ */
+int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ u8 *cur_digest = NULL, hash_algo = ima_hash_algo;
u32 cur_digestsize = 0;
+ u8 digest_type = DIGEST_TYPE_IMA;
if (event_data->violation) /* recording a violation. */
goto out;
@@ -381,9 +434,12 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
cur_digestsize = event_data->iint->ima_hash->length;
hash_algo = event_data->iint->ima_hash->algo;
+ if (event_data->iint->flags & IMA_VERITY_REQUIRED)
+ digest_type = DIGEST_TYPE_VERITY;
out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
- hash_algo, field_data);
+ digest_type, hash_algo,
+ field_data);
}
/*
@@ -418,7 +474,8 @@ int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
}
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
- hash_algo, field_data);
+ DIGEST_TYPE__LAST, hash_algo,
+ field_data);
}
static int ima_eventname_init_common(struct ima_event_data *event_data,
@@ -478,7 +535,9 @@ int ima_eventsig_init(struct ima_event_data *event_data,
{
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
- if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
+ if (!xattr_value ||
+ (xattr_value->type != EVM_IMA_XATTR_DIGSIG &&
+ xattr_value->type != IMA_VERITY_DIGSIG))
return ima_eventevmsig_init(event_data, field_data);
return ima_write_template_field_data(xattr_value, event_data->xattr_len,
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index c71f1de95753..9f7c335f304f 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -21,6 +21,8 @@ void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
+void ima_show_template_digest_ngv2(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data);
void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
@@ -38,6 +40,8 @@ int ima_eventname_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventname_ng_init(struct ima_event_data *event_data,
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 547425c20e11..7167a6e99bdc 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include <linux/integrity.h>
#include <crypto/sha1.h>
+#include <crypto/hash.h>
#include <linux/key.h>
#include <linux/audit.h>
@@ -30,8 +31,8 @@
#define IMA_HASH 0x00000100
#define IMA_HASHED 0x00000200
-/* iint cache flags */
-#define IMA_ACTION_FLAGS 0xff000000
+/* 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
@@ -39,6 +40,7 @@
#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)
@@ -77,6 +79,7 @@ enum evm_ima_xattr_type {
EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG,
EVM_XATTR_PORTABLE_DIGSIG,
+ IMA_VERITY_DIGSIG,
IMA_XATTR_LAST
};
@@ -91,7 +94,7 @@ struct evm_xattr {
u8 digest[SHA1_DIGEST_SIZE];
} __packed;
-#define IMA_MAX_DIGEST_SIZE 64
+#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
struct ima_digest_data {
u8 algo;
@@ -111,7 +114,23 @@ struct ima_digest_data {
} __packed;
/*
- * signature format v2 - for using with asymmetric keys
+ * Instead of wrapping the ima_digest_data struct inside a local structure
+ * with the maximum hash size, define ima_max_digest_data struct.
+ */
+struct ima_max_digest_data {
+ struct ima_digest_data hdr;
+ u8 digest[HASH_MAX_DIGESTSIZE];
+} __packed;
+
+/*
+ * signature header format v2 - for using with asymmetric keys
+ *
+ * The signature_v2_hdr struct includes a signature format version
+ * to simplify defining new signature formats.
+ *
+ * signature format:
+ * version 2: regular file data hash based signature
+ * version 3: struct ima_file_id data based signature
*/
struct signature_v2_hdr {
uint8_t type; /* xattr type */
@@ -122,6 +141,20 @@ struct signature_v2_hdr {
uint8_t sig[]; /* signature payload */
} __packed;
+/*
+ * IMA signature version 3 disambiguates the data that is signed, by
+ * indirectly signing the hash of the ima_file_id structure data,
+ * containing either the fsverity_descriptor struct digest or, in the
+ * future, the regular IMA file hash.
+ *
+ * (The hash of the ima_file_id structure is only of the portion used.)
+ */
+struct ima_file_id {
+ __u8 hash_type; /* xattr type [enum evm_ima_xattr_type] */
+ __u8 hash_algorithm; /* Digest algorithm [enum hash_algo] */
+ __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 */
@@ -151,7 +184,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
#define INTEGRITY_KEYRING_EVM 0
#define INTEGRITY_KEYRING_IMA 1
#define INTEGRITY_KEYRING_PLATFORM 2
-#define INTEGRITY_KEYRING_MAX 3
+#define INTEGRITY_KEYRING_MACHINE 3
+#define INTEGRITY_KEYRING_MAX 4
extern struct dentry *integrity_dir;
@@ -283,3 +317,17 @@ static inline void __init add_to_platform_keyring(const char *source,
{
}
#endif
+
+#ifdef CONFIG_INTEGRITY_MACHINE_KEYRING
+void __init add_to_machine_keyring(const char *source, const void *data, size_t len);
+bool __init trust_moklist(void);
+#else
+static inline void __init add_to_machine_keyring(const char *source,
+ const void *data, size_t len)
+{
+}
+static inline bool __init trust_moklist(void)
+{
+ return false;
+}
+#endif
diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c
index 29220056207f..0ec5e4c22cb2 100644
--- a/security/integrity/integrity_audit.c
+++ b/security/integrity/integrity_audit.c
@@ -45,6 +45,8 @@ void integrity_audit_message(int audit_msgno, struct inode *inode,
return;
ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno);
+ if (!ab)
+ return;
audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
task_pid_nr(current),
from_kuid(&init_user_ns, current_uid()),
diff --git a/security/integrity/platform_certs/keyring_handler.c b/security/integrity/platform_certs/keyring_handler.c
index 5604bd57c990..8a1124e4d769 100644
--- a/security/integrity/platform_certs/keyring_handler.c
+++ b/security/integrity/platform_certs/keyring_handler.c
@@ -9,6 +9,7 @@
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "../integrity.h"
+#include "keyring_handler.h"
static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID;
static efi_guid_t efi_cert_x509_sha256_guid __initdata =
@@ -16,34 +17,12 @@ static efi_guid_t efi_cert_x509_sha256_guid __initdata =
static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID;
/*
- * Blacklist a hash.
- */
-static __init void uefi_blacklist_hash(const char *source, const void *data,
- size_t len, const char *type,
- size_t type_len)
-{
- char *hash, *p;
-
- hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL);
- if (!hash)
- return;
- p = memcpy(hash, type, type_len);
- p += type_len;
- bin2hex(p, data, len);
- p += len * 2;
- *p = 0;
-
- mark_hash_blacklisted(hash);
- kfree(hash);
-}
-
-/*
* Blacklist an X509 TBS hash.
*/
static __init void uefi_blacklist_x509_tbs(const char *source,
const void *data, size_t len)
{
- uefi_blacklist_hash(source, data, len, "tbs:", 4);
+ mark_hash_blacklisted(data, len, BLACKLIST_HASH_X509_TBS);
}
/*
@@ -52,7 +31,7 @@ static __init void uefi_blacklist_x509_tbs(const char *source,
static __init void uefi_blacklist_binary(const char *source,
const void *data, size_t len)
{
- uefi_blacklist_hash(source, data, len, "bin:", 4);
+ mark_hash_blacklisted(data, len, BLACKLIST_HASH_BINARY);
}
/*
@@ -66,13 +45,28 @@ static __init void uefi_revocation_list_x509(const char *source,
/*
* Return the appropriate handler for particular signature list types found in
- * the UEFI db and MokListRT tables.
+ * the UEFI db tables.
*/
__init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return add_to_platform_keyring;
- return 0;
+ return NULL;
+}
+
+/*
+ * Return the appropriate handler for particular signature list types found in
+ * the MokListRT tables.
+ */
+__init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)
+{
+ if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) {
+ if (IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) && trust_moklist())
+ return add_to_machine_keyring;
+ else
+ return add_to_platform_keyring;
+ }
+ return NULL;
}
/*
@@ -87,5 +81,5 @@ __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type)
return uefi_blacklist_binary;
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return uefi_revocation_list_x509;
- return 0;
+ return NULL;
}
diff --git a/security/integrity/platform_certs/keyring_handler.h b/security/integrity/platform_certs/keyring_handler.h
index 2462bfa08fe3..212d894a8c0c 100644
--- a/security/integrity/platform_certs/keyring_handler.h
+++ b/security/integrity/platform_certs/keyring_handler.h
@@ -25,8 +25,21 @@ void blacklist_binary(const char *source, const void *data, size_t len);
efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type);
/*
+ * Return the handler for particular signature list types found in the mok.
+ */
+efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type);
+
+/*
* Return the handler for particular signature list types found in the dbx.
*/
efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type);
#endif
+
+#ifndef UEFI_QUIRK_SKIP_CERT
+#define UEFI_QUIRK_SKIP_CERT(vendor, product) \
+ .matches = { \
+ DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
+ DMI_MATCH(DMI_PRODUCT_NAME, product), \
+ },
+#endif
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
index 08b6d12f99b4..b78753d27d8e 100644
--- a/security/integrity/platform_certs/load_uefi.c
+++ b/security/integrity/platform_certs/load_uefi.c
@@ -3,6 +3,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
+#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/efi.h>
#include <linux/slab.h>
@@ -13,6 +14,31 @@
#include "keyring_handler.h"
/*
+ * On T2 Macs reading the db and dbx efi variables to load UEFI Secure Boot
+ * certificates causes occurrence of a page fault in Apple's firmware and
+ * a crash disabling EFI runtime services. The following quirk skips reading
+ * these variables.
+ */
+static const struct dmi_system_id uefi_skip_cert[] = {
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,2") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,3") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,4") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,2") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,3") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,4") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,2") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir9,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "Macmini8,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacPro7,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,1") },
+ { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,2") },
+ { }
+};
+
+/*
* Look to see if a UEFI variable called MokIgnoreDB exists and return true if
* it does.
*
@@ -95,7 +121,7 @@ static int __init load_moklist_certs(void)
rc = parse_efi_signature_list("UEFI:MokListRT (MOKvar table)",
mokvar_entry->data,
mokvar_entry->data_size,
- get_handler_for_db);
+ get_handler_for_mok);
/* All done if that worked. */
if (!rc)
return rc;
@@ -110,7 +136,7 @@ static int __init load_moklist_certs(void)
mok = get_cert_list(L"MokListRT", &mok_var, &moksize, &status);
if (mok) {
rc = parse_efi_signature_list("UEFI:MokListRT",
- mok, moksize, get_handler_for_db);
+ mok, moksize, get_handler_for_mok);
kfree(mok);
if (rc)
pr_err("Couldn't parse MokListRT signatures: %d\n", rc);
@@ -138,6 +164,13 @@ static int __init load_uefi_certs(void)
unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0;
efi_status_t status;
int rc = 0;
+ const struct dmi_system_id *dmi_id;
+
+ dmi_id = dmi_first_match(uefi_skip_cert);
+ if (dmi_id) {
+ pr_err("Reading UEFI Secure Boot Certs is not supported on T2 Macs.\n");
+ return false;
+ }
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return false;
diff --git a/security/integrity/platform_certs/machine_keyring.c b/security/integrity/platform_certs/machine_keyring.c
new file mode 100644
index 000000000000..7aaed7950b6e
--- /dev/null
+++ b/security/integrity/platform_certs/machine_keyring.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Machine keyring routines.
+ *
+ * Copyright (c) 2021, Oracle and/or its affiliates.
+ */
+
+#include <linux/efi.h>
+#include "../integrity.h"
+
+static bool trust_mok;
+
+static __init int machine_keyring_init(void)
+{
+ int rc;
+
+ rc = integrity_init_keyring(INTEGRITY_KEYRING_MACHINE);
+ if (rc)
+ return rc;
+
+ pr_notice("Machine keyring initialized\n");
+ return 0;
+}
+device_initcall(machine_keyring_init);
+
+void __init add_to_machine_keyring(const char *source, const void *data, size_t len)
+{
+ key_perm_t perm;
+ int rc;
+
+ perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW;
+ rc = integrity_load_cert(INTEGRITY_KEYRING_MACHINE, source, data, len, perm);
+
+ /*
+ * Some MOKList keys may not pass the machine keyring restrictions.
+ * If the restriction check does not pass and the platform keyring
+ * is configured, try to add it into that keyring instead.
+ */
+ if (rc && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING))
+ rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source,
+ data, len, perm);
+
+ if (rc)
+ pr_info("Error adding keys to machine keyring %s\n", source);
+}
+
+/*
+ * Try to load the MokListTrustedRT MOK variable to see if we should trust
+ * the MOK keys within the kernel. It is not an error if this variable
+ * does not exist. If it does not exist, MOK keys should not be trusted
+ * within the machine keyring.
+ */
+static __init bool uefi_check_trust_mok_keys(void)
+{
+ struct efi_mokvar_table_entry *mokvar_entry;
+
+ mokvar_entry = efi_mokvar_entry_find("MokListTrustedRT");
+
+ if (mokvar_entry)
+ return true;
+
+ return false;
+}
+
+bool __init trust_moklist(void)
+{
+ static bool initialized;
+
+ if (!initialized) {
+ initialized = true;
+
+ if (uefi_check_trust_mok_keys())
+ trust_mok = true;
+ }
+
+ return trust_mok;
+}