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