General HTTP authentication cleanup and fixes

This commit is contained in:
Daniel Stenberg 2004-05-04 07:52:53 +00:00
parent e7ee1ccf45
commit fc6eff13b5
20 changed files with 636 additions and 309 deletions

13
CHANGES
View File

@ -6,6 +6,19 @@
Changelog Changelog
Daniel (3 May 2004)
- Rewritten HTTP authentication code. The previous code could not properly
deal with the added test cases 167, 168 and 169. I've now rewritten the code
to better separate host and proxy authentication and not re-use the same
variables as much as before as it proved non working in the more involved
cases. All the current tests run OK now, and so do the new ones. The curl
tool got a new option named --proxy-digest to enable HTTP Digest
authentication with the proxy. I also made the library support it.
- Gisle Vanem made the LDAP code work with wldap32.dll as supplied with
Win-98/ME/2000/XP, so no extra .dlls are required when curl/libcurl is used
on these Windows versions.
Daniel (30 April 2004) Daniel (30 April 2004)
- runtests.pl now scans the valgrind log for valgrind-detected memory leaks - runtests.pl now scans the valgrind log for valgrind-detected memory leaks
after each test case if valgrind was found and used. after each test case if valgrind was found and used.

View File

@ -2,12 +2,14 @@ Curl and libcurl 7.12.0.
Public curl release number: 81 Public curl release number: 81
Releases counted from the very beginning: 108 Releases counted from the very beginning: 108
Available command line options: 94 Available command line options: 95
Available curl_easy_setopt() options: 113 Available curl_easy_setopt() options: 113
Number of public functions in libcurl: 35 Number of public functions in libcurl: 35
This release includes the following changes: This release includes the following changes:
o curl --proxy-digest is a new command line option
o the Windows version of libcurl can use wldap32.dll for LDAP
o curl_easy_strerror(), curl_multi_strerror() and curl_share_strerror() o curl_easy_strerror(), curl_multi_strerror() and curl_share_strerror()
o IPv6-enabled Windows hosts now resolves names threaded/asynch as well o IPv6-enabled Windows hosts now resolves names threaded/asynch as well
o configure --with-libidn can be used to point out the root dir of a libidn o configure --with-libidn can be used to point out the root dir of a libidn
@ -16,6 +18,7 @@ This release includes the following changes:
This release includes the following bugfixes: This release includes the following bugfixes:
o HTTP Digest authentication with the proxy works
o mulipart formposting with -F and file names with spaces work again o mulipart formposting with -F and file names with spaces work again
o curl_easy_duphandle() now works when ares-enabled o curl_easy_duphandle() now works when ares-enabled
o HTTP Digest authentication works a lot more like the RFC says o HTTP Digest authentication works a lot more like the RFC says

View File

