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 46static const char _ngstar[] = "*"; 47static struct netgroup *_nghead = (struct netgroup *)NULL; 48static struct netgroup *_nglist = (struct netgroup *)NULL; 49static DB *_ng_db; 50 51/* 52 * Simple string list 53 */ 54struct stringlist { 55 char **sl_str; 56 size_t sl_max; 57 size_t sl_cur; 58}; 59 60static struct stringlist *_ng_sl_init(void); 61static int _ng_sl_add(struct stringlist *, char *); 62static void _ng_sl_free(struct stringlist *, int); 63static char *_ng_sl_find(struct stringlist *, char *); 64static char *_ng_makekey(const char *, const char *, size_t); 65static int _ng_parse(char **, char **, struct netgroup **); 66static void _ng_print(char *, size_t, const struct netgroup *); 67 68static int getstring(char **, int, char **); 69static struct netgroup *getnetgroup(char **); 70static int lookup(const char *, char *, char **, int); 71static void addgroup(char *, struct stringlist *, char *); 72static int in_check(const char *, const char *, 73 const char *, struct netgroup *); 74static int in_find(char *, struct stringlist *, 75 char *, const char *, const char *, const char *); 76static char *in_lookup1(const char *, const char *, 77 const char *, int); 78static int in_lookup(const char *, const char *, 79 const char *, const char *, int); 80 81/* 82 * _ng_sl_init(): Initialize a string list 83 */ 84static 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 */ 105static 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 */ 128static 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 */ 144static 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 */ 161static int 162getstring(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 */ 203static struct netgroup * 204getnetgroup(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 230baddomain: 231 free(ng->ng_user); 232baduser: 233 free(ng->ng_host); 234badhost: 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 */ 244static int 245lookup(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 */ 315static 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 */ 355static void 356addgroup(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 */ 411static int 412in_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 */ 434static int 435in_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 */ 503static 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 521static 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 */ 533static char * 534in_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 */ 554static int 555in_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 595void 596endnetgrent(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} 611DEF_WEAK(endnetgrent); 612 613 614void 615setnetgrent(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} 653DEF_WEAK(setnetgrent); 654 655 656int 657getnetgrent(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} 670DEF_WEAK(getnetgrent); 671 672 673int 674innetgr(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} 726DEF_WEAK(innetgr); 727