@@ -51,6 +51,7 @@ Description:
appraise_flag:= [check_blacklist]
Currently, blacklist check is only for files signed with appended
signature.
+ digest_type:= [hash|verity]
keyrings:= list of keyrings
(eg, .builtin_trusted_keys|.ima). Only valid
when action is "measure" and func is KEY_CHECK.
@@ -149,3 +150,9 @@ Description:
security.ima xattr of a file:
appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512
+
+ Example of measure rule allowing fs-verity's digests on a
+ particular filesystem with indication of type of digest.
+
+ measure func=FILE_CHECK digest_type=hash|verity \
+ fsuuid=... template=ima-ngv2
@@ -69,6 +69,7 @@ descriptors by adding their identifier to the format string
algorithm (field format: [<hash algo>:]digest, where the digest
prefix is shown only if the hash algorithm is not SHA1 or MD5);
- '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;
@@ -106,3 +107,8 @@ currently the following methods are supported:
the ``ima_template=`` parameter;
- register a new template descriptor with custom format through the kernel
command line parameter ``ima_template_fmt=``.
+
+
+References
+==========
+[1] Documentation/filesystems/fsverity.rst
@@ -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,23 @@ 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_digest_data *hash)
+{
+ u8 verity_digest[FS_VERITY_MAX_DIGEST_SIZE];
+ enum hash_algo verity_alg;
+ int rc;
+
+ rc = fsverity_get_digest(iint->inode, verity_digest, &verity_alg);
+ if (rc)
+ return -EINVAL;
+ if (hash->algo != verity_alg)
+ return -EINVAL;
+ hash->length = hash_digest_size[verity_alg];
+ memcpy(hash->digest, verity_digest, hash->length);
+ return 0;
+}
+
/*
* ima_collect_measurement - collect file measurement
*
@@ -248,10 +266,17 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
/* Initialize hash digest to 0's in case of failure */
memset(&hash.digest, 0, sizeof(hash.digest));
- if (buf)
+ if (buf) {
result = ima_calc_buffer_hash(buf, size, &hash.hdr);
- else
+ } else if (iint->flags & IMA_VERITY_ALLOWED) {
+ result = ima_get_verity_digest(iint, &hash.hdr);
+ if (result < 0)
+ result = ima_calc_file_hash(file, &hash.hdr);
+ else
+ iint->flags |= IMA_VERITY_DIGEST;
+ } else {
result = ima_calc_file_hash(file, &hash.hdr);
+ }
if (result && result != -EBADF && result != -EINVAL)
goto out;
@@ -1024,6 +1024,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 +1067,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 +1175,21 @@ static void check_template_modsig(const struct ima_template_desc *template)
#undef MSG
}
+/*
+ * Make sure the policy rule and template format are in sync.
+ */
+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 +1232,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_ALLOWED))
return false;
break;
@@ -1708,6 +1726,13 @@ 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 ((strcmp(args[0].from, "hash|verity")) == 0)
+ entry->flags |= IMA_VERITY_ALLOWED;
+ else
+ result = -EINVAL;
+ break;
case Opt_appraise_type:
ima_log_string(ab, "appraise_type", args[0].from);
if ((strcmp(args[0].from, "imasig")) == 0)
@@ -1798,6 +1823,15 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
check_template_modsig(template_desc);
}
+ /* d-type template field recommended for unsigned fs-verity digests */
+ if (!result && entry->action == MEASURE &&
+ entry->flags & IMA_VERITY_ALLOWED) {
+ template_desc = entry->template ? entry->template :
+ ima_template_desc_current();
+ check_template_field(template_desc, "d-type",
+ "verity rules should include d-type");
+ }
+
audit_log_format(ab, "res=%d", !result);
audit_log_end(ab);
return result;
@@ -2147,6 +2181,8 @@ int ima_policy_show(struct seq_file *m, void *v)
else
seq_puts(m, "appraise_type=imasig ");
}
+ if (entry->flags & IMA_VERITY_ALLOWED)
+ seq_puts(m, "digest_type=hash|verity ");
if (entry->flags & IMA_CHECK_BLACKLIST)
seq_puts(m, "appraise_flag=check_blacklist ");
if (entry->flags & IMA_PERMIT_DIRECTIO)
@@ -392,7 +392,14 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
int ima_eventdigest_type_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
- static const char * const digest_type[] = {"hash"};
+ static const char * const digest_type[] = {"hash", "verity"};
+
+ if (event_data->iint->flags & IMA_VERITY_DIGEST) {
+ return ima_write_template_field_data(digest_type[1],
+ strlen(digest_type[1]),
+ DATA_FMT_STRING,
+ field_data);
+ }
return ima_write_template_field_data(digest_type[0],
strlen(digest_type[0]),
@@ -31,7 +31,7 @@
#define IMA_HASHED 0x00000200
/* iint policy rule cache flags */
-#define IMA_NONACTION_FLAGS 0xff000000
+#define IMA_NONACTION_FLAGS 0xff800000
#define IMA_DIGSIG_REQUIRED 0x01000000
#define IMA_PERMIT_DIRECTIO 0x02000000
#define IMA_NEW_FILE 0x04000000
@@ -39,6 +39,8 @@
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
+#define IMA_VERITY_ALLOWED 0x80000000
+#define IMA_VERITY_DIGEST 0x00800000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_HASH | IMA_APPRAISE_SUBMASK)
Allow fsverity's file digests to be included in the IMA measurement list based on policy. Define a new measurement policy rule option named 'digest_type=' to allow fsverity file digests to be included in the measurement list in the d-ng field. Including the 'd-type' template field is recommended for unsigned fs-verity digests to distinguish between d-ng digest types. The following policy rule, for example, specifies the new 'ima-ngv2' template. measure func=FILE_CHECK digest_type=hash|verity template=ima-ngv2 Signed-off-by: Mimi Zohar <zohar@linux.ibm.com> --- Comment: - To avoid unnecessary patch churn for the proposed IMA namespacing patch set, don't increase the iint->flags size, but include an additional bit in the IMA_NONACTION_FLAGS. Documentation/ABI/testing/ima_policy | 7 +++++ Documentation/security/IMA-templates.rst | 6 ++++ security/integrity/ima/ima_api.c | 29 +++++++++++++++-- security/integrity/ima/ima_policy.c | 38 ++++++++++++++++++++++- security/integrity/ima/ima_template_lib.c | 9 +++++- security/integrity/integrity.h | 4 ++- 6 files changed, 88 insertions(+), 5 deletions(-)