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 * $FreeBSD: src/lib/libc/gen/getnetgrent.c,v 1.26 1999/11/04 04:16:27 ache Exp $ 37 * $DragonFly: src/lib/libc/gen/getnetgrent.c,v 1.3 2004/06/06 15:05:55 hmp Exp $ 38 * 39 * @(#)getnetgrent.c 8.2 (Berkeley) 4/27/95 40 */ 41 42 #include <stdio.h> 43 #include <strings.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 47 #ifdef YP 48 /* 49 * Notes: 50 * We want to be able to use NIS netgroups properly while retaining 51 * the ability to use a local /etc/netgroup file. Unfortunately, you 52 * can't really do both at the same time - at least, not efficiently. 53 * NetBSD deals with this problem by creating a netgroup database 54 * using Berkeley DB (just like the password database) that allows 55 * for lookups using netgroup, netgroup.byuser or netgroup.byhost 56 * searches. This is a neat idea, but I don't have time to implement 57 * something like that now. (I think ultimately it would be nice 58 * if we DB-fied the group and netgroup stuff all in one shot, but 59 * for now I'm satisfied just to have something that works well 60 * without requiring massive code changes.) 61 * 62 * Therefore, to still permit the use of the local file and maintain 63 * optimum NIS performance, we allow for the following conditions: 64 * 65 * - If /etc/netgroup does not exist and NIS is turned on, we use 66 * NIS netgroups only. 67 * 68 * - If /etc/netgroup exists but is empty, we use NIS netgroups 69 * only. 70 * 71 * - If /etc/netgroup exists and contains _only_ a '+', we use 72 * NIS netgroups only. 73 * 74 * - If /etc/netgroup exists, contains locally defined netgroups 75 * and a '+', we use a mixture of NIS and the local entries. 76 * This method should return the same NIS data as just using 77 * NIS alone, but it will be slower if the NIS netgroup database 78 * is large (innetgr() in particular will suffer since extra 79 * processing has to be done in order to determine memberships 80 * using just the raw netgroup data). 81 * 82 * - If /etc/netgroup exists and contains only locally defined 83 * netgroup entries, we use just those local entries and ignore 84 * NIS (this is the original, pre-NIS behavior). 85 */ 86 87 #include <rpc/rpc.h> 88 #include <rpcsvc/yp_prot.h> 89 #include <rpcsvc/ypclnt.h> 90 #include <sys/types.h> 91 #include <sys/stat.h> 92 #include <sys/param.h> 93 #include <sys/errno.h> 94 static char *_netgr_yp_domain; 95 int _use_only_yp; 96 static int _netgr_yp_enabled; 97 static int _yp_innetgr; 98 #endif 99 100 #ifndef _PATH_NETGROUP 101 #define _PATH_NETGROUP "/etc/netgroup" 102 #endif 103 104 /* 105 * Static Variables and functions used by setnetgrent(), getnetgrent() and 106 * endnetgrent(). 107 * There are two linked lists: 108 * - linelist is just used by setnetgrent() to parse the net group file via. 109 * parse_netgrp() 110 * - netgrp is the list of entries for the current netgroup 111 */ 112 struct linelist { 113 struct linelist *l_next; /* Chain ptr. */ 114 int l_parsed; /* Flag for cycles */ 115 char *l_groupname; /* Name of netgroup */ 116 char *l_line; /* Netgroup entrie(s) to be parsed */ 117 }; 118 119 struct netgrp { 120 struct netgrp *ng_next; /* Chain ptr */ 121 char *ng_str[3]; /* Field pointers, see below */ 122 }; 123 #define NG_HOST 0 /* Host name */ 124 #define NG_USER 1 /* User name */ 125 #define NG_DOM 2 /* and Domain name */ 126 127 static struct linelist *linehead = (struct linelist *)0; 128 static struct netgrp *nextgrp = (struct netgrp *)0; 129 static struct { 130 struct netgrp *gr; 131 char *grname; 132 } grouphead = { 133 (struct netgrp *)0, 134 (char *)0, 135 }; 136 static FILE *netf = (FILE *)0; 137 static int parse_netgrp(); 138 static struct linelist *read_for_group(); 139 void setnetgrent(), endnetgrent(); 140 int getnetgrent(), innetgr(); 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(group) 152 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(hostp, userp, domp) 222 char **hostp, **userp, **domp; 223 { 224 #ifdef YP 225 _yp_innetgr = 0; 226 #endif 227 228 if (nextgrp) { 229 *hostp = nextgrp->ng_str[NG_HOST]; 230 *userp = nextgrp->ng_str[NG_USER]; 231 *domp = nextgrp->ng_str[NG_DOM]; 232 nextgrp = nextgrp->ng_next; 233 return (1); 234 } 235 return (0); 236 } 237 238 /* 239 * endnetgrent() - cleanup 240 */ 241 void 242 endnetgrent() 243 { 244 struct linelist *lp, *olp; 245 struct netgrp *gp, *ogp; 246 247 lp = linehead; 248 while (lp) { 249 olp = lp; 250 lp = lp->l_next; 251 free(olp->l_groupname); 252 free(olp->l_line); 253 free((char *)olp); 254 } 255 linehead = (struct linelist *)0; 256 if (grouphead.grname) { 257 free(grouphead.grname); 258 grouphead.grname = (char *)0; 259 } 260 gp = grouphead.gr; 261 while (gp) { 262 ogp = gp; 263 gp = gp->ng_next; 264 if (ogp->ng_str[NG_HOST]) 265 free(ogp->ng_str[NG_HOST]); 266 if (ogp->ng_str[NG_USER]) 267 free(ogp->ng_str[NG_USER]); 268 if (ogp->ng_str[NG_DOM]) 269 free(ogp->ng_str[NG_DOM]); 270 free((char *)ogp); 271 } 272 grouphead.gr = (struct netgrp *)0; 273 #ifdef YP 274 _netgr_yp_enabled = 0; 275 #endif 276 } 277 278 #ifdef YP 279 static int _listmatch(list, group, len) 280 char *list, *group; 281 int len; 282 { 283 char *ptr = list, *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 _buildkey(key, str, dom, rotation) 304 char *key, *str, *dom; 305 int *rotation; 306 { 307 (*rotation)++; 308 if (*rotation > 4) 309 return(0); 310 switch(*rotation) { 311 case(1): sprintf((char *)key, "%s.%s", str, dom ? dom : "*"); 312 break; 313 case(2): sprintf((char *)key, "%s.*", str); 314 break; 315 case(3): sprintf((char *)key, "*.%s", dom ? dom : "*"); 316 break; 317 case(4): sprintf((char *)key, "*.*"); 318 break; 319 } 320 return(1); 321 } 322 #endif 323 324 /* 325 * Search for a match in a netgroup. 326 */ 327 int 328 innetgr(group, host, user, dom) 329 const char *group, *host, *user, *dom; 330 { 331 char *hst, *usr, *dm; 332 #ifdef YP 333 char *result; 334 int resultlen; 335 int rv; 336 #endif 337 /* Sanity check */ 338 339 if (group == NULL || !strlen(group)) 340 return (0); 341 342 #ifdef YP 343 _yp_innetgr = 1; 344 #endif 345 setnetgrent(group); 346 #ifdef YP 347 _yp_innetgr = 0; 348 /* 349 * If we're in NIS-only mode, do the search using 350 * NIS 'reverse netgroup' lookups. 351 */ 352 if (_use_only_yp) { 353 char _key[MAXHOSTNAMELEN]; 354 int rot = 0, y = 0; 355 356 if(yp_get_default_domain(&_netgr_yp_domain)) 357 return(0); 358 while(_buildkey(_key, user ? user : host, dom, &rot)) { 359 y = yp_match(_netgr_yp_domain, user? "netgroup.byuser": 360 "netgroup.byhost", _key, strlen(_key), &result, 361 &resultlen); 362 if (y) { 363 /* 364 * If we get an error other than 'no 365 * such key in map' then something is 366 * wrong and we should stop the search. 367 */ 368 if (y != YPERR_KEY) 369 break; 370 } else { 371 rv = _listmatch(result, group, resultlen); 372 free(result); 373 if (rv) 374 return(1); 375 else 376 return(0); 377 } 378 } 379 /* 380 * Couldn't match using NIS-exclusive mode. If the error 381 * was YPERR_MAP, then the failure happened because there 382 * was no netgroup.byhost or netgroup.byuser map. The odds 383 * are we are talking to an Sun NIS+ server in YP emulation 384 * mode; if this is the case, then we have to do the check 385 * the 'old-fashioned' way by grovelling through the netgroup 386 * map and resolving memberships on the fly. 387 */ 388 if (y != YPERR_MAP) 389 return(0); 390 } 391 392 setnetgrent(group); 393 #endif /* YP */ 394 395 while (getnetgrent(&hst, &usr, &dm)) 396 if ((host == NULL || hst == NULL || !strcmp(host, hst)) && 397 (user == NULL || usr == NULL || !strcmp(user, usr)) && 398 ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { 399 endnetgrent(); 400 return (1); 401 } 402 endnetgrent(); 403 return (0); 404 } 405 406 /* 407 * Parse the netgroup file setting up the linked lists. 408 */ 409 static int 410 parse_netgrp(group) 411 char *group; 412 { 413 char *spos, *epos; 414 int len, strpos; 415 #ifdef DEBUG 416 int fields; 417 #endif 418 char *pos, *gpos; 419 struct netgrp *grp; 420 struct linelist *lp = linehead; 421 422 /* 423 * First, see if the line has already been read in. 424 */ 425 while (lp) { 426 if (!strcmp(group, lp->l_groupname)) 427 break; 428 lp = lp->l_next; 429 } 430 if (lp == (struct linelist *)0 && 431 (lp = read_for_group(group)) == (struct linelist *)0) 432 return (1); 433 if (lp->l_parsed) { 434 #ifdef DEBUG 435 /* 436 * This error message is largely superflous since the 437 * code handles the error condition sucessfully, and 438 * spewing it out from inside libc can actually hose 439 * certain programs. 440 */ 441 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); 442 #endif 443 return (1); 444 } else 445 lp->l_parsed = 1; 446 pos = lp->l_line; 447 /* Watch for null pointer dereferences, dammit! */ 448 while (pos != NULL && *pos != '\0') { 449 if (*pos == '(') { 450 grp = (struct netgrp *)malloc(sizeof (struct netgrp)); 451 bzero((char *)grp, sizeof (struct netgrp)); 452 grp->ng_next = grouphead.gr; 453 grouphead.gr = grp; 454 pos++; 455 gpos = strsep(&pos, ")"); 456 #ifdef DEBUG 457 fields = 0; 458 #endif 459 for (strpos = 0; strpos < 3; strpos++) { 460 if ((spos = strsep(&gpos, ","))) { 461 #ifdef DEBUG 462 fields++; 463 #endif 464 while (*spos == ' ' || *spos == '\t') 465 spos++; 466 if ((epos = strpbrk(spos, " \t"))) { 467 *epos = '\0'; 468 len = epos - spos; 469 } else 470 len = strlen(spos); 471 if (len > 0) { 472 grp->ng_str[strpos] = (char *) 473 malloc(len + 1); 474 bcopy(spos, grp->ng_str[strpos], 475 len + 1); 476 } 477 } else { 478 /* 479 * All other systems I've tested 480 * return NULL for empty netgroup 481 * fields. It's up to user programs 482 * to handle the NULLs appropriately. 483 */ 484 grp->ng_str[strpos] = NULL; 485 } 486 } 487 #ifdef DEBUG 488 /* 489 * Note: on other platforms, malformed netgroup 490 * entries are not normally flagged. While we 491 * can catch bad entries and report them, we should 492 * stay silent by default for compatibility's sake. 493 */ 494 if (fields < 3) 495 fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", 496 grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], 497 grp->ng_str[NG_USER] == NULL ? "" : ",", 498 grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], 499 grp->ng_str[NG_DOM] == NULL ? "" : ",", 500 grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], 501 lp->l_groupname); 502 #endif 503 } else { 504 spos = strsep(&pos, ", \t"); 505 if (parse_netgrp(spos)) 506 continue; 507 } 508 if (pos == NULL) 509 break; 510 while (*pos == ' ' || *pos == ',' || *pos == '\t') 511 pos++; 512 } 513 return (0); 514 } 515 516 /* 517 * Read the netgroup file and save lines until the line for the netgroup 518 * is found. Return 1 if eof is encountered. 519 */ 520 static struct linelist * 521 read_for_group(group) 522 char *group; 523 { 524 char *pos, *spos, *linep, *olinep; 525 int len, olen; 526 int cont; 527 struct linelist *lp; 528 char line[LINSIZ + 2]; 529 #ifdef YP 530 char *result; 531 int resultlen; 532 533 while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) { 534 if (_netgr_yp_enabled) { 535 if(!_netgr_yp_domain) 536 if(yp_get_default_domain(&_netgr_yp_domain)) 537 continue; 538 if (yp_match(_netgr_yp_domain, "netgroup", group, 539 strlen(group), &result, &resultlen)) { 540 free(result); 541 if (_use_only_yp) 542 return ((struct linelist *)0); 543 else { 544 _netgr_yp_enabled = 0; 545 continue; 546 } 547 } 548 snprintf(line, LINSIZ, "%s %s", group, result); 549 free(result); 550 } 551 #else 552 while (fgets(line, LINSIZ, netf) != NULL) { 553 #endif 554 pos = (char *)&line; 555 #ifdef YP 556 if (*pos == '+') { 557 _netgr_yp_enabled = 1; 558 continue; 559 } 560 #endif 561 if (*pos == '#') 562 continue; 563 while (*pos == ' ' || *pos == '\t') 564 pos++; 565 spos = pos; 566 while (*pos != ' ' && *pos != '\t' && *pos != '\n' && 567 *pos != '\0') 568 pos++; 569 len = pos - spos; 570 while (*pos == ' ' || *pos == '\t') 571 pos++; 572 if (*pos != '\n' && *pos != '\0') { 573 lp = (struct linelist *)malloc(sizeof (*lp)); 574 lp->l_parsed = 0; 575 lp->l_groupname = (char *)malloc(len + 1); 576 bcopy(spos, lp->l_groupname, len); 577 *(lp->l_groupname + len) = '\0'; 578 len = strlen(pos); 579 olen = 0; 580 581 /* 582 * Loop around handling line continuations. 583 */ 584 do { 585 if (*(pos + len - 1) == '\n') 586 len--; 587 if (*(pos + len - 1) == '\\') { 588 len--; 589 cont = 1; 590 } else 591 cont = 0; 592 if (len > 0) { 593 linep = (char *)malloc(olen + len + 1); 594 if (olen > 0) { 595 bcopy(olinep, linep, olen); 596 free(olinep); 597 } 598 bcopy(pos, linep + olen, len); 599 olen += len; 600 *(linep + olen) = '\0'; 601 olinep = linep; 602 } 603 if (cont) { 604 if (fgets(line, LINSIZ, netf)) { 605 pos = line; 606 len = strlen(pos); 607 } else 608 cont = 0; 609 } 610 } while (cont); 611 lp->l_line = linep; 612 lp->l_next = linehead; 613 linehead = lp; 614 615 /* 616 * If this is the one we wanted, we are done. 617 */ 618 if (!strcmp(lp->l_groupname, group)) 619 return (lp); 620 } 621 } 622 #ifdef YP 623 /* 624 * Yucky. The recursive nature of this whole mess might require 625 * us to make more than one pass through the netgroup file. 626 * This might be best left outside the #ifdef YP, but YP is 627 * defined by default anyway, so I'll leave it like this 628 * until I know better. 629 */ 630 rewind(netf); 631 #endif 632 return ((struct linelist *)0); 633 } 634