smtp: use the upload buffer size for scratch buffer malloc
[platform/upstream/curl.git] / lib / http_ntlm.c
index 756ff52..0f1edcf 100644 (file)
@@ -5,11 +5,11 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
  *
  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  * copies of the Software, and permit persons to whom the Software is
  *
  ***************************************************************************/
 
-#include "setup.h"
+#include "curl_setup.h"
 
-/* NTLM details:
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM)
 
-   http://davenport.sourceforge.net/ntlm.html
-   http://www.innovation.ch/java/ntlm.html
-*/
-
-#ifndef CURL_DISABLE_HTTP
-#ifdef USE_NTLM
+/*
+ * NTLM details:
+ *
+ * https://davenport.sourceforge.io/ntlm.html
+ * https://www.innovation.ch/java/ntlm.html
+ */
 
 #define DEBUG_ME 0
 
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-
-#if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
-#include <netdb.h>
-#endif
-
 #include "urldata.h"
-#include "non-ascii.h"  /* for Curl_convert_... prototypes */
 #include "sendf.h"
-#include "select.h"
-#include "rawstr.h"
-#include "curl_base64.h"
+#include "strcase.h"
 #include "http_ntlm.h"
+#include "curl_ntlm_core.h"
+#include "curl_ntlm_wb.h"
+#include "vauth/vauth.h"
 #include "url.h"
-#include "strerror.h"
-#include "curl_gethostname.h"
-#include "curl_memory.h"
 
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+/* SSL backend-specific #if branches in this file must be kept in the order
+   documented in curl_ntlm_core. */
+#if defined(NTLM_NEEDS_NSS_INIT)
+#include "vtls/nssg.h"
+#elif defined(USE_WINDOWS_SSPI)
+#include "curl_sspi.h"
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
 
 #if DEBUG_ME
 # define DEBUG_OUT(x) x
 #else
