mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 0/2] ima: reject modsig when detached data cannot be supplied
@ 2026-10-24  9:39 Jérémy Jean
  2026-10-24  9:39 ` [PATCH v4 1/2] ima: discard " Jérémy Jean
  2026-10-24  9:39 ` [PATCH v4 2/2] ima: audit failure to supply detached data for modsig Jérémy Jean
  0 siblings, 2 replies; 4+ messages in thread
From: Jérémy Jean @ 2026-10-24  9:39 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin
  Cc: Eric Snowberg, linux-integrity, linux-security-module,
	linux-kernel, Jérémy Jean

ima_collect_modsig() supplies the file contents to the parsed PKCS#7
message as detached data. If the message already contains embedded data,
pkcs7_supply_detached_data() returns -EINVAL, but IMA ignores the error.
ima_modsig_verify() then verifies the embedded data instead of the file
being appraised. This could bypass the crypto check if one supplies data
with an already known valid signature.

Patch 1 fixes the verification path by discarding a modsig if detached
data cannot be supplied.

Patch 2 keeps the exact pkcs7_supply_detached_data() error for a
separate failed-modsig audit record. This part has been split out on
request from Mimi Zohar so that the verification fix can be backported
without this audit change.

---
Changes in v4:
- Keep the call to pkcs7_get_digest() unchanged.
- Adapt the calls to integrity_audit_msg() to ca1e0f3cce8a4.

Changes in v3:
- Split the verification fix from the audit change.

Changes in v2:
- If detached data cannot be supplied, discard the modsig but continue
  hashing and xattr appraisal.
- Keep signatures without exported digests; leave d-modsig empty.
- Audit failure to supply detached data separately, even with O_DIRECT.

v1: https://lore.kernel.org/all/20260822082434.489470-2-Jeremy.Jean@oss.cyber.gouv.fr/
v2: https://lore.kernel.org/all/20260924085516.3111521-2-Jeremy.Jean@oss.cyber.gouv.fr/
v3: https://lore.kernel.org/all/20261021210145.1851396-2-Jeremy.Jean@oss.cyber.gouv.fr/

Jérémy Jean (2):
  ima: discard modsig when detached data cannot be supplied
  ima: audit failure to supply detached data for modsig

 Documentation/security/IMA-templates.rst  |  3 ++-
 security/integrity/ima/ima.h              |  9 +++----
 security/integrity/ima/ima_api.c          | 29 ++++++++++++++++-------
 security/integrity/ima/ima_main.c         |  2 +-
 security/integrity/ima/ima_modsig.c       | 10 ++++++--
 security/integrity/ima/ima_template_lib.c |  4 ++--
 6 files changed, 39 insertions(+), 18 deletions(-)

-- 
2.47.3


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 1/2] ima: discard modsig when detached data cannot be supplied
  2026-10-24  9:39 [PATCH v4 0/2] ima: reject modsig when detached data cannot be supplied Jérémy Jean
