Serge Semashko added CURLOPT_PROXYAUTH support, and now NTLM for proxies
authorDaniel Stenberg <daniel@haxx.se>
Mon, 11 Aug 2003 11:47:45 +0000 (11:47 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 11 Aug 2003 11:47:45 +0000 (11:47 +0000)
work.

lib/http.c
lib/http.h
lib/http_ntlm.c
lib/transfer.c
lib/url.c
lib/urldata.h

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