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 return (NULL); 311 } 312 grp_storage_size <<= 1; 313 grp_storage = malloc(grp_storage_size); 314 if (grp_storage == NULL) 315 return (NULL); 316 } 317 } while (res == NULL && rv == ERANGE); 318 return (res); 319 } 320 321 322 static int 323 wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, 324 struct group **res) 325 { 326 return (getgrnam_r(key.name, grp, buffer, bufsize, res)); 327 } 328 329 330 static int 331 wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, 332 struct group **res) 333 { 334 return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); 335 } 336 337 338 static int 339 wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, 340 size_t bufsize, struct group **res) 341 { 342 return (getgrent_r(grp, buffer, bufsize, res)); 343 } 344 345 346 struct group * 347 getgrnam(const char *name) 348 { 349 union key key; 350 351 key.name = name; 352 return (getgr(wrap_getgrnam_r, key)); 353 } 354 355 356 struct group * 357 getgrgid(gid_t gid) 358 { 359 union key key; 360 361 key.gid = gid; 362 return (getgr(wrap_getgrgid_r, key)); 363 } 364 365 366 struct group * 367 getgrent(void) 368 { 369 union key key; 370 371 key.gid = 0; /* not used */ 372 return (getgr(wrap_getgrent_r, key)); 373 } 374 375 376 static int 377 is_comment_line(const char *s, size_t n) 378 { 379 const char *eom; 380 381 eom = &s[n]; 382 383 for (; s < eom; s++) 384 if (*s == '#' || !isspace((unsigned char)*s)) 385 break; 386 return (*s == '#' || s == eom); 387 } 388 389 390 /* 391 * files backend 392 */ 393 static void 394 files_endstate(void *p) 395 { 396 397 if (p == NULL) 398 return; 399 if (((struct files_state *)p)->fp != NULL) 400 fclose(((struct files_state *)p)->fp); 401 free(p); 402 } 403 404 405 static int 406 files_setgrent(void *retval, void *mdata, va_list ap) 407 { 408 struct files_state *st; 409 int rv, stayopen; 410 411 rv = files_getstate(&st); 412 if (rv != 0) 413 return (NS_UNAVAIL); 414 switch ((enum constants)mdata) { 415 case SETGRENT: 416 stayopen = va_arg(ap, int); 417 if (st->fp != NULL) 418 rewind(st->fp); 419 else if (stayopen) 420 st->fp = fopen(_PATH_GROUP, "r"); 421 break; 422 case ENDGRENT: 423 if (st->fp != NULL) { 424 fclose(st->fp); 425 st->fp = NULL; 426 } 427 break; 428 default: 429 break; 430 } 431 return (NS_UNAVAIL); 432 } 433 434 435 static int 436 files_group(void *retval, void *mdata, va_list ap) 437 { 438 struct files_state *st; 439 enum nss_lookup_type how; 440 const char *name, *line; 441 struct group *grp; 442 gid_t gid; 443 char *buffer; 444 size_t bufsize, linesize; 445 int rv, stayopen, *errnop; 446 447 name = NULL; 448 gid = (gid_t)-1; 449 how = (enum nss_lookup_type)mdata; 450 switch (how) { 451 case nss_lt_name: 452 name = va_arg(ap, const char *); 453 break; 454 case nss_lt_id: 455 gid = va_arg(ap, gid_t); 456 break; 457 case nss_lt_all: 458 break; 459 default: 460 return (NS_NOTFOUND); 461 } 462 grp = va_arg(ap, struct group *); 463 buffer = va_arg(ap, char *); 464 bufsize = va_arg(ap, size_t); 465 errnop = va_arg(ap, int *); 466 *errnop = files_getstate(&st); 467 if (*errnop != 0) 468 return (NS_UNAVAIL); 469 if (st->fp == NULL && 470 ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { 471 *errnop = errno; 472 return (NS_UNAVAIL); 473 } 474 if (how == nss_lt_all) 475 stayopen = 1; 476 else { 477 rewind(st->fp); 478 stayopen = st->stayopen; 479 } 480 rv = NS_NOTFOUND; 481 while ((line = fgetln(st->fp, &linesize)) != NULL) { 482 if (line[linesize-1] == '\n') 483 linesize--; 484 rv = __gr_match_entry(line, linesize, how, name, gid); 485 if (rv != NS_SUCCESS) 486 continue; 487 /* We need room at least for the line, a string NUL 488 * terminator, alignment padding, and one (char *) 489 * pointer for the member list terminator. 490 */ 491 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 492 *errnop = ERANGE; 493 rv = NS_RETURN; 494 break; 495 } 496 memcpy(buffer, line, linesize); 497 buffer[linesize] = '\0'; 498 rv = __gr_parse_entry(buffer, linesize, grp, 499 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 500 if (rv & NS_TERMINATE) 501 break; 502 } 503 if (!stayopen && st->fp != NULL) { 504 fclose(st->fp); 505 st->fp = NULL; 506 } 507 if (rv == NS_SUCCESS && retval != NULL) 508 *(struct group **)retval = grp; 509 return (rv); 510 } 511 512 513 #ifdef HESIOD 514 /* 515 * dns backend 516 */ 517 static void 518 dns_endstate(void *p) 519 { 520 521 free(p); 522 } 523 524 525 static int 526 dns_setgrent(void *retval, void *cb_data, va_list ap) 527 { 528 struct dns_state *st; 529 int rv; 530 531 rv = dns_getstate(&st); 532 if (rv != 0) 533 return (NS_UNAVAIL); 534 st->counter = 0; 535 return (NS_UNAVAIL); 536 } 537 538 539 static int 540 dns_group(void *retval, void *mdata, va_list ap) 541 { 542 char buf[HESIOD_NAME_MAX]; 543 struct dns_state *st; 544 struct group *grp; 545 const char *name, *label; 546 void *ctx; 547 char *buffer, **hes; 548 size_t bufsize, adjsize, linesize; 549 gid_t gid; 550 enum nss_lookup_type how; 551 int rv, *errnop; 552 553 ctx = NULL; 554 hes = NULL; 555 name = NULL; 556 gid = (gid_t)-1; 557 how = (enum nss_lookup_type)mdata; 558 switch (how) { 559 case nss_lt_name: 560 name = va_arg(ap, const char *); 561 break; 562 case nss_lt_id: 563 gid = va_arg(ap, gid_t); 564 break; 565 case nss_lt_all: 566 break; 567 } 568 grp = va_arg(ap, struct group *); 569 buffer = va_arg(ap, char *); 570 bufsize = va_arg(ap, size_t); 571 errnop = va_arg(ap, int *); 572 *errnop = dns_getstate(&st); 573 if (*errnop != 0) 574 return (NS_UNAVAIL); 575 if (hesiod_init(&ctx) != 0) { 576 *errnop = errno; 577 rv = NS_UNAVAIL; 578 goto fin; 579 } 580 do { 581 rv = NS_NOTFOUND; 582 switch (how) { 583 case nss_lt_name: 584 label = name; 585 break; 586 case nss_lt_id: 587 if (snprintf(buf, sizeof(buf), "%lu", 588 (unsigned long)gid) >= sizeof(buf)) 589 goto fin; 590 label = buf; 591 break; 592 case nss_lt_all: 593 if (st->counter < 0) 594 goto fin; 595 if (snprintf(buf, sizeof(buf), "group-%ld", 596 st->counter++) >= sizeof(buf)) 597 goto fin; 598 label = buf; 599 break; 600 } 601 hes = hesiod_resolve(ctx, label, 602 how == nss_lt_id ? "gid" : "group"); 603 if ((how == nss_lt_id && hes == NULL && 604 (hes = hesiod_resolve(ctx, buf, "group")) == NULL) || 605 hes == NULL) { 606 if (how == nss_lt_all) 607 st->counter = -1; 608 if (errno != ENOENT) 609 *errnop = errno; 610 goto fin; 611 } 612 rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid); 613 if (rv != NS_SUCCESS) { 614 hesiod_free_list(ctx, hes); 615 hes = NULL; 616 continue; 617 } 618 /* We need room at least for the line, a string NUL 619 * terminator, alignment padding, and one (char *) 620 * pointer for the member list terminator. 621 */ 622 adjsize = bufsize - _ALIGNBYTES - sizeof(char *); 623 linesize = strlcpy(buffer, hes[0], adjsize); 624 if (linesize >= adjsize) { 625 *errnop = ERANGE; 626 rv = NS_RETURN; 627 goto fin; 628 } 629 hesiod_free_list(ctx, hes); 630 hes = NULL; 631 rv = __gr_parse_entry(buffer, linesize, grp, 632 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 633 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 634 fin: 635 if (hes != NULL) 636 hesiod_free_list(ctx, hes); 637 if (ctx != NULL) 638 hesiod_end(ctx); 639 if (rv == NS_SUCCESS && retval != NULL) 640 *(struct group **)retval = grp; 641 return (rv); 642 } 643 #endif /* HESIOD */ 644 645 646 #ifdef YP 647 /* 648 * nis backend 649 */ 650 static void 651 nis_endstate(void *p) 652 { 653 654 if (p == NULL) 655 return; 656 free(((struct nis_state *)p)->key); 657 free(p); 658 } 659 660 661 static int 662 nis_setgrent(void *retval, void *cb_data, va_list ap) 663 { 664 struct nis_state *st; 665 int rv; 666 667 rv = nis_getstate(&st); 668 if (rv != 0) 669 return (NS_UNAVAIL); 670 st->done = 0; 671 free(st->key); 672 st->key = NULL; 673 return (NS_UNAVAIL); 674 } 675 676 677 static int 678 nis_group(void *retval, void *mdata, va_list ap) 679 { 680 char *map; 681 struct nis_state *st; 682 struct group *grp; 683 const char *name; 684 char *buffer, *key, *result; 685 size_t bufsize; 686 gid_t gid; 687 enum nss_lookup_type how; 688 int *errnop, keylen, resultlen, rv; 689 690 name = NULL; 691 gid = (gid_t)-1; 692 how = (enum nss_lookup_type)mdata; 693 switch (how) { 694 case nss_lt_name: 695 name = va_arg(ap, const char *); 696 map = "group.byname"; 697 break; 698 case nss_lt_id: 699 gid = va_arg(ap, gid_t); 700 map = "group.bygid"; 701 break; 702 case nss_lt_all: 703 map = "group.byname"; 704 break; 705 } 706 grp = va_arg(ap, struct group *); 707 buffer = va_arg(ap, char *); 708 bufsize = va_arg(ap, size_t); 709 errnop = va_arg(ap, int *); 710 *errnop = nis_getstate(&st); 711 if (*errnop != 0) 712 return (NS_UNAVAIL); 713 if (st->domain[0] == '\0') { 714 if (getdomainname(st->domain, sizeof(st->domain)) != 0) { 715 *errnop = errno; 716 return (NS_UNAVAIL); 717 } 718 } 719 result = NULL; 720 do { 721 rv = NS_NOTFOUND; 722 switch (how) { 723 case nss_lt_name: 724 if (strlcpy(buffer, name, bufsize) >= bufsize) 725 goto erange; 726 break; 727 case nss_lt_id: 728 if (snprintf(buffer, bufsize, "%lu", 729 (unsigned long)gid) >= bufsize) 730 goto erange; 731 break; 732 case nss_lt_all: 733 if (st->done) 734 goto fin; 735 break; 736 } 737 result = NULL; 738 if (how == nss_lt_all) { 739 if (st->key == NULL) 740 rv = yp_first(st->domain, map, &st->key, 741 &st->keylen, &result, &resultlen); 742 else { 743 key = st->key; 744 keylen = st->keylen; 745 st->key = NULL; 746 rv = yp_next(st->domain, map, key, keylen, 747 &st->key, &st->keylen, &result, 748 &resultlen); 749 free(key); 750 } 751 if (rv != 0) { 752 free(result); 753 free(st->key); 754 st->key = NULL; 755 if (rv == YPERR_NOMORE) { 756 st->done = 1; 757 rv = NS_NOTFOUND; 758 } else 759 rv = NS_UNAVAIL; 760 goto fin; 761 } 762 } else { 763 rv = yp_match(st->domain, map, buffer, strlen(buffer), 764 &result, &resultlen); 765 if (rv == YPERR_KEY) { 766 rv = NS_NOTFOUND; 767 continue; 768 } else if (rv != 0) { 769 free(result); 770 rv = NS_UNAVAIL; 771 continue; 772 } 773 } 774 /* We need room at least for the line, a string NUL 775 * terminator, alignment padding, and one (char *) 776 * pointer for the member list terminator. 777 */ 778 if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) 779 goto erange; 780 memcpy(buffer, result, resultlen); 781 buffer[resultlen] = '\0'; 782 free(result); 783 rv = __gr_match_entry(buffer, resultlen, how, name, gid); 784 if (rv == NS_SUCCESS) 785 rv = __gr_parse_entry(buffer, resultlen, grp, 786 &buffer[resultlen+1], bufsize - resultlen - 1, 787 errnop); 788 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 789 fin: 790 if (rv == NS_SUCCESS && retval != NULL) 791 *(struct group **)retval = grp; 792 return (rv); 793 erange: 794 *errnop = ERANGE; 795 return (NS_RETURN); 796 } 797 #endif /* YP */ 798 799 800 801 /* 802 * compat backend 803 */ 804 static void 805 compat_endstate(void *p) 806 { 807 struct compat_state *st; 808 809 if (p == NULL) 810 return; 811 st = (struct compat_state *)p; 812 free(st->name); 813 if (st->fp != NULL) 814 fclose(st->fp); 815 free(p); 816 } 817 818 819 static int 820 compat_setgrent(void *retval, void *mdata, va_list ap) 821 { 822 static const ns_src compatsrc[] = { 823 #ifdef YP 824 { NSSRC_NIS, NS_SUCCESS }, 825 #endif 826 { NULL, 0 } 827 }; 828 ns_dtab dtab[] = { 829 #ifdef HESIOD 830 { NSSRC_DNS, dns_setgrent, NULL }, 831 #endif 832 #ifdef YP 833 { NSSRC_NIS, nis_setgrent, NULL }, 834 #endif 835 { NULL, NULL, NULL } 836 }; 837 struct compat_state *st; 838 int rv, stayopen; 839 840 #define set_setent(x, y) do { \ 841 int i; \ 842 \ 843 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 844 x[i].mdata = (void *)y; \ 845 } while (0) 846 847 rv = compat_getstate(&st); 848 if (rv != 0) 849 return (NS_UNAVAIL); 850 switch ((enum constants)mdata) { 851 case SETGRENT: 852 stayopen = va_arg(ap, int); 853 if (st->fp != NULL) 854 rewind(st->fp); 855 else if (stayopen) 856 st->fp = fopen(_PATH_GROUP, "r"); 857 set_setent(dtab, mdata); 858 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent", 859 compatsrc, 0); 860 break; 861 case ENDGRENT: 862 if (st->fp != NULL) { 863 fclose(st->fp); 864 st->fp = NULL; 865 } 866 set_setent(dtab, mdata); 867 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent", 868 compatsrc, 0); 869 break; 870 default: 871 break; 872 } 873 st->compat = COMPAT_MODE_OFF; 874 free(st->name); 875 st->name = NULL; 876 return (NS_UNAVAIL); 877 #undef set_setent 878 } 879 880 881 static int 882 compat_group(void *retval, void *mdata, va_list ap) 883 { 884 static const ns_src compatsrc[] = { 885 #ifdef YP 886 { NSSRC_NIS, NS_SUCCESS }, 887 #endif 888 { NULL, 0 } 889 }; 890 ns_dtab dtab[] = { 891 #ifdef YP 892 { NSSRC_NIS, nis_group, NULL }, 893 #endif 894 #ifdef HESIOD 895 { NSSRC_DNS, dns_group, NULL }, 896 #endif 897 { NULL, NULL, NULL } 898 }; 899 struct compat_state *st; 900 enum nss_lookup_type how; 901 const char *name, *line; 902 struct group *grp; 903 gid_t gid; 904 char *buffer, *p; 905 void *discard; 906 size_t bufsize, linesize; 907 int rv, stayopen, *errnop; 908 909 #define set_lookup_type(x, y) do { \ 910 int i; \ 911 \ 912 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 913 x[i].mdata = (void *)y; \ 914 } while (0) 915 916 name = NULL; 917 gid = (gid_t)-1; 918 how = (enum nss_lookup_type)mdata; 919 switch (how) { 920 case nss_lt_name: 921 name = va_arg(ap, const char *); 922 break; 923 case nss_lt_id: 924 gid = va_arg(ap, gid_t); 925 break; 926 case nss_lt_all: 927 break; 928 default: 929 return (NS_NOTFOUND); 930 } 931 grp = va_arg(ap, struct group *); 932 buffer = va_arg(ap, char *); 933 bufsize = va_arg(ap, size_t); 934 errnop = va_arg(ap, int *); 935 *errnop = compat_getstate(&st); 936 if (*errnop != 0) 937 return (NS_UNAVAIL); 938 if (st->fp == NULL && 939 ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { 940 *errnop = errno; 941 rv = NS_UNAVAIL; 942 goto fin; 943 } 944 if (how == nss_lt_all) 945 stayopen = 1; 946 else { 947 rewind(st->fp); 948 stayopen = st->stayopen; 949 } 950 docompat: 951 switch (st->compat) { 952 case COMPAT_MODE_ALL: 953 set_lookup_type(dtab, how); 954 switch (how) { 955 case nss_lt_all: 956 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 957 "getgrent_r", compatsrc, grp, buffer, bufsize, 958 errnop); 959 break; 960 case nss_lt_id: 961 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 962 "getgrgid_r", compatsrc, gid, grp, buffer, bufsize, 963 errnop); 964 break; 965 case nss_lt_name: 966 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 967 "getgrnam_r", compatsrc, name, grp, buffer, 968 bufsize, errnop); 969 break; 970 } 971 if (rv & NS_TERMINATE) 972 goto fin; 973 st->compat = COMPAT_MODE_OFF; 974 break; 975 case COMPAT_MODE_NAME: 976 set_lookup_type(dtab, nss_lt_name); 977 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 978 "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize, 979 errnop); 980 switch (rv) { 981 case NS_SUCCESS: 982 switch (how) { 983 case nss_lt_name: 984 if (strcmp(name, grp->gr_name) != 0) 985 rv = NS_NOTFOUND; 986 break; 987 case nss_lt_id: 988 if (gid != grp->gr_gid) 989 rv = NS_NOTFOUND; 990 break; 991 default: 992 break; 993 } 994 break; 995 case NS_RETURN: 996 goto fin; 997 default: 998 break; 999 } 1000 free(st->name); 1001 st->name = NULL; 1002 st->compat = COMPAT_MODE_OFF; 1003 if (rv == NS_SUCCESS) 1004 goto fin; 1005 break; 1006 default: 1007 break; 1008 } 1009 rv = NS_NOTFOUND; 1010 while ((line = fgetln(st->fp, &linesize)) != NULL) { 1011 if (line[linesize-1] == '\n') 1012 linesize--; 1013 if (linesize > 2 && line[0] == '+') { 1014 p = memchr(&line[1], ':', linesize); 1015 if (p == NULL || p == &line[1]) 1016 st->compat = COMPAT_MODE_ALL; 1017 else { 1018 st->name = malloc(p - line); 1019 if (st->name == NULL) { 1020 syslog(LOG_ERR, 1021 "getgrent memory allocation failure"); 1022 *errnop = ENOMEM; 1023 rv = NS_UNAVAIL; 1024 break; 1025 } 1026 memcpy(st->name, &line[1], p - line - 1); 1027 st->name[p - line - 1] = '\0'; 1028 st->compat = COMPAT_MODE_NAME; 1029 } 1030 goto docompat; 1031 } 1032 rv = __gr_match_entry(line, linesize, how, name, gid); 1033 if (rv != NS_SUCCESS) 1034 continue; 1035 /* We need room at least for the line, a string NUL 1036 * terminator, alignment padding, and one (char *) 1037 * pointer for the member list terminator. 1038 */ 1039 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 1040 *errnop = ERANGE; 1041 rv = NS_RETURN; 1042 break; 1043 } 1044 memcpy(buffer, line, linesize); 1045 buffer[linesize] = '\0'; 1046 rv = __gr_parse_entry(buffer, linesize, grp, 1047 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 1048 if (rv & NS_TERMINATE) 1049 break; 1050 } 1051 fin: 1052 if (!stayopen && st->fp != NULL) { 1053 fclose(st->fp); 1054 st->fp = NULL; 1055 } 1056 if (rv == NS_SUCCESS && retval != NULL) 1057 *(struct group **)retval = grp; 1058 return (rv); 1059 #undef set_lookup_type 1060 } 1061 1062 1063 /* 1064 * common group line matching and parsing 1065 */ 1066 int 1067 __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how, 1068 const char *name, gid_t gid) 1069 { 1070 size_t namesize; 1071 const char *p, *eol; 1072 char *q; 1073 unsigned long n; 1074 int i, needed; 1075 1076 if (linesize == 0 || is_comment_line(line, linesize)) 1077 return (NS_NOTFOUND); 1078 switch (how) { 1079 case nss_lt_name: needed = 1; break; 1080 case nss_lt_id: needed = 2; break; 1081 default: needed = 2; break; 1082 } 1083 eol = &line[linesize]; 1084 for (p = line, i = 0; i < needed && p < eol; p++) 1085 if (*p == ':') 1086 i++; 1087 if (i < needed) 1088 return (NS_NOTFOUND); 1089 switch (how) { 1090 case nss_lt_name: 1091 namesize = strlen(name); 1092 if (namesize + 1 == (size_t)(p - line) && 1093 memcmp(line, name, namesize) == 0) 1094 return (NS_SUCCESS); 1095 break; 1096 case nss_lt_id: 1097 n = strtoul(p, &q, 10); 1098 if (q < eol && *q == ':' && gid == (gid_t)n) 1099 return (NS_SUCCESS); 1100 break; 1101 case nss_lt_all: 1102 return (NS_SUCCESS); 1103 default: 1104 break; 1105 } 1106 return (NS_NOTFOUND); 1107 } 1108 1109 1110 int 1111 __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, 1112 size_t membufsize, int *errnop) 1113 { 1114 char *s_gid, *s_mem, *p, **members; 1115 unsigned long n; 1116 int maxmembers; 1117 1118 memset(grp, 0, sizeof(*grp)); 1119 members = (char **)_ALIGN(membuf); 1120 membufsize -= (char *)members - membuf; 1121 maxmembers = membufsize / sizeof(*members); 1122 if (maxmembers <= 0 || 1123 (grp->gr_name = strsep(&line, ":")) == NULL || 1124 grp->gr_name[0] == '\0' || 1125 (grp->gr_passwd = strsep(&line, ":")) == NULL || 1126 (s_gid = strsep(&line, ":")) == NULL || 1127 s_gid[0] == '\0') 1128 return (NS_NOTFOUND); 1129 s_mem = line; 1130 n = strtoul(s_gid, &s_gid, 10); 1131 if (s_gid[0] != '\0') 1132 return (NS_NOTFOUND); 1133 grp->gr_gid = (gid_t)n; 1134 grp->gr_mem = members; 1135 while (maxmembers > 1 && s_mem != NULL) { 1136 p = strsep(&s_mem, ","); 1137 if (p != NULL && *p != '\0') { 1138 *members++ = p; 1139 maxmembers--; 1140 } 1141 } 1142 *members = NULL; 1143 if (s_mem == NULL) 1144 return (NS_SUCCESS); 1145 else { 1146 *errnop = ERANGE; 1147 return (NS_RETURN); 1148 } 1149 } 1150