@ 2026-10-24  9:39 ` Jérémy Jean
  2026-10-24 13:47   ` Jérémy Jean
  2026-10-24  9:39 ` [PATCH v4 2/2] ima: audit failure to supply detached data for modsig Jérémy Jean
  1 sibling, 1 reply; 4+ messages in thread
From: Jérémy Jean @ 2026-10-24  9:39 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin
  Cc: Eric Snowberg, linux-integrity, linux-security-module,
	linux-kernel, Jérémy Jean, stable

ima_collect_modsig() supplies the file contents to the parsed PKCS#7
message as detached data. If the message already contains embedded data,
pkcs7_supply_detached_data() returns -EINVAL, but IMA ignores the error.
ima_modsig_verify() then verifies the embedded data instead of the file
being appraised.

Make ima_collect_modsig() report whether the file data was supplied,
and discard the modsig if it was not.

Fixes: 15588227e086 ("ima: Collect modsig")
Cc: stable@vger.kernel.org
Signed-off-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
---
 Documentation/security/IMA-templates.rst  |  3 ++-
 security/integrity/ima/ima.h              |  7 ++++---
 security/integrity/ima/ima_api.c          | 10 +++++++---
 security/integrity/ima/ima_main.c         |  2 +-
 security/integrity/ima/ima_modsig.c       | 10 ++++++++--
 security/integrity/ima/ima_template_lib.c |  4 ++--
 6 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
index 15b4add314fc9..de7a3dfee33db 100644
--- a/Documentation/security/IMA-templates.rst
+++ b/Documentation/security/IMA-templates.rst
@@ -69,7 +69,8 @@ descriptors by adding their identifier to the format string
    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;
+ - 'd-modsig': the digest of the event without the appended modsig, empty
+   when no signature digest can be exported;
  - 'n-ng': the name of the event, without size limitations;
  - '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.
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index b502854f28ee7..6280d7ec23326 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -431,7 +431,7 @@ int ima_get_action(const struct mnt_idmap *idmap, struct inode *inode,
 int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
 int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 			    void *buf, loff_t size, enum hash_algo algo,
-			    struct modsig *modsig);
+			    struct modsig **modsig);
 void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
 			   const unsigned char *filename,
 			   struct evm_ima_xattr_data *xattr_value,
@@ -559,7 +559,7 @@ static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
 #ifdef CONFIG_IMA_APPRAISE_MODSIG
 int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
 		    struct modsig **modsig);
-void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
+bool ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
 int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo,
 			  const u8 **digest, u32 *digest_size);
 int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
@@ -572,9 +572,10 @@ static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
 	return -EOPNOTSUPP;
 }
 
-static inline void ima_collect_modsig(struct modsig *modsig, const void *buf,
+static inline bool ima_collect_modsig(struct modsig *modsig, const void *buf,
 				      loff_t size)
 {
+	return false;
 }
 
 static inline int ima_get_modsig_digest(const struct modsig *modsig,
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 45701f04ba62f..7859c9d1a27f5 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -235,6 +235,8 @@ static bool ima_get_verity_digest(struct ima_iint_cache *iint,
  *
  * Calculate the file hash, if it doesn't already exist,
  * storing the measurement and i_version in the iint.
+ * If modsig is provided, discard it when detached data cannot be supplied
+ * so ordinary measurement and xattr appraisal can proceed.
  *
  * Must be called with iint->mutex held.
  *
@@ -242,7 +244,7 @@ static bool ima_get_verity_digest(struct ima_iint_cache *iint,
  */
 int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 			    void *buf, loff_t size, enum hash_algo algo,
-			    struct modsig *modsig)
+			    struct modsig **modsig)
 {
 	const char *audit_cause = "failed";
 	struct inode *inode = file_inode(file);
@@ -262,8 +264,10 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 	 * the file digest without collecting the modsig in a previous
 	 * measurement rule.
 	 */
-	if (modsig)
-		ima_collect_modsig(modsig, buf, size);
+	if (modsig && *modsig && !ima_collect_modsig(*modsig, buf, size)) {
+		ima_free_modsig(*modsig);
+		*modsig = NULL;
+	}
 
 	if (iint->flags & IMA_COLLECTED)
 		goto out;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index fed80da3ee096..336e17ec4f7a3 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -422,7 +422,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);
+	rc = ima_collect_measurement(iint, file, buf, size, hash_algo, &modsig);
 	if (rc != 0 && rc != -EBADF && rc != -EINVAL)
 		goto out_locked;
 
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index 632c746fd81e4..e640a9a53508e 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -96,8 +96,12 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
  * Since the modsig is part of the file contents, the hash used in its signature
  * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code
  * calculates a separate one for signature verification.
+ *
+ * Digest export is best-effort; not all signatures expose a single digest.
+ *
+ * Return: true if the file data was supplied, false otherwise.
  */
-void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
+bool ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
 {
 	int rc;
 
@@ -109,11 +113,13 @@ void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
 		sizeof(struct module_signature);
 	rc = pkcs7_supply_detached_data(modsig->pkcs7_msg, buf, size);
 	if (rc)
-		return;
+		return false;
 
 	/* Ask the PKCS7 code to calculate the file hash. */
 	rc = pkcs7_get_digest(modsig->pkcs7_msg, &modsig->digest,
 			      &modsig->digest_size, &modsig->hash_algo);
+
+	return true;
 }
 
 int ima_modsig_verify(struct key *keyring, const struct modsig *modsig)
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 9bd80ca1159a2..585cd59f00a2e 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -478,8 +478,8 @@ int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
 		if (rc)
 			return rc;
 		else if (hash_algo == HASH_ALGO__LAST || cur_digestsize == 0)
-			/* There was some error collecting the digest. */
-			return -EINVAL;
+			/* Keep the ordinary measurement without an exported digest. */
+			return 0;
 	}
 
 	return ima_eventdigest_init_common(cur_digest, cur_digestsize,
-- 
2.47.3


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 2/2] ima: audit failure to supply detached data for modsig
  2026-10-24  9:39 [PATCH v4 0/2] ima: reject modsig when detached data cannot be supplied Jérémy Jean
  2026-10-24  9:39 ` [PATCH v4 1/2] ima: discard " Jérémy Jean
