Author: markt
Date: Sat Mar 16 20:25:35 2013
New Revision: 1457303

URL: http://svn.apache.org/r1457303
Log: (empty)

Modified:
    tomcat/tc7.0.x/trunk/   (props changed)
    tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java
    
tomcat/tc7.0.x/trunk/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
    tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml

Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
  Merged /tomcat/trunk:r1457299,1457301

Modified: 
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java?rev=1457303&r1=1457302&r2=1457303&view=diff
==============================================================================
--- 
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java 
(original)
+++ 
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java 
Sat Mar 16 20:25:35 2013
@@ -48,8 +48,7 @@ public class HttpParser {
     private static final Integer FIELD_TYPE_QUOTED_STRING = Integer.valueOf(1);
     private static final Integer FIELD_TYPE_TOKEN_OR_QUOTED_STRING = 
Integer.valueOf(2);
     private static final Integer FIELD_TYPE_LHEX = Integer.valueOf(3);
-    private static final Integer FIELD_TYPE_QUOTED_LHEX = Integer.valueOf(4);
-    private static final Integer FIELD_TYPE_QUOTED_TOKEN = Integer.valueOf(5);
+    private static final Integer FIELD_TYPE_QUOTED_TOKEN = Integer.valueOf(4);
 
     private static final Map<String,Integer> fieldTypes =
             new HashMap<String,Integer>();
@@ -59,16 +58,23 @@ public class HttpParser {
     private static final boolean isHex[] = new boolean[128];
 
     static {
-        // Digest field types
+        // Digest field types.
+        // Note: These are more relaxed than RFC2617. This adheres to the
+        //       recommendation of RFC2616 that servers are tolerant of buggy
+        //       clients when they can be so without ambiguity.
         fieldTypes.put("username", FIELD_TYPE_QUOTED_STRING);
         fieldTypes.put("realm", FIELD_TYPE_QUOTED_STRING);
         fieldTypes.put("nonce", FIELD_TYPE_QUOTED_STRING);
         fieldTypes.put("digest-uri", FIELD_TYPE_QUOTED_STRING);
-        fieldTypes.put("response", FIELD_TYPE_QUOTED_LHEX);
+        // RFC2617 says response is <">32LHEX<">. 32LHEX will also be accepted
+        fieldTypes.put("response", FIELD_TYPE_LHEX);
+        // RFC2617 says algorithm is token. <">token<"> will also be accepted
         fieldTypes.put("algorithm", FIELD_TYPE_QUOTED_TOKEN);
         fieldTypes.put("cnonce", FIELD_TYPE_QUOTED_STRING);
         fieldTypes.put("opaque", FIELD_TYPE_QUOTED_STRING);
+        // RFC2617 says qop is token. <">token<"> will also be accepted
         fieldTypes.put("qop", FIELD_TYPE_QUOTED_TOKEN);
+        // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted
         fieldTypes.put("nc", FIELD_TYPE_LHEX);
 
         // Setup the flag arrays
@@ -145,14 +151,10 @@ public class HttpParser {
                     value = readTokenOrQuotedString(input, false);
                     break;
                 case 3:
-                    // FIELD_TYPE_LHEX
+                    // FIELD_TYPE_QUOTED_LHEX
                     value = readLhex(input);
                     break;
                 case 4:
-                    // FIELD_TYPE_QUOTED_LHEX
-                    value = readQuotedLhex(input);
-                    break;
-                case 5:
                     // FIELD_TYPE_QUOTED_TOKEN
                     value = readQuotedToken(input);
                     break;
@@ -265,6 +267,8 @@ public class HttpParser {
         int len = constant.length();
 
         int c = input.read();
+
+        // Skip lws
         while (c == 32 || c == 9) {
             c = input.read();
         }
@@ -361,9 +365,10 @@ public class HttpParser {
 
     private static String readTokenOrQuotedString(StringReader input,
             boolean returnQuoted) throws IOException {
-        input.mark(1);
+
         int c = input.read();
-        input.reset();
+        // Skip back so first character is available to be read again
+        input.skip(-1);
 
         if (c == '"') {
             return readQuotedString(input, returnQuoted);
@@ -373,9 +378,12 @@ public class HttpParser {
     }
 
     /**
+     * Token can be read unambiguously with or without surrounding quotes so
+     * this parsing method for token permits optional surrounding double 
quotes.
      * This is not defined in any RFC. It is a special case to handle data from
-     * buggy clients (known buggy clients include Microsoft IE 8 & 9, Apple
-     * Safari for OSX and iOS) that add quotes to values that should be tokens.
+     * buggy clients (known buggy clients for DIGEST auth include Microsoft IE 
8
+     * & 9, Apple Safari for OSX and iOS) that add quotes to values that should
+     * be tokens.
      *
      * @return the token if one was found, null if data other than a token or
      *         quoted token was found or null if the end of data was reached
@@ -396,7 +404,7 @@ public class HttpParser {
 
         if (c == '"') {
             quoted = true;
-        } else if (c == -1) {
+        } else if (c == -1 || !isToken(c)) {
             return null;
         } else {
             result.append((char) c);
@@ -425,14 +433,19 @@ public class HttpParser {
     }
 
     /**
-     * Parses lower case hex but permits upper case hex to be used (converting
-     * it to lower case before returning).
+     * LHEX can be read unambiguously with or without surrounding quotes so 
this
+     * parsing method for LHEX permits optional surrounding double quotes. Some
+     * buggy clients (libwww-perl for DIGEST auth) are known to send quoted 
LHEX
+     * when the specification requires just LHEX.
      *
-     * @return the lower case hex if present or <code>null</code> if data other
-     *         than lower case hex was found
+     * @return  the sequence of LHEX (minus any surrounding quotes) if any was
+     *          found, or <code>null</code> if data other LHEX was found
      */
-    private static String readLhex(StringReader input) throws IOException {
+    private static String readLhex(StringReader input)
+            throws IOException {
+
         StringBuilder result = new StringBuilder();
+        boolean quoted = false;
 
         int c = input.read();
 
@@ -441,32 +454,34 @@ public class HttpParser {
             c = input.read();
         }
 
+        if (c == '"') {
+            quoted = true;
+        } else if (c == -1 || !isHex(c)) {
+            return null;
+        } else {
+            result.append((char) c);
+        }
+        c = input.read();
+
         while (c != -1 && isHex(c)) {
             result.append((char) c);
             c = input.read();
         }
-        // Skip back so non-hex character is available for next read
-        input.skip(-1);
 
-        if (result.length() == 0) {
-            return null;
+        if (quoted) {
+            if (c != '"') {
+                return null;
+            }
         } else {
-            return result.toString().toLowerCase(Locale.US);
+            // Skip back so non-token character is available for next read
+            input.skip(-1);
         }
-    }
 
-    private static String readQuotedLhex(StringReader input)
-            throws IOException {
-
-        if (skipConstant(input, "\"") != SkipConstantResult.FOUND) {
-            return null;
-        }
-        String result = readLhex(input);
-        if (skipConstant(input, "\"") == SkipConstantResult.NOT_FOUND) {
+        if (c != -1 && result.length() == 0) {
             return null;
+        } else {
+            return result.toString().toLowerCase(Locale.US);
         }
-
-        return result;
     }
 
     private static enum SkipConstantResult {

Modified: 
tomcat/tc7.0.x/trunk/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java?rev=1457303&r1=1457302&r2=1457303&view=diff
==============================================================================
--- 
tomcat/tc7.0.x/trunk/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
 (original)
+++ 
tomcat/tc7.0.x/trunk/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
 Sat Mar 16 20:25:35 2013
@@ -126,6 +126,28 @@ public class TestAuthorizationDigest {
     }
 
     @Test
+    public void testQuotedLhex() throws Exception {
+        String header = "Digest nc=\"00000001\"";
+
+        StringReader input = new StringReader(header);
+
+        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
+
+        Assert.assertEquals("00000001", result.get("nc"));
+    }
+
+    @Test
+    public void testUnclosedQuotedLhex() throws Exception {
+        String header = "Digest nc=\"00000001";
+
+        StringReader input = new StringReader(header);
+
+        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
+
+        Assert.assertNull(result);
+    }
+
+    @Test
     public void testUnclosedQuotedString1() throws Exception {
         String header = "Digest username=\"test";
 
@@ -226,6 +248,16 @@ public class TestAuthorizationDigest {
     }
 
     @Test
+    public void testWrongCharacterInToken2() throws Exception {
+        String header = "Digest qop=\u044f";
+
+        StringReader input = new StringReader(header);
+
+        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
+        Assert.assertNull(result);
+    }
+
+    @Test
     public void testWrongCharacterInQuotedToken() throws Exception {
         String header = "Digest qop=\"\u044f\"";
 
@@ -244,4 +276,14 @@ public class TestAuthorizationDigest {
         Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
         Assert.assertNull(result);
     }
+
+    @Test
+    public void testWrongCharacterInQuotedHex() throws Exception {
+        String header = "Digest nc=\"\u044f\"";
+
+        StringReader input = new StringReader(header);
+
+        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
+        Assert.assertNull(result);
+    }
 }

Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1457303&r1=1457302&r2=1457303&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Sat Mar 16 20:25:35 2013
@@ -87,6 +87,11 @@
         are closed when parsing web application deployment descriptors.
         (violetagg)
       </fix>
+      <fix>
+        <bug>54707</bug>: Further relax the parsing of DIGEST authentication
+        headers to allow for buggy clients that quote values that RFC2617 
states
+        should not be quoted. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to