Revert "Update to 7.44.0"
[platform/upstream/curl.git] / lib / curl_ntlm_wb.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
26     defined(NTLM_WB_ENABLED)
27
28 /*
29  * NTLM details:
30  *
31  * http://davenport.sourceforge.net/ntlm.html
32  * http://www.innovation.ch/java/ntlm.html
33  */
34
35 #define DEBUG_ME 0
36
37 #ifdef HAVE_SYS_WAIT_H
38 #include <sys/wait.h>
39 #endif
40 #ifdef HAVE_SIGNAL_H
41 #include <signal.h>
42 #endif
43 #ifdef HAVE_PWD_H
44 #include <pwd.h>
45 #endif
46
47 #include "urldata.h"
48 #include "sendf.h"
49 #include "select.h"
50 #include "curl_ntlm_msgs.h"
51 #include "curl_ntlm_wb.h"
52 #include "url.h"
53 #include "strerror.h"
54 #include "curl_memory.h"
55
56 #define _MPRINTF_REPLACE /* use our functions only */
57 #include <curl/mprintf.h>
58
59 /* The last #include file should be: */
60 #include "memdebug.h"
61
62 #if DEBUG_ME
63 # define DEBUG_OUT(x) x
64 #else
65 # define DEBUG_OUT(x) Curl_nop_stmt
66 #endif
67
68 /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
69    to avoid fooling the socket leak detector */
70 #if defined(HAVE_CLOSESOCKET)
71 #  define sclose_nolog(x)  closesocket((x))
72 #elif defined(HAVE_CLOSESOCKET_CAMEL)
73 #  define sclose_nolog(x)  CloseSocket((x))
74 #else
75 #  define sclose_nolog(x)  close((x))
76 #endif
77
78 void Curl_ntlm_wb_cleanup(struct connectdata *conn)
79 {
80   if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
81     sclose(conn->ntlm_auth_hlpr_socket);
82     conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
83   }
84
85   if(conn->ntlm_auth_hlpr_pid) {
86     int i;
87     for(i = 0; i < 4; i++) {
88       pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG);
89       if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD)
90         break;
91       switch(i) {
92       case 0:
93         kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
94         break;
95       case 1:
96         /* Give the process another moment to shut down cleanly before
97            bringing down the axe */
98         Curl_wait_ms(1);
99         break;
100       case 2:
101         kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
102         break;
103       case 3:
104         break;
105       }
106     }
107     conn->ntlm_auth_hlpr_pid = 0;
108   }
109
110   Curl_safefree(conn->challenge_header);
111   conn->challenge_header = NULL;
112   Curl_safefree(conn->response_header);
113   conn->response_header = NULL;
114 }
115
116 static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
117 {
118   curl_socket_t sockfds[2];
119   pid_t child_pid;
120   const char *username;
121   char *slash, *domain = NULL;
122   const char *ntlm_auth = NULL;
123   char *ntlm_auth_alloc = NULL;
124 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
125   struct passwd pw, *pw_res;
126   char pwbuf[1024];
127 #endif
128   int error;
129
130   /* Return if communication with ntlm_auth already set up */
131   if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
132      conn->ntlm_auth_hlpr_pid)
133     return CURLE_OK;
134
135   username = userp;
136   /* The real ntlm_auth really doesn't like being invoked with an
137      empty username. It won't make inferences for itself, and expects
138      the client to do so (mostly because it's really designed for
139      servers like squid to use for auth, and client support is an
140      afterthought for it). So try hard to provide a suitable username
141      if we don't already have one. But if we can't, provide the
142      empty one anyway. Perhaps they have an implementation of the
143      ntlm_auth helper which *doesn't* need it so we might as well try */
144   if(!username || !username[0]) {
145     username = getenv("NTLMUSER");
146     if(!username || !username[0])
147       username = getenv("LOGNAME");
148     if(!username || !username[0])
149       username = getenv("USER");
150 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
151     if((!username || !username[0]) &&
152        !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
153        pw_res) {
154       username = pw.pw_name;
155     }
156 #endif
157     if(!username || !username[0])
158       username = userp;
159   }
160   slash = strpbrk(username, "\\/");
161   if(slash) {
162     if((domain = strdup(username)) == NULL)
163       return CURLE_OUT_OF_MEMORY;
164     slash = domain + (slash - username);
165     *slash = '\0';
166     username = username + (slash - domain) + 1;
167   }
168
169   /* For testing purposes, when DEBUGBUILD is defined and environment
170      variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
171      NTLM challenge/response which only accepts commands and output
172      strings pre-written in test case definitions */
173 #ifdef DEBUGBUILD
174   ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
175   if(ntlm_auth_alloc)
176     ntlm_auth = ntlm_auth_alloc;
177   else
178 #endif
179     ntlm_auth = NTLM_WB_FILE;
180
181   if(access(ntlm_auth, X_OK) != 0) {
182     error = ERRNO;
183     failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
184           ntlm_auth, error, Curl_strerror(conn, error));
185     goto done;
186   }
187
188   if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
189     error = ERRNO;
190     failf(conn->data, "Could not open socket pair. errno %d: %s",
191           error, Curl_strerror(conn, error));
192     goto done;
193   }
194
195   child_pid = fork();
196   if(child_pid == -1) {
197     error = ERRNO;
198     sclose(sockfds[0]);
199     sclose(sockfds[1]);
200     failf(conn->data, "Could not fork. errno %d: %s",
201           error, Curl_strerror(conn, error));
202     goto done;
203   }
204   else if(!child_pid) {
205     /*
206      * child process
207      */
208
209     /* Don't use sclose in the child since it fools the socket leak detector */
210     sclose_nolog(sockfds[0]);
211     if(dup2(sockfds[1], STDIN_FILENO) == -1) {
212       error = ERRNO;
213       failf(conn->data, "Could not redirect child stdin. errno %d: %s",
214             error, Curl_strerror(conn, error));
215       exit(1);
216     }
217
218     if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
219       error = ERRNO;
220       failf(conn->data, "Could not redirect child stdout. errno %d: %s",
221             error, Curl_strerror(conn, error));
222       exit(1);
223     }
224
225     if(domain)
226       execl(ntlm_auth, ntlm_auth,
227             "--helper-protocol", "ntlmssp-client-1",
228             "--use-cached-creds",
229             "--username", username,
230             "--domain", domain,
231             NULL);
232     else
233       execl(ntlm_auth, ntlm_auth,
234             "--helper-protocol", "ntlmssp-client-1",
235             "--use-cached-creds",
236             "--username", username,
237             NULL);
238
239     error = ERRNO;
240     sclose_nolog(sockfds[1]);
241     failf(conn->data, "Could not execl(). errno %d: %s",
242           error, Curl_strerror(conn, error));
243     exit(1);
244   }
245
246   sclose(sockfds[1]);
247   conn->ntlm_auth_hlpr_socket = sockfds[0];
248   conn->ntlm_auth_hlpr_pid = child_pid;
249   Curl_safefree(domain);
250   Curl_safefree(ntlm_auth_alloc);
251   return CURLE_OK;
252
253 done:
254   Curl_safefree(domain);
255   Curl_safefree(ntlm_auth_alloc);
256   return CURLE_REMOTE_ACCESS_DENIED;
257 }
258
259 static CURLcode ntlm_wb_response(struct connectdata *conn,
260                                  const char *input, curlntlm state)
261 {
262   char *buf = malloc(NTLM_BUFSIZE);
263   size_t len_in = strlen(input), len_out = 0;
264
265   if(!buf)
266     return CURLE_OUT_OF_MEMORY;
267
268   while(len_in > 0) {
269     ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
270     if(written == -1) {
271       /* Interrupted by a signal, retry it */
272       if(errno == EINTR)
273         continue;
274       /* write failed if other errors happen */
275       goto done;
276     }
277     input += written;
278     len_in -= written;
279   }
280   /* Read one line */
281   while(1) {
282     ssize_t size;
283     char *newbuf;
284
285     size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE);
286     if(size == -1) {
287       if(errno == EINTR)
288         continue;
289       goto done;
290     }
291     else if(size == 0)
292       goto done;
293
294     len_out += size;
295     if(buf[len_out - 1] == '\n') {
296       buf[len_out - 1] = '\0';
297       break;
298     }
299     newbuf = realloc(buf, len_out + NTLM_BUFSIZE);
300     if(!newbuf) {
301       free(buf);
302       return CURLE_OUT_OF_MEMORY;
303     }
304     buf = newbuf;
305   }
306
307   /* Samba/winbind installed but not configured */
308   if(state == NTLMSTATE_TYPE1 &&
309      len_out == 3 &&
310      buf[0] == 'P' && buf[1] == 'W')
311     return CURLE_REMOTE_ACCESS_DENIED;
312   /* invalid response */
313   if(len_out < 4)
314     goto done;
315   if(state == NTLMSTATE_TYPE1 &&
316      (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
317     goto done;
318   if(state == NTLMSTATE_TYPE2 &&
319      (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
320      (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
321     goto done;
322
323   conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
324   free(buf);
325   return CURLE_OK;
326 done:
327   free(buf);
328   return CURLE_REMOTE_ACCESS_DENIED;
329 }
330
331 /*
332  * This is for creating ntlm header output by delegating challenge/response
333  * to Samba's winbind daemon helper ntlm_auth.
334  */
335 CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
336                               bool proxy)
337 {
338   /* point to the address of the pointer that holds the string to send to the
339      server, which is for a plain host or for a HTTP proxy */
340   char **allocuserpwd;
341   /* point to the name and password for this */
342   const char *userp;
343   /* point to the correct struct with this */
344   struct ntlmdata *ntlm;
345   struct auth *authp;
346
347   CURLcode res = CURLE_OK;
348   char *input;
349
350   DEBUGASSERT(conn);
351   DEBUGASSERT(conn->data);
352
353   if(proxy) {
354     allocuserpwd = &conn->allocptr.proxyuserpwd;
355     userp = conn->proxyuser;
356     ntlm = &conn->proxyntlm;
357     authp = &conn->data->state.authproxy;
358   }
359   else {
360     allocuserpwd = &conn->allocptr.userpwd;
361     userp = conn->user;
362     ntlm = &conn->ntlm;
363     authp = &conn->data->state.authhost;
364   }
365   authp->done = FALSE;
366
367   /* not set means empty */
368   if(!userp)
369     userp="";
370
371   switch(ntlm->state) {
372   case NTLMSTATE_TYPE1:
373   default:
374     /* Use Samba's 'winbind' daemon to support NTLM authentication,
375      * by delegating the NTLM challenge/response protocal to a helper
376      * in ntlm_auth.
377      * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
378      * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
379      * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
380      * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
381      * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
382      * filename of ntlm_auth helper.
383      * If NTLM authentication using winbind fails, go back to original
384      * request handling process.
385      */
386     /* Create communication with ntlm_auth */
387     res = ntlm_wb_init(conn, userp);
388     if(res)
389       return res;
390     res = ntlm_wb_response(conn, "YR\n", ntlm->state);
391     if(res)
392       return res;
393
394     Curl_safefree(*allocuserpwd);
395     *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
396                             proxy ? "Proxy-" : "",
397                             conn->response_header);
398     DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
399     Curl_safefree(conn->response_header);
400     conn->response_header = NULL;
401     break;
402   case NTLMSTATE_TYPE2:
403     input = aprintf("TT %s\n", conn->challenge_header);
404     if(!input)
405       return CURLE_OUT_OF_MEMORY;
406     res = ntlm_wb_response(conn, input, ntlm->state);
407     free(input);
408     input = NULL;
409     if(res)
410       return res;
411
412     Curl_safefree(*allocuserpwd);
413     *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
414                             proxy ? "Proxy-" : "",
415                             conn->response_header);
416     DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
417     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
418     authp->done = TRUE;
419     Curl_ntlm_wb_cleanup(conn);
420     break;
421   case NTLMSTATE_TYPE3:
422     /* connection is already authenticated,
423      * don't send a header in future requests */
424     if(*allocuserpwd) {
425       free(*allocuserpwd);
426       *allocuserpwd=NULL;
427     }
428     authp->done = TRUE;
429     break;
430   }
431
432   return CURLE_OK;
433 }
434
435 #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */