1 /* $OpenBSD: getnetgrent.c,v 1.30 2023/01/04 13:00:11 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <stdio.h> 31 #include <netgroup.h> 32 #include <string.h> 33 #include <fcntl.h> 34 #include <err.h> 35 #include <ctype.h> 36 #include <stdlib.h> 37 #include <db.h> 38 #ifdef YP 39 #include <rpcsvc/ypclnt.h> 40 #endif 41 42 #define _NG_STAR(s) (((s) == NULL || *(s) == '\0') ? _ngstar : s) 43 #define _NG_EMPTY(s) ((s) == NULL ? "" : s) 44 #define _NG_ISSPACE(p) (isspace((unsigned char) (p)) || (p) == '\n') 45 46 static const char _ngstar[] = "*"; 47 static struct netgroup *_nghead = (struct netgroup *)NULL; 48 static struct netgroup *_nglist = (struct netgroup *)NULL; 49 static DB *_ng_db; 50 51 /* 52 * Simple string list 53 */ 54 struct stringlist { 55 char **sl_str; 56 size_t sl_max; 57 size_t sl_cur; 58 }; 59 60 static struct stringlist *_ng_sl_init(void); 61 static int _ng_sl_add(struct stringlist *, char *); 62 static void _ng_sl_free(struct stringlist *, int); 63 static char *_ng_sl_find(struct stringlist *, char *); 64 static char *_ng_makekey(const char *, const char *, size_t); 65 static int _ng_parse(char **, char **, struct netgroup **); 66 static void _ng_print(char *, size_t, const struct netgroup *); 67 68 static int getstring(char **, int, char **); 69 static struct netgroup *getnetgroup(char **); 70 static int lookup(const char *, char *, char **, int); 71 static void addgroup(char *, struct stringlist *, char *); 72 static int in_check(const char *, const char *, 73 const char *, struct netgroup *); 74 static int in_find(char *, struct stringlist *, 75 char *, const char *, const char *, const char *); 76 static char *in_lookup1(const char *, const char *, 77 const char *, int); 78 static int in_lookup(const char *, const char *, 79 const char *, const char *, int); 80 81 /* 82 * _ng_sl_init(): Initialize a string list 83 */ 84 static struct stringlist * 85 _ng_sl_init(void) 86 { 87 struct stringlist *sl = malloc(sizeof(struct stringlist)); 88 if (sl == NULL) 89 return NULL; 90 91 sl->sl_cur = 0; 92 sl->sl_max = 20; 93 sl->sl_str = calloc(sl->sl_max, sizeof(char *)); 94 if (sl->sl_str == NULL) { 95 free(sl); 96 return NULL; 97 } 98 return sl; 99 } 100 101 102 /* 103 * _ng_sl_add(): Add an item to the string list 104 */ 105 static int 106 _ng_sl_add(struct stringlist *sl, char *name) 107 { 108 if (sl->sl_cur == sl->sl_max - 1) { 109 char **slstr; 110 111 sl->sl_max += 20; 112 slstr = reallocarray(sl->sl_str, sl->sl_max, sizeof(char *)); 113 if (slstr == NULL) { 114 free(sl->sl_str); 115 sl->sl_str = NULL; 116 return -1; 117 } 118 sl->sl_str = slstr; 119 } 120 sl->sl_str[sl->sl_cur++] = name; 121 return 0; 122 } 123 124 125 /* 126 * _ng_sl_free(): Free a stringlist 127 */ 128 static void 129 _ng_sl_free(struct stringlist *sl, int all) 130 { 131 size_t i; 132 133 if (all) 134 for (i = 0; i < sl->sl_cur; i++) 135 free(sl->sl_str[i]); 136 free(sl->sl_str); 137 free(sl); 138 } 139 140 141 /* 142 * sl_find(): Find a name in the string list 143 */ 144 static char * 145 _ng_sl_find(struct stringlist *sl, char *name) 146 { 147 size_t i; 148 149 for (i = 0; i < sl->sl_cur; i++) 150 if (strcmp(sl->sl_str[i], name) == 0) 151 return sl->sl_str[i]; 152 153 return NULL; 154 } 155 156 157 /* 158 * getstring(): Get a string delimited by the character, skipping leading and 159 * trailing blanks and advancing the pointer 160 */ 161 static int 162 getstring(char **pp, int del, char **str) 163 { 164 char *sp, *ep, *dp; 165 166 /* skip leading blanks */ 167 for (sp = *pp; *sp && _NG_ISSPACE(*sp); sp++) 168 continue; 169 170 /* accumulate till delimiter or space */ 171 for (ep = sp; *ep && *ep != del && !_NG_ISSPACE(*ep); ep++) 172 continue; 173 174 /* hunt for the delimiter */ 175 for (dp = ep; *dp && *dp != del && _NG_ISSPACE(*dp); dp++) 176 continue; 177 178 if (*dp != del) { 179 *str = NULL; 180 return 0; 181 } 182 183 *pp = ++dp; 184 185 del = (ep - sp) + 1; 186 if (del > 1) { 187 dp = malloc(del); 188 if (dp == NULL) 189 return 0; 190 memcpy(dp, sp, del); 191 dp[del - 1] = '\0'; 192 } else 193 dp = NULL; 194 195 *str = dp; 196 return 1; 197 } 198 199 200 /* 201 * getnetgroup(): Parse a netgroup, and advance the pointer 202 */ 203 static struct netgroup * 204 getnetgroup(char **pp) 205 { 206 struct netgroup *ng = malloc(sizeof(struct netgroup)); 207 208 if (ng == NULL) 209 return NULL; 210 211 (*pp)++; /* skip '(' */ 212 if (!getstring(pp, ',', &ng->ng_host)) 213 goto badhost; 214 215 if (!getstring(pp, ',', &ng->ng_user)) 216 goto baduser; 217 218 if (!getstring(pp, ')', &ng->ng_domain)) 219 goto baddomain; 220 221 #ifdef DEBUG_NG 222 { 223 char buf[1024]; 224 _ng_print(buf, sizeof(buf), ng); 225 fprintf(stderr, "netgroup %s\n", buf); 226 } 227 #endif 228 return ng; 229 230 baddomain: 231 free(ng->ng_user); 232 baduser: 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 static 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 free(ng->ng_host); 474 free(ng->ng_user); 475 free(ng->ng_domain); 476 free(ng); 477 if (i) { 478 free(line); 479 return 1; 480 } 481 break; 482 483 case _NG_NAME: 484 /* netgroup name */ 485 if (in_find(ypdom, sl, name, host, user, domain)) { 486 free(line); 487 return 1; 488 } 489 break; 490 491 case _NG_ERROR: 492 free(line); 493 return 0; 494 } 495 } 496 } 497 498 499 /* 500 * _ng_makekey(): Make a key from the two names given. The key is of the form 501 * <name1>.<name2> Names strings are replaced with * if they are empty; 502 */ 503 static char * 504 _ng_makekey(const char *s1, const char *s2, size_t len) 505 { 506 char *buf = malloc(len); 507 int ret; 508 509 if (buf == NULL) 510 return NULL; 511 ret = snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2)); 512 if (ret < 0 || ret >= len) { 513 free(buf); 514 return NULL; 515 } 516 517 return buf; 518 } 519 520 static void 521 _ng_print(char *buf, size_t len, const struct netgroup *ng) 522 { 523 (void) snprintf(buf, len, "(%s,%s,%s)", _NG_EMPTY(ng->ng_host), 524 _NG_EMPTY(ng->ng_user), _NG_EMPTY(ng->ng_domain)); 525 } 526 527 528 /* 529 * in_lookup1(): Fast lookup for a key in the appropriate map 530 */ 531 static char * 532 in_lookup1(const char *ypdom, const char *key, const char *domain, int map) 533 { 534 char *line; 535 size_t len; 536 char *ptr; 537 int res; 538 539 len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2; 540 ptr = _ng_makekey(key, domain, len); 541 if (ptr == NULL) 542 return NULL; 543 res = lookup(ypdom, ptr, &line, map); 544 free(ptr); 545 return res ? line : NULL; 546 } 547 548 549 /* 550 * in_lookup(): Fast lookup for a key in the appropriate map 551 */ 552 static int 553 in_lookup(const char *ypdom, const char *group, const char *key, 554 const char *domain, int map) 555 { 556 size_t len; 557 char *ptr, *line; 558 559 if (domain != NULL) { 560 /* Domain specified; look in "group.domain" and "*.domain" */ 561 if ((line = in_lookup1(ypdom, key, domain, map)) == NULL) 562 line = in_lookup1(ypdom, NULL, domain, map); 563 } else 564 line = NULL; 565 566 if (line == NULL) { 567 /* 568 * domain not specified or domain lookup failed; look in 569 * "group.*" and "*.*" 570 */ 571 if (((line = in_lookup1(ypdom, key, NULL, map)) == NULL) && 572 ((line = in_lookup1(ypdom, NULL, NULL, map)) == NULL)) 573 return 0; 574 } 575 576 len = strlen(group); 577 578 for (ptr = line; (ptr = strstr(ptr, group)) != NULL;) 579 /* Make sure we did not find a substring */ 580 if ((ptr != line && ptr[-1] != ',') || 581 (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL)) 582 ptr++; 583 else { 584 free(line); 585 return 1; 586 } 587 588 free(line); 589 return 0; 590 } 591 592 593 void 594 endnetgrent(void) 595 { 596 for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) { 597 _nghead = _nglist->ng_next; 598 free(_nglist->ng_host); 599 free(_nglist->ng_user); 600 free(_nglist->ng_domain); 601 free(_nglist); 602 } 603 604 if (_ng_db) { 605 (void) (_ng_db->close) (_ng_db); 606 _ng_db = NULL; 607 } 608 } 609 DEF_WEAK(endnetgrent); 610 611 612 void 613 setnetgrent(const char *ng) 614 { 615 struct stringlist *sl; 616 #ifdef YP 617 static char *__ypdomain; 618 char *line = NULL; 619 #endif 620 char *ng_copy, *ypdom = NULL; 621 622 /* Cleanup any previous storage */ 623 if (_nghead != NULL) 624 endnetgrent(); 625 626 sl = _ng_sl_init(); 627 if (sl == NULL) 628 return; 629 630 if (_ng_db == NULL) 631 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL); 632 633 #ifdef YP 634 /* 635 * We use yp if there is a "+" in the netgroup file, or if there is 636 * no netgroup file at all 637 */ 638 if (_ng_db == NULL || lookup(NULL, "+", &line, _NG_KEYBYNAME) == 0) { 639 if (!__ypdomain) 640 yp_get_default_domain(&__ypdomain); 641 ypdom = __ypdomain; 642 } 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 DEF_WEAK(setnetgrent); 652 653 654 int 655 getnetgrent(const char **host, const char **user, const char **domain) 656 { 657 if (_nglist == NULL) 658 return 0; 659 660 *host = _nglist->ng_host; 661 *user = _nglist->ng_user; 662 *domain = _nglist->ng_domain; 663 664 _nglist = _nglist->ng_next; 665 666 return 1; 667 } 668 DEF_WEAK(getnetgrent); 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 static char *__ypdomain; 677 char *line = NULL; 678 #endif 679 int found; 680 struct stringlist *sl; 681 682 if (_ng_db == NULL) 683 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL); 684 685 #ifdef YP 686 /* 687 * We use yp if there is a "+" in the netgroup file, or if there is 688 * no netgroup file at all 689 */ 690 if (_ng_db == NULL || lookup(NULL, "+", &line, _NG_KEYBYNAME) == 0) { 691 if (!__ypdomain) 692 yp_get_default_domain(&__ypdomain); 693 ypdom = __ypdomain; 694 } 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