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 == (struct netgrp *)0 || 165 strcmp(group, grouphead.grname)) { 166 endnetgrent(); 167 #ifdef YP 168 /* Presumed guilty until proven innocent. */ 169 _use_only_yp = 0; 170 /* 171 * If /etc/netgroup doesn't exist or is empty, 172 * use NIS exclusively. 173 */ 174 if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) && 175 errno == ENOENT) || _yp_statp.st_size == 0) 176 _use_only_yp = _netgr_yp_enabled = 1; 177 if ((netf = fopen(_PATH_NETGROUP,"r")) != NULL ||_use_only_yp){ 178 /* 179 * Icky: grab the first character of the netgroup file 180 * and turn on NIS if it's a '+'. rewind the stream 181 * afterwards so we don't goof up read_for_group() later. 182 */ 183 if (netf) { 184 fscanf(netf, "%c", &_yp_plus); 185 rewind(netf); 186 if (_yp_plus == '+') 187 _use_only_yp = _netgr_yp_enabled = 1; 188 } 189 /* 190 * If we were called specifically for an innetgr() 191 * lookup and we're in NIS-only mode, short-circuit 192 * parse_netgroup() and cut directly to the chase. 193 */ 194 if (_use_only_yp && _yp_innetgr) { 195 /* dohw! */ 196 if (netf != NULL) 197 fclose(netf); 198 return; 199 } 200 #else 201 if ((netf = fopen(_PATH_NETGROUP, "r"))) { 202 #endif 203 if (parse_netgrp(group)) 204 endnetgrent(); 205 else { 206 grouphead.grname = (char *) 207 malloc(strlen(group) + 1); 208 strcpy(grouphead.grname, group); 209 } 210 if (netf) 211 fclose(netf); 212 } 213 } 214 nextgrp = grouphead.gr; 215 } 216 217 /* 218 * Get the next netgroup off the list. 219 */ 220 int 221 getnetgrent(char **hostp, char **userp, char **domp) 222 { 223 #ifdef YP 224 _yp_innetgr = 0; 225 #endif 226 227 if (nextgrp) { 228 *hostp = nextgrp->ng_str[NG_HOST]; 229 *userp = nextgrp->ng_str[NG_USER]; 230 *domp = nextgrp->ng_str[NG_DOM]; 231 nextgrp = nextgrp->ng_next; 232 return (1); 233 } 234 return (0); 235 } 236 237 /* 238 * endnetgrent() - cleanup 239 */ 240 void 241 endnetgrent(void) 242 { 243 struct linelist *lp, *olp; 244 struct netgrp *gp, *ogp; 245 246 lp = linehead; 247 while (lp) { 248 olp = lp; 249 lp = lp->l_next; 250 free(olp->l_groupname); 251 free(olp->l_line); 252 free((char *)olp); 253 } 254 linehead = (struct linelist *)0; 255 if (grouphead.grname) { 256 free(grouphead.grname); 257 grouphead.grname = (char *)0; 258 } 259 gp = grouphead.gr; 260 while (gp) { 261 ogp = gp; 262 gp = gp->ng_next; 263 if (ogp->ng_str[NG_HOST]) 264 free(ogp->ng_str[NG_HOST]); 265 if (ogp->ng_str[NG_USER]) 266 free(ogp->ng_str[NG_USER]); 267 if (ogp->ng_str[NG_DOM]) 268 free(ogp->ng_str[NG_DOM]); 269 free((char *)ogp); 270 } 271 grouphead.gr = (struct netgrp *)0; 272 nextgrp = (struct netgrp *)0; 273 #ifdef YP 274 _netgr_yp_enabled = 0; 275 #endif 276 } 277 278 #ifdef YP 279 static int 280 _listmatch(const char *list, const char *group, int len) 281 { 282 const char *ptr = list; 283 const char *cptr; 284 int glen = strlen(group); 285 286 /* skip possible leading whitespace */ 287 while(isspace((unsigned char)*ptr)) 288 ptr++; 289 290 while (ptr < list + len) { 291 cptr = ptr; 292 while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr)) 293 ptr++; 294 if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr)) 295 return(1); 296 while(*ptr == ',' || isspace((unsigned char)*ptr)) 297 ptr++; 298 } 299 300 return(0); 301 } 302 303 static int 304 _revnetgr_lookup(char* lookupdom, char* map, const char* str, 305 const char* dom, const char* group) 306 { 307 int y, rv, rot; 308 char key[MAXHOSTNAMELEN]; 309 char *result; 310 int resultlen; 311 312 for (rot = 0; ; rot++) { 313 switch (rot) { 314 case(0): snprintf(key, MAXHOSTNAMELEN, "%s.%s", 315 str, dom?dom:lookupdom); 316 break; 317 case(1): snprintf(key, MAXHOSTNAMELEN, "%s.*", 318 str); 319 break; 320 case(2): snprintf(key, MAXHOSTNAMELEN, "*.%s", 321 dom?dom:lookupdom); 322 break; 323 case(3): snprintf(key, MAXHOSTNAMELEN, "*.*"); 324 break; 325 default: return(0); 326 } 327 y = yp_match(lookupdom, map, key, strlen(key), &result, 328 &resultlen); 329 if (y == 0) { 330 rv = _listmatch(result, group, resultlen); 331 free(result); 332 if (rv) return(1); 333 } else if (y != YPERR_KEY) { 334 /* 335 * If we get an error other than 'no 336 * such key in map' then something is 337 * wrong and we should stop the search. 338 */ 339 return(-1); 340 } 341 } 342 } 343 #endif 344 345 /* 346 * Search for a match in a netgroup. 347 */ 348 int 349 innetgr(const char *group, const char *host, const char *user, const char *dom) 350 { 351 char *hst, *usr, *dm; 352 /* Sanity check */ 353 354 if (group == NULL || !strlen(group)) 355 return (0); 356 357 #ifdef YP 358 _yp_innetgr = 1; 359 #endif 360 setnetgrent(group); 361 #ifdef YP 362 _yp_innetgr = 0; 363 /* 364 * If we're in NIS-only mode, do the search using 365 * NIS 'reverse netgroup' lookups. 366 * 367 * What happens with 'reverse netgroup' lookups: 368 * 369 * 1) try 'reverse netgroup' lookup 370 * 1.a) if host is specified and user is null: 371 * look in netgroup.byhost 372 * (try host.domain, host.*, *.domain or *.*) 373 * if found, return yes 374 * 1.b) if user is specified and host is null: 375 * look in netgroup.byuser 376 * (try host.domain, host.*, *.domain or *.*) 377 * if found, return yes 378 * 1.c) if both host and user are specified, 379 * don't do 'reverse netgroup' lookup. It won't work. 380 * 1.d) if neither host ane user are specified (why?!?) 381 * don't do 'reverse netgroup' lookup either. 382 * 2) if domain is specified and 'reverse lookup' is done: 383 * 'reverse lookup' was authoritative. bye bye. 384 * 3) otherwise, too bad, try it the slow way. 385 */ 386 if (_use_only_yp && (host == NULL) != (user == NULL)) { 387 int ret; 388 if(yp_get_default_domain(&_netgr_yp_domain)) 389 return(0); 390 ret = _revnetgr_lookup(_netgr_yp_domain, 391 host?"netgroup.byhost":"netgroup.byuser", 392 host?host:user, dom, group); 393 if (ret == 1) 394 return(1); 395 else if (ret == 0 && dom != NULL) 396 return(0); 397 } 398 399 setnetgrent(group); 400 #endif /* YP */ 401 402 while (getnetgrent(&hst, &usr, &dm)) 403 if ((host == NULL || hst == NULL || !strcmp(host, hst)) && 404 (user == NULL || usr == NULL || !strcmp(user, usr)) && 405 ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { 406 endnetgrent(); 407 return (1); 408 } 409 endnetgrent(); 410 return (0); 411 } 412 413 /* 414 * Parse the netgroup file setting up the linked lists. 415 */ 416 static int 417 parse_netgrp(const char *group) 418 { 419 char *spos, *epos; 420 int len, strpos; 421 #ifdef DEBUG 422 int fields; 423 #endif 424 char *pos, *gpos; 425 struct netgrp *grp; 426 struct linelist *lp = linehead; 427 428 /* 429 * First, see if the line has already been read in. 430 */ 431 while (lp) { 432 if (!strcmp(group, lp->l_groupname)) 433 break; 434 lp = lp->l_next; 435 } 436 if (lp == (struct linelist *)0 && 437 (lp = read_for_group(group)) == (struct linelist *)0) 438 return (1); 439 if (lp->l_parsed) { 440 #ifdef DEBUG 441 /* 442 * This error message is largely superflous since the 443 * code handles the error condition sucessfully, and 444 * spewing it out from inside libc can actually hose 445 * certain programs. 446 */ 447 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); 448 #endif 449 return (1); 450 } else 451 lp->l_parsed = 1; 452 pos = lp->l_line; 453 /* Watch for null pointer dereferences, dammit! */ 454 while (pos != NULL && *pos != '\0') { 455 if (*pos == '(') { 456 grp = (struct netgrp *)malloc(sizeof (struct netgrp)); 457 bzero((char *)grp, sizeof (struct netgrp)); 458 grp->ng_next = grouphead.gr; 459 grouphead.gr = grp; 460 pos++; 461 gpos = strsep(&pos, ")"); 462 #ifdef DEBUG 463 fields = 0; 464 #endif 465 for (strpos = 0; strpos < 3; strpos++) { 466 if ((spos = strsep(&gpos, ","))) { 467 #ifdef DEBUG 468 fields++; 469 #endif 470 while (*spos == ' ' || *spos == '\t') 471 spos++; 472 if ((epos = strpbrk(spos, " \t"))) { 473 *epos = '\0'; 474 len = epos - spos; 475 } else 476 len = strlen(spos); 477 if (len > 0) { 478 grp->ng_str[strpos] = (char *) 479 malloc(len + 1); 480 bcopy(spos, grp->ng_str[strpos], 481 len + 1); 482 } 483 } else { 484 /* 485 * All other systems I've tested 486 * return NULL for empty netgroup 487 * fields. It's up to user programs 488 * to handle the NULLs appropriately. 489 */ 490 grp->ng_str[strpos] = NULL; 491 } 492 } 493 #ifdef DEBUG 494 /* 495 * Note: on other platforms, malformed netgroup 496 * entries are not normally flagged. While we 497 * can catch bad entries and report them, we should 498 * stay silent by default for compatibility's sake. 499 */ 500 if (fields < 3) 501 fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", 502 grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], 503 grp->ng_str[NG_USER] == NULL ? "" : ",", 504 grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], 505 grp->ng_str[NG_DOM] == NULL ? "" : ",", 506 grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], 507 lp->l_groupname); 508 #endif 509 } else { 510 spos = strsep(&pos, ", \t"); 511 if (parse_netgrp(spos)) 512 continue; 513 } 514 if (pos == NULL) 515 break; 516 while (*pos == ' ' || *pos == ',' || *pos == '\t') 517 pos++; 518 } 519 return (0); 520 } 521 522 /* 523 * Read the netgroup file and save lines until the line for the netgroup 524 * is found. Return 1 if eof is encountered. 525 */ 526 static struct linelist * 527 read_for_group(const char *group) 528 { 529 char *pos, *spos, *linep, *olinep; 530 int len, olen; 531 int cont; 532 struct linelist *lp; 533 char line[LINSIZ + 2]; 534 #ifdef YP 535 char *result; 536 int resultlen; 537 538 while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) { 539 if (_netgr_yp_enabled) { 540 if(!_netgr_yp_domain) 541 if(yp_get_default_domain(&_netgr_yp_domain)) 542 continue; 543 if (yp_match(_netgr_yp_domain, "netgroup", group, 544 strlen(group), &result, &resultlen)) { 545 free(result); 546 if (_use_only_yp) 547 return ((struct linelist *)0); 548 else { 549 _netgr_yp_enabled = 0; 550 continue; 551 } 552 } 553 snprintf(line, LINSIZ, "%s %s", group, result); 554 free(result); 555 } 556 #else 557 while (fgets(line, LINSIZ, netf) != NULL) { 558 #endif 559 pos = (char *)&line; 560 #ifdef YP 561 if (*pos == '+') { 562 _netgr_yp_enabled = 1; 563 continue; 564 } 565 #endif 566 if (*pos == '#') 567 continue; 568 while (*pos == ' ' || *pos == '\t') 569 pos++; 570 spos = pos; 571 while (*pos != ' ' && *pos != '\t' && *pos != '\n' && 572 *pos != '\0') 573 pos++; 574 len = pos - spos; 575 while (*pos == ' ' || *pos == '\t') 576 pos++; 577 if (*pos != '\n' && *pos != '\0') { 578 lp = (struct linelist *)malloc(sizeof (*lp)); 579 lp->l_parsed = 0; 580 lp->l_groupname = (char *)malloc(len + 1); 581 bcopy(spos, lp->l_groupname, len); 582 *(lp->l_groupname + len) = '\0'; 583 len = strlen(pos); 584 olen = 0; 585 586 /* 587 * Loop around handling line continuations. 588 */ 589 do { 590 if (*(pos + len - 1) == '\n') 591 len--; 592 if (*(pos + len - 1) == '\\') { 593 len--; 594 cont = 1; 595 } else 596 cont = 0; 597 if (len > 0) { 598 linep = (char *)malloc(olen + len + 1); 599 if (olen > 0) { 600 bcopy(olinep, linep, olen); 601 free(olinep); 602 } 603 bcopy(pos, linep + olen, len); 604 olen += len; 605 *(linep + olen) = '\0'; 606 olinep = linep; 607 } 608 if (cont) { 609 if (fgets(line, LINSIZ, netf)) { 610 pos = line; 611 len = strlen(pos); 612 } else 613 cont = 0; 614 } 615 } while (cont); 616 lp->l_line = linep; 617 lp->l_next = linehead; 618 linehead = lp; 619 620 /* 621 * If this is the one we wanted, we are done. 622 */ 623 if (!strcmp(lp->l_groupname, group)) 624 return (lp); 625 } 626 } 627 #ifdef YP 628 /* 629 * Yucky. The recursive nature of this whole mess might require 630 * us to make more than one pass through the netgroup file. 631 * This might be best left outside the #ifdef YP, but YP is 632 * defined by default anyway, so I'll leave it like this 633 * until I know better. 634 */ 635 rewind(netf); 636 #endif 637 return ((struct linelist *)0); 638 } 639