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