Merge tag 'integrity-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2022 20:50:39 +0000 (13:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2022 20:50:39 +0000 (13:50 -0700)
Pull IMA updates from Mimi Zohar:
 "New is IMA support for including fs-verity file digests and signatures
  in the IMA measurement list as well as verifying the fs-verity file
  digest based signatures, both based on policy.

  In addition, are two bug fixes:

   - avoid reading UEFI variables, which cause a page fault, on Apple
     Macs with T2 chips.

   - remove the original "ima" template Kconfig option to address a boot
     command line ordering issue.

  The rest is a mixture of code/documentation cleanup"

* tag 'integrity-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  integrity: Fix sparse warnings in keyring_handler
  evm: Clean up some variables
  evm: Return INTEGRITY_PASS for enum integrity_status value '0'
  efi: Do not import certificates from UEFI Secure Boot for T2 Macs
  fsverity: update the documentation
  ima: support fs-verity file digest based version 3 signatures
  ima: permit fsverity's file digests in the IMA measurement list
  ima: define a new template field named 'd-ngv2' and templates
  fs-verity: define a function to return the integrity protected file digest
  ima: use IMA default hash algorithm for integrity violations
  ima: fix 'd-ng' comments and documentation
  ima: remove the IMA_TEMPLATE Kconfig option
  ima: remove redundant initialization of pointer 'file'.

24 files changed:
Documentation/ABI/testing/ima_policy
Documentation/admin-guide/kernel-parameters.txt
Documentation/filesystems/fsverity.rst
Documentation/security/IMA-templates.rst
fs/verity/Kconfig
fs/verity/fsverity_private.h
fs/verity/measure.c
include/linux/fsverity.h
security/integrity/digsig.c
security/integrity/evm/evm.h
security/integrity/evm/evm_crypto.c
security/integrity/evm/evm_main.c
security/integrity/ima/Kconfig
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/ima/ima_template.c
security/integrity/ima/ima_template_lib.c
security/integrity/ima/ima_template_lib.h
security/integrity/integrity.h
security/integrity/platform_certs/keyring_handler.c
security/integrity/platform_certs/keyring_handler.h
security/integrity/platform_certs/load_uefi.c

index 839fab811b1864de229c4a913a94c83469bd30a6..db17fc8a0c9f6e3416631a9804efce98bc17c2c4 100644 (file)
@@ -27,8 +27,9 @@ Description:
                                [fowner=] [fgroup=]]
                        lsm:    [[subj_user=] [subj_role=] [subj_type=]
                                 [obj_user=] [obj_role=] [obj_type=]]
-                       option: [[appraise_type=]] [template=] [permit_directio]
-                               [appraise_flag=] [appraise_algos=] [keyrings=]
+                       option: [digest_type=] [template=] [permit_directio]
+                               [appraise_type=] [appraise_flag=]
+                               [appraise_algos=] [keyrings=]
                  base:
                        func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
                                [FIRMWARE_CHECK]
@@ -47,10 +48,21 @@ Description:
                        fgroup:= decimal value
                  lsm:  are LSM specific
                  option:
-                       appraise_type:= [imasig] [imasig|modsig]
+                       appraise_type:= [imasig] | [imasig|modsig] | [sigv3]
+                           where 'imasig' is the original or the signature
+                               format v2.
+                           where 'modsig' is an appended signature,
+                           where 'sigv3' is the signature format v3. (Currently
+                               limited to fsverity digest based signatures
+                               stored in security.ima xattr. Requires
+                               specifying "digest_type=verity" first.)
+
                        appraise_flag:= [check_blacklist]
                        Currently, blacklist check is only for files signed with appended
                        signature.
+                       digest_type:= verity
+                           Require fs-verity's file digest instead of the
+                           regular IMA file hash.
                        keyrings:= list of keyrings
                        (eg, .builtin_trusted_keys|.ima). Only valid
                        when action is "measure" and func is KEY_CHECK.
@@ -149,3 +161,30 @@ Description:
                security.ima xattr of a file:
 
                        appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512
+
+               Example of a 'measure' rule requiring fs-verity's digests
+               with indication of type of digest in the measurement list.
+
+                       measure func=FILE_CHECK digest_type=verity \
+                               template=ima-ngv2
+
+               Example of 'measure' and 'appraise' rules requiring fs-verity
+               signatures (format version 3) stored in security.ima xattr.
+
+               The 'measure' rule specifies the 'ima-sigv3' template option,
+               which includes the indication of type of digest and the file
+               signature in the measurement list.
+
+                       measure func=BPRM_CHECK digest_type=verity \
+                               template=ima-sigv3
+
+
+               The 'appraise' rule specifies the type and signature format
+               version (sigv3) required.
+
+                       appraise func=BPRM_CHECK digest_type=verity \
+                               appraise_type=sigv3
+
+               All of these policy rules could, for example, be constrained
+               either based on a filesystem's UUID (fsuuid) or based on LSM
+               labels.
index a1a0eba0a2e5b0e523ad3868c68857ff14f10f06..2d67cdfbf9c9ac7cc78b56ab182391ad66189ac8 100644 (file)
 
        ima_template=   [IMA]
                        Select one of defined IMA measurements template formats.
-                       Formats: { "ima" | "ima-ng" | "ima-sig" }
+                       Formats: { "ima" | "ima-ng" | "ima-ngv2" | "ima-sig" |
+                                  "ima-sigv2" }
                        Default: "ima-ng"
 
        ima_template_fmt=
index 8cc536d08f51fec34cbea15f8283dda76cc72900..b7d42fd65e9d005fc68220026954729dc539a2f0 100644 (file)
@@ -70,12 +70,23 @@ must live on a read-write filesystem because they are independently
 updated and potentially user-installed, so dm-verity cannot be used.
 
 The base fs-verity feature is a hashing mechanism only; actually
-authenticating the files is up to userspace.  However, to meet some
-users' needs, fs-verity optionally supports a simple signature
-verification mechanism where users can configure the kernel to require
-that all fs-verity files be signed by a key loaded into a keyring; see
-`Built-in signature verification`_.  Support for fs-verity file hashes
-in IMA (Integrity Measurement Architecture) policies is also planned.
+authenticating the files may be done by:
+
+* Userspace-only
+
+* Builtin signature verification + userspace policy
+
+  fs-verity optionally supports a simple signature verification
+  mechanism where users can configure the kernel to require that
+  all fs-verity files be signed by a key loaded into a keyring;
+  see `Built-in signature verification`_.
+
+* Integrity Measurement Architecture (IMA)
+
+  IMA supports including fs-verity file digests and signatures in the
+  IMA measurement list and verifying fs-verity based file signatures
+  stored as security.ima xattrs, based on policy.
+
 
 User API
 ========
@@ -653,12 +664,12 @@ weren't already directly answered in other parts of this document.
     hashed and what to do with those hashes, such as log them,
     authenticate them, or add them to a measurement list.
 
-    IMA is planned to support the fs-verity hashing mechanism as an
-    alternative to doing full file hashes, for people who want the
-    performance and security benefits of the Merkle tree based hash.
-    But it doesn't make sense to force all uses of fs-verity to be
-    through IMA.  As a standalone filesystem feature, fs-verity
-    already meets many users' needs, and it's testable like other
+    IMA supports the fs-verity hashing mechanism as an alternative
+    to full file hashes, for those who want the performance and
+    security benefits of the Merkle tree based hash.  However, it
+    doesn't make sense to force all uses of fs-verity to be through
+    IMA.  fs-verity already meets many users' needs even as a
+    standalone filesystem feature, and it's testable like other
     filesystem features e.g. with xfstests.
 
 :Q: Isn't fs-verity useless because the attacker can just modify the
index 1a91d92950a79a4a7e3684d99dc8522f22a234e8..15b4add314fc9f1660940e7ecfe5d4f601777f00 100644 (file)
@@ -66,12 +66,13 @@ descriptors by adding their identifier to the format string
    calculated with the SHA1 or MD5 hash algorithm;
  - 'n': the name of the event (i.e. the file name), with size up to 255 bytes;
  - 'd-ng': the digest of the event, calculated with an arbitrary hash
-   algorithm (field format: [<hash algo>:]digest, where the digest
-   prefix is shown only if the hash algorithm is not SHA1 or MD5);
+   algorithm (field format: <hash algo>:digest);
+ - 'd-ngv2': same as d-ng, but prefixed with the "ima" or "verity" digest type
+   (field format: <digest type>:<hash algo>:digest);
  - 'd-modsig': the digest of the event without the appended modsig;
  - 'n-ng': the name of the event, without size limitations;
- - 'sig': the file signature, or the EVM portable signature if the file
-   signature is not found;
+ - 'sig': the file signature, based on either the file's/fsverity's digest[1],
+   or the EVM portable signature, if 'security.ima' contains a file hash.
  - 'modsig' the appended file signature;
  - 'buf': the buffer data that was used to generate the hash without size limitations;
  - 'evmsig': the EVM portable signature;
@@ -88,7 +89,9 @@ Below, there is the list of defined template descriptors:
 
  - "ima": its format is ``d|n``;
  - "ima-ng" (default): its format is ``d-ng|n-ng``;
+ - "ima-ngv2": its format is ``d-ngv2|n-ng``;
  - "ima-sig": its format is ``d-ng|n-ng|sig``;
+ - "ima-sigv2": its format is ``d-ngv2|n-ng|sig``;
  - "ima-buf": its format is ``d-ng|n-ng|buf``;
  - "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``;
  - "evm-sig": its format is ``d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode``;
index 24d1b54de8079f8df6ff26924468685449b033e7..54598cd8014570c91786d158936beb39a45f15ad 100644 (file)
@@ -3,6 +3,7 @@
 config FS_VERITY
        bool "FS Verity (read-only file-based authenticity protection)"
        select CRYPTO
+       select CRYPTO_HASH_INFO
        # SHA-256 is implied as it's intended to be the default hash algorithm.
        # To avoid bloat, other wanted algorithms must be selected explicitly.
        # Note that CRYPTO_SHA256 denotes the generic C implementation, but
index caf9d248ec2de7c542c067f67d33907827b430ee..629785c95007641865a2b32165f382774ac18753 100644 (file)
@@ -14,7 +14,6 @@
 
 #define pr_fmt(fmt) "fs-verity: " fmt
 
-#include <crypto/sha2.h>
 #include <linux/fsverity.h>
 #include <linux/mempool.h>
 
@@ -26,12 +25,6 @@ struct ahash_request;
  */
 #define FS_VERITY_MAX_LEVELS           8
 
-/*
- * Largest digest size among all hash algorithms supported by fs-verity.
- * Currently assumed to be <= size of fsverity_descriptor::root_hash.
- */
-#define FS_VERITY_MAX_DIGEST_SIZE      SHA512_DIGEST_SIZE
-
 /* A hash algorithm supported by fs-verity */
 struct fsverity_hash_alg {
        struct crypto_ahash *tfm; /* hash tfm, allocated on demand */
index f0d7b30c62db2bdf31db764a8d8966808993add2..e99c00350c28dc16ff1fa605848258945efe410b 100644 (file)
@@ -57,3 +57,46 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
        return 0;
 }
 EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
+
+/**
+ * fsverity_get_digest() - get a verity file's digest
+ * @inode: inode to get digest of
+ * @digest: (out) pointer to the digest
+ * @alg: (out) pointer to the hash algorithm enumeration
+ *
+ * Return the file hash algorithm and digest of an fsverity protected file.
+ * Assumption: before calling fsverity_get_digest(), the file must have been
+ * opened.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int fsverity_get_digest(struct inode *inode,
+                       u8 digest[FS_VERITY_MAX_DIGEST_SIZE],
+                       enum hash_algo *alg)
+{
+       const struct fsverity_info *vi;
+       const struct fsverity_hash_alg *hash_alg;
+       int i;
+
+       vi = fsverity_get_info(inode);
+       if (!vi)
+               return -ENODATA; /* not a verity file */
+
+       hash_alg = vi->tree_params.hash_alg;
+       memset(digest, 0, FS_VERITY_MAX_DIGEST_SIZE);
+
+       /* convert the verity hash algorithm name to a hash_algo_name enum */
+       i = match_string(hash_algo_name, HASH_ALGO__LAST, hash_alg->name);
+       if (i < 0)
+               return -EINVAL;
+       *alg = i;
+
+       if (WARN_ON_ONCE(hash_alg->digest_size != hash_digest_size[*alg]))
+               return -EINVAL;
+       memcpy(digest, vi->file_digest, hash_alg->digest_size);
+
+       pr_debug("file digest %s:%*phN\n", hash_algo_name[*alg],
+                hash_digest_size[*alg], digest);
+
+       return 0;
+}
index a7afc800bd8d27a927cce6c1eb08680d828fde38..7af030fa3c366cb18cd1ca794d65d68e95268c9a 100644 (file)
 #define _LINUX_FSVERITY_H
 
 #include <linux/fs.h>
+#include <crypto/hash_info.h>
+#include <crypto/sha2.h>
 #include <uapi/linux/fsverity.h>
 
+/*
+ * Largest digest size among all hash algorithms supported by fs-verity.
+ * Currently assumed to be <= size of fsverity_descriptor::root_hash.
+ */
+#define FS_VERITY_MAX_DIGEST_SIZE      SHA512_DIGEST_SIZE
+
 /* Verity operations for filesystems */
 struct fsverity_operations {
 
@@ -131,6 +139,9 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *arg);
 /* measure.c */
 
 int fsverity_ioctl_measure(struct file *filp, void __user *arg);
+int fsverity_get_digest(struct inode *inode,
+                       u8 digest[FS_VERITY_MAX_DIGEST_SIZE],
+                       enum hash_algo *alg);
 
 /* open.c */
 
