1 /* 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #if defined(LIBC_SCCS) && !defined(lint) 34 static char sccsid[] = "@(#)getnetgrent.c 8.2 (Berkeley) 4/27/95"; 35 #endif /* LIBC_SCCS and not lint */ 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 #include <ctype.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #ifdef YP 46 /* 47 * Notes: 48 * We want to be able to use NIS netgroups properly while retaining 49 * the ability to use a local /etc/netgroup file. Unfortunately, you 50 * can't really do both at the same time - at least, not efficiently. 51 * NetBSD deals with this problem by creating a netgroup database 52 * using Berkeley DB (just like the password database) that allows 53 * for lookups using netgroup, netgroup.byuser or netgroup.byhost 54 * searches. This is a neat idea, but I don't have time to implement 55 * something like that now. (I think ultimately it would be nice 56 * if we DB-fied the group and netgroup stuff all in one shot, but 57 * for now I'm satisfied just to have something that works well 58 * without requiring massive code changes.) 59 * 60 * Therefore, to still permit the use of the local file and maintain 61 * optimum NIS performance, we allow for the following conditions: 62 * 63 * - If /etc/netgroup does not exist and NIS is turned on, we use 64 * NIS netgroups only. 65 * 66 * - If /etc/netgroup exists but is empty, we use NIS netgroups 67 * only. 68 * 69 * - If /etc/netgroup exists and contains _only_ a '+', we use 70 * NIS netgroups only. 71 * 72 * - If /etc/netgroup exists, contains locally defined netgroups 73 * and a '+', we use a mixture of NIS and the local entries. 74 * This method should return the same NIS data as just using 75 * NIS alone, but it will be slower if the NIS netgroup database 76 * is large (innetgr() in particular will suffer since extra 77 * processing has to be done in order to determine memberships 78 * using just the raw netgroup data). 79 * 80 * - If /etc/netgroup exists and contains only locally defined 81 * netgroup entries, we use just those local entries and ignore 82 * NIS (this is the original, pre-NIS behavior). 83 */ 84 85 #include <rpc/rpc.h> 86 #include <rpcsvc/yp_prot.h> 87 #include <rpcsvc/ypclnt.h> 88 #include <sys/param.h> 89 #include <sys/stat.h> 90 #include <sys/errno.h> 91 static char *_netgr_yp_domain; 92 int _use_only_yp; 93 static int _netgr_yp_enabled; 94 static int _yp_innetgr; 95 #endif 96 97 #ifndef _PATH_NETGROUP 98 #define _PATH_NETGROUP "/etc/netgroup" 99 #endif 100 101 /* 102 * Static Variables and functions used by setnetgrent(), getnetgrent() and 103 * endnetgrent(). 104 * There are two linked lists: 105 * - linelist is just used by setnetgrent() to parse the net group file via. 106 * parse_netgrp() 107 * - netgrp is the list of entries for the current netgroup 108 */ 109 struct linelist { 110 struct linelist *l_next; /* Chain ptr. */ 111 int l_parsed; /* Flag for cycles */ 112 char *l_groupname; /* Name of netgroup */ 113 char *l_line; /* Netgroup entrie(s) to be parsed */ 114 }; 115 116 struct netgrp { 117 struct netgrp *ng_next; /* Chain ptr */ 118 char *ng_str[3]; /* Field pointers, see below */ 119 }; 120 #define NG_HOST 0 /* Host name */ 121 #define NG_USER 1 /* User name */ 122 #define NG_DOM 2 /* and Domain name */ 123 124 static struct linelist *linehead = (struct linelist *)0; 125 static struct netgrp *nextgrp = (struct netgrp *)0; 126 static struct { 127 struct netgrp *gr; 128 char *grname; 129 } grouphead = { 130 (struct netgrp *)0, 131 (char *)0, 132 }; 133 static FILE *netf = (FILE *)0; 134 135 static int parse_netgrp(const char *); 136 static struct linelist *read_for_group(const char *); 137 void setnetgrent(const char *); 138 void endnetgrent(void); 139 int getnetgrent(char **, char **, char **); 140 int innetgr(const char *, const char *, const char *, const char *); 141 142 #define LINSIZ 1024 /* Length of netgroup file line */ 143 144 /* 145 * setnetgrent() 146 * Parse the netgroup file looking for the netgroup and build the list 147 * of netgrp structures. Let parse_netgrp() and read_for_group() do 148 * most of the work. 149 */ 150 void 151 setnetgrent(const char *group) 152 { 153 #ifdef YP 154 struct stat _yp_statp; 155 char _yp_plus; 156 #endif 157 158 /* Sanity check */ 159 160 if (group == NULL || !strlen(group)) 161 return; 162 163 if (grouphead.gr == NULL || strcmp(group, grouphead.grname)) { 164 endnetgrent(); 165 #ifdef YP 166 /* Presumed guilty until proven innocent. */ 167 _use_only_yp = 0; 168 /* 169 * If /etc/netgroup doesn't exist or is empty, 170 * use NIS exclusively. 171 */ 172 if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) && 173 errno == ENOENT) || _yp_statp.st_size == 0) 174 _use_only_yp = _netgr_yp_enabled = 1; 175 if ((netf = fopen(_PATH_NETGROUP,"re")) != NULL ||_use_only_yp){ 176 /* 177 * Icky: grab the first character of the netgroup file 178 * and turn on NIS if it's a '+'. rewind the stream 179 * afterwards so we don't goof up read_for_group() later. 180 */ 181 if (netf) { 182 fscanf(netf, "%c", &_yp_plus); 183 rewind(netf); 184 if (_yp_plus == '+') 185 _use_only_yp = _netgr_yp_enabled = 1; 186 } 187 /* 188 * If we were called specifically for an innetgr() 189 * lookup and we're in NIS-only mode, short-circuit 190 * parse_netgroup() and cut directly to the chase. 191 */ 192 if (_use_only_yp && _yp_innetgr) { 193 /* dohw! */ 194 if (netf != NULL) 195 fclose(netf); 196 return; 197 } 198 #else 199 if ((netf = fopen(_PATH_NETGROUP, "re"))) { 200 #endif 201 if (parse_netgrp(group)) 202 endnetgrent(); 203 else { 204 grouphead.grname = strdup(group); 205 } 206 if (netf) 207 fclose(netf); 208 } 209 } 210 nextgrp = grouphead.gr; 211 } 212 213 /* 214 * Get the next netgroup off the list. 215 */ 216 int 217 getnetgrent(char **hostp, char **userp, char **domp) 218 { 219 #ifdef YP 220 _yp_innetgr = 0; 221 #endif 222 223 if (nextgrp) { 224 *hostp = nextgrp->ng_str[NG_HOST]; 225 *userp = nextgrp->ng_str[NG_USER]; 226 *domp = nextgrp->ng_str[NG_DOM]; 227 nextgrp = nextgrp->ng_next; 228 return (1); 229 } 230 return (0); 231 } 232 233 /* 234 * endnetgrent() - cleanup 235 */ 236 void 237 endnetgrent(void) 238 { 239 struct linelist *lp, *olp; 240 struct netgrp *gp, *ogp; 241 242 lp = linehead; 243 while (lp) { 244 olp = lp; 245 lp = lp->l_next; 246 free(olp->l_groupname); 247 free(olp->l_line); 248 free(olp); 249 } 250 linehead = NULL; 251 if (grouphead.grname) { 252 free(grouphead.grname); 253 grouphead.grname = NULL; 254 } 255 gp = grouphead.gr; 256 while (gp) { 257 ogp = gp; 258 gp = gp->ng_next; 259 free(ogp->ng_str[NG_HOST]); 260 free(ogp->ng_str[NG_USER]); 261 free(ogp->ng_str[NG_DOM]); 262 free(ogp); 263 } 264 grouphead.gr = NULL; 265 nextgrp = NULL; 266 #ifdef YP 267 _netgr_yp_enabled = 0; 268 #endif 269 } 270 271 #ifdef YP 272 static int 273 _listmatch(const char *list, const char *group, int len) 274 { 275 const char *ptr = list; 276 const char *cptr; 277 int glen = strlen(group); 278 279 /* skip possible leading whitespace */ 280 while (isspace((unsigned char)*ptr)) 281 ptr++; 282 283 while (ptr < list + len) { 284 cptr = ptr; 285 while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr)) 286 ptr++; 287 if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr)) 288 return (1); 289 while (*ptr == ',' || isspace((unsigned char)*ptr)) 290 ptr++; 291 } 292 293 return (0); 294 } 295 296 static int 297 _revnetgr_lookup(char* lookupdom, char* map, const char* str, 298 const char* dom, const char* group) 299 { 300 int y, rv, rot; 301 char key[MAXHOSTNAMELEN]; 302 char *result; 303 int resultlen; 304 305 for (rot = 0; ; rot++) { 306 switch (rot) { 307 case 0: 308 snprintf(key, MAXHOSTNAMELEN, "%s.%s", str, 309 dom ? dom : lookupdom); 310 break; 311 case 1: 312 snprintf(key, MAXHOSTNAMELEN, "%s.*", str); 313 break; 314 case 2: 315 snprintf(key, MAXHOSTNAMELEN, "*.%s", 316 dom ? dom : lookupdom); 317 break; 318 case 3: 319 snprintf(key, MAXHOSTNAMELEN, "*.*"); 320 break; 321 default: 322 return (0); 323 } 324 y = yp_match(lookupdom, map, key, strlen(key), &result, 325 &resultlen); 326 if (y == 0) { 327 rv = _listmatch(result, group, resultlen); 328 free(result); 329 if (rv) 330 return (1); 331 } else if (y != YPERR_KEY) { 332 /* 333 * If we get an error other than 'no 334 * such key in map' then something is 335 * wrong and we should stop the search. 336 */ 337 return (-1); 338 } 339 } 340 } 341 #endif 342 343 /* 344 * Search for a match in a netgroup. 345 */ 346 int 347 innetgr(const char *group, const char *host, const char *user, const char *dom) 348 { 349 char *hst, *usr, *dm; 350 /* Sanity check */ 351 352 if (group == NULL || !strlen(group)) 353 return (0); 354 355 #ifdef YP 356 _yp_innetgr = 1; 357 #endif 358 setnetgrent(group); 359 #ifdef YP 360 _yp_innetgr = 0; 361 /* 362 * If we're in NIS-only mode, do the search using 363 * NIS 'reverse netgroup' lookups. 364 * 365 * What happens with 'reverse netgroup' lookups: 366 * 367 * 1) try 'reverse netgroup' lookup 368 * 1.a) if host is specified and user is null: 369 * look in netgroup.byhost 370 * (try host.domain, host.*, *.domain or *.*) 371 * if found, return yes 372 * 1.b) if user is specified and host is null: 373 * look in netgroup.byuser 374 * (try host.domain, host.*, *.domain or *.*) 375 * if found, return yes 376 * 1.c) if both host and user are specified, 377 * don't do 'reverse netgroup' lookup. It won't work. 378 * 1.d) if neither host ane user are specified (why?!?) 379 * don't do 'reverse netgroup' lookup either. 380 * 2) if domain is specified and 'reverse lookup' is done: 381 * 'reverse lookup' was authoritative. bye bye. 382 * 3) otherwise, too bad, try it the slow way. 383 */ 384 if (_use_only_yp && (host == NULL) != (user == NULL)) { 385 int ret; 386 if(yp_get_default_domain(&_netgr_yp_domain)) 387 return (0); 388 ret = _revnetgr_lookup(_netgr_yp_domain, 389 host?"netgroup.byhost":"netgroup.byuser", 390 host?host:user, dom, group); 391 if (ret == 1) 392 return (1); 393 else if (ret == 0 && dom != NULL) 394 return (0); 395 } 396 397 setnetgrent(group); 398 #endif /* YP */ 399 400 while (getnetgrent(&hst, &usr, &dm)) 401 if ((host == NULL || hst == NULL || !strcmp(host, hst)) && 402 (user == NULL || usr == NULL || !strcmp(user, usr)) && 403 ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { 404 endnetgrent(); 405 return (1); 406 } 407 endnetgrent(); 408 return (0); 409 } 410 411 /* 412 * Parse the netgroup file setting up the linked lists. 413 */ 414 static int 415 parse_netgrp(const char *group) 416 { 417 struct netgrp *grp; 418 struct linelist *lp = linehead; 419 char **ng; 420 char *epos, *gpos, *pos, *spos; 421 int freepos, len, strpos; 422 #ifdef DEBUG 423 int fields; 424 #endif 425 426 /* 427 * First, see if the line has already been read in. 428 */ 429 while (lp) { 430 if (!strcmp(group, lp->l_groupname)) 431 break; 432 lp = lp->l_next; 433 } 434 if (lp == NULL && (lp = read_for_group(group)) == NULL) 435 return (1); 436 if (lp->l_parsed) { 437 #ifdef DEBUG 438 /* 439 * This error message is largely superflous since the 440 * code handles the error condition sucessfully, and 441 * spewing it out from inside libc can actually hose 442 * certain programs. 443 */ 444 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); 445 #endif 446 return (1); 447 } else 448 lp->l_parsed = 1; 449 pos = lp->l_line; 450 /* Watch for null pointer dereferences, dammit! */ 451 while (pos != NULL && *pos != '\0') { 452 if (*pos == '(') { 453 grp = malloc(sizeof(*grp)); 454 if (grp == NULL) 455 return (1); 456 ng = grp->ng_str; 457 bzero(grp, sizeof(*grp)); 458 pos++; 459 gpos = strsep(&pos, ")"); 460 #ifdef DEBUG 461 fields = 0; 462 #endif 463 for (strpos = 0; strpos < 3; strpos++) { 464 if ((spos = strsep(&gpos, ",")) == NULL) { 465 /* 466 * All other systems I've tested 467 * return NULL for empty netgroup 468 * fields. It's up to user programs 469 * to handle the NULLs appropriately. 470 */ 471 ng[strpos] = NULL; 472 continue; 473 } 474 #ifdef DEBUG 475 fields++; 476 #endif 477 while (*spos == ' ' || *spos == '\t') 478 spos++; 479 if ((epos = strpbrk(spos, " \t"))) { 480 *epos = '\0'; 481 len = epos - spos; 482 } else 483 len = strlen(spos); 484 if (len <= 0) 485 continue; 486 ng[strpos] = malloc(len + 1); 487 if (ng[strpos] == NULL) { 488 for (freepos = 0; freepos < strpos; 489 freepos++) 490 free(ng[freepos]); 491 free(grp); 492 return (1); 493 } 494 bcopy(spos, ng[strpos], len + 1); 495 } 496 grp->ng_next = grouphead.gr; 497 grouphead.gr = grp; 498 #ifdef DEBUG 499 /* 500 * Note: on other platforms, malformed netgroup 501 * entries are not normally flagged. While we 502 * can catch bad entries and report them, we should 503 * stay silent by default for compatibility's sake. 504 */ 505 if (fields < 3) { 506 fprintf(stderr, 507 "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", 508 ng[NG_HOST] == NULL ? "" : ng[NG_HOST], 509 ng[NG_USER] == NULL ? "" : ",", 510 ng[NG_USER] == NULL ? "" : ng[NG_USER], 511 ng[NG_DOM] == NULL ? "" : ",", 512 ng[NG_DOM] == NULL ? "" : ng[NG_DOM], 513 lp->l_groupname); 514 } 515 #endif 516 } else { 517 spos = strsep(&pos, ", \t"); 518 if (parse_netgrp(spos)) 519 continue; 520 } 521 if (pos == NULL) 522 break; 523 while (*pos == ' ' || *pos == ',' || *pos == '\t') 524 pos++; 525 } 526 return (0); 527 } 528 529 /* 530 * Read the netgroup file and save lines until the line for the netgroup 531 * is found. Return 1 if eof is encountered. 532 */ 533 static struct linelist * 534 read_for_group(const char *group) 535 { 536 char *linep, *olinep, *pos, *spos; 537 int len, olen; 538 int cont; 539 struct linelist *lp; 540 char line[LINSIZ + 2]; 541 #ifdef YP 542 char *result; 543 int resultlen; 544 linep = NULL; 545 546 while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) { 547 if (_netgr_yp_enabled) { 548 if(!_netgr_yp_domain) 549 if(yp_get_default_domain(&_netgr_yp_domain)) 550 continue; 551 if (yp_match(_netgr_yp_domain, "netgroup", group, 552 strlen(group), &result, &resultlen)) { 553 free(result); 554 if (_use_only_yp) 555 return ((struct linelist *)0); 556 else { 557 _netgr_yp_enabled = 0; 558 continue; 559 } 560 } 561 snprintf(line, LINSIZ, "%s %s", group, result); 562 free(result); 563 } 564 #else 565 linep = NULL; 566 while (fgets(line, LINSIZ, netf) != NULL) { 567 #endif 568 pos = (char *)&line; 569 #ifdef YP 570 if (*pos == '+') { 571 _netgr_yp_enabled = 1; 572 continue; 573 } 574 #endif 575 if (*pos == '#') 576 continue; 577 while (*pos == ' ' || *pos == '\t') 578 pos++; 579 spos = pos; 580 while (*pos != ' ' && *pos != '\t' && *pos != '\n' && 581 *pos != '\0') 582 pos++; 583 len = pos - spos; 584 while (*pos == ' ' || *pos == '\t') 585 pos++; 586 if (*pos != '\n' && *pos != '\0') { 587 lp = (struct linelist *)malloc(sizeof (*lp)); 588 if (lp == NULL) 589 return (NULL); 590 lp->l_parsed = 0; 591 lp->l_groupname = (char *)malloc(len + 1); 592 if (lp->l_groupname == NULL) { 593 free(lp); 594 return (NULL); 595 } 596 bcopy(spos, lp->l_groupname, len); 597 *(lp->l_groupname + len) = '\0'; 598 len = strlen(pos); 599 olen = 0; 600 601 /* 602 * Loop around handling line continuations. 603 */ 604 do { 605 if (*(pos + len - 1) == '\n') 606 len--; 607 if (*(pos + len - 1) == '\\') { 608 len--; 609 cont = 1; 610 } else 611 cont = 0; 612 if (len > 0) { 613 linep = malloc(olen + len + 1); 614 if (linep == NULL) { 615 free(lp->l_groupname); 616 free(lp); 617 return (NULL); 618 } 619 if (olen > 0) { 620 bcopy(olinep, linep, olen); 621 free(olinep); 622 } 623 bcopy(pos, linep + olen, len); 624 olen += len; 625 *(linep + olen) = '\0'; 626 olinep = linep; 627 } 628 if (cont) { 629 if (fgets(line, LINSIZ, netf)) { 630 pos = line; 631 len = strlen(pos); 632 } else 633 cont = 0; 634 } 635 } while (cont); 636 lp->l_line = linep; 637 lp->l_next = linehead; 638 linehead = lp; 639 640 /* 641 * If this is the one we wanted, we are done. 642 */ 643 if (!strcmp(lp->l_groupname, group)) 644 return (lp); 645 } 646 } 647 #ifdef YP 648 /* 649 * Yucky. The recursive nature of this whole mess might require 650 * us to make more than one pass through the netgroup file. 651 * This might be best left outside the #ifdef YP, but YP is 652 * defined by default anyway, so I'll leave it like this 653 * until I know better. 654 */ 655 rewind(netf); 656 #endif 657 return (NULL); 658 } 659