diff --git a/lib/http.c b/lib/http.c index a59ed8ac1..35f795725 100644 --- a/lib/http.c +++ b/lib/http.c @@ -93,6 +93,7 @@ #include "http_negotiate.h" #include "url.h" #include "share.h" +#include "http.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -102,6 +103,8 @@ #include "memdebug.h" #endif +static CURLcode Curl_output_basic_proxy(struct connectdata *conn); + /* fread() emulation to provide POST and/or request data */ static int readmoredata(char *buffer, size_t size, @@ -430,6 +433,13 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port); + /* + * This code currently only supports Basic authentication for this CONNECT + * request to a proxy. + */ + if(conn->bits.proxy_user_passwd) + Curl_output_basic_proxy(conn); + /* OK, now send the connect request to the proxy */ result = Curl_sendf(tunnelsocket, conn, @@ -561,6 +571,8 @@ 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 */ + infof (data, "Proxy replied to CONNECT request\n"); return CURLE_OK; } @@ -672,6 +684,37 @@ static CURLcode Curl_output_basic(struct connectdata *conn) return CURLE_OK; } +static CURLcode Curl_output_basic_proxy(struct connectdata *conn) +{ + char *authorization; + struct SessionHandle *data=conn->data; + + 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; +} + +void Curl_http_auth_stage(struct SessionHandle *data, + int stage) +{ + if(stage == 401) + data->state.authwant = data->set.httpauth; + else if(stage == 407) + data->state.authwant = data->set.proxyauth; + else + return; /* bad input stage */ + data->state.authstage = stage; + data->state.authavail = CURLAUTH_NONE; +} + CURLcode Curl_http(struct connectdata *conn) { struct SessionHandle *data=conn->data; @@ -685,6 +728,13 @@ CURLcode Curl_http(struct connectdata *conn) char *ptr; char *request; + if(!data->state.authstage) { + if(conn->bits.httpproxy) + Curl_http_auth_stage(data, 407); + else + Curl_http_auth_stage(data, 401); + } + if(!conn->proto.http) { /* Only allocate this struct if we don't already have it! */ @@ -728,39 +778,62 @@ CURLcode Curl_http(struct connectdata *conn) curl_strequal(data->state.auth_host, conn->hostname) || data->set.http_disable_hostname_check_before_authentication) { -#ifdef GSSAPI - if((data->state.authwant == CURLAUTH_GSSNEGOTIATE) && - data->state.negotiate.context && - !GSS_ERROR(data->state.negotiate.status)) { - result = Curl_output_negotiate(conn); - if (result) - return result; - } - else -#endif + /* Send proxy authentication header if needed */ + if (data->state.authstage == 407) { #ifdef USE_SSLEAY - if(data->state.authwant == CURLAUTH_NTLM) { - result = Curl_output_ntlm(conn, FALSE); - if(result) - return result; - } - else -#endif - { - if((data->state.authwant == CURLAUTH_DIGEST) && - data->state.digest.nonce) { - result = Curl_output_digest(conn, - (unsigned char *)request, - (unsigned char *)ppath); + if(data->state.authwant == CURLAUTH_NTLM) { + result = Curl_output_ntlm(conn, TRUE); if(result) return result; } - else if((data->state.authwant == CURLAUTH_BASIC) && /* Basic */ - conn->bits.user_passwd && - !checkheaders(data, "Authorization:")) { - result = Curl_output_basic(conn); + else +#endif + if((data->state.authwant == CURLAUTH_BASIC) && /* Basic */ + conn->bits.proxy_user_passwd && + !checkheaders(data, "Proxy-authorization:")) { + result = Curl_output_basic_proxy(conn); if(result) return result; + /* Switch to web authentication after proxy authentication is done */ + Curl_http_auth_stage(data, 401); + } + } + /* Send web authentication header if needed */ + if (data->state.authstage == 401) { +#ifdef GSSAPI + if((data->state.authwant == CURLAUTH_GSSNEGOTIATE) && + data->state.negotiate.context && + !GSS_ERROR(data->state.negotiate.status)) { + result = Curl_output_negotiate(conn); + if (result) + return result; + } + else +#endif +#ifdef USE_SSLEAY + if(data->state.authwant == CURLAUTH_NTLM) { + result = Curl_output_ntlm(conn, FALSE); + if(result) + return result; + } + else +#endif + { + if((data->state.authwant == CURLAUTH_DIGEST) && + data->state.digest.nonce) { + result = Curl_output_digest(conn, + (unsigned char *)request, + (unsigned char *)ppath); + if(result) + return result; + } + else if((data->state.authwant == CURLAUTH_BASIC) && /* Basic */ + conn->bits.user_passwd && + !checkheaders(data, "Authorization:")) { + result = Curl_output_basic(conn); + if(result) + return result; + } } } } diff --git a/lib/http.h b/lib/http.h index da8e5db71..3cbbf841d 100644 --- a/lib/http.h +++ b/lib/http.h @@ -42,5 +42,6 @@ CURLcode Curl_http_connect(struct connectdata *conn); void Curl_httpchunk_init(struct connectdata *conn); CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, ssize_t length, ssize_t *wrote); +void Curl_http_auth_stage(struct SessionHandle *data, int stage); #endif #endif diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 5c6e3df4f..29615a470 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -551,7 +551,10 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; /* FIX TODO */ ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */ - + + /* Switch to web authentication after proxy authentication is done */ + if (proxy) + Curl_http_auth_stage(conn->data, 401); } break; diff --git a/lib/transfer.c b/lib/transfer.c index d9f3c8f9b..f33660812 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -726,12 +726,24 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(data->set.get_filetime) data->info.filetime = k->timeofdoc; } - else if(checkprefix("WWW-Authenticate:", k->p) && - (401 == k->httpcode)) { + else if((checkprefix("WWW-Authenticate:", k->p) && + (401 == k->httpcode)) || + (checkprefix("Proxy-authenticate:", k->p) && + (407 == k->httpcode))) { /* * This page requires authentication */ - char *start = k->p+strlen("WWW-Authenticate:"); + char *start = (k->httpcode == 407) ? + k->p+strlen("Proxy-authenticate:"): + k->p+strlen("WWW-Authenticate:"); + /* + * Switch from proxy to web authentication and back if needed + */ + if (k->httpcode == 407 && data->state.authstage != 407) + Curl_http_auth_stage(data, 407); + + else if (k->httpcode == 401 && data->state.authstage != 401) + Curl_http_auth_stage(data, 401); /* pass all white spaces */ while(*start && isspace((int)*start)) @@ -757,7 +769,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(data->state.authwant == CURLAUTH_NTLM) { /* NTLM authentication is activated */ CURLntlm ntlm = - Curl_input_ntlm(conn, FALSE, start); + Curl_input_ntlm(conn, k->httpcode == 407, start); if(CURLNTLM_BAD != ntlm) conn->newurl = strdup(data->change.url); /* clone string */ @@ -1506,8 +1518,8 @@ CURLcode Curl_pretransfer(struct SessionHandle *data) data->state.errorbuf = FALSE; /* no error has occurred */ /* set preferred authentication, default to basic */ - data->state.authwant = data->set.httpauth?data->set.httpauth:CURLAUTH_BASIC; - data->state.authavail = CURLAUTH_NONE; /* nothing so far */ + + data->state.authstage = 0; /* initialize authentication later */ /* If there was a list of cookie files to read and we haven't done it before, do it now! */ diff --git a/lib/url.c b/lib/url.c index 03059d84a..46245a5ce 100644 --- a/lib/url.c +++ b/lib/url.c @@ -312,6 +312,9 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ + data->set.httpauth = CURLAUTH_BASIC; /* defaults to basic authentication */ + data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic authentication */ + /* create an array with connection data struct pointers */ data->state.numconnects = 5; /* hard-coded right now */ data->state.connects = (struct connectdata **) @@ -878,6 +881,26 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) } break; + case CURLOPT_PROXYAUTH: + /* + * Set HTTP Authentication type BITMASK. + */ + { + long auth = va_arg(param, long); + /* switch off bits we can't support */ +#ifndef USE_SSLEAY + auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ +#endif +#ifndef GSSAPI + auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */ +#endif + if(!auth) + return CURLE_FAILED_INIT; /* no supported types left! */ + + data->set.proxyauth = auth; + } + break; + case CURLOPT_USERPWD: /* * user:password to use in the operation @@ -3066,6 +3089,7 @@ static CURLcode SetupConnection(struct connectdata *conn, /************************************************************* * Proxy authentication *************************************************************/ +#if 0 /* This code is not needed anymore (moved to http.c) */ if(conn->bits.proxy_user_passwd) { char *authorization; snprintf(data->state.buffer, BUFSIZE, "%s:%s", @@ -3078,6 +3102,7 @@ static CURLcode SetupConnection(struct connectdata *conn, free(authorization); } } +#endif /************************************************************* * Send user-agent to HTTP proxies even if the target protocol diff --git a/lib/urldata.h b/lib/urldata.h index 877c2e98e..a8c8dd13f 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -686,7 +686,12 @@ struct UrlState { struct negotiatedata negotiate; #endif - long authwant; /* inherited from what the user set with CURLOPT_HTTPAUTH */ + 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 */ #ifdef USE_ARES @@ -741,6 +746,7 @@ struct UserDefined { long use_port; /* which port to use (when not using default) */ char *userpwd; /* , if used */ long httpauth; /* what kind of HTTP authentication to use (bitmask) */ + long proxyauth; /* what kind of proxy authentication to use (bitmask) */ char *set_range; /* range, if used. See README for detailed specification on this syntax. */ long followlocation; /* as in HTTP Location: */