1/* 2 Unix SMB/CIFS implementation. 3 4 winbind client common code 5 6 Copyright (C) Tim Potter 2000 7 Copyright (C) Andrew Tridgell 2000 8 Copyright (C) Andrew Bartlett 2002 9 10 11 This library is free software; you can redistribute it and/or 12 modify it under the terms of the GNU Lesser General Public 13 License as published by the Free Software Foundation; either 14 version 3 of the License, or (at your option) any later version. 15 16 This library is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 Library General Public License for more details. 20 21 You should have received a copy of the GNU Lesser General Public License 22 along with this program. If not, see <http://www.gnu.org/licenses/>. 23*/ 24 25#include "winbind_client.h" 26 27/* Global variables. These are effectively the client state information */ 28 29int winbindd_fd = -1; /* fd for winbindd socket */ 30static int is_privileged = 0; 31 32/* Free a response structure */ 33 34void winbindd_free_response(struct winbindd_response *response) 35{ 36 /* Free any allocated extra_data */ 37 38 if (response) 39 SAFE_FREE(response->extra_data.data); 40} 41 42/* Initialise a request structure */ 43 44void winbindd_init_request(struct winbindd_request *request, int request_type) 45{ 46 request->length = sizeof(struct winbindd_request); 47 48 request->cmd = (enum winbindd_cmd)request_type; 49 request->pid = getpid(); 50 51} 52 53/* Initialise a response structure */ 54 55static void init_response(struct winbindd_response *response) 56{ 57 /* Initialise return value */ 58 59 response->result = WINBINDD_ERROR; 60} 61 62/* Close established socket */ 63 64#if HAVE_FUNCTION_ATTRIBUTE_DESTRUCTOR 65__attribute__((destructor)) 66#endif 67void winbind_close_sock(void) 68{ 69 if (winbindd_fd != -1) { 70 close(winbindd_fd); 71 winbindd_fd = -1; 72 } 73} 74 75#define CONNECT_TIMEOUT 30 76 77/* Make sure socket handle isn't stdin, stdout or stderr */ 78#define RECURSION_LIMIT 3 79 80static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */) 81{ 82 int new_fd; 83 if (fd >= 0 && fd <= 2) { 84#ifdef F_DUPFD 85 if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) { 86 return -1; 87 } 88 /* Paranoia */ 89 if (new_fd < 3) { 90 close(new_fd); 91 return -1; 92 } 93 close(fd); 94 return new_fd; 95#else 96 if (limit <= 0) 97 return -1; 98 99 new_fd = dup(fd); 100 if (new_fd == -1) 101 return -1; 102 103 /* use the program stack to hold our list of FDs to close */ 104 new_fd = make_nonstd_fd_internals(new_fd, limit - 1); 105 close(fd); 106 return new_fd; 107#endif 108 } 109 return fd; 110} 111 112/**************************************************************************** 113 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, 114 else 115 if SYSV use O_NDELAY 116 if BSD use FNDELAY 117 Set close on exec also. 118****************************************************************************/ 119 120static int make_safe_fd(int fd) 121{ 122 int result, flags; 123 int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT); 124 if (new_fd == -1) { 125 close(fd); 126 return -1; 127 } 128 129 /* Socket should be nonblocking. */ 130#ifdef O_NONBLOCK 131#define FLAG_TO_SET O_NONBLOCK 132#else 133#ifdef SYSV 134#define FLAG_TO_SET O_NDELAY 135#else /* BSD */ 136#define FLAG_TO_SET FNDELAY 137#endif 138#endif 139 140 if ((flags = fcntl(new_fd, F_GETFL)) == -1) { 141 close(new_fd); 142 return -1; 143 } 144 145 flags |= FLAG_TO_SET; 146 if (fcntl(new_fd, F_SETFL, flags) == -1) { 147 close(new_fd); 148 return -1; 149 } 150 151#undef FLAG_TO_SET 152 153 /* Socket should be closed on exec() */ 154#ifdef FD_CLOEXEC 155 result = flags = fcntl(new_fd, F_GETFD, 0); 156 if (flags >= 0) { 157 flags |= FD_CLOEXEC; 158 result = fcntl( new_fd, F_SETFD, flags ); 159 } 160 if (result < 0) { 161 close(new_fd); 162 return -1; 163 } 164#endif 165 return new_fd; 166} 167 168/* Connect to winbindd socket */ 169 170static int winbind_named_pipe_sock(const char *dir) 171{ 172 struct sockaddr_un sunaddr; 173 struct stat st; 174 char *path = NULL; 175 int fd; 176 int wait_time; 177 int slept; 178 179 /* Check permissions on unix socket directory */ 180 181 if (lstat(dir, &st) == -1) { 182 errno = ENOENT; 183 return -1; 184 } 185 186 if (!S_ISDIR(st.st_mode) || 187 (st.st_uid != 0 && st.st_uid != geteuid())) { 188 errno = ENOENT; 189 return -1; 190 } 191 192 /* Connect to socket */ 193 194 if (asprintf(&path, "%s/%s", dir, WINBINDD_SOCKET_NAME) < 0) { 195 return -1; 196 } 197 198 ZERO_STRUCT(sunaddr); 199 sunaddr.sun_family = AF_UNIX; 200 strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1); 201 202 /* If socket file doesn't exist, don't bother trying to connect 203 with retry. This is an attempt to make the system usable when 204 the winbindd daemon is not running. */ 205 206 if (lstat(path, &st) == -1) { 207 errno = ENOENT; 208 SAFE_FREE(path); 209 return -1; 210 } 211 212 SAFE_FREE(path); 213 /* Check permissions on unix socket file */ 214 215 if (!S_ISSOCK(st.st_mode) || 216 (st.st_uid != 0 && st.st_uid != geteuid())) { 217 errno = ENOENT; 218 return -1; 219 } 220 221 /* Connect to socket */ 222 223 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 224 return -1; 225 } 226 227 /* Set socket non-blocking and close on exec. */ 228 229 if ((fd = make_safe_fd( fd)) == -1) { 230 return fd; 231 } 232 233 for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1; 234 wait_time += slept) { 235 struct timeval tv; 236 fd_set w_fds; 237 int ret; 238 int connect_errno = 0; 239 socklen_t errnosize; 240 241 if (wait_time >= CONNECT_TIMEOUT) 242 goto error_out; 243 244 switch (errno) { 245 case EINPROGRESS: 246 FD_ZERO(&w_fds); 247 if (fd < 0 || fd >= FD_SETSIZE) { 248 errno = EBADF; 249 goto error_out; 250 } 251 FD_SET(fd, &w_fds); 252 tv.tv_sec = CONNECT_TIMEOUT - wait_time; 253 tv.tv_usec = 0; 254 255 ret = select(fd + 1, NULL, &w_fds, NULL, &tv); 256 257 if (ret > 0) { 258 errnosize = sizeof(connect_errno); 259 260 ret = getsockopt(fd, SOL_SOCKET, 261 SO_ERROR, &connect_errno, &errnosize); 262 263 if (ret >= 0 && connect_errno == 0) { 264 /* Connect succeed */ 265 goto out; 266 } 267 } 268 269 slept = CONNECT_TIMEOUT; 270 break; 271 case EAGAIN: 272 slept = rand() % 3 + 1; 273 sleep(slept); 274 break; 275 default: 276 goto error_out; 277 } 278 279 } 280 281 out: 282 283 return fd; 284 285 error_out: 286 287 close(fd); 288 return -1; 289} 290 291static const char *winbindd_socket_dir(void) 292{ 293#ifdef SOCKET_WRAPPER 294 const char *env_dir; 295 296 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR); 297 if (env_dir) { 298 return env_dir; 299 } 300#endif 301 302 return WINBINDD_SOCKET_DIR; 303} 304 305/* Connect to winbindd socket */ 306 307static int winbind_open_pipe_sock(int recursing, int need_priv) 308{ 309#ifdef HAVE_UNIXSOCKET 310 static pid_t our_pid; 311 struct winbindd_request request; 312 struct winbindd_response response; 313 ZERO_STRUCT(request); 314 ZERO_STRUCT(response); 315 316 if (our_pid != getpid()) { 317 winbind_close_sock(); 318 our_pid = getpid(); 319 } 320 321 if ((need_priv != 0) && (is_privileged == 0)) { 322 winbind_close_sock(); 323 } 324 325 if (winbindd_fd != -1) { 326 return winbindd_fd; 327 } 328 329 if (recursing) { 330 return -1; 331 } 332 333 if ((winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir())) == -1) { 334 return -1; 335 } 336 337 is_privileged = 0; 338 339 /* version-check the socket */ 340 341 request.wb_flags = WBFLAG_RECURSE; 342 if ((winbindd_request_response(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) { 343 winbind_close_sock(); 344 return -1; 345 } 346 347 /* try and get priv pipe */ 348 349 request.wb_flags = WBFLAG_RECURSE; 350 if (winbindd_request_response(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) { 351 int fd; 352 if ((fd = winbind_named_pipe_sock((char *)response.extra_data.data)) != -1) { 353 close(winbindd_fd); 354 winbindd_fd = fd; 355 is_privileged = 1; 356 } 357 } 358 359 if ((need_priv != 0) && (is_privileged == 0)) { 360 return -1; 361 } 362 363 SAFE_FREE(response.extra_data.data); 364 365 return winbindd_fd; 366#else 367 return -1; 368#endif /* HAVE_UNIXSOCKET */ 369} 370 371/* Write data to winbindd socket */ 372 373int winbind_write_sock(void *buffer, int count, int recursing, int need_priv) 374{ 375 int result, nwritten; 376 377 /* Open connection to winbind daemon */ 378 379 restart: 380 381 if (winbind_open_pipe_sock(recursing, need_priv) == -1) { 382 errno = ENOENT; 383 return -1; 384 } 385 386 /* Write data to socket */ 387 388 nwritten = 0; 389 390 while(nwritten < count) { 391 struct timeval tv; 392 fd_set r_fds; 393 394 /* Catch pipe close on other end by checking if a read() 395 call would not block by calling select(). */ 396 397 FD_ZERO(&r_fds); 398 if (winbindd_fd < 0 || winbindd_fd >= FD_SETSIZE) { 399 errno = EBADF; 400 winbind_close_sock(); 401 return -1; 402 } 403 FD_SET(winbindd_fd, &r_fds); 404 ZERO_STRUCT(tv); 405 406 if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) { 407 winbind_close_sock(); 408 return -1; /* Select error */ 409 } 410 411 /* Write should be OK if fd not available for reading */ 412 413 if (!FD_ISSET(winbindd_fd, &r_fds)) { 414 415 /* Do the write */ 416 417 result = write(winbindd_fd, 418 (char *)buffer + nwritten, 419 count - nwritten); 420 421 if ((result == -1) || (result == 0)) { 422 423 /* Write failed */ 424 425 winbind_close_sock(); 426 return -1; 427 } 428 429 nwritten += result; 430 431 } else { 432 433 /* Pipe has closed on remote end */ 434 435 winbind_close_sock(); 436 goto restart; 437 } 438 } 439 440 return nwritten; 441} 442 443/* Read data from winbindd socket */ 444 445int winbind_read_sock(void *buffer, int count) 446{ 447 int nread = 0; 448 int total_time = 0, selret; 449 450 if (winbindd_fd == -1) { 451 return -1; 452 } 453 454 /* Read data from socket */ 455 while(nread < count) { 456 struct timeval tv; 457 fd_set r_fds; 458 459 /* Catch pipe close on other end by checking if a read() 460 call would not block by calling select(). */ 461 462 FD_ZERO(&r_fds); 463 if (winbindd_fd < 0 || winbindd_fd >= FD_SETSIZE) { 464 errno = EBADF; 465 winbind_close_sock(); 466 return -1; 467 } 468 FD_SET(winbindd_fd, &r_fds); 469 ZERO_STRUCT(tv); 470 /* Wait for 5 seconds for a reply. May need to parameterise this... */ 471 tv.tv_sec = 5; 472 473 if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) { 474 winbind_close_sock(); 475 return -1; /* Select error */ 476 } 477 478 if (selret == 0) { 479 /* Not ready for read yet... */ 480 if (total_time >= 30) { 481 /* Timeout */ 482 winbind_close_sock(); 483 return -1; 484 } 485 total_time += 5; 486 continue; 487 } 488 489 if (FD_ISSET(winbindd_fd, &r_fds)) { 490 491 /* Do the Read */ 492 493 int result = read(winbindd_fd, (char *)buffer + nread, 494 count - nread); 495 496 if ((result == -1) || (result == 0)) { 497 498 /* Read failed. I think the only useful thing we 499 can do here is just return -1 and fail since the 500 transaction has failed half way through. */ 501 502 winbind_close_sock(); 503 return -1; 504 } 505 506 nread += result; 507 508 } 509 } 510 511 return nread; 512} 513 514/* Read reply */ 515 516int winbindd_read_reply(struct winbindd_response *response) 517{ 518 int result1, result2 = 0; 519 520 if (!response) { 521 return -1; 522 } 523 524 /* Read fixed length response */ 525 526 result1 = winbind_read_sock(response, 527 sizeof(struct winbindd_response)); 528 if (result1 == -1) { 529 return -1; 530 } 531 532 /* We actually send the pointer value of the extra_data field from 533 the server. This has no meaning in the client's address space 534 so we clear it out. */ 535 536 response->extra_data.data = NULL; 537 538 /* Read variable length response */ 539 540 if (response->length > sizeof(struct winbindd_response)) { 541 int extra_data_len = response->length - 542 sizeof(struct winbindd_response); 543 544 /* Mallocate memory for extra data */ 545 546 if (!(response->extra_data.data = malloc(extra_data_len))) { 547 return -1; 548 } 549 550 result2 = winbind_read_sock(response->extra_data.data, 551 extra_data_len); 552 if (result2 == -1) { 553 winbindd_free_response(response); 554 return -1; 555 } 556 } 557 558 /* Return total amount of data read */ 559 560 return result1 + result2; 561} 562 563/* 564 * send simple types of requests 565 */ 566 567NSS_STATUS winbindd_send_request(int req_type, int need_priv, 568 struct winbindd_request *request) 569{ 570 struct winbindd_request lrequest; 571 572 /* Check for our tricky environment variable */ 573 574 if (winbind_env_set()) { 575 return NSS_STATUS_NOTFOUND; 576 } 577 578 if (!request) { 579 ZERO_STRUCT(lrequest); 580 request = &lrequest; 581 } 582 583 /* Fill in request and send down pipe */ 584 585 winbindd_init_request(request, req_type); 586 587 if (winbind_write_sock(request, sizeof(*request), 588 request->wb_flags & WBFLAG_RECURSE, 589 need_priv) == -1) 590 { 591 /* Set ENOENT for consistency. Required by some apps */ 592 errno = ENOENT; 593 594 return NSS_STATUS_UNAVAIL; 595 } 596 597 if ((request->extra_len != 0) && 598 (winbind_write_sock(request->extra_data.data, 599 request->extra_len, 600 request->wb_flags & WBFLAG_RECURSE, 601 need_priv) == -1)) 602 { 603 /* Set ENOENT for consistency. Required by some apps */ 604 errno = ENOENT; 605 606 return NSS_STATUS_UNAVAIL; 607 } 608 609 return NSS_STATUS_SUCCESS; 610} 611 612/* 613 * Get results from winbindd request 614 */ 615 616NSS_STATUS winbindd_get_response(struct winbindd_response *response) 617{ 618 struct winbindd_response lresponse; 619 620 if (!response) { 621 ZERO_STRUCT(lresponse); 622 response = &lresponse; 623 } 624 625 init_response(response); 626 627 /* Wait for reply */ 628 if (winbindd_read_reply(response) == -1) { 629 /* Set ENOENT for consistency. Required by some apps */ 630 errno = ENOENT; 631 632 return NSS_STATUS_UNAVAIL; 633 } 634 635 /* Throw away extra data if client didn't request it */ 636 if (response == &lresponse) { 637 winbindd_free_response(response); 638 } 639 640 /* Copy reply data from socket */ 641 if (response->result != WINBINDD_OK) { 642 return NSS_STATUS_NOTFOUND; 643 } 644 645 return NSS_STATUS_SUCCESS; 646} 647 648/* Handle simple types of requests */ 649 650NSS_STATUS winbindd_request_response(int req_type, 651 struct winbindd_request *request, 652 struct winbindd_response *response) 653{ 654 NSS_STATUS status = NSS_STATUS_UNAVAIL; 655 int count = 0; 656 657 while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) { 658 status = winbindd_send_request(req_type, 0, request); 659 if (status != NSS_STATUS_SUCCESS) 660 return(status); 661 status = winbindd_get_response(response); 662 count += 1; 663 } 664 665 return status; 666} 667 668NSS_STATUS winbindd_priv_request_response(int req_type, 669 struct winbindd_request *request, 670 struct winbindd_response *response) 671{ 672 NSS_STATUS status = NSS_STATUS_UNAVAIL; 673 int count = 0; 674 675 while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) { 676 status = winbindd_send_request(req_type, 1, request); 677 if (status != NSS_STATUS_SUCCESS) 678 return(status); 679 status = winbindd_get_response(response); 680 count += 1; 681 } 682 683 return status; 684} 685 686/************************************************************************* 687 ************************************************************************/ 688 689const char *nss_err_str(NSS_STATUS ret) 690{ 691 switch (ret) { 692 case NSS_STATUS_TRYAGAIN: 693 return "NSS_STATUS_TRYAGAIN"; 694 case NSS_STATUS_SUCCESS: 695 return "NSS_STATUS_SUCCESS"; 696 case NSS_STATUS_NOTFOUND: 697 return "NSS_STATUS_NOTFOUND"; 698 case NSS_STATUS_UNAVAIL: 699 return "NSS_STATUS_UNAVAIL"; 700#ifdef NSS_STATUS_RETURN 701 case NSS_STATUS_RETURN: 702 return "NSS_STATUS_RETURN"; 703#endif 704 default: 705 return "UNKNOWN RETURN CODE!!!!!!!"; 706 } 707} 708