1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, 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 * http://www.innovation.ch/java/ntlm.html
37 #ifdef HAVE_SYS_WAIT_H
50 #include "curl_ntlm_msgs.h"
51 #include "curl_ntlm_wb.h"
54 #include "curl_printf.h"
56 /* The last #include files should be: */
57 #include "curl_memory.h"
61 # define DEBUG_OUT(x) x
63 # define DEBUG_OUT(x) Curl_nop_stmt
66 /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
67 to avoid fooling the socket leak detector */
68 #if defined(HAVE_CLOSESOCKET)
69 # define sclose_nolog(x) closesocket((x))
70 #elif defined(HAVE_CLOSESOCKET_CAMEL)
71 # define sclose_nolog(x) CloseSocket((x))
73 # define sclose_nolog(x) close((x))
76 void Curl_ntlm_wb_cleanup(struct connectdata *conn)
78 if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
79 sclose(conn->ntlm_auth_hlpr_socket);
80 conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
83 if(conn->ntlm_auth_hlpr_pid) {
85 for(i = 0; i < 4; i++) {
86 pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG);
87 if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD)
91 kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
94 /* Give the process another moment to shut down cleanly before
95 bringing down the axe */
99 kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
105 conn->ntlm_auth_hlpr_pid = 0;
108 free(conn->challenge_header);
109 conn->challenge_header = NULL;
110 free(conn->response_header);
111 conn->response_header = NULL;
114 static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
116 curl_socket_t sockfds[2];
118 const char *username;
119 char *slash, *domain = NULL;
120 const char *ntlm_auth = NULL;
121 char *ntlm_auth_alloc = NULL;
122 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
123 struct passwd pw, *pw_res;
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)
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) &&
152 username = pw.pw_name;
155 if(!username || !username[0])
158 slash = strpbrk(username, "\\/");
160 if((domain = strdup(username)) == NULL)
161 return CURLE_OUT_OF_MEMORY;
162 slash = domain + (slash - username);
164 username = username + (slash - domain) + 1;
167 /* For testing purposes, when DEBUGBUILD is defined and environment
168 variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
169 NTLM challenge/response which only accepts commands and output
170 strings pre-written in test case definitions */
172 ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
174 ntlm_auth = ntlm_auth_alloc;
177 ntlm_auth = NTLM_WB_FILE;
179 if(access(ntlm_auth, X_OK) != 0) {
181 failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
182 ntlm_auth, error, Curl_strerror(conn, error));
186 if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
188 failf(conn->data, "Could not open socket pair. errno %d: %s",
189 error, Curl_strerror(conn, error));
194 if(child_pid == -1) {
198 failf(conn->data, "Could not fork. errno %d: %s",
199 error, Curl_strerror(conn, error));
202 else if(!child_pid) {
207 /* Don't use sclose in the child since it fools the socket leak detector */
208 sclose_nolog(sockfds[0]);
209 if(dup2(sockfds[1], STDIN_FILENO) == -1) {
211 failf(conn->data, "Could not redirect child stdin. errno %d: %s",
212 error, Curl_strerror(conn, error));
216 if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
218 failf(conn->data, "Could not redirect child stdout. errno %d: %s",
219 error, Curl_strerror(conn, error));
224 execl(ntlm_auth, ntlm_auth,
225 "--helper-protocol", "ntlmssp-client-1",
226 "--use-cached-creds",
227 "--username", username,
231 execl(ntlm_auth, ntlm_auth,
232 "--helper-protocol", "ntlmssp-client-1",
233 "--use-cached-creds",
234 "--username", username,
238 sclose_nolog(sockfds[1]);
239 failf(conn->data, "Could not execl(). errno %d: %s",
240 error, Curl_strerror(conn, error));
245 conn->ntlm_auth_hlpr_socket = sockfds[0];
246 conn->ntlm_auth_hlpr_pid = child_pid;
248 free(ntlm_auth_alloc);
253 free(ntlm_auth_alloc);
254 return CURLE_REMOTE_ACCESS_DENIED;
257 static CURLcode ntlm_wb_response(struct connectdata *conn,
258 const char *input, curlntlm state)
260 char *buf = malloc(NTLM_BUFSIZE);
261 size_t len_in = strlen(input), len_out = 0;
264 return CURLE_OUT_OF_MEMORY;
267 ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
269 /* Interrupted by a signal, retry it */
272 /* write failed if other errors happen */
283 size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE);
293 if(buf[len_out - 1] == '\n') {
294 buf[len_out - 1] = '\0';
297 newbuf = realloc(buf, len_out + NTLM_BUFSIZE);
300 return CURLE_OUT_OF_MEMORY;
305 /* Samba/winbind installed but not configured */
306 if(state == NTLMSTATE_TYPE1 &&
308 buf[0] == 'P' && buf[1] == 'W')
310 /* invalid response */
313 if(state == NTLMSTATE_TYPE1 &&
314 (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
316 if(state == NTLMSTATE_TYPE2 &&
317 (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
318 (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
321 conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
326 return CURLE_REMOTE_ACCESS_DENIED;
330 * This is for creating ntlm header output by delegating challenge/response
331 * to Samba's winbind daemon helper ntlm_auth.
333 CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
336 /* point to the address of the pointer that holds the string to send to the
337 server, which is for a plain host or for a HTTP proxy */
339 /* point to the name and password for this */
341 /* point to the correct struct with this */
342 struct ntlmdata *ntlm;
345 CURLcode res = CURLE_OK;
349 DEBUGASSERT(conn->data);
352 allocuserpwd = &conn->allocptr.proxyuserpwd;
353 userp = conn->proxyuser;
354 ntlm = &conn->proxyntlm;
355 authp = &conn->data->state.authproxy;
358 allocuserpwd = &conn->allocptr.userpwd;
361 authp = &conn->data->state.authhost;
365 /* not set means empty */
369 switch(ntlm->state) {
370 case NTLMSTATE_TYPE1:
372 /* Use Samba's 'winbind' daemon to support NTLM authentication,
373 * by delegating the NTLM challenge/response protocal to a helper
375 * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
376 * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
377 * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
378 * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
379 * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
380 * filename of ntlm_auth helper.
381 * If NTLM authentication using winbind fails, go back to original
382 * request handling process.
384 /* Create communication with ntlm_auth */
385 res = ntlm_wb_init(conn, userp);
388 res = ntlm_wb_response(conn, "YR\n", ntlm->state);
393 *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
394 proxy ? "Proxy-" : "",
395 conn->response_header);
396 DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
397 free(conn->response_header);
398 conn->response_header = NULL;
400 case NTLMSTATE_TYPE2:
401 input = aprintf("TT %s\n", conn->challenge_header);
403 return CURLE_OUT_OF_MEMORY;
404 res = ntlm_wb_response(conn, input, ntlm->state);
411 *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
412 proxy ? "Proxy-" : "",
413 conn->response_header);
414 DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
415 ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
417 Curl_ntlm_wb_cleanup(conn);
419 case NTLMSTATE_TYPE3:
420 /* connection is already authenticated,
421 * don't send a header in future requests */
431 #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */