VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/curl_ntlm_wb.c@ 86643

Last change on this file since 86643 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette