[PATCH v2 5/6] ima: support fs-verity file digest based signatures

From: Mimi Zohar
Date: Sun Jan 09 2022 - 13:55:47 EST


Instead of calculating a file hash and verifying the signature stored
in the security.ima xattr against the calculated file hash, verify the
signature based on a hash of fs-verity's file digest and the digest's
metadata.

To differentiate between a regular file hash and an fs-verity file digest
based signature stored as security.ima xattr, define a new signature type
named IMA_VERITY_DIGSIG.

The hash format of fs-verity's file digest and the digest's metadata to
be signed is defined as a structure named "ima_tbs_hash".

Update the 'ima-sig' template field to display the new fs-verity signature
type as well.

For example:
appraise func=BPRM_CHECK digest_type=hash|verity

Signed-off-by: Mimi Zohar <zohar@xxxxxxxxxxxxx>
---
Documentation/ABI/testing/ima_policy | 10 +++
Documentation/security/IMA-templates.rst | 4 +-
include/uapi/linux/ima.h | 26 ++++++++
security/integrity/ima/ima_appraise.c | 81 +++++++++++++++++++++++
security/integrity/ima/ima_template_lib.c | 3 +-
security/integrity/integrity.h | 1 +
6 files changed, 122 insertions(+), 3 deletions(-)
create mode 100644 include/uapi/linux/ima.h

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index 444bb7ccbe03..fadf90dde289 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -151,6 +151,16 @@ Description:

appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512

+ Example of measure and appraise rules allowing fs-verity
+ signed digests on a particular filesystem identified by
+ it's fsuuid:
+
+ measure func=BPRM_CHECK digest_type=hash|verity \
+ fsuuid=... template=ima-sig
+ appraise func=BPRM_CHECK digest_type=hash|verity \
+ fsuuid=...
+
+
Example of measure rule allowing fs-verity's digests on a
particular filesystem with indication of type of digest.

diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
index 5e31513e8ec4..390936810ebc 100644
--- a/Documentation/security/IMA-templates.rst
+++ b/Documentation/security/IMA-templates.rst
@@ -71,8 +71,8 @@ descriptors by adding their identifier to the format string
- 'd-modsig': the digest of the event without the appended modsig;
- 'd-type': the type of file digest (e.g. hash, verity[1]);
- '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 the file signature is not found;
- 'modsig' the appended file signature;
- 'buf': the buffer data that was used to generate the hash without size limitations;
- 'evmsig': the EVM portable signature;
diff --git a/include/uapi/linux/ima.h b/include/uapi/linux/ima.h
new file mode 100644
index 000000000000..6a2a68fc0fad
--- /dev/null
+++ b/include/uapi/linux/ima.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * IMA user API
+ *
+ */
+#ifndef _UAPI_LINUX_IMA_H
+#define _UAPI_LINUX_IMA_H
+
+#include <linux/types.h>
+
+/*
+ * The hash format of fs-verity's file digest and other file metadata
+ * to be signed. The resulting signature is stored as a security.ima
+ * xattr.
+ *
+ * "type" is defined as IMA_VERITY_DIGSIG
+ * "algo" is the hash_algo enum of fs-verity's file digest
+ * (e.g. HASH_ALGO_SHA256, HASH_ALGO_SHA512).
+ */
+struct ima_tbs_hash {
+ __u8 type; /* xattr type [enum evm_ima_xattr_type] */
+ __u8 algo; /* Digest algorithm [enum hash_algo] */
+ __u8 digest[]; /* fs-verity digest */
+};
+
+#endif /* _UAPI_LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index dbba51583e7c..4e092c189ed0 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -13,7 +13,10 @@
#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 <uapi/linux/ima.h>

#include "ima.h"

@@ -183,6 +186,8 @@ 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:
+ fallthrough;
case EVM_IMA_XATTR_DIGSIG:
sig = (typeof(sig))xattr_value;
if (sig->version != 2 || xattr_len <= sizeof(*sig)
@@ -225,6 +230,47 @@ int ima_read_xattr(struct dentry *dentry,
return ret;
}

+/*
+ * calc_tbs_hash - calculate hash of a digest and digest metadata
+ * @type: signature type [IMA_VERITY_DIGSIG]
+ * @algo: hash algorithm [enum hash_algo]
+ * @digest: pointer to the digest to be hashed
+ * @hash: (out) pointer to the hash
+ *
+ * The IMA signature is a signature over the hash of fs-verity's file digest
+ * with other digest metadata, not just fs-verity's file digest. Calculate
+ * the to be signed hash.
+ *
+ * Return 0 on success, error code otherwise.
+ */
+static int calc_tbs_hash(enum evm_ima_xattr_type xattr_type,
+ enum hash_algo algo, const u8 *digest,
+ struct ima_digest_data *hash)
+{
+ struct ima_tbs_hash *tbs_h;
+ int rc = 0;
+
+ if (xattr_type != IMA_VERITY_DIGSIG)
+ return -EINVAL;
+
+ tbs_h = kzalloc(sizeof(*tbs_h) + hash_digest_size[algo], GFP_KERNEL);
+ if (!tbs_h)
+ return -ENOMEM;
+
+ tbs_h->type = xattr_type;
+ tbs_h->algo = algo;
+ memcpy(tbs_h->digest, digest, hash_digest_size[algo]);
+
+ hash->algo = algo;
+ hash->length = hash_digest_size[algo];
+
+ rc = ima_calc_buffer_hash(tbs_h,
+ sizeof(*tbs_h) + hash_digest_size[algo],
+ hash);
+ kfree(tbs_h);
+ return rc;
+}
+
/*
* xattr_verify - verify xattr digest or signature
*
@@ -236,7 +282,9 @@ 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_digest_data *hash = NULL;
int rc = -EINVAL, hash_start = 0;
+ u8 algo; /* Digest algorithm [enum hash_algo] */

switch (xattr_value->type) {
case IMA_XATTR_DIGEST_NG:
@@ -271,6 +319,38 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
break;
}
*status = INTEGRITY_PASS;
+ break;
+ case IMA_VERITY_DIGSIG:
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+
+ algo = iint->ima_hash->algo;
+ hash = kzalloc(sizeof(*hash) + hash_digest_size[algo],
+ GFP_KERNEL);
+ if (!hash) {
+ *cause = "verity-hashing-error";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ rc = calc_tbs_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
+ iint->ima_hash->digest, hash);
+ if (rc) {
+ *cause = "verity-hashing-error";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
+ (const char *)xattr_value,
+ xattr_len, hash->digest,
+ hash->length);
+ if (rc) {
+ *cause = "invalid-verity-signature";
+ *status = INTEGRITY_FAIL;
+ } else {
+ *status = INTEGRITY_PASS;
+ }
+
break;
case EVM_IMA_XATTR_DIGSIG:
set_bit(IMA_DIGSIG, &iint->atomic_flags);
@@ -303,6 +383,7 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
break;
}

+ kfree(hash);
return rc;
}

diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 1c0cea2b805f..31a14943e459 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -498,7 +498,8 @@ 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 | 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/integrity.h b/security/integrity/integrity.h
index e7ac1086d1d9..51124708c072 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -79,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
};

--
2.27.0