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