diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/http.c | 400 | ||||
| -rw-r--r-- | lib/http.h | 6 | ||||
| -rw-r--r-- | lib/http_digest.c | 76 | ||||
| -rw-r--r-- | lib/http_digest.h | 5 | ||||
| -rw-r--r-- | lib/http_ntlm.c | 14 | ||||
| -rw-r--r-- | lib/transfer.c | 50 | ||||
| -rw-r--r-- | lib/url.c | 22 | ||||
| -rw-r--r-- | lib/urldata.h | 22 | 
8 files changed, 327 insertions, 268 deletions
| diff --git a/lib/http.c b/lib/http.c index fb538e516..b8243b35a 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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; - -  sprintf(data->state.buffer, "%s:%s", conn->user, conn->passwd); -  if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer), +  char **userp; +  char *user; +  char *pwd; + +  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); +    /* 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(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 -    return CURLE_OUT_OF_MEMORY; -  return CURLE_OK; +    return FALSE; + +  return picked;  }  /* - * 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. + * 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.   */ -void Curl_http_auth_act(struct connectdata *conn) +CURLcode Curl_http_auth_act(struct connectdata *conn)  {    struct SessionHandle *data = conn->data; +  bool pickhost; +  bool pickproxy; +  CURLcode code = CURLE_OK; -  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.authproblem) +    return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK; -    if(data->state.authwant) -      conn->newurl = strdup(data->change.url); /* clone URL */ -    data->state.authavail = CURLAUTH_NONE; /* clear it here */ +  if(conn->bits.user_passwd) { +    pickhost = pickoneauth(&data->state.authhost); +    if(!pickhost && (conn->keep.httpcode == 401)) +      data->state.authproblem = TRUE;    } -  else if(!data->state.authdone && (data->info.httpcode < 400)) { +  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; -          } +        if(CURLDIGEST_FINE != dig) { +          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)) { +        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. */ -          data->state.authavail = CURLAUTH_NONE; +          authp->avail = CURLAUTH_NONE;            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; -        }        } +    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; @@ -1190,24 +1220,6 @@ CURLcode Curl_http_done(struct connectdata *conn)  }  /* - * 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   * HTTP request. @@ -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) */ diff --git a/lib/http.h b/lib/http.h index 5dff8cb71..944314a55 100644 --- a/lib/http.h +++ b/lib/http.h @@ -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 diff --git a/lib/http_digest.c b/lib/http_digest.c index 9d5864496..35ba17180 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -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 diff --git a/lib/http_digest.h b/lib/http_digest.h index a8b33add8..c7a41f1b4 100644 --- a/lib/http_digest.h +++ b/lib/http_digest.h @@ -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 diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index d1f4e306c..9629b34d1 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -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;    } diff --git a/lib/transfer.c b/lib/transfer.c index 3df1f1fd0..ed1245fe9 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -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! */ @@ -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) || diff --git a/lib/urldata.h b/lib/urldata.h index 9cda637b9..8d29cf66e 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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 | 
