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