General HTTP authentication cleanup and fixes
authorDaniel Stenberg <daniel@haxx.se>
Tue, 4 May 2004 07:52:53 +0000 (07:52 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 4 May 2004 07:52:53 +0000 (07:52 +0000)
20 files changed:
CHANGES
RELEASE-NOTES
lib/http.c
lib/http.h
lib/http_digest.c
lib/http_digest.h
lib/http_ntlm.c
lib/transfer.c
lib/url.c
lib/urldata.h
tests/data/Makefile.am
tests/data/test16
tests/data/test167 [new file with mode: 0644]
tests/data/test168 [new file with mode: 0644]
tests/data/test169 [new file with mode: 0644]
tests/data/test503
tests/data/test63
tests/data/test80
tests/data/test82
tests/data/test85

diff --git a/CHANGES b/CHANGES
index 15e066f..0c291a0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,19 @@
 
                                   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)
 - runtests.pl now scans the valgrind log for valgrind-detected memory leaks
   after each test case if valgrind was found and used.
index da90af9..eeecfc9 100644 (file)
@@ -2,12 +2,14 @@ Curl and libcurl 7.12.0.
 
  Public curl release number:               81
  Releases counted from the very beginning: 108
- Available command line options:           94
+ Available command line options:           95
  Available curl_easy_setopt() options:     113
  Number of public functions in libcurl:    35
 
 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 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
@@ -16,6 +18,7 @@ This release includes the following changes:
 
 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 curl_easy_duphandle() now works when ares-enabled
  o HTTP Digest authentication works a lot more like the RFC says
index fb538e5..b8243b3 100644 (file)
 #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) */
