]> granicus.if.org Git - curl/commitdiff
- Alexey Borzov filed bug report #2535504
authorDaniel Stenberg <daniel@haxx.se>
Mon, 26 Jan 2009 13:19:03 +0000 (13:19 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 26 Jan 2009 13:19:03 +0000 (13:19 +0000)
  (http://curl.haxx.se/bug/view.cgi?id=2535504) pointing out that realms with
  quoted quotation marks in HTTP Digest headers didn't work. I've now added
  test case 1095 that verifies my fix.

CHANGES
RELEASE-NOTES
lib/http_digest.c
tests/data/Makefile.am
tests/data/test1095 [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index e65da208f3816352fac902ebb92300f394d26fd1..9e3e6ed95c5604c6bcbc60386b691df11f578f1f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,11 @@
                                   Changelog
 
 Daniel Stenberg (26 Jan 2009)
+- Alexey Borzov filed bug report #2535504
+  (http://curl.haxx.se/bug/view.cgi?id=2535504) pointing out that realms with
+  quoted quotation marks in HTTP Digest headers didn't work. I've now added 
+  test case 1095 that verifies my fix.
+
 - Craig A West brought CURLOPT_NOPROXY and the corresponding --noproxy option.
   They basically offer the same thing the NO_PROXY environment variable only
   offered previously: list a set of host names that shall not use the proxy
index bb76da6970ca1d67add008770c7a77ba452d20e8..a54f611cbaadcba5734567d1faaf787cb428664c 100644 (file)
@@ -15,6 +15,7 @@ This release includes the following bugfixes:
 
  o missing ssh.obj in VS makefiles
  o FTP ;type=i URLs now work with CURLOPT_PROXY_TRANSFER_MODE in Turkish locale
+ o realms with quoted quotation marks in HTTP Digest headers
 
 This release includes the following known bugs:
 
@@ -23,6 +24,6 @@ This release includes the following known bugs:
 This release would not have looked like this without help, code, reports and
 advice from friends like these:
 
- Lisa Xu, Daniel Fandrich, Craig A West
+ Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov
 
         Thanks! (and sorry if I forgot to mention someone)
index bab95e9dee833193bd6a13210f6169b1c7239822..1f452c7f52575d452b3c19f4c419b016d00bd209 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
 /* The last #include file should be: */
 #include "memdebug.h"
 
+#define MAX_VALUE_LENGTH 256
+#define MAX_CONTENT_LENGTH 1024
+
+/*
+ * Return 0 on success and then the buffers are filled in fine.
+ *
+ * Non-zero means failure to parse.
+ */
+static int get_pair(const char *str, char *value, char *content,
+                    const char **endptr)
+{
+  int c;
+  bool starts_with_quote = FALSE;
+  bool escape = FALSE;
+
+  for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); )
+    *value++ = *str++;
+  *value=0;
+
+  if('=' != *str++)
+    /* eek, no match */
+    return 1;
+
+  if('\"' == *str) {
+    /* this starts with a quote so it must end with one as well! */
+    str++;
+    starts_with_quote = TRUE;
+  }
+
+  for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) {
+    switch(*str) {
+    case '\\':
+      if(!escape) {
+        /* possibly the start of an escaped quote */
+        escape = TRUE;
+        *content++ = '\\'; /* even though this is an escape character, we still
+                              store it as-is in the target buffer */
+        continue;
+      }
+      break;
+    case ',':
+      if(!starts_with_quote) {
+        /* this signals the end of the content if we didn't get a starting quote
+           and then we do "sloppy" parsing */
+        c=0; /* the end */
+        continue;
+      }
+      break;
+    case '\r':
+    case '\n':
+      /* end of string */
+      c=0;
+      continue;
+    case '\"':
+      if(!escape && starts_with_quote) {
+        /* end of string */
+        c=0;
+        continue;
+      }
+      break;
+    }
+    escape = FALSE;
+    *content++ = *str;
+  }
+  *content=0;
+
+  *endptr = str;
+
+  return 0; /* all is fine! */
+}
+
 /* Test example headers:
 
 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
@@ -90,26 +161,16 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
     Curl_digest_cleanup_one(d);
 
     while(more) {
-      char value[256];
-      char content[1024];
+      char value[MAX_VALUE_LENGTH];
+      char content[MAX_CONTENT_LENGTH];
       size_t totlen=0;
 
       while(*header && ISSPACE(*header))
         header++;
 
-      /* how big can these strings be? */
-      if((2 == sscanf(header, "%255[^=]=\"%1023[^\"]\"",
-                      value, content)) ||
-         /* try the same scan but without quotes around the content but don't
-            include the possibly trailing comma, newline or carriage return */
-         (2 ==  sscanf(header, "%255[^=]=%1023[^\r\n,]",
-                       value, content)) ) {
-        if(!strcmp("\"\"", content)) {
-          /* for the name="" case where we get only the "" in the content
-           * variable, simply clear the content then
-           */
-          content[0]=0;
-        }
+      /* extract a value=content pair */
+      if(!get_pair(header, value, content, &header)) {
+
         if(Curl_raw_equal(value, "nonce")) {
           d->nonce = strdup(content);
           if(!d->nonce)
@@ -185,7 +246,6 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
       else
         break; /* we're done here */
 
-      header += totlen;
       /* pass all additional spaces here */
       while(*header && ISSPACE(*header))
         header++;
@@ -247,7 +307,7 @@ CURLcode Curl_output_digest(struct connectdata *conn,
 #ifdef CURL_DOES_CONVERSIONS
   CURLcode rc;
 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
-   It converts digest text to ASCII so the MD5 will be correct for 
+   It converts digest text to ASCII so the MD5 will be correct for
    what ultimately goes over the network.
 */
 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
index 4a459e0123686ed7bfc5868b9c7e3ac6135983e5..94a8bdea07b304603467bb5f34ef4dd506cd5ea3 100644 (file)
@@ -60,7 +60,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46           \
  test1072 test1073 test1074 test1075 test1076 test1077 test1078 test1079   \
  test1080 test1081 test1082 test1083 test1084 test1085 test633 test634     \
  test635 test636 test637 test558 test559 test1086 test1087 test1088        \
- test1089 test1090 test1091 test1092 test1093 test1094
+ test1089 test1090 test1091 test1092 test1093 test1094 test1095
 
 filecheck:
        @mkdir test-place; \
diff --git a/tests/data/test1095 b/tests/data/test1095
new file mode 100644 (file)
index 0000000..d401b52
--- /dev/null
@@ -0,0 +1,83 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP Digest auth
+</keywords>
+</info>
+# Server-side
+<reply>
+<data>
+HTTP/1.1 401 Authorization Required swsclose\r
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2\r
+WWW-Authenticate: Digest realm="test \"this\" realm!!", nonce="1053604145"\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 26\r
+\r
+This is not the real page
+</data>
+
+# This is supposed to be returned when the server gets a
+# Authorization: Digest line passed-in from the client
+<data1000>
+HTTP/1.1 200 OK swsclose\r
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 23\r
+\r
+This IS the real page!
+</data1000>
+
+<datacheck>
+HTTP/1.1 401 Authorization Required swsclose\r
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2\r
+WWW-Authenticate: Digest realm="test \"this\" realm!!", nonce="1053604145"\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 26\r
+\r
+HTTP/1.1 200 OK swsclose\r
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2\r
+Content-Type: text/html; charset=iso-8859-1\r
+Content-Length: 23\r
+\r
+This IS the real page!
+</datacheck>
+
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+crypto
+</features>
+ <name>
+HTTP with Digest and realm with quoted quotes
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/1095 -u testuser:testpass --digest
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /1095 HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /1095 HTTP/1.1\r
+Authorization: Digest username="testuser", realm="test \"this\" realm!!", nonce="1053604145", uri="/1095", response="a1c7931ece9e8617bae2715045e4f49f"\r
+User-Agent: curl/7.10.5 (i686-pc-linux-gnu) libcurl/7.10.5 OpenSSL/0.9.7a ipv6 zlib/1.1.3\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+</protocol>
+</verify>
+</testcase>