1 /*- 2 * Copyright (c) 2003 Networks Associates Technology, Inc. 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by 6 * Jacques A. Vidrine, Safeport Network Services, and Network 7 * Associates Laboratories, the Security Research Division of Network 8 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 9 * ("CBOSS"), as part of the DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "namespace.h" 37 #include <sys/param.h> 38 #ifdef YP 39 #include <rpc/rpc.h> 40 #include <rpcsvc/yp_prot.h> 41 #include <rpcsvc/ypclnt.h> 42 #endif 43 #include <ctype.h> 44 #include <errno.h> 45 #ifdef HESIOD 46 #include <hesiod.h> 47 #endif 48 #include <grp.h> 49 #include <nsswitch.h> 50 #include <pthread.h> 51 #include <pthread_np.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <syslog.h> 56 #include <unistd.h> 57 #include "un-namespace.h" 58 #include "libc_private.h" 59 #include "nss_tls.h" 60 61 62 enum constants { 63 GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ 64 GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ 65 SETGRENT = 1, 66 ENDGRENT = 2, 67 HESIOD_NAME_MAX = 256, 68 }; 69 70 static const ns_src defaultsrc[] = { 71 { NSSRC_COMPAT, NS_SUCCESS }, 72 { NULL, 0 } 73 }; 74 75 int __gr_match_entry(const char *, size_t, enum nss_lookup_type, 76 const char *, gid_t); 77 int __gr_parse_entry(char *, size_t, struct group *, char *, size_t, 78 int *); 79 80 static int is_comment_line(const char *, size_t); 81 82 union key { 83 const char *name; 84 gid_t gid; 85 }; 86 static struct group *getgr(int (*)(union key, struct group *, char *, size_t, 87 struct group **), union key); 88 static int wrap_getgrnam_r(union key, struct group *, char *, size_t, 89 struct group **); 90 static int wrap_getgrgid_r(union key, struct group *, char *, size_t, 91 struct group **); 92 static int wrap_getgrent_r(union key, struct group *, char *, size_t, 93 struct group **); 94 95 struct files_state { 96 FILE *fp; 97 int stayopen; 98 }; 99 static void files_endstate(void *); 100 NSS_TLS_HANDLING(files); 101 static int files_setgrent(void *, void *, va_list); 102 static int files_group(void *, void *, va_list); 103 104 105 #ifdef HESIOD 106 struct dns_state { 107 long counter; 108 }; 109 static void dns_endstate(void *); 110 NSS_TLS_HANDLING(dns); 111 static int dns_setgrent(void *, void *, va_list); 112 static int dns_group(void *, void *, va_list); 113 #endif 114 115 116 #ifdef YP 117 struct nis_state { 118 char domain[MAXHOSTNAMELEN]; 119 int done; 120 char *key; 121 int keylen; 122 }; 123 static void nis_endstate(void *); 124 NSS_TLS_HANDLING(nis); 125 static int nis_setgrent(void *, void *, va_list); 126 static int nis_group(void *, void *, va_list); 127 #endif 128 129 struct compat_state { 130 FILE *fp; 131 int stayopen; 132 char *name; 133 enum _compat { 134 COMPAT_MODE_OFF = 0, 135 COMPAT_MODE_ALL, 136 COMPAT_MODE_NAME 137 } compat; 138 }; 139 static void compat_endstate(void *); 140 NSS_TLS_HANDLING(compat); 141 static int compat_setgrent(void *, void *, va_list); 142 static int compat_group(void *, void *, va_list); 143 144 145 /* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ 146 int 147 setgrent(void) 148 { 149 static const ns_dtab dtab[] = { 150 { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, 151 #ifdef HESIOD 152 { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, 153 #endif 154 #ifdef YP 155 { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, 156 #endif 157 { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, 158 { NULL, NULL, NULL } 159 }; 160 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); 161 return (1); 162 } 163 164 165 int 166 setgroupent(int stayopen) 167 { 168 static const ns_dtab dtab[] = { 169 { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, 170 #ifdef HESIOD 171 { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, 172 #endif 173 #ifdef YP 174 { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, 175 #endif 176 { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, 177 { NULL, NULL, NULL } 178 }; 179 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 180 stayopen); 181 return (1); 182 } 183 184 185 void 186 endgrent(void) 187 { 188 static const ns_dtab dtab[] = { 189 { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, 190 #ifdef HESIOD 191 { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, 192 #endif 193 #ifdef YP 194 { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, 195 #endif 196 { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, 197 { NULL, NULL, NULL } 198 }; 199 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc); 200 } 201 202 203 int 204 getgrent_r(struct group *grp, char *buffer, size_t bufsize, 205 struct group **result) 206 { 207 static const ns_dtab dtab[] = { 208 { NSSRC_FILES, files_group, (void *)nss_lt_all }, 209 #ifdef HESIOD 210 { NSSRC_DNS, dns_group, (void *)nss_lt_all }, 211 #endif 212 #ifdef YP 213 { NSSRC_NIS, nis_group, (void *)nss_lt_all }, 214 #endif 215 { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, 216 { NULL, NULL, NULL } 217 }; 218 int rv, ret_errno; 219 220 ret_errno = 0; 221 *result = NULL; 222 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc, 223 grp, buffer, bufsize, &ret_errno); 224 if (rv == NS_SUCCESS) 225 return (0); 226 else 227 return (ret_errno); 228 } 229 230 231 int 232 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, 233 struct group **result) 234 { 235 static const ns_dtab dtab[] = { 236 { NSSRC_FILES, files_group, (void *)nss_lt_name }, 237 #ifdef HESIOD 238 { NSSRC_DNS, dns_group, (void *)nss_lt_name }, 239 #endif 240 #ifdef YP 241 { NSSRC_NIS, nis_group, (void *)nss_lt_name }, 242 #endif 243 { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, 244 { NULL, NULL, NULL } 245 }; 246 int rv, ret_errno; 247 248 ret_errno = 0; 249 *result = NULL; 250 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc, 251 name, grp, buffer, bufsize, &ret_errno); 252 if (rv == NS_SUCCESS) 253 return (0); 254 else 255 return (ret_errno); 256 } 257 258 259 int 260 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, 261 struct group **result) 262 { 263 static const ns_dtab dtab[] = { 264 { NSSRC_FILES, files_group, (void *)nss_lt_id }, 265 #ifdef HESIOD 266 { NSSRC_DNS, dns_group, (void *)nss_lt_id }, 267 #endif 268 #ifdef YP 269 { NSSRC_NIS, nis_group, (void *)nss_lt_id }, 270 #endif 271 { NSSRC_COMPAT, compat_group, (void *)nss_lt_id }, 272 { NULL, NULL, NULL } 273 }; 274 int rv, ret_errno; 275 276 ret_errno = 0; 277 *result = NULL; 278 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc, 279 gid, grp, buffer, bufsize, &ret_errno); 280 if (rv == NS_SUCCESS) 281 return (0); 282 else 283 return (ret_errno); 284 } 285 286 287 static struct group grp; 288 static char *grp_storage; 289 static size_t grp_storage_size; 290 291 static struct group * 292 getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **), 293 union key key) 294 { 295 int rv; 296 struct group *res; 297 298 if (grp_storage == NULL) { 299 grp_storage = malloc(GRP_STORAGE_INITIAL); 300 if (grp_storage == NULL) 301 return (NULL); 302 grp_storage_size = GRP_STORAGE_INITIAL; 303 } 304 do { 305 rv = fn(key, &grp, grp_storage, grp_storage_size, &res); 306 if (res == NULL && rv == ERANGE) { 307 free(grp_storage); 308 if ((grp_storage_size << 1) > GRP_STORAGE_MAX) { 309 grp_storage = NULL; 310 errno = ERANGE; 311 return (NULL); 312 } 313 grp_storage_size <<= 1; 314 grp_storage = malloc(grp_storage_size); 315 if (grp_storage == NULL) 316 return (NULL); 317 } 318 } while (res == NULL && rv == ERANGE); 319 if (rv != 0) 320 errno = rv; 321 return (res); 322 } 323 324 325 static int 326 wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, 327 struct group **res) 328 { 329 return (getgrnam_r(key.name, grp, buffer, bufsize, res)); 330 } 331 332 333 static int 334 wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, 335 struct group **res) 336 { 337 return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); 338 } 339 340 341 static int 342 wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, 343 size_t bufsize, struct group **res) 344 { 345 return (getgrent_r(grp, buffer, bufsize, res)); 346 } 347 348 349 struct group * 350 getgrnam(const char *name) 351 { 352 union key key; 353 354 key.name = name; 355 return (getgr(wrap_getgrnam_r, key)); 356 } 357 358 359 struct group * 360 getgrgid(gid_t gid) 361 { 362 union key key; 363 364 key.gid = gid; 365 return (getgr(wrap_getgrgid_r, key)); 366 } 367 368 369 struct group * 370 getgrent(void) 371 { 372 union key key; 373 374 key.gid = 0; /* not used */ 375 return (getgr(wrap_getgrent_r, key)); 376 } 377 378 379 static int 380 is_comment_line(const char *s, size_t n) 381 { 382 const char *eom; 383 384 eom = &s[n]; 385 386 for (; s < eom; s++) 387 if (*s == '#' || !isspace((unsigned char)*s)) 388 break; 389 return (*s == '#' || s == eom); 390 } 391 392 393 /* 394 * files backend 395 */ 396 static void 397 files_endstate(void *p) 398 { 399 400 if (p == NULL) 401 return; 402 if (((struct files_state *)p)->fp != NULL) 403 fclose(((struct files_state *)p)->fp); 404 free(p); 405 } 406 407 408 static int 409 files_setgrent(void *retval, void *mdata, va_list ap) 410 { 411 struct files_state *st; 412 int rv, stayopen; 413 414 rv = files_getstate(&st); 415 if (rv != 0) 416 return (NS_UNAVAIL); 417 switch ((enum constants)mdata) { 418 case SETGRENT: 419 stayopen = va_arg(ap, int); 420 if (st->fp != NULL) 421 rewind(st->fp); 422 else if (stayopen) 423 st->fp = fopen(_PATH_GROUP, "r"); 424 break; 425 case ENDGRENT: 426 if (st->fp != NULL) { 427 fclose(st->fp); 428 st->fp = NULL; 429 } 430 break; 431 default: 432 break; 433 } 434 return (NS_UNAVAIL); 435 } 436 437 438 static int 439 files_group(void *retval, void *mdata, va_list ap) 440 { 441 struct files_state *st; 442 enum nss_lookup_type how; 443 const char *name, *line; 444 struct group *grp; 445 gid_t gid; 446 char *buffer; 447 size_t bufsize, linesize; 448 int rv, stayopen, *errnop; 449 450 name = NULL; 451 gid = (gid_t)-1; 452 how = (enum nss_lookup_type)mdata; 453 switch (how) { 454 case nss_lt_name: 455 name = va_arg(ap, const char *); 456 break; 457 case nss_lt_id: 458 gid = va_arg(ap, gid_t); 459 break; 460 case nss_lt_all: 461 break; 462 default: 463 return (NS_NOTFOUND); 464 } 465 grp = va_arg(ap, struct group *); 466 buffer = va_arg(ap, char *); 467 bufsize = va_arg(ap, size_t); 468 errnop = va_arg(ap, int *); 469 *errnop = files_getstate(&st); 470 if (*errnop != 0) 471 return (NS_UNAVAIL); 472 if (st->fp == NULL && 473 ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { 474 *errnop = errno; 475 return (NS_UNAVAIL); 476 } 477 if (how == nss_lt_all) 478 stayopen = 1; 479 else { 480 rewind(st->fp); 481 stayopen = st->stayopen; 482 } 483 rv = NS_NOTFOUND; 484 while ((line = fgetln(st->fp, &linesize)) != NULL) { 485 if (line[linesize-1] == '\n') 486 linesize--; 487 rv = __gr_match_entry(line, linesize, how, name, gid); 488 if (rv != NS_SUCCESS) 489 continue; 490 /* We need room at least for the line, a string NUL 491 * terminator, alignment padding, and one (char *) 492 * pointer for the member list terminator. 493 */ 494 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 495 *errnop = ERANGE; 496 rv = NS_RETURN; 497 break; 498 } 499 memcpy(buffer, line, linesize); 500 buffer[linesize] = '\0'; 501 rv = __gr_parse_entry(buffer, linesize, grp, 502 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 503 if (rv & NS_TERMINATE) 504 break; 505 } 506 if (!stayopen && st->fp != NULL) { 507 fclose(st->fp); 508 st->fp = NULL; 509 } 510 if (rv == NS_SUCCESS && retval != NULL) 511 *(struct group **)retval = grp; 512 return (rv); 513 } 514 515 516 #ifdef HESIOD 517 /* 518 * dns backend 519 */ 520 static void 521 dns_endstate(void *p) 522 { 523 524 free(p); 525 } 526 527 528 static int 529 dns_setgrent(void *retval, void *cb_data, va_list ap) 530 { 531 struct dns_state *st; 532 int rv; 533 534 rv = dns_getstate(&st); 535 if (rv != 0) 536 return (NS_UNAVAIL); 537 st->counter = 0; 538 return (NS_UNAVAIL); 539 } 540 541 542 static int 543 dns_group(void *retval, void *mdata, va_list ap) 544 { 545 char buf[HESIOD_NAME_MAX]; 546 struct dns_state *st; 547 struct group *grp; 548 const char *name, *label; 549 void *ctx; 550 char *buffer, **hes; 551 size_t bufsize, adjsize, linesize; 552 gid_t gid; 553 enum nss_lookup_type how; 554 int rv, *errnop; 555 556 ctx = NULL; 557 hes = NULL; 558 name = NULL; 559 gid = (gid_t)-1; 560 how = (enum nss_lookup_type)mdata; 561 switch (how) { 562 case nss_lt_name: 563 name = va_arg(ap, const char *); 564 break; 565 case nss_lt_id: 566 gid = va_arg(ap, gid_t); 567 break; 568 case nss_lt_all: 569 break; 570 } 571 grp = va_arg(ap, struct group *); 572 buffer = va_arg(ap, char *); 573 bufsize = va_arg(ap, size_t); 574 errnop = va_arg(ap, int *); 575 *errnop = dns_getstate(&st); 576 if (*errnop != 0) 577 return (NS_UNAVAIL); 578 if (hesiod_init(&ctx) != 0) { 579 *errnop = errno; 580 rv = NS_UNAVAIL; 581 goto fin; 582 } 583 do { 584 rv = NS_NOTFOUND; 585 switch (how) { 586 case nss_lt_name: 587 label = name; 588 break; 589 case nss_lt_id: 590 if (snprintf(buf, sizeof(buf), "%lu", 591 (unsigned long)gid) >= sizeof(buf)) 592 goto fin; 593 label = buf; 594 break; 595 case nss_lt_all: 596 if (st->counter < 0) 597 goto fin; 598 if (snprintf(buf, sizeof(buf), "group-%ld", 599 st->counter++) >= sizeof(buf)) 600 goto fin; 601 label = buf; 602 break; 603 } 604 hes = hesiod_resolve(ctx, label, 605 how == nss_lt_id ? "gid" : "group"); 606 if ((how == nss_lt_id && hes == NULL && 607 (hes = hesiod_resolve(ctx, buf, "group")) == NULL) || 608 hes == NULL) { 609 if (how == nss_lt_all) 610 st->counter = -1; 611 if (errno != ENOENT) 612 *errnop = errno; 613 goto fin; 614 } 615 rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid); 616 if (rv != NS_SUCCESS) { 617 hesiod_free_list(ctx, hes); 618 hes = NULL; 619 continue; 620 } 621 /* We need room at least for the line, a string NUL 622 * terminator, alignment padding, and one (char *) 623 * pointer for the member list terminator. 624 */ 625 adjsize = bufsize - _ALIGNBYTES - sizeof(char *); 626 linesize = strlcpy(buffer, hes[0], adjsize); 627 if (linesize >= adjsize) { 628 *errnop = ERANGE; 629 rv = NS_RETURN; 630 goto fin; 631 } 632 hesiod_free_list(ctx, hes); 633 hes = NULL; 634 rv = __gr_parse_entry(buffer, linesize, grp, 635 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 636 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 637 fin: 638 if (hes != NULL) 639 hesiod_free_list(ctx, hes); 640 if (ctx != NULL) 641 hesiod_end(ctx); 642 if (rv == NS_SUCCESS && retval != NULL) 643 *(struct group **)retval = grp; 644 return (rv); 645 } 646 #endif /* HESIOD */ 647 648 649 #ifdef YP 650 /* 651 * nis backend 652 */ 653 static void 654 nis_endstate(void *p) 655 { 656 657 if (p == NULL) 658 return; 659 free(((struct nis_state *)p)->key); 660 free(p); 661 } 662 663 664 static int 665 nis_setgrent(void *retval, void *cb_data, va_list ap) 666 { 667 struct nis_state *st; 668 int rv; 669 670 rv = nis_getstate(&st); 671 if (rv != 0) 672 return (NS_UNAVAIL); 673 st->done = 0; 674 free(st->key); 675 st->key = NULL; 676 return (NS_UNAVAIL); 677 } 678 679 680 static int 681 nis_group(void *retval, void *mdata, va_list ap) 682 { 683 char *map; 684 struct nis_state *st; 685 struct group *grp; 686 const char *name; 687 char *buffer, *key, *result; 688 size_t bufsize; 689 gid_t gid; 690 enum nss_lookup_type how; 691 int *errnop, keylen, resultlen, rv; 692 693 name = NULL; 694 gid = (gid_t)-1; 695 how = (enum nss_lookup_type)mdata; 696 switch (how) { 697 case nss_lt_name: 698 name = va_arg(ap, const char *); 699 map = "group.byname"; 700 break; 701 case nss_lt_id: 702 gid = va_arg(ap, gid_t); 703 map = "group.bygid"; 704 break; 705 case nss_lt_all: 706 map = "group.byname"; 707 break; 708 } 709 grp = va_arg(ap, struct group *); 710 buffer = va_arg(ap, char *); 711 bufsize = va_arg(ap, size_t); 712 errnop = va_arg(ap, int *); 713 *errnop = nis_getstate(&st); 714 if (*errnop != 0) 715 return (NS_UNAVAIL); 716 if (st->domain[0] == '\0') { 717 if (getdomainname(st->domain, sizeof(st->domain)) != 0) { 718 *errnop = errno; 719 return (NS_UNAVAIL); 720 } 721 } 722 result = NULL; 723 do { 724 rv = NS_NOTFOUND; 725 switch (how) { 726 case nss_lt_name: 727 if (strlcpy(buffer, name, bufsize) >= bufsize) 728 goto erange; 729 break; 730 case nss_lt_id: 731 if (snprintf(buffer, bufsize, "%lu", 732 (unsigned long)gid) >= bufsize) 733 goto erange; 734 break; 735 case nss_lt_all: 736 if (st->done) 737 goto fin; 738 break; 739 } 740 result = NULL; 741 if (how == nss_lt_all) { 742 if (st->key == NULL) 743 rv = yp_first(st->domain, map, &st->key, 744 &st->keylen, &result, &resultlen); 745 else { 746 key = st->key; 747 keylen = st->keylen; 748 st->key = NULL; 749 rv = yp_next(st->domain, map, key, keylen, 750 &st->key, &st->keylen, &result, 751 &resultlen); 752 free(key); 753 } 754 if (rv != 0) { 755 free(result); 756 free(st->key); 757 st->key = NULL; 758 if (rv == YPERR_NOMORE) { 759 st->done = 1; 760 rv = NS_NOTFOUND; 761 } else 762 rv = NS_UNAVAIL; 763 goto fin; 764 } 765 } else { 766 rv = yp_match(st->domain, map, buffer, strlen(buffer), 767 &result, &resultlen); 768 if (rv == YPERR_KEY) { 769 rv = NS_NOTFOUND; 770 continue; 771 } else if (rv != 0) { 772 free(result); 773 rv = NS_UNAVAIL; 774 continue; 775 } 776 } 777 /* We need room at least for the line, a string NUL 778 * terminator, alignment padding, and one (char *) 779 * pointer for the member list terminator. 780 */ 781 if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) 782 goto erange; 783 memcpy(buffer, result, resultlen); 784 buffer[resultlen] = '\0'; 785 free(result); 786 rv = __gr_match_entry(buffer, resultlen, how, name, gid); 787 if (rv == NS_SUCCESS) 788 rv = __gr_parse_entry(buffer, resultlen, grp, 789 &buffer[resultlen+1], bufsize - resultlen - 1, 790 errnop); 791 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 792 fin: 793 if (rv == NS_SUCCESS && retval != NULL) 794 *(struct group **)retval = grp; 795 return (rv); 796 erange: 797 *errnop = ERANGE; 798 return (NS_RETURN); 799 } 800 #endif /* YP */ 801 802 803 804 /* 805 * compat backend 806 */ 807 static void 808 compat_endstate(void *p) 809 { 810 struct compat_state *st; 811 812 if (p == NULL) 813 return; 814 st = (struct compat_state *)p; 815 free(st->name); 816 if (st->fp != NULL) 817 fclose(st->fp); 818 free(p); 819 } 820 821 822 static int 823 compat_setgrent(void *retval, void *mdata, va_list ap) 824 { 825 static const ns_src compatsrc[] = { 826 #ifdef YP 827 { NSSRC_NIS, NS_SUCCESS }, 828 #endif 829 { NULL, 0 } 830 }; 831 ns_dtab dtab[] = { 832 #ifdef HESIOD 833 { NSSRC_DNS, dns_setgrent, NULL }, 834 #endif 835 #ifdef YP 836 { NSSRC_NIS, nis_setgrent, NULL }, 837 #endif 838 { NULL, NULL, NULL } 839 }; 840 struct compat_state *st; 841 int rv, stayopen; 842 843 #define set_setent(x, y) do { \ 844 int i; \ 845 \ 846 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 847 x[i].mdata = (void *)y; \ 848 } while (0) 849 850 rv = compat_getstate(&st); 851 if (rv != 0) 852 return (NS_UNAVAIL); 853 switch ((enum constants)mdata) { 854 case SETGRENT: 855 stayopen = va_arg(ap, int); 856 if (st->fp != NULL) 857 rewind(st->fp); 858 else if (stayopen) 859 st->fp = fopen(_PATH_GROUP, "r"); 860 set_setent(dtab, mdata); 861 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent", 862 compatsrc, 0); 863 break; 864 case ENDGRENT: 865 if (st->fp != NULL) { 866 fclose(st->fp); 867 st->fp = NULL; 868 } 869 set_setent(dtab, mdata); 870 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent", 871 compatsrc, 0); 872 break; 873 default: 874 break; 875 } 876 st->compat = COMPAT_MODE_OFF; 877 free(st->name); 878 st->name = NULL; 879 return (NS_UNAVAIL); 880 #undef set_setent 881 } 882 883 884 static int 885 compat_group(void *retval, void *mdata, va_list ap) 886 { 887 static const ns_src compatsrc[] = { 888 #ifdef YP 889 { NSSRC_NIS, NS_SUCCESS }, 890 #endif 891 { NULL, 0 } 892 }; 893 ns_dtab dtab[] = { 894 #ifdef YP 895 { NSSRC_NIS, nis_group, NULL }, 896 #endif 897 #ifdef HESIOD 898 { NSSRC_DNS, dns_group, NULL }, 899 #endif 900 { NULL, NULL, NULL } 901 }; 902 struct compat_state *st; 903 enum nss_lookup_type how; 904 const char *name, *line; 905 struct group *grp; 906 gid_t gid; 907 char *buffer, *p; 908 void *discard; 909 size_t bufsize, linesize; 910 int rv, stayopen, *errnop; 911 912 #define set_lookup_type(x, y) do { \ 913 int i; \ 914 \ 915 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 916 x[i].mdata = (void *)y; \ 917 } while (0) 918 919 name = NULL; 920 gid = (gid_t)-1; 921 how = (enum nss_lookup_type)mdata; 922 switch (how) { 923 case nss_lt_name: 924 name = va_arg(ap, const char *); 925 break; 926 case nss_lt_id: 927 gid = va_arg(ap, gid_t); 928 break; 929 case nss_lt_all: 930 break; 931 default: 932 return (NS_NOTFOUND); 933 } 934 grp = va_arg(ap, struct group *); 935 buffer = va_arg(ap, char *); 936 bufsize = va_arg(ap, size_t); 937 errnop = va_arg(ap, int *); 938 *errnop = compat_getstate(&st); 939 if (*errnop != 0) 940 return (NS_UNAVAIL); 941 if (st->fp == NULL && 942 ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { 943 *errnop = errno; 944 rv = NS_UNAVAIL; 945 goto fin; 946 } 947 if (how == nss_lt_all) 948 stayopen = 1; 949 else { 950 rewind(st->fp); 951 stayopen = st->stayopen; 952 } 953 docompat: 954 switch (st->compat) { 955 case COMPAT_MODE_ALL: 956 set_lookup_type(dtab, how); 957 switch (how) { 958 case nss_lt_all: 959 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 960 "getgrent_r", compatsrc, grp, buffer, bufsize, 961 errnop); 962 break; 963 case nss_lt_id: 964 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 965 "getgrgid_r", compatsrc, gid, grp, buffer, bufsize, 966 errnop); 967 break; 968 case nss_lt_name: 969 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 970 "getgrnam_r", compatsrc, name, grp, buffer, 971 bufsize, errnop); 972 break; 973 } 974 if (rv & NS_TERMINATE) 975 goto fin; 976 st->compat = COMPAT_MODE_OFF; 977 break; 978 case COMPAT_MODE_NAME: 979 set_lookup_type(dtab, nss_lt_name); 980 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 981 "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize, 982 errnop); 983 switch (rv) { 984 case NS_SUCCESS: 985 switch (how) { 986 case nss_lt_name: 987 if (strcmp(name, grp->gr_name) != 0) 988 rv = NS_NOTFOUND; 989 break; 990 case nss_lt_id: 991 if (gid != grp->gr_gid) 992 rv = NS_NOTFOUND; 993 break; 994 default: 995 break; 996 } 997 break; 998 case NS_RETURN: 999 goto fin; 1000 default: 1001 break; 1002 } 1003 free(st->name); 1004 st->name = NULL; 1005 st->compat = COMPAT_MODE_OFF; 1006 if (rv == NS_SUCCESS) 1007 goto fin; 1008 break; 1009 default: 1010 break; 1011 } 1012 rv = NS_NOTFOUND; 1013 while ((line = fgetln(st->fp, &linesize)) != NULL) { 1014 if (line[linesize-1] == '\n') 1015 linesize--; 1016 if (linesize > 2 && line[0] == '+') { 1017 p = memchr(&line[1], ':', linesize); 1018 if (p == NULL || p == &line[1]) 1019 st->compat = COMPAT_MODE_ALL; 1020 else { 1021 st->name = malloc(p - line); 1022 if (st->name == NULL) { 1023 syslog(LOG_ERR, 1024 "getgrent memory allocation failure"); 1025 *errnop = ENOMEM; 1026 rv = NS_UNAVAIL; 1027 break; 1028 } 1029 memcpy(st->name, &line[1], p - line - 1); 1030 st->name[p - line - 1] = '\0'; 1031 st->compat = COMPAT_MODE_NAME; 1032 } 1033 goto docompat; 1034 } 1035 rv = __gr_match_entry(line, linesize, how, name, gid); 1036 if (rv != NS_SUCCESS) 1037 continue; 1038 /* We need room at least for the line, a string NUL 1039 * terminator, alignment padding, and one (char *) 1040 * pointer for the member list terminator. 1041 */ 1042 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 1043 *errnop = ERANGE; 1044 rv = NS_RETURN; 1045 break; 1046 } 1047 memcpy(buffer, line, linesize); 1048 buffer[linesize] = '\0'; 1049 rv = __gr_parse_entry(buffer, linesize, grp, 1050 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 1051 if (rv & NS_TERMINATE) 1052 break; 1053 } 1054 fin: 1055 if (!stayopen && st->fp != NULL) { 1056 fclose(st->fp); 1057 st->fp = NULL; 1058 } 1059 if (rv == NS_SUCCESS && retval != NULL) 1060 *(struct group **)retval = grp; 1061 return (rv); 1062 #undef set_lookup_type 1063 } 1064 1065 1066 /* 1067 * common group line matching and parsing 1068 */ 1069 int 1070 __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how, 1071 const char *name, gid_t gid) 1072 { 1073 size_t namesize; 1074 const char *p, *eol; 1075 char *q; 1076 unsigned long n; 1077 int i, needed; 1078 1079 if (linesize == 0 || is_comment_line(line, linesize)) 1080 return (NS_NOTFOUND); 1081 switch (how) { 1082 case nss_lt_name: needed = 1; break; 1083 case nss_lt_id: needed = 2; break; 1084 default: needed = 2; break; 1085 } 1086 eol = &line[linesize]; 1087 for (p = line, i = 0; i < needed && p < eol; p++) 1088 if (*p == ':') 1089 i++; 1090 if (i < needed) 1091 return (NS_NOTFOUND); 1092 switch (how) { 1093 case nss_lt_name: 1094 namesize = strlen(name); 1095 if (namesize + 1 == (size_t)(p - line) && 1096 memcmp(line, name, namesize) == 0) 1097 return (NS_SUCCESS); 1098 break; 1099 case nss_lt_id: 1100 n = strtoul(p, &q, 10); 1101 if (q < eol && *q == ':' && gid == (gid_t)n) 1102 return (NS_SUCCESS); 1103 break; 1104 case nss_lt_all: 1105 return (NS_SUCCESS); 1106 default: 1107 break; 1108 } 1109 return (NS_NOTFOUND); 1110 } 1111 1112 1113 int 1114 __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, 1115 size_t membufsize, int *errnop) 1116 { 1117 char *s_gid, *s_mem, *p, **members; 1118 unsigned long n; 1119 int maxmembers; 1120 1121 memset(grp, 0, sizeof(*grp)); 1122 members = (char **)_ALIGN(membuf); 1123 membufsize -= (char *)members - membuf; 1124 maxmembers = membufsize / sizeof(*members); 1125 if (maxmembers <= 0 || 1126 (grp->gr_name = strsep(&line, ":")) == NULL || 1127 grp->gr_name[0] == '\0' || 1128 (grp->gr_passwd = strsep(&line, ":")) == NULL || 1129 (s_gid = strsep(&line, ":")) == NULL || 1130 s_gid[0] == '\0') 1131 return (NS_NOTFOUND); 1132 s_mem = line; 1133 n = strtoul(s_gid, &s_gid, 10); 1134 if (s_gid[0] != '\0') 1135 return (NS_NOTFOUND); 1136 grp->gr_gid = (gid_t)n; 1137 grp->gr_mem = members; 1138 while (maxmembers > 1 && s_mem != NULL) { 1139 p = strsep(&s_mem, ","); 1140 if (p != NULL && *p != '\0') { 1141 *members++ = p; 1142 maxmembers--; 1143 } 1144 } 1145 *members = NULL; 1146 if (s_mem == NULL) 1147 return (NS_SUCCESS); 1148 else { 1149 *errnop = ERANGE; 1150 return (NS_RETURN); 1151 } 1152 } 1153