getnetgrent.c revision 1.30
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 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 89static 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 */ 101static int 102getstring(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 */ 147static struct netgroup * 148getnetgroup(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 179baddomain: 180 if (ng->ng_user) 181 free(ng->ng_user); 182baduser: 183 if (ng->ng_host) 184 free(ng->ng_host); 185badhost: 186 free(ng); 187 return NULL; 188} 189 190 191static int _local_lookup(void *, void *, va_list); 192 193/*ARGSUSED*/ 194static 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 239static int _nis_lookup(void *, void *, va_list); 240 241/*ARGSUSED*/ 242static 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 */ 303static int 304lookup(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 */ 328int 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 */ 377static int 378addgroup(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 */ 444static int 445in_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 */ 475static int 476in_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 */ 560char * 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 574void 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 */ 588static char * 589in_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 */ 612static int 613in_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 656void 657endnetgrent(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 677void 678setnetgrent(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 704int 705getnetgrent(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 724int 725innetgr(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