1/* 2 Unix SMB/CIFS implementation. 3 4 Windows NT Domain nsswitch module 5 6 Copyright (C) Tim Potter 2000 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 3 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Library General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#include "winbind_client.h" 23 24#if HAVE_PTHREAD_H 25#include <pthread.h> 26#endif 27 28#if HAVE_PTHREAD 29static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER; 30#endif 31 32/* Maximum number of users to pass back over the unix domain socket 33 per call. This is not a static limit on the total number of users 34 or groups returned in total. */ 35 36#define MAX_GETPWENT_USERS 250 37#define MAX_GETGRENT_USERS 250 38 39NSS_STATUS _nss_winbind_setpwent(void); 40NSS_STATUS _nss_winbind_endpwent(void); 41NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer, 42 size_t buflen, int *errnop); 43NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, 44 char *buffer, size_t buflen, int *errnop); 45NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result, 46 char *buffer, size_t buflen, int *errnop); 47NSS_STATUS _nss_winbind_setgrent(void); 48NSS_STATUS _nss_winbind_endgrent(void); 49NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer, 50 size_t buflen, int *errnop); 51NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer, 52 size_t buflen, int *errnop); 53NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result, 54 char *buffer, size_t buflen, int *errnop); 55NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer, 56 size_t buflen, int *errnop); 57NSS_STATUS _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start, 58 long int *size, gid_t **groups, 59 long int limit, int *errnop); 60NSS_STATUS _nss_winbind_getusersids(const char *user_sid, char **group_sids, 61 int *num_groups, char *buffer, size_t buf_size, 62 int *errnop); 63NSS_STATUS _nss_winbind_nametosid(const char *name, char **sid, char *buffer, 64 size_t buflen, int *errnop); 65NSS_STATUS _nss_winbind_sidtoname(const char *sid, char **name, char *buffer, 66 size_t buflen, int *errnop); 67NSS_STATUS _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop); 68NSS_STATUS _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop); 69NSS_STATUS _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer, 70 size_t buflen, int *errnop); 71NSS_STATUS _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer, 72 size_t buflen, int *errnop); 73 74/* Prototypes from wb_common.c */ 75 76/* Allocate some space from the nss static buffer. The buffer and buflen 77 are the pointers passed in by the C library to the _nss_ntdom_* 78 functions. */ 79 80static char *get_static(char **buffer, size_t *buflen, size_t len) 81{ 82 char *result; 83 84 /* Error check. We return false if things aren't set up right, or 85 there isn't enough buffer space left. */ 86 87 if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) { 88 return NULL; 89 } 90 91 /* Return an index into the static buffer */ 92 93 result = *buffer; 94 *buffer += len; 95 *buflen -= len; 96 97 return result; 98} 99 100/* I've copied the strtok() replacement function next_token_Xalloc() from 101 lib/util_str.c as I really don't want to have to link in any other 102 objects if I can possibly avoid it. */ 103 104static bool next_token_alloc(const char **ptr, 105 char **pp_buff, 106 const char *sep) 107{ 108 const char *s; 109 const char *saved_s; 110 char *pbuf; 111 bool quoted; 112 size_t len=1; 113 114 *pp_buff = NULL; 115 if (!ptr) { 116 return(false); 117 } 118 119 s = *ptr; 120 121 /* default to simple separators */ 122 if (!sep) { 123 sep = " \t\n\r"; 124 } 125 126 /* find the first non sep char */ 127 while (*s && strchr(sep,*s)) { 128 s++; 129 } 130 131 /* nothing left? */ 132 if (!*s) { 133 return false; 134 } 135 136 /* When restarting we need to go from here. */ 137 saved_s = s; 138 139 /* Work out the length needed. */ 140 for (quoted = false; *s && 141 (quoted || !strchr(sep,*s)); s++) { 142 if (*s == '\"') { 143 quoted = !quoted; 144 } else { 145 len++; 146 } 147 } 148 149 /* We started with len = 1 so we have space for the nul. */ 150 *pp_buff = (char *)malloc(len); 151 if (!*pp_buff) { 152 return false; 153 } 154 155 /* copy over the token */ 156 pbuf = *pp_buff; 157 s = saved_s; 158 for (quoted = false; *s && 159 (quoted || !strchr(sep,*s)); s++) { 160 if ( *s == '\"' ) { 161 quoted = !quoted; 162 } else { 163 *pbuf++ = *s; 164 } 165 } 166 167 *ptr = (*s) ? s+1 : s; 168 *pbuf = 0; 169 170 return true; 171} 172 173/* Fill a pwent structure from a winbindd_response structure. We use 174 the static data passed to us by libc to put strings and stuff in. 175 Return NSS_STATUS_TRYAGAIN if we run out of memory. */ 176 177static NSS_STATUS fill_pwent(struct passwd *result, 178 struct winbindd_pw *pw, 179 char **buffer, size_t *buflen) 180{ 181 /* User name */ 182 183 if ((result->pw_name = 184 get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) { 185 186 /* Out of memory */ 187 188 return NSS_STATUS_TRYAGAIN; 189 } 190 191 strcpy(result->pw_name, pw->pw_name); 192 193 /* Password */ 194 195 if ((result->pw_passwd = 196 get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) { 197 198 /* Out of memory */ 199 200 return NSS_STATUS_TRYAGAIN; 201 } 202 203 strcpy(result->pw_passwd, pw->pw_passwd); 204 205 /* [ug]id */ 206 207 result->pw_uid = pw->pw_uid; 208 result->pw_gid = pw->pw_gid; 209 210 /* GECOS */ 211 212 if ((result->pw_gecos = 213 get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) { 214 215 /* Out of memory */ 216 217 return NSS_STATUS_TRYAGAIN; 218 } 219 220 strcpy(result->pw_gecos, pw->pw_gecos); 221 222 /* Home directory */ 223 224 if ((result->pw_dir = 225 get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) { 226 227 /* Out of memory */ 228 229 return NSS_STATUS_TRYAGAIN; 230 } 231 232 strcpy(result->pw_dir, pw->pw_dir); 233 234 /* Logon shell */ 235 236 if ((result->pw_shell = 237 get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) { 238 239 /* Out of memory */ 240 241 return NSS_STATUS_TRYAGAIN; 242 } 243 244 strcpy(result->pw_shell, pw->pw_shell); 245 246 /* The struct passwd for Solaris has some extra fields which must 247 be initialised or nscd crashes. */ 248 249#if HAVE_PASSWD_PW_COMMENT 250 result->pw_comment = ""; 251#endif 252 253#if HAVE_PASSWD_PW_AGE 254 result->pw_age = ""; 255#endif 256 257 return NSS_STATUS_SUCCESS; 258} 259 260/* Fill a grent structure from a winbindd_response structure. We use 261 the static data passed to us by libc to put strings and stuff in. 262 Return NSS_STATUS_TRYAGAIN if we run out of memory. */ 263 264static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr, 265 char *gr_mem, char **buffer, size_t *buflen) 266{ 267 char *name; 268 int i; 269 char *tst; 270 271 /* Group name */ 272 273 if ((result->gr_name = 274 get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) { 275 276 /* Out of memory */ 277 278 return NSS_STATUS_TRYAGAIN; 279 } 280 281 strcpy(result->gr_name, gr->gr_name); 282 283 /* Password */ 284 285 if ((result->gr_passwd = 286 get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) { 287 288 /* Out of memory */ 289 return NSS_STATUS_TRYAGAIN; 290 } 291 292 strcpy(result->gr_passwd, gr->gr_passwd); 293 294 /* gid */ 295 296 result->gr_gid = gr->gr_gid; 297 298 /* Group membership */ 299 300 if ((gr->num_gr_mem < 0) || !gr_mem) { 301 gr->num_gr_mem = 0; 302 } 303 304 /* this next value is a pointer to a pointer so let's align it */ 305 306 /* Calculate number of extra bytes needed to align on pointer size boundry */ 307 if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0) 308 i = sizeof(char*) - i; 309 310 if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) * 311 sizeof(char *)+i))) == NULL) { 312 313 /* Out of memory */ 314 315 return NSS_STATUS_TRYAGAIN; 316 } 317 result->gr_mem = (char **)(tst + i); 318 319 if (gr->num_gr_mem == 0) { 320 321 /* Group is empty */ 322 323 *(result->gr_mem) = NULL; 324 return NSS_STATUS_SUCCESS; 325 } 326 327 /* Start looking at extra data */ 328 329 i = 0; 330 331 while(next_token_alloc((const char **)&gr_mem, &name, ",")) { 332 /* Allocate space for member */ 333 if (((result->gr_mem)[i] = 334 get_static(buffer, buflen, strlen(name) + 1)) == NULL) { 335 free(name); 336 /* Out of memory */ 337 return NSS_STATUS_TRYAGAIN; 338 } 339 strcpy((result->gr_mem)[i], name); 340 free(name); 341 i++; 342 } 343 344 /* Terminate list */ 345 346 (result->gr_mem)[i] = NULL; 347 348 return NSS_STATUS_SUCCESS; 349} 350 351/* 352 * NSS user functions 353 */ 354 355static struct winbindd_response getpwent_response; 356 357static int ndx_pw_cache; /* Current index into pwd cache */ 358static int num_pw_cache; /* Current size of pwd cache */ 359 360/* Rewind "file pointer" to start of ntdom password database */ 361 362NSS_STATUS 363_nss_winbind_setpwent(void) 364{ 365 NSS_STATUS ret; 366#ifdef DEBUG_NSS 367 fprintf(stderr, "[%5d]: setpwent\n", getpid()); 368#endif 369 370#if HAVE_PTHREAD 371 pthread_mutex_lock(&winbind_nss_mutex); 372#endif 373 374 if (num_pw_cache > 0) { 375 ndx_pw_cache = num_pw_cache = 0; 376 winbindd_free_response(&getpwent_response); 377 } 378 379 ret = winbindd_request_response(WINBINDD_SETPWENT, NULL, NULL); 380#ifdef DEBUG_NSS 381 fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(), 382 nss_err_str(ret), ret); 383#endif 384 385#if HAVE_PTHREAD 386 pthread_mutex_unlock(&winbind_nss_mutex); 387#endif 388 return ret; 389} 390 391/* Close ntdom password database "file pointer" */ 392 393NSS_STATUS 394_nss_winbind_endpwent(void) 395{ 396 NSS_STATUS ret; 397#ifdef DEBUG_NSS 398 fprintf(stderr, "[%5d]: endpwent\n", getpid()); 399#endif 400 401#if HAVE_PTHREAD 402 pthread_mutex_lock(&winbind_nss_mutex); 403#endif 404 405 if (num_pw_cache > 0) { 406 ndx_pw_cache = num_pw_cache = 0; 407 winbindd_free_response(&getpwent_response); 408 } 409 410 ret = winbindd_request_response(WINBINDD_ENDPWENT, NULL, NULL); 411#ifdef DEBUG_NSS 412 fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(), 413 nss_err_str(ret), ret); 414#endif 415 416#if HAVE_PTHREAD 417 pthread_mutex_unlock(&winbind_nss_mutex); 418#endif 419 420 return ret; 421} 422 423/* Fetch the next password entry from ntdom password database */ 424 425NSS_STATUS 426_nss_winbind_getpwent_r(struct passwd *result, char *buffer, 427 size_t buflen, int *errnop) 428{ 429 NSS_STATUS ret; 430 struct winbindd_request request; 431 static int called_again; 432 433#ifdef DEBUG_NSS 434 fprintf(stderr, "[%5d]: getpwent\n", getpid()); 435#endif 436 437#if HAVE_PTHREAD 438 pthread_mutex_lock(&winbind_nss_mutex); 439#endif 440 441 /* Return an entry from the cache if we have one, or if we are 442 called again because we exceeded our static buffer. */ 443 444 if ((ndx_pw_cache < num_pw_cache) || called_again) { 445 goto return_result; 446 } 447 448 /* Else call winbindd to get a bunch of entries */ 449 450 if (num_pw_cache > 0) { 451 winbindd_free_response(&getpwent_response); 452 } 453 454 ZERO_STRUCT(request); 455 ZERO_STRUCT(getpwent_response); 456 457 request.data.num_entries = MAX_GETPWENT_USERS; 458 459 ret = winbindd_request_response(WINBINDD_GETPWENT, &request, 460 &getpwent_response); 461 462 if (ret == NSS_STATUS_SUCCESS) { 463 struct winbindd_pw *pw_cache; 464 465 /* Fill cache */ 466 467 ndx_pw_cache = 0; 468 num_pw_cache = getpwent_response.data.num_entries; 469 470 /* Return a result */ 471 472 return_result: 473 474 pw_cache = (struct winbindd_pw *) 475 getpwent_response.extra_data.data; 476 477 /* Check data is valid */ 478 479 if (pw_cache == NULL) { 480 ret = NSS_STATUS_NOTFOUND; 481 goto done; 482 } 483 484 ret = fill_pwent(result, &pw_cache[ndx_pw_cache], 485 &buffer, &buflen); 486 487 /* Out of memory - try again */ 488 489 if (ret == NSS_STATUS_TRYAGAIN) { 490 called_again = true; 491 *errnop = errno = ERANGE; 492 goto done; 493 } 494 495 *errnop = errno = 0; 496 called_again = false; 497 ndx_pw_cache++; 498 499 /* If we've finished with this lot of results free cache */ 500 501 if (ndx_pw_cache == num_pw_cache) { 502 ndx_pw_cache = num_pw_cache = 0; 503 winbindd_free_response(&getpwent_response); 504 } 505 } 506 done: 507#ifdef DEBUG_NSS 508 fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(), 509 nss_err_str(ret), ret); 510#endif 511 512#if HAVE_PTHREAD 513 pthread_mutex_unlock(&winbind_nss_mutex); 514#endif 515 return ret; 516} 517 518/* Return passwd struct from uid */ 519 520NSS_STATUS 521_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, 522 size_t buflen, int *errnop) 523{ 524 NSS_STATUS ret; 525 static struct winbindd_response response; 526 struct winbindd_request request; 527 static int keep_response; 528 529#ifdef DEBUG_NSS 530 fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid); 531#endif 532 533#if HAVE_PTHREAD 534 pthread_mutex_lock(&winbind_nss_mutex); 535#endif 536 537 /* If our static buffer needs to be expanded we are called again */ 538 if (!keep_response || uid != response.data.pw.pw_uid) { 539 540 /* Call for the first time */ 541 542 ZERO_STRUCT(response); 543 ZERO_STRUCT(request); 544 545 request.data.uid = uid; 546 547 ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response); 548 549 if (ret == NSS_STATUS_SUCCESS) { 550 ret = fill_pwent(result, &response.data.pw, 551 &buffer, &buflen); 552 553 if (ret == NSS_STATUS_TRYAGAIN) { 554 keep_response = true; 555 *errnop = errno = ERANGE; 556 goto done; 557 } 558 } 559 560 } else { 561 562 /* We've been called again */ 563 564 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen); 565 566 if (ret == NSS_STATUS_TRYAGAIN) { 567 *errnop = errno = ERANGE; 568 goto done; 569 } 570 571 keep_response = false; 572 *errnop = errno = 0; 573 } 574 575 winbindd_free_response(&response); 576 577 done: 578 579#ifdef DEBUG_NSS 580 fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(), 581 (unsigned int)uid, nss_err_str(ret), ret); 582#endif 583 584#if HAVE_PTHREAD 585 pthread_mutex_unlock(&winbind_nss_mutex); 586#endif 587 588 return ret; 589} 590 591/* Return passwd struct from username */ 592NSS_STATUS 593_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer, 594 size_t buflen, int *errnop) 595{ 596 NSS_STATUS ret; 597 static struct winbindd_response response; 598 struct winbindd_request request; 599 static int keep_response; 600 601#ifdef DEBUG_NSS 602 fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name); 603#endif 604 605#if HAVE_PTHREAD 606 pthread_mutex_lock(&winbind_nss_mutex); 607#endif 608 609 /* If our static buffer needs to be expanded we are called again */ 610 611 if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) { 612 613 /* Call for the first time */ 614 615 ZERO_STRUCT(response); 616 ZERO_STRUCT(request); 617 618 strncpy(request.data.username, name, 619 sizeof(request.data.username) - 1); 620 request.data.username 621 [sizeof(request.data.username) - 1] = '\0'; 622 623 ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response); 624 625 if (ret == NSS_STATUS_SUCCESS) { 626 ret = fill_pwent(result, &response.data.pw, &buffer, 627 &buflen); 628 629 if (ret == NSS_STATUS_TRYAGAIN) { 630 keep_response = true; 631 *errnop = errno = ERANGE; 632 goto done; 633 } 634 } 635 636 } else { 637 638 /* We've been called again */ 639 640 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen); 641 642 if (ret == NSS_STATUS_TRYAGAIN) { 643 keep_response = true; 644 *errnop = errno = ERANGE; 645 goto done; 646 } 647 648 keep_response = false; 649 *errnop = errno = 0; 650 } 651 652 winbindd_free_response(&response); 653 done: 654#ifdef DEBUG_NSS 655 fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(), 656 name, nss_err_str(ret), ret); 657#endif 658 659#if HAVE_PTHREAD 660 pthread_mutex_unlock(&winbind_nss_mutex); 661#endif 662 663 return ret; 664} 665 666/* 667 * NSS group functions 668 */ 669 670static struct winbindd_response getgrent_response; 671 672static int ndx_gr_cache; /* Current index into grp cache */ 673static int num_gr_cache; /* Current size of grp cache */ 674 675/* Rewind "file pointer" to start of ntdom group database */ 676 677NSS_STATUS 678_nss_winbind_setgrent(void) 679{ 680 NSS_STATUS ret; 681#ifdef DEBUG_NSS 682 fprintf(stderr, "[%5d]: setgrent\n", getpid()); 683#endif 684 685#if HAVE_PTHREAD 686 pthread_mutex_lock(&winbind_nss_mutex); 687#endif 688 689 if (num_gr_cache > 0) { 690 ndx_gr_cache = num_gr_cache = 0; 691 winbindd_free_response(&getgrent_response); 692 } 693 694 ret = winbindd_request_response(WINBINDD_SETGRENT, NULL, NULL); 695#ifdef DEBUG_NSS 696 fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(), 697 nss_err_str(ret), ret); 698#endif 699 700#if HAVE_PTHREAD 701 pthread_mutex_unlock(&winbind_nss_mutex); 702#endif 703 704 return ret; 705} 706 707/* Close "file pointer" for ntdom group database */ 708 709NSS_STATUS 710_nss_winbind_endgrent(void) 711{ 712 NSS_STATUS ret; 713#ifdef DEBUG_NSS 714 fprintf(stderr, "[%5d]: endgrent\n", getpid()); 715#endif 716 717#if HAVE_PTHREAD 718 pthread_mutex_lock(&winbind_nss_mutex); 719#endif 720 721 if (num_gr_cache > 0) { 722 ndx_gr_cache = num_gr_cache = 0; 723 winbindd_free_response(&getgrent_response); 724 } 725 726 ret = winbindd_request_response(WINBINDD_ENDGRENT, NULL, NULL); 727#ifdef DEBUG_NSS 728 fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(), 729 nss_err_str(ret), ret); 730#endif 731 732#if HAVE_PTHREAD 733 pthread_mutex_unlock(&winbind_nss_mutex); 734#endif 735 736 return ret; 737} 738 739/* Get next entry from ntdom group database */ 740 741static NSS_STATUS 742winbind_getgrent(enum winbindd_cmd cmd, 743 struct group *result, 744 char *buffer, size_t buflen, int *errnop) 745{ 746 NSS_STATUS ret; 747 static struct winbindd_request request; 748 static int called_again; 749 750 751#ifdef DEBUG_NSS 752 fprintf(stderr, "[%5d]: getgrent\n", getpid()); 753#endif 754 755#if HAVE_PTHREAD 756 pthread_mutex_lock(&winbind_nss_mutex); 757#endif 758 759 /* Return an entry from the cache if we have one, or if we are 760 called again because we exceeded our static buffer. */ 761 762 if ((ndx_gr_cache < num_gr_cache) || called_again) { 763 goto return_result; 764 } 765 766 /* Else call winbindd to get a bunch of entries */ 767 768 if (num_gr_cache > 0) { 769 winbindd_free_response(&getgrent_response); 770 } 771 772 ZERO_STRUCT(request); 773 ZERO_STRUCT(getgrent_response); 774 775 request.data.num_entries = MAX_GETGRENT_USERS; 776 777 ret = winbindd_request_response(cmd, &request, 778 &getgrent_response); 779 780 if (ret == NSS_STATUS_SUCCESS) { 781 struct winbindd_gr *gr_cache; 782 int mem_ofs; 783 784 /* Fill cache */ 785 786 ndx_gr_cache = 0; 787 num_gr_cache = getgrent_response.data.num_entries; 788 789 /* Return a result */ 790 791 return_result: 792 793 gr_cache = (struct winbindd_gr *) 794 getgrent_response.extra_data.data; 795 796 /* Check data is valid */ 797 798 if (gr_cache == NULL) { 799 ret = NSS_STATUS_NOTFOUND; 800 goto done; 801 } 802 803 /* Fill group membership. The offset into the extra data 804 for the group membership is the reported offset plus the 805 size of all the winbindd_gr records returned. */ 806 807 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs + 808 num_gr_cache * sizeof(struct winbindd_gr); 809 810 ret = fill_grent(result, &gr_cache[ndx_gr_cache], 811 ((char *)getgrent_response.extra_data.data)+mem_ofs, 812 &buffer, &buflen); 813 814 /* Out of memory - try again */ 815 816 if (ret == NSS_STATUS_TRYAGAIN) { 817 called_again = true; 818 *errnop = errno = ERANGE; 819 goto done; 820 } 821 822 *errnop = 0; 823 called_again = false; 824 ndx_gr_cache++; 825 826 /* If we've finished with this lot of results free cache */ 827 828 if (ndx_gr_cache == num_gr_cache) { 829 ndx_gr_cache = num_gr_cache = 0; 830 winbindd_free_response(&getgrent_response); 831 } 832 } 833 done: 834#ifdef DEBUG_NSS 835 fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(), 836 nss_err_str(ret), ret); 837#endif 838 839#if HAVE_PTHREAD 840 pthread_mutex_unlock(&winbind_nss_mutex); 841#endif 842 843 return ret; 844} 845 846 847NSS_STATUS 848_nss_winbind_getgrent_r(struct group *result, 849 char *buffer, size_t buflen, int *errnop) 850{ 851 return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop); 852} 853 854NSS_STATUS 855_nss_winbind_getgrlst_r(struct group *result, 856 char *buffer, size_t buflen, int *errnop) 857{ 858 return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop); 859} 860 861/* Return group struct from group name */ 862 863NSS_STATUS 864_nss_winbind_getgrnam_r(const char *name, 865 struct group *result, char *buffer, 866 size_t buflen, int *errnop) 867{ 868 NSS_STATUS ret; 869 static struct winbindd_response response; 870 struct winbindd_request request; 871 static int keep_response; 872 873#ifdef DEBUG_NSS 874 fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name); 875#endif 876 877#if HAVE_PTHREAD 878 pthread_mutex_lock(&winbind_nss_mutex); 879#endif 880 881 /* If our static buffer needs to be expanded we are called again */ 882 /* Or if the stored response group name differs from the request. */ 883 884 if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) { 885 886 /* Call for the first time */ 887 888 ZERO_STRUCT(request); 889 ZERO_STRUCT(response); 890 891 strncpy(request.data.groupname, name, 892 sizeof(request.data.groupname)); 893 request.data.groupname 894 [sizeof(request.data.groupname) - 1] = '\0'; 895 896 ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response); 897 898 if (ret == NSS_STATUS_SUCCESS) { 899 ret = fill_grent(result, &response.data.gr, 900 (char *)response.extra_data.data, 901 &buffer, &buflen); 902 903 if (ret == NSS_STATUS_TRYAGAIN) { 904 keep_response = true; 905 *errnop = errno = ERANGE; 906 goto done; 907 } 908 } 909 910 } else { 911 912 /* We've been called again */ 913 914 ret = fill_grent(result, &response.data.gr, 915 (char *)response.extra_data.data, &buffer, 916 &buflen); 917 918 if (ret == NSS_STATUS_TRYAGAIN) { 919 keep_response = true; 920 *errnop = errno = ERANGE; 921 goto done; 922 } 923 924 keep_response = false; 925 *errnop = 0; 926 } 927 928 winbindd_free_response(&response); 929 done: 930#ifdef DEBUG_NSS 931 fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(), 932 name, nss_err_str(ret), ret); 933#endif 934 935#if HAVE_PTHREAD 936 pthread_mutex_unlock(&winbind_nss_mutex); 937#endif 938 939 return ret; 940} 941 942/* Return group struct from gid */ 943 944NSS_STATUS 945_nss_winbind_getgrgid_r(gid_t gid, 946 struct group *result, char *buffer, 947 size_t buflen, int *errnop) 948{ 949 NSS_STATUS ret; 950 static struct winbindd_response response; 951 struct winbindd_request request; 952 static int keep_response; 953 954#ifdef DEBUG_NSS 955 fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid); 956#endif 957 958#if HAVE_PTHREAD 959 pthread_mutex_lock(&winbind_nss_mutex); 960#endif 961 962 /* If our static buffer needs to be expanded we are called again */ 963 /* Or if the stored response group name differs from the request. */ 964 965 if (!keep_response || gid != response.data.gr.gr_gid) { 966 967 /* Call for the first time */ 968 969 ZERO_STRUCT(request); 970 ZERO_STRUCT(response); 971 972 request.data.gid = gid; 973 974 ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response); 975 976 if (ret == NSS_STATUS_SUCCESS) { 977 978 ret = fill_grent(result, &response.data.gr, 979 (char *)response.extra_data.data, 980 &buffer, &buflen); 981 982 if (ret == NSS_STATUS_TRYAGAIN) { 983 keep_response = true; 984 *errnop = errno = ERANGE; 985 goto done; 986 } 987 } 988 989 } else { 990 991 /* We've been called again */ 992 993 ret = fill_grent(result, &response.data.gr, 994 (char *)response.extra_data.data, &buffer, 995 &buflen); 996 997 if (ret == NSS_STATUS_TRYAGAIN) { 998 keep_response = true; 999 *errnop = errno = ERANGE; 1000 goto done; 1001 } 1002 1003 keep_response = false; 1004 *errnop = 0; 1005 } 1006 1007 winbindd_free_response(&response); 1008 done: 1009#ifdef DEBUG_NSS 1010 fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(), 1011 (unsigned int)gid, nss_err_str(ret), ret); 1012#endif 1013 1014#if HAVE_PTHREAD 1015 pthread_mutex_unlock(&winbind_nss_mutex); 1016#endif 1017 return ret; 1018} 1019 1020/* Initialise supplementary groups */ 1021 1022NSS_STATUS 1023_nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start, 1024 long int *size, gid_t **groups, long int limit, 1025 int *errnop) 1026{ 1027 NSS_STATUS ret; 1028 struct winbindd_request request; 1029 struct winbindd_response response; 1030 int i; 1031 1032#ifdef DEBUG_NSS 1033 fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(), 1034 user, group); 1035#endif 1036 1037#if HAVE_PTHREAD 1038 pthread_mutex_lock(&winbind_nss_mutex); 1039#endif 1040 1041 ZERO_STRUCT(request); 1042 ZERO_STRUCT(response); 1043 1044 strncpy(request.data.username, user, 1045 sizeof(request.data.username) - 1); 1046 1047 ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response); 1048 1049 if (ret == NSS_STATUS_SUCCESS) { 1050 int num_gids = response.data.num_entries; 1051 gid_t *gid_list = (gid_t *)response.extra_data.data; 1052 1053#ifdef DEBUG_NSS 1054 fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS " 1055 "and %d gids\n", getpid(), 1056 user, num_gids); 1057#endif 1058 if (gid_list == NULL) { 1059 ret = NSS_STATUS_NOTFOUND; 1060 goto done; 1061 } 1062 1063 /* Copy group list to client */ 1064 1065 for (i = 0; i < num_gids; i++) { 1066 1067#ifdef DEBUG_NSS 1068 fprintf(stderr, "[%5d]: initgroups %s (%d): " 1069 "processing gid %d \n", getpid(), 1070 user, group, gid_list[i]); 1071#endif 1072 1073 /* Skip primary group */ 1074 1075 if (gid_list[i] == group) { 1076 continue; 1077 } 1078 1079 /* Filled buffer ? If so, resize. */ 1080 1081 if (*start == *size) { 1082 long int newsize; 1083 gid_t *newgroups; 1084 1085 newsize = 2 * (*size); 1086 if (limit > 0) { 1087 if (*size == limit) { 1088 goto done; 1089 } 1090 if (newsize > limit) { 1091 newsize = limit; 1092 } 1093 } 1094 1095 newgroups = (gid_t *) 1096 realloc((*groups), 1097 newsize * sizeof(**groups)); 1098 if (!newgroups) { 1099 *errnop = ENOMEM; 1100 ret = NSS_STATUS_NOTFOUND; 1101 goto done; 1102 } 1103 *groups = newgroups; 1104 *size = newsize; 1105 } 1106 1107 /* Add to buffer */ 1108 1109 (*groups)[*start] = gid_list[i]; 1110 *start += 1; 1111 } 1112 } 1113 1114 /* Back to your regularly scheduled programming */ 1115 1116 done: 1117#ifdef DEBUG_NSS 1118 fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(), 1119 user, nss_err_str(ret), ret); 1120#endif 1121 1122#if HAVE_PTHREAD 1123 pthread_mutex_unlock(&winbind_nss_mutex); 1124#endif 1125 1126 return ret; 1127} 1128 1129 1130/* return a list of group SIDs for a user SID */ 1131NSS_STATUS 1132_nss_winbind_getusersids(const char *user_sid, char **group_sids, 1133 int *num_groups, 1134 char *buffer, size_t buf_size, int *errnop) 1135{ 1136 NSS_STATUS ret; 1137 struct winbindd_request request; 1138 struct winbindd_response response; 1139 1140#ifdef DEBUG_NSS 1141 fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid); 1142#endif 1143 1144#if HAVE_PTHREAD 1145 pthread_mutex_lock(&winbind_nss_mutex); 1146#endif 1147 1148 ZERO_STRUCT(request); 1149 ZERO_STRUCT(response); 1150 1151 strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1); 1152 request.data.sid[sizeof(request.data.sid) - 1] = '\0'; 1153 1154 ret = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response); 1155 1156 if (ret != NSS_STATUS_SUCCESS) { 1157 goto done; 1158 } 1159 1160 if (buf_size < response.length - sizeof(response)) { 1161 ret = NSS_STATUS_TRYAGAIN; 1162 errno = *errnop = ERANGE; 1163 goto done; 1164 } 1165 1166 *num_groups = response.data.num_entries; 1167 *group_sids = buffer; 1168 memcpy(buffer, response.extra_data.data, response.length - sizeof(response)); 1169 errno = *errnop = 0; 1170 1171 done: 1172 winbindd_free_response(&response); 1173 1174#if HAVE_PTHREAD 1175 pthread_mutex_unlock(&winbind_nss_mutex); 1176#endif 1177 1178 return ret; 1179} 1180 1181 1182/* map a user or group name to a SID string */ 1183NSS_STATUS 1184_nss_winbind_nametosid(const char *name, char **sid, char *buffer, 1185 size_t buflen, int *errnop) 1186{ 1187 NSS_STATUS ret; 1188 struct winbindd_response response; 1189 struct winbindd_request request; 1190 1191#ifdef DEBUG_NSS 1192 fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name); 1193#endif 1194 1195#if HAVE_PTHREAD 1196 pthread_mutex_lock(&winbind_nss_mutex); 1197#endif 1198 1199 ZERO_STRUCT(response); 1200 ZERO_STRUCT(request); 1201 1202 strncpy(request.data.name.name, name, 1203 sizeof(request.data.name.name) - 1); 1204 request.data.name.name[sizeof(request.data.name.name) - 1] = '\0'; 1205 1206 ret = winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response); 1207 if (ret != NSS_STATUS_SUCCESS) { 1208 *errnop = errno = EINVAL; 1209 goto failed; 1210 } 1211 1212 if (buflen < strlen(response.data.sid.sid)+1) { 1213 ret = NSS_STATUS_TRYAGAIN; 1214 *errnop = errno = ERANGE; 1215 goto failed; 1216 } 1217 1218 *errnop = errno = 0; 1219 *sid = buffer; 1220 strcpy(*sid, response.data.sid.sid); 1221 1222failed: 1223 winbindd_free_response(&response); 1224 1225#if HAVE_PTHREAD 1226 pthread_mutex_unlock(&winbind_nss_mutex); 1227#endif 1228 1229 return ret; 1230} 1231 1232/* map a sid string to a user or group name */ 1233NSS_STATUS 1234_nss_winbind_sidtoname(const char *sid, char **name, char *buffer, 1235 size_t buflen, int *errnop) 1236{ 1237 NSS_STATUS ret; 1238 struct winbindd_response response; 1239 struct winbindd_request request; 1240 static char sep_char; 1241 unsigned needed; 1242 1243#ifdef DEBUG_NSS 1244 fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid); 1245#endif 1246 1247#if HAVE_PTHREAD 1248 pthread_mutex_lock(&winbind_nss_mutex); 1249#endif 1250 1251 ZERO_STRUCT(response); 1252 ZERO_STRUCT(request); 1253 1254 /* we need to fetch the separator first time through */ 1255 if (!sep_char) { 1256 ret = winbindd_request_response(WINBINDD_INFO, &request, &response); 1257 if (ret != NSS_STATUS_SUCCESS) { 1258 *errnop = errno = EINVAL; 1259 goto failed; 1260 } 1261 1262 sep_char = response.data.info.winbind_separator; 1263 winbindd_free_response(&response); 1264 } 1265 1266 1267 strncpy(request.data.sid, sid, 1268 sizeof(request.data.sid) - 1); 1269 request.data.sid[sizeof(request.data.sid) - 1] = '\0'; 1270 1271 ret = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response); 1272 if (ret != NSS_STATUS_SUCCESS) { 1273 *errnop = errno = EINVAL; 1274 goto failed; 1275 } 1276 1277 needed = 1278 strlen(response.data.name.dom_name) + 1279 strlen(response.data.name.name) + 2; 1280 1281 if (buflen < needed) { 1282 ret = NSS_STATUS_TRYAGAIN; 1283 *errnop = errno = ERANGE; 1284 goto failed; 1285 } 1286 1287 snprintf(buffer, needed, "%s%c%s", 1288 response.data.name.dom_name, 1289 sep_char, 1290 response.data.name.name); 1291 1292 *name = buffer; 1293 *errnop = errno = 0; 1294 1295failed: 1296 winbindd_free_response(&response); 1297 1298#if HAVE_PTHREAD 1299 pthread_mutex_unlock(&winbind_nss_mutex); 1300#endif 1301 1302 return ret; 1303} 1304 1305/* map a sid to a uid */ 1306NSS_STATUS 1307_nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop) 1308{ 1309 NSS_STATUS ret; 1310 struct winbindd_response response; 1311 struct winbindd_request request; 1312 1313#ifdef DEBUG_NSS 1314 fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid); 1315#endif 1316 1317#if HAVE_PTHREAD 1318 pthread_mutex_lock(&winbind_nss_mutex); 1319#endif 1320 1321 ZERO_STRUCT(request); 1322 ZERO_STRUCT(response); 1323 1324 strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1); 1325 request.data.sid[sizeof(request.data.sid) - 1] = '\0'; 1326 1327 ret = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response); 1328 if (ret != NSS_STATUS_SUCCESS) { 1329 *errnop = errno = EINVAL; 1330 goto failed; 1331 } 1332 1333 *uid = response.data.uid; 1334 1335failed: 1336 1337#if HAVE_PTHREAD 1338 pthread_mutex_unlock(&winbind_nss_mutex); 1339#endif 1340 1341 return ret; 1342} 1343 1344/* map a sid to a gid */ 1345NSS_STATUS 1346_nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop) 1347{ 1348 NSS_STATUS ret; 1349 struct winbindd_response response; 1350 struct winbindd_request request; 1351 1352#ifdef DEBUG_NSS 1353 fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid); 1354#endif 1355 1356#if HAVE_PTHREAD 1357 pthread_mutex_lock(&winbind_nss_mutex); 1358#endif 1359 1360 ZERO_STRUCT(request); 1361 ZERO_STRUCT(response); 1362 1363 strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1); 1364 request.data.sid[sizeof(request.data.sid) - 1] = '\0'; 1365 1366 ret = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response); 1367 if (ret != NSS_STATUS_SUCCESS) { 1368 *errnop = errno = EINVAL; 1369 goto failed; 1370 } 1371 1372 *gid = response.data.gid; 1373 1374failed: 1375 1376#if HAVE_PTHREAD 1377 pthread_mutex_unlock(&winbind_nss_mutex); 1378#endif 1379 1380 return ret; 1381} 1382 1383/* map a uid to a SID string */ 1384NSS_STATUS 1385_nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer, 1386 size_t buflen, int *errnop) 1387{ 1388 NSS_STATUS ret; 1389 struct winbindd_response response; 1390 struct winbindd_request request; 1391 1392#ifdef DEBUG_NSS 1393 fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid); 1394#endif 1395 1396#if HAVE_PTHREAD 1397 pthread_mutex_lock(&winbind_nss_mutex); 1398#endif 1399 1400 ZERO_STRUCT(response); 1401 ZERO_STRUCT(request); 1402 1403 request.data.uid = uid; 1404 1405 ret = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response); 1406 if (ret != NSS_STATUS_SUCCESS) { 1407 *errnop = errno = EINVAL; 1408 goto failed; 1409 } 1410 1411 if (buflen < strlen(response.data.sid.sid)+1) { 1412 ret = NSS_STATUS_TRYAGAIN; 1413 *errnop = errno = ERANGE; 1414 goto failed; 1415 } 1416 1417 *errnop = errno = 0; 1418 *sid = buffer; 1419 strcpy(*sid, response.data.sid.sid); 1420 1421failed: 1422 winbindd_free_response(&response); 1423 1424#if HAVE_PTHREAD 1425 pthread_mutex_unlock(&winbind_nss_mutex); 1426#endif 1427 1428 return ret; 1429} 1430 1431/* map a gid to a SID string */ 1432NSS_STATUS 1433_nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer, 1434 size_t buflen, int *errnop) 1435{ 1436 NSS_STATUS ret; 1437 struct winbindd_response response; 1438 struct winbindd_request request; 1439 1440#ifdef DEBUG_NSS 1441 fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid); 1442#endif 1443 1444#if HAVE_PTHREAD 1445 pthread_mutex_lock(&winbind_nss_mutex); 1446#endif 1447 1448 ZERO_STRUCT(response); 1449 ZERO_STRUCT(request); 1450 1451 request.data.gid = gid; 1452 1453 ret = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response); 1454 if (ret != NSS_STATUS_SUCCESS) { 1455 *errnop = errno = EINVAL; 1456 goto failed; 1457 } 1458 1459 if (buflen < strlen(response.data.sid.sid)+1) { 1460 ret = NSS_STATUS_TRYAGAIN; 1461 *errnop = errno = ERANGE; 1462 goto failed; 1463 } 1464 1465 *errnop = errno = 0; 1466 *sid = buffer; 1467 strcpy(*sid, response.data.sid.sid); 1468 1469failed: 1470 winbindd_free_response(&response); 1471 1472#if HAVE_PTHREAD 1473 pthread_mutex_unlock(&winbind_nss_mutex); 1474#endif 1475 1476 return ret; 1477} 1478