curl_ntlm_wb.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 #include "curl_setup.h"
00024 
00025 #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
00026     defined(NTLM_WB_ENABLED)
00027 
00028 /*
00029  * NTLM details:
00030  *
00031  * http://davenport.sourceforge.net/ntlm.html
00032  * https://www.innovation.ch/java/ntlm.html
00033  */
00034 
00035 #define DEBUG_ME 0
00036 
00037 #ifdef HAVE_SYS_WAIT_H
00038 #include <sys/wait.h>
00039 #endif
00040 #ifdef HAVE_SIGNAL_H
00041 #include <signal.h>
00042 #endif
00043 #ifdef HAVE_PWD_H
00044 #include <pwd.h>
00045 #endif
00046 
00047 #include "urldata.h"
00048 #include "sendf.h"
00049 #include "select.h"
00050 #include "vauth/ntlm.h"
00051 #include "curl_ntlm_wb.h"
00052 #include "url.h"
00053 #include "strerror.h"
00054 #include "strdup.h"
00055 /* The last 3 #include files should be in this order */
00056 #include "curl_printf.h"
00057 #include "curl_memory.h"
00058 #include "memdebug.h"
00059 
00060 #if DEBUG_ME
00061 # define DEBUG_OUT(x) x
00062 #else
00063 # define DEBUG_OUT(x) Curl_nop_stmt
00064 #endif
00065 
00066 /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
00067    to avoid fooling the socket leak detector */
00068 #if defined(HAVE_CLOSESOCKET)
00069 #  define sclose_nolog(x)  closesocket((x))
00070 #elif defined(HAVE_CLOSESOCKET_CAMEL)
00071 #  define sclose_nolog(x)  CloseSocket((x))
00072 #else
00073 #  define sclose_nolog(x)  close((x))
00074 #endif
00075 
00076 void Curl_ntlm_wb_cleanup(struct connectdata *conn)
00077 {
00078   if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
00079     sclose(conn->ntlm_auth_hlpr_socket);
00080     conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
00081   }
00082 
00083   if(conn->ntlm_auth_hlpr_pid) {
00084     int i;
00085     for(i = 0; i < 4; i++) {
00086       pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG);
00087       if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD)
00088         break;
00089       switch(i) {
00090       case 0:
00091         kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
00092         break;
00093       case 1:
00094         /* Give the process another moment to shut down cleanly before
00095            bringing down the axe */
00096         Curl_wait_ms(1);
00097         break;
00098       case 2:
00099         kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
00100         break;
00101       case 3:
00102         break;
00103       }
00104     }
00105     conn->ntlm_auth_hlpr_pid = 0;
00106   }
00107 
00108   free(conn->challenge_header);
00109   conn->challenge_header = NULL;
00110   free(conn->response_header);
00111   conn->response_header = NULL;
00112 }
00113 
00114 static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
00115 {
00116   curl_socket_t sockfds[2];
00117   pid_t child_pid;
00118   const char *username;
00119   char *slash, *domain = NULL;
00120   const char *ntlm_auth = NULL;
00121   char *ntlm_auth_alloc = NULL;
00122 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
00123   struct passwd pw, *pw_res;
00124   char pwbuf[1024];
00125 #endif
00126   int error;
00127 
00128   /* Return if communication with ntlm_auth already set up */
00129   if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
00130      conn->ntlm_auth_hlpr_pid)
00131     return CURLE_OK;
00132 
00133   username = userp;
00134   /* The real ntlm_auth really doesn't like being invoked with an
00135      empty username. It won't make inferences for itself, and expects
00136      the client to do so (mostly because it's really designed for
00137      servers like squid to use for auth, and client support is an
00138      afterthought for it). So try hard to provide a suitable username
00139      if we don't already have one. But if we can't, provide the
00140      empty one anyway. Perhaps they have an implementation of the
00141      ntlm_auth helper which *doesn't* need it so we might as well try */
00142   if(!username || !username[0]) {
00143     username = getenv("NTLMUSER");
00144     if(!username || !username[0])
00145       username = getenv("LOGNAME");
00146     if(!username || !username[0])
00147       username = getenv("USER");
00148 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
00149     if((!username || !username[0]) &&
00150        !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
00151        pw_res) {
00152       username = pw.pw_name;
00153     }
00154 #endif
00155     if(!username || !username[0])
00156       username = userp;
00157   }
00158   slash = strpbrk(username, "\\/");
00159   if(slash) {
00160     domain = strdup(username);
00161     if(!domain)
00162       return CURLE_OUT_OF_MEMORY;
00163     slash = domain + (slash - username);
00164     *slash = '\0';
00165     username = username + (slash - domain) + 1;
00166   }
00167 
00168   /* For testing purposes, when DEBUGBUILD is defined and environment
00169      variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
00170      NTLM challenge/response which only accepts commands and output
00171      strings pre-written in test case definitions */
00172 #ifdef DEBUGBUILD
00173   ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
00174   if(ntlm_auth_alloc)
00175     ntlm_auth = ntlm_auth_alloc;
00176   else
00177 #endif
00178     ntlm_auth = NTLM_WB_FILE;
00179 
00180   if(access(ntlm_auth, X_OK) != 0) {
00181     error = ERRNO;
00182     failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
00183           ntlm_auth, error, Curl_strerror(conn, error));
00184     goto done;
00185   }
00186 
00187   if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
00188     error = ERRNO;
00189     failf(conn->data, "Could not open socket pair. errno %d: %s",
00190           error, Curl_strerror(conn, error));
00191     goto done;
00192   }
00193 
00194   child_pid = fork();
00195   if(child_pid == -1) {
00196     error = ERRNO;
00197     sclose(sockfds[0]);
00198     sclose(sockfds[1]);
00199     failf(conn->data, "Could not fork. errno %d: %s",
00200           error, Curl_strerror(conn, error));
00201     goto done;
00202   }
00203   else if(!child_pid) {
00204     /*
00205      * child process
00206      */
00207 
00208     /* Don't use sclose in the child since it fools the socket leak detector */
00209     sclose_nolog(sockfds[0]);
00210     if(dup2(sockfds[1], STDIN_FILENO) == -1) {
00211       error = ERRNO;
00212       failf(conn->data, "Could not redirect child stdin. errno %d: %s",
00213             error, Curl_strerror(conn, error));
00214       exit(1);
00215     }
00216 
00217     if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
00218       error = ERRNO;
00219       failf(conn->data, "Could not redirect child stdout. errno %d: %s",
00220             error, Curl_strerror(conn, error));
00221       exit(1);
00222     }
00223 
00224     if(domain)
00225       execl(ntlm_auth, ntlm_auth,
00226             "--helper-protocol", "ntlmssp-client-1",
00227             "--use-cached-creds",
00228             "--username", username,
00229             "--domain", domain,
00230             NULL);
00231     else
00232       execl(ntlm_auth, ntlm_auth,
00233             "--helper-protocol", "ntlmssp-client-1",
00234             "--use-cached-creds",
00235             "--username", username,
00236             NULL);
00237 
00238     error = ERRNO;
00239     sclose_nolog(sockfds[1]);
00240     failf(conn->data, "Could not execl(). errno %d: %s",
00241           error, Curl_strerror(conn, error));
00242     exit(1);
00243   }
00244 
00245   sclose(sockfds[1]);
00246   conn->ntlm_auth_hlpr_socket = sockfds[0];
00247   conn->ntlm_auth_hlpr_pid = child_pid;
00248   free(domain);
00249   free(ntlm_auth_alloc);
00250   return CURLE_OK;
00251 
00252 done:
00253   free(domain);
00254   free(ntlm_auth_alloc);
00255   return CURLE_REMOTE_ACCESS_DENIED;
00256 }
00257 
00258 static CURLcode ntlm_wb_response(struct connectdata *conn,
00259                                  const char *input, curlntlm state)
00260 {
00261   char *buf = malloc(NTLM_BUFSIZE);
00262   size_t len_in = strlen(input), len_out = 0;
00263 
00264   if(!buf)
00265     return CURLE_OUT_OF_MEMORY;
00266 
00267   while(len_in > 0) {
00268     ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
00269     if(written == -1) {
00270       /* Interrupted by a signal, retry it */
00271       if(errno == EINTR)
00272         continue;
00273       /* write failed if other errors happen */
00274       goto done;
00275     }
00276     input += written;
00277     len_in -= written;
00278   }
00279   /* Read one line */
00280   while(1) {
00281     ssize_t size;
00282     char *newbuf;
00283 
00284     size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE);
00285     if(size == -1) {
00286       if(errno == EINTR)
00287         continue;
00288       goto done;
00289     }
00290     else if(size == 0)
00291       goto done;
00292 
00293     len_out += size;
00294     if(buf[len_out - 1] == '\n') {
00295       buf[len_out - 1] = '\0';
00296       break;
00297     }
00298     newbuf = Curl_saferealloc(buf, len_out + NTLM_BUFSIZE);
00299     if(!newbuf)
00300       return CURLE_OUT_OF_MEMORY;
00301 
00302     buf = newbuf;
00303   }
00304 
00305   /* Samba/winbind installed but not configured */
00306   if(state == NTLMSTATE_TYPE1 &&
00307      len_out == 3 &&
00308      buf[0] == 'P' && buf[1] == 'W')
00309     goto done;
00310   /* invalid response */
00311   if(len_out < 4)
00312     goto done;
00313   if(state == NTLMSTATE_TYPE1 &&
00314      (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
00315     goto done;
00316   if(state == NTLMSTATE_TYPE2 &&
00317      (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
00318      (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
00319     goto done;
00320 
00321   conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
00322   free(buf);
00323   return CURLE_OK;
00324 done:
00325   free(buf);
00326   return CURLE_REMOTE_ACCESS_DENIED;
00327 }
00328 
00329 /*
00330  * This is for creating ntlm header output by delegating challenge/response
00331  * to Samba's winbind daemon helper ntlm_auth.
00332  */
00333 CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
00334                               bool proxy)
00335 {
00336   /* point to the address of the pointer that holds the string to send to the
00337      server, which is for a plain host or for a HTTP proxy */
00338   char **allocuserpwd;
00339   /* point to the name and password for this */
00340   const char *userp;
00341   /* point to the correct struct with this */
00342   struct ntlmdata *ntlm;
00343   struct auth *authp;
00344 
00345   CURLcode res = CURLE_OK;
00346   char *input;
00347 
00348   DEBUGASSERT(conn);
00349   DEBUGASSERT(conn->data);
00350 
00351   if(proxy) {
00352     allocuserpwd = &conn->allocptr.proxyuserpwd;
00353     userp = conn->http_proxy.user;
00354     ntlm = &conn->proxyntlm;
00355     authp = &conn->data->state.authproxy;
00356   }
00357   else {
00358     allocuserpwd = &conn->allocptr.userpwd;
00359     userp = conn->user;
00360     ntlm = &conn->ntlm;
00361     authp = &conn->data->state.authhost;
00362   }
00363   authp->done = FALSE;
00364 
00365   /* not set means empty */
00366   if(!userp)
00367     userp="";
00368 
00369   switch(ntlm->state) {
00370   case NTLMSTATE_TYPE1:
00371   default:
00372     /* Use Samba's 'winbind' daemon to support NTLM authentication,
00373      * by delegating the NTLM challenge/response protocal to a helper
00374      * in ntlm_auth.
00375      * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
00376      * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
00377      * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
00378      * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
00379      * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
00380      * filename of ntlm_auth helper.
00381      * If NTLM authentication using winbind fails, go back to original
00382      * request handling process.
00383      */
00384     /* Create communication with ntlm_auth */
00385     res = ntlm_wb_init(conn, userp);
00386     if(res)
00387       return res;
00388     res = ntlm_wb_response(conn, "YR\n", ntlm->state);
00389     if(res)
00390       return res;
00391 
00392     free(*allocuserpwd);
00393     *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
00394                             proxy ? "Proxy-" : "",
00395                             conn->response_header);
00396     DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
00397     free(conn->response_header);
00398     conn->response_header = NULL;
00399     break;
00400   case NTLMSTATE_TYPE2:
00401     input = aprintf("TT %s\n", conn->challenge_header);
00402     if(!input)
00403       return CURLE_OUT_OF_MEMORY;
00404     res = ntlm_wb_response(conn, input, ntlm->state);
00405     free(input);
00406     input = NULL;
00407     if(res)
00408       return res;
00409 
00410     free(*allocuserpwd);
00411     *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
00412                             proxy ? "Proxy-" : "",
00413                             conn->response_header);
00414     DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
00415     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
00416     authp->done = TRUE;
00417     Curl_ntlm_wb_cleanup(conn);
00418     break;
00419   case NTLMSTATE_TYPE3:
00420     /* connection is already authenticated,
00421      * don't send a header in future requests */
00422     free(*allocuserpwd);
00423     *allocuserpwd=NULL;
00424     authp->done = TRUE;
00425     break;
00426   }
00427 
00428   return CURLE_OK;
00429 }
00430 
00431 #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02