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