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/types.h> 89 #include <sys/stat.h> 90 #include <sys/param.h> 91 #include <sys/errno.h> 92 static char *_netgr_yp_domain; 93 int _use_only_yp; 94 static int _netgr_yp_enabled; 95 static int _yp_innetgr; 96 #endif 97 98 #ifndef _PATH_NETGROUP 99 #define _PATH_NETGROUP "/etc/netgroup" 100 #endif 101 102 /* 103 * Static Variables and functions used by setnetgrent(), getnetgrent() and 104 * endnetgrent(). 105 * There are two linked lists: 106 * - linelist is just used by setnetgrent() to parse the net group file via. 107 * parse_netgrp() 108 * - netgrp is the list of entries for the current netgroup 109 */ 110 struct linelist { 111 struct linelist *l_next; /* Chain ptr. */ 112 int l_parsed; /* Flag for cycles */ 113 char *l_groupname; /* Name of netgroup */ 114 char *l_line; /* Netgroup entrie(s) to be parsed */ 115 }; 116 117 struct netgrp { 118 struct netgrp *ng_next; /* Chain ptr */ 119 char *ng_str[3]; /* Field pointers, see below */ 120 }; 121 #define NG_HOST 0 /* Host name */ 122 #define NG_USER 1 /* User name */ 123 #define NG_DOM 2 /* and Domain name */ 124 125 static struct linelist *linehead = (struct linelist *)0; 126 static struct netgrp *nextgrp = (struct netgrp *)0; 127 static struct { 128 struct netgrp *gr; 129 char *grname; 130 } grouphead = { 131 (struct netgrp *)0, 132 (char *)0, 133 }; 134 static FILE *netf = (FILE *)0; 135 136 static int parse_netgrp(const char *); 137 static struct linelist *read_for_group(const char *); 138 void setnetgrent(const char *); 139 void endnetgrent(void); 140 int getnetgrent(char **, char **, char **); 141 int innetgr(const char *, const char *, const char *, const char *); 142 143 #define LINSIZ 1024 /* Length of netgroup file line */ 144 145 /* 146 * setnetgrent() 147 * Parse the netgroup file looking for the netgroup and build the list 148 * of netgrp structures. Let parse_netgrp() and read_for_group() do 149 * most of the work. 150 */ 151 void 152 setnetgrent(const char *group) 153 { 154 #ifdef YP 155 struct stat _yp_statp; 156 char _yp_plus; 157 #endif 158 159 /* Sanity check */ 160 161 if (group == NULL || !strlen(group)) 162 return; 163 164 if (grouphead.gr == NULL || strcmp(group, grouphead.grname)) { 165 endnetgrent(); 166 #ifdef YP 167 /* Presumed guilty until proven innocent. */ 168 _use_only_yp = 0; 169 /* 170 * If /etc/netgroup doesn't exist or is empty, 171 * use NIS exclusively. 172 */ 173 if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) && 174 errno == ENOENT) || _yp_statp.st_size == 0) 175 _use_only_yp = _netgr_yp_enabled = 1; 176 if ((netf = fopen(_PATH_NETGROUP,"re")) != NULL ||_use_only_yp){ 177 /* 178 * Icky: grab the first character of the netgroup file 179 * and turn on NIS if it's a '+'. rewind the stream 180 * afterwards so we don't goof up read_for_group() later. 181 */ 182 if (netf) { 183 fscanf(netf, "%c", &_yp_plus); 184 rewind(netf); 185 if (_yp_plus == '+') 186 _use_only_yp = _netgr_yp_enabled = 1; 187 } 188 /* 189 * If we were called specifically for an innetgr() 190 * lookup and we're in NIS-only mode, short-circuit 191 * parse_netgroup() and cut directly to the chase. 192 */ 193 if (_use_only_yp && _yp_innetgr) { 194 /* dohw! */ 195 if (netf != NULL) 196 fclose(netf); 197 return; 198 } 199 #else 200 if ((netf = fopen(_PATH_NETGROUP, "re"))) { 201 #endif 202 if (parse_netgrp(group)) 203 endnetgrent(); 204 else { 205 grouphead.grname = strdup(group); 206 } 207 if (netf) 208 fclose(netf); 209 } 210 } 211 nextgrp = grouphead.gr; 212 } 213 214 /* 215 * Get the next netgroup off the list. 216 */ 217 int 218 getnetgrent(char **hostp, char **userp, char **domp) 219 { 220 #ifdef YP 221 _yp_innetgr = 0; 222 #endif 223 224 if (nextgrp) { 225 *hostp = nextgrp->ng_str[NG_HOST]; 226 *userp = nextgrp->ng_str[NG_USER]; 227 *domp = nextgrp->ng_str[NG_DOM]; 228 nextgrp = nextgrp->ng_next; 229 return (1); 230 } 231 return (0); 232 } 233 234 /* 235 * endnetgrent() - cleanup 236 */ 237 void 238 endnetgrent(void) 239 { 240 struct linelist *lp, *olp; 241 struct netgrp *gp, *ogp; 242 243 lp = linehead; 244 while (lp) { 245 olp = lp; 246 lp = lp->l_next; 247 free(olp->l_groupname); 248 free(olp->l_line); 249 free(olp); 250 } 251 linehead = NULL; 252 if (grouphead.grname) { 253 free(grouphead.grname); 254 grouphead.grname = NULL; 255 } 256 gp = grouphead.gr; 257 while (gp) { 258 ogp = gp; 259 gp = gp->ng_next; 260 free(ogp->ng_str[NG_HOST]); 261 free(ogp->ng_str[NG_USER]); 262 free(ogp->ng_str[NG_DOM]); 263 free(ogp); 264 } 265 grouphead.gr = NULL; 266 nextgrp = NULL; 267 #ifdef YP 268 _netgr_yp_enabled = 0; 269 #endif 270 } 271 272 #ifdef YP 273 static int 274 _listmatch(const char *list, const char *group, int len) 275 { 276 const char *ptr = list; 277 const char *cptr; 278 int glen = strlen(group); 279 280 /* skip possible leading whitespace */ 281 while (isspace((unsigned char)*ptr)) 282 ptr++; 283 284 while (ptr < list + len) { 285 cptr = ptr; 286 while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr)) 287 ptr++; 288 if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr)) 289 return (1); 290 while (*ptr == ',' || isspace((unsigned char)*ptr)) 291 ptr++; 292 } 293 294 return (0); 295 } 296 297 static int 298 _revnetgr_lookup(char* lookupdom, char* map, const char* str, 299 const char* dom, const char* group) 300 { 301 int y, rv, rot; 302 char key[MAXHOSTNAMELEN]; 303 char *result; 304 int resultlen; 305 306 for (rot = 0; ; rot++) { 307 switch (rot) { 308 case 0: 309 snprintf(key, MAXHOSTNAMELEN, "%s.%s", str, 310 dom ? dom : lookupdom); 311 break; 312 case 1: 313 snprintf(key, MAXHOSTNAMELEN, "%s.*", str); 314 break; 315 case 2: 316 snprintf(key, MAXHOSTNAMELEN, "*.%s", 317 dom ? dom : lookupdom); 318 break; 319 case 3: 320 snprintf(key, MAXHOSTNAMELEN, "*.*"); 321 break; 322 default: 323 return (0); 324 } 325 y = yp_match(lookupdom, map, key, strlen(key), &result, 326 &resultlen); 327 if (y == 0) { 328 rv = _listmatch(result, group, resultlen); 329 free(result); 330 if (rv) 331 return (1); 332 } else if (y != YPERR_KEY) { 333 /* 334 * If we get an error other than 'no 335 * such key in map' then something is 336 * wrong and we should stop the search. 337 */ 338 return (-1); 339 } 340 } 341 } 342 #endif 343 344 /* 345 * Search for a match in a netgroup. 346 */ 347 int 348 innetgr(const char *group, const char *host, const char *user, const char *dom) 349 { 350 char *hst, *usr, *dm; 351 /* Sanity check */ 352 353 if (group == NULL || !strlen(group)) 354 return (0); 355 356 #ifdef YP 357 _yp_innetgr = 1; 358 #endif 359 setnetgrent(group); 360 #ifdef YP 361 _yp_innetgr = 0; 362 /* 363 * If we're in NIS-only mode, do the search using 364 * NIS 'reverse netgroup' lookups. 365 * 366 * What happens with 'reverse netgroup' lookups: 367 * 368 * 1) try 'reverse netgroup' lookup 369 * 1.a) if host is specified and user is null: 370 * look in netgroup.byhost 371 * (try host.domain, host.*, *.domain or *.*) 372 * if found, return yes 373 * 1.b) if user is specified and host is null: 374 * look in netgroup.byuser 375 * (try host.domain, host.*, *.domain or *.*) 376 * if found, return yes 377 * 1.c) if both host and user are specified, 378 * don't do 'reverse netgroup' lookup. It won't work. 379 * 1.d) if neither host ane user are specified (why?!?) 380 * don't do 'reverse netgroup' lookup either. 381 * 2) if domain is specified and 'reverse lookup' is done: 382 * 'reverse lookup' was authoritative. bye bye. 383 * 3) otherwise, too bad, try it the slow way. 384 */ 385 if (_use_only_yp && (host == NULL) != (user == NULL)) { 386 int ret; 387 if(yp_get_default_domain(&_netgr_yp_domain)) 388 return (0); 389 ret = _revnetgr_lookup(_netgr_yp_domain, 390 host?"netgroup.byhost":"netgroup.byuser", 391 host?host:user, dom, group); 392 if (ret == 1) 393 return (1); 394 else if (ret == 0 && dom != NULL) 395 return (0); 396 } 397 398 setnetgrent(group); 399 #endif /* YP */ 400 401 while (getnetgrent(&hst, &usr, &dm)) 402 if ((host == NULL || hst == NULL || !strcmp(host, hst)) && 403 (user == NULL || usr == NULL || !strcmp(user, usr)) && 404 ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { 405 endnetgrent(); 406 return (1); 407 } 408 endnetgrent(); 409 return (0); 410 } 411 412 /* 413 * Parse the netgroup file setting up the linked lists. 414 */ 415 static int 416 parse_netgrp(const char *group) 417 { 418 struct netgrp *grp; 419 struct linelist *lp = linehead; 420 char **ng; 421 char *epos, *gpos, *pos, *spos; 422 int freepos, len, strpos; 423 #ifdef DEBUG 424 int fields; 425 #endif 426 427 /* 428 * First, see if the line has already been read in. 429 */ 430 while (lp) { 431 if (!strcmp(group, lp->l_groupname)) 432 break; 433 lp = lp->l_next; 434 } 435 if (lp == NULL && (lp = read_for_group(group)) == NULL) 436 return (1); 437 if (lp->l_parsed) { 438 #ifdef DEBUG 439 /* 440 * This error message is largely superflous since the 441 * code handles the error condition sucessfully, and 442 * spewing it out from inside libc can actually hose 443 * certain programs. 444 */ 445 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); 446 #endif 447 return (1); 448 } else 449 lp->l_parsed = 1; 450 pos = lp->l_line; 451 /* Watch for null pointer dereferences, dammit! */ 452 while (pos != NULL && *pos != '\0') { 453 if (*pos == '(') { 454 grp = malloc(sizeof(*grp)); 455 if (grp == NULL) 456 return (1); 457 ng = grp->ng_str; 458 bzero(grp, sizeof(*grp)); 459 pos++; 460 gpos = strsep(&pos, ")"); 461 #ifdef DEBUG 462 fields = 0; 463 #endif 464 for (strpos = 0; strpos < 3; strpos++) { 465 if ((spos = strsep(&gpos, ",")) == NULL) { 466 /* 467 * All other systems I've tested 468 * return NULL for empty netgroup 469 * fields. It's up to user programs 470 * to handle the NULLs appropriately. 471 */ 472 ng[strpos] = NULL; 473 continue; 474 } 475 #ifdef DEBUG 476 fields++; 477 #endif 478 while (*spos == ' ' || *spos == '\t') 479 spos++; 480 if ((epos = strpbrk(spos, " \t"))) { 481 *epos = '\0'; 482 len = epos - spos; 483 } else 484 len = strlen(spos); 485 if (len <= 0) 486 continue; 487 ng[strpos] = malloc(len + 1); 488 if (ng[strpos] == NULL) { 489 for (freepos = 0; freepos < strpos; 490 freepos++) 491 free(ng[freepos]); 492 free(grp); 493 return (1); 494 } 495 bcopy(spos, ng[strpos], len + 1); 496 } 497 grp->ng_next = grouphead.gr; 498 grouphead.gr = grp; 499 #ifdef DEBUG 500 /* 501 * Note: on other platforms, malformed netgroup 502 * entries are not normally flagged. While we 503 * can catch bad entries and report them, we should 504 * stay silent by default for compatibility's sake. 505 */ 506 if (fields < 3) { 507 fprintf(stderr, 508 "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", 509 ng[NG_HOST] == NULL ? "" : ng[NG_HOST], 510 ng[NG_USER] == NULL ? "" : ",", 511 ng[NG_USER] == NULL ? "" : ng[NG_USER], 512 ng[NG_DOM] == NULL ? "" : ",", 513 ng[NG_DOM] == NULL ? "" : ng[NG_DOM], 514 lp->l_groupname); 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