1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
26 defined(NTLM_WB_ENABLED)
31 * http://davenport.sourceforge.net/ntlm.html
32 * https://www.innovation.ch/java/ntlm.html
37 #ifdef HAVE_SYS_WAIT_H
50 #include "vauth/ntlm.h"
51 #include "curl_ntlm_wb.h"
54 /* The last 3 #include files should be in this order */
55 #include "curl_printf.h"
56 #include "curl_memory.h"
60 # define DEBUG_OUT(x) x
62 # define DEBUG_OUT(x) Curl_nop_stmt
65 /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
66 to avoid fooling the socket leak detector */
67 #if defined(HAVE_CLOSESOCKET)
68 # define sclose_nolog(x) closesocket((x))
69 #elif defined(HAVE_CLOSESOCKET_CAMEL)
70 # define sclose_nolog(x) CloseSocket((x))
72 # define sclose_nolog(x) close((x))
75 void Curl_ntlm_wb_cleanup(struct connectdata *conn)
77 if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
78 sclose(conn->ntlm_auth_hlpr_socket);
79 conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
82 if(conn->ntlm_auth_hlpr_pid) {
84 for(i = 0; i < 4; i++) {
85 pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG);
86 if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD)
90 kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
93 /* Give the process another moment to shut down cleanly before
94 bringing down the axe */
98 kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
104 conn->ntlm_auth_hlpr_pid = 0;
107 free(conn->challenge_header);
108 conn->challenge_header = NULL;
109 free(conn->response_header);
110 conn->response_header = NULL;
113 static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
115 curl_socket_t sockfds[2];
117 const char *username;
118 char *slash, *domain = NULL;
119 const char *ntlm_auth = NULL;
120 char *ntlm_auth_alloc = NULL;
121 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
122 struct passwd pw, *pw_res;
127 /* Return if communication with ntlm_auth already set up */
128 if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
129 conn->ntlm_auth_hlpr_pid)
133 /* The real ntlm_auth really doesn't like being invoked with an
134 empty username. It won't make inferences for itself, and expects
135 the client to do so (mostly because it's really designed for
136 servers like squid to use for auth, and client support is an
137 afterthought for it). So try hard to provide a suitable username
138 if we don't already have one. But if we can't, provide the
139 empty one anyway. Perhaps they have an implementation of the
140 ntlm_auth helper which *doesn't* need it so we might as well try */
141 if(!username || !username[0]) {
142 username = getenv("NTLMUSER");
143 if(!username || !username[0])
144 username = getenv("LOGNAME");
145 if(!username || !username[0])
146 username = getenv("USER");
147 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
148 if((!username || !username[0]) &&
149 !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
151 username = pw.pw_name;
154 if(!username || !username[0])
157 slash = strpbrk(username, "\\/");
159 if((domain = strdup(username)) == NULL)
160 return CURLE_OUT_OF_MEMORY;
161 slash = domain + (slash - username);
163 username = username + (slash - domain) + 1;
166 /* For testing purposes, when DEBUGBUILD is defined and environment
167 variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
168 NTLM challenge/response which only accepts commands and output
169 strings pre-written in test case definitions */
171 ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
173 ntlm_auth = ntlm_auth_alloc;
176 ntlm_auth = NTLM_WB_FILE;
178 if(access(ntlm_auth, X_OK) != 0) {
180 failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
181 ntlm_auth, error, Curl_strerror(conn, error));
185 if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
187 failf(conn->data, "Could not open socket pair. errno %d: %s",
188 error, Curl_strerror(conn, error));
193 if(child_pid == -1) {
197 failf(conn->data, "Could not fork. errno %d: %s",
198 error, Curl_strerror(conn, error));
201 else if(!child_pid) {
206 /* Don't use sclose in the child since it fools the socket leak detector */
207 sclose_nolog(sockfds[0]);
208 if(dup2(sockfds[1], STDIN_FILENO) == -1) {
210 failf(conn->data, "Could not redirect child stdin. errno %d: %s",
211 error, Curl_strerror(conn, error));
215 if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
217 failf(conn->data, "Could not redirect child stdout. errno %d: %s",
218 error, Curl_strerror(conn, error));
223 execl(ntlm_auth, ntlm_auth,
224 "--helper-protocol", "ntlmssp-client-1",
225 "--use-cached-creds",
226 "--username", username,
230 execl(ntlm_auth, ntlm_auth,
231 "--helper-protocol", "ntlmssp-client-1",
232 "--use-cached-creds",
233 "--username", username,
237 sclose_nolog(sockfds[1]);
238 failf(conn->data, "Could not execl(). errno %d: %s",
239 error, Curl_strerror(conn, error));
244 conn->ntlm_auth_hlpr_socket = sockfds[0];
245 conn->ntlm_auth_hlpr_pid = child_pid;
247 free(ntlm_auth_alloc);
252 free(ntlm_auth_alloc);
253 return CURLE_REMOTE_ACCESS_DENIED;
256 static CURLcode ntlm_wb_response(struct connectdata *conn,
257 const char *input, curlntlm state)
259 char *buf = malloc(NTLM_BUFSIZE);
260 size_t len_in = strlen(input), len_out = 0;
263 return CURLE_OUT_OF_MEMORY;
266 ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
268 /* Interrupted by a signal, retry it */
271 /* write failed if other errors happen */
282 size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE);
292 if(buf[len_out - 1] == '\n') {
293 buf[len_out - 1] = '\0';
296 newbuf = realloc(buf, len_out + NTLM_BUFSIZE);
299 return CURLE_OUT_OF_MEMORY;
304 /* Samba/winbind installed but not configured */
305 if(state == NTLMSTATE_TYPE1 &&
307 buf[0] == 'P' && buf[1] == 'W')
309 /* invalid response */
312 if(state == NTLMSTATE_TYPE1 &&
313 (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
315 if(state == NTLMSTATE_TYPE2 &&
316 (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
317 (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
320 conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
325 return CURLE_REMOTE_ACCESS_DENIED;
329 * This is for creating ntlm header output by delegating challenge/response
330 * to Samba's winbind daemon helper ntlm_auth.
332 CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
335 /* point to the address of the pointer that holds the string to send to the
336 server, which is for a plain host or for a HTTP proxy */
338 /* point to the name and password for this */
340 /* point to the correct struct with this */
341 struct ntlmdata *ntlm;
344 CURLcode res = CURLE_OK;
348 DEBUGASSERT(conn->data);
351 allocuserpwd = &conn->allocptr.proxyuserpwd;
352 userp = conn->proxyuser;
353 ntlm = &conn->proxyntlm;
354 authp = &conn->data->state.authproxy;
357 allocuserpwd = &conn->allocptr.userpwd;
360 authp = &conn->data->state.authhost;
364 /* not set means empty */
368 switch(ntlm->state) {
369 case NTLMSTATE_TYPE1:
371 /* Use Samba's 'winbind' daemon to support NTLM authentication,
372 * by delegating the NTLM challenge/response protocal to a helper
374 * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
375 * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
376 * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
377 * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
378 * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
379 * filename of ntlm_auth helper.
380 * If NTLM authentication using winbind fails, go back to original
381 * request handling process.
383 /* Create communication with ntlm_auth */
384 res = ntlm_wb_init(conn, userp);
387 res = ntlm_wb_response(conn, "YR\n", ntlm->state);
392 *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
393 proxy ? "Proxy-" : "",
394 conn->response_header);
395 DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
396 free(conn->response_header);
397 conn->response_header = NULL;
399 case NTLMSTATE_TYPE2:
400 input = aprintf("TT %s\n", conn->challenge_header);
402 return CURLE_OUT_OF_MEMORY;
403 res = ntlm_wb_response(conn, input, ntlm->state);
410 *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
411 proxy ? "Proxy-" : "",
412 conn->response_header);
413 DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
414 ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
416 Curl_ntlm_wb_cleanup(conn);
418 case NTLMSTATE_TYPE3:
419 /* connection is already authenticated,
420 * don't send a header in future requests */
430 #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */