This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-jdkim.git
commit e2c458cbbcfa5182ff0443f1ef500bff450ea9fe Author: Benoit TELLIER <[email protected]> AuthorDate: Sun Nov 1 19:59:16 2026 +0000 [FIX] Message: parse with mime4j's permissive limits Message parsed with mime4j's default limit of 1000 header fields and hard-coded limits of 30000 octets per field and 10000 per line, so RFC 5322-valid mail with long Received: traces or a To: of a few hundred recipients could be neither signed nor verified. The MimeIOException was rethrown as an IOException: the signer sent the mail unsigned and the verifier gave no result at all. mime4j is upgraded to 0.8.15 and Message now uses its MimeConfig.PERMISSIVE profile (4096 fields, 64 KB per field, no line limit, 100 MB of content), well above what real mail uses; the next mime4j release makes these limits configurable with the james.mime4j.permissive.* system properties. Message rethrows a parsing failure as a MimeException, which sign and verify already turn into a PermFailException. --- docs/modules/ROOT/pages/usage.adoc | 9 +++ .../java/org/apache/james/jdkim/impl/Message.java | 17 +++-- .../apache/james/jdkim/impl/MessageLimitsTest.java | 89 ++++++++++++++++++++++ pom.xml | 2 +- 4 files changed, 110 insertions(+), 7 deletions(-) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index ca2ef34..2ef4adf 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -286,6 +286,15 @@ slightly malformed signatures or keys. Start the JVM with with a semantic effect -- a key for another service (`s=chat`), an expired signature, an unsupported algorithm -- are always applied. +=== Message parsing limits + +`sign(InputStream)` and `verify(InputStream)` parse the message with mime4j's +`MimeConfig.PERMISSIVE` profile, whose limits (4096 header fields, 64 KB per +header field, no line limit, 100 MB of content) are well above what real mail +uses. The next mime4j release makes them configurable with the +`james.mime4j.permissive.*` system properties. A message exceeding them is a permanent error +(`PermFailException`), not an I/O error. + == Applying several signatures Signing with more than one key lets you offer several algorithms on one diff --git a/main/src/main/java/org/apache/james/jdkim/impl/Message.java b/main/src/main/java/org/apache/james/jdkim/impl/Message.java index 8bb580d..3015562 100644 --- a/main/src/main/java/org/apache/james/jdkim/impl/Message.java +++ b/main/src/main/java/org/apache/james/jdkim/impl/Message.java @@ -52,7 +52,7 @@ public class Message implements Headers { * * @param is the stream to read the header from. * @throws IOException on I/O errors. - * @throws MimeIOException on MIME protocol violations. + * @throws MimeException on MIME protocol violations, or when a parsing limit is exceeded. */ public Message(InputStream is) throws IOException, MimeException { MessageBuilder mb = newMessageBuilder().newMessageBuilder(); @@ -60,7 +60,12 @@ public class Message implements Headers { // mime4j drops the fields whose name it cannot parse and normalizes the // others: the fields are taken from the raw header bytes instead. HeaderCapturingInputStream header = new HeaderCapturingInputStream(new EOLConvertingInputStream(is)); - this.message = mb.parseMessage(header); + try { + this.message = mb.parseMessage(header); + } catch (MimeIOException e) { + // a parsing limit or a MIME violation, not an I/O error: callers report it as a permanent error + throw e.getCause() != null ? e.getCause() : new MimeException(e); + } List<String> all = splitFields(new String(header.getHeader(), StandardCharsets.ISO_8859_1)); Map<String, List<String>> byName = new HashMap<>(); @@ -73,10 +78,10 @@ public class Message implements Headers { } private MessageServiceFactory newMessageBuilder() throws MimeException { - MimeConfig mec = MimeConfig.custom() - .setMaxLineLen(10000) - .setMaxHeaderLen(30000) - .build(); + // the limits of mime4j's permissive profile (4096 fields, 64 KB per field, no line limit) are well + // above what real mail uses; the next mime4j release makes them configurable with the + // james.mime4j.permissive.* system properties + MimeConfig mec = MimeConfig.PERMISSIVE; MessageServiceFactory mbf = MessageServiceFactory.newInstance(); mbf.setAttribute("MimeEntityConfig", mec); diff --git a/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java b/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java new file mode 100644 index 0000000..bfc4439 --- /dev/null +++ b/main/src/test/java/org/apache/james/jdkim/impl/MessageLimitsTest.java @@ -0,0 +1,89 @@ +/****************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ******************************************************************************/ + +package org.apache.james.jdkim.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.james.jdkim.DKIMSigner; +import org.apache.james.jdkim.TestKeys; +import org.junit.Test; + +public class MessageLimitsTest { + + private static ByteArrayInputStream stream(StringBuilder sb) { + return new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.US_ASCII)); + } + + @Test + public void shouldParseMoreThanThousandHeaderFields() throws Exception { + StringBuilder sb = new StringBuilder("From: [email protected]\r\n"); + for (int i = 0; i < 2500; i++) { + sb.append("X-Trace: hop ").append(i).append("\r\n"); + } + sb.append("\r\nbody\r\n"); + + Message message = new Message(stream(sb)); + + assertThat(message.getFields("X-Trace")).hasSize(2500); + } + + @Test + public void shouldParseLongFoldedField() throws Exception { + StringBuilder sb = new StringBuilder("From: [email protected]\r\nTo: [email protected]"); + for (int i = 1; i < 2000; i++) { + sb.append(",\r\n r").append(i).append("@example.com"); + } + sb.append("\r\n\r\nbody\r\n"); + + Message message = new Message(stream(sb)); + + assertThat(message.getFields("To")).hasSize(1); + } + + @Test + public void shouldParseLongLine() throws Exception { + StringBuilder sb = new StringBuilder("From: [email protected]\r\nX-Long: "); + for (int i = 0; i < 20000; i++) { + sb.append('x'); + } + sb.append("\r\n\r\nbody\r\n"); + + Message message = new Message(stream(sb)); + + assertThat(message.getFields("X-Long")).hasSize(1); + } + + @Test + public void signerShouldSignMessagesWithManyHeaderFields() throws Exception { + StringBuilder sb = new StringBuilder("From: [email protected]\r\n"); + for (int i = 0; i < 2500; i++) { + sb.append("Received: hop ").append(i).append("\r\n"); + } + sb.append("\r\nbody\r\n"); + + String field = new DKIMSigner("v=1; a=rsa-sha256; c=simple; d=messiah.edu; h=from; s=selector2;", TestKeys.privateKey) + .sign(stream(sb)); + + assertThat(field).startsWith("DKIM-Signature:"); + } +} diff --git a/pom.xml b/pom.xml index 29fe43d..6a34050 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ </distributionManagement> <properties> - <apache-mime4j.version>0.8.12</apache-mime4j.version> + <apache-mime4j.version>0.8.15</apache-mime4j.version> <dnsjava.version>3.6.3</dnsjava.version> <james-skin.version>1.8</james-skin.version> <junit.version>4.13.2</junit.version> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
