diff mbox series

[v7,2/5] ima: define a new template field named 'd-ngv2' and templates

Message ID 20220325223824.310119-3-zohar@linux.ibm.com (mailing list archive)
State Superseded
Headers show
Series ima: support fs-verity digests and signatures | expand

Commit Message

Mimi Zohar March 25, 2022, 10:38 p.m. UTC
In preparation to differentiate between unsigned regular IMA file
hashes and fs-verity's file digests in the IMA measurement list,
define a new template field named 'd-ngv2'.

Also define two new templates named 'ima-ngv2' and 'ima-sigv2', which
include the new 'd-ngv2' field.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
 .../admin-guide/kernel-parameters.txt         |  3 +-
 security/integrity/ima/ima_template.c         |  4 +
 security/integrity/ima/ima_template_lib.c     | 78 ++++++++++++++++---
 security/integrity/ima/ima_template_lib.h     |  4 +
 4 files changed, 79 insertions(+), 10 deletions(-)

Comments

Guozihua (Scott) March 28, 2022, 6:14 a.m. UTC | #1
On 2022/3/26 6:38, Mimi Zohar wrote:
> In preparation to differentiate between unsigned regular IMA file
> hashes and fs-verity's file digests in the IMA measurement list,
> define a new template field named 'd-ngv2'.
> 
> Also define two new templates named 'ima-ngv2' and 'ima-sigv2', which
> include the new 'd-ngv2' field.
> 
> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
>   .../admin-guide/kernel-parameters.txt         |  3 +-
>   security/integrity/ima/ima_template.c         |  4 +
>   security/integrity/ima/ima_template_lib.c     | 78 ++++++++++++++++---
>   security/integrity/ima/ima_template_lib.h     |  4 +
>   4 files changed, 79 insertions(+), 10 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index f5a27f067db9..47386ac10471 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -1876,7 +1876,8 @@
>   
>   	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=
> diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
> index db1ad6d7a57f..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",
> @@ -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,
> diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
> index 7155d17a3b75..af269b94b369 100644
> --- a/security/integrity/ima/ima_template_lib.c
> +++ b/security/integrity/ima/ima_template_lib.c
> @@ -24,11 +24,22 @@ 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__LAST
> +};
> +
> +#define DIGEST_TYPE_NAME_LEN_MAX 4	/* including NULL */
> +static const char * const digest_type_name[DIGEST_TYPE__LAST] = {
> +	[DIGEST_TYPE_IMA] = "ima"
> +};
> +
>   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 +83,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 +190,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 +285,39 @@ 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,
> +	 *  - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
> +	 *	[<digest type> + ':' + <hash algo>] + ':' + '\0' + digest,
> +	 *    where <hash type> is either "ima" or "verity",
>   	 *    where <hash algo> is provided if the hash algorithm is not
>   	 *    SHA1 or MD5
>   	 */
> -	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 };

Hi Mimi,

Shouldn't this contains an additional ":", Thus should +1 again?

>   	enum data_formats fmt = DATA_FMT_DIGEST;
>   	u32 offset = 0;
>   
> -	if (hash_algo < HASH_ALGO__LAST) {
> -		fmt = DATA_FMT_DIGEST_WITH_ALGO;
> -		offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s",
> +	if (digest_type < DIGEST_TYPE__LAST && hash_algo < HASH_ALGO__LAST) {
> +		fmt = DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO;
> +		offset += snprintf(buffer, DIGEST_TYPE_NAME_LEN_MAX +
> +				   CRYPTO_MAX_ALG_NAME + 1, "%*s:%s",
> +				   (int)strlen(digest_type_name[digest_type]),
> +				   digest_type_name[digest_type],
>   				   hash_algo_name[hash_algo]);
>   		buffer[offset] = ':';
>   		offset += 2;
> +	} 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;
>   	}
>   
>   	if (digest)
> @@ -359,7 +392,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);
>   }
>   
>   /*
> @@ -380,7 +414,32 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
>   	hash_algo = event_data->iint->ima_hash->algo;
>   out:
>   	return ima_eventdigest_init_common(cur_digest, cur_digestsize,
> -					   hash_algo, field_data);
> +					   DIGEST_TYPE__LAST, hash_algo,
> +					   field_data);
> +}
> +
> +/*
> + * This function writes the digest of an event (without size limit),
> + * prefixed with both the hash type and algorithm.
> + */
> +int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
> +			      struct ima_field_data *field_data)
> +{
> +	u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;
> +	u32 cur_digestsize = 0;
> +	u8 digest_type = DIGEST_TYPE_IMA;
> +
> +	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, 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,
> 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,
Mimi Zohar March 28, 2022, 1:50 p.m. UTC | #2
On Mon, 2022-03-28 at 14:14 +0800, Guozihua (Scott) wrote:
> > @@ -265,26 +285,39 @@ 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,
> > +      *  - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
> > +      *      [<digest type> + ':' + <hash algo>] + ':' + '\0' + digest,
> > +      *    where <hash type> is either "ima" or "verity",
> >        *    where <hash algo> is provided if the hash algorithm is not
> >        *    SHA1 or MD5
> >        */
> > -     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 };
> 
> Hi Mimi,
> 
> Shouldn't this contains an additional ":", Thus should +1 again?

