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

View file

@ -103,8 +103,6 @@
#include "memdebug.h"
#endif
static CURLcode Curl_output_basic_proxy(struct connectdata *conn);
/*
* checkheaders() checks the linked list of custom HTTP headers for a
* 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
* authentication. It uses the conn->user, conn->passwd fields for it.
* Curl_output_basic() sets up an Authorization: header (or the proxy version)
* for HTTP Basic authentication.
*
* Returns CURLcode.
*/
static CURLcode Curl_output_basic(struct connectdata *conn)
static CURLcode Curl_output_basic(struct connectdata *conn, bool proxy)
{
char *authorization;
struct SessionHandle *data=conn->data;
char **userp;
char *user;
char *pwd;
sprintf(data->state.buffer, "%s:%s", conn->user, conn->passwd);
if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
if(proxy) {
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) {
if(conn->allocptr.userpwd)
free(conn->allocptr.userpwd);
conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012",
authorization);
if(*userp)
free(*userp);
*userp = aprintf( "%sAuthorization: Basic %s\015\012",
proxy?"Proxy-":"",
authorization);
free(authorization);
}
else
@ -148,61 +162,74 @@ static CURLcode Curl_output_basic(struct connectdata *conn)
return CURLE_OK;
}
/*
* Curl_output_basic_proxy() sets up a proxy-Authorization: header for HTTP
* Basic proxy authentication. It uses the conn->proxyuser and
* conn->proxypasswd fields for it.
/* pickoneauth() selects the most favourable authentication method from the
* ones available and the ones we want.
*
* 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;
struct SessionHandle *data=conn->data;
bool picked;
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
of preference in case of the existance of multiple accepted types. */
if(data->state.authavail & CURLAUTH_GSSNEGOTIATE)
data->state.authwant = CURLAUTH_GSSNEGOTIATE;
else if(data->state.authavail & CURLAUTH_DIGEST)
data->state.authwant = CURLAUTH_DIGEST;
else if(data->state.authavail & CURLAUTH_NTLM)
data->state.authwant = CURLAUTH_NTLM;
else if(data->state.authavail & CURLAUTH_BASIC)
data->state.authwant = CURLAUTH_BASIC;
else
data->state.authwant = CURLAUTH_NONE; /* clear it */
if(data->state.authwant)
conn->newurl = strdup(data->change.url); /* clone URL */
data->state.authavail = CURLAUTH_NONE; /* clear it here */
if(avail & CURLAUTH_GSSNEGOTIATE)
pick->picked = CURLAUTH_GSSNEGOTIATE;
else if(avail & CURLAUTH_DIGEST)
pick->picked = CURLAUTH_DIGEST;
else if(avail & CURLAUTH_NTLM)
pick->picked = CURLAUTH_NTLM;
else if(avail & CURLAUTH_BASIC)
pick->picked = CURLAUTH_BASIC;
else {
pick->picked = CURLAUTH_NONE; /* none was picked clear it */
picked = FALSE;
}
pick->avail = 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,
authentication is not "done" yet 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) &&
(data->set.httpreq != HTTPREQ_HEAD)) {
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
* and the correct authentication method. conn->data->state.authdone is set to
* TRUE when authentication is done.
* Curl_http_output_auth() setups the authentication headers for the
* host/proxy and the correct authentication
* method. conn->data->state.authdone is set to TRUE when authentication is
* done.
*
* @param conn all information about the current connection
*
* Returns CURLcode
*/
static CURLcode http_auth_headers(struct connectdata *conn,
char *request,
char *path)
static CURLcode
Curl_http_output_auth(struct connectdata *conn,
char *request,
char *path,
bool proxytunnel) /* TRUE if this is the request setting
up the proxy tunnel */
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
@ -234,19 +272,29 @@ static CURLcode http_auth_headers(struct connectdata *conn,
curlassert(data);
if(!data->state.authstage) {
if(conn->bits.httpproxy && conn->bits.proxy_user_passwd) {
data->state.authdone = FALSE;
Curl_http_auth_stage(data, 407);
}
else if(conn->bits.user_passwd) {
data->state.authdone = FALSE;
Curl_http_auth_stage(data, 401);
}
else {
data->state.authdone = TRUE;
return CURLE_OK; /* no authentication with no user or password */
}
if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
conn->bits.user_passwd)
/* continue please */ ;
else {
data->state.authhost.done = TRUE;
data->state.authproxy.done = TRUE;
return CURLE_OK; /* no authentication with no user or password */
}
if(data->state.authhost.want &&
!data->state.authhost.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.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
@ -256,10 +304,11 @@ static CURLcode http_auth_headers(struct connectdata *conn,
curl_strequal(data->state.auth_host, conn->host.name) ||
data->set.http_disable_hostname_check_before_authentication) {
/* Send proxy authentication header if needed */
if (data->state.authstage == 407) {
/* Send proxy authentication header if needed */
if (conn->bits.httpproxy &&
(data->set.tunnel_thru_httpproxy == proxytunnel)) {
#ifdef USE_SSLEAY
if(data->state.authwant == CURLAUTH_NTLM) {
if(data->state.authproxy.want == CURLAUTH_NTLM) {
auth=(char *)"NTLM";
result = Curl_output_ntlm(conn, TRUE);
if(result)
@ -267,39 +316,52 @@ static CURLcode http_auth_headers(struct connectdata *conn,
}
else
#endif
if(data->state.authwant == CURLAUTH_BASIC) {
if(data->state.authproxy.want == CURLAUTH_BASIC) {
/* Basic */
if(conn->bits.proxy_user_passwd &&
!checkheaders(data, "Proxy-authorization:")) {
auth=(char *)"Basic";
result = Curl_output_basic_proxy(conn);
result = Curl_output_basic(conn, TRUE);
if(result)
return result;
}
data->state.authdone = TRUE;
/* Switch to web authentication after proxy authentication is done */
Curl_http_auth_stage(data, 401);
data->state.authproxy.done = TRUE;
}
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",
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 */
if (data->state.authstage == 401) {
{
auth = NULL;
#ifdef HAVE_GSSAPI
if((data->state.authwant == CURLAUTH_GSSNEGOTIATE) &&
if((data->state.authhost.want == CURLAUTH_GSSNEGOTIATE) &&
data->state.negotiate.context &&
!GSS_ERROR(data->state.negotiate.status)) {
auth=(char *)"GSS-Negotiate";
result = Curl_output_negotiate(conn);
if (result)
return result;
data->state.authdone = TRUE;
data->state.authhost.done = TRUE;
}
else
#endif
#ifdef USE_SSLEAY
if(data->state.authwant == CURLAUTH_NTLM) {
if(data->state.authhost.picked == CURLAUTH_NTLM) {
auth=(char *)"NTLM";
result = Curl_output_ntlm(conn, FALSE);
if(result)
@ -308,26 +370,25 @@ static CURLcode http_auth_headers(struct connectdata *conn,
else
#endif
{
if((data->state.authwant == CURLAUTH_DIGEST) &&
data->state.digest.nonce) {
if(data->state.authhost.picked == CURLAUTH_DIGEST) {
auth=(char *)"Digest";
result = Curl_output_digest(conn,
FALSE, /* not a proxy */
(unsigned char *)request,
(unsigned char *)path);
if(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 &&
!checkheaders(data, "Authorization:")) {
auth=(char *)"Basic";
result = Curl_output_basic(conn);
result = Curl_output_basic(conn, FALSE);
if(result)
return result;
}
/* basic is always ready */
data->state.authdone = TRUE;
data->state.authhost.done = TRUE;
}
}
if(auth)
@ -336,21 +397,21 @@ static CURLcode http_auth_headers(struct connectdata *conn,
}
}
else
data->state.authdone = TRUE;
data->state.authhost.done = TRUE;
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
* proxy CONNECT loop.
*/
CURLcode Curl_http_auth(struct connectdata *conn,
int httpcode,
char *header) /* pointing to the first non-space */
CURLcode Curl_http_input_auth(struct connectdata *conn,
int httpcode,
char *header) /* the first non-space */
{
/*
* This resource requires authentication
@ -359,23 +420,18 @@ CURLcode Curl_http_auth(struct connectdata *conn,
long *availp;
char *start;
struct auth *authp;
if (httpcode == 407) {
start = header+strlen("Proxy-authenticate:");
availp = &data->info.proxyauthavail;
authp = &data->state.authproxy;
}
else {
start = header+strlen("WWW-Authenticate:");
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 */
while(*start && isspace((int)*start))
@ -394,7 +450,8 @@ CURLcode Curl_http_auth(struct connectdata *conn,
if (checkprefix("GSS-Negotiate", start) ||
checkprefix("Negotiate", start)) {
*availp |= CURLAUTH_GSSNEGOTIATE;
if(data->state.authwant == CURLAUTH_GSSNEGOTIATE) {
authp->avail |= CURLAUTH_GSSNEGOTIATE;
if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
/* if exactly this is wanted, go */
int neg = Curl_input_negotiate(conn, start);
if (neg == 0) {
@ -406,9 +463,6 @@ CURLcode Curl_http_auth(struct connectdata *conn,
data->state.authproblem = TRUE;
}
}
else
if(data->state.authwant & CURLAUTH_GSSNEGOTIATE)
data->state.authavail |= CURLAUTH_GSSNEGOTIATE;
}
else
#endif
@ -416,76 +470,50 @@ CURLcode Curl_http_auth(struct connectdata *conn,
/* NTLM support requires the SSL crypto libs */
if(checkprefix("NTLM", start)) {
*availp |= CURLAUTH_NTLM;
if(data->state.authwant == CURLAUTH_NTLM) {
/* NTLM authentication is activated */
authp->avail |= CURLAUTH_NTLM;
if(authp->picked == CURLAUTH_NTLM) {
/* NTLM authentication is picked and activated */
CURLntlm ntlm =
Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
if(CURLNTLM_BAD != ntlm) {
conn->newurl = strdup(data->change.url); /* clone string */
data->state.authproblem = (conn->newurl == NULL);
}
if(CURLNTLM_BAD != ntlm)
data->state.authproblem = FALSE;
else {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
else
if(data->state.authwant & CURLAUTH_NTLM)
data->state.authavail |= CURLAUTH_NTLM;
}
else
#endif
if(checkprefix("Digest", start)) {
CURLdigest dig;
*availp |= CURLAUTH_DIGEST;
if(data->state.authwant == CURLAUTH_DIGEST) {
/* Digest authentication is activated */
CURLdigest dig = Curl_input_digest(conn, start);
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, (bool)(httpcode == 407), start);
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;
if(CURLDIGEST_FINE != dig) {
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
else if(data->state.authwant & CURLAUTH_BASIC) {
data->state.authavail |= CURLAUTH_BASIC;
} else {
/*
** We asked for something besides basic but got
** Basic anyway. This is no good.
*/
infof(data, "Server expects Basic auth, but we're doing something else.\n");
data->state.authproblem = TRUE;
}
else 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 basicly means our name+password isn't
valid. */
authp->avail = CURLAUTH_NONE;
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
}
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);
#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
** 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,
int remote_port)
{
int httpcode=0;
int subversion=0;
struct SessionHandle *data=conn->data;
struct Curl_transfer_keeper *k = &conn->keep;
CURLcode result;
int res;
@ -916,7 +945,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY;
/* 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) {
/* OK, now send the connect request to the proxy */
@ -1039,18 +1068,18 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
letter = line_start[perline];
line_start[perline]=0; /* zero terminate the buffer */
if((checkprefix("WWW-Authenticate:", line_start) &&
(401 == httpcode)) ||
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", line_start) &&
(407 == httpcode))) {
result = Curl_http_auth(conn, httpcode, line_start);
(407 == k->httpcode))) {
result = Curl_http_input_auth(conn, k->httpcode, line_start);
if(result)
return result;
}
else if(2 == sscanf(line_start, "HTTP/1.%d %d",
&subversion,
&httpcode)) {
&k->httpcode)) {
/* store the HTTP code */
data->info.httpproxycode = httpcode;
data->info.httpproxycode = k->httpcode;
}
/* put back the letter we blanked out before */
line_start[perline]= letter;
@ -1073,8 +1102,9 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
} while(conn->newurl);
if(200 != httpcode) {
failf(data, "Received HTTP code %d from proxy after CONNECT", httpcode);
if(200 != k->httpcode) {
failf(data, "Received HTTP code %d from proxy after CONNECT",
k->httpcode);
return CURLE_RECV_ERROR;
}
@ -1084,7 +1114,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
Curl_safefree(conn->allocptr.proxyuserpwd);
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");
return CURLE_OK;
@ -1189,24 +1219,6 @@ CURLcode Curl_http_done(struct connectdata *conn)
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
* 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 */
result = http_auth_headers(conn, request, ppath);
result = Curl_http_output_auth(conn, request, ppath, FALSE);
if(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
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
@ -1583,7 +1596,7 @@ CURLcode Curl_http(struct connectdata *conn)
request,
ppath,
httpstring,
(conn->bits.httpproxy && conn->allocptr.proxyuserpwd)?
conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"",
conn->allocptr.userpwd?conn->allocptr.userpwd:"",
(conn->bits.use_range && conn->allocptr.rangeline)?
@ -1755,8 +1768,8 @@ CURLcode Curl_http(struct connectdata *conn)
/* setup variables for the upcoming transfer */
result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
data->state.authdone?FIRSTSOCKET:-1,
data->state.authdone?&http->writebytecount:NULL);
FIRSTSOCKET,
&http->writebytecount);
if(result) {
Curl_formclean(http->sendit); /* free that whole lot */
return result;
@ -1794,8 +1807,8 @@ CURLcode Curl_http(struct connectdata *conn)
/* prepare for transfer */
result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
data->state.authdone?FIRSTSOCKET:-1,
data->state.authdone?&http->writebytecount:NULL);
FIRSTSOCKET,
&http->writebytecount);
if(result)
return result;
break;
@ -1826,7 +1839,8 @@ CURLcode Curl_http(struct connectdata *conn)
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
to actually send off any data yet. Hence, we delay the sending of
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 */
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
headers which actually speeds up post operations (as there is
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 */
void Curl_http_auth_stage(struct SessionHandle *data, int stage);
CURLcode Curl_http_auth(struct connectdata *conn,
int httpcode, char *header);
void Curl_http_auth_act(struct connectdata *conn);
CURLcode Curl_http_input_auth(struct connectdata *conn,
int httpcode, char *header);
CURLcode Curl_http_auth_act(struct connectdata *conn);
int Curl_http_should_fail(struct connectdata *conn);
#endif

View file

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

View file

@ -38,12 +38,15 @@ enum {
};
/* 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 */
CURLcode Curl_output_digest(struct connectdata *conn,
bool proxy,
unsigned char *request,
unsigned char *uripath);
void Curl_digest_cleanup(struct SessionHandle *data);
void Curl_digest_cleanup_one(struct digestdata *dig);
#endif

View file

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

View file

@ -445,9 +445,9 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
/*
** Now that all of the headers have been parsed, see
** if we should give up and return an error.
*/
* When all the headers have been parsed, see if we should give
* up and return an error.
*/
if (Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d",
k->httpcode);
@ -483,19 +483,23 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
else {
/* we wanted to resume a download, although the server
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) */
* 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) */
failf (data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume.");
return CURLE_HTTP_RANGE_ERROR;
}
}
if(!stop_reading)
/* *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. */
Curl_http_auth_act(conn);
if(!stop_reading) {
/* 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. */
result = Curl_http_auth_act(conn);
if(result)
return result;
}
if(!k->header) {
/*
@ -593,22 +597,17 @@ CURLcode Curl_readwrite(struct connectdata *conn,
data->info.httpversion = k->httpversion;
/*
** This code executes as part of processing
** the header. As a result, it's not
** totally clear how to interpret the
** response code yet as that depends on what
** other headers may be present. 401 and
** 407 may be errors, but may be OK
** depending on how authentication is
** working. Other codes are definitely
** errors, so give up here.
*/
* This code executes as part of processing the header. As a
* result, it's not totally clear how to interpret the
* response code yet as that depends on what other headers may
* be present. 401 and 407 may be errors, but may be OK
* depending on how authentication is working. Other codes
* are definitely errors, so give up here.
*/
if (data->set.http_fail_on_error &&
(k->httpcode >= 400) &&
(k->httpcode != 401) &&
(k->httpcode != 407)) {
/* If we have been told to fail hard on HTTP-errors,
here is the check for that: */
/* serious error, go home! */
failf (data, "The requested URL returned error: %d",
k->httpcode);
@ -821,7 +820,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", k->p) &&
(407 == k->httpcode))) {
result = Curl_http_auth(conn, k->httpcode, k->p);
result = Curl_http_input_auth(conn, k->httpcode, k->p);
if(result)
return result;
}
@ -1514,10 +1513,9 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
data->state.this_is_a_follow = FALSE; /* reset this */
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.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,
do it now! */

View file

@ -1332,9 +1332,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
CURLcode Curl_disconnect(struct connectdata *conn)
{
struct SessionHandle *data;
if(!conn)
return CURLE_OK; /* this is closed and fine already */
data = conn->data;
/*
* 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
@ -1346,11 +1349,20 @@ CURLcode Curl_disconnect(struct connectdata *conn)
}
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-
related stuff. NTLM is connection-related so when we close the shop
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)
/* This is set if protocol-specific cleanups should be made */
@ -1358,8 +1370,8 @@ CURLcode Curl_disconnect(struct connectdata *conn)
if(-1 != conn->connectindex) {
/* unlink ourselves! */
infof(conn->data, "Closing connection #%d\n", conn->connectindex);
conn->data->state.connects[conn->connectindex] = NULL;
infof(data, "Closing connection #%d\n", conn->connectindex);
data->state.connects[conn->connectindex] = NULL;
}
Curl_safefree(conn->proto.generic);
@ -1488,7 +1500,7 @@ ConnectionExists(struct SessionHandle *data,
}
if((needle->protocol & PROT_FTP) ||
((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
and password as well */
if(!strequal(needle->user, check->user) ||

View file

@ -678,6 +678,16 @@ typedef enum {
#define MAX_CURL_USER_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 {
enum {
Curl_if_none,
@ -724,22 +734,16 @@ struct UrlState {
is always set TRUE when curl_easy_perform() is called. */
struct digestdata digest;
struct digestdata proxydigest;
#ifdef HAVE_GSSAPI
struct negotiatedata negotiate;
#endif
long authstage; /* 0 - authwant and authavail are still not initialized
401 - web authentication is performed
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 */
struct auth authhost;
struct auth authproxy;
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
ares_channel areschannel; /* for name resolves */
#endif