1 /* $OpenBSD: getnetgrent.c,v 1.32 2024/01/22 17:21:52 deraadt 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 #ifdef DEBUG_NG 521 static 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 #endif 528 529 530 /* 531 * in_lookup1(): Fast lookup for a key in the appropriate map 532 */ 533 static char * 534 in_lookup1(const char *ypdom, const char *key, const char *domain, int map) 535 { 536 char *line; 537 size_t len; 538 char *ptr; 539 int res; 540 541 len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2; 542 ptr = _ng_makekey(key, domain, len); 543 if (ptr == NULL) 544 return NULL; 545 res = lookup(ypdom, ptr, &line, map); 546 free(ptr); 547 return res ? line : NULL; 548 } 549 550 551 /* 552 * in_lookup(): Fast lookup for a key in the appropriate map 553 */ 554 static int 555 in_lookup(const char *ypdom, const char *group, const char *key, 556 const char *domain, int map) 557 { 558 size_t len; 559 char *ptr, *line; 560 561 if (domain != NULL) { 562 /* Domain specified; look in "group.domain" and "*.domain" */ 563 if ((line = in_lookup1(ypdom, key, domain, map)) == NULL) 564 line = in_lookup1(ypdom, NULL, domain, map); 565 } else 566 line = NULL; 567 568 if (line == NULL) { 569 /* 570 * domain not specified or domain lookup failed; look in 571 * "group.*" and "*.*" 572 */ 573 if (((line = in_lookup1(ypdom, key, NULL, map)) == NULL) && 574 ((line = in_lookup1(ypdom, NULL, NULL, map)) == NULL)) 575 return 0; 576 } 577 578 len = strlen(group); 579 580 for (ptr = line; (ptr = strstr(ptr, group)) != NULL;) 581 /* Make sure we did not find a substring */ 582 if ((ptr != line && ptr[-1] != ',') || 583 (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL)) 584 ptr++; 585 else { 586 free(line); 587 return 1; 588 } 589 590 free(line); 591 return 0; 592 } 593 594 595 void 596 endnetgrent(void) 597 { 598 for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) { 599 _nghead = _nglist->ng_next; 600 free(_nglist->ng_host); 601 free(_nglist->ng_user); 602 free(_nglist->ng_domain); 603 free(_nglist); 604 } 605 606 if (_ng_db) { 607 (void) (_ng_db->close) (_ng_db); 608 _ng_db = NULL; 609 } 610 } 611 DEF_WEAK(endnetgrent); 612 613 614 void 615 setnetgrent(const char *ng) 616 { 617 struct stringlist *sl; 618 #ifdef YP 619 static char *__ypdomain; 620 char *line = NULL; 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 = __hash_open(_PATH_NETGROUP_DB, O_RDONLY, 0, NULL, 0); 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 if (!__ypdomain) 642 yp_get_default_domain(&__ypdomain); 643 ypdom = __ypdomain; 644 } 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 static char *__ypdomain; 679 char *line = NULL; 680 #endif 681 int found; 682 struct stringlist *sl; 683 684 if (_ng_db == NULL) 685 _ng_db = __hash_open(_PATH_NETGROUP_DB, O_RDONLY, 0, NULL, 0); 686 687 #ifdef YP 688 /* 689 * We use yp if there is a "+" in the netgroup file, or if there is 690 * no netgroup file at all 691 */ 692 if (_ng_db == NULL || lookup(NULL, "+", &line, _NG_KEYBYNAME) == 0) { 693 if (!__ypdomain) 694 yp_get_default_domain(&__ypdomain); 695 ypdom = __ypdomain; 696 } 697 698 free(line); 699 #endif 700 701 /* Try the fast lookup first */ 702 if (host != NULL && user == NULL) { 703 if (in_lookup(ypdom, grp, host, domain, _NG_KEYBYHOST)) 704 return 1; 705 } else if (host == NULL && user != NULL) { 706 if (in_lookup(ypdom, grp, user, domain, _NG_KEYBYUSER)) 707 return 1; 708 } 709 710 /* Too bad need the slow recursive way */ 711 sl = _ng_sl_init(); 712 if (sl == NULL) 713 return 0; 714 715 grpdup = strdup(grp); 716 if (grpdup == NULL) { 717 _ng_sl_free(sl, 1); 718 return 0; 719 } 720 721 found = in_find(ypdom, sl, grpdup, host, user, domain); 722 _ng_sl_free(sl, 1); 723 724 return found; 725 } 726 DEF_WEAK(innetgr); 727