@@ -170,6 +181,13 @@ static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg)
        return -EOPNOTSUPP;
 }
 
+static inline int fsverity_get_digest(struct inode *inode,
+                                     u8 digest[FS_VERITY_MAX_DIGEST_SIZE],
+                                     enum hash_algo *alg)
+{
+       return -EOPNOTSUPP;
+}
+
 /* open.c */
 
 static inline int fsverity_file_open(struct inode *inode, struct file *filp)
index c8c8a4a4e7a00c800f10ab939839ca00bf378691..8a82a6c7f48a44def6d1aa127b66ea8afa3d7e34 100644 (file)
@@ -75,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);
        }
index 0d44f41d16f87088c56a22b4713098bffbb1a059..f8b8c5004fc7c6fadba3bcdc4a6f49c44b3641cd 100644 (file)
@@ -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;
 
index 0450d79afdc8fc7dd85d77f35b82ea921957f6d7..a733aff02006381826ea7e9c1b588734bbffd4fa 100644 (file)
@@ -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);
index 7d87772f0ce681971db412ad606a0e84ab1d7a51..cc88f02c7562159c258e0de27dc5f4868144d8a1 100644 (file)
@@ -436,7 +436,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);
 }
 
index f3a9cc201c8c2a0cc276b8bb6f1c4be2559002b9..7249f16257c72c8f72fe17debde1a0b1f7aee845 100644 (file)
@@ -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
index c6805af4621187767d87ba1dd9cce908f8f8f38a..c1e76282b5ee5ad38ae61dc532c3091c75950738 100644 (file)
@@ -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
  *
@@ -242,16 +269,30 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
         */
        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;
index 17232bbfb9f96c541804e0c0e0a2ff45f73943f1..cdb84dccd24e01bf13152c0fe7168af5ba8088e8 100644 (file)
@@ -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];
@@ -225,6 +232,40 @@ int ima_read_xattr(struct dentry *dentry,
        return ret;
 }
 
+/*
+ * 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
  *
@@ -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,
@@ -296,6 +357,44 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
                } else {
                        *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;
@@ -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;
index 3d3f8c5c502ba9f401ea3973d849f06b1381edce..040b03ddc1c776665c75fde3081a7d9775d17041 100644 (file)
@@ -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 */
@@ -432,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;
index eea6e92500b8ea03853b119cfab9f5622c6672fb..73917413365b3cead675a1b2b89d05e8322892d7 100644 (file)
@@ -1023,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
@@ -1065,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"},
@@ -1172,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 */
@@ -1214,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;
@@ -1292,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;
 }
 
@@ -1707,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);
@@ -1797,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;
@@ -2149,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)
index db1ad6d7a57fbf7d5866ccf4d6cafb397c6f4f3f..c25079faa208859214c2a9d2aa24d18de870f108 100644 (file)
@@ -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",
@@ -38,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,
index 7155d17a3b75f52aaa42ad23c0323350db110846..c877f01a54713ea165219b04fb514f25a85d48c0 100644 (file)
@@ -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 algorithm 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)
@@ -359,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);
 }
 
 /*
@@ -368,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;
@@ -378,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);
 }
 
 /*
@@ -415,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,
@@ -475,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,
index c71f1de95753d92a37c87fc9eef744f164f683bb..9f7c335f304f4ff714c741424bc0a446c267024c 100644 (file)
@@ -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,
index 3510e413ea179e4e738649b32220d9c3122b5952..7167a6e99bdc02c9093b70eb90354008262aa486 100644 (file)
@@ -40,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)
@@ -78,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
 };
 
@@ -92,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;
@@ -121,7 +123,14 @@ struct ima_max_digest_data {
 } __packed;
 
 /*
- * signature format v2 - for using with asymmetric keys
+ * 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 */
@@ -132,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 */
index a2464f3e66ccf386dd1630a096185850409ccf27..8a1124e4d769602ec6a0d990a132e304b7ea7eac 100644 (file)
@@ -51,7 +51,7 @@ __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;
 }
 
 /*
@@ -66,7 +66,7 @@ __init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)
                else
                        return add_to_platform_keyring;
        }
-       return 0;
+       return NULL;
 }
 
 /*
@@ -81,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;
 }
index 284558f30411eb25004500bc96a49f8a51b3bb5a..212d894a8c0c0f238cc927a51d24ae87649a5483 100644 (file)
@@ -35,3 +35,11 @@ efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type);
 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
index 5f45c3c07dbd481ed087f878a1f286652e6fd235..093894a640dca6f4592268077428832df0449976 100644 (file)
@@ -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>
 #include "../integrity.h"
 #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.
@@ -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;