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