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