This is an automated email from the ASF dual-hosted git repository.

Arsnael pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new ffbb4a75b1 [FIX] IsSMIMESigned needs to account for protocol
ffbb4a75b1 is described below

commit ffbb4a75b14165e35088f5da026ac69c16e39093
Author: Benoit TELLIER <[email protected]>
AuthorDate: Sun Aug 23 22:27:04 2026 +0700

    [FIX] IsSMIMESigned needs to account for protocol
    
    Processing PGP mails triggers:
    
    org.bouncycastle.cms.CMSException: IOException reading content.
    ...Caused by: java.io.IOException: unknown tag 13 encountered
    
    Explanation:
    
    -----BEGIN PGP SIGNATURE----- starts with '-' = 0x2D.
    As an ASN.1 identifier octet that is universal/primitive, tag number 0x2D & 
0x1F = 13 — an unassigned universal tag.
---
 .../transport/mailets/SMIMECheckSignature.java     |   7 +-
 .../james/transport/matcher/IsSMIMESigned.java     |  40 ++++++-
 .../james/transport/matcher/IsSMIMESignedTest.java | 122 +++++++++++++++------
 3 files changed, 129 insertions(+), 40 deletions(-)

diff --git 
a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
 
b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
index 7dfa7c072d..791973ebfa 100644
--- 
a/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
+++ 
b/mailet/crypto/src/main/java/org/apache/james/transport/mailets/SMIMECheckSignature.java
@@ -176,13 +176,14 @@ public class SMIMECheckSignature extends GenericMailet {
             // These errors are logged but they don't cause the message to 
change its state. The message
             // is considered as not signed and the process will go on.
         } catch (CMSException | SMIMEException e) {
-            LOGGER.error("Error during the analysis of the signed message", e);
+            // Signatures are remote sender controlled thus we do not consider 
a malformed one to be a server error.
+            LOGGER.warn("Error during the analysis of the signed message", e);
             signers = null;
         } catch (IOException e) {
-            LOGGER.error("IO error during the analysis of the signed message", 
e);
+            LOGGER.warn("IO error during the analysis of the signed message", 
e);
             signers = null;
         } catch (Exception e) {
-            LOGGER.error("Generic error occured during the analysis of the 
message", e);
+            LOGGER.warn("Generic error occured during the analysis of the 
message", e);
             signers = null;
         }
         
diff --git 
a/mailet/crypto/src/main/java/org/apache/james/transport/matcher/IsSMIMESigned.java
 
b/mailet/crypto/src/main/java/org/apache/james/transport/matcher/IsSMIMESigned.java
index 343d87f909..db5c4babb1 100644
--- 
a/mailet/crypto/src/main/java/org/apache/james/transport/matcher/IsSMIMESigned.java
+++ 
b/mailet/crypto/src/main/java/org/apache/james/transport/matcher/IsSMIMESigned.java
@@ -21,19 +21,32 @@
 package org.apache.james.transport.matcher;
 
 import java.util.Collection;
+import java.util.Locale;
+import java.util.Optional;
 
 import jakarta.mail.MessagingException;
+import jakarta.mail.internet.ContentType;
 import jakarta.mail.internet.MimeMessage;
+import jakarta.mail.internet.ParseException;
 
 import org.apache.james.core.MailAddress;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
 
 /**
  * checks if a mail is smime signed. 
 
  */
 public class IsSMIMESigned extends GenericMatcher {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(IsSMIMESigned.class);
+
+    private static final ImmutableSet<String> PKCS7_SIGNATURE_PROTOCOLS = 
ImmutableSet.of(
+        "application/pkcs7-signature",
+        "application/x-pkcs7-signature");
 
     @Override
     public Collection<MailAddress> match(Mail mail) throws MessagingException {
@@ -46,7 +59,7 @@ public class IsSMIMESigned extends GenericMatcher {
             return null;
         }
 
-        if (message.isMimeType("multipart/signed") 
+        if (isPkcs7SignedMultipart(message)
                 || message.isMimeType("application/pkcs7-signature")
                 || message.isMimeType("application/x-pkcs7-signature")
                 || ((message.isMimeType("application/pkcs7-mime") || 
message.isMimeType("application/x-pkcs7-mime")) 
@@ -56,4 +69,29 @@ public class IsSMIMESigned extends GenericMatcher {
             return null;
         }
     }
+
+    /**
+     * A <code>multipart/signed</code> body is not necessarily S/MIME: 
PGP/MIME (RFC 3156) relies on the very
+     * same content type and only the <code>protocol</code> parameter tells 
the two apart (RFC 8551 section 3.4.3).
+     * Handing a PGP signature over to the S/MIME parser makes it choke on 
ASN.1 it can not read, hence we require
+     * the protocol to explicitly designate a PKCS#7 signature.
+     */
+    private boolean isPkcs7SignedMultipart(MimeMessage message) throws 
MessagingException {
+        if (!message.isMimeType("multipart/signed")) {
+            return false;
+        }
+        return protocolParameter(message)
+            .map(protocol -> protocol.toLowerCase(Locale.US).trim())
+            .filter(PKCS7_SIGNATURE_PROTOCOLS::contains)
+            .isPresent();
+    }
+
+    private Optional<String> protocolParameter(MimeMessage message) throws 
MessagingException {
+        try {
+            return Optional.ofNullable(new 
ContentType(message.getContentType()).getParameter("protocol"));
+        } catch (ParseException e) {
+            LOGGER.info("Could not parse Content-Type of a multipart/signed 
message, treating it as not S/MIME signed", e);
+            return Optional.empty();
+        }
+    }
 }
diff --git 
a/mailet/crypto/src/test/java/org/apache/james/transport/matcher/IsSMIMESignedTest.java
 
b/mailet/crypto/src/test/java/org/apache/james/transport/matcher/IsSMIMESignedTest.java
index 58f068a14e..b40d64afbb 100644
--- 
a/mailet/crypto/src/test/java/org/apache/james/transport/matcher/IsSMIMESignedTest.java
+++ 
b/mailet/crypto/src/test/java/org/apache/james/transport/matcher/IsSMIMESignedTest.java
@@ -19,62 +19,112 @@
 
 package org.apache.james.transport.matcher;
 
-import static org.apache.mailet.base.MailAddressFixture.RECIPIENT1;
-import static org.apache.mailet.base.MailAddressFixture.SENDER;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.mailet.base.MailAddressFixture.ANY_AT_JAMES;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import org.apache.james.core.builder.MimeMessageBuilder;
+import java.io.ByteArrayInputStream;
+import java.util.Properties;
+
+import jakarta.mail.Session;
+import jakarta.mail.internet.MimeMessage;
+
+import org.apache.mailet.Mail;
 import org.apache.mailet.base.test.FakeMail;
+import org.apache.mailet.base.test.FakeMatcherConfig;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
 
-public class IsSMIMESignedTest {
-    private IsSMIMESigned isSMIMESigned;
+class IsSMIMESignedTest {
+    private IsSMIMESigned testee;
 
     @BeforeEach
-    void beforeEach() {
-        isSMIMESigned = new IsSMIMESigned();
+    void setUp() throws Exception {
+        testee = new IsSMIMESigned();
+        testee.init(FakeMatcherConfig.builder()
+            .matcherName("IsSMIMESigned")
+            .build());
     }
 
-    @ParameterizedTest
-    @ValueSource(strings = {"multipart/signed",
-        "application/pkcs7-signature",
-        "application/x-pkcs7-signature",
-        "application/pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\"",
-        "application/x-pkcs7-mime; smime-type=signed-data; 
name=\"smime.p7m\""})
-    void 
matchShouldReturnNonEmptyListWhenMessageContentTypeIsSMIMERelated(String 
contentType) throws Exception {
-        FakeMail mail = FakeMail.builder()
-            .name("mail")
-            
.mimeMessage(MimeMessageBuilder.mimeMessageBuilder().addHeader("Content-Type", 
contentType))
-            .sender(SENDER)
-            .recipient(RECIPIENT1)
-            .build();
-        assertThat(isSMIMESigned.match(mail)).isNotEmpty();
+    @Test
+    void shouldMatchMultipartSignedWithPkcs7Protocol() throws Exception {
+        assertThat(testee.match(mailWithContentType(
+                "multipart/signed; protocol=\"application/pkcs7-signature\"; 
micalg=sha-256; boundary=\"bound\"")))
+            .containsOnly(ANY_AT_JAMES);
     }
 
     @Test
-    void matchShouldReturnNullWhenMessageContentTypeIsNotSMIMERelated() throws 
Exception {
-        FakeMail mail = FakeMail.builder()
-            .name("mail")
-            
.mimeMessage(MimeMessageBuilder.mimeMessageBuilder().addHeader("Content-Type", 
"text/plain"))
-            .sender(SENDER)
-            .recipient(RECIPIENT1)
-            .build();
-        assertThat(isSMIMESigned.match(mail)).isNull();
+    void shouldMatchMultipartSignedWithLegacyPkcs7Protocol() throws Exception {
+        assertThat(testee.match(mailWithContentType(
+                "multipart/signed; protocol=\"application/x-pkcs7-signature\"; 
micalg=sha1; boundary=\"bound\"")))
+            .containsOnly(ANY_AT_JAMES);
+    }
+
+    @Test
+    void shouldMatchWhenProtocolCaseDiffers() throws Exception {
+        assertThat(testee.match(mailWithContentType(
+                "multipart/signed; protocol=\"Application/PKCS7-Signature\"; 
boundary=\"bound\"")))
+            .containsOnly(ANY_AT_JAMES);
+    }
+
+    @Test
+    void shouldNotMatchPgpMimeSignedMail() throws Exception {
+        assertThat(testee.match(mailWithContentType(
+                "multipart/signed; protocol=\"application/pgp-signature\"; 
micalg=pgp-sha256; boundary=\"bound\"")))
+            .isNull();
+    }
+
+    @Test
+    void shouldNotMatchMultipartSignedWithoutProtocol() throws Exception {
+        assertThat(testee.match(mailWithContentType("multipart/signed; 
boundary=\"bound\"")))
+            .isNull();
+    }
+
+    @Test
+    void shouldNotMatchMultipartMixed() throws Exception {
+        assertThat(testee.match(mailWithContentType("multipart/mixed; 
boundary=\"bound\"")))
+            .isNull();
+    }
+
+    @Test
+    void shouldMatchPkcs7SignatureMail() throws Exception {
+        
assertThat(testee.match(mailWithContentType("application/pkcs7-signature")))
+            .containsOnly(ANY_AT_JAMES);
+    }
+
+    @Test
+    void shouldMatchPkcs7MimeSignedData() throws Exception {
+        assertThat(testee.match(mailWithContentType("application/pkcs7-mime; 
smime-type=signed-data; name=smime.p7m")))
+            .containsOnly(ANY_AT_JAMES);
     }
 
     @Test
-    void matchShouldReturnNullWhenMailIsNull() throws Exception {
-        assertThat(isSMIMESigned.match(null)).isNull();
+    void shouldNotMatchPkcs7MimeEnvelopedData() throws Exception {
+        assertThat(testee.match(mailWithContentType("application/pkcs7-mime; 
smime-type=enveloped-data; name=smime.p7m")))
+            .isNull();
     }
 
     @Test
-    void matchShouldReturnNullWhenMessageIsNull() throws Exception {
-        FakeMail mail = FakeMail.builder()
+    void shouldNotMatchTextPlain() throws Exception {
+        assertThat(testee.match(mailWithContentType("text/plain; 
charset=UTF-8")))
+            .isNull();
+    }
+
+    private Mail mailWithContentType(String contentType) throws Exception {
+        String message = "Subject: any\r\n"
+            + "Content-Type: " + contentType + "\r\n"
+            + "\r\n"
+            + "--bound\r\n"
+            + "Content-Type: text/plain\r\n"
+            + "\r\n"
+            + "content\r\n"
+            + "--bound--\r\n";
+
+        return FakeMail.builder()
             .name("mail")
+            .recipient(ANY_AT_JAMES)
+            .mimeMessage(new MimeMessage(Session.getInstance(new Properties()),
+                new ByteArrayInputStream(message.getBytes(UTF_8))))
             .build();
-        assertThat(isSMIMESigned.match(mail)).isNull();
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to