The length of the CRYPTO_MAX_ALG_NAME includes room for the terminating
NULL.  In this case, the terminating NULL isn't needed.  It's replaced
with the ':'.

thanks,

Mimi

> 
> >       enum data_formats fmt = DATA_FMT_DIGEST;
> >       u32 offset = 0;
> >
Eric Biggers April 5, 2022, 7:11 p.m. UTC | #3
On Fri, Mar 25, 2022 at 06:38:21PM -0400, Mimi Zohar wrote:
>  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,
> +	 *  - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
> +	 *	[<digest type> + ':' + <hash algo>] + ':' + '\0' + digest,
> +	 *    where <hash type> is either "ima" or "verity",
>  	 *    where <hash algo> is provided if the hash algorithm is not
>  	 *    SHA1 or MD5

This says both "hash type" and "digest type".  It should be one or the other.

The square brackets are meant to indicate that the part within it is optional,
right?  Are they in the right place?  I don't see how this matches the code.
There is also no explanation for why or when <digest type> is optional with
DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO.

> +	if (digest_type < DIGEST_TYPE__LAST && hash_algo < HASH_ALGO__LAST) {
> +		fmt = DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO;
> +		offset += snprintf(buffer, DIGEST_TYPE_NAME_LEN_MAX +
> +				   CRYPTO_MAX_ALG_NAME + 1, "%*s:%s",
> +				   (int)strlen(digest_type_name[digest_type]),
> +				   digest_type_name[digest_type],
>  				   hash_algo_name[hash_algo]);
>  		buffer[offset] = ':';
>  		offset += 2;

There's no need to use %*s if the length argument is just going to be strlen().
It should just use %s.

Also, this is not correct use of snprintf(), given that the string is
unconditionally appended to at the offset which snprintf() returns.  So it is
not providing buffer overflow protection.  It might as well just be:

                offset += 1 + sprintf(buffer, "%s:%s:",
                                      digest_type_name[digest_type],
                                      hash_algo_name[hash_algo]);

and likewise for the other case:

                offset += 1 + sprintf(buffer, "%s:", hash_algo_name[hash_algo]);

> +/*
> + * This function writes the digest of an event (without size limit),
> + * prefixed with both the hash type and algorithm.
> + */
> +int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
> +			      struct ima_field_data *field_data)
> +{
> +	u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;

Why is this defaulting to SHA-1?

- Eric
Mimi Zohar April 28, 2022, 2:03 a.m. UTC | #4
On Tue, 2022-04-05 at 19:11 +0000, Eric Biggers wrote:
> On Fri, Mar 25, 2022 at 06:38:21PM -0400, Mimi Zohar wrote:
> >  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,
> > +	 *  - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
> > +	 *	[<digest type> + ':' + <hash algo>] + ':' + '\0' + digest,
> > +	 *    where <hash type> is either "ima" or "verity",
> >  	 *    where <hash algo> is provided if the hash algorithm is not
> >  	 *    SHA1 or MD5
> 
> This says both "hash type" and "digest type".  It should be one or the other.
> 
> The square brackets are meant to indicate that the part within it is optional,
> right?  Are they in the right place?  I don't see how this matches the code.
> There is also no explanation for why or when <digest type> is optional with
> DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO.

Agreed.

> 
> > +	if (digest_type < DIGEST_TYPE__LAST && hash_algo < HASH_ALGO__LAST) {
> > +		fmt = DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO;
> > +		offset += snprintf(buffer, DIGEST_TYPE_NAME_LEN_MAX +
> > +				   CRYPTO_MAX_ALG_NAME + 1, "%*s:%s",
> > +				   (int)strlen(digest_type_name[digest_type]),
> > +				   digest_type_name[digest_type],
> >  				   hash_algo_name[hash_algo]);
> >  		buffer[offset] = ':';
> >  		offset += 2;
> 
> There's no need to use %*s if the length argument is just going to be strlen().
> It should just use %s.

Using "%*s" prevents having a trailing NULL.

> 
> Also, this is not correct use of snprintf(), given that the string is
> unconditionally appended to at the offset which snprintf() returns.  So it is
> not providing buffer overflow protection.  It might as well just be:
> 
>                 offset += 1 + sprintf(buffer, "%s:%s:",
>                                       digest_type_name[digest_type],
>                                       hash_algo_name[hash_algo]);
> 
> and likewise for the other case:
> 
>                 offset += 1 + sprintf(buffer, "%s:", hash_algo_name[hash_algo]);
> 
> > +/*
> > + * This function writes the digest of an event (without size limit),
> > + * prefixed with both the hash type and algorithm.
> > + */
> > +int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
> > +			      struct ima_field_data *field_data)
> > +{
> > +	u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;
> 
> Why is this defaulting to SHA-1?

Violation records contain 0's in the file hash and the template data
hash fields. Changing the default hash algorithm would result in larger
violation digests without any real benefit other than cosmetic.  Will
make the change anyway in the next version.

thanks,

Mimi
diff mbox series

Patch

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index f5a27f067db9..47386ac10471 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1876,7 +1876,8 @@ 
 
 	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=
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index db1ad6d7a57f..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",
@@ -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,
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 7155d17a3b75..af269b94b369 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -24,11 +24,22 @@  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__LAST
+};
+
+#define DIGEST_TYPE_NAME_LEN_MAX 4	/* including NULL */
+static const char * const digest_type_name[DIGEST_TYPE__LAST] = {
+	[DIGEST_TYPE_IMA] = "ima"
+};
+
 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 +83,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 +190,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 +285,39 @@  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,
+	 *  - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
+	 *	[<digest type> + ':' + <hash algo>] + ':' + '\0' + digest,
+	 *    where <hash type> is either "ima" or "verity",
 	 *    where <hash algo> is provided if the hash algorithm is not
 	 *    SHA1 or MD5
 	 */
-	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) {
-		fmt = DATA_FMT_DIGEST_WITH_ALGO;
-		offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s",
+	if (digest_type < DIGEST_TYPE__LAST && hash_algo < HASH_ALGO__LAST) {
+		fmt = DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO;
+		offset += snprintf(buffer, DIGEST_TYPE_NAME_LEN_MAX +
+				   CRYPTO_MAX_ALG_NAME + 1, "%*s:%s",
+				   (int)strlen(digest_type_name[digest_type]),
+				   digest_type_name[digest_type],
 				   hash_algo_name[hash_algo]);
 		buffer[offset] = ':';
 		offset += 2;
+	} 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;
 	}
 
 	if (digest)
@@ -359,7 +392,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);
 }
 
 /*
@@ -380,7 +414,32 @@  int ima_eventdigest_ng_init(struct ima_event_data *event_data,
 	hash_algo = event_data->iint->ima_hash->algo;
 out:
 	return ima_eventdigest_init_common(cur_digest, cur_digestsize,
-					   hash_algo, field_data);
+					   DIGEST_TYPE__LAST, hash_algo,
+					   field_data);
+}
+
+/*
+ * This function writes the digest of an event (without size limit),
+ * prefixed with both the hash type and algorithm.
+ */
+int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
+			      struct ima_field_data *field_data)
+{
+	u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;
+	u32 cur_digestsize = 0;
+	u8 digest_type = DIGEST_TYPE_IMA;
+
+	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, 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,
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,