1 /* $NetBSD: getnetgrent.c,v 1.29 2002/05/26 14:03:20 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1994 Christos Zoulas 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Christos Zoulas. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 #if defined(LIBC_SCCS) && !defined(lint) 36 __RCSID("$NetBSD: getnetgrent.c,v 1.29 2002/05/26 14:03:20 wiz Exp $"); 37 #endif /* LIBC_SCCS and not lint */ 38 39 #include "namespace.h" 40 #include <sys/types.h> 41 42 #include <assert.h> 43 #include <ctype.h> 44 #include <db.h> 45 #include <err.h> 46 #include <fcntl.h> 47 #define _NETGROUP_PRIVATE 48 #include <netgroup.h> 49 #include <nsswitch.h> 50 #include <stdarg.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <stringlist.h> 55 56 #ifdef YP 57 #include <rpc/rpc.h> 58 #include <rpcsvc/ypclnt.h> 59 #include <rpcsvc/yp_prot.h> 60 #endif 61 62 #ifdef __weak_alias 63 __weak_alias(endnetgrent,_endnetgrent) 64 __weak_alias(getnetgrent,_getnetgrent) 65 __weak_alias(innetgr,_innetgr) 66 __weak_alias(setnetgrent,_setnetgrent) 67 #endif 68 69 #define _NG_STAR(s) (((s) == NULL || *(s) == '\0') ? _ngstar : s) 70 #define _NG_EMPTY(s) ((s) == NULL ? "" : s) 71 #define _NG_ISSPACE(p) (isspace((unsigned char) (p)) || (p) == '\n') 72 73 static const char _ngstar[] = "*"; 74 static struct netgroup *_nghead = (struct netgroup *)NULL; 75 static struct netgroup *_nglist = (struct netgroup *)NULL; 76 static DB *_ng_db; 77 78 static int getstring __P((char **, int, __aconst char **)); 79 static struct netgroup *getnetgroup __P((char **)); 80 static int lookup __P((char *, char **, int)); 81 static int addgroup __P((StringList *, char *)); 82 static int in_check __P((const char *, const char *, 83 const char *, struct netgroup *)); 84 static int in_find __P((StringList *, char *, const char *, 85 const char *, const char *)); 86 static char *in_lookup1 __P((const char *, const char *, int)); 87 static int in_lookup __P((const char *, const char *, 88 const char *, int)); 89 90 static const ns_src default_files_nis[] = { 91 { NSSRC_FILES, NS_SUCCESS | NS_NOTFOUND }, 92 #ifdef YP 93 { NSSRC_NIS, NS_SUCCESS }, 94 #endif 95 { 0 } 96 }; 97 98 /* 99 * getstring(): Get a string delimited by the character, skipping leading and 100 * trailing blanks and advancing the pointer 101 */ 102 static int 103 getstring(pp, del, str) 104 char **pp; 105 int del; 106 char __aconst **str; 107 { 108 size_t len; 109 char *sp, *ep, *dp; 110 111 _DIAGASSERT(pp != NULL); 112 _DIAGASSERT(str != NULL); 113 114 /* skip leading blanks */ 115 for (sp = *pp; *sp && _NG_ISSPACE(*sp); sp++) 116 continue; 117 118 /* accumulate till delimiter or space */ 119 for (ep = sp; *ep && *ep != del && !_NG_ISSPACE(*ep); ep++) 120 continue; 121 122 /* hunt for the delimiter */ 123 for (dp = ep; *dp && *dp != del && _NG_ISSPACE(*dp); dp++) 124 continue; 125 126 if (*dp != del) { 127 *str = NULL; 128 return 0; 129 } 130 131 *pp = ++dp; 132 133 len = (ep - sp) + 1; 134 if (len > 1) { 135 dp = malloc(len); 136 if (dp == NULL) 137 return 0; 138 memcpy(dp, sp, len); 139 dp[len - 1] = '\0'; 140 } else 141 dp = NULL; 142 143 *str = dp; 144 return 1; 145 } 146 147 148 /* 149 * getnetgroup(): Parse a netgroup, and advance the pointer 150 */ 151 static struct netgroup * 152 getnetgroup(pp) 153 char **pp; 154 { 155 struct netgroup *ng; 156 157 _DIAGASSERT(pp != NULL); 158 _DIAGASSERT(*pp != NULL); 159 160 ng = malloc(sizeof(struct netgroup)); 161 if (ng == NULL) 162 return NULL; 163 164 (*pp)++; /* skip '(' */ 165 if (!getstring(pp, ',', &ng->ng_host)) 166 goto badhost; 167 168 if (!getstring(pp, ',', &ng->ng_user)) 169 goto baduser; 170 171 if (!getstring(pp, ')', &ng->ng_domain)) 172 goto baddomain; 173 174 #ifdef DEBUG_NG 175 { 176 char buf[1024]; 177 (void) fprintf(stderr, "netgroup %s\n", 178 _ng_print(buf, sizeof(buf), ng)); 179 } 180 #endif 181 return ng; 182 183 baddomain: 184 if (ng->ng_user) 185 free((char *)ng->ng_user); 186 baduser: 187 if (ng->ng_host) 188 free((char *)ng->ng_host); 189 badhost: 190 free(ng); 191 return NULL; 192 } 193 194 195 static int _local_lookup __P((void *, void *, va_list)); 196 197 /*ARGSUSED*/ 198 static int 199 _local_lookup(rv, cb_data, ap) 200 void *rv; 201 void *cb_data; 202 va_list ap; 203 { 204 char *name = va_arg(ap, char *); 205 char **line = va_arg(ap, char **); 206 int bywhat = va_arg(ap, int); 207 208 DBT key, data; 209 size_t len; 210 char *ks; 211 int r; 212 213 if (_ng_db == NULL) 214 return NS_UNAVAIL; 215 216 len = strlen(name) + 2; 217 ks = malloc(len); 218 if (ks == NULL) 219 return NS_UNAVAIL; 220 221 ks[0] = bywhat; 222 memcpy(&ks[1], name, len - 1); 223 224 key.data = (u_char *) ks; 225 key.size = len; 226 227 r = (_ng_db->get) (_ng_db, &key, &data, 0); 228 free(ks); 229 switch (r) { 230 case 0: 231 break; 232 case 1: 233 return NS_NOTFOUND; 234 case -1: 235 /* XXX: call endnetgrent() here ? */ 236 return NS_UNAVAIL; 237 } 238 239 *line = strdup(data.data); 240 if (*line == NULL) 241 return NS_UNAVAIL; 242 return NS_SUCCESS; 243 } 244 245 #ifdef YP 246 static int _nis_lookup __P((void *, void *, va_list)); 247 248 /*ARGSUSED*/ 249 static int 250 _nis_lookup(rv, cb_data, ap) 251 void *rv; 252 void *cb_data; 253 va_list ap; 254 { 255 char *name = va_arg(ap, char *); 256 char **line = va_arg(ap, char **); 257 int bywhat = va_arg(ap, int); 258 259 static char *__ypdomain; 260 int i; 261 char *map = NULL; 262 263 if(__ypdomain == NULL) { 264 switch (yp_get_default_domain(&__ypdomain)) { 265 case 0: 266 break; 267 case YPERR_RESRC: 268 return NS_TRYAGAIN; 269 default: 270 return NS_UNAVAIL; 271 } 272 } 273 274 switch (bywhat) { 275 case _NG_KEYBYNAME: 276 map = "netgroup"; 277 break; 278 279 case _NG_KEYBYUSER: 280 map = "netgroup.byuser"; 281 break; 282 283 case _NG_KEYBYHOST: 284 map = "netgroup.byhost"; 285 break; 286 287 default: 288 abort(); 289 } 290 291 *line = NULL; 292 switch (yp_match(__ypdomain, map, name, (int)strlen(name), line, &i)) { 293 case 0: 294 return NS_SUCCESS; 295 case YPERR_KEY: 296 if (*line) 297 free(*line); 298 return NS_NOTFOUND; 299 default: 300 if (*line) 301 free(*line); 302 return NS_UNAVAIL; 303 } 304 /* NOTREACHED */ 305 } 306 #endif 307 308 309 /* 310 * lookup(): Find the given key in the database or yp, and return its value 311 * in *line; returns 1 if key was found, 0 otherwise 312 */ 313 static int 314 lookup(name, line, bywhat) 315 char *name; 316 char **line; 317 int bywhat; 318 { 319 int r; 320 static const ns_dtab dtab[] = { 321 NS_FILES_CB(_local_lookup, NULL) 322 NS_NIS_CB(_nis_lookup, NULL) 323 { 0 } 324 }; 325 326 _DIAGASSERT(name != NULL); 327 _DIAGASSERT(line != NULL); 328 329 r = nsdispatch(NULL, dtab, NSDB_NETGROUP, "lookup", default_files_nis, 330 name, line, bywhat); 331 return (r == NS_SUCCESS) ? 1 : 0; 332 } 333 334 /* 335 * _ng_parse(): Parse a line and return: _NG_ERROR: Syntax Error _NG_NONE: 336 * line was empty or a comment _NG_GROUP: line had a netgroup definition, 337 * returned in ng _NG_NAME: line had a netgroup name, returned in name 338 * 339 * Public since used by netgroup_mkdb 340 */ 341 int 342 _ng_parse(p, name, ng) 343 char **p; 344 char **name; 345 struct netgroup **ng; 346 { 347 348 _DIAGASSERT(p != NULL); 349 _DIAGASSERT(*p != NULL); 350 _DIAGASSERT(name != NULL); 351 _DIAGASSERT(ng != NULL); 352 353 while (**p) { 354 if (**p == '#') 355 /* comment */ 356 return _NG_NONE; 357 358 while (**p && _NG_ISSPACE(**p)) 359 /* skipblank */ 360 (*p)++; 361 362 if (**p == '(') { 363 if ((*ng = getnetgroup(p)) == NULL) 364 return _NG_ERROR; 365 return _NG_GROUP; 366 } else { 367 char *np; 368 size_t i; 369 370 for (np = *p; **p && !_NG_ISSPACE(**p); (*p)++) 371 continue; 372 if (np != *p) { 373 i = (*p - np) + 1; 374 *name = malloc(i); 375 if (*name == NULL) 376 return _NG_ERROR; 377 memcpy(*name, np, i); 378 (*name)[i - 1] = '\0'; 379 return _NG_NAME; 380 } 381 } 382 } 383 return _NG_NONE; 384 } 385 386 387 /* 388 * addgroup(): Recursively add all the members of the netgroup to this group. 389 * returns 0 upon failure, nonzero upon success. 390 * grp is not a valid pointer after return (either free(3)ed or allocated 391 * to a stringlist). in either case, it shouldn't be used again. 392 */ 393 static int 394 addgroup(sl, grp) 395 StringList *sl; 396 char *grp; 397 { 398 char *line, *p; 399 struct netgroup *ng; 400 char *name; 401 402 _DIAGASSERT(sl != NULL); 403 _DIAGASSERT(grp != NULL); 404 405 #ifdef DEBUG_NG 406 (void) fprintf(stderr, "addgroup(%s)\n", grp); 407 #endif 408 /* check for cycles */ 409 if (sl_find(sl, grp) != NULL) { 410 free(grp); 411 warnx("netgroup: Cycle in group `%s'", grp); 412 return 0; 413 } 414 if (sl_add(sl, grp) == -1) { 415 free(grp); 416 return 0; 417 } 418 419 /* Lookup this netgroup */ 420 line = NULL; 421 if (!lookup(grp, &line, _NG_KEYBYNAME)) { 422 if (line != NULL) 423 free(line); 424 return 0; 425 } 426 427 p = line; 428 429 for (;;) { 430 switch (_ng_parse(&p, &name, &ng)) { 431 case _NG_NONE: 432 /* Done with the line */ 433 free(line); 434 return 1; 435 436 case _NG_GROUP: 437 /* new netgroup */ 438 /* add to the list */ 439 ng->ng_next = _nglist; 440 _nglist = ng; 441 break; 442 443 case _NG_NAME: 444 /* netgroup name */ 445 if (!addgroup(sl, name)) 446 return 0; 447 break; 448 449 case _NG_ERROR: 450 return 0; 451 452 default: 453 abort(); 454 } 455 } 456 } 457 458 459 /* 460 * in_check(): Compare the spec with the netgroup 461 */ 462 static int 463 in_check(host, user, domain, ng) 464 const char *host; 465 const char *user; 466 const char *domain; 467 struct netgroup *ng; 468 { 469 470 /* host may be NULL */ 471 /* user may be NULL */ 472 /* domain may be NULL */ 473 _DIAGASSERT(ng != NULL); 474 475 if ((host != NULL) && (ng->ng_host != NULL) 476 && strcmp(ng->ng_host, host) != 0) 477 return 0; 478 479 if ((user != NULL) && (ng->ng_user != NULL) 480 && strcmp(ng->ng_user, user) != 0) 481 return 0; 482 483 if ((domain != NULL) && (ng->ng_domain != NULL) 484 && strcmp(ng->ng_domain, domain) != 0) 485 return 0; 486 487 return 1; 488 } 489 490 491 /* 492 * in_find(): Find a match for the host, user, domain spec. 493 * grp is not a valid pointer after return (either free(3)ed or allocated 494 * to a stringlist). in either case, it shouldn't be used again. 495 */ 496 static int 497 in_find(sl, grp, host, user, domain) 498 StringList *sl; 499 char *grp; 500 const char *host; 501 const char *user; 502 const char *domain; 503 { 504 char *line, *p; 505 int i; 506 struct netgroup *ng; 507 char *name; 508 509 _DIAGASSERT(sl != NULL); 510 _DIAGASSERT(grp != NULL); 511 /* host may be NULL */ 512 /* user may be NULL */ 513 /* domain may be NULL */ 514 515 #ifdef DEBUG_NG 516 (void) fprintf(stderr, "in_find(%s)\n", grp); 517 #endif 518 /* check for cycles */ 519 if (sl_find(sl, grp) != NULL) { 520 free(grp); 521 warnx("netgroup: Cycle in group `%s'", grp); 522 return 0; 523 } 524 if (sl_add(sl, grp) == -1) { 525 free(grp); 526 return 0; 527 } 528 529 /* Lookup this netgroup */ 530 line = NULL; 531 if (!lookup(grp, &line, _NG_KEYBYNAME)) { 532 if (line) 533 free(line); 534 return 0; 535 } 536 537 p = line; 538 539 for (;;) { 540 switch (_ng_parse(&p, &name, &ng)) { 541 case _NG_NONE: 542 /* Done with the line */ 543 free(line); 544 return 0; 545 546 case _NG_GROUP: 547 /* new netgroup */ 548 i = in_check(host, user, domain, ng); 549 if (ng->ng_host != NULL) 550 free((char *)ng->ng_host); 551 if (ng->ng_user != NULL) 552 free((char *)ng->ng_user); 553 if (ng->ng_domain != NULL) 554 free((char *)ng->ng_domain); 555 free(ng); 556 if (i) { 557 free(line); 558 return 1; 559 } 560 break; 561 562 case _NG_NAME: 563 /* netgroup name */ 564 if (in_find(sl, name, host, user, domain)) { 565 free(line); 566 return 1; 567 } 568 break; 569 570 case _NG_ERROR: 571 free(line); 572 return 0; 573 574 default: 575 abort(); 576 } 577 } 578 } 579 580 /* 581 * _ng_makekey(): Make a key from the two names given. The key is of the form 582 * <name1>.<name2> Names strings are replaced with * if they are empty; 583 * Returns NULL if there's a problem. 584 */ 585 char * 586 _ng_makekey(s1, s2, len) 587 const char *s1, *s2; 588 size_t len; 589 { 590 char *buf; 591 592 /* s1 may be NULL */ 593 /* s2 may be NULL */ 594 595 buf = malloc(len); 596 if (buf != NULL) 597 (void) snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2)); 598 return buf; 599 } 600 601 void 602 _ng_print(buf, len, ng) 603 char *buf; 604 size_t len; 605 const struct netgroup *ng; 606 { 607 _DIAGASSERT(buf != NULL); 608 _DIAGASSERT(ng != NULL); 609 610 (void) snprintf(buf, len, "(%s,%s,%s)", _NG_EMPTY(ng->ng_host), 611 _NG_EMPTY(ng->ng_user), _NG_EMPTY(ng->ng_domain)); 612 } 613 614 615 /* 616 * in_lookup1(): Fast lookup for a key in the appropriate map 617 */ 618 static char * 619 in_lookup1(key, domain, map) 620 const char *key; 621 const char *domain; 622 int map; 623 { 624 char *line; 625 size_t len; 626 char *ptr; 627 int res; 628 629 /* key may be NULL */ 630 /* domain may be NULL */ 631 632 len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2; 633 ptr = _ng_makekey(key, domain, len); 634 if (ptr == NULL) 635 return NULL; 636 res = lookup(ptr, &line, map); 637 free(ptr); 638 return res ? line : NULL; 639 } 640 641 642 /* 643 * in_lookup(): Fast lookup for a key in the appropriate map 644 */ 645 static int 646 in_lookup(group, key, domain, map) 647 const char *group; 648 const char *key; 649 const char *domain; 650 int map; 651 { 652 size_t len; 653 char *ptr, *line; 654 655 _DIAGASSERT(group != NULL); 656 /* key may be NULL */ 657 /* domain may be NULL */ 658 659 if (domain != NULL) { 660 /* Domain specified; look in "group.domain" and "*.domain" */ 661 if ((line = in_lookup1(key, domain, map)) == NULL) 662 line = in_lookup1(NULL, domain, map); 663 } else 664 line = NULL; 665 666 if (line == NULL) { 667 /* 668 * domain not specified or domain lookup failed; look in 669 * "group.*" and "*.*" 670 */ 671 if (((line = in_lookup1(key, NULL, map)) == NULL) && 672 ((line = in_lookup1(NULL, NULL, map)) == NULL)) 673 return 0; 674 } 675 676 len = strlen(group); 677 678 for (ptr = line; (ptr = strstr(ptr, group)) != NULL;) 679 /* Make sure we did not find a substring */ 680 if ((ptr != line && ptr[-1] != ',') || 681 (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL)) 682 ptr++; 683 else { 684 free(line); 685 return 1; 686 } 687 688 free(line); 689 return 0; 690 } 691 692 693 void 694 endnetgrent() 695 { 696 for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) { 697 _nghead = _nglist->ng_next; 698 if (_nglist->ng_host != NULL) 699 free((char *)_nglist->ng_host); 700 if (_nglist->ng_user != NULL) 701 free((char *)_nglist->ng_user); 702 if (_nglist->ng_domain != NULL) 703 free((char *)_nglist->ng_domain); 704 free(_nglist); 705 } 706 707 if (_ng_db) { 708 (void) (_ng_db->close) (_ng_db); 709 _ng_db = NULL; 710 } 711 } 712 713 714 void 715 setnetgrent(ng) 716 const char *ng; 717 { 718 StringList *sl; 719 char *ng_copy; 720 721 _DIAGASSERT(ng != NULL); 722 723 sl = sl_init(); 724 if (sl == NULL) 725 return; 726 727 /* Cleanup any previous storage */ 728 if (_nghead != NULL) 729 endnetgrent(); 730 731 if (_ng_db == NULL) 732 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL); 733 734 ng_copy = strdup(ng); 735 if (ng_copy != NULL) 736 addgroup(sl, ng_copy); 737 _nghead = _nglist; 738 sl_free(sl, 1); 739 } 740 741 742 int 743 getnetgrent(host, user, domain) 744 const char **host; 745 const char **user; 746 const char **domain; 747 { 748 _DIAGASSERT(host != NULL); 749 _DIAGASSERT(user != NULL); 750 _DIAGASSERT(domain != NULL); 751 752 if (_nglist == NULL) 753 return 0; 754 755 *host = _nglist->ng_host; 756 *user = _nglist->ng_user; 757 *domain = _nglist->ng_domain; 758 759 _nglist = _nglist->ng_next; 760 761 return 1; 762 } 763 764 765 int 766 innetgr(grp, host, user, domain) 767 const char *grp, *host, *user, *domain; 768 { 769 int found; 770 StringList *sl; 771 772 _DIAGASSERT(grp != NULL); 773 /* host may be NULL */ 774 /* user may be NULL */ 775 /* domain may be NULL */ 776 777 if (_ng_db == NULL) 778 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL); 779 780 /* Try the fast lookup first */ 781 if (host != NULL && user == NULL) { 782 if (in_lookup(grp, host, domain, _NG_KEYBYHOST)) 783 return 1; 784 } else if (host == NULL && user != NULL) { 785 if (in_lookup(grp, user, domain, _NG_KEYBYUSER)) 786 return 1; 787 } 788 /* If a domainname is given, we would have found a match */ 789 if (domain != NULL) 790 return 0; 791 792 /* Too bad need the slow recursive way */ 793 sl = sl_init(); 794 if (sl == NULL) 795 return 0; 796 found = in_find(sl, strdup(grp), host, user, domain); 797 sl_free(sl, 1); 798 799 return found; 800 } 801