-# define DEBUG_OUT(x)
+# define DEBUG_OUT(x) Curl_nop_stmt
 #endif
 
 CURLcode Curl_input_ntlm(struct connectdata *conn,
@@ -77,18 +70,8 @@ CURLcode Curl_input_ntlm(struct connectdata *conn,
   struct ntlmdata *ntlm;
   CURLcode result = CURLE_OK;
 
-#ifdef USE_NSS
-  result = Curl_nss_force_init(conn->data);
-  if(result)
-    return result;
-#endif
-
   ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
 
-  /* skip initial whitespaces */
-  while(*header && ISSPACE(*header))
-    header++;
-
   if(checkprefix("NTLM", header)) {
     header += strlen("NTLM");
 
@@ -96,360 +79,45 @@ CURLcode Curl_input_ntlm(struct connectdata *conn,
       header++;
 
     if(*header) {
-      /* We got a type-2 message */
-
-      result = Curl_ntlm_decode_type2_message(conn->data, header, ntlm);
-      if(CURLE_OK != result)
+      result = Curl_auth_decode_ntlm_type2_message(conn->data, header, ntlm);
+      if(result)
         return result;
+
+      ntlm->state = NTLMSTATE_TYPE2; /* We got a type-2 message */
     }
     else {
-      if(ntlm->state >= NTLMSTATE_TYPE1) {
+      if(ntlm->state == NTLMSTATE_LAST) {
+        infof(conn->data, "NTLM auth restarted\n");
+        Curl_http_ntlm_cleanup(conn);
+      }
+      else if(ntlm->state == NTLMSTATE_TYPE3) {
+        infof(conn->data, "NTLM handshake rejected\n");
+        Curl_http_ntlm_cleanup(conn);
+        ntlm->state = NTLMSTATE_NONE;
+        return CURLE_REMOTE_ACCESS_DENIED;
+      }
+      else if(ntlm->state >= NTLMSTATE_TYPE1) {
         infof(conn->data, "NTLM handshake failure (internal error)\n");
         return CURLE_REMOTE_ACCESS_DENIED;
       }
 
-      ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
+      ntlm->state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
     }
   }
 
   return result;
 }
 
-#ifdef WINBIND_NTLM_AUTH_ENABLED
-static void sso_ntlm_close(struct connectdata *conn)
-{
-  if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
-    sclose(conn->ntlm_auth_hlpr_socket);
-    conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
-  }
-
-  if(conn->ntlm_auth_hlpr_pid) {
-    int i;
-    for(i = 0; i < 4; i++) {
-      pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG);
-      if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD)
-        break;
-      switch(i) {
-      case 0:
-        kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
-        break;
-      case 1:
-        /* Give the process another moment to shut down cleanly before
-           bringing down the axe */
-        Curl_wait_ms(1);
-        break;
-      case 2:
-        kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
-        break;
-      case 3:
-        break;
-      }
-    }
-    conn->ntlm_auth_hlpr_pid = 0;
-  }
-
-  Curl_safefree(conn->challenge_header);
-  conn->challenge_header = NULL;
-  Curl_safefree(conn->response_header);
-  conn->response_header = NULL;
-}
-
-static CURLcode sso_ntlm_initiate(struct connectdata *conn,
-                                  const char *userp)
-{
-  curl_socket_t sockfds[2];
-  pid_t child_pid;
-  const char *username;
-  char *slash, *domain = NULL;
-  const char *ntlm_auth = NULL;
-  char *ntlm_auth_alloc = NULL;
-  int error;
-
-  /* Return if communication with ntlm_auth already set up */
-  if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
-     conn->ntlm_auth_hlpr_pid)
-    return CURLE_OK;
-
-  username = userp;
-  slash = strpbrk(username, "\\/");
-  if(slash) {
-    if((domain = strdup(username)) == NULL)
-      return CURLE_OUT_OF_MEMORY;
-    slash = domain + (slash - username);
-    *slash = '\0';
-    username = username + (slash - domain) + 1;
-  }
-
-  /* For testing purposes, when DEBUGBUILD is defined and environment
-     variable CURL_NTLM_AUTH is set a fake_ntlm is used to perform
-     NTLM challenge/response which only accepts commands and output
-     strings pre-written in test case definitions */
-#ifdef DEBUGBUILD
-  ntlm_auth_alloc = curl_getenv("CURL_NTLM_AUTH");
-  if(ntlm_auth_alloc)
-    ntlm_auth = ntlm_auth_alloc;
-  else
-#endif
-    ntlm_auth = WINBIND_NTLM_AUTH_FILE;
-
-  if(access(ntlm_auth, X_OK) != 0) {
-    error = ERRNO;
-    failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
-          ntlm_auth, error, Curl_strerror(conn, error));
-    goto done;
-  }
-
-  if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
-    error = ERRNO;
-    failf(conn->data, "Could not open socket pair. errno %d: %s",
-          error, Curl_strerror(conn, error));
-    goto done;
-  }
-
-  child_pid = fork();
-  if(child_pid == -1) {
-    error = ERRNO;
-    sclose(sockfds[0]);
-    sclose(sockfds[1]);
-    failf(conn->data, "Could not fork. errno %d: %s",
-          error, Curl_strerror(conn, error));
-    goto done;
-  }
-  else if(!child_pid) {
-    /*
-     * child process
-     */
-
-    sclose(sockfds[0]);
-
-    if(dup2(sockfds[1], STDIN_FILENO) == -1) {
-      error = ERRNO;
-      failf(conn->data, "Could not redirect child stdin. errno %d: %s",
-            error, Curl_strerror(conn, error));
-      exit(1);
-    }
-
-    if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
-      error = ERRNO;
-      failf(conn->data, "Could not redirect child stdout. errno %d: %s",
-            error, Curl_strerror(conn, error));
-      exit(1);
-    }
-
-    if(domain)
-      execl(ntlm_auth, ntlm_auth,
-            "--helper-protocol", "ntlmssp-client-1",
-            "--use-cached-creds",
-            "--username", username,
-            "--domain", domain,
-            NULL);
-    else
-      execl(ntlm_auth, ntlm_auth,
-            "--helper-protocol", "ntlmssp-client-1",
-            "--use-cached-creds",
-            "--username", username,
-            NULL);
-
-    error = ERRNO;
-    sclose(sockfds[1]);
-    failf(conn->data, "Could not execl(). errno %d: %s",
-          error, Curl_strerror(conn, error));
-    exit(1);
-  }
-
-  sclose(sockfds[1]);
-  conn->ntlm_auth_hlpr_socket = sockfds[0];
-  conn->ntlm_auth_hlpr_pid = child_pid;
-  Curl_safefree(domain);
-  Curl_safefree(ntlm_auth_alloc);
-  return CURLE_OK;
-
-done:
-  Curl_safefree(domain);
-  Curl_safefree(ntlm_auth_alloc);
-  return CURLE_REMOTE_ACCESS_DENIED;
-}
-
-static CURLcode sso_ntlm_response(struct connectdata *conn,
-                                  const char *input, curlntlm state)
-{
-  ssize_t size;
-  char buf[200]; /* enough, type 1, 3 message length is less then 200 */
-  char *tmpbuf = buf;
-  size_t len_in = strlen(input), len_out = sizeof(buf);
-
-  while(len_in > 0) {
-    ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
-    if(written == -1) {
-      /* Interrupted by a signal, retry it */
-      if(errno == EINTR)
-        continue;
-      /* write failed if other errors happen */
-      goto done;
-    }
-    input += written;
-    len_in -= written;
-  }
-  /* Read one line */
-  while(len_out > 0) {
-    size = sread(conn->ntlm_auth_hlpr_socket, tmpbuf, len_out);
-    if(size == -1) {
-      if(errno == EINTR)
-        continue;
-      goto done;
-    }
-    else if(size == 0)
-      goto done;
-    else if(tmpbuf[size - 1] == '\n') {
-      tmpbuf[size - 1] = '\0';
-      goto wrfinish;
-    }
-    tmpbuf += size;
-    len_out -= size;
-  }
-  goto done;
-wrfinish:
-  /* Samba/winbind installed but not configured */
-  if(state == NTLMSTATE_TYPE1 &&
-     size == 3 &&
-     buf[0] == 'P' && buf[1] == 'W')
-    return CURLE_REMOTE_ACCESS_DENIED;
-  /* invalid response */
-  if(size < 4)
-    goto done;
-  if(state == NTLMSTATE_TYPE1 &&
-     (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
-    goto done;
-  if(state == NTLMSTATE_TYPE2 &&
-     (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
-     (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
-    goto done;
-
-  conn->response_header = aprintf("NTLM %.*s", size - 4, buf + 3);
-  return CURLE_OK;
-done:
-  return CURLE_REMOTE_ACCESS_DENIED;
-}
-
-/*
- * This is for creating ntlm header output by delegating challenge/response
- * to a Samba's daemon helper ntlm_auth
- */
-CURLcode Curl_output_ntlm_sso(struct connectdata *conn,
-                              bool proxy)
-{
-  /* point to the address of the pointer that holds the string to sent to the
-     server, which is for a plain host or for a HTTP proxy */
-  char **allocuserpwd;
-  /* point to the name and password for this */
-  const char *userp;
-  /* point to the correct struct with this */
-  struct ntlmdata *ntlm;
-  struct auth *authp;
-
-  CURLcode res = CURLE_OK;
-  char *input;
-
-  DEBUGASSERT(conn);
-  DEBUGASSERT(conn->data);
-
-  if(proxy) {
-    allocuserpwd = &conn->allocptr.proxyuserpwd;
-    userp = conn->proxyuser;
-    ntlm = &conn->proxyntlm;
-    authp = &conn->data->state.authproxy;
-  }
-  else {
-    allocuserpwd = &conn->allocptr.userpwd;
-    userp = conn->user;
-    ntlm = &conn->ntlm;
-    authp = &conn->data->state.authhost;
-  }
-  authp->done = FALSE;
-
-  /* not set means empty */
-  if(!userp)
-    userp="";
-
-  switch(ntlm->state) {
-  case NTLMSTATE_TYPE1:
-  default:
-    /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
-     * by delegating the NTLM challenge/response protocal to a helper
-     * in ntlm_auth.
-     * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
-     * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
-     * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
-     * Preprocessor symbol 'WINBIND_NTLM_AUTH_ENABLED' is defined when
-     * this feature is enabled and 'WINBIND_NTLM_AUTH_FILE' symbol holds
-     * absolute filename of ntlm_auth helper.
-     * If NTLM single-sign-on fails, go back to original request
-     * handling process.
-     */
-    /* Create communication with ntlm_auth */
-    res = sso_ntlm_initiate(conn, userp);
-    if(res)
-      return res;
-    res = sso_ntlm_response(conn, "YR\n", ntlm->state);
-    if(res)
-      return res;
-
-    Curl_safefree(*allocuserpwd);
-    *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
-                            proxy ? "Proxy-" : "",
-                            conn->response_header);
-    DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
-    Curl_safefree(conn->response_header);
-    conn->response_header = NULL;
-    break;
-  case NTLMSTATE_TYPE2:
-    input = aprintf("TT %s", conn->challenge_header);
-    if(!input)
-      return CURLE_OUT_OF_MEMORY;
-    res = sso_ntlm_response(conn,
-                            input,
-                            ntlm->state);
-    free(input);
-    input = NULL;
-    if(res)
-      return res;
-
-    Curl_safefree(*allocuserpwd);
-    *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
-                            proxy ? "Proxy-" : "",
-                            conn->response_header);
-    DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
-    ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
-    authp->done = TRUE;
-    sso_ntlm_close(conn);
-    break;
-  case NTLMSTATE_TYPE3:
-    /* connection is already authenticated,
-     * don't send a header in future requests */
-    if(*allocuserpwd) {
-      free(*allocuserpwd);
-      *allocuserpwd=NULL;
-    }
-    authp->done = TRUE;
-    break;
-  }
-
-  return CURLE_OK;
-}
-#endif /* WINBIND_NTLM_AUTH_ENABLED */
-
 /*
  * This is for creating ntlm header output
  */
-CURLcode Curl_output_ntlm(struct connectdata *conn,
-                          bool proxy)
+CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
 {
-  size_t size;
   char *base64 = NULL;
-  unsigned char ntlmbuf[NTLM_BUFSIZE];
-  CURLcode res;
+  size_t len = 0;
+  CURLcode result;
 
-  /* point to the address of the pointer that holds the string to sent to the
+  /* point to the address of the pointer that holds the string to send to the
      server, which is for a plain host or for a HTTP proxy */
   char **allocuserpwd;
 
@@ -461,22 +129,18 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
   struct ntlmdata *ntlm;
   struct auth *authp;
 
-  struct SessionHandle *data;
-
   DEBUGASSERT(conn);
   DEBUGASSERT(conn->data);
 
-  data = conn->data;
-
-#ifdef USE_NSS
+#if defined(NTLM_NEEDS_NSS_INIT)
   if(CURLE_OK != Curl_nss_force_init(conn->data))
     return CURLE_OUT_OF_MEMORY;
 #endif
 
   if(proxy) {
     allocuserpwd = &conn->allocptr.proxyuserpwd;
-    userp = conn->proxyuser;
-    passwdp = conn->proxypasswd;
+    userp = conn->http_proxy.user;
+    passwdp = conn->http_proxy.passwd;
     ntlm = &conn->proxyntlm;
     authp = &conn->data->state.authproxy;
   }
@@ -508,64 +172,55 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
   switch(ntlm->state) {
   case NTLMSTATE_TYPE1:
   default: /* for the weird cases we (re)start here */
-
     /* Create a type-1 message */
+    result = Curl_auth_create_ntlm_type1_message(conn->data, userp, passwdp,
+                                                 ntlm, &base64, &len);
+    if(result)
+      return result;
 
-    res = Curl_ntlm_create_type1_message(userp, passwdp,
-                                         ntlm, ntlmbuf, &size);
-
-    if(CURLE_OK == res) {
-      /* now size is the size of the base64 encoded package size */
-      size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, &base64);
-
-      if(size > 0) {
-        Curl_safefree(*allocuserpwd);
-        *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
-                                proxy ? "Proxy-" : "",
-                                base64);
-        DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
-        free(base64);
-      }
+    if(base64) {
+      free(*allocuserpwd);
+      *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+                              proxy ? "Proxy-" : "",
+                              base64);
+      free(base64);
+      if(!*allocuserpwd)
+        return CURLE_OUT_OF_MEMORY;
+
+      DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
     }
-    else
-      return CURLE_OUT_OF_MEMORY; /* FIX TODO */
-
     break;
 
   case NTLMSTATE_TYPE2:
     /* We already received the type-2 message, create a type-3 message */
+    result = Curl_auth_create_ntlm_type3_message(conn->data, userp, passwdp,
+                                                 ntlm, &base64, &len);
+    if(result)
+      return result;
 
-    res = Curl_ntlm_create_type3_message(conn->data, userp, passwdp,
-                                         ntlm, ntlmbuf, &size);
+    if(base64) {
+      free(*allocuserpwd);
+      *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+                              proxy ? "Proxy-" : "",
+                              base64);
+      free(base64);
+      if(!*allocuserpwd)
+        return CURLE_OUT_OF_MEMORY;
 
-    if(CURLE_OK == res) {
-      /* convert the binary blob into base64 */
-      size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, &base64);
+      DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
 
-      if(size > 0) {
-        Curl_safefree(*allocuserpwd);
-        *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
-                                proxy ? "Proxy-" : "",
-                                base64);
-        DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
-        free(base64);
-      }
+      ntlm->state = NTLMSTATE_TYPE3; /* we send a type-3 */
+      authp->done = TRUE;
     }
-    else
-      return CURLE_OUT_OF_MEMORY; /* FIX TODO */
-
-    ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
-    authp->done = TRUE;
-
     break;
 
   case NTLMSTATE_TYPE3:
     /* connection is already authenticated,
      * don't send a header in future requests */
-    if(*allocuserpwd) {
-      free(*allocuserpwd);
-      *allocuserpwd = NULL;
-    }
+    ntlm->state = NTLMSTATE_LAST;
+    /* fall-through */
+  case NTLMSTATE_LAST:
+    Curl_safefree(*allocuserpwd);
     authp->done = TRUE;
     break;
   }
@@ -575,15 +230,12 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
 
 void Curl_http_ntlm_cleanup(struct connectdata *conn)
 {
-#ifdef USE_WINDOWS_SSPI
-  Curl_ntlm_sspi_cleanup(&conn->ntlm);
-  Curl_ntlm_sspi_cleanup(&conn->proxyntlm);
-#elif defined(WINBIND_NTLM_AUTH_ENABLED)
-  sso_ntlm_close(conn);
-#else
-  (void)conn;
+  Curl_auth_ntlm_cleanup(&conn->ntlm);
+  Curl_auth_ntlm_cleanup(&conn->proxyntlm);
+
+#if defined(NTLM_WB_ENABLED)
+  Curl_ntlm_wb_cleanup(conn);
 #endif
 }
 
-#endif /* USE_NTLM */
-#endif /* !CURL_DISABLE_HTTP */
+#endif /* !CURL_DISABLE_HTTP && USE_NTLM */