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