@ -103,8 +103,6 @@
#include "memdebug.h" #include "memdebug.h"
#endif #endif
static CURLcode Curl_output_basic_proxy(struct connectdata *conn);
/* /*
* checkheaders() checks the linked list of custom HTTP headers for a * checkheaders() checks the linked list of custom HTTP headers for a
* particular header (prefix). * particular header (prefix).
@ -124,23 +122,39 @@ static char *checkheaders(struct SessionHandle *data, const char *thisheader)
} }
/* /*
* Curl_output_basic() sets up an Authorization: header for HTTP Basic * Curl_output_basic() sets up an Authorization: header (or the proxy version)
* authentication. It uses the conn->user, conn->passwd fields for it. * for HTTP Basic authentication.
* *
* Returns CURLcode. * Returns CURLcode.
*/ */
static CURLcode Curl_output_basic(struct connectdata *conn) static CURLcode Curl_output_basic(struct connectdata *conn, bool proxy)
{ {
char *authorization; char *authorization;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
char **userp;
char *user;
char *pwd;
sprintf(data->state.buffer, "%s:%s", conn->user, conn->passwd); if(proxy) {
if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer), userp = &conn->allocptr.proxyuserpwd;
user = conn->proxyuser;
pwd = conn->proxypasswd;
}
else {
userp = &conn->allocptr.userpwd;
user = conn->user;
pwd = conn->passwd;
}
sprintf(data->state.buffer, "%s:%s", user, pwd);
if(Curl_base64_encode(data->state.buffer,
strlen(data->state.buffer),
&authorization) > 0) { &authorization) > 0) {
if(conn->allocptr.userpwd) if(*userp)
free(conn->allocptr.userpwd); free(*userp);
conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012", *userp = aprintf( "%sAuthorization: Basic %s\015\012",
authorization); proxy?"Proxy-":"",
authorization);
free(authorization); free(authorization);
} }
else else
@ -148,61 +162,74 @@ static CURLcode Curl_output_basic(struct connectdata *conn)
return CURLE_OK; return CURLE_OK;
} }
/* /* pickoneauth() selects the most favourable authentication method from the
* Curl_output_basic_proxy() sets up a proxy-Authorization: header for HTTP * ones available and the ones we want.
* Basic proxy authentication. It uses the conn->proxyuser and
* conn->proxypasswd fields for it.
* *
* Returns CURLcode. * return TRUE if one was picked
*/ */
static CURLcode Curl_output_basic_proxy(struct connectdata *conn) static bool pickoneauth(struct auth *pick)
{ {
char *authorization; bool picked;
struct SessionHandle *data=conn->data; if(pick->avail) {
/* only deal with authentication we want */
long avail = pick->avail & pick->want;
picked = TRUE;
sprintf(data->state.buffer, "%s:%s",
conn->proxyuser, conn->proxypasswd);
if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
&authorization) > 0) {
Curl_safefree(conn->allocptr.proxyuserpwd);
conn->allocptr.proxyuserpwd =
aprintf("Proxy-authorization: Basic %s\015\012", authorization);
free(authorization);
}
else
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
/*
* Curl_http_auth_act() checks what authentication methods that are available
* and decides which one (if any) to use. It will set 'newurl' if an auth
* metod was picked.
*/
void Curl_http_auth_act(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
if(data->state.authavail) {
/* The order of these checks is highly relevant, as this will be the order /* The order of these checks is highly relevant, as this will be the order
of preference in case of the existance of multiple accepted types. */ of preference in case of the existance of multiple accepted types. */
if(data->state.authavail & CURLAUTH_GSSNEGOTIATE) if(avail & CURLAUTH_GSSNEGOTIATE)
data->state.authwant = CURLAUTH_GSSNEGOTIATE; pick->picked = CURLAUTH_GSSNEGOTIATE;
else if(data->state.authavail & CURLAUTH_DIGEST) else if(avail & CURLAUTH_DIGEST)
data->state.authwant = CURLAUTH_DIGEST; pick->picked = CURLAUTH_DIGEST;
else if(data->state.authavail & CURLAUTH_NTLM) else if(avail & CURLAUTH_NTLM)
data->state.authwant = CURLAUTH_NTLM; pick->picked = CURLAUTH_NTLM;
else if(data->state.authavail & CURLAUTH_BASIC) else if(avail & CURLAUTH_BASIC)
data->state.authwant = CURLAUTH_BASIC; pick->picked = CURLAUTH_BASIC;
else else {
data->state.authwant = CURLAUTH_NONE; /* clear it */ pick->picked = CURLAUTH_NONE; /* none was picked clear it */
picked = FALSE;
if(data->state.authwant) }
conn->newurl = strdup(data->change.url); /* clone URL */ pick->avail = CURLAUTH_NONE; /* clear it here */
data->state.authavail = CURLAUTH_NONE; /* clear it here */
} }
else if(!data->state.authdone && (data->info.httpcode < 400)) { else
return FALSE;
return picked;
}
/*
* Curl_http_auth_act() gets called when a all HTTP headers have been received
* and it checks what authentication methods that are available and decides
* which one (if any) to use. It will set 'newurl' if an auth metod was
* picked.
*/
CURLcode Curl_http_auth_act(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
bool pickhost;
bool pickproxy;
CURLcode code = CURLE_OK;
if(data->state.authproblem)
return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
if(conn->bits.user_passwd) {
pickhost = pickoneauth(&data->state.authhost);
if(!pickhost && (conn->keep.httpcode == 401))
data->state.authproblem = TRUE;
}
if(conn->bits.proxy_user_passwd) {
pickproxy = pickoneauth(&data->state.authproxy);
if(!pickproxy && (conn->keep.httpcode == 407))
data->state.authproblem = TRUE;
}
if(pickhost || pickproxy)
conn->newurl = strdup(data->change.url); /* clone URL */
else if((data->info.httpcode < 400) &&
(!data->state.authhost.done)) {
/* no (known) authentication available, /* no (known) authentication available,
authentication is not "done" yet and authentication is not "done" yet and
no authentication seems to be required and no authentication seems to be required and
@ -210,23 +237,34 @@ void Curl_http_auth_act(struct connectdata *conn)
if((data->set.httpreq != HTTPREQ_GET) && if((data->set.httpreq != HTTPREQ_GET) &&
(data->set.httpreq != HTTPREQ_HEAD)) { (data->set.httpreq != HTTPREQ_HEAD)) {
conn->newurl = strdup(data->change.url); /* clone URL */ conn->newurl = strdup(data->change.url); /* clone URL */
data->state.authdone = TRUE; data->state.authhost.done = TRUE;
} }
} }
if (Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d",
conn->keep.httpcode);
code = CURLE_HTTP_RETURNED_ERROR;
}
return code;
} }
/** /**
* http_auth_headers() setups the authentication headers for the host/proxy * Curl_http_output_auth() setups the authentication headers for the
* and the correct authentication method. conn->data->state.authdone is set to * host/proxy and the correct authentication
* TRUE when authentication is done. * method. conn->data->state.authdone is set to TRUE when authentication is
* done.
* *
* @param conn all information about the current connection * @param conn all information about the current connection
* *
* Returns CURLcode * Returns CURLcode
*/ */
static CURLcode http_auth_headers(struct connectdata *conn, static CURLcode
char *request, Curl_http_output_auth(struct connectdata *conn,
char *path) char *request,
char *path,
bool proxytunnel) /* TRUE if this is the request setting
up the proxy tunnel */
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
@ -234,19 +272,29 @@ static CURLcode http_auth_headers(struct connectdata *conn,
curlassert(data); curlassert(data);
if(!data->state.authstage) { if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
if(conn->bits.httpproxy && conn->bits.proxy_user_passwd) { conn->bits.user_passwd)
data->state.authdone = FALSE; /* continue please */ ;
Curl_http_auth_stage(data, 407); else {
} data->state.authhost.done = TRUE;
else if(conn->bits.user_passwd) { data->state.authproxy.done = TRUE;
data->state.authdone = FALSE; return CURLE_OK; /* no authentication with no user or password */
Curl_http_auth_stage(data, 401); }
}
else { if(data->state.authhost.want &&
data->state.authdone = TRUE; !data->state.authhost.picked) {
return CURLE_OK; /* no authentication with no user or password */ /* The app has selected one or more methods, but none has been picked
} so far by a server round-trip. Then we set the picked one to the
want one, and if this is one single bit it'll be used instantly. */
data->state.authhost.picked = data->state.authhost.want;
}
if(data->state.authproxy.want &&
!data->state.authproxy.picked) {
/* The app has selected one or more methods, but none has been picked
so far by a server round-trip. Then we set the picked one to the
want one, and if this is one single bit it'll be used instantly. */
data->state.authproxy.picked = data->state.authproxy.want;
} }
/* To prevent the user+password to get sent to other than the original /* To prevent the user+password to get sent to other than the original
@ -256,10 +304,11 @@ static CURLcode http_auth_headers(struct connectdata *conn,
curl_strequal(data->state.auth_host, conn->host.name) || curl_strequal(data->state.auth_host, conn->host.name) ||
data->set.http_disable_hostname_check_before_authentication) { data->set.http_disable_hostname_check_before_authentication) {
/* Send proxy authentication header if needed */ /* Send proxy authentication header if needed */
if (data->state.authstage == 407) { if (conn->bits.httpproxy &&
(data->set.tunnel_thru_httpproxy == proxytunnel)) {
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
if(data->state.authwant == CURLAUTH_NTLM) { if(data->state.authproxy.want == CURLAUTH_NTLM) {
auth=(char *)"NTLM"; auth=(char *)"NTLM";
result = Curl_output_ntlm(conn, TRUE); result = Curl_output_ntlm(conn, TRUE);
if(result) if(result)
@ -267,39 +316,52 @@ static CURLcode http_auth_headers(struct connectdata *conn,
} }
else else
#endif #endif
if(data->state.authwant == CURLAUTH_BASIC) { if(data->state.authproxy.want == CURLAUTH_BASIC) {
/* Basic */ /* Basic */
if(conn->bits.proxy_user_passwd && if(conn->bits.proxy_user_passwd &&
!checkheaders(data, "Proxy-authorization:")) { !checkheaders(data, "Proxy-authorization:")) {
auth=(char *)"Basic"; auth=(char *)"Basic";
result = Curl_output_basic_proxy(conn); result = Curl_output_basic(conn, TRUE);
if(result) if(result)
return result; return result;
} }
data->state.authdone = TRUE; data->state.authproxy.done = TRUE;
/* Switch to web authentication after proxy authentication is done */
Curl_http_auth_stage(data, 401);
} }
else if(data->state.authproxy.want == CURLAUTH_DIGEST) {
auth=(char *)"Digest";
result = Curl_output_digest(conn,
TRUE, /* proxy */
(unsigned char *)request,
(unsigned char *)path);
if(result)
return result;
}
infof(data, "Proxy auth using %s with user '%s'\n", infof(data, "Proxy auth using %s with user '%s'\n",
auth, conn->proxyuser?conn->proxyuser:""); auth, conn->proxyuser?conn->proxyuser:"");
} }
else
/* we have no proxy so let's pretend we're done authenticating
with it */
data->state.authproxy.done = TRUE;
/* Send web authentication header if needed */ /* Send web authentication header if needed */
if (data->state.authstage == 401) { {
auth = NULL; auth = NULL;
#ifdef HAVE_GSSAPI #ifdef HAVE_GSSAPI
if((data->state.authwant == CURLAUTH_GSSNEGOTIATE) && if((data->state.authhost.want == CURLAUTH_GSSNEGOTIATE) &&
data->state.negotiate.context && data->state.negotiate.context &&
!GSS_ERROR(data->state.negotiate.status)) { !GSS_ERROR(data->state.negotiate.status)) {
auth=(char *)"GSS-Negotiate"; auth=(char *)"GSS-Negotiate";
result = Curl_output_negotiate(conn); result = Curl_output_negotiate(conn);
if (result) if (result)
return result; return result;
data->state.authdone = TRUE; data->state.authhost.done = TRUE;
} }
else else
#endif #endif
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
if(data->state.authwant == CURLAUTH_NTLM) { if(data->state.authhost.picked == CURLAUTH_NTLM) {
auth=(char *)"NTLM"; auth=(char *)"NTLM";
result = Curl_output_ntlm(conn, FALSE); result = Curl_output_ntlm(conn, FALSE);
if(result) if(result)
@ -308,26 +370,25 @@ static CURLcode http_auth_headers(struct connectdata *conn,
else else
#endif #endif
{ {
if((data->state.authwant == CURLAUTH_DIGEST) && if(data->state.authhost.picked == CURLAUTH_DIGEST) {
data->state.digest.nonce) {
auth=(char *)"Digest"; auth=(char *)"Digest";
result = Curl_output_digest(conn, result = Curl_output_digest(conn,
FALSE, /* not a proxy */
(unsigned char *)request, (unsigned char *)request,
(unsigned char *)path); (unsigned char *)path);
if(result) if(result)
return result; return result;
data->state.authdone = TRUE;
} }
else if(data->state.authwant == CURLAUTH_BASIC) {/* Basic */ else if(data->state.authhost.picked == CURLAUTH_BASIC) {
if(conn->bits.user_passwd && if(conn->bits.user_passwd &&
!checkheaders(data, "Authorization:")) { !checkheaders(data, "Authorization:")) {
auth=(char *)"Basic"; auth=(char *)"Basic";
result = Curl_output_basic(conn); result = Curl_output_basic(conn, FALSE);
if(result) if(result)
return result; return result;
} }
/* basic is always ready */ /* basic is always ready */
data->state.authdone = TRUE; data->state.authhost.done = TRUE;
} }
} }
if(auth) if(auth)
@ -336,21 +397,21 @@ static CURLcode http_auth_headers(struct connectdata *conn,
} }
} }
else else
data->state.authdone = TRUE; data->state.authhost.done = TRUE;
return result; return result;
} }
/* /*
* Curl_http_auth() deals with Proxy-Authenticate: and WWW-Authenticate: * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
* headers. They are dealt with both in the transfer.c main loop and in the * headers. They are dealt with both in the transfer.c main loop and in the
* proxy CONNECT loop. * proxy CONNECT loop.
*/ */
CURLcode Curl_http_auth(struct connectdata *conn, CURLcode Curl_http_input_auth(struct connectdata *conn,
int httpcode, int httpcode,
char *header) /* pointing to the first non-space */ char *header) /* the first non-space */
{ {
/* /*
* This resource requires authentication * This resource requires authentication
@ -359,23 +420,18 @@ CURLcode Curl_http_auth(struct connectdata *conn,
long *availp; long *availp;
char *start; char *start;
struct auth *authp;
if (httpcode == 407) { if (httpcode == 407) {
start = header+strlen("Proxy-authenticate:"); start = header+strlen("Proxy-authenticate:");
availp = &data->info.proxyauthavail; availp = &data->info.proxyauthavail;
authp = &data->state.authproxy;
} }
else { else {
start = header+strlen("WWW-Authenticate:"); start = header+strlen("WWW-Authenticate:");
availp = &data->info.httpauthavail; availp = &data->info.httpauthavail;
authp = &data->state.authhost;
} }
/*
* Switch from proxy to web authentication and back if needed
*/
if (httpcode == 407 && data->state.authstage != 407)
Curl_http_auth_stage(data, 407);
else if (httpcode == 401 && data->state.authstage != 401)
Curl_http_auth_stage(data, 401);
/* pass all white spaces */ /* pass all white spaces */
while(*start && isspace((int)*start)) while(*start && isspace((int)*start))
@ -394,7 +450,8 @@ CURLcode Curl_http_auth(struct connectdata *conn,
if (checkprefix("GSS-Negotiate", start) || if (checkprefix("GSS-Negotiate", start) ||
checkprefix("Negotiate", start)) { checkprefix("Negotiate", start)) {
*availp |= CURLAUTH_GSSNEGOTIATE; *availp |= CURLAUTH_GSSNEGOTIATE;
if(data->state.authwant == CURLAUTH_GSSNEGOTIATE) { authp->avail |= CURLAUTH_GSSNEGOTIATE;
if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
/* if exactly this is wanted, go */ /* if exactly this is wanted, go */
int neg = Curl_input_negotiate(conn, start); int neg = Curl_input_negotiate(conn, start);
if (neg == 0) { if (neg == 0) {
@ -406,9 +463,6 @@ CURLcode Curl_http_auth(struct connectdata *conn,
data->state.authproblem = TRUE; data->state.authproblem = TRUE;
} }
} }
else
if(data->state.authwant & CURLAUTH_GSSNEGOTIATE)
data->state.authavail |= CURLAUTH_GSSNEGOTIATE;
} }
else else
#endif #endif
@ -416,76 +470,50 @@ CURLcode Curl_http_auth(struct connectdata *conn,
/* NTLM support requires the SSL crypto libs */ /* NTLM support requires the SSL crypto libs */
if(checkprefix("NTLM", start)) { if(checkprefix("NTLM", start)) {
*availp |= CURLAUTH_NTLM; *availp |= CURLAUTH_NTLM;
if(data->state.authwant == CURLAUTH_NTLM) { authp->avail |= CURLAUTH_NTLM;
/* NTLM authentication is activated */ if(authp->picked == CURLAUTH_NTLM) {
/* NTLM authentication is picked and activated */
CURLntlm ntlm = CURLntlm ntlm =
Curl_input_ntlm(conn, (bool)(httpcode == 407), start); Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
if(CURLNTLM_BAD != ntlm) { if(CURLNTLM_BAD != ntlm)
conn->newurl = strdup(data->change.url); /* clone string */ data->state.authproblem = FALSE;
data->state.authproblem = (conn->newurl == NULL);
}
else { else {
infof(data, "Authentication problem. Ignoring this.\n"); infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE; data->state.authproblem = TRUE;
} }
} }
else
if(data->state.authwant & CURLAUTH_NTLM)
data->state.authavail |= CURLAUTH_NTLM;
} }
else else
#endif #endif
if(checkprefix("Digest", start)) { if(checkprefix("Digest", start)) {
CURLdigest dig;
*availp |= CURLAUTH_DIGEST; *availp |= CURLAUTH_DIGEST;
if(data->state.authwant == CURLAUTH_DIGEST) { authp->avail |= CURLAUTH_DIGEST;
/* Digest authentication is activated */
CURLdigest dig = Curl_input_digest(conn, start); /* 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, (bool)(httpcode == 407), start);
if(CURLDIGEST_FINE == dig) { if(CURLDIGEST_FINE != dig) {
/* We act on it. Store our new url, which happens to be
the same one we already use! */
conn->newurl = strdup(data->change.url); /* clone string */
data->state.authproblem = (conn->newurl == NULL);
}
else {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
else
if(data->state.authwant & CURLAUTH_DIGEST) {
/* We don't know if Digest is what we're gonna use, but we
call this function anyway to store the digest data that
is provided on this line, to skip the extra round-trip
we need to do otherwise. We must sure to free this
data! */
Curl_input_digest(conn, start);
data->state.authavail |= CURLAUTH_DIGEST;
}
}
else if(checkprefix("Basic", start)) {
*availp |= CURLAUTH_BASIC;
if((data->state.authwant == CURLAUTH_BASIC) &&
(httpcode == data->state.authstage)) {
/* We asked for Basic authentication but got a 40X back
anyway, which basicly means our name+password isn't
valid. */
data->state.authavail = CURLAUTH_NONE;
infof(data, "Authentication problem. Ignoring this.\n"); infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE; data->state.authproblem = TRUE;
} }
else if(data->state.authwant & CURLAUTH_BASIC) { }
data->state.authavail |= CURLAUTH_BASIC; else if(checkprefix("Basic", start)) {
} else { *availp |= CURLAUTH_BASIC;
/* authp->avail |= CURLAUTH_BASIC;
** We asked for something besides basic but got if(authp->picked == CURLAUTH_BASIC) {
** Basic anyway. This is no good. /* We asked for Basic authentication but got a 40X back
*/ anyway, which basicly means our name+password isn't
infof(data, "Server expects Basic auth, but we're doing something else.\n"); valid. */
data->state.authproblem = TRUE; authp->avail = CURLAUTH_NONE;
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
} }
} }
return CURLE_OK; return CURLE_OK;
} }
@ -562,15 +590,16 @@ int Curl_http_should_fail(struct connectdata *conn)
infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem); infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem);
#endif #endif
if (data->state.authstage &&
(data->state.authstage == k->httpcode))
return (data->state.authdone || data->state.authproblem);
/* /*
** Either we're not authenticating, or we're supposed to ** Either we're not authenticating, or we're supposed to
** be authenticating something else. This is an error. ** be authenticating something else. This is an error.
*/ */
return 1; if((k->httpcode == 401) && !conn->bits.user_passwd)
return TRUE;
if((k->httpcode == 407) && !conn->bits.proxy_user_passwd)
return TRUE;
return data->state.authproblem;
} }
/* /*
@ -876,9 +905,9 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
char *hostname, char *hostname,
int remote_port) int remote_port)
{ {
int httpcode=0;
int subversion=0; int subversion=0;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
struct Curl_transfer_keeper *k = &conn->keep;
CURLcode result; CURLcode result;
int res; int res;
@ -916,7 +945,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
/* Setup the proxy-authorization header, if any */ /* Setup the proxy-authorization header, if any */
result = http_auth_headers(conn, (char *)"CONNECT", host_port); result = Curl_http_output_auth(conn, (char *)"CONNECT", host_port, TRUE);
if(CURLE_OK == result) { if(CURLE_OK == result) {
/* OK, now send the connect request to the proxy */ /* OK, now send the connect request to the proxy */
@ -1039,18 +1068,18 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
letter = line_start[perline]; letter = line_start[perline];
line_start[perline]=0; /* zero terminate the buffer */ line_start[perline]=0; /* zero terminate the buffer */
if((checkprefix("WWW-Authenticate:", line_start) && if((checkprefix("WWW-Authenticate:", line_start) &&
(401 == httpcode)) || (401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", line_start) && (checkprefix("Proxy-authenticate:", line_start) &&
(407 == httpcode))) { (407 == k->httpcode))) {
result = Curl_http_auth(conn, httpcode, line_start); result = Curl_http_input_auth(conn, k->httpcode, line_start);
if(result) if(result)
return result; return result;
} }
else if(2 == sscanf(line_start, "HTTP/1.%d %d", else if(2 == sscanf(line_start, "HTTP/1.%d %d",
&subversion, &subversion,
&httpcode)) { &k->httpcode)) {
/* store the HTTP code */ /* store the HTTP code */
data->info.httpproxycode = httpcode; data->info.httpproxycode = k->httpcode;
} }
/* put back the letter we blanked out before */ /* put back the letter we blanked out before */
line_start[perline]= letter; line_start[perline]= letter;
@ -1073,8 +1102,9 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
} while(conn->newurl); } while(conn->newurl);
if(200 != httpcode) { if(200 != k->httpcode) {
failf(data, "Received HTTP code %d from proxy after CONNECT", httpcode); failf(data, "Received HTTP code %d from proxy after CONNECT",
k->httpcode);
return CURLE_RECV_ERROR; return CURLE_RECV_ERROR;
} }
@ -1084,7 +1114,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
Curl_safefree(conn->allocptr.proxyuserpwd); Curl_safefree(conn->allocptr.proxyuserpwd);
conn->allocptr.proxyuserpwd = NULL; conn->allocptr.proxyuserpwd = NULL;
Curl_http_auth_stage(data, 401); /* move on to the host auth */ data->state.authproxy.done = TRUE;
infof (data, "Proxy replied OK to CONNECT request\n"); infof (data, "Proxy replied OK to CONNECT request\n");
return CURLE_OK; return CURLE_OK;
@ -1189,24 +1219,6 @@ CURLcode Curl_http_done(struct connectdata *conn)
return CURLE_OK; return CURLE_OK;
} }
/*
* Curl_http_auth_stage() sets the "authentication stage" - which is 407 for
* proxy authentication or 401 for host authentication.
*/
void Curl_http_auth_stage(struct SessionHandle *data,
int stage)
{
curlassert((stage == 401) || (stage == 407));
/* We set none, one or more bits for which authentication types we accept
for this stage. */
data->state.authwant = (stage == 401)?
data->set.httpauth:data->set.proxyauth;
data->state.authstage = stage;
data->state.authavail = CURLAUTH_NONE; /* no type available yet */
}
/* /*
* Curl_http() gets called from the generic Curl_do() function when a HTTP * Curl_http() gets called from the generic Curl_do() function when a HTTP
* request is to be performed. This creates and sends a propperly constructed * request is to be performed. This creates and sends a propperly constructed
@ -1284,11 +1296,12 @@ CURLcode Curl_http(struct connectdata *conn)
} }
/* setup the authentication headers */ /* setup the authentication headers */
result = http_auth_headers(conn, request, ppath); result = Curl_http_output_auth(conn, request, ppath, FALSE);
if(result) if(result)
return result; return result;
if(!data->state.authdone && (httpreq != HTTPREQ_GET)) { if((!data->state.authhost.done || !data->state.authproxy.done ) &&
(httpreq != HTTPREQ_GET)) {
/* Until we are authenticated, we switch over to HEAD. Unless its a GET /* Until we are authenticated, we switch over to HEAD. Unless its a GET
we want to do. The explanation for this is rather long and boring, but we want to do. The explanation for this is rather long and boring, but
the point is that it can't be done otherwise without risking having to the point is that it can't be done otherwise without risking having to
@ -1583,7 +1596,7 @@ CURLcode Curl_http(struct connectdata *conn)
request, request,
ppath, ppath,
httpstring, httpstring,
(conn->bits.httpproxy && conn->allocptr.proxyuserpwd)? conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"", conn->allocptr.proxyuserpwd:"",
conn->allocptr.userpwd?conn->allocptr.userpwd:"", conn->allocptr.userpwd?conn->allocptr.userpwd:"",
(conn->bits.use_range && conn->allocptr.rangeline)? (conn->bits.use_range && conn->allocptr.rangeline)?
@ -1755,8 +1768,8 @@ CURLcode Curl_http(struct connectdata *conn)
/* setup variables for the upcoming transfer */ /* setup variables for the upcoming transfer */
result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount, &http->readbytecount,
data->state.authdone?FIRSTSOCKET:-1, FIRSTSOCKET,
data->state.authdone?&http->writebytecount:NULL); &http->writebytecount);
if(result) { if(result) {
Curl_formclean(http->sendit); /* free that whole lot */ Curl_formclean(http->sendit); /* free that whole lot */
return result; return result;
@ -1794,8 +1807,8 @@ CURLcode Curl_http(struct connectdata *conn)
/* prepare for transfer */ /* prepare for transfer */
result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE, result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount, &http->readbytecount,
data->state.authdone?FIRSTSOCKET:-1, FIRSTSOCKET,
data->state.authdone?&http->writebytecount:NULL); &http->writebytecount);
if(result) if(result)
return result; return result;
break; break;
@ -1826,7 +1839,8 @@ CURLcode Curl_http(struct connectdata *conn)
if(data->set.postfields) { if(data->set.postfields) {
if(data->state.authdone && (postsize < (100*1024))) { if((data->state.authhost.done || data->state.authproxy.done )
&& (postsize < (100*1024))) {
/* If we're not done with the authentication phase, we don't expect /* If we're not done with the authentication phase, we don't expect
to actually send off any data yet. Hence, we delay the sending of to actually send off any data yet. Hence, we delay the sending of
the body until we receive that friendly 100-continue response */ the body until we receive that friendly 100-continue response */
@ -1862,7 +1876,7 @@ CURLcode Curl_http(struct connectdata *conn)
/* set the upload size to the progress meter */ /* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize); Curl_pgrsSetUploadSize(data, http->postsize);
if(!data->state.authdone && !checkheaders(data, "Expect:")) { if(!checkheaders(data, "Expect:")) {
/* if not disabled explicitly we add a Expect: 100-continue to the /* if not disabled explicitly we add a Expect: 100-continue to the
headers which actually speeds up post operations (as there is headers which actually speeds up post operations (as there is
one packet coming back from the web server) */ one packet coming back from the web server) */

View File

@ -45,9 +45,9 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap,
/* These functions are in http.c */ /* These functions are in http.c */
void Curl_http_auth_stage(struct SessionHandle *data, int stage); void Curl_http_auth_stage(struct SessionHandle *data, int stage);
CURLcode Curl_http_auth(struct connectdata *conn, CURLcode Curl_http_input_auth(struct connectdata *conn,
int httpcode, char *header); int httpcode, char *header);
void Curl_http_auth_act(struct connectdata *conn); CURLcode Curl_http_auth_act(struct connectdata *conn);
int Curl_http_should_fail(struct connectdata *conn); int Curl_http_should_fail(struct connectdata *conn);
#endif #endif

View File

@ -47,14 +47,16 @@
#include "memdebug.h" #include "memdebug.h"
#endif #endif
/* Test example header: /* Test example headers:
WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
*/ */
CURLdigest Curl_input_digest(struct connectdata *conn, CURLdigest Curl_input_digest(struct connectdata *conn,
char *header) /* rest of the www-authenticate: bool proxy,
char *header) /* rest of the *-authenticate:
header */ header */
{ {
bool more = TRUE; bool more = TRUE;
@ -64,7 +66,14 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
bool foundAuthInt = FALSE; bool foundAuthInt = FALSE;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
bool before = FALSE; /* got a nonce before */ bool before = FALSE; /* got a nonce before */
struct digestdata *d = &data->state.digest; struct digestdata *d;
if(proxy) {
d = &data->state.proxydigest;
}
else {
d = &data->state.digest;
}
/* skip initial whitespaces */ /* skip initial whitespaces */
while(*header && isspace((int)*header)) while(*header && isspace((int)*header))
@ -78,7 +87,7 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
before = TRUE; before = TRUE;
/* clear off any former leftovers and init to defaults */ /* clear off any former leftovers and init to defaults */
Curl_digest_cleanup(data); Curl_digest_cleanup_one(d);
while(more) { while(more) {
char value[32]; char value[32];
@ -183,6 +192,7 @@ static void md5_to_ascii(unsigned char *source, /* 16 bytes */
} }
CURLcode Curl_output_digest(struct connectdata *conn, CURLcode Curl_output_digest(struct connectdata *conn,
bool proxy,
unsigned char *request, unsigned char *request,
unsigned char *uripath) unsigned char *uripath)
{ {
@ -198,9 +208,28 @@ CURLcode Curl_output_digest(struct connectdata *conn,
char *cnonce; char *cnonce;
char *tmp = NULL; char *tmp = NULL;
struct timeval now; struct timeval now;
struct auth *authp;
char **userp;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct digestdata *d = &data->state.digest; struct digestdata *d;
if(proxy) {
d = &data->state.proxydigest;
authp = &data->state.authproxy;
userp = &conn->allocptr.proxyuserpwd;
}
else {
d = &data->state.digest;
authp = &data->state.authhost;
userp = &conn->allocptr.userpwd;
}
if(!d->nonce) {
authp->done = FALSE;
return CURLE_OK;
}
authp->done = TRUE;
ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */ ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
@ -293,8 +322,8 @@ CURLcode Curl_output_digest(struct connectdata *conn,
Curl_safefree(conn->allocptr.userpwd); Curl_safefree(conn->allocptr.userpwd);
if (d->qop) { if (d->qop) {
conn->allocptr.userpwd = *userp =
aprintf( "Authorization: Digest " aprintf( "%sAuthorization: Digest "
"username=\"%s\", " "username=\"%s\", "
"realm=\"%s\", " "realm=\"%s\", "
"nonce=\"%s\", " "nonce=\"%s\", "
@ -303,6 +332,7 @@ CURLcode Curl_output_digest(struct connectdata *conn,
"nc=\"%08x\", " "nc=\"%08x\", "
"qop=\"%s\", " "qop=\"%s\", "
"response=\"%s\"", "response=\"%s\"",
proxy?"Proxy-":"",
conn->user, conn->user,
d->realm, d->realm,
d->nonce, d->nonce,
@ -318,13 +348,14 @@ CURLcode Curl_output_digest(struct connectdata *conn,
same nonce in the qop=auth mode. */ same nonce in the qop=auth mode. */
} }
else { else {
conn->allocptr.userpwd = *userp =
aprintf( "Authorization: Digest " aprintf( "%sAuthorization: Digest "
"username=\"%s\", " "username=\"%s\", "
"realm=\"%s\", " "realm=\"%s\", "
"nonce=\"%s\", " "nonce=\"%s\", "
"uri=\"%s\", " "uri=\"%s\", "
"response=\"%s\"", "response=\"%s\"",
proxy?"Proxy-":"",
conn->user, conn->user,
d->realm, d->realm,
d->nonce, d->nonce,
@ -336,36 +367,28 @@ CURLcode Curl_output_digest(struct connectdata *conn,
if(d->opaque) { if(d->opaque) {
/* append opaque */ /* append opaque */
tmp = aprintf(", opaque=\"%s\"", d->opaque); tmp = aprintf(", opaque=\"%s\"", d->opaque);
conn->allocptr.userpwd = (char*) *userp = (char*) realloc(*userp, strlen(*userp) + strlen(tmp) + 1);
realloc(conn->allocptr.userpwd, strcat(*userp, tmp);
strlen(conn->allocptr.userpwd) + strlen(tmp) + 1);
strcat(conn->allocptr.userpwd, tmp);
free(tmp); free(tmp);
} }
if(d->algorithm) { if(d->algorithm) {
/* append algorithm */ /* append algorithm */
tmp = aprintf(", algorithm=\"%s\"", d->algorithm); tmp = aprintf(", algorithm=\"%s\"", d->algorithm);
conn->allocptr.userpwd = (char*) *userp = (char*) realloc(*userp, strlen(*userp) + strlen(tmp) + 1);
realloc(conn->allocptr.userpwd,
strlen(conn->allocptr.userpwd) + strlen(tmp) + 1);
strcat(conn->allocptr.userpwd, tmp); strcat(conn->allocptr.userpwd, tmp);
free(tmp); free(tmp);
} }
/* append CRLF to the userpwd header */ /* append CRLF to the userpwd header */
conn->allocptr.userpwd = (char*) *userp = (char*) realloc(*userp, strlen(*userp) + 3 + 1);
realloc(conn->allocptr.userpwd, strcat(*userp, "\r\n");
strlen(conn->allocptr.userpwd) + 3 + 1);
strcat(conn->allocptr.userpwd, "\r\n");
return CURLE_OK; return CURLE_OK;
} }
void Curl_digest_cleanup(struct SessionHandle *data) void Curl_digest_cleanup_one(struct digestdata *d)
{ {
struct digestdata *d = &data->state.digest;
if(d->nonce) if(d->nonce)
free(d->nonce); free(d->nonce);
d->nonce = NULL; d->nonce = NULL;
@ -395,4 +418,11 @@ void Curl_digest_cleanup(struct SessionHandle *data)
d->stale = FALSE; /* default means normal, not stale */ d->stale = FALSE; /* default means normal, not stale */
} }
void Curl_digest_cleanup(struct SessionHandle *data)
{
Curl_digest_cleanup_one(&data->state.digest);
Curl_digest_cleanup_one(&data->state.proxydigest);
}
#endif #endif

View File

@ -38,12 +38,15 @@ enum {
}; };
/* this is for digest header input */ /* this is for digest header input */
CURLdigest Curl_input_digest(struct connectdata *conn, char *header); CURLdigest Curl_input_digest(struct connectdata *conn,
bool proxy, char *header);
/* this is for creating digest header output */ /* this is for creating digest header output */
CURLcode Curl_output_digest(struct connectdata *conn, CURLcode Curl_output_digest(struct connectdata *conn,
bool proxy,
unsigned char *request, unsigned char *request,
unsigned char *uripath); unsigned char *uripath);
void Curl_digest_cleanup(struct SessionHandle *data); void Curl_digest_cleanup(struct SessionHandle *data);
void Curl_digest_cleanup_one(struct digestdata *dig);
#endif #endif

View File

@ -46,7 +46,6 @@
#include "base64.h" #include "base64.h"
#include "http_ntlm.h" #include "http_ntlm.h"
#include "url.h" #include "url.h"
#include "http.h" /* for Curl_http_auth_stage() */
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -298,23 +297,26 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
char *passwdp; char *passwdp;
/* point to the correct struct with this */ /* point to the correct struct with this */
struct ntlmdata *ntlm; struct ntlmdata *ntlm;
struct auth *authp;
curlassert(conn); curlassert(conn);
curlassert(conn->data); curlassert(conn->data);
conn->data->state.authdone = FALSE;
if(proxy) { if(proxy) {
allocuserpwd = &conn->allocptr.proxyuserpwd; allocuserpwd = &conn->allocptr.proxyuserpwd;
userp = conn->proxyuser; userp = conn->proxyuser;
passwdp = conn->proxypasswd; passwdp = conn->proxypasswd;
ntlm = &conn->proxyntlm; ntlm = &conn->proxyntlm;
authp = &conn->data->state.authproxy;
} }
else { else {
allocuserpwd = &conn->allocptr.userpwd; allocuserpwd = &conn->allocptr.userpwd;
userp = conn->user; userp = conn->user;
passwdp = conn->passwd; passwdp = conn->passwd;
ntlm = &conn->ntlm; ntlm = &conn->ntlm;
authp = &conn->data->state.authhost;
} }
authp->done = FALSE;
/* not set means empty */ /* not set means empty */
if(!userp) if(!userp)
@ -563,11 +565,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY; /* FIX TODO */ return CURLE_OUT_OF_MEMORY; /* FIX TODO */
ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */ ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
conn->data->state.authdone = TRUE; authp->done = TRUE;
/* Switch to web authentication after proxy authentication is done */
if (proxy)
Curl_http_auth_stage(conn->data, 401);
} }
break; break;
@ -578,7 +576,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
free(*allocuserpwd); free(*allocuserpwd);
*allocuserpwd=NULL; *allocuserpwd=NULL;
} }
conn->data->state.authdone = TRUE; authp->done = TRUE;
break; break;
} }

View File

@ -445,9 +445,9 @@ CURLcode Curl_readwrite(struct connectdata *conn,
} }
/* /*
** Now that all of the headers have been parsed, see * When all the headers have been parsed, see if we should give
** if we should give up and return an error. * up and return an error.
*/ */
if (Curl_http_should_fail(conn)) { if (Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d", failf (data, "The requested URL returned error: %d",
k->httpcode); k->httpcode);
@ -483,19 +483,23 @@ CURLcode Curl_readwrite(struct connectdata *conn,
} }
else { else {
/* we wanted to resume a download, although the server /* we wanted to resume a download, although the server
doesn't seem to support this and we did this with a GET * doesn't seem to support this and we did this with a GET
(if it wasn't a GET we did a POST or PUT resume) */ * (if it wasn't a GET we did a POST or PUT resume) */
failf (data, "HTTP server doesn't seem to support " failf (data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume."); "byte ranges. Cannot resume.");
return CURLE_HTTP_RANGE_ERROR; return CURLE_HTTP_RANGE_ERROR;
} }
} }
if(!stop_reading) if(!stop_reading) {
/* *auth_act() checks what authentication methods that are /* Curl_http_auth_act() checks what authentication methods
available and decides which one (if any) to use. It will * that are available and decides which one (if any) to
set 'newurl' if an auth metod was picked. */ * use. It will set 'newurl' if an auth metod was picked. */
Curl_http_auth_act(conn); result = Curl_http_auth_act(conn);
if(result)
return result;
}
if(!k->header) { if(!k->header) {
/* /*
@ -593,22 +597,17 @@ CURLcode Curl_readwrite(struct connectdata *conn,
data->info.httpversion = k->httpversion; data->info.httpversion = k->httpversion;
/* /*
** This code executes as part of processing * This code executes as part of processing the header. As a
** the header. As a result, it's not * result, it's not totally clear how to interpret the
** totally clear how to interpret the * response code yet as that depends on what other headers may
** response code yet as that depends on what * be present. 401 and 407 may be errors, but may be OK
** other headers may be present. 401 and * depending on how authentication is working. Other codes
** 407 may be errors, but may be OK * are definitely errors, so give up here.
** depending on how authentication is */
** working. Other codes are definitely
** errors, so give up here.
*/
if (data->set.http_fail_on_error && if (data->set.http_fail_on_error &&
(k->httpcode >= 400) && (k->httpcode >= 400) &&
(k->httpcode != 401) && (k->httpcode != 401) &&
(k->httpcode != 407)) { (k->httpcode != 407)) {
/* If we have been told to fail hard on HTTP-errors,
here is the check for that: */
/* serious error, go home! */ /* serious error, go home! */
failf (data, "The requested URL returned error: %d", failf (data, "The requested URL returned error: %d",
k->httpcode); k->httpcode);
@ -821,7 +820,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
(401 == k->httpcode)) || (401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", k->p) && (checkprefix("Proxy-authenticate:", k->p) &&
(407 == k->httpcode))) { (407 == k->httpcode))) {
result = Curl_http_auth(conn, k->httpcode, k->p); result = Curl_http_input_auth(conn, k->httpcode, k->p);
if(result) if(result)
return result; return result;
} }
@ -1514,10 +1513,9 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
data->state.this_is_a_follow = FALSE; /* reset this */ data->state.this_is_a_follow = FALSE; /* reset this */
data->state.errorbuf = FALSE; /* no error has occurred */ data->state.errorbuf = FALSE; /* no error has occurred */
/* set preferred authentication, default to basic */
data->state.authstage = 0; /* initialize authentication later */
data->state.authproblem = FALSE; data->state.authproblem = FALSE;
data->state.authhost.want = data->set.httpauth;
data->state.authproxy.want = data->set.proxyauth;
/* If there was a list of cookie files to read and we haven't done it before, /* If there was a list of cookie files to read and we haven't done it before,
do it now! */ do it now! */

View File

@ -1332,9 +1332,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
CURLcode Curl_disconnect(struct connectdata *conn) CURLcode Curl_disconnect(struct connectdata *conn)
{ {
struct SessionHandle *data;
if(!conn) if(!conn)
return CURLE_OK; /* this is closed and fine already */ return CURLE_OK; /* this is closed and fine already */
data = conn->data;
/* /*
* The range string is usually freed in curl_done(), but we might * The range string is usually freed in curl_done(), but we might
* get here *instead* if we fail prematurely. Thus we need to be able * get here *instead* if we fail prematurely. Thus we need to be able
@ -1346,11 +1349,20 @@ CURLcode Curl_disconnect(struct connectdata *conn)
} }
if((conn->ntlm.state != NTLMSTATE_NONE) || if((conn->ntlm.state != NTLMSTATE_NONE) ||
(conn->proxyntlm.state != NTLMSTATE_NONE)) (conn->proxyntlm.state != NTLMSTATE_NONE)) {
/* Authentication data is a mix of connection-related and sessionhandle- /* Authentication data is a mix of connection-related and sessionhandle-
related stuff. NTLM is connection-related so when we close the shop related stuff. NTLM is connection-related so when we close the shop
we shall forget. */ we shall forget. */
conn->data->state.authstage = 0; data->state.authhost.done = FALSE;
data->state.authhost.picked =
data->state.authhost.want;
data->state.authproxy.done = FALSE;
data->state.authproxy.picked =
data->state.authhost.want;
data->state.authproblem = FALSE;
}
if(conn->curl_disconnect) if(conn->curl_disconnect)
/* This is set if protocol-specific cleanups should be made */ /* This is set if protocol-specific cleanups should be made */
@ -1358,8 +1370,8 @@ CURLcode Curl_disconnect(struct connectdata *conn)
if(-1 != conn->connectindex) { if(-1 != conn->connectindex) {
/* unlink ourselves! */ /* unlink ourselves! */
infof(conn->data, "Closing connection #%d\n", conn->connectindex); infof(data, "Closing connection #%d\n", conn->connectindex);
conn->data->state.connects[conn->connectindex] = NULL; data->state.connects[conn->connectindex] = NULL;
} }
Curl_safefree(conn->proto.generic); Curl_safefree(conn->proto.generic);
@ -1488,7 +1500,7 @@ ConnectionExists(struct SessionHandle *data,
} }
if((needle->protocol & PROT_FTP) || if((needle->protocol & PROT_FTP) ||
((needle->protocol & PROT_HTTP) && ((needle->protocol & PROT_HTTP) &&
(needle->data->state.authwant==CURLAUTH_NTLM))) { (needle->data->state.authhost.want==CURLAUTH_NTLM))) {
/* This is FTP or HTTP+NTLM, verify that we're using the same name /* This is FTP or HTTP+NTLM, verify that we're using the same name
and password as well */ and password as well */
if(!strequal(needle->user, check->user) || if(!strequal(needle->user, check->user) ||

View File

@ -678,6 +678,16 @@ typedef enum {
#define MAX_CURL_USER_LENGTH_TXT "255" #define MAX_CURL_USER_LENGTH_TXT "255"
#define MAX_CURL_PASSWORD_LENGTH_TXT "255" #define MAX_CURL_PASSWORD_LENGTH_TXT "255"
struct auth {
long want; /* Bitmask set to the authentication methods wanted by the app
(with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */
long picked;
long avail; /* bitmask for what the server reports to support for this
resource */
bool done; /* TRUE when the auth phase is done and ready to do the *actual*
request */
};
struct UrlState { struct UrlState {
enum { enum {
Curl_if_none, Curl_if_none,
@ -724,22 +734,16 @@ struct UrlState {
is always set TRUE when curl_easy_perform() is called. */ is always set TRUE when curl_easy_perform() is called. */
struct digestdata digest; struct digestdata digest;
struct digestdata proxydigest;
#ifdef HAVE_GSSAPI #ifdef HAVE_GSSAPI
struct negotiatedata negotiate; struct negotiatedata negotiate;
#endif #endif
long authstage; /* 0 - authwant and authavail are still not initialized struct auth authhost;
401 - web authentication is performed struct auth authproxy;
407 - proxy authentication is performed */
long authwant; /* initially set to authentication methods requested by
client (either with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH
depending on authstage) */
long authavail; /* what the server reports */
bool authproblem; /* TRUE if there's some problem authenticating */ bool authproblem; /* TRUE if there's some problem authenticating */
bool authdone; /* TRUE when the auth phase is done and ready
to do the *actual* request */
#ifdef USE_ARES #ifdef USE_ARES
ares_channel areschannel; /* for name resolves */ ares_channel areschannel; /* for name resolves */
#endif #endif

View File

@ -3,26 +3,27 @@ install:
test: test:
EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test10 test109 test118 test13 test200 test28 test36 test47 test100 \ test10 test109 test118 test13 test200 test28 test36 test47 test100 \
test11 test119 test14 test201 test29 test37 test5 test101 test110 \ test11 test119 test14 test201 test29 test37 test5 test101 test110 \
test12 test15 test202 test3 test4 test6 test102 test111 test120 test16 \ test12 test15 test202 test3 test4 test6 test102 test111 test120 \
test21 test30 test7 test103 test112 test121 test17 test22 test300 \ test16 test21 test30 test7 test103 test112 test121 test17 test22 \
test8 test104 test113 test122 test18 test23 test301 test9 test105 \ test300 test8 test104 test113 test122 test18 test23 test301 test9 \
test114 test123 test19 test24 test302 test43 test31 test106 test115 \ test105 test114 test123 test19 test24 test302 test43 test31 test106 \
test124 test190 test25 test303 test44 test38 test107 test116 test125 \ test115 test124 test190 test25 test303 test44 test38 test107 test116 \
test2 test26 test33 test45 test126 test304 test39 test32 test128 \ test125 test2 test26 test33 test45 test126 test304 test39 test32 \
test48 test306 test130 test131 test132 test133 test134 test135 test305 \ test128 test48 test306 test130 test131 test132 test133 test134 \
test49 test50 test51 test52 test53 test54 test55 test56 test500 \ test135 test305 test49 test50 test51 test52 test53 test54 test55 \
test501 test502 test503 test504 test136 test57 test137 test138 test58 \ test56 test500 test501 test502 test503 test504 test136 test57 test137 \
test139 test140 test141 test59 test60 test61 test142 test143 test62 \ test138 test58 test139 test140 test141 test59 test60 test61 test142 \
test63 test64 test65 test66 test144 test145 test67 test68 test41 \ test143 test62 test63 test64 test65 test66 test144 test145 test67 \
test40 test42 test69 test70 test71 test72 test73 test146 test505 \ test68 test41 test40 test42 test69 test70 test71 test72 test73 \
test74 test75 test76 test77 test78 test147 test148 test506 test79 \ test146 test505 test74 test75 test76 test77 test78 test147 test148 \
test80 test81 test82 test83 test84 test85 test86 test87 test507 \ test506 test79 test80 test81 test82 test83 test84 test85 test86 \
test149 test88 test89 test90 test508 test91 test92 test203 test93 \ test87 test507 test149 test88 test89 test90 test508 test91 test92 \
test94 test95 test509 test510 test97 test98 test99 test150 test151 \ test203 test93 test94 test95 test509 test510 test97 test98 test99 \
test152 test153 test154 test155 test156 test157 test158 test159 test511 \ test150 test151 test152 test153 test154 test155 test156 test157 \
test160 test161 test162 test163 test164 test512 test165 test166 test158 test159 test511 test160 test161 test162 test163 test164 \
test512 test165 test166 test167 test168 test169
# The following tests have been removed from the dist since they no longer # The following tests have been removed from the dist since they no longer
# work. We need to fix the test suite's FTPS server first, then bring them # work. We need to fix the test suite's FTPS server first, then bring them

View File

@ -32,7 +32,7 @@ HTTP with proxy athorization
</strip> </strip>
<protocol> <protocol>
GET http://we.want.that.site.com/16 HTTP/1.1 GET http://we.want.that.site.com/16 HTTP/1.1
Proxy-authorization: Basic ZmFrZUB1c2VyOqenp2xvb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29uZw== Proxy-Authorization: Basic ZmFrZUB1c2VyOqenp2xvb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29uZw==
Host: we.want.that.site.com Host: we.want.that.site.com
Pragma: no-cache Pragma: no-cache
Accept: */* Accept: */*

62
tests/data/test167 Normal file
View File

@ -0,0 +1,62 @@
# Server-side
<reply>
<data>
HTTP/1.1 401 Authorization Required swsclose
WWW-Authenticate: Digest realm="weirdorealm", nonce="12345"
</data>
<data1000>
HTTP/1.1 200 OK swsclose
Server: no
Nice auth sir!
</data1000>
<datacheck>
HTTP/1.1 401 Authorization Required swsclose
WWW-Authenticate: Digest realm="weirdorealm", nonce="12345"
HTTP/1.1 200 OK swsclose
Server: no
Nice auth sir!
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP with proxy-requiring-Basic to site-requiring-Digest
</name>
<command>
http://data.from.server.requiring.digest.hohoho.com/167 --proxy http://%HOSTIP:%HOSTPORT --proxy-user foo:bar --digest --user digest:alot
</command>
</test>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent: curl/.*
</strip>
<protocol>
GET http://data.from.server.requiring.digest.hohoho.com/167 HTTP/1.1
Proxy-Authorization: Basic Zm9vOmJhcg==
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b zlib/1.1.4 c-ares/1.2.0 libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
GET http://data.from.server.requiring.digest.hohoho.com/167 HTTP/1.1
Proxy-Authorization: Basic Zm9vOmJhcg==
Authorization: Digest username="digest", realm="weirdorealm", nonce="12345", uri="/167", response="13c7c02a252cbe1c46d8669898a3be26"
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b zlib/1.1.4 c-ares/1.2.0 libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
</protocol>
</verify>

82
tests/data/test168 Normal file
View File

@ -0,0 +1,82 @@
# Server-side
<reply>
# this is returned first since we get no proxy-auth
<data>
HTTP/1.1 407 Authorization Required to proxy me my dear swsclose
Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"
And you should ignore this data.
</data>
# then this is returned since we get no server-auth
<data1000>
HTTP/1.1 401 Authorization to the remote host as well swsbounce swsclose
WWW-Authenticate: Digest realm="realmweirdo", nonce="123456"
you should ignore this data too
</data1000>
<data1001>
HTTP/1.1 200 OK swsclose
Server: no
Nice auth sir!
</data1001>
<datacheck>
HTTP/1.1 407 Authorization Required to proxy me my dear swsclose
Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"
HTTP/1.1 401 Authorization to the remote host as well swsbounce swsclose
WWW-Authenticate: Digest realm="realmweirdo", nonce="123456"
HTTP/1.1 200 OK swsclose
Server: no
Nice auth sir!
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP with proxy-requiring-Digest to site-requiring-Digest
</name>
<command>
http://data.from.server.requiring.digest.hohoho.com/168 --proxy http://%HOSTIP:%HOSTPORT --proxy-user foo:bar --proxy-digest --digest --user digest:alot
</command>
</test>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent: curl/.*
</strip>
<protocol>
GET http://data.from.server.requiring.digest.hohoho.com/168 HTTP/1.1
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b zlib/1.1.4 c-ares/1.2.0 libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
GET http://data.from.server.requiring.digest.hohoho.com/168 HTTP/1.1
Proxy-Authorization: Digest username="digest", realm="weirdorealm", nonce="12345", uri="/168", response="4e79e4fc104ef1f16ab4567e1ad4dede"
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b zlib/1.1.4 c-ares/1.2.0 libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
GET http://data.from.server.requiring.digest.hohoho.com/168 HTTP/1.1
Proxy-Authorization: Digest username="digest", realm="weirdorealm", nonce="12345", uri="/168", response="4e79e4fc104ef1f16ab4567e1ad4dede"
Authorization: Digest username="digest", realm="realmweirdo", nonce="123456", uri="/168", response="ca87f2d768a231e2d637a55698d5c416"
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
</protocol>
</verify>

107
tests/data/test169 Normal file
View File

@ -0,0 +1,107 @@
# Server-side
<reply>
# this is returned first since we get no proxy-auth
<data>
HTTP/1.1 407 Authorization Required to proxy me my dear swsclose
Proxy-Authenticate: NTLM
And you should ignore this data.
</data>
# then this is returned since we get no server-auth
<data1000>
HTTP/1.1 200 Authorizated fine
Content-Length: 27
Welcome to the end station
</data1000>
<data1001>
HTTP/1.1 407 NTLM type-1 received sending back type-2
Server: Microsoft-IIS/5.0
Content-Length: 34
Content-Type: text/html; charset=iso-8859-1
Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==
This is not the real page either!
</data1001>
# This is supposed to be returned when the server gets the second
# Authorization: NTLM line passed-in from the client
<data1002>
HTTP/1.1 401 You now need to authenticate with the host
Server: Microsoft-IIS/5.0
WWW-Authenticate: Digest realm="r e a l m", nonce="abcdef"
Content-Length: 40
Content-Type: text/html; charset=iso-8859-1
We have not authenticated with the server yet
</data1002>
<datacheck>
HTTP/1.1 407 NTLM type-1 received sending back type-2
Server: Microsoft-IIS/5.0
Content-Length: 34
Content-Type: text/html; charset=iso-8859-1
Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==
HTTP/1.1 401 You now need to authenticate with the host
Server: Microsoft-IIS/5.0
WWW-Authenticate: Digest realm="r e a l m", nonce="abcdef"
Content-Length: 40
Content-Type: text/html; charset=iso-8859-1
HTTP/1.1 200 Authorizated fine
Content-Length: 27
Welcome to the end station
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
# NTLM only works if we are built with SSL
<features>
SSL
</features>
<name>
HTTP with proxy-requiring-NTLM to site-requiring-Digest
</name>
<command>
http://data.from.server.requiring.digest.hohoho.com/169 --proxy http://%HOSTIP:%HOSTPORT --proxy-user foo:bar --proxy-ntlm --digest --user digest:alot
</command>
</test>
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent: curl/.*
</strip>
<protocol>
GET http://data.from.server.requiring.digest.hohoho.com/169 HTTP/1.1
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAAgIAAAAAAAAgAAAAAAAAACAAAAA=
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
GET http://data.from.server.requiring.digest.hohoho.com/169 HTTP/1.1
Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAEMAAAAYABgAWwAAAAAAAABAAAAAAwADAEAAAAAAAAAAQwAAAAAAAABzAAAAAYIAAGZvb4P6B+XVQ6vQsx3DfDXUVhd9436GAxPu0IYcl2Z7LxHmNeOAWQ+vxUmhuCFJBUgXCQ==
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
GET http://data.from.server.requiring.digest.hohoho.com/169 HTTP/1.1
Authorization: Digest username="digest", realm="r e a l m", nonce="abcdef", uri="/169", response="95d48591985a03c4b49cb962aa7bd3e6"
User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS libidn/0.4.3
Host: data.from.server.requiring.digest.hohoho.com
Pragma: no-cache
Accept: */*
</protocol>
</verify>

View File

@ -49,7 +49,7 @@ moo
<verify> <verify>
<protocol> <protocol>
CONNECT 127.0.0.1:8433 HTTP/1.0 CONNECT 127.0.0.1:8433 HTTP/1.0
Proxy-authorization: Basic dGVzdDppbmc= Proxy-Authorization: Basic dGVzdDppbmc=
GET /503 HTTP/1.1 GET /503 HTTP/1.1
Authorization: Basic dGVzdDppbmc= Authorization: Basic dGVzdDppbmc=

View File

@ -32,7 +32,7 @@ http://we.want.that.site.com/63
</strip> </strip>
<protocol> <protocol>
GET http://we.want.that.site.com/63 HTTP/1.1 GET http://we.want.that.site.com/63 HTTP/1.1
Proxy-authorization: Basic ZmFrZTp1c2Vy Proxy-Authorization: Basic ZmFrZTp1c2Vy
Host: we.want.that.site.com Host: we.want.that.site.com
Pragma: no-cache Pragma: no-cache
Accept: */* Accept: */*

View File

@ -45,7 +45,7 @@ http://%HOSTIP:%HOSTPORT/we/want/that/page/80 -p -x %HOSTIP:%HOSTPORT --user iam
</strip> </strip>
<protocol> <protocol>
CONNECT 127.0.0.1:8999 HTTP/1.0 CONNECT 127.0.0.1:8999 HTTP/1.0
Proxy-authorization: Basic eW91YXJlOnlvdXJzZWxm Proxy-Authorization: Basic eW91YXJlOnlvdXJzZWxm
User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3
GET /we/want/that/page/80 HTTP/1.1 GET /we/want/that/page/80 HTTP/1.1

View File

@ -35,7 +35,7 @@ http://%HOSTIP:%HOSTPORT/82 --proxy-user testuser:testpass -x http://%HOSTIP:%HO
</strip> </strip>
<protocol> <protocol>
GET http://127.0.0.1:8999/82 HTTP/1.1 GET http://127.0.0.1:8999/82 HTTP/1.1
Proxy-authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M= Proxy-Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
User-Agent: curl/7.10.6-pre1 (i686-pc-linux-gnu) libcurl/7.10.6-pre1 OpenSSL/0.9.7a ipv6 zlib/1.1.3 User-Agent: curl/7.10.6-pre1 (i686-pc-linux-gnu) libcurl/7.10.6-pre1 OpenSSL/0.9.7a ipv6 zlib/1.1.3
Host: 127.0.0.1:8999 Host: 127.0.0.1:8999
Pragma: no-cache Pragma: no-cache

View File

@ -34,7 +34,7 @@ http://%HOSTIP:%HOSTPORT/we/want/that/page/85 -x %HOSTIP:%HOSTPORT --user iam:my
</strip> </strip>
<protocol> <protocol>
GET http://127.0.0.1:8999/we/want/that/page/85 HTTP/1.1 GET http://127.0.0.1:8999/we/want/that/page/85 HTTP/1.1
Proxy-authorization: Basic dGVzdGluZzp0aGlz Proxy-Authorization: Basic dGVzdGluZzp0aGlz
Authorization: Basic aWFtOm15c2VsZg== Authorization: Basic aWFtOm15c2VsZg==
User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3
Host: 127.0.0.1:8999 Host: 127.0.0.1:8999