1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel (at) 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_printf.h" 55 56 /* The last #include files should be: */ 57 #include "curl_memory.h" 58 #include "memdebug.h" 59 60 #if DEBUG_ME 61 # define DEBUG_OUT(x) x 62 #else 63 # define DEBUG_OUT(x) Curl_nop_stmt 64 #endif 65 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)) 72 #else 73 # define sclose_nolog(x) close((x)) 74 #endif 75 76 void Curl_ntlm_wb_cleanup(struct connectdata *conn) 77 { 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; 81 } 82 83 if(conn->ntlm_auth_hlpr_pid) { 84 int i; 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) 88 break; 89 switch(i) { 90 case 0: 91 kill(conn->ntlm_auth_hlpr_pid, SIGTERM); 92 break; 93 case 1: 94 /* Give the process another moment to shut down cleanly before 95 bringing down the axe */ 96 Curl_wait_ms(1); 97 break; 98 case 2: 99 kill(conn->ntlm_auth_hlpr_pid, SIGKILL); 100 break; 101 case 3: 102 break; 103 } 104 } 105 conn->ntlm_auth_hlpr_pid = 0; 106 } 107 108 free(conn->challenge_header); 109 conn->challenge_header = NULL; 110 free(conn->response_header); 111 conn->response_header = NULL; 112 } 113 114 static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) 115 { 116 curl_socket_t sockfds[2]; 117 pid_t child_pid; 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; 124 char pwbuf[1024]; 125 #endif 126 int error; 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 if((domain = strdup(username)) == NULL) 161 return CURLE_OUT_OF_MEMORY; 162 slash = domain + (slash - username); 163 *slash = '\0'; 164 username = username + (slash - domain) + 1; 165 } 166 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 */ 171 #ifdef DEBUGBUILD 172 ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE"); 173 if(ntlm_auth_alloc) 174 ntlm_auth = ntlm_auth_alloc; 175 else 176 #endif 177 ntlm_auth = NTLM_WB_FILE; 178 179 if(access(ntlm_auth, X_OK) != 0) { 180 error = ERRNO; 181 failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s", 182 ntlm_auth, error, Curl_strerror(conn, error)); 183 goto done; 184 } 185 186 if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { 187 error = ERRNO; 188 failf(conn->data, "Could not open socket pair. errno %d: %s", 189 error, Curl_strerror(conn, error)); 190 goto done; 191 } 192 193 child_pid = fork(); 194 if(child_pid == -1) { 195 error = ERRNO; 196 sclose(sockfds[0]); 197 sclose(sockfds[1]); 198 failf(conn->data, "Could not fork. errno %d: %s", 199 error, Curl_strerror(conn, error)); 200 goto done; 201 } 202 else if(!child_pid) { 203 /* 204 * child process 205 */ 206 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) { 210 error = ERRNO; 211 failf(conn->data, "Could not redirect child stdin. errno %d: %s", 212 error, Curl_strerror(conn, error)); 213 exit(1); 214 } 215 216 if(dup2(sockfds[1], STDOUT_FILENO) == -1) { 217 error = ERRNO; 218 failf(conn->data, "Could not redirect child stdout. errno %d: %s", 219 error, Curl_strerror(conn, error)); 220 exit(1); 221 } 222 223 if(domain) 224 execl(ntlm_auth, ntlm_auth, 225 "--helper-protocol", "ntlmssp-client-1", 226 "--use-cached-creds", 227 "--username", username, 228 "--domain", domain, 229 NULL); 230 else 231 execl(ntlm_auth, ntlm_auth, 232 "--helper-protocol", "ntlmssp-client-1", 233 "--use-cached-creds", 234 "--username", username, 235 NULL); 236 237 error = ERRNO; 238 sclose_nolog(sockfds[1]); 239 failf(conn->data, "Could not execl(). errno %d: %s", 240 error, Curl_strerror(conn, error)); 241 exit(1); 242 } 243 244 sclose(sockfds[1]); 245 conn->ntlm_auth_hlpr_socket = sockfds[0]; 246 conn->ntlm_auth_hlpr_pid = child_pid; 247 free(domain); 248 free(ntlm_auth_alloc); 249 return CURLE_OK; 250 251 done: 252 free(domain); 253 free(ntlm_auth_alloc); 254 return CURLE_REMOTE_ACCESS_DENIED; 255 } 256 257 static CURLcode ntlm_wb_response(struct connectdata *conn, 258 const char *input, curlntlm state) 259 { 260 char *buf = malloc(NTLM_BUFSIZE); 261 size_t len_in = strlen(input), len_out = 0; 262 263 if(!buf) 264 return CURLE_OUT_OF_MEMORY; 265 266 while(len_in > 0) { 267 ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in); 268 if(written == -1) { 269 /* Interrupted by a signal, retry it */ 270 if(errno == EINTR) 271 continue; 272 /* write failed if other errors happen */ 273 goto done; 274 } 275 input += written; 276 len_in -= written; 277 } 278 /* Read one line */ 279 while(1) { 280 ssize_t size; 281 char *newbuf; 282 283 size = sread(conn->ntlm_auth_hlpr_socket, buf + len_out, NTLM_BUFSIZE); 284 if(size == -1) { 285 if(errno == EINTR) 286 continue; 287 goto done; 288 } 289 else if(size == 0) 290 goto done; 291 292 len_out += size; 293 if(buf[len_out - 1] == '\n') { 294 buf[len_out - 1] = '\0'; 295 break; 296 } 297 newbuf = realloc(buf, len_out + NTLM_BUFSIZE); 298 if(!newbuf) { 299 free(buf); 300 return CURLE_OUT_OF_MEMORY; 301 } 302 buf = newbuf; 303 } 304 305 /* Samba/winbind installed but not configured */ 306 if(state == NTLMSTATE_TYPE1 && 307 len_out == 3 && 308 buf[0] == 'P' && buf[1] == 'W') 309 return CURLE_REMOTE_ACCESS_DENIED; 310 /* invalid response */ 311 if(len_out < 4) 312 goto done; 313 if(state == NTLMSTATE_TYPE1 && 314 (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' ')) 315 goto done; 316 if(state == NTLMSTATE_TYPE2 && 317 (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') && 318 (buf[0]!='A' || buf[1]!='F' || buf[2]!=' ')) 319 goto done; 320 321 conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3); 322 free(buf); 323 return CURLE_OK; 324 done: 325 free(buf); 326 return CURLE_REMOTE_ACCESS_DENIED; 327 } 328 329 /* 330 * This is for creating ntlm header output by delegating challenge/response 331 * to Samba's winbind daemon helper ntlm_auth. 332 */ 333 CURLcode Curl_output_ntlm_wb(struct connectdata *conn, 334 bool proxy) 335 { 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 */ 338 char **allocuserpwd; 339 /* point to the name and password for this */ 340 const char *userp; 341 /* point to the correct struct with this */ 342 struct ntlmdata *ntlm; 343 struct auth *authp; 344 345 CURLcode res = CURLE_OK; 346 char *input; 347 348 DEBUGASSERT(conn); 349 DEBUGASSERT(conn->data); 350 351 if(proxy) { 352 allocuserpwd = &conn->allocptr.proxyuserpwd; 353 userp = conn->proxyuser; 354 ntlm = &conn->proxyntlm; 355 authp = &conn->data->state.authproxy; 356 } 357 else { 358 allocuserpwd = &conn->allocptr.userpwd; 359 userp = conn->user; 360 ntlm = &conn->ntlm; 361 authp = &conn->data->state.authhost; 362 } 363 authp->done = FALSE; 364 365 /* not set means empty */ 366 if(!userp) 367 userp=""; 368 369 switch(ntlm->state) { 370 case NTLMSTATE_TYPE1: 371 default: 372 /* Use Samba's 'winbind' daemon to support NTLM authentication, 373 * by delegating the NTLM challenge/response protocal to a helper 374 * in ntlm_auth. 375 * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html 376 * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html 377 * http://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. 383 */ 384 /* Create communication with ntlm_auth */ 385 res = ntlm_wb_init(conn, userp); 386 if(res) 387 return res; 388 res = ntlm_wb_response(conn, "YR\n", ntlm->state); 389 if(res) 390 return res; 391 392 free(*allocuserpwd); 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; 399 break; 400 case NTLMSTATE_TYPE2: 401 input = aprintf("TT %s\n", conn->challenge_header); 402 if(!input) 403 return CURLE_OUT_OF_MEMORY; 404 res = ntlm_wb_response(conn, input, ntlm->state); 405 free(input); 406 input = NULL; 407 if(res) 408 return res; 409 410 free(*allocuserpwd); 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 */ 416 authp->done = TRUE; 417 Curl_ntlm_wb_cleanup(conn); 418 break; 419 case NTLMSTATE_TYPE3: 420 /* connection is already authenticated, 421 * don't send a header in future requests */ 422 free(*allocuserpwd); 423 *allocuserpwd=NULL; 424 authp->done = TRUE; 425 break; 426 } 427 428 return CURLE_OK; 429 } 430 431 #endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ 432