getnetgrent.c revision 1.37
1/* $NetBSD: getnetgrent.c,v 1.37 2006/10/15 16:14:46 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.37 2006/10/15 16:14:46 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 <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 73static const char _ngstar[] = "*"; 74static struct netgroup *_nghead = NULL; 75static struct netgroup *_nglist = NULL; 76static DB *_ng_db; 77 78static int getstring(char **, int, __aconst char **); 79static struct netgroup *getnetgroup(char **); 80static int lookup(char *, char **, int); 81static int addgroup(StringList *, char *); 82static int in_check(const char *, const char *, const char *, 83 struct netgroup *); 84static int in_find(StringList *, char *, const char *, const char *, 85 const char *); 86static char *in_lookup1(const char *, const char *, int); 87static int in_lookup(const char *, const char *, const char *, int); 88 89#ifdef NSSRC_FILES 90static 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, 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 */ 103static int 104getstring(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 */ 149static struct netgroup * 150getnetgroup(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 181baddomain: 182 if (ng->ng_user) 183 free(ng->ng_user); 184baduser: 185 if (ng->ng_host) 186 free(ng->ng_host); 187badhost: 188 free(ng); 189 return NULL; 190} 191 192void 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 203static int _local_lookup(void *, void *, va_list); 204 205/*ARGSUSED*/ 206static 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 251static int _nis_lookup(void *, void *, va_list); 252 253/*ARGSUSED*/ 254static 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 */ 315static int 316lookup(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 NS_NULL_CB 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 333static 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 344static int 345lookup(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 */ 361int 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 */ 410static int 411addgroup(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 if (sl_add(sl, grp) == -1) { 430 free(grp); 431 return 0; 432 } 433 434 /* Lookup this netgroup */ 435 line = NULL; 436 if (!lookup(grp, &line, _NG_KEYBYNAME)) { 437 if (line) 438 free(line); 439 return 0; 440 } 441 442 p = line; 443 444 for (;;) { 445 switch (_ng_parse(&p, &name, &ng)) { 446 case _NG_NONE: 447 /* Done with the line */ 448 free(line); 449 return 1; 450 451 case _NG_GROUP: 452 /* new netgroup */ 453 /* add to the list */ 454 ng->ng_next = _nglist; 455 _nglist = ng; 456 break; 457 458 case _NG_NAME: 459 /* netgroup name */ 460 if (!addgroup(sl, name)) 461 return 0; 462 break; 463 464 case _NG_ERROR: 465 return 0; 466 467 default: 468 abort(); 469 } 470 } 471} 472 473 474/* 475 * in_check(): Compare the spec with the netgroup 476 */ 477static int 478in_check(const char *host, const char *user, const char *domain, 479 struct netgroup *ng) 480{ 481 482 /* host may be NULL */ 483 /* user may be NULL */ 484 /* domain may be NULL */ 485 _DIAGASSERT(ng != NULL); 486 487 if ((host != NULL) && (ng->ng_host != NULL) 488 && strcmp(ng->ng_host, host) != 0) 489 return 0; 490 491 if ((user != NULL) && (ng->ng_user != NULL) 492 && strcmp(ng->ng_user, user) != 0) 493 return 0; 494 495 if ((domain != NULL) && (ng->ng_domain != NULL) 496 && strcmp(ng->ng_domain, domain) != 0) 497 return 0; 498 499 return 1; 500} 501 502 503/* 504 * in_find(): Find a match for the host, user, domain spec. 505 * grp is not a valid pointer after return (either free(3)ed or allocated 506 * to a stringlist). in either case, it shouldn't be used again. 507 */ 508static int 509in_find(StringList *sl, char *grp, const char *host, const char *user, 510 const char *domain) 511{ 512 char *line, *p; 513 int i; 514 struct netgroup *ng; 515 char *name; 516 517 _DIAGASSERT(sl != NULL); 518 _DIAGASSERT(grp != NULL); 519 /* host may be NULL */ 520 /* user may be NULL */ 521 /* domain may be NULL */ 522 523#ifdef DEBUG_NG 524 (void)fprintf(stderr, "in_find(%s)\n", grp); 525#endif 526 /* check for cycles */ 527 if (sl_find(sl, grp) != NULL) { 528 _ng_cycle(grp, sl); 529 free(grp); 530 return 0; 531 } 532 if (sl_add(sl, grp) == -1) { 533 free(grp); 534 return 0; 535 } 536 537 /* Lookup this netgroup */ 538 line = NULL; 539 if (!lookup(grp, &line, _NG_KEYBYNAME)) { 540 if (line) 541 free(line); 542 return 0; 543 } 544 545 p = line; 546 547 for (;;) { 548 switch (_ng_parse(&p, &name, &ng)) { 549 case _NG_NONE: 550 /* Done with the line */ 551 free(line); 552 return 0; 553 554 case _NG_GROUP: 555 /* new netgroup */ 556 i = in_check(host, user, domain, ng); 557 if (ng->ng_host != NULL) 558 free(ng->ng_host); 559 if (ng->ng_user != NULL) 560 free(ng->ng_user); 561 if (ng->ng_domain != NULL) 562 free(ng->ng_domain); 563 free(ng); 564 if (i) { 565 free(line); 566 return 1; 567 } 568 break; 569 570 case _NG_NAME: 571 /* netgroup name */ 572 if (in_find(sl, name, host, user, domain)) { 573 free(line); 574 return 1; 575 } 576 break; 577 578 case _NG_ERROR: 579 free(line); 580 return 0; 581 582 default: 583 abort(); 584 } 585 } 586} 587 588/* 589 * _ng_makekey(): Make a key from the two names given. The key is of the form 590 * <name1>.<name2> Names strings are replaced with * if they are empty; 591 * Returns NULL if there's a problem. 592 */ 593char * 594_ng_makekey(const char *s1, const char *s2, size_t len) 595{ 596 char *buf; 597 598 /* s1 may be NULL */ 599 /* s2 may be NULL */ 600 601 buf = malloc(len); 602 if (buf != NULL) 603 (void)snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2)); 604 return buf; 605} 606 607void 608_ng_print(char *buf, size_t len, const struct netgroup *ng) 609{ 610 _DIAGASSERT(buf != NULL); 611 _DIAGASSERT(ng != NULL); 612 613 (void)snprintf(buf, len, "(%s,%s,%s)", _NG_EMPTY(ng->ng_host), 614 _NG_EMPTY(ng->ng_user), _NG_EMPTY(ng->ng_domain)); 615} 616 617 618/* 619 * in_lookup1(): Fast lookup for a key in the appropriate map 620 */ 621static char * 622in_lookup1(const char *key, const char *domain, 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 */ 645static int 646in_lookup(const char *group, const char *key, const char *domain, int map) 647{ 648 size_t len; 649 char *ptr, *line; 650 651 _DIAGASSERT(group != NULL); 652 /* key may be NULL */ 653 /* domain may be NULL */ 654 655 if (domain != NULL) { 656 /* Domain specified; look in "group.domain" and "*.domain" */ 657 if ((line = in_lookup1(key, domain, map)) == NULL) 658 line = in_lookup1(NULL, domain, map); 659 } else 660 line = NULL; 661 662 if (line == NULL) { 663 /* 664 * domain not specified or domain lookup failed; look in 665 * "group.*" and "*.*" 666 */ 667 if (((line = in_lookup1(key, NULL, map)) == NULL) && 668 ((line = in_lookup1(NULL, NULL, map)) == NULL)) 669 return 0; 670 } 671 672 len = strlen(group); 673 674 for (ptr = line; (ptr = strstr(ptr, group)) != NULL;) 675 /* Make sure we did not find a substring */ 676 if ((ptr != line && ptr[-1] != ',') || 677 (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL)) 678 ptr++; 679 else { 680 free(line); 681 return 1; 682 } 683 684 free(line); 685 return 0; 686} 687 688 689void 690endnetgrent(void) 691{ 692 for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) { 693 _nghead = _nglist->ng_next; 694 if (_nglist->ng_host != NULL) 695 free(_nglist->ng_host); 696 if (_nglist->ng_user != NULL) 697 free(_nglist->ng_user); 698 if (_nglist->ng_domain != NULL) 699 free(_nglist->ng_domain); 700 free(_nglist); 701 } 702 703 if (_ng_db) { 704 (void)(*_ng_db->close)(_ng_db); 705 _ng_db = NULL; 706 } 707} 708 709 710void 711setnetgrent(const char *ng) 712{ 713 StringList *sl; 714 char *ng_copy; 715 716 _DIAGASSERT(ng != NULL); 717 718 sl = sl_init(); 719 if (sl == NULL) 720 return; 721 722 /* Cleanup any previous storage */ 723 if (_nghead != NULL) 724 endnetgrent(); 725 726 if (_ng_db == NULL) 727 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL); 728 729 ng_copy = strdup(ng); 730 if (ng_copy != NULL) 731 addgroup(sl, ng_copy); 732 _nghead = _nglist; 733 sl_free(sl, 1); 734} 735 736 737int 738getnetgrent(const char **host, const char **user, const char **domain) 739{ 740 _DIAGASSERT(host != NULL); 741 _DIAGASSERT(user != NULL); 742 _DIAGASSERT(domain != NULL); 743 744 if (_nglist == NULL) 745 return 0; 746 747 *host = _nglist->ng_host; 748 *user = _nglist->ng_user; 749 *domain = _nglist->ng_domain; 750 751 _nglist = _nglist->ng_next; 752 753 return 1; 754} 755 756 757int 758innetgr(const char *grp, const char *host, const char *user, const char *domain) 759{ 760 int found; 761 StringList *sl; 762 char *grcpy; 763 764 _DIAGASSERT(grp != NULL); 765 /* host may be NULL */ 766 /* user may be NULL */ 767 /* domain may be NULL */ 768 769 if (_ng_db == NULL) 770 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL); 771 772 /* Try the fast lookup first */ 773 if (host != NULL && user == NULL) { 774 if (in_lookup(grp, host, domain, _NG_KEYBYHOST)) 775 return 1; 776 } else if (host == NULL && user != NULL) { 777 if (in_lookup(grp, user, domain, _NG_KEYBYUSER)) 778 return 1; 779 } 780 /* If a domainname is given, we would have found a match */ 781 if (domain != NULL) 782 return 0; 783 784 /* Too bad need the slow recursive way */ 785 sl = sl_init(); 786 if (sl == NULL) 787 return 0; 788 if ((grcpy = strdup(grp)) == NULL) { 789 sl_free(sl, 1); 790 return 0; 791 } 792 found = in_find(sl, grcpy, host, user, domain); 793 sl_free(sl, 1); 794 795 return found; 796} 797