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 29/* Global variables. These are effectively the client state information */ 30 31int winbindd_fd = -1; /* fd for winbindd socket */ 32 33/* Free a response structure */ 34 35void free_response(struct winbindd_response *response) 36{ 37 /* Free any allocated extra_data */ 38 39 if (response) 40 SAFE_FREE(response->extra_data); 41} 42 43/* Initialise a request structure */ 44 45void init_request(struct winbindd_request *request, int request_type) 46{ 47 request->length = sizeof(struct winbindd_request); 48 49 request->cmd = (enum winbindd_cmd)request_type; 50 request->pid = getpid(); 51 52} 53 54/* Initialise a response structure */ 55 56void init_response(struct winbindd_response *response) 57{ 58 /* Initialise return value */ 59 60 response->result = WINBINDD_ERROR; 61} 62 63/* Close established socket */ 64 65void close_sock(void) 66{ 67 if (winbindd_fd != -1) { 68 close(winbindd_fd); 69 winbindd_fd = -1; 70 } 71} 72 73#define CONNECT_TIMEOUT 30 74#define WRITE_TIMEOUT CONNECT_TIMEOUT 75#define READ_TIMEOUT CONNECT_TIMEOUT 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 pstring path; 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 return -1; 183 } 184 185 if (!S_ISDIR(st.st_mode) || 186 (st.st_uid != 0 && st.st_uid != geteuid())) { 187 return -1; 188 } 189 190 /* Connect to socket */ 191 192 strncpy(path, dir, sizeof(path) - 1); 193 path[sizeof(path) - 1] = '\0'; 194 195 strncat(path, "/", sizeof(path) - 1 - strlen(path)); 196 path[sizeof(path) - 1] = '\0'; 197 198 strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1 - strlen(path)); 199 path[sizeof(path) - 1] = '\0'; 200 201 ZERO_STRUCT(sunaddr); 202 sunaddr.sun_family = AF_UNIX; 203 strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1); 204 205 /* If socket file doesn't exist, don't bother trying to connect 206 with retry. This is an attempt to make the system usable when 207 the winbindd daemon is not running. */ 208 209 if (lstat(path, &st) == -1) { 210 return -1; 211 } 212 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 return -1; 218 } 219 220 /* Connect to socket */ 221 222 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 223 return -1; 224 } 225 226 /* Set socket non-blocking and close on exec. */ 227 228 if ((fd = make_safe_fd( fd)) == -1) { 229 return fd; 230 } 231 232 for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1; 233 wait_time += slept) { 234 struct timeval tv; 235 fd_set w_fds; 236 int ret; 237 int connect_errno = 0, errnosize; 238 239 if (wait_time >= CONNECT_TIMEOUT) 240 goto error_out; 241 242 switch (errno) { 243 case EINPROGRESS: 244 FD_ZERO(&w_fds); 245 FD_SET(fd, &w_fds); 246 tv.tv_sec = CONNECT_TIMEOUT - wait_time; 247 tv.tv_usec = 0; 248 249 ret = select(fd + 1, NULL, &w_fds, NULL, &tv); 250 251 if (ret > 0) { 252 errnosize = sizeof(connect_errno); 253 254 ret = getsockopt(fd, SOL_SOCKET, 255 SO_ERROR, &connect_errno, &errnosize); 256 257 if (ret >= 0 && connect_errno == 0) { 258 /* Connect succeed */ 259 goto out; 260 } 261 } 262 263 slept = CONNECT_TIMEOUT; 264 break; 265 case EAGAIN: 266 slept = rand() % 3 + 1; 267 sleep(slept); 268 break; 269 default: 270 goto error_out; 271 } 272 273 } 274 275 out: 276 277 return fd; 278 279 error_out: 280 281 close(fd); 282 return -1; 283 284 if (connect(fd, (struct sockaddr *)&sunaddr, 285 sizeof(sunaddr)) == -1) { 286 close(fd); 287 return -1; 288 } 289 290 return fd; 291} 292 293/* Connect to winbindd socket */ 294 295int winbind_open_pipe_sock(void) 296{ 297#ifdef HAVE_UNIXSOCKET 298 static pid_t our_pid; 299 struct winbindd_request request; 300 struct winbindd_response response; 301 ZERO_STRUCT(request); 302 ZERO_STRUCT(response); 303 304 if (our_pid != getpid()) { 305 close_sock(); 306 our_pid = getpid(); 307 } 308 309 if (winbindd_fd != -1) { 310 return winbindd_fd; 311 } 312 313 if ((winbindd_fd = winbind_named_pipe_sock(WINBINDD_SOCKET_DIR)) == -1) { 314 return -1; 315 } 316 317 /* version-check the socket */ 318 319 if ((winbindd_request(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) { 320 close_sock(); 321 return -1; 322 } 323 324 /* try and get priv pipe */ 325 326 if (winbindd_request(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) { 327 int fd; 328 if ((fd = winbind_named_pipe_sock(response.extra_data)) != -1) { 329 close(winbindd_fd); 330 winbindd_fd = fd; 331 } 332 } 333 334 SAFE_FREE(response.extra_data); 335 336 return winbindd_fd; 337#else 338 return -1; 339#endif /* HAVE_UNIXSOCKET */ 340} 341 342/* Write data to winbindd socket */ 343 344int write_sock(void *buffer, int count) 345{ 346 int result, nwritten; 347 348 /* Open connection to winbind daemon */ 349 350 restart: 351 352 if (winbind_open_pipe_sock() == -1) { 353 return -1; 354 } 355 356 /* Write data to socket */ 357 358 nwritten = 0; 359 360 while(nwritten < count) { 361 struct timeval tv; 362 fd_set r_fds; 363 364 /* Catch pipe close on other end by checking if a read() 365 call would not block by calling select(). */ 366 367 FD_ZERO(&r_fds); 368 FD_SET(winbindd_fd, &r_fds); 369 ZERO_STRUCT(tv); 370 371 if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) { 372 close_sock(); 373 return -1; /* Select error */ 374 } 375 376 /* Write should be OK if fd not available for reading */ 377 378 if (!FD_ISSET(winbindd_fd, &r_fds)) { 379 380 /* Do the write */ 381 382 result = write(winbindd_fd, 383 (char *)buffer + nwritten, 384 count - nwritten); 385 386 if ((result == -1) || (result == 0)) { 387 388 /* Write failed */ 389 390 close_sock(); 391 return -1; 392 } 393 394 nwritten += result; 395 396 } else { 397 398 /* Pipe has closed on remote end */ 399 400 close_sock(); 401 goto restart; 402 } 403 } 404 405 return nwritten; 406} 407 408/* Read data from winbindd socket */ 409 410static int read_sock(void *buffer, int count) 411{ 412 int result = 0, nread = 0; 413 int total_time = 0, selret; 414 415 /* Read data from socket */ 416 while(nread < count) { 417 struct timeval tv; 418 fd_set r_fds; 419 420 /* Catch pipe close on other end by checking if a read() 421 call would not block by calling select(). */ 422 423 FD_ZERO(&r_fds); 424 FD_SET(winbindd_fd, &r_fds); 425 ZERO_STRUCT(tv); 426 /* Wait for 5 seconds for a reply. May need to parameterise this... */ 427 tv.tv_sec = 5; 428 429 if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) { 430 close_sock(); 431 return -1; /* Select error */ 432 } 433 434 if (selret == 0) { 435 /* Not ready for read yet... */ 436 if (total_time >= 30) { 437 /* Timeout */ 438 close_sock(); 439 return -1; 440 } 441 total_time += 5; 442 continue; 443 } 444 445 if (FD_ISSET(winbindd_fd, &r_fds)) { 446 447 /* Do the Read */ 448 449 result = read(winbindd_fd, (char *)buffer + nread, 450 count - nread); 451 452 if ((result == -1) || (result == 0)) { 453 454 /* Read failed. I think the only useful thing we 455 can do here is just return -1 and fail since the 456 transaction has failed half way through. */ 457 458 close_sock(); 459 return -1; 460 } 461 462 nread += result; 463 464 } 465 } 466 467 return result; 468} 469 470/* Read reply */ 471 472int read_reply(struct winbindd_response *response) 473{ 474 int result1, result2 = 0; 475 476 if (!response) { 477 return -1; 478 } 479 480 /* Read fixed length response */ 481 482 if ((result1 = read_sock(response, sizeof(struct winbindd_response))) 483 == -1) { 484 485 return -1; 486 } 487 488 /* We actually send the pointer value of the extra_data field from 489 the server. This has no meaning in the client's address space 490 so we clear it out. */ 491 492 response->extra_data = NULL; 493 494 /* Read variable length response */ 495 496 if (response->length > sizeof(struct winbindd_response)) { 497 int extra_data_len = response->length - 498 sizeof(struct winbindd_response); 499 500 /* Mallocate memory for extra data */ 501 502 if (!(response->extra_data = malloc(extra_data_len))) { 503 return -1; 504 } 505 506 if ((result2 = read_sock(response->extra_data, extra_data_len)) 507 == -1) { 508 free_response(response); 509 return -1; 510 } 511 } 512 513 /* Return total amount of data read */ 514 515 return result1 + result2; 516} 517 518/* 519 * send simple types of requests 520 */ 521 522NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request) 523{ 524 struct winbindd_request lrequest; 525 char *env; 526 int value; 527 528 /* Check for our tricky environment variable */ 529 530 if ( (env = getenv(WINBINDD_DONT_ENV)) != NULL ) { 531 value = atoi(env); 532 if ( value == 1 ) 533 return NSS_STATUS_NOTFOUND; 534 } 535 536 if (!request) { 537 ZERO_STRUCT(lrequest); 538 request = &lrequest; 539 } 540 541 /* Fill in request and send down pipe */ 542 543 init_request(request, req_type); 544 545 if (write_sock(request, sizeof(*request)) == -1) { 546 return NSS_STATUS_UNAVAIL; 547 } 548 549 return NSS_STATUS_SUCCESS; 550} 551 552/* 553 * Get results from winbindd request 554 */ 555 556NSS_STATUS winbindd_get_response(struct winbindd_response *response) 557{ 558 struct winbindd_response lresponse; 559 560 if (!response) { 561 ZERO_STRUCT(lresponse); 562 response = &lresponse; 563 } 564 565 init_response(response); 566 567 /* Wait for reply */ 568 if (read_reply(response) == -1) { 569 return NSS_STATUS_UNAVAIL; 570 } 571 572 /* Throw away extra data if client didn't request it */ 573 if (response == &lresponse) { 574 free_response(response); 575 } 576 577 /* Copy reply data from socket */ 578 if (response->result != WINBINDD_OK) { 579 return NSS_STATUS_NOTFOUND; 580 } 581 582 return NSS_STATUS_SUCCESS; 583} 584 585/* Handle simple types of requests */ 586 587NSS_STATUS winbindd_request(int req_type, 588 struct winbindd_request *request, 589 struct winbindd_response *response) 590{ 591 NSS_STATUS status; 592 593 status = winbindd_send_request(req_type, request); 594 if (status != NSS_STATUS_SUCCESS) 595 return(status); 596 return winbindd_get_response(response); 597} 598 599/************************************************************************* 600 A couple of simple functions to disable winbindd lookups and re- 601 enable them 602 ************************************************************************/ 603 604/* Use putenv() instead of setenv() in these functions as not all 605 environments have the latter. */ 606 607BOOL winbind_off( void ) 608{ 609 static char *s = WINBINDD_DONT_ENV "=1"; 610 611 return putenv(s) != -1; 612} 613 614BOOL winbind_on( void ) 615{ 616 static char *s = WINBINDD_DONT_ENV "=0"; 617 618 return putenv(s) != -1; 619} 620