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 #ifdef NS_CACHING 61 #include "nscache.h" 62 #endif 63 64 enum constants { 65 GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ 66 GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ 67 SETGRENT = 1, 68 ENDGRENT = 2, 69 HESIOD_NAME_MAX = 256, 70 }; 71 72 static const ns_src defaultsrc[] = { 73 { NSSRC_COMPAT, NS_SUCCESS }, 74 { NULL, 0 } 75 }; 76 77 int __gr_match_entry(const char *, size_t, enum nss_lookup_type, 78 const char *, gid_t); 79 int __gr_parse_entry(char *, size_t, struct group *, char *, size_t, 80 int *); 81 82 static int is_comment_line(const char *, size_t); 83 84 union key { 85 const char *name; 86 gid_t gid; 87 }; 88 static struct group *getgr(int (*)(union key, struct group *, char *, size_t, 89 struct group **), union key); 90 static int wrap_getgrnam_r(union key, struct group *, char *, size_t, 91 struct group **); 92 static int wrap_getgrgid_r(union key, struct group *, char *, size_t, 93 struct group **); 94 static int wrap_getgrent_r(union key, struct group *, char *, size_t, 95 struct group **); 96 97 struct files_state { 98 FILE *fp; 99 int stayopen; 100 }; 101 static void files_endstate(void *); 102 NSS_TLS_HANDLING(files); 103 static int files_setgrent(void *, void *, va_list); 104 static int files_group(void *, void *, va_list); 105 106 107 #ifdef HESIOD 108 struct dns_state { 109 long counter; 110 }; 111 static void dns_endstate(void *); 112 NSS_TLS_HANDLING(dns); 113 static int dns_setgrent(void *, void *, va_list); 114 static int dns_group(void *, void *, va_list); 115 #endif 116 117 118 #ifdef YP 119 struct nis_state { 120 char domain[MAXHOSTNAMELEN]; 121 int done; 122 char *key; 123 int keylen; 124 }; 125 static void nis_endstate(void *); 126 NSS_TLS_HANDLING(nis); 127 static int nis_setgrent(void *, void *, va_list); 128 static int nis_group(void *, void *, va_list); 129 #endif 130 131 struct compat_state { 132 FILE *fp; 133 int stayopen; 134 char *name; 135 enum _compat { 136 COMPAT_MODE_OFF = 0, 137 COMPAT_MODE_ALL, 138 COMPAT_MODE_NAME 139 } compat; 140 }; 141 static void compat_endstate(void *); 142 NSS_TLS_HANDLING(compat); 143 static int compat_setgrent(void *, void *, va_list); 144 static int compat_group(void *, void *, va_list); 145 146 #ifdef NS_CACHING 147 static int grp_id_func(char *, size_t *, va_list, void *); 148 static int grp_marshal_func(char *, size_t *, void *, va_list, void *); 149 static int grp_unmarshal_func(char *, size_t, void *, va_list, void *); 150 151 static int 152 grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) 153 { 154 char *name; 155 gid_t gid; 156 157 size_t desired_size, size; 158 int res = NS_UNAVAIL; 159 enum nss_lookup_type lookup_type; 160 161 162 lookup_type = (enum nss_lookup_type)cache_mdata; 163 switch (lookup_type) { 164 case nss_lt_name: 165 name = va_arg(ap, char *); 166 size = strlen(name); 167 desired_size = sizeof(enum nss_lookup_type) + size + 1; 168 if (desired_size > *buffer_size) { 169 res = NS_RETURN; 170 goto fin; 171 } 172 173 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); 174 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); 175 176 res = NS_SUCCESS; 177 break; 178 case nss_lt_id: 179 gid = va_arg(ap, gid_t); 180 desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t); 181 if (desired_size > *buffer_size) { 182 res = NS_RETURN; 183 goto fin; 184 } 185 186 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); 187 memcpy(buffer + sizeof(enum nss_lookup_type), &gid, 188 sizeof(gid_t)); 189 190 res = NS_SUCCESS; 191 break; 192 default: 193 /* should be unreachable */ 194 return (NS_UNAVAIL); 195 } 196 197 fin: 198 *buffer_size = desired_size; 199 return (res); 200 } 201 202 static int 203 grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, 204 void *cache_mdata) 205 { 206 char *name; 207 gid_t gid; 208 struct group *grp; 209 char *orig_buf; 210 size_t orig_buf_size; 211 212 struct group new_grp; 213 size_t desired_size, size, mem_size; 214 char *p, **mem; 215 216 switch ((enum nss_lookup_type)cache_mdata) { 217 case nss_lt_name: 218 name = va_arg(ap, char *); 219 break; 220 case nss_lt_id: 221 gid = va_arg(ap, gid_t); 222 break; 223 case nss_lt_all: 224 break; 225 default: 226 /* should be unreachable */ 227 return (NS_UNAVAIL); 228 } 229 230 grp = va_arg(ap, struct group *); 231 orig_buf = va_arg(ap, char *); 232 orig_buf_size = va_arg(ap, size_t); 233 234 desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *); 235 236 if (grp->gr_name != NULL) 237 desired_size += strlen(grp->gr_name) + 1; 238 if (grp->gr_passwd != NULL) 239 desired_size += strlen(grp->gr_passwd) + 1; 240 241 if (grp->gr_mem != NULL) { 242 mem_size = 0; 243 for (mem = grp->gr_mem; *mem; ++mem) { 244 desired_size += strlen(*mem) + 1; 245 ++mem_size; 246 } 247 248 desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *); 249 } 250 251 if (desired_size > *buffer_size) { 252 /* this assignment is here for future use */ 253 *buffer_size = desired_size; 254 return (NS_RETURN); 255 } 256 257 memcpy(&new_grp, grp, sizeof(struct group)); 258 memset(buffer, 0, desired_size); 259 260 *buffer_size = desired_size; 261 p = buffer + sizeof(struct group) + sizeof(char *); 262 memcpy(buffer + sizeof(struct group), &p, sizeof(char *)); 263 p = (char *)_ALIGN(p); 264 265 if (new_grp.gr_name != NULL) { 266 size = strlen(new_grp.gr_name); 267 memcpy(p, new_grp.gr_name, size); 268 new_grp.gr_name = p; 269 p += size + 1; 270 } 271 272 if (new_grp.gr_passwd != NULL) { 273 size = strlen(new_grp.gr_passwd); 274 memcpy(p, new_grp.gr_passwd, size); 275 new_grp.gr_passwd = p; 276 p += size + 1; 277 } 278 279 if (new_grp.gr_mem != NULL) { 280 p = (char *)_ALIGN(p); 281 memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size); 282 new_grp.gr_mem = (char **)p; 283 p += sizeof(char *) * (mem_size + 1); 284 285 for (mem = new_grp.gr_mem; *mem; ++mem) { 286 size = strlen(*mem); 287 memcpy(p, *mem, size); 288 *mem = p; 289 p += size + 1; 290 } 291 } 292 293 memcpy(buffer, &new_grp, sizeof(struct group)); 294 return (NS_SUCCESS); 295 } 296 297 static int 298 grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, 299 void *cache_mdata) 300 { 301 char *name; 302 gid_t gid; 303 struct group *grp; 304 char *orig_buf; 305 size_t orig_buf_size; 306 int *ret_errno; 307 308 char *p; 309 char **mem; 310 311 switch ((enum nss_lookup_type)cache_mdata) { 312 case nss_lt_name: 313 name = va_arg(ap, char *); 314 break; 315 case nss_lt_id: 316 gid = va_arg(ap, gid_t); 317 break; 318 case nss_lt_all: 319 break; 320 default: 321 /* should be unreachable */ 322 return (NS_UNAVAIL); 323 } 324 325 grp = va_arg(ap, struct group *); 326 orig_buf = va_arg(ap, char *); 327 orig_buf_size = va_arg(ap, size_t); 328 ret_errno = va_arg(ap, int *); 329 330 if (orig_buf_size < 331 buffer_size - sizeof(struct group) - sizeof(char *)) { 332 *ret_errno = ERANGE; 333 return (NS_RETURN); 334 } 335 336 memcpy(grp, buffer, sizeof(struct group)); 337 memcpy(&p, buffer + sizeof(struct group), sizeof(char *)); 338 339 orig_buf = (char *)_ALIGN(orig_buf); 340 memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) + 341 _ALIGN(p) - (size_t)p, 342 buffer_size - sizeof(struct group) - sizeof(char *) - 343 _ALIGN(p) + (size_t)p); 344 p = (char *)_ALIGN(p); 345 346 NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *); 347 NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *); 348 if (grp->gr_mem != NULL) { 349 NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **); 350 351 for (mem = grp->gr_mem; *mem; ++mem) 352 NS_APPLY_OFFSET(*mem, orig_buf, p, char *); 353 } 354 355 if (retval != NULL) 356 *((struct group **)retval) = grp; 357 358 return (NS_SUCCESS); 359 } 360 361 NSS_MP_CACHE_HANDLING(group); 362 #endif /* NS_CACHING */ 363 364 365 /* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ 366 int 367 setgrent(void) 368 { 369 #ifdef NS_CACHING 370 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 371 group, (void *)nss_lt_all, 372 NULL, NULL); 373 #endif 374 375 static const ns_dtab dtab[] = { 376 { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, 377 #ifdef HESIOD 378 { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, 379 #endif 380 #ifdef YP 381 { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, 382 #endif 383 { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, 384 #ifdef NS_CACHING 385 NS_CACHE_CB(&cache_info) 386 #endif 387 { NULL, NULL, NULL } 388 }; 389 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); 390 return (1); 391 } 392 393 394 int 395 setgroupent(int stayopen) 396 { 397 #ifdef NS_CACHING 398 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 399 group, (void *)nss_lt_all, 400 NULL, NULL); 401 #endif 402 403 static const ns_dtab dtab[] = { 404 { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, 405 #ifdef HESIOD 406 { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, 407 #endif 408 #ifdef YP 409 { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, 410 #endif 411 { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, 412 #ifdef NS_CACHING 413 NS_CACHE_CB(&cache_info) 414 #endif 415 { NULL, NULL, NULL } 416 }; 417 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 418 stayopen); 419 return (1); 420 } 421 422 423 void 424 endgrent(void) 425 { 426 #ifdef NS_CACHING 427 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 428 group, (void *)nss_lt_all, 429 NULL, NULL); 430 #endif 431 432 static const ns_dtab dtab[] = { 433 { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, 434 #ifdef HESIOD 435 { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, 436 #endif 437 #ifdef YP 438 { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, 439 #endif 440 { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, 441 #ifdef NS_CACHING 442 NS_CACHE_CB(&cache_info) 443 #endif 444 { NULL, NULL, NULL } 445 }; 446 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc); 447 } 448 449 450 int 451 getgrent_r(struct group *grp, char *buffer, size_t bufsize, 452 struct group **result) 453 { 454 #ifdef NS_CACHING 455 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 456 group, (void *)nss_lt_all, 457 grp_marshal_func, grp_unmarshal_func); 458 #endif 459 460 static const ns_dtab dtab[] = { 461 { NSSRC_FILES, files_group, (void *)nss_lt_all }, 462 #ifdef HESIOD 463 { NSSRC_DNS, dns_group, (void *)nss_lt_all }, 464 #endif 465 #ifdef YP 466 { NSSRC_NIS, nis_group, (void *)nss_lt_all }, 467 #endif 468 { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, 469 #ifdef NS_CACHING 470 NS_CACHE_CB(&cache_info) 471 #endif 472 { NULL, NULL, NULL } 473 }; 474 int rv, ret_errno; 475 476 ret_errno = 0; 477 *result = NULL; 478 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc, 479 grp, buffer, bufsize, &ret_errno); 480 if (rv == NS_SUCCESS) 481 return (0); 482 else 483 return (ret_errno); 484 } 485 486 487 int 488 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, 489 struct group **result) 490 { 491 #ifdef NS_CACHING 492 static const nss_cache_info cache_info = 493 NS_COMMON_CACHE_INFO_INITIALIZER( 494 group, (void *)nss_lt_name, 495 grp_id_func, grp_marshal_func, grp_unmarshal_func); 496 #endif 497 498 static const ns_dtab dtab[] = { 499 { NSSRC_FILES, files_group, (void *)nss_lt_name }, 500 #ifdef HESIOD 501 { NSSRC_DNS, dns_group, (void *)nss_lt_name }, 502 #endif 503 #ifdef YP 504 { NSSRC_NIS, nis_group, (void *)nss_lt_name }, 505 #endif 506 { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, 507 #ifdef NS_CACHING 508 NS_CACHE_CB(&cache_info) 509 #endif 510 { NULL, NULL, NULL } 511 }; 512 int rv, ret_errno; 513 514 ret_errno = 0; 515 *result = NULL; 516 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc, 517 name, grp, buffer, bufsize, &ret_errno); 518 if (rv == NS_SUCCESS) 519 return (0); 520 else 521 return (ret_errno); 522 } 523 524 525 int 526 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, 527 struct group **result) 528 { 529 #ifdef NS_CACHING 530 static const nss_cache_info cache_info = 531 NS_COMMON_CACHE_INFO_INITIALIZER( 532 group, (void *)nss_lt_id, 533 grp_id_func, grp_marshal_func, grp_unmarshal_func); 534 #endif 535 536 static const ns_dtab dtab[] = { 537 { NSSRC_FILES, files_group, (void *)nss_lt_id }, 538 #ifdef HESIOD 539 { NSSRC_DNS, dns_group, (void *)nss_lt_id }, 540 #endif 541 #ifdef YP 542 { NSSRC_NIS, nis_group, (void *)nss_lt_id }, 543 #endif 544 { NSSRC_COMPAT, compat_group, (void *)nss_lt_id }, 545 #ifdef NS_CACHING 546 NS_CACHE_CB(&cache_info) 547 #endif 548 { NULL, NULL, NULL } 549 }; 550 int rv, ret_errno; 551 552 ret_errno = 0; 553 *result = NULL; 554 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc, 555 gid, grp, buffer, bufsize, &ret_errno); 556 if (rv == NS_SUCCESS) 557 return (0); 558 else 559 return (ret_errno); 560 } 561 562 563 static struct group grp; 564 static char *grp_storage; 565 static size_t grp_storage_size; 566 567 static struct group * 568 getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **), 569 union key key) 570 { 571 int rv; 572 struct group *res; 573 574 if (grp_storage == NULL) { 575 grp_storage = malloc(GRP_STORAGE_INITIAL); 576 if (grp_storage == NULL) 577 return (NULL); 578 grp_storage_size = GRP_STORAGE_INITIAL; 579 } 580 do { 581 rv = fn(key, &grp, grp_storage, grp_storage_size, &res); 582 if (res == NULL && rv == ERANGE) { 583 free(grp_storage); 584 if ((grp_storage_size << 1) > GRP_STORAGE_MAX) { 585 grp_storage = NULL; 586 errno = ERANGE; 587 return (NULL); 588 } 589 grp_storage_size <<= 1; 590 grp_storage = malloc(grp_storage_size); 591 if (grp_storage == NULL) 592 return (NULL); 593 } 594 } while (res == NULL && rv == ERANGE); 595 if (rv != 0) 596 errno = rv; 597 return (res); 598 } 599 600 601 static int 602 wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, 603 struct group **res) 604 { 605 return (getgrnam_r(key.name, grp, buffer, bufsize, res)); 606 } 607 608 609 static int 610 wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, 611 struct group **res) 612 { 613 return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); 614 } 615 616 617 static int 618 wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, 619 size_t bufsize, struct group **res) 620 { 621 return (getgrent_r(grp, buffer, bufsize, res)); 622 } 623 624 625 struct group * 626 getgrnam(const char *name) 627 { 628 union key key; 629 630 key.name = name; 631 return (getgr(wrap_getgrnam_r, key)); 632 } 633 634 635 struct group * 636 getgrgid(gid_t gid) 637 { 638 union key key; 639 640 key.gid = gid; 641 return (getgr(wrap_getgrgid_r, key)); 642 } 643 644 645 struct group * 646 getgrent(void) 647 { 648 union key key; 649 650 key.gid = 0; /* not used */ 651 return (getgr(wrap_getgrent_r, key)); 652 } 653 654 655 static int 656 is_comment_line(const char *s, size_t n) 657 { 658 const char *eom; 659 660 eom = &s[n]; 661 662 for (; s < eom; s++) 663 if (*s == '#' || !isspace((unsigned char)*s)) 664 break; 665 return (*s == '#' || s == eom); 666 } 667 668 669 /* 670 * files backend 671 */ 672 static void 673 files_endstate(void *p) 674 { 675 676 if (p == NULL) 677 return; 678 if (((struct files_state *)p)->fp != NULL) 679 fclose(((struct files_state *)p)->fp); 680 free(p); 681 } 682 683 684 static int 685 files_setgrent(void *retval, void *mdata, va_list ap) 686 { 687 struct files_state *st; 688 int rv, stayopen; 689 690 rv = files_getstate(&st); 691 if (rv != 0) 692 return (NS_UNAVAIL); 693 switch ((enum constants)mdata) { 694 case SETGRENT: 695 stayopen = va_arg(ap, int); 696 if (st->fp != NULL) 697 rewind(st->fp); 698 else if (stayopen) 699 st->fp = fopen(_PATH_GROUP, "r"); 700 break; 701 case ENDGRENT: 702 if (st->fp != NULL) { 703 fclose(st->fp); 704 st->fp = NULL; 705 } 706 break; 707 default: 708 break; 709 } 710 return (NS_UNAVAIL); 711 } 712 713 714 static int 715 files_group(void *retval, void *mdata, va_list ap) 716 { 717 struct files_state *st; 718 enum nss_lookup_type how; 719 const char *name, *line; 720 struct group *grp; 721 gid_t gid; 722 char *buffer; 723 size_t bufsize, linesize; 724 off_t pos; 725 int rv, stayopen, *errnop; 726 727 name = NULL; 728 gid = (gid_t)-1; 729 how = (enum nss_lookup_type)mdata; 730 switch (how) { 731 case nss_lt_name: 732 name = va_arg(ap, const char *); 733 break; 734 case nss_lt_id: 735 gid = va_arg(ap, gid_t); 736 break; 737 case nss_lt_all: 738 break; 739 default: 740 return (NS_NOTFOUND); 741 } 742 grp = va_arg(ap, struct group *); 743 buffer = va_arg(ap, char *); 744 bufsize = va_arg(ap, size_t); 745 errnop = va_arg(ap, int *); 746 *errnop = files_getstate(&st); 747 if (*errnop != 0) 748 return (NS_UNAVAIL); 749 if (st->fp == NULL && 750 ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { 751 *errnop = errno; 752 return (NS_UNAVAIL); 753 } 754 if (how == nss_lt_all) 755 stayopen = 1; 756 else { 757 rewind(st->fp); 758 stayopen = st->stayopen; 759 } 760 rv = NS_NOTFOUND; 761 pos = ftello(st->fp); 762 while ((line = fgetln(st->fp, &linesize)) != NULL) { 763 if (line[linesize-1] == '\n') 764 linesize--; 765 rv = __gr_match_entry(line, linesize, how, name, gid); 766 if (rv != NS_SUCCESS) 767 continue; 768 /* We need room at least for the line, a string NUL 769 * terminator, alignment padding, and one (char *) 770 * pointer for the member list terminator. 771 */ 772 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 773 *errnop = ERANGE; 774 rv = NS_RETURN; 775 break; 776 } 777 memcpy(buffer, line, linesize); 778 buffer[linesize] = '\0'; 779 rv = __gr_parse_entry(buffer, linesize, grp, 780 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 781 if (rv & NS_TERMINATE) 782 break; 783 pos = ftello(st->fp); 784 } 785 if (!stayopen && st->fp != NULL) { 786 fclose(st->fp); 787 st->fp = NULL; 788 } 789 if (rv == NS_SUCCESS && retval != NULL) 790 *(struct group **)retval = grp; 791 else if (*errnop == ERANGE) 792 fseeko(st->fp, pos, SEEK_SET); 793 return (rv); 794 } 795 796 797 #ifdef HESIOD 798 /* 799 * dns backend 800 */ 801 static void 802 dns_endstate(void *p) 803 { 804 805 free(p); 806 } 807 808 809 static int 810 dns_setgrent(void *retval, void *cb_data, va_list ap) 811 { 812 struct dns_state *st; 813 int rv; 814 815 rv = dns_getstate(&st); 816 if (rv != 0) 817 return (NS_UNAVAIL); 818 st->counter = 0; 819 return (NS_UNAVAIL); 820 } 821 822 823 static int 824 dns_group(void *retval, void *mdata, va_list ap) 825 { 826 char buf[HESIOD_NAME_MAX]; 827 struct dns_state *st; 828 struct group *grp; 829 const char *name, *label; 830 void *ctx; 831 char *buffer, **hes; 832 size_t bufsize, adjsize, linesize; 833 gid_t gid; 834 enum nss_lookup_type how; 835 int rv, *errnop; 836 837 ctx = NULL; 838 hes = NULL; 839 name = NULL; 840 gid = (gid_t)-1; 841 how = (enum nss_lookup_type)mdata; 842 switch (how) { 843 case nss_lt_name: 844 name = va_arg(ap, const char *); 845 break; 846 case nss_lt_id: 847 gid = va_arg(ap, gid_t); 848 break; 849 case nss_lt_all: 850 break; 851 } 852 grp = va_arg(ap, struct group *); 853 buffer = va_arg(ap, char *); 854 bufsize = va_arg(ap, size_t); 855 errnop = va_arg(ap, int *); 856 *errnop = dns_getstate(&st); 857 if (*errnop != 0) 858 return (NS_UNAVAIL); 859 if (hesiod_init(&ctx) != 0) { 860 *errnop = errno; 861 rv = NS_UNAVAIL; 862 goto fin; 863 } 864 do { 865 rv = NS_NOTFOUND; 866 switch (how) { 867 case nss_lt_name: 868 label = name; 869 break; 870 case nss_lt_id: 871 if (snprintf(buf, sizeof(buf), "%lu", 872 (unsigned long)gid) >= sizeof(buf)) 873 goto fin; 874 label = buf; 875 break; 876 case nss_lt_all: 877 if (st->counter < 0) 878 goto fin; 879 if (snprintf(buf, sizeof(buf), "group-%ld", 880 st->counter++) >= sizeof(buf)) 881 goto fin; 882 label = buf; 883 break; 884 } 885 hes = hesiod_resolve(ctx, label, 886 how == nss_lt_id ? "gid" : "group"); 887 if ((how == nss_lt_id && hes == NULL && 888 (hes = hesiod_resolve(ctx, buf, "group")) == NULL) || 889 hes == NULL) { 890 if (how == nss_lt_all) 891 st->counter = -1; 892 if (errno != ENOENT) 893 *errnop = errno; 894 goto fin; 895 } 896 rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid); 897 if (rv != NS_SUCCESS) { 898 hesiod_free_list(ctx, hes); 899 hes = NULL; 900 continue; 901 } 902 /* We need room at least for the line, a string NUL 903 * terminator, alignment padding, and one (char *) 904 * pointer for the member list terminator. 905 */ 906 adjsize = bufsize - _ALIGNBYTES - sizeof(char *); 907 linesize = strlcpy(buffer, hes[0], adjsize); 908 if (linesize >= adjsize) { 909 *errnop = ERANGE; 910 rv = NS_RETURN; 911 goto fin; 912 } 913 hesiod_free_list(ctx, hes); 914 hes = NULL; 915 rv = __gr_parse_entry(buffer, linesize, grp, 916 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 917 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 918 fin: 919 if (hes != NULL) 920 hesiod_free_list(ctx, hes); 921 if (ctx != NULL) 922 hesiod_end(ctx); 923 if (rv == NS_SUCCESS && retval != NULL) 924 *(struct group **)retval = grp; 925 return (rv); 926 } 927 #endif /* HESIOD */ 928 929 930 #ifdef YP 931 /* 932 * nis backend 933 */ 934 static void 935 nis_endstate(void *p) 936 { 937 938 if (p == NULL) 939 return; 940 free(((struct nis_state *)p)->key); 941 free(p); 942 } 943 944 945 static int 946 nis_setgrent(void *retval, void *cb_data, va_list ap) 947 { 948 struct nis_state *st; 949 int rv; 950 951 rv = nis_getstate(&st); 952 if (rv != 0) 953 return (NS_UNAVAIL); 954 st->done = 0; 955 free(st->key); 956 st->key = NULL; 957 return (NS_UNAVAIL); 958 } 959 960 961 static int 962 nis_group(void *retval, void *mdata, va_list ap) 963 { 964 char *map; 965 struct nis_state *st; 966 struct group *grp; 967 const char *name; 968 char *buffer, *key, *result; 969 size_t bufsize; 970 gid_t gid; 971 enum nss_lookup_type how; 972 int *errnop, keylen, resultlen, rv; 973 974 name = NULL; 975 gid = (gid_t)-1; 976 how = (enum nss_lookup_type)mdata; 977 switch (how) { 978 case nss_lt_name: 979 name = va_arg(ap, const char *); 980 map = "group.byname"; 981 break; 982 case nss_lt_id: 983 gid = va_arg(ap, gid_t); 984 map = "group.bygid"; 985 break; 986 case nss_lt_all: 987 map = "group.byname"; 988 break; 989 } 990 grp = va_arg(ap, struct group *); 991 buffer = va_arg(ap, char *); 992 bufsize = va_arg(ap, size_t); 993 errnop = va_arg(ap, int *); 994 *errnop = nis_getstate(&st); 995 if (*errnop != 0) 996 return (NS_UNAVAIL); 997 if (st->domain[0] == '\0') { 998 if (getdomainname(st->domain, sizeof(st->domain)) != 0) { 999 *errnop = errno; 1000 return (NS_UNAVAIL); 1001 } 1002 } 1003 result = NULL; 1004 do { 1005 rv = NS_NOTFOUND; 1006 switch (how) { 1007 case nss_lt_name: 1008 if (strlcpy(buffer, name, bufsize) >= bufsize) 1009 goto erange; 1010 break; 1011 case nss_lt_id: 1012 if (snprintf(buffer, bufsize, "%lu", 1013 (unsigned long)gid) >= bufsize) 1014 goto erange; 1015 break; 1016 case nss_lt_all: 1017 if (st->done) 1018 goto fin; 1019 break; 1020 } 1021 result = NULL; 1022 if (how == nss_lt_all) { 1023 if (st->key == NULL) 1024 rv = yp_first(st->domain, map, &st->key, 1025 &st->keylen, &result, &resultlen); 1026 else { 1027 key = st->key; 1028 keylen = st->keylen; 1029 st->key = NULL; 1030 rv = yp_next(st->domain, map, key, keylen, 1031 &st->key, &st->keylen, &result, 1032 &resultlen); 1033 free(key); 1034 } 1035 if (rv != 0) { 1036 free(result); 1037 free(st->key); 1038 st->key = NULL; 1039 if (rv == YPERR_NOMORE) { 1040 st->done = 1; 1041 rv = NS_NOTFOUND; 1042 } else 1043 rv = NS_UNAVAIL; 1044 goto fin; 1045 } 1046 } else { 1047 rv = yp_match(st->domain, map, buffer, strlen(buffer), 1048 &result, &resultlen); 1049 if (rv == YPERR_KEY) { 1050 rv = NS_NOTFOUND; 1051 continue; 1052 } else if (rv != 0) { 1053 free(result); 1054 rv = NS_UNAVAIL; 1055 continue; 1056 } 1057 } 1058 /* We need room at least for the line, a string NUL 1059 * terminator, alignment padding, and one (char *) 1060 * pointer for the member list terminator. 1061 */ 1062 if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) 1063 goto erange; 1064 memcpy(buffer, result, resultlen); 1065 buffer[resultlen] = '\0'; 1066 free(result); 1067 rv = __gr_match_entry(buffer, resultlen, how, name, gid); 1068 if (rv == NS_SUCCESS) 1069 rv = __gr_parse_entry(buffer, resultlen, grp, 1070 &buffer[resultlen+1], bufsize - resultlen - 1, 1071 errnop); 1072 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 1073 fin: 1074 if (rv == NS_SUCCESS && retval != NULL) 1075 *(struct group **)retval = grp; 1076 return (rv); 1077 erange: 1078 *errnop = ERANGE; 1079 return (NS_RETURN); 1080 } 1081 #endif /* YP */ 1082 1083 1084 1085 /* 1086 * compat backend 1087 */ 1088 static void 1089 compat_endstate(void *p) 1090 { 1091 struct compat_state *st; 1092 1093 if (p == NULL) 1094 return; 1095 st = (struct compat_state *)p; 1096 free(st->name); 1097 if (st->fp != NULL) 1098 fclose(st->fp); 1099 free(p); 1100 } 1101 1102 1103 static int 1104 compat_setgrent(void *retval, void *mdata, va_list ap) 1105 { 1106 static const ns_src compatsrc[] = { 1107 #ifdef YP 1108 { NSSRC_NIS, NS_SUCCESS }, 1109 #endif 1110 { NULL, 0 } 1111 }; 1112 ns_dtab dtab[] = { 1113 #ifdef HESIOD 1114 { NSSRC_DNS, dns_setgrent, NULL }, 1115 #endif 1116 #ifdef YP 1117 { NSSRC_NIS, nis_setgrent, NULL }, 1118 #endif 1119 { NULL, NULL, NULL } 1120 }; 1121 struct compat_state *st; 1122 int rv, stayopen; 1123 1124 #define set_setent(x, y) do { \ 1125 int i; \ 1126 \ 1127 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1128 x[i].mdata = (void *)y; \ 1129 } while (0) 1130 1131 rv = compat_getstate(&st); 1132 if (rv != 0) 1133 return (NS_UNAVAIL); 1134 switch ((enum constants)mdata) { 1135 case SETGRENT: 1136 stayopen = va_arg(ap, int); 1137 if (st->fp != NULL) 1138 rewind(st->fp); 1139 else if (stayopen) 1140 st->fp = fopen(_PATH_GROUP, "r"); 1141 set_setent(dtab, mdata); 1142 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent", 1143 compatsrc, 0); 1144 break; 1145 case ENDGRENT: 1146 if (st->fp != NULL) { 1147 fclose(st->fp); 1148 st->fp = NULL; 1149 } 1150 set_setent(dtab, mdata); 1151 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent", 1152 compatsrc, 0); 1153 break; 1154 default: 1155 break; 1156 } 1157 st->compat = COMPAT_MODE_OFF; 1158 free(st->name); 1159 st->name = NULL; 1160 return (NS_UNAVAIL); 1161 #undef set_setent 1162 } 1163 1164 1165 static int 1166 compat_group(void *retval, void *mdata, va_list ap) 1167 { 1168 static const ns_src compatsrc[] = { 1169 #ifdef YP 1170 { NSSRC_NIS, NS_SUCCESS }, 1171 #endif 1172 { NULL, 0 } 1173 }; 1174 ns_dtab dtab[] = { 1175 #ifdef YP 1176 { NSSRC_NIS, nis_group, NULL }, 1177 #endif 1178 #ifdef HESIOD 1179 { NSSRC_DNS, dns_group, NULL }, 1180 #endif 1181 { NULL, NULL, NULL } 1182 }; 1183 struct compat_state *st; 1184 enum nss_lookup_type how; 1185 const char *name, *line; 1186 struct group *grp; 1187 gid_t gid; 1188 char *buffer, *p; 1189 void *discard; 1190 size_t bufsize, linesize; 1191 off_t pos; 1192 int rv, stayopen, *errnop; 1193 1194 #define set_lookup_type(x, y) do { \ 1195 int i; \ 1196 \ 1197 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1198 x[i].mdata = (void *)y; \ 1199 } while (0) 1200 1201 name = NULL; 1202 gid = (gid_t)-1; 1203 how = (enum nss_lookup_type)mdata; 1204 switch (how) { 1205 case nss_lt_name: 1206 name = va_arg(ap, const char *); 1207 break; 1208 case nss_lt_id: 1209 gid = va_arg(ap, gid_t); 1210 break; 1211 case nss_lt_all: 1212 break; 1213 default: 1214 return (NS_NOTFOUND); 1215 } 1216 grp = va_arg(ap, struct group *); 1217 buffer = va_arg(ap, char *); 1218 bufsize = va_arg(ap, size_t); 1219 errnop = va_arg(ap, int *); 1220 *errnop = compat_getstate(&st); 1221 if (*errnop != 0) 1222 return (NS_UNAVAIL); 1223 if (st->fp == NULL && 1224 ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { 1225 *errnop = errno; 1226 rv = NS_UNAVAIL; 1227 goto fin; 1228 } 1229 if (how == nss_lt_all) 1230 stayopen = 1; 1231 else { 1232 rewind(st->fp); 1233 stayopen = st->stayopen; 1234 } 1235 docompat: 1236 switch (st->compat) { 1237 case COMPAT_MODE_ALL: 1238 set_lookup_type(dtab, how); 1239 switch (how) { 1240 case nss_lt_all: 1241 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1242 "getgrent_r", compatsrc, grp, buffer, bufsize, 1243 errnop); 1244 break; 1245 case nss_lt_id: 1246 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1247 "getgrgid_r", compatsrc, gid, grp, buffer, bufsize, 1248 errnop); 1249 break; 1250 case nss_lt_name: 1251 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1252 "getgrnam_r", compatsrc, name, grp, buffer, 1253 bufsize, errnop); 1254 break; 1255 } 1256 if (rv & NS_TERMINATE) 1257 goto fin; 1258 st->compat = COMPAT_MODE_OFF; 1259 break; 1260 case COMPAT_MODE_NAME: 1261 set_lookup_type(dtab, nss_lt_name); 1262 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1263 "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize, 1264 errnop); 1265 switch (rv) { 1266 case NS_SUCCESS: 1267 switch (how) { 1268 case nss_lt_name: 1269 if (strcmp(name, grp->gr_name) != 0) 1270 rv = NS_NOTFOUND; 1271 break; 1272 case nss_lt_id: 1273 if (gid != grp->gr_gid) 1274 rv = NS_NOTFOUND; 1275 break; 1276 default: 1277 break; 1278 } 1279 break; 1280 case NS_RETURN: 1281 goto fin; 1282 default: 1283 break; 1284 } 1285 free(st->name); 1286 st->name = NULL; 1287 st->compat = COMPAT_MODE_OFF; 1288 if (rv == NS_SUCCESS) 1289 goto fin; 1290 break; 1291 default: 1292 break; 1293 } 1294 rv = NS_NOTFOUND; 1295 pos = ftello(st->fp); 1296 while ((line = fgetln(st->fp, &linesize)) != NULL) { 1297 if (line[linesize-1] == '\n') 1298 linesize--; 1299 if (linesize > 2 && line[0] == '+') { 1300 p = memchr(&line[1], ':', linesize); 1301 if (p == NULL || p == &line[1]) 1302 st->compat = COMPAT_MODE_ALL; 1303 else { 1304 st->name = malloc(p - line); 1305 if (st->name == NULL) { 1306 syslog(LOG_ERR, 1307 "getgrent memory allocation failure"); 1308 *errnop = ENOMEM; 1309 rv = NS_UNAVAIL; 1310 break; 1311 } 1312 memcpy(st->name, &line[1], p - line - 1); 1313 st->name[p - line - 1] = '\0'; 1314 st->compat = COMPAT_MODE_NAME; 1315 } 1316 goto docompat; 1317 } 1318 rv = __gr_match_entry(line, linesize, how, name, gid); 1319 if (rv != NS_SUCCESS) 1320 continue; 1321 /* We need room at least for the line, a string NUL 1322 * terminator, alignment padding, and one (char *) 1323 * pointer for the member list terminator. 1324 */ 1325 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 1326 *errnop = ERANGE; 1327 rv = NS_RETURN; 1328 break; 1329 } 1330 memcpy(buffer, line, linesize); 1331 buffer[linesize] = '\0'; 1332 rv = __gr_parse_entry(buffer, linesize, grp, 1333 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 1334 if (rv & NS_TERMINATE) 1335 break; 1336 pos = ftello(st->fp); 1337 } 1338 fin: 1339 if (!stayopen && st->fp != NULL) { 1340 fclose(st->fp); 1341 st->fp = NULL; 1342 } 1343 if (rv == NS_SUCCESS && retval != NULL) 1344 *(struct group **)retval = grp; 1345 else if (*errnop == ERANGE) 1346 fseeko(st->fp, pos, SEEK_SET); 1347 return (rv); 1348 #undef set_lookup_type 1349 } 1350 1351 1352 /* 1353 * common group line matching and parsing 1354 */ 1355 int 1356 __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how, 1357 const char *name, gid_t gid) 1358 { 1359 size_t namesize; 1360 const char *p, *eol; 1361 char *q; 1362 unsigned long n; 1363 int i, needed; 1364 1365 if (linesize == 0 || is_comment_line(line, linesize)) 1366 return (NS_NOTFOUND); 1367 switch (how) { 1368 case nss_lt_name: needed = 1; break; 1369 case nss_lt_id: needed = 2; break; 1370 default: needed = 2; break; 1371 } 1372 eol = &line[linesize]; 1373 for (p = line, i = 0; i < needed && p < eol; p++) 1374 if (*p == ':') 1375 i++; 1376 if (i < needed) 1377 return (NS_NOTFOUND); 1378 switch (how) { 1379 case nss_lt_name: 1380 namesize = strlen(name); 1381 if (namesize + 1 == (size_t)(p - line) && 1382 memcmp(line, name, namesize) == 0) 1383 return (NS_SUCCESS); 1384 break; 1385 case nss_lt_id: 1386 n = strtoul(p, &q, 10); 1387 if (q < eol && *q == ':' && gid == (gid_t)n) 1388 return (NS_SUCCESS); 1389 break; 1390 case nss_lt_all: 1391 return (NS_SUCCESS); 1392 default: 1393 break; 1394 } 1395 return (NS_NOTFOUND); 1396 } 1397 1398 1399 int 1400 __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, 1401 size_t membufsize, int *errnop) 1402 { 1403 char *s_gid, *s_mem, *p, **members; 1404 unsigned long n; 1405 int maxmembers; 1406 1407 memset(grp, 0, sizeof(*grp)); 1408 members = (char **)_ALIGN(membuf); 1409 membufsize -= (char *)members - membuf; 1410 maxmembers = membufsize / sizeof(*members); 1411 if (maxmembers <= 0 || 1412 (grp->gr_name = strsep(&line, ":")) == NULL || 1413 grp->gr_name[0] == '\0' || 1414 (grp->gr_passwd = strsep(&line, ":")) == NULL || 1415 (s_gid = strsep(&line, ":")) == NULL || 1416 s_gid[0] == '\0') 1417 return (NS_NOTFOUND); 1418 s_mem = line; 1419 n = strtoul(s_gid, &s_gid, 10); 1420 if (s_gid[0] != '\0') 1421 return (NS_NOTFOUND); 1422 grp->gr_gid = (gid_t)n; 1423 grp->gr_mem = members; 1424 while (maxmembers > 1 && s_mem != NULL) { 1425 p = strsep(&s_mem, ","); 1426 if (p != NULL && *p != '\0') { 1427 *members++ = p; 1428 maxmembers--; 1429 } 1430 } 1431 *members = NULL; 1432 if (s_mem == NULL) 1433 return (NS_SUCCESS); 1434 else { 1435 *errnop = ERANGE; 1436 return (NS_RETURN); 1437 } 1438 } 1439