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