@ 2026-10-24  9:39 ` Jérémy Jean
  1 sibling, 0 replies; 4+ messages in thread
From: Jérémy Jean @ 2026-10-24  9:39 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin
  Cc: Eric Snowberg, linux-integrity, linux-security-module,
	linux-kernel, Jérémy Jean

ima_collect_measurement() discards a modsig when detached data cannot be
supplied, but the reason is lost.

Return the pkcs7_supply_detached_data() error from ima_collect_modsig()
and emit a separate failed-modsig audit record. Keep ordinary collection
errors on their existing audit path so both causes are reported when they
happen together.

Signed-off-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
---
 security/integrity/ima/ima.h        |  8 ++++----
 security/integrity/ima/ima_api.c    | 25 +++++++++++++++++--------
 security/integrity/ima/ima_modsig.c |  8 ++++----
 3 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 6280d7ec23326..a3670a479d095 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -559,7 +559,7 @@ static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
 #ifdef CONFIG_IMA_APPRAISE_MODSIG
 int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
 		    struct modsig **modsig);
-bool ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
+int ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
 int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo,
 			  const u8 **digest, u32 *digest_size);
 int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
@@ -572,10 +572,10 @@ static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
 	return -EOPNOTSUPP;
 }
 
-static inline bool ima_collect_modsig(struct modsig *modsig, const void *buf,
-				      loff_t size)
+static inline int ima_collect_modsig(struct modsig *modsig, const void *buf,
+				     loff_t size)
 {
-	return false;
+	return -EOPNOTSUPP;
 }
 
 static inline int ima_get_modsig_digest(const struct modsig *modsig,
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 7859c9d1a27f5..5f6178948eb3e 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -254,7 +254,7 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 						struct ima_digest_data, hdr);
 	struct name_snapshot filename;
 	struct kstat stat;
-	int result = 0;
+	int result = 0, modsig_result = 0;
 	int length;
 	void *tmpbuf;
 	u64 i_version = 0;
@@ -264,9 +264,12 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 	 * the file digest without collecting the modsig in a previous
 	 * measurement rule.
 	 */
-	if (modsig && *modsig && !ima_collect_modsig(*modsig, buf, size)) {
-		ima_free_modsig(*modsig);
-		*modsig = NULL;
+	if (modsig && *modsig) {
+		modsig_result = ima_collect_modsig(*modsig, buf, size);
+		if (modsig_result) {
+			ima_free_modsig(*modsig);
+			*modsig = NULL;
+		}
 	}
 
 	if (iint->flags & IMA_COLLECTED)
@@ -326,15 +329,21 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 	if (!result)
 		iint->flags |= IMA_COLLECTED;
 out:
-	if (result) {
+	if (result || modsig_result) {
 		if (file->f_flags & O_DIRECT)
 			audit_cause = "failed(directio)";
 
 		take_dentry_name_snapshot(&filename, file->f_path.dentry);
 
-		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
-				    filename.name.name, "collect_data",
-				    audit_cause, result, 0, result);
+		if (modsig_result)
+			integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
+					    filename.name.name, "collect_data",
+					    "failed-modsig", modsig_result, 0,
+					    modsig_result);
+		if (result)
+			integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
+					    filename.name.name, "collect_data",
+					    audit_cause, result, 0, result);
 
 		release_dentry_name_snapshot(&filename);
 	}
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index e640a9a53508e..c5bd4770298a1 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -99,9 +99,9 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
  *
  * Digest export is best-effort; not all signatures expose a single digest.
  *
- * Return: true if the file data was supplied, false otherwise.
+ * Return: 0 if the file data was supplied, error code otherwise.
  */
-bool ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
+int ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
 {
 	int rc;
 
@@ -113,13 +113,13 @@ bool ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
 		sizeof(struct module_signature);
 	rc = pkcs7_supply_detached_data(modsig->pkcs7_msg, buf, size);
 	if (rc)
-		return false;
+		return rc;
 
 	/* Ask the PKCS7 code to calculate the file hash. */
 	rc = pkcs7_get_digest(modsig->pkcs7_msg, &modsig->digest,
 			      &modsig->digest_size, &modsig->hash_algo);
 
-	return true;
+	return 0;
 }
 
 int ima_modsig_verify(struct key *keyring, const struct modsig *modsig)
-- 
2.47.3


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v4 1/2] ima: discard modsig when detached data cannot be supplied
  2026-10-24  9:39 ` [PATCH v4 1/2] ima: discard " Jérémy Jean
@ 2026-10-24 13:47   ` Jérémy Jean
  0 siblings, 0 replies; 4+ messages in thread
From: Jérémy Jean @ 2026-10-24 13:47 UTC (permalink / raw)
  To: Jérémy Jean
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	linux-integrity, linux-security-module, linux-kernel, stable

On 2026-10-24 11:39, Jérémy Jean wrote:
> ima_collect_modsig() supplies the file contents to the parsed PKCS#7
> message as detached data. If the message already contains embedded 
> data,
> pkcs7_supply_detached_data() returns -EINVAL, but IMA ignores the 
> error.
> ima_modsig_verify() then verifies the embedded data instead of the file
> being appraised.
> 
> Make ima_collect_modsig() report whether the file data was supplied,
> and discard the modsig if it was not.
> 
> Fixes: 15588227e086 ("ima: Collect modsig")

The public instance of Sashiko raised some relevant feedbacks for 1/2
that my local Sashiko run did not see.
I will address them in a new version.

Regards,
Jérémy

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-24 13:47 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-24  9:39 [PATCH v4 0/2] ima: reject modsig when detached data cannot be supplied Jérémy Jean
2026-10-24  9:39 ` [PATCH v4 1/2] ima: discard " Jérémy Jean
2026-10-24 13:47   ` Jérémy Jean
2026-10-24  9:39 ` [PATCH v4 2/2] ima: audit failure to supply detached data for modsig Jérémy Jean

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®