1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 http://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_memory.h"
56 #define _MPRINTF_REPLACE /* use our functions only */
57 #include <curl/mprintf.h>
59 /* The last #include file should be: */
63 # define DEBUG_OUT(x) x
65 # define DEBUG_OUT(x) Curl_nop_stmt
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))
75 # define sclose_nolog(x) close((x))
78 void Curl_ntlm_wb_cleanup(struct connectdata *conn)
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;
85 if(conn->ntlm_auth_hlpr_pid) {
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)
93 kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
96 /* Give the process another moment to shut down cleanly before
97 bringing down the axe */
101 kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
107 conn->ntlm_auth_hlpr_pid = 0;
110 Curl_safefree(conn->challenge_header);
111 conn->challenge_header = NULL;
112 Curl_safefree(conn->response_header);
113 conn->response_header = NULL;
116 static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
118 curl_socket_t sockfds[2];
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;
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)
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) &&
154 username = pw.pw_name;
157 if(!username || !username[0])
160 slash = strpbrk(username, "\\/");
162 if((domain = strdup(username)) == NULL)
163 return CURLE_OUT_OF_MEMORY;
164 slash = domain + (slash - username);
166 username = username + (slash - domain) + 1;
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 */
174 ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
176 ntlm_auth = ntlm_auth_alloc;
179 ntlm_auth = NTLM_WB_FILE;
181 if(access(ntlm_auth, X_OK) != 0) {
183 failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
184 ntlm_auth, error, Curl_strerror(conn, error));
188 if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
190 failf(conn->data, "Could not open socket pair. errno %d: %s",
191 error, Curl_strerror(conn, error));
196 if(child_pid == -1) {
200 failf(conn->data, "Could not fork. errno %d: %s",
201 error, Curl_strerror(conn, error));
204 else if(!child_pid) {
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) {
213 failf(conn->data, "Could not redirect child stdin. errno %d: %s",
214 error, Curl_strerror(conn, error));
218 if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
220 failf(conn->data, "Could not redirect child stdout. errno %d: %s",
221 error, Curl_strerror(conn, error));
226 execl(ntlm_auth, ntlm_auth,
227 "--helper-protocol", "ntlmssp-client-1",
228 "--use-cached-creds",
229 "--username", username,
233 execl(ntlm_auth, ntlm_auth,
234 "--helper-protocol", "ntlmssp-client-1",
235 "--use-cached-creds",
236 "--username", username,
240 sclose_nolog(sockfds[1]);
241 failf(conn->data, "Could not execl(). errno %d: %s",
242 error, Curl_strerror(conn, error));
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);
254 Curl_safefree(domain);
255 Curl_safefree(ntlm_auth_alloc);
256 return CURLE_REMOTE_ACCESS_DENIED;
259 static CURLcode ntlm_wb_response(struct connectdata *conn,
260 const char *input, curlntlm state)
262 char *buf = malloc(NTLM_BUFSIZE);
263 size_t len_in = strlen(input), len_out = 0;
266 return CURLE_OUT_OF_MEMORY;
269 ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
271 /* Interrupted by a signal, retry it */
274 /* write failed if other errors happen */
285 size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE);
295 if(buf[len_out - 1] == '\n') {
296 buf[len_out - 1] = '\0';
299 newbuf = realloc(buf, len_out + NTLM_BUFSIZE);
302 return CURLE_OUT_OF_MEMORY;
307 /* Samba/winbind installed but not configured */
308 if(state == NTLMSTATE_TYPE1 &&
310 buf[0] == 'P' && buf[1] == 'W')
311 return CURLE_REMOTE_ACCESS_DENIED;
312 /* invalid response */
315 if(state == NTLMSTATE_TYPE1 &&
316 (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
318 if(state == NTLMSTATE_TYPE2 &&
319 (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
320 (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
323 conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
328 return CURLE_REMOTE_ACCESS_DENIED;
332 * This is for creating ntlm header output by delegating challenge/response
333 * to Samba's winbind daemon helper ntlm_auth.
335 CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
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 */
341 /* point to the name and password for this */
343 /* point to the correct struct with this */
344 struct ntlmdata *ntlm;
347 CURLcode res = CURLE_OK;
351 DEBUGASSERT(conn->data);
354 allocuserpwd = &conn->allocptr.proxyuserpwd;
355 userp = conn->proxyuser;
356 ntlm = &conn->proxyntlm;
357 authp = &conn->data->state.authproxy;
360 allocuserpwd = &conn->allocptr.userpwd;
363 authp = &conn->data->state.authhost;
367 /* not set means empty */
371 switch(ntlm->state) {
372 case NTLMSTATE_TYPE1:
374 /* Use Samba's 'winbind' daemon to support NTLM authentication,
375 * by delegating the NTLM challenge/response protocal to a helper
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.
386 /* Create communication with ntlm_auth */
387 res = ntlm_wb_init(conn, userp);
390 res = ntlm_wb_response(conn, "YR\n", ntlm->state);
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;
402 case NTLMSTATE_TYPE2:
403 input = aprintf("TT %s\n", conn->challenge_header);
405 return CURLE_OUT_OF_MEMORY;
406 res = ntlm_wb_response(conn, input, ntlm->state);
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 */
419 Curl_ntlm_wb_cleanup(conn);
421 case NTLMSTATE_TYPE3:
422 /* connection is already authenticated,
423 * don't send a header in future requests */
435 #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */