You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ol...@apache.org on 2008/09/21 15:07:49 UTC

svn commit: r697507 - in /james/mime4j/trunk/src: main/java/org/apache/james/mime4j/io/ main/java/org/apache/james/mime4j/parser/ test/java/org/apache/james/mime4j/parser/

Author: olegk
Date: Sun Sep 21 06:07:49 2008
New Revision: 697507

URL: http://svn.apache.org/viewvc?rev=697507&view=rev
Log:
MIME4J-57:
* Added javadocs to the MimeEntityConfig class
* Made sure the max line length limit is correctly enforced for folded lines
* Added max header limit check; intended as a protection from a DoS condition if a message contains too many headers (more than 1000) 

Added:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java   (with props)
Modified:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/MimeEntityConfig.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/parser/MimeEntityTest.java

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java?rev=697507&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java Sun Sep 21 06:07:49 2008
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.mime4j.io;
+
+import org.apache.james.mime4j.MimeException;
+
+/**
+ * Signals a I/O error due to the header count exceeding the maximum limit.
+ */
+public class MaxHeaderLimitException extends MimeException {
+    
+    private static final long serialVersionUID = 2154269045186186769L;
+
+    public MaxHeaderLimitException(final String message) {
+        super(message);
+    }
+
+}

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/io/MaxHeaderLimitException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java?rev=697507&r1=697506&r2=697507&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/AbstractEntity.java Sun Sep 21 06:07:49 2008
@@ -30,6 +30,8 @@
 import org.apache.james.mime4j.descriptor.MaximalBodyDescriptor;
 import org.apache.james.mime4j.descriptor.MutableBodyDescriptor;
 import org.apache.james.mime4j.io.LineReaderInputStream;
+import org.apache.james.mime4j.io.MaxHeaderLimitException;
+import org.apache.james.mime4j.io.MaxLineLimitException;
 import org.apache.james.mime4j.util.ByteArrayBuffer;
 import org.apache.james.mime4j.util.CharArrayBuffer;
 import org.apache.james.mime4j.util.MessageUtils;
@@ -55,6 +57,7 @@
     private int lineCount;
     private String field, fieldName, fieldValue;
     private boolean endOfHeader;
+    private int headerCount;
 
     private static final BitSet fieldChars = new BitSet();
 
@@ -92,6 +95,7 @@
         this.fieldbuf = new CharArrayBuffer(64);
         this.lineCount = 0;
         this.endOfHeader = false;
+        this.headerCount = 0;
     }
 
     public int getState() {
@@ -121,12 +125,16 @@
         if (endOfHeader) {
             return;
         }
+        int maxLineLen = config.getMaxLineLen();
         LineReaderInputStream instream = getDataStream();
         fieldbuf.clear();
         for (;;) {
             // If there's still data stuck in the line buffer
             // copy it to the field buffer
             int len = linebuf.length();
+            if (maxLineLen > 0 && fieldbuf.length() + len >= maxLineLen) {
+                throw new MaxLineLimitException("Maximum line length limit exceeded");
+            }
             if (len > 0) {
                 fieldbuf.append(linebuf, 0, len);
             }
@@ -160,11 +168,17 @@
     }
 
     protected boolean parseField() throws IOException {
+        int maxHeaderLimit = config.getMaxHeaderCount();
         for (;;) {
             if (endOfHeader) {
                 return false;
             }
+            if (headerCount >= maxHeaderLimit) {
+                throw new MaxHeaderLimitException("Maximum header limit exceeded");
+            }
+            
             fillFieldBuffer();
+            headerCount++;
             
             // Strip away line delimiter
             int len = fieldbuf.length();

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/MimeEntityConfig.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/MimeEntityConfig.java?rev=697507&r1=697506&r2=697507&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/MimeEntityConfig.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/parser/MimeEntityConfig.java Sun Sep 21 06:07:49 2008
@@ -19,6 +19,7 @@
 
 package org.apache.james.mime4j.parser;
 
+import org.apache.james.mime4j.MimeException;
 import org.apache.james.mime4j.util.CharArrayBuffer;
 
 /**
@@ -29,12 +30,14 @@
     private boolean maximalBodyDescriptor;
     private boolean strictParsing;
     private int maxLineLen;
+    private int maxHeaderCount;
     
     public MimeEntityConfig() {
         super();
         this.maximalBodyDescriptor = false;
         this.strictParsing = false;
         this.maxLineLen = 1000;
+        this.maxHeaderCount = 1000;
     }
     
     public boolean isMaximalBodyDescriptor() {
@@ -45,22 +48,73 @@
         this.maximalBodyDescriptor = maximalBodyDescriptor;
     }
     
-    public boolean isStrictParsing() {
-        return this.strictParsing;
-    }
-    
+    /**
+     * Defines whether minor violations of the MIME specification should be 
+     * tolerated or should result in a {@link MimeException}. If this parameter
+     * is set to <code>true</code>, a strict interpretation of the MIME 
+     * specification will be enforced, If this parameter is set to <code>false</code>
+     * minor violations will result in a warning in the log.
+     * 
+     * @return value of the strict parsing mode
+     */
     public void setStrictParsing(boolean strictParsing) {
         this.strictParsing = strictParsing;
     }
 
+    /**
+     * Returns the value of the strict parsing mode
+     * @see #setStrictParsing(boolean)
+     * 
+     * @return value of the strict parsing mode
+     */
+    public boolean isStrictParsing() {
+        return this.strictParsing;
+    }
+    
+    /**
+     * Sets the maximum line length limit. Parsing of a MIME entity will be terminated 
+     * with a {@link MimeException} if a line is encountered that exceeds the maximum
+     * length limit. If this parameter is set to a non positive value the line length
+     * check will be disabled.
+     * 
+     * @param maximum line length limit
+     */
     public void setMaxLineLen(int maxLineLen) {
         this.maxLineLen = maxLineLen;
     }
     
+    /** 
+     * Returns the maximum line length limit
+     * @see #setMaxLineLen(int)
+     * 
+     * @return value of the the maximum line length limit
+     */
     public int getMaxLineLen() {
         return this.maxLineLen;
     }
 
+    /**
+     * Sets the maximum header limit. Parsing of a MIME entity will be terminated 
+     * with a {@link MimeException} if the number of headers exceeds the maximum
+     * limit. If this parameter is set to a non positive value the header limit check 
+     * will be disabled.
+     * 
+     * @param maximum header limit
+     */
+    public void setMaxHeaderCount(int maxHeaderCount) {
+        this.maxHeaderCount = maxHeaderCount;
+    }
+
+    /** 
+     * Returns the maximum header limit
+     * @see #setMaxHeaderCount(int)
+     * 
+     * @return value of the the maximum header limit
+     */
+    public int getMaxHeaderCount() {
+        return this.maxHeaderCount;
+    }
+
     public Object clone() throws CloneNotSupportedException {
         return super.clone();
     }

Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/parser/MimeEntityTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/parser/MimeEntityTest.java?rev=697507&r1=697506&r2=697507&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/parser/MimeEntityTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/parser/MimeEntityTest.java Sun Sep 21 06:07:49 2008
@@ -23,6 +23,8 @@
 
 import org.apache.commons.io.IOUtils;
 import org.apache.james.mime4j.io.BufferedLineReaderInputStream;
+import org.apache.james.mime4j.io.MaxHeaderLimitException;
+import org.apache.james.mime4j.io.MaxLineLimitException;
 import org.apache.james.mime4j.io.RootInputStream;
 import org.apache.james.mime4j.parser.EntityStateMachine;
 import org.apache.james.mime4j.parser.EntityStates;
@@ -329,4 +331,156 @@
         assertEquals(EntityStates.T_END_OF_STREAM, entity.getState());
     }
 
+    public void testMaxLineLimitCheck() throws Exception {
+        String message = 
+            "To: Road Runner <ru...@example.org>\r\n" +
+            "From: Wile E. Cayote <wi...@example.org>\r\n" +
+            "Date: Tue, 12 Feb 2008 17:34:09 +0000 (GMT)\r\n" +
+            "Subject: Mail\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "Content-Type: text/plain\r\n" +
+            "\r\n" +
+            "a very important message";
+        byte[] raw = message.getBytes("US-ASCII");
+        ByteArrayInputStream instream = new ByteArrayInputStream(raw);
+        RootInputStream rootStream = new RootInputStream(instream); 
+        BufferedLineReaderInputStream rawstream = new BufferedLineReaderInputStream(rootStream, 12); 
+        
+        MimeEntityConfig config = new MimeEntityConfig();
+        config.setMaxLineLen(50);
+        MimeEntity entity = new MimeEntity(
+                rootStream,
+                rawstream,
+                null,
+                EntityStates.T_START_MESSAGE,
+                EntityStates.T_END_MESSAGE,
+                config);
+        
+        assertEquals(EntityStates.T_START_MESSAGE, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_START_HEADER, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        try {
+            entity.advance();
+            fail("MaxLineLimitException should have been thrown");
+        } catch (MaxLineLimitException expected) {
+        }
+    }
+    
+    public void testMaxLineLimitCheckFoldedLines() throws Exception {
+        String message = 
+            "To: Road Runner <ru...@example.org>\r\n" +
+            "From: Wile E. Cayote <wi...@example.org>\r\n" +
+            "Date: Tue, 12 Feb 2008 17:34:09 +0000 (GMT)\r\n" +
+            "Subject: Mail\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "    xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "Content-Type: text/plain\r\n" +
+            "\r\n" +
+            "a very important message";
+        byte[] raw = message.getBytes("US-ASCII");
+        ByteArrayInputStream instream = new ByteArrayInputStream(raw);
+        RootInputStream rootStream = new RootInputStream(instream); 
+        BufferedLineReaderInputStream rawstream = new BufferedLineReaderInputStream(rootStream, 12); 
+        
+        MimeEntityConfig config = new MimeEntityConfig();
+        config.setMaxLineLen(50);
+        MimeEntity entity = new MimeEntity(
+                rootStream,
+                rawstream,
+                null,
+                EntityStates.T_START_MESSAGE,
+                EntityStates.T_END_MESSAGE,
+                config);
+        
+        assertEquals(EntityStates.T_START_MESSAGE, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_START_HEADER, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_FIELD, entity.getState());
+        try {
+            entity.advance();
+            fail("MaxLineLimitException should have been thrown");
+        } catch (MaxLineLimitException expected) {
+        }
+    }
+
+    public void testMaxHeaderCount() throws Exception {
+        String message = 
+            "To: Road Runner <ru...@example.org>\r\n" +
+            "From: Wile E. Cayote <wi...@example.org>\r\n" +
+            "Date: Tue, 12 Feb 2008 17:34:09 +0000 (GMT)\r\n" +
+            "Subject: Mail\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "DoS: xxxxxxxxxxxxxxxxxxxxx\r\n" +
+            "Content-Type: text/plain\r\n" +
+            "\r\n" +
+            "a very important message";
+        byte[] raw = message.getBytes("US-ASCII");
+        ByteArrayInputStream instream = new ByteArrayInputStream(raw);
+        RootInputStream rootStream = new RootInputStream(instream); 
+        BufferedLineReaderInputStream rawstream = new BufferedLineReaderInputStream(rootStream, 12); 
+        
+        MimeEntityConfig config = new MimeEntityConfig();
+        config.setMaxHeaderCount(20);
+        MimeEntity entity = new MimeEntity(
+                rootStream,
+                rawstream,
+                null,
+                EntityStates.T_START_MESSAGE,
+                EntityStates.T_END_MESSAGE,
+                config);
+        
+        assertEquals(EntityStates.T_START_MESSAGE, entity.getState());
+        entity.advance();
+        assertEquals(EntityStates.T_START_HEADER, entity.getState());
+        
+        for (int i = 0; i < 20; i++) {
+            entity.advance();
+            assertEquals(EntityStates.T_FIELD, entity.getState());
+        }
+        try {
+            entity.advance();
+            fail("MaxHeaderLimitException should have been thrown");
+        } catch (MaxHeaderLimitException expected) {
+        }
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org