[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <40351a94-c46c-772a-6dff-3e4dd921b68d@linux.ibm.com>
Date: Fri, 29 Apr 2022 13:30:46 -0400
From: Stefan Berger <stefanb@...ux.ibm.com>
To: Mimi Zohar <zohar@...ux.ibm.com>, linux-integrity@...r.kernel.org
Cc: Eric Biggers <ebiggers@...nel.org>, linux-fscrypt@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH v8 5/7] ima: permit fsverity's file digests in the IMA
measurement list
On 4/29/22 07:25, Mimi Zohar wrote:
> Permit fsverity's file digest (a hash of struct fsverity_descriptor) to
> be included in the IMA measurement list, based on the new measurement
> policy rule 'digest_type=verity' option.
>
> To differentiate between a regular IMA file hash from an fsverity's
> file digest, use the new d-ngv2 format field included in the ima-ngv2
> template.
>
> The following policy rule requires fsverity file digests and specifies
> the new 'ima-ngv2' template, which contains the new 'd-ngv2' field. The
> policy rule may be constrained, for example based on a fsuuid or LSM
> label.
>
> measure func=FILE_CHECK digest_type=verity template=ima-ngv2
>
> Signed-off-by: Mimi Zohar <zohar@...ux.ibm.com>
> ---
> Documentation/ABI/testing/ima_policy | 14 ++++++-
> Documentation/security/IMA-templates.rst | 2 +-
> security/integrity/ima/ima_api.c | 49 +++++++++++++++++++++--
> security/integrity/ima/ima_main.c | 2 +-
> security/integrity/ima/ima_policy.c | 38 +++++++++++++++++-
> security/integrity/ima/ima_template_lib.c | 10 +++--
> security/integrity/integrity.h | 1 +
> 7 files changed, 105 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
> index 839fab811b18..0a8caed393e3 100644
> --- a/Documentation/ABI/testing/ima_policy
> +++ b/Documentation/ABI/testing/ima_policy
> @@ -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]
> @@ -51,6 +52,9 @@ Description:
> 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 +153,9 @@ 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
> diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
> index eafc4e34f890..09b5fac38195 100644
> --- a/Documentation/security/IMA-templates.rst
> +++ b/Documentation/security/IMA-templates.rst
> @@ -67,7 +67,7 @@ descriptors by adding their identifier to the format string
> - '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);
> - - 'd-ngv2': same as d-ng, but prefixed with the "ima" digest type
> + - '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;
> diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
> index c6805af46211..d64ec031b1b4 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,34 @@ 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) {
> + memset(hash->digest, 0, sizeof(hash->digest));
> + 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 +271,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));
You seem to be doing this above as well in ima_get_verity_digest(). I
guess the above one could go.
>
> - 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_main.c b/security/integrity/ima/ima_main.c
> index 1aebf63ad7a6..040b03ddc1c7 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -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 */
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index eea6e92500b8..390a8faa77f9 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -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;
> @@ -1707,6 +1725,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, "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)
> @@ -1797,6 +1822,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;
> @@ -2154,6 +2188,8 @@ int ima_policy_show(struct seq_file *m, void *v)
> 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)
> diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
> index ff82e699149c..2ebcf6cd92b8 100644
> --- a/security/integrity/ima/ima_template_lib.c
> +++ b/security/integrity/ima/ima_template_lib.c
> @@ -32,12 +32,14 @@ enum data_formats {
>
> enum digest_type {
> DIGEST_TYPE_IMA,
> + DIGEST_TYPE_VERITY,
> DIGEST_TYPE__LAST
> };
>
> -#define DIGEST_TYPE_NAME_LEN_MAX 4 /* including NULL */
> +#define DIGEST_TYPE_NAME_LEN_MAX 7 /* including NULL */
> static const char * const digest_type_name[DIGEST_TYPE__LAST] = {
> - [DIGEST_TYPE_IMA] = "ima"
> + [DIGEST_TYPE_IMA] = "ima",
> + [DIGEST_TYPE_VERITY] = "verity"
> };
>
> static int ima_write_template_field_data(const void *data, const u32 datalen,
> @@ -297,7 +299,7 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
> *
> * where 'DATA_FMT_DIGEST' is the original digest format ('d')
> * with a hash size limitation of 20 bytes,
> - * where <digest type> is "ima",
> + * where <digest type> is either "ima" or "verity",
> * where <hash algo> is the hash_algo_name[] string.
> */
> u8 buffer[DIGEST_TYPE_NAME_LEN_MAX + CRYPTO_MAX_ALG_NAME + 2 +
> @@ -435,6 +437,8 @@ int ima_eventdigest_ngv2_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,
> digest_type, hash_algo,
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index 3510e413ea17..04e2b99cd912 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -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)
Acked-by: Stefan Berger <stefanb@...ux.ibm.com>
Powered by blists - more mailing lists