From ff0a295cdbc9cf95db0c364ede5cca4063ba2882 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 20 Oct 2011 13:05:09 +0200 Subject: [PATCH] Curl_http_input_auth: handle multiple auths in WWW-Authenticate The fix is pretty much the one Nick Zitzmann provided, just edited to do the right indent levels and with test case 1204 added to verify the fix. Bug: http://curl.haxx.se/mail/lib-2011-10/0190.html Reported by: Nick Zitzmann --- lib/http.c | 184 ++++++++++++++++++++++------------------- tests/data/Makefile.am | 5 +- tests/data/test1204 | 79 ++++++++++++++++++ 3 files changed, 179 insertions(+), 89 deletions(-) create mode 100644 tests/data/test1204 diff --git a/lib/http.c b/lib/http.c index 5b30f8aa0..e41cb7af3 100644 --- a/lib/http.c +++ b/lib/http.c @@ -731,95 +731,73 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, * */ + while(*start) { #ifdef USE_HTTP_NEGOTIATE - if(checkprefix("GSS-Negotiate", start) || - checkprefix("Negotiate", start)) { - int neg; - *availp |= CURLAUTH_GSSNEGOTIATE; - authp->avail |= CURLAUTH_GSSNEGOTIATE; + if(checkprefix("GSS-Negotiate", start) || + checkprefix("Negotiate", start)) { + int neg; + *availp |= CURLAUTH_GSSNEGOTIATE; + authp->avail |= CURLAUTH_GSSNEGOTIATE; - if(data->state.negotiate.state == GSS_AUTHSENT) { - /* if we sent GSS authentication in the outgoing request and we get this - back, we're in trouble */ - infof(data, "Authentication problem. Ignoring this.\n"); - data->state.authproblem = TRUE; - } - else { - neg = Curl_input_negotiate(conn, (httpcode == 407)?TRUE:FALSE, start); - if(neg == 0) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->change.url); - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - data->state.authproblem = FALSE; - /* we received GSS auth info and we dealt with it fine */ - data->state.negotiate.state = GSS_AUTHRECV; - } - else { + if(data->state.negotiate.state == GSS_AUTHSENT) { + /* if we sent GSS authentication in the outgoing request and we get + this back, we're in trouble */ + infof(data, "Authentication problem. Ignoring this.\n"); data->state.authproblem = TRUE; } - } - } - else -#endif -#ifdef USE_NTLM - /* NTLM support requires the SSL crypto libs */ - if(checkprefix("NTLM", start)) { - *availp |= CURLAUTH_NTLM; - authp->avail |= CURLAUTH_NTLM; - if(authp->picked == CURLAUTH_NTLM || - authp->picked == CURLAUTH_NTLM_WB) { - /* NTLM authentication is picked and activated */ - CURLcode ntlm = - Curl_input_ntlm(conn, (httpcode == 407)?TRUE:FALSE, start); - if(CURLE_OK == ntlm) { + else { + neg = Curl_input_negotiate(conn, (httpcode == 407)?TRUE:FALSE, start); + if(neg == 0) { + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->change.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; data->state.authproblem = FALSE; -#ifdef NTLM_WB_ENABLED - if(authp->picked == CURLAUTH_NTLM_WB) { - *availp &= ~CURLAUTH_NTLM; - authp->avail &= ~CURLAUTH_NTLM; - *availp |= CURLAUTH_NTLM_WB; - authp->avail |= CURLAUTH_NTLM_WB; - - /* Get the challenge-message which will be passed to - * ntlm_auth for generating the type 3 message later */ - while(*start && ISSPACE(*start)) - start++; - if(checkprefix("NTLM", start)) { - start += strlen("NTLM"); - while(*start && ISSPACE(*start)) - start++; - if(*start) - if((conn->challenge_header = strdup(start)) == NULL) - return CURLE_OUT_OF_MEMORY; - } - } -#endif + /* we received GSS auth info and we dealt with it fine */ + data->state.negotiate.state = GSS_AUTHRECV; } else { - infof(data, "Authentication problem. Ignoring this.\n"); data->state.authproblem = TRUE; } } } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(checkprefix("Digest", start)) { - if((authp->avail & CURLAUTH_DIGEST) != 0) { - infof(data, "Ignoring duplicate digest auth header.\n"); - } - else { - CURLdigest dig; - *availp |= CURLAUTH_DIGEST; - authp->avail |= CURLAUTH_DIGEST; +#ifdef USE_NTLM + /* NTLM support requires the SSL crypto libs */ + if(checkprefix("NTLM", start)) { + *availp |= CURLAUTH_NTLM; + authp->avail |= CURLAUTH_NTLM; + if(authp->picked == CURLAUTH_NTLM || + authp->picked == CURLAUTH_NTLM_WB) { + /* NTLM authentication is picked and activated */ + CURLcode ntlm = + Curl_input_ntlm(conn, (httpcode == 407)?TRUE:FALSE, start); + if(CURLE_OK == ntlm) { + data->state.authproblem = FALSE; +#ifdef NTLM_WB_ENABLED + if(authp->picked == CURLAUTH_NTLM_WB) { + *availp &= ~CURLAUTH_NTLM; + authp->avail &= ~CURLAUTH_NTLM; + *availp |= CURLAUTH_NTLM_WB; + authp->avail |= CURLAUTH_NTLM_WB; - /* We call this function on input Digest headers even if Digest - * authentication isn't activated yet, as we need to store the - * incoming data from this header in case we are gonna use Digest. */ - dig = Curl_input_digest(conn, (httpcode == 407)?TRUE:FALSE, start); - - if(CURLDIGEST_FINE != dig) { + /* Get the challenge-message which will be passed to + * ntlm_auth for generating the type 3 message later */ + while(*start && ISSPACE(*start)) + start++; + if(checkprefix("NTLM", start)) { + start += strlen("NTLM"); + while(*start && ISSPACE(*start)) + start++; + if(*start) + if((conn->challenge_header = strdup(start)) == NULL) + return CURLE_OUT_OF_MEMORY; + } + } +#endif + } + else { infof(data, "Authentication problem. Ignoring this.\n"); data->state.authproblem = TRUE; } @@ -827,19 +805,51 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, } else #endif - if(checkprefix("Basic", start)) { - *availp |= CURLAUTH_BASIC; - authp->avail |= CURLAUTH_BASIC; - if(authp->picked == CURLAUTH_BASIC) { - /* We asked for Basic authentication but got a 40X back - anyway, which basically means our name+password isn't - valid. */ - authp->avail = CURLAUTH_NONE; - infof(data, "Authentication problem. Ignoring this.\n"); - data->state.authproblem = TRUE; - } - } +#ifndef CURL_DISABLE_CRYPTO_AUTH + if(checkprefix("Digest", start)) { + if((authp->avail & CURLAUTH_DIGEST) != 0) { + infof(data, "Ignoring duplicate digest auth header.\n"); + } + else { + CURLdigest dig; + *availp |= CURLAUTH_DIGEST; + authp->avail |= CURLAUTH_DIGEST; + /* We call this function on input Digest headers even if Digest + * authentication isn't activated yet, as we need to store the + * incoming data from this header in case we are gonna use + * Digest. */ + dig = Curl_input_digest(conn, (httpcode == 407)?TRUE:FALSE, start); + + if(CURLDIGEST_FINE != dig) { + infof(data, "Authentication problem. Ignoring this.\n"); + data->state.authproblem = TRUE; + } + } + } + else +#endif + if(checkprefix("Basic", start)) { + *availp |= CURLAUTH_BASIC; + authp->avail |= CURLAUTH_BASIC; + if(authp->picked == CURLAUTH_BASIC) { + /* We asked for Basic authentication but got a 40X back + anyway, which basically means our name+password isn't + valid. */ + authp->avail = CURLAUTH_NONE; + infof(data, "Authentication problem. Ignoring this.\n"); + data->state.authproblem = TRUE; + } + } + + /* there may be multiple methods on one line, so keep reading */ + while(*start && *start != ',') /* read up to the next comma */ + start++; + if(*start == ',') /* if we're on a comma, skip it */ + start++; + while(*start && ISSPACE(*start)) + start++; + } return CURLE_OK; } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 2030e0bca..8471736ca 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -73,8 +73,9 @@ test1094 test1095 test1096 test1097 test1098 test1099 test1100 test1101 \ test1102 test1103 test1104 test1105 test1106 test1107 test1108 test1109 \ test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117 \ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 \ -test1126 test1127 test1128 test1129 test1130 test1131 test1200 test1201 \ -test1202 test1203 test1300 test1301 test1302 test1303 test1304 test1305 \ +test1126 test1127 test1128 test1129 test1130 test1131 \ +test1200 test1201 test1202 test1203 test1204 \ +test1300 test1301 test1302 test1303 test1304 test1305 \ test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \ test1314 \ test2000 test2001 test2002 test2003 test2004 diff --git a/tests/data/test1204 b/tests/data/test1204 new file mode 100644 index 000000000..02502fb8e --- /dev/null +++ b/tests/data/test1204 @@ -0,0 +1,79 @@ + + + +HTTP +HTTP GET +HTTP Basic auth +--anyauth + + +# Server-side + + +HTTP/1.1 401 Authorization Required swsbounce +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: X-MobileMe-AuthToken realm="Newcastle", Basic realm="fun fun fun" +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 the second request + +HTTP/1.1 200 OK +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 swsbounce +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: X-MobileMe-AuthToken realm="Newcastle", Basic realm="fun fun fun" +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 26 + +HTTP/1.1 200 OK +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 + + +HTTP with WWW-Authenticate and multiple auths in a single line + + +http://%HOSTIP:%HTTPPORT/1204 -u testuser:testpass --anyauth + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /1204 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +GET /1204 HTTP/1.1 +Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M= +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +