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