getgrent.c revision 89999
1/* $NetBSD: getgrent.c,v 1.34.2.1 1999/04/27 14:10:58 perry Exp $ */ 2/* 3 * Copyright (c) 1989, 1993 4 * The Regents of the University of California. All rights reserved. 5 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#if defined(LIBC_SCCS) && !defined(lint) 37static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; 38#endif /* LIBC_SCCS and not lint */ 39__FBSDID("$FreeBSD: head/lib/libc/gen/getgrent.c 89999 2002-01-30 21:36:57Z obrien $"); 40 41#include <sys/types.h> 42 43#include <errno.h> 44#include <grp.h> 45#include <limits.h> 46#include <nsswitch.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <syslog.h> 51 52#ifdef HESIOD 53#include <hesiod.h> 54#include <arpa/nameser.h> 55#endif 56#ifdef YP 57#include <rpc/rpc.h> 58#include <rpcsvc/yp_prot.h> 59#include <rpcsvc/ypclnt.h> 60#endif 61 62#if defined(YP) || defined(HESIOD) 63#define _GROUP_COMPAT 64#endif 65 66static FILE *_gr_fp; 67static struct group _gr_group; 68static int _gr_stayopen; 69static int _gr_filesdone; 70 71static void grcleanup __P((void)); 72static int grscan __P((int, gid_t, const char *)); 73static char *getline __P((void)); 74static int copyline __P((const char*)); 75static int matchline __P((int, gid_t, const char *)); 76static int start_gr __P((void)); 77 78 79 80 81/* initial size for malloc and increase steps for realloc */ 82#define MAXGRP 64 83#define MAXLINELENGTH 256 84 85#ifdef HESIOD 86#if MAXLINELENGTH < NS_MAXLABEL + 1 87#error "MAXLINELENGTH must be at least NS_MAXLABEL + 1" 88#endif 89#endif 90 91static char **members; /* list of group members */ 92static int maxgrp; /* current length of **members */ 93static char *line; /* buffer for group line */ 94static int maxlinelength; /* current length of *line */ 95 96/* 97 * Lines longer than MAXLINELENGTHLIMIT will be counted as an error. 98 * <= 0 disable check for maximum line length 99 * 256K is enough for 64,000 uids 100 */ 101#define MAXLINELENGTHLIMIT (256 * 1024) 102 103#ifdef YP 104static char *__ypcurrent, *__ypdomain; 105static int __ypcurrentlen; 106static int _gr_ypdone; 107#endif 108 109#ifdef HESIOD 110static int _gr_hesnum; 111#endif 112 113#ifdef _GROUP_COMPAT 114enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME }; 115static enum _grmode __grmode; 116#endif 117 118struct group * 119getgrent() 120{ 121 if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL)) 122 return (NULL); 123 return &_gr_group; 124} 125 126struct group * 127getgrnam(name) 128 const char *name; 129{ 130 int rval; 131 132 if (!start_gr()) 133 return NULL; 134 rval = grscan(1, 0, name); 135 if (!_gr_stayopen) 136 endgrent(); 137 return (rval) ? &_gr_group : NULL; 138} 139 140struct group * 141getgrgid(gid) 142 gid_t gid; 143{ 144 int rval; 145 146 if (!start_gr()) 147 return NULL; 148 rval = grscan(1, gid, NULL); 149 if (!_gr_stayopen) 150 endgrent(); 151 return (rval) ? &_gr_group : NULL; 152} 153 154void 155grcleanup() 156{ 157 _gr_filesdone = 0; 158#ifdef YP 159 if (__ypcurrent) 160 free(__ypcurrent); 161 __ypcurrent = NULL; 162 _gr_ypdone = 0; 163#endif 164#ifdef HESIOD 165 _gr_hesnum = 0; 166#endif 167#ifdef _GROUP_COMPAT 168 __grmode = GRMODE_NONE; 169#endif 170} 171 172static int 173start_gr() 174{ 175 grcleanup(); 176 if (maxlinelength == 0) { 177 if ((line = (char *)malloc(MAXLINELENGTH)) == NULL) 178 return 0; 179 maxlinelength = MAXLINELENGTH; 180 } 181 if (maxgrp == 0) { 182 if ((members = (char **) malloc(sizeof(char**) * 183 MAXGRP)) == NULL) 184 return 0; 185 maxgrp = MAXGRP; 186 } 187 if (_gr_fp) { 188 rewind(_gr_fp); 189 return 1; 190 } 191 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0; 192} 193 194int 195setgrent(void) 196{ 197 return setgroupent(0); 198} 199 200int 201setgroupent(stayopen) 202 int stayopen; 203{ 204 if (!start_gr()) 205 return 0; 206 _gr_stayopen = stayopen; 207 return 1; 208} 209 210void 211endgrent() 212{ 213 grcleanup(); 214 if (_gr_fp) { 215 (void)fclose(_gr_fp); 216 _gr_fp = NULL; 217 } 218} 219 220 221static int _local_grscan __P((void *, void *, va_list)); 222 223/*ARGSUSED*/ 224static int 225_local_grscan(rv, cb_data, ap) 226 void *rv; 227 void *cb_data; 228 va_list ap; 229{ 230 int search = va_arg(ap, int); 231 gid_t gid = va_arg(ap, gid_t); 232 const char *name = va_arg(ap, const char *); 233 234 if (_gr_filesdone) 235 return NS_NOTFOUND; 236 for (;;) { 237 if (getline() == NULL) { 238 if (!search) 239 _gr_filesdone = 1; 240 return NS_NOTFOUND; 241 } 242 if (matchline(search, gid, name)) 243 return NS_SUCCESS; 244 } 245 /* NOTREACHED */ 246} 247 248#ifdef HESIOD 249static int _dns_grscan __P((void *, void *, va_list)); 250 251/*ARGSUSED*/ 252static int 253_dns_grscan(rv, cb_data, ap) 254 void *rv; 255 void *cb_data; 256 va_list ap; 257{ 258 int search = va_arg(ap, int); 259 gid_t gid = va_arg(ap, gid_t); 260 const char *name = va_arg(ap, const char *); 261 262 char **hp; 263 void *context; 264 int r; 265 size_t sz; 266 267 r = NS_UNAVAIL; 268 if (!search && _gr_hesnum == -1) 269 return NS_NOTFOUND; 270 if (hesiod_init(&context) == -1) 271 return (r); 272 273 for (;;) { 274 if (search) { 275 if (name) 276 strlcpy(line, name, maxlinelength); 277 else 278 snprintf(line, maxlinelength, "%u", 279 (unsigned int)gid); 280 } else { 281 snprintf(line, maxlinelength, "group-%u", _gr_hesnum); 282 _gr_hesnum++; 283 } 284 285 hp = hesiod_resolve(context, line, "group"); 286 if (hp == NULL) { 287 if (errno == ENOENT) { 288 if (!search) 289 _gr_hesnum = -1; 290 r = NS_NOTFOUND; 291 } 292 break; 293 } 294 295 /* only check first elem */ 296 if (copyline(hp[0]) == 0) 297 return NS_UNAVAIL; 298 hesiod_free_list(context, hp); 299 if (matchline(search, gid, name)) { 300 r = NS_SUCCESS; 301 break; 302 } else if (search) { 303 r = NS_NOTFOUND; 304 break; 305 } 306 } 307 hesiod_end(context); 308 return (r); 309} 310#endif 311 312#ifdef YP 313static int _nis_grscan __P((void *, void *, va_list)); 314 315/*ARGSUSED*/ 316static int 317_nis_grscan(rv, cb_data, ap) 318 void *rv; 319 void *cb_data; 320 va_list ap; 321{ 322 int search = va_arg(ap, int); 323 gid_t gid = va_arg(ap, gid_t); 324 const char *name = va_arg(ap, const char *); 325 326 char *key, *data; 327 int keylen, datalen; 328 int r; 329 size_t sz; 330 331 if(__ypdomain == NULL) { 332 switch (yp_get_default_domain(&__ypdomain)) { 333 case 0: 334 break; 335 case YPERR_RESRC: 336 return NS_TRYAGAIN; 337 default: 338 return NS_UNAVAIL; 339 } 340 } 341 342 if (search) { /* specific group or gid */ 343 if (name) 344 strlcpy(line, name, maxlinelength); 345 else 346 snprintf(line, maxlinelength, "%u", (unsigned int)gid); 347 data = NULL; 348 r = yp_match(__ypdomain, 349 (name) ? "group.byname" : "group.bygid", 350 line, (int)strlen(line), &data, &datalen); 351 switch (r) { 352 case 0: 353 break; 354 case YPERR_KEY: 355 if (data) 356 free(data); 357 return NS_NOTFOUND; 358 default: 359 if (data) 360 free(data); 361 return NS_UNAVAIL; 362 } 363 data[datalen] = '\0'; /* clear trailing \n */ 364 if (copyline(data) == 0) 365 return NS_UNAVAIL; 366 free(data); 367 if (matchline(search, gid, name)) 368 return NS_SUCCESS; 369 else 370 return NS_NOTFOUND; 371 } 372 373 /* ! search */ 374 if (_gr_ypdone) 375 return NS_NOTFOUND; 376 for (;;) { 377 data = NULL; 378 if(__ypcurrent) { 379 key = NULL; 380 r = yp_next(__ypdomain, "group.byname", 381 __ypcurrent, __ypcurrentlen, 382 &key, &keylen, &data, &datalen); 383 free(__ypcurrent); 384 switch (r) { 385 case 0: 386 break; 387 case YPERR_NOMORE: 388 __ypcurrent = NULL; 389 if (key) 390 free(key); 391 if (data) 392 free(data); 393 _gr_ypdone = 1; 394 return NS_NOTFOUND; 395 default: 396 if (key) 397 free(key); 398 if (data) 399 free(data); 400 return NS_UNAVAIL; 401 } 402 __ypcurrent = key; 403 __ypcurrentlen = keylen; 404 } else { 405 if (yp_first(__ypdomain, "group.byname", 406 &__ypcurrent, &__ypcurrentlen, 407 &data, &datalen)) { 408 if (data) 409 free(data); 410 return NS_UNAVAIL; 411 } 412 } 413 data[datalen] = '\0'; /* clear trailing \n */ 414 if (copyline(data) == 0) 415 return NS_UNAVAIL; 416 free(data); 417 if (matchline(search, gid, name)) 418 return NS_SUCCESS; 419 } 420 /* NOTREACHED */ 421} 422#endif 423 424#ifdef _GROUP_COMPAT 425/* 426 * log an error if "files" or "compat" is specified in group_compat database 427 */ 428static int _bad_grscan __P((void *, void *, va_list)); 429 430/*ARGSUSED*/ 431static int 432_bad_grscan(rv, cb_data, ap) 433 void *rv; 434 void *cb_data; 435 va_list ap; 436{ 437 static int warned; 438 439 if (!warned) { 440 syslog(LOG_ERR, 441 "nsswitch.conf group_compat database can't use '%s'", 442 (char *)cb_data); 443 } 444 warned = 1; 445 return NS_UNAVAIL; 446} 447 448/* 449 * when a name lookup in compat mode is required, look it up in group_compat 450 * nsswitch database. only Hesiod and NIS is supported - it doesn't make 451 * sense to lookup compat names from 'files' or 'compat' 452 */ 453 454static int __grscancompat __P((int, gid_t, const char *)); 455 456static int 457__grscancompat(search, gid, name) 458 int search; 459 gid_t gid; 460 const char *name; 461{ 462 static const ns_dtab dtab[] = { 463 NS_FILES_CB(_bad_grscan, "files") 464 NS_DNS_CB(_dns_grscan, NULL) 465 NS_NIS_CB(_nis_grscan, NULL) 466 NS_COMPAT_CB(_bad_grscan, "compat") 467 { 0 } 468 }; 469 static const ns_src defaultnis[] = { 470 { NSSRC_NIS, NS_SUCCESS }, 471 { 0 } 472 }; 473 474 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", 475 defaultnis, search, gid, name)); 476} 477#endif 478 479 480static int _compat_grscan __P((void *, void *, va_list)); 481 482/*ARGSUSED*/ 483static int 484_compat_grscan(rv, cb_data, ap) 485 void *rv; 486 void *cb_data; 487 va_list ap; 488{ 489 int search = va_arg(ap, int); 490 gid_t gid = va_arg(ap, gid_t); 491 const char *name = va_arg(ap, const char *); 492 493#ifdef _GROUP_COMPAT 494 static char *grname = NULL; 495#endif 496 497 for (;;) { 498#ifdef _GROUP_COMPAT 499 if(__grmode != GRMODE_NONE) { 500 int r; 501 502 switch(__grmode) { 503 case GRMODE_FULL: 504 r = __grscancompat(search, gid, name); 505 if (r == NS_SUCCESS) 506 return r; 507 __grmode = GRMODE_NONE; 508 break; 509 case GRMODE_NAME: 510 if(grname == (char *)NULL) { 511 __grmode = GRMODE_NONE; 512 break; 513 } 514 r = __grscancompat(1, 0, grname); 515 free(grname); 516 grname = (char *)NULL; 517 if (r != NS_SUCCESS) 518 break; 519 if (!search) 520 return NS_SUCCESS; 521 if (name) { 522 if (! strcmp(_gr_group.gr_name, name)) 523 return NS_SUCCESS; 524 } else { 525 if (_gr_group.gr_gid == gid) 526 return NS_SUCCESS; 527 } 528 break; 529 case GRMODE_NONE: 530 abort(); 531 } 532 continue; 533 } 534#endif /* _GROUP_COMPAT */ 535 536 if (getline() == NULL) 537 return NS_NOTFOUND; 538 539#ifdef _GROUP_COMPAT 540 if (line[0] == '+') { 541 char *tptr, *bp; 542 543 switch(line[1]) { 544 case ':': 545 case '\0': 546 case '\n': 547 __grmode = GRMODE_FULL; 548 break; 549 default: 550 __grmode = GRMODE_NAME; 551 bp = line; 552 tptr = strsep(&bp, ":\n"); 553 grname = strdup(tptr + 1); 554 break; 555 } 556 continue; 557 } 558#endif /* _GROUP_COMPAT */ 559 if (matchline(search, gid, name)) 560 return NS_SUCCESS; 561 } 562 /* NOTREACHED */ 563} 564 565static int 566grscan(search, gid, name) 567 int search; 568 gid_t gid; 569 const char *name; 570{ 571 int r; 572 static const ns_dtab dtab[] = { 573 NS_FILES_CB(_local_grscan, NULL) 574 NS_DNS_CB(_dns_grscan, NULL) 575 NS_NIS_CB(_nis_grscan, NULL) 576 NS_COMPAT_CB(_compat_grscan, NULL) 577 { 0 } 578 }; 579 static const ns_src compatsrc[] = { 580 { NSSRC_COMPAT, NS_SUCCESS }, 581 { 0 } 582 }; 583 584 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, 585 search, gid, name); 586 return (r == NS_SUCCESS) ? 1 : 0; 587} 588 589static int 590matchline(search, gid, name) 591 int search; 592 gid_t gid; 593 const char *name; 594{ 595 unsigned long id; 596 char **m; 597 char *cp, *bp, *ep; 598 599 if (line[0] == '+') 600 return 0; /* sanity check to prevent recursion */ 601 bp = line; 602 _gr_group.gr_name = strsep(&bp, ":\n"); 603 if (search && name && strcmp(_gr_group.gr_name, name)) 604 return 0; 605 _gr_group.gr_passwd = strsep(&bp, ":\n"); 606 if (!(cp = strsep(&bp, ":\n"))) 607 return 0; 608 id = strtoul(cp, &ep, 10); 609 if (*ep != '\0') 610 return 0; 611 _gr_group.gr_gid = (gid_t)id; 612 if (search && name == NULL && _gr_group.gr_gid != gid) 613 return 0; 614 cp = NULL; 615 if (bp == NULL) 616 return 0; 617 for (_gr_group.gr_mem = m = members;; bp++) { 618 if (m == &members[maxgrp - 1]) { 619 members = (char **) reallocf(members, sizeof(char **) * 620 (maxgrp + MAXGRP)); 621 if (members == NULL) 622 return 0; 623 _gr_group.gr_mem = members; 624 m = &members[maxgrp - 1]; 625 maxgrp += MAXGRP; 626 } 627 if (*bp == ',') { 628 if (cp) { 629 *bp = '\0'; 630 *m++ = cp; 631 cp = NULL; 632 } 633 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 634 if (cp) { 635 *bp = '\0'; 636 *m++ = cp; 637 } 638 break; 639 } else if (cp == NULL) 640 cp = bp; 641 } 642 *m = NULL; 643 return 1; 644} 645 646static char * 647getline(void) 648{ 649 const char *cp; 650 651 tryagain: 652 if (fgets(line, maxlinelength, _gr_fp) == NULL) 653 return NULL; 654 if (index(line, '\n') == NULL) { 655 do { 656 if (feof(_gr_fp)) 657 return NULL; 658 if (MAXLINELENGTHLIMIT > 0 && 659 maxlinelength >= MAXLINELENGTHLIMIT) 660 return NULL; 661 line = (char *)reallocf(line, maxlinelength + 662 MAXLINELENGTH); 663 if (line == NULL) 664 return NULL; 665 if (fgets(line + maxlinelength - 1, 666 MAXLINELENGTH + 1, _gr_fp) == NULL) 667 return NULL; 668 maxlinelength += MAXLINELENGTH; 669 } while (index(line + maxlinelength - MAXLINELENGTH - 1, 670 '\n') == NULL); 671 } 672 673 674 /* 675 * Ignore comments: ^[ \t]*# 676 */ 677 for (cp = line; *cp != '\0'; cp++) 678 if (*cp != ' ' && *cp != '\t') 679 break; 680 if (*cp == '#' || *cp == '\0') 681 goto tryagain; 682 683 if (cp != line) /* skip white space at beginning of line */ 684 bcopy(cp, line, strlen(cp)); 685 686 return line; 687} 688 689static int 690copyline(const char *src) 691{ 692 size_t sz; 693 694 sz = strlen(src); 695 if (sz > maxlinelength - 1) { 696 sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH; 697 if ((line = (char *) reallocf(line, sz)) == NULL) 698 return 0; 699 maxlinelength = sz; 700 } 701 strlcpy(line, src, maxlinelength); 702 return 1; 703} 704 705