diff --git a/CHANGES b/CHANGES index e65da208f..9e3e6ed95 100644 --- 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 diff --git a/RELEASE-NOTES b/RELEASE-NOTES index bb76da697..a54f611cb 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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) diff --git a/lib/http_digest.c b/lib/http_digest.c index bab95e9de..1f452c7f5 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,6 +47,77 @@ /* 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) \ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 4a459e012..94a8bdea0 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -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 index 000000000..d401b52be --- /dev/null +++ b/tests/data/test1095 @@ -0,0 +1,83 @@ + + + +HTTP +HTTP GET +HTTP Digest auth + + +# Server-side + + +HTTP/1.1 401 Authorization Required swsclose +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Digest realm="test \"this\" realm!!", nonce="1053604145" +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 26 + +This is not the real page + + +# This is supposed to be returned when the server gets a +# Authorization: Digest line passed-in from the client + +HTTP/1.1 200 OK swsclose +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 + +This IS the real page! + + + +HTTP/1.1 401 Authorization Required swsclose +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Digest realm="test \"this\" realm!!", nonce="1053604145" +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 26 + +HTTP/1.1 200 OK swsclose +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 + +This IS the real page! + + + + +# Client-side + + +http + + +crypto + + +HTTP with Digest and realm with quoted quotes + + +http://%HOSTIP:%HTTPPORT/1095 -u testuser:testpass --digest + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /1095 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +GET /1095 HTTP/1.1 +Authorization: Digest username="testuser", realm="test \"this\" realm!!", nonce="1053604145", uri="/1095", response="a1c7931ece9e8617bae2715045e4f49f" +User-Agent: curl/7.10.5 (i686-pc-linux-gnu) libcurl/7.10.5 OpenSSL/0.9.7a ipv6 zlib/1.1.3 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +