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