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