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
This commit is contained in:
Daniel Stenberg
2011-10-20 13:05:09 +02:00
parent 4fa0166173
commit ff0a295cdb
3 changed files with 179 additions and 89 deletions

View File

@@ -731,95 +731,73 @@ CURLcode Curl_http_input_auth(struct connectdata *conn,
* *
*/ */
while(*start) {
#ifdef USE_HTTP_NEGOTIATE #ifdef USE_HTTP_NEGOTIATE
if(checkprefix("GSS-Negotiate", start) || if(checkprefix("GSS-Negotiate", start) ||
checkprefix("Negotiate", start)) { checkprefix("Negotiate", start)) {
int neg; int neg;
*availp |= CURLAUTH_GSSNEGOTIATE; *availp |= CURLAUTH_GSSNEGOTIATE;
authp->avail |= CURLAUTH_GSSNEGOTIATE; authp->avail |= CURLAUTH_GSSNEGOTIATE;
if(data->state.negotiate.state == GSS_AUTHSENT) { if(data->state.negotiate.state == GSS_AUTHSENT) {
/* if we sent GSS authentication in the outgoing request and we get this /* if we sent GSS authentication in the outgoing request and we get
back, we're in trouble */ this back, we're in trouble */
infof(data, "Authentication problem. Ignoring this.\n"); 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 {
data->state.authproblem = TRUE; data->state.authproblem = TRUE;
} }
} else {
} neg = Curl_input_negotiate(conn, (httpcode == 407)?TRUE:FALSE, start);
else if(neg == 0) {
#endif DEBUGASSERT(!data->req.newurl);
#ifdef USE_NTLM data->req.newurl = strdup(data->change.url);
/* NTLM support requires the SSL crypto libs */ if(!data->req.newurl)
if(checkprefix("NTLM", start)) { return CURLE_OUT_OF_MEMORY;
*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; data->state.authproblem = FALSE;
#ifdef NTLM_WB_ENABLED /* we received GSS auth info and we dealt with it fine */
if(authp->picked == CURLAUTH_NTLM_WB) { data->state.negotiate.state = GSS_AUTHRECV;
*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
} }
else { else {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE; data->state.authproblem = TRUE;
} }
} }
} }
else else
#endif #endif
#ifndef CURL_DISABLE_CRYPTO_AUTH #ifdef USE_NTLM
if(checkprefix("Digest", start)) { /* NTLM support requires the SSL crypto libs */
if((authp->avail & CURLAUTH_DIGEST) != 0) { if(checkprefix("NTLM", start)) {
infof(data, "Ignoring duplicate digest auth header.\n"); *availp |= CURLAUTH_NTLM;
} authp->avail |= CURLAUTH_NTLM;
else { if(authp->picked == CURLAUTH_NTLM ||
CURLdigest dig; authp->picked == CURLAUTH_NTLM_WB) {
*availp |= CURLAUTH_DIGEST; /* NTLM authentication is picked and activated */
authp->avail |= CURLAUTH_DIGEST; 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 /* Get the challenge-message which will be passed to
* authentication isn't activated yet, as we need to store the * ntlm_auth for generating the type 3 message later */
* incoming data from this header in case we are gonna use Digest. */ while(*start && ISSPACE(*start))
dig = Curl_input_digest(conn, (httpcode == 407)?TRUE:FALSE, start); start++;
if(checkprefix("NTLM", start)) {
if(CURLDIGEST_FINE != dig) { 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"); infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE; data->state.authproblem = TRUE;
} }
@@ -827,19 +805,51 @@ CURLcode Curl_http_input_auth(struct connectdata *conn,
} }
else else
#endif #endif
if(checkprefix("Basic", start)) { #ifndef CURL_DISABLE_CRYPTO_AUTH
*availp |= CURLAUTH_BASIC; if(checkprefix("Digest", start)) {
authp->avail |= CURLAUTH_BASIC; if((authp->avail & CURLAUTH_DIGEST) != 0) {
if(authp->picked == CURLAUTH_BASIC) { infof(data, "Ignoring duplicate digest auth header.\n");
/* We asked for Basic authentication but got a 40X back }
anyway, which basically means our name+password isn't else {
valid. */ CURLdigest dig;
authp->avail = CURLAUTH_NONE; *availp |= CURLAUTH_DIGEST;
infof(data, "Authentication problem. Ignoring this.\n"); authp->avail |= CURLAUTH_DIGEST;
data->state.authproblem = TRUE;
}
}
/* 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; return CURLE_OK;
} }

View File

@@ -73,8 +73,9 @@ test1094 test1095 test1096 test1097 test1098 test1099 test1100 test1101 \
test1102 test1103 test1104 test1105 test1106 test1107 test1108 test1109 \ test1102 test1103 test1104 test1105 test1106 test1107 test1108 test1109 \
test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117 \ test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117 \
test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 \ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 \
test1126 test1127 test1128 test1129 test1130 test1131 test1200 test1201 \ test1126 test1127 test1128 test1129 test1130 test1131 \
test1202 test1203 test1300 test1301 test1302 test1303 test1304 test1305 \ test1200 test1201 test1202 test1203 test1204 \
test1300 test1301 test1302 test1303 test1304 test1305 \
test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \ test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
test1314 \ test1314 \
test2000 test2001 test2002 test2003 test2004 test2000 test2001 test2002 test2003 test2004

79
tests/data/test1204 Normal file
View File

@@ -0,0 +1,79 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
HTTP Basic auth
--anyauth
</keywords>
</info>
# Server-side
<reply>
<data>
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
</data>
# This is supposed to be returned when the server gets the second request
<data1>
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!
</data1>
<datacheck>
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!
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP with WWW-Authenticate and multiple auths in a single line
</name>
<command>
http://%HOSTIP:%HTTPPORT/1204 -u testuser:testpass --anyauth
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
GET /1204 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /1204 HTTP/1.1
Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
</verify>
</testcase>