index 5dff8cb..944314a 100644 (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
index 9d58644..35ba171 100644 (file)
 #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
index a8b33ad..c7a41f1 100644 (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
index d1f4e30..9629b34 100644 (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;
   }
 
index 3df1f1f..ed1245f 100644 (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! */
index 7962d26..df07f53 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -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) ||
index 9cda637..8d29cf6 100644 (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
index 3e3749a..69d71d3 100644 (file)
@@ -3,26 +3,27 @@ install:
 test:
 
 EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
-test10 test109 test118 test13 test200 test28 test36 test47 test100     \
-test11 test119 test14 test201 test29 test37 test5 test101 test110      \
-test12 test15 test202 test3 test4 test6 test102 test111 test120 test16 \
-test21 test30 test7 test103 test112 test121 test17 test22 test300      \
-test8 test104 test113 test122 test18 test23 test301 test9 test105      \
-test114 test123 test19 test24 test302 test43 test31 test106 test115    \
-test124 test190 test25 test303 test44 test38 test107 test116 test125   \
-test2 test26 test33 test45 test126 test304 test39 test32 test128       \
-test48 test306 test130 test131 test132 test133 test134 test135 test305 \
-test49 test50 test51 test52 test53 test54 test55 test56 test500                \
-test501 test502 test503 test504 test136 test57 test137 test138 test58  \
-test139 test140 test141 test59 test60 test61 test142 test143 test62    \
-test63 test64 test65 test66 test144 test145 test67 test68 test41       \
-test40 test42 test69 test70 test71 test72 test73 test146 test505       \
-test74 test75 test76 test77 test78 test147 test148 test506 test79      \
-test80 test81 test82 test83 test84 test85 test86 test87 test507                \
-test149 test88 test89 test90 test508 test91 test92 test203 test93      \
-test94 test95 test509 test510 test97 test98 test99 test150 test151     \
-test152 test153 test154 test155 test156 test157 test158 test159 test511 \
-test160 test161 test162 test163 test164 test512 test165 test166
+ test10 test109 test118 test13 test200 test28 test36 test47 test100    \
+ test11 test119 test14 test201 test29 test37 test5 test101 test110     \
+ test12 test15 test202 test3 test4 test6 test102 test111 test120       \
+ test16 test21 test30 test7 test103 test112 test121 test17 test22      \
+ test300 test8 test104 test113 test122 test18 test23 test301 test9     \
+ test105 test114 test123 test19 test24 test302 test43 test31 test106   \
+ test115 test124 test190 test25 test303 test44 test38 test107 test116  \
+ test125 test2 test26 test33 test45 test126 test304 test39 test32      \
+ test128 test48 test306 test130 test131 test132 test133 test134                \
+ test135 test305 test49 test50 test51 test52 test53 test54 test55      \
+ test56 test500 test501 test502 test503 test504 test136 test57 test137 \
+ test138 test58 test139 test140 test141 test59 test60 test61 test142   \
+ test143 test62 test63 test64 test65 test66 test144 test145 test67     \
+ test68 test41 test40 test42 test69 test70 test71 test72 test73                \
+ test146 test505 test74 test75 test76 test77 test78 test147 test148    \
+ test506 test79 test80 test81 test82 test83 test84 test85 test86       \
+ test87 test507 test149 test88 test89 test90 test508 test91 test92     \
+ test203 test93 test94 test95 test509 test510 test97 test98 test99     \
+ test150 test151 test152 test153 test154 test155 test156 test157       \
+ 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
 # work. We need to fix the test suite's FTPS server first, then bring them
index 9e30ce8..4adea4a 100644 (file)
@@ -32,7 +32,7 @@ HTTP with proxy athorization
 </strip>
 <protocol>
 GET http://we.want.that.site.com/16 HTTP/1.1\r
-Proxy-authorization: Basic ZmFrZUB1c2VyOqenp2xvb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29uZw==
+Proxy-Authorization: Basic ZmFrZUB1c2VyOqenp2xvb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb29uZw==
 Host: we.want.that.site.com\r
 Pragma: no-cache\r
 Accept: */*\r
diff --git a/tests/data/test167 b/tests/data/test167
new file mode 100644 (file)
index 0000000..5dda877
--- /dev/null
@@ -0,0 +1,62 @@
+# Server-side
+<reply>
+<data>
+HTTP/1.1 401 Authorization Required swsclose\r
+WWW-Authenticate: Digest realm="weirdorealm", nonce="12345"\r
+\r
+</data>
+
+<data1000>
+HTTP/1.1 200 OK swsclose\r
+Server: no
+\r
+Nice auth sir!
+</data1000>
+
+<datacheck>
+HTTP/1.1 401 Authorization Required swsclose\r
+WWW-Authenticate: Digest realm="weirdorealm", nonce="12345"\r
+\r
+HTTP/1.1 200 OK swsclose\r
+Server: no
+\r
+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\r
+Proxy-Authorization: Basic Zm9vOmJhcg==\r
+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\r
+Host: data.from.server.requiring.digest.hohoho.com\r
+Pragma: no-cache\r
+Accept: */*\r
+\r
+GET http://data.from.server.requiring.digest.hohoho.com/167 HTTP/1.1\r
+Proxy-Authorization: Basic Zm9vOmJhcg==\r
+Authorization: Digest username="digest", realm="weirdorealm", nonce="12345", uri="/167", response="13c7c02a252cbe1c46d8669898a3be26"\r
+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\r
+Host: data.from.server.requiring.digest.hohoho.com\r
+Pragma: no-cache\r
+Accept: */*\r
+\r
+</protocol>
+</verify>
diff --git a/tests/data/test168 b/tests/data/test168
new file mode 100644 (file)
index 0000000..e726b51
--- /dev/null
@@ -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\r
+Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"\r
+\r
+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\r
+WWW-Authenticate: Digest realm="realmweirdo", nonce="123456"\r
+\r
+you should ignore this data too
+</data1000>
+
+<data1001>
+HTTP/1.1 200 OK swsclose\r
+Server: no
+\r
+Nice auth sir!
+</data1001>
+
+<datacheck>
+HTTP/1.1 407 Authorization Required to proxy me my dear swsclose\r
+Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"\r
+\r
+HTTP/1.1 401 Authorization to the remote host as well swsbounce swsclose\r
+WWW-Authenticate: Digest realm="realmweirdo", nonce="123456"\r
+\r
+HTTP/1.1 200 OK swsclose\r
+Server: no
+\r
+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\r
+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\r
+Host: data.from.server.requiring.digest.hohoho.com\r
+Pragma: no-cache\r
+Accept: */*\r
+\r
+GET http://data.from.server.requiring.digest.hohoho.com/168 HTTP/1.1\r
+Proxy-Authorization: Digest username="digest", realm="weirdorealm", nonce="12345", uri="/168", response="4e79e4fc104ef1f16ab4567e1ad4dede"\r
+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\r
+Host: data.from.server.requiring.digest.hohoho.com\r
+Pragma: no-cache\r
+Accept: */*\r
+\r
+GET http://data.from.server.requiring.digest.hohoho.com/168 HTTP/1.1\r
+Proxy-Authorization: Digest username="digest", realm="weirdorealm", nonce="12345", uri="/168", response="4e79e4fc104ef1f16ab4567e1ad4dede"\r
+Authorization: Digest username="digest", realm="realmweirdo", nonce="123456", uri="/168", response="ca87f2d768a231e2d637a55698d5c416"\r
+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\r
+Host: data.from.server.requiring.digest.hohoho.com\r
+Pragma: no-cache\r
+Accept: */*\r
+\r
+</protocol>
+</verify>
diff --git a/tests/data/test169 b/tests/data/test169
new file mode 100644 (file)
index 0000000..ddd8e4c
--- /dev/null
@@ -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\r
+Proxy-Authenticate: NTLM\r
+\r
+And you should ignore this data.
+</data>
+
+# then this is returned since we get no server-auth
+<data1000>
+HTTP/1.1 200 Authorizated fine\r
+Content-Length: 27\r
+\r
+Welcome to the end station
+</data1000>
+
+<data1001>
+HTTP/1.1 407 NTLM type-1 received sending back type-2\r
+Server: Microsoft-IIS/5.0\r
+Content-Length: 34\r
+Content-Type: text/html; charset=iso-8859-1\r
+Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==\r
+\r
+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\r
+Server: Microsoft-IIS/5.0\r
+WWW-Authenticate: Digest realm="r e a l m", nonce="abcdef"\r
+Content-Length: 40\r
+Content-Type: text/html; charset=iso-8859-1\r
+\r
+We have not authenticated with the server yet
+</data1002>
+
+<datacheck>
+HTTP/1.1 407 NTLM type-1 received sending back type-2\r
+Server: Microsoft-IIS/5.0\r
+Content-Length: 34\r
+Content-Type: text/html; charset=iso-8859-1\r
+Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgAyAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwBjAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==\r
+\r
+HTTP/1.1 401 You now need to authenticate with the host\r
+Server: Microsoft-IIS/5.0\r
+WWW-Authenticate: Digest realm="r e a l m", nonce="abcdef"\r
+Content-Length: 40\r
+Content-Type: text/html; charset=iso-8859-1\r
+\r
+HTTP/1.1 200 Authorizated fine\r
+Content-Length: 27\r
+\r
+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>
index 24d55dc..9f26e29 100644 (file)
@@ -49,7 +49,7 @@ moo
 <verify>
 <protocol>
 CONNECT 127.0.0.1:8433 HTTP/1.0\r
-Proxy-authorization: Basic dGVzdDppbmc=\r
+Proxy-Authorization: Basic dGVzdDppbmc=\r
 \r
 GET /503 HTTP/1.1\r
 Authorization: Basic dGVzdDppbmc=\r
index 3806237..ecca6b2 100644 (file)
@@ -32,7 +32,7 @@ http://we.want.that.site.com/63
 </strip>
 <protocol>
 GET http://we.want.that.site.com/63 HTTP/1.1\r
-Proxy-authorization: Basic ZmFrZTp1c2Vy
+Proxy-Authorization: Basic ZmFrZTp1c2Vy
 Host: we.want.that.site.com\r
 Pragma: no-cache\r
 Accept: */*\r
index 21e9bbd..dd4f060 100644 (file)
@@ -45,7 +45,7 @@ http://%HOSTIP:%HOSTPORT/we/want/that/page/80 -p -x %HOSTIP:%HOSTPORT --user iam
 </strip>
 <protocol>
 CONNECT 127.0.0.1:8999 HTTP/1.0\r
-Proxy-authorization: Basic eW91YXJlOnlvdXJzZWxm\r
+Proxy-Authorization: Basic eW91YXJlOnlvdXJzZWxm\r
 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3\r
 \r
 GET /we/want/that/page/80 HTTP/1.1\r
index fc4e490..7b49f89 100644 (file)
@@ -35,7 +35,7 @@ http://%HOSTIP:%HOSTPORT/82 --proxy-user testuser:testpass -x http://%HOSTIP:%HO
 </strip>
 <protocol>
 GET http://127.0.0.1:8999/82 HTTP/1.1\r
-Proxy-authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=\r
+Proxy-Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=\r
 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\r
 Host: 127.0.0.1:8999\r
 Pragma: no-cache\r
index a1fe043..b5c6e96 100644 (file)
@@ -34,7 +34,7 @@ http://%HOSTIP:%HOSTPORT/we/want/that/page/85 -x %HOSTIP:%HOSTPORT --user iam:my
 </strip>
 <protocol>
 GET http://127.0.0.1:8999/we/want/that/page/85 HTTP/1.1\r
-Proxy-authorization: Basic dGVzdGluZzp0aGlz\r
+Proxy-Authorization: Basic dGVzdGluZzp0aGlz\r
 Authorization: Basic aWFtOm15c2VsZg==\r
 User-Agent: curl/7.10.7-pre2 (i686-pc-linux-gnu) libcurl/7.10.7-pre2 OpenSSL/0.9.7a zlib/1.1.3\r
 Host: 127.0.0.1:8999\r