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.1 (Berkeley) 6/4/93"; 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 #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 static int parse_netgrp(); 136 static struct linelist *read_for_group(); 137 void setnetgrent(), endnetgrent(); 138 int getnetgrent(), innetgr(); 139 140 #define LINSIZ 1024 /* Length of netgroup file line */ 141 142 /* 143 * setnetgrent() 144 * Parse the netgroup file looking for the netgroup and build the list 145 * of netgrp structures. Let parse_netgrp() and read_for_group() do 146 * most of the work. 147 */ 148 void 149 setnetgrent(group) 150 char *group; 151 { 152 #ifdef YP 153 struct stat _yp_statp; 154 char _yp_plus; 155 #endif 156 157 /* Sanity check */ 158 159 if (group == NULL || !strlen(group)) 160 return; 161 162 if (grouphead.gr == (struct netgrp *)0 || 163 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,"r")) != 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, "r")) { 200 #endif 201 if (parse_netgrp(group)) 202 endnetgrent(); 203 else { 204 grouphead.grname = (char *) 205 malloc(strlen(group) + 1); 206 strcpy(grouphead.grname, group); 207 } 208 if (netf) 209 fclose(netf); 210 } 211 } 212 nextgrp = grouphead.gr; 213 } 214 215 /* 216 * Get the next netgroup off the list. 217 */ 218 int 219 getnetgrent(hostp, userp, domp) 220 char **hostp, **userp, **domp; 221 { 222 #ifdef YP 223 _yp_innetgr = 0; 224 #endif 225 226 if (nextgrp) { 227 *hostp = nextgrp->ng_str[NG_HOST]; 228 *userp = nextgrp->ng_str[NG_USER]; 229 *domp = nextgrp->ng_str[NG_DOM]; 230 nextgrp = nextgrp->ng_next; 231 return (1); 232 } 233 return (0); 234 } 235 236 /* 237 * endnetgrent() - cleanup 238 */ 239 void 240 endnetgrent() 241 { 242 register struct linelist *lp, *olp; 243 register struct netgrp *gp, *ogp; 244 245 lp = linehead; 246 while (lp) { 247 olp = lp; 248 lp = lp->l_next; 249 free(olp->l_groupname); 250 free(olp->l_line); 251 free((char *)olp); 252 } 253 linehead = (struct linelist *)0; 254 if (grouphead.grname) { 255 free(grouphead.grname); 256 grouphead.grname = (char *)0; 257 } 258 gp = grouphead.gr; 259 while (gp) { 260 ogp = gp; 261 gp = gp->ng_next; 262 if (ogp->ng_str[NG_HOST]) 263 free(ogp->ng_str[NG_HOST]); 264 if (ogp->ng_str[NG_USER]) 265 free(ogp->ng_str[NG_USER]); 266 if (ogp->ng_str[NG_DOM]) 267 free(ogp->ng_str[NG_DOM]); 268 free((char *)ogp); 269 } 270 grouphead.gr = (struct netgrp *)0; 271 #ifdef YP 272 _netgr_yp_enabled = 0; 273 #endif 274 } 275 276 #ifdef YP 277 static int _listmatch(list, group, len) 278 char *list, *group; 279 int len; 280 { 281 char *ptr = list; 282 283 while ( (ptr = strstr(ptr, group)) ) { 284 285 ptr += strlen(group); 286 287 if (*ptr == ',' || *ptr == '\0') 288 return(1); 289 } 290 291 return(0); 292 } 293 294 static int _buildkey(key, str, dom, rotation) 295 char *key, *str, *dom; 296 int *rotation; 297 { 298 (*rotation)++; 299 if (*rotation > 4) 300 return(0); 301 switch(*rotation) { 302 case(1): sprintf((char *)key, "%s.%s", str, dom ? dom : "*"); 303 break; 304 case(2): sprintf((char *)key, "%s.*", str); 305 break; 306 case(3): sprintf((char *)key, "*.%s", dom ? dom : "*"); 307 break; 308 case(4): sprintf((char *)key, "*.*"); 309 break; 310 } 311 return(1); 312 } 313 #endif 314 315 /* 316 * Search for a match in a netgroup. 317 */ 318 int 319 innetgr(group, host, user, dom) 320 const char *group, *host, *user, *dom; 321 { 322 char *hst, *usr, *dm; 323 #ifdef YP 324 char *result; 325 int resultlen; 326 int rv; 327 #endif 328 /* Sanity check */ 329 330 if (group == NULL || !strlen(group)) 331 return (0); 332 333 #ifdef YP 334 _yp_innetgr = 1; 335 #endif 336 setnetgrent(group); 337 #ifdef YP 338 _yp_innetgr = 0; 339 /* 340 * If we're in NIS-only mode, do the search using 341 * NIS 'reverse netgroup' lookups. 342 */ 343 if (_use_only_yp) { 344 char _key[MAXHOSTNAMELEN]; 345 int rot = 0; 346 347 if(yp_get_default_domain(&_netgr_yp_domain)) 348 return(0); 349 while(_buildkey(&_key, user ? user : host, dom, &rot)) { 350 if (!yp_match(_netgr_yp_domain, user? "netgroup.byuser": 351 "netgroup.byhost", _key, strlen(_key), &result, 352 &resultlen)) { 353 rv = _listmatch(result, group, resultlen); 354 free(result); 355 if (rv) 356 return(1); 357 else 358 return(0); 359 } 360 } 361 #ifdef CHARITABLE 362 } 363 /* 364 * Couldn't match using NIS-exclusive mode -- try 365 * standard mode. 366 */ 367 setnetgrent(group); 368 #else 369 return(0); 370 } 371 #endif /* CHARITABLE */ 372 #endif /* YP */ 373 while (getnetgrent(&hst, &usr, &dm)) 374 if ((host == NULL || hst == NULL || !strcmp(host, hst)) && 375 (user == NULL || usr == NULL || !strcmp(user, usr)) && 376 ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { 377 endnetgrent(); 378 return (1); 379 } 380 endnetgrent(); 381 return (0); 382 } 383 384 /* 385 * Parse the netgroup file setting up the linked lists. 386 */ 387 static int 388 parse_netgrp(group) 389 char *group; 390 { 391 register char *spos, *epos; 392 register int len, strpos; 393 #ifdef DEBUG 394 register int fields; 395 #endif 396 char *pos, *gpos; 397 struct netgrp *grp; 398 struct linelist *lp = linehead; 399 400 /* 401 * First, see if the line has already been read in. 402 */ 403 while (lp) { 404 if (!strcmp(group, lp->l_groupname)) 405 break; 406 lp = lp->l_next; 407 } 408 if (lp == (struct linelist *)0 && 409 (lp = read_for_group(group)) == (struct linelist *)0) 410 return (1); 411 if (lp->l_parsed) { 412 #ifdef DEBUG 413 /* 414 * This error message is largely superflous since the 415 * code handles the error condition sucessfully, and 416 * spewing it out from inside libc can actually hose 417 * certain programs. 418 */ 419 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); 420 #endif 421 return (1); 422 } else 423 lp->l_parsed = 1; 424 pos = lp->l_line; 425 /* Watch for null pointer dereferences, dammit! */ 426 while (pos != NULL && *pos != '\0') { 427 if (*pos == '(') { 428 grp = (struct netgrp *)malloc(sizeof (struct netgrp)); 429 bzero((char *)grp, sizeof (struct netgrp)); 430 grp->ng_next = grouphead.gr; 431 grouphead.gr = grp; 432 pos++; 433 gpos = strsep(&pos, ")"); 434 #ifdef DEBUG 435 fields = 0; 436 #endif 437 for (strpos = 0; strpos < 3; strpos++) { 438 if ((spos = strsep(&gpos, ","))) { 439 #ifdef DEBUG 440 fields++; 441 #endif 442 while (*spos == ' ' || *spos == '\t') 443 spos++; 444 if ((epos = strpbrk(spos, " \t"))) { 445 *epos = '\0'; 446 len = epos - spos; 447 } else 448 len = strlen(spos); 449 if (len > 0) { 450 grp->ng_str[strpos] = (char *) 451 malloc(len + 1); 452 bcopy(spos, grp->ng_str[strpos], 453 len + 1); 454 } 455 } else { 456 /* 457 * All other systems I've tested 458 * return NULL for empty netgroup 459 * fields. It's up to user programs 460 * to handle the NULLs appropriately. 461 */ 462 grp->ng_str[strpos] = NULL; 463 } 464 } 465 #ifdef DEBUG 466 /* 467 * Note: on other platforms, malformed netgroup 468 * entries are not normally flagged. While we 469 * can catch bad entries and report them, we should 470 * stay silent by default for compatibility's sake. 471 */ 472 if (fields < 3) 473 fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", 474 grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], 475 grp->ng_str[NG_USER] == NULL ? "" : ",", 476 grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], 477 grp->ng_str[NG_DOM] == NULL ? "" : ",", 478 grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], 479 lp->l_groupname); 480 #endif 481 } else { 482 spos = strsep(&pos, ", \t"); 483 if (parse_netgrp(spos)) 484 continue; 485 } 486 /* Watch for null pointer dereferences, dammit! */ 487 if (pos != NULL) 488 while (*pos == ' ' || *pos == ',' || *pos == '\t') 489 pos++; 490 } 491 return (0); 492 } 493 494 /* 495 * Read the netgroup file and save lines until the line for the netgroup 496 * is found. Return 1 if eof is encountered. 497 */ 498 static struct linelist * 499 read_for_group(group) 500 char *group; 501 { 502 register char *pos, *spos, *linep, *olinep; 503 register int len, olen; 504 int cont; 505 struct linelist *lp; 506 char line[LINSIZ + 1]; 507 #ifdef YP 508 char *result; 509 int resultlen; 510 511 while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) { 512 if (_netgr_yp_enabled) { 513 if(!_netgr_yp_domain) 514 if(yp_get_default_domain(&_netgr_yp_domain)) 515 continue; 516 if (yp_match(_netgr_yp_domain, "netgroup", group, 517 strlen(group), &result, &resultlen)) { 518 free(result); 519 if (_use_only_yp) 520 return ((struct linelist *)0); 521 else { 522 _netgr_yp_enabled = 0; 523 continue; 524 } 525 } 526 sprintf(line, "%s %s", group, result); 527 free(result); 528 } 529 #else 530 while (fgets(line, LINSIZ, netf) != NULL) { 531 #endif 532 pos = (char *)&line; 533 #ifdef YP 534 if (*pos == '+') { 535 _netgr_yp_enabled = 1; 536 continue; 537 } 538 #endif 539 if (*pos == '#') 540 continue; 541 while (*pos == ' ' || *pos == '\t') 542 pos++; 543 spos = pos; 544 while (*pos != ' ' && *pos != '\t' && *pos != '\n' && 545 *pos != '\0') 546 pos++; 547 len = pos - spos; 548 while (*pos == ' ' || *pos == '\t') 549 pos++; 550 if (*pos != '\n' && *pos != '\0') { 551 lp = (struct linelist *)malloc(sizeof (*lp)); 552 lp->l_parsed = 0; 553 lp->l_groupname = (char *)malloc(len + 1); 554 bcopy(spos, lp->l_groupname, len); 555 *(lp->l_groupname + len) = '\0'; 556 len = strlen(pos); 557 olen = 0; 558 559 /* 560 * Loop around handling line continuations. 561 */ 562 do { 563 if (*(pos + len - 1) == '\n') 564 len--; 565 if (*(pos + len - 1) == '\\') { 566 len--; 567 cont = 1; 568 } else 569 cont = 0; 570 if (len > 0) { 571 linep = (char *)malloc(olen + len + 1); 572 if (olen > 0) { 573 bcopy(olinep, linep, olen); 574 free(olinep); 575 } 576 bcopy(pos, linep + olen, len); 577 olen += len; 578 *(linep + olen) = '\0'; 579 olinep = linep; 580 } 581 if (cont) { 582 if (fgets(line, LINSIZ, netf)) { 583 pos = line; 584 len = strlen(pos); 585 } else 586 cont = 0; 587 } 588 } while (cont); 589 lp->l_line = linep; 590 lp->l_next = linehead; 591 linehead = lp; 592 593 /* 594 * If this is the one we wanted, we are done. 595 */ 596 if (!strcmp(lp->l_groupname, group)) 597 return (lp); 598 } 599 } 600 #ifdef YP 601 /* 602 * Yucky. The recursive nature of this whole mess might require 603 * us to make more than one pass through the netgroup file. 604 * This might be best left outside the #ifdef YP, but YP is 605 * defined by default anyway, so I'll leave it like this 606 * until I know better. 607 */ 608 rewind(netf); 609 #endif 610 return ((struct linelist *)0); 611 } 612