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