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 r = copyline(hp[0]); 297 hesiod_free_list(context, hp); 298 if (r == 0) { 299 r = NS_UNAVAIL; 300 break; 301 } 302 if (matchline(search, gid, name)) { 303 r = NS_SUCCESS; 304 break; 305 } else if (search) { 306 r = NS_NOTFOUND; 307 break; 308 } 309 } 310 hesiod_end(context); 311 return (r); 312 } 313 #endif 314 315 #ifdef YP 316 static int _nis_grscan(void *, void *, va_list); 317 318 /*ARGSUSED*/ 319 static int 320 _nis_grscan(rv, cb_data, ap) 321 void *rv; 322 void *cb_data; 323 va_list ap; 324 { 325 int search = va_arg(ap, int); 326 gid_t gid = va_arg(ap, gid_t); 327 const char *name = va_arg(ap, const char *); 328 329 char *key, *data; 330 int keylen, datalen; 331 int r; 332 333 if(__ypdomain == NULL) { 334 switch (yp_get_default_domain(&__ypdomain)) { 335 case 0: 336 break; 337 case YPERR_RESRC: 338 return NS_TRYAGAIN; 339 default: 340 return NS_UNAVAIL; 341 } 342 } 343 344 if (search) { /* specific group or gid */ 345 if (name) 346 strlcpy(line, name, maxlinelength); 347 else 348 snprintf(line, maxlinelength, "%u", (unsigned int)gid); 349 data = NULL; 350 r = yp_match(__ypdomain, 351 (name) ? "group.byname" : "group.bygid", 352 line, (int)strlen(line), &data, &datalen); 353 switch (r) { 354 case 0: 355 break; 356 case YPERR_KEY: 357 if (data) 358 free(data); 359 return NS_NOTFOUND; 360 default: 361 if (data) 362 free(data); 363 return NS_UNAVAIL; 364 } 365 data[datalen] = '\0'; /* clear trailing \n */ 366 r = copyline(data); 367 free(data); 368 if (r == 0) 369 return NS_UNAVAIL; 370 if (matchline(search, gid, name)) 371 return NS_SUCCESS; 372 else 373 return NS_NOTFOUND; 374 } 375 376 /* ! search */ 377 if (_gr_ypdone) 378 return NS_NOTFOUND; 379 for (;;) { 380 data = NULL; 381 if(__ypcurrent) { 382 key = NULL; 383 r = yp_next(__ypdomain, "group.byname", 384 __ypcurrent, __ypcurrentlen, 385 &key, &keylen, &data, &datalen); 386 free(__ypcurrent); 387 switch (r) { 388 case 0: 389 break; 390 case YPERR_NOMORE: 391 __ypcurrent = NULL; 392 if (key) 393 free(key); 394 if (data) 395 free(data); 396 _gr_ypdone = 1; 397 return NS_NOTFOUND; 398 default: 399 if (key) 400 free(key); 401 if (data) 402 free(data); 403 return NS_UNAVAIL; 404 } 405 __ypcurrent = key; 406 __ypcurrentlen = keylen; 407 } else { 408 if (yp_first(__ypdomain, "group.byname", 409 &__ypcurrent, &__ypcurrentlen, 410 &data, &datalen)) { 411 if (data) 412 free(data); 413 return NS_UNAVAIL; 414 } 415 } 416 data[datalen] = '\0'; /* clear trailing \n */ 417 r = copyline(data); 418 free(data); 419 if (r == 0) 420 return NS_UNAVAIL; 421 if (matchline(search, gid, name)) 422 return NS_SUCCESS; 423 } 424 /* NOTREACHED */ 425 } 426 #endif 427 428 #ifdef _GROUP_COMPAT 429 /* 430 * log an error if "files" or "compat" is specified in group_compat database 431 */ 432 static int _bad_grscan(void *, void *, va_list); 433 434 /*ARGSUSED*/ 435 static int 436 _bad_grscan(rv, cb_data, ap) 437 void *rv; 438 void *cb_data; 439 va_list ap; 440 { 441 static int warned; 442 443 if (!warned) { 444 syslog(LOG_ERR, 445 "nsswitch.conf group_compat database can't use '%s'", 446 (char *)cb_data); 447 } 448 warned = 1; 449 return NS_UNAVAIL; 450 } 451 452 /* 453 * when a name lookup in compat mode is required, look it up in group_compat 454 * nsswitch database. only Hesiod and NIS is supported - it doesn't make 455 * sense to lookup compat names from 'files' or 'compat' 456 */ 457 458 static int __grscancompat(int, gid_t, const char *); 459 460 static int 461 __grscancompat(search, gid, name) 462 int search; 463 gid_t gid; 464 const char *name; 465 { 466 static const ns_dtab dtab[] = { 467 NS_FILES_CB(_bad_grscan, "files") 468 NS_DNS_CB(_dns_grscan, NULL) 469 NS_NIS_CB(_nis_grscan, NULL) 470 NS_COMPAT_CB(_bad_grscan, "compat") 471 { 0 } 472 }; 473 static const ns_src defaultnis[] = { 474 { NSSRC_NIS, NS_SUCCESS }, 475 { 0 } 476 }; 477 478 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", 479 defaultnis, search, gid, name)); 480 } 481 #endif 482 483 484 static int _compat_grscan(void *, void *, va_list); 485 486 /*ARGSUSED*/ 487 static int 488 _compat_grscan(rv, cb_data, ap) 489 void *rv; 490 void *cb_data; 491 va_list ap; 492 { 493 int search = va_arg(ap, int); 494 gid_t gid = va_arg(ap, gid_t); 495 const char *name = va_arg(ap, const char *); 496 497 #ifdef _GROUP_COMPAT 498 static char *grname = NULL; 499 #endif 500 501 for (;;) { 502 #ifdef _GROUP_COMPAT 503 if(__grmode != GRMODE_NONE) { 504 int r; 505 506 switch(__grmode) { 507 case GRMODE_FULL: 508 r = __grscancompat(search, gid, name); 509 if (r == NS_SUCCESS) 510 return r; 511 __grmode = GRMODE_NONE; 512 break; 513 case GRMODE_NAME: 514 if(grname == (char *)NULL) { 515 __grmode = GRMODE_NONE; 516 break; 517 } 518 r = __grscancompat(1, 0, grname); 519 free(grname); 520 grname = (char *)NULL; 521 if (r != NS_SUCCESS) 522 break; 523 if (!search) 524 return NS_SUCCESS; 525 if (name) { 526 if (! strcmp(_gr_group.gr_name, name)) 527 return NS_SUCCESS; 528 } else { 529 if (_gr_group.gr_gid == gid) 530 return NS_SUCCESS; 531 } 532 break; 533 case GRMODE_NONE: 534 abort(); 535 } 536 continue; 537 } 538 #endif /* _GROUP_COMPAT */ 539 540 if (getline() == NULL) 541 return NS_NOTFOUND; 542 543 #ifdef _GROUP_COMPAT 544 if (line[0] == '+') { 545 char *tptr, *bp; 546 547 switch(line[1]) { 548 case ':': 549 case '\0': 550 case '\n': 551 __grmode = GRMODE_FULL; 552 break; 553 default: 554 __grmode = GRMODE_NAME; 555 bp = line; 556 tptr = strsep(&bp, ":\n"); 557 grname = strdup(tptr + 1); 558 break; 559 } 560 continue; 561 } 562 #endif /* _GROUP_COMPAT */ 563 if (matchline(search, gid, name)) 564 return NS_SUCCESS; 565 } 566 /* NOTREACHED */ 567 } 568 569 static int 570 grscan(search, gid, name) 571 int search; 572 gid_t gid; 573 const char *name; 574 { 575 int r; 576 static const ns_dtab dtab[] = { 577 NS_FILES_CB(_local_grscan, NULL) 578 NS_DNS_CB(_dns_grscan, NULL) 579 NS_NIS_CB(_nis_grscan, NULL) 580 NS_COMPAT_CB(_compat_grscan, NULL) 581 { 0 } 582 }; 583 static const ns_src compatsrc[] = { 584 { NSSRC_COMPAT, NS_SUCCESS }, 585 { 0 } 586 }; 587 588 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, 589 search, gid, name); 590 return (r == NS_SUCCESS) ? 1 : 0; 591 } 592 593 static int 594 matchline(search, gid, name) 595 int search; 596 gid_t gid; 597 const char *name; 598 { 599 unsigned long id; 600 char **m; 601 char *cp, *bp, *ep; 602 603 if (line[0] == '+') 604 return 0; /* sanity check to prevent recursion */ 605 bp = line; 606 _gr_group.gr_name = strsep(&bp, ":\n"); 607 if (search && name && strcmp(_gr_group.gr_name, name)) 608 return 0; 609 _gr_group.gr_passwd = strsep(&bp, ":\n"); 610 if (!(cp = strsep(&bp, ":\n"))) 611 return 0; 612 id = strtoul(cp, &ep, 10); 613 if (*ep != '\0') 614 return 0; 615 _gr_group.gr_gid = (gid_t)id; 616 if (search && name == NULL && _gr_group.gr_gid != gid) 617 return 0; 618 cp = NULL; 619 if (bp == NULL) 620 return 0; 621 for (_gr_group.gr_mem = m = members;; bp++) { 622 if (m == &members[maxgrp - 1]) { 623 members = (char **) reallocf(members, sizeof(char **) * 624 (maxgrp + MAXGRP)); 625 if (members == NULL) 626 return 0; 627 _gr_group.gr_mem = members; 628 m = &members[maxgrp - 1]; 629 maxgrp += MAXGRP; 630 } 631 if (*bp == ',') { 632 if (cp) { 633 *bp = '\0'; 634 *m++ = cp; 635 cp = NULL; 636 } 637 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 638 if (cp) { 639 *bp = '\0'; 640 *m++ = cp; 641 } 642 break; 643 } else if (cp == NULL) 644 cp = bp; 645 } 646 *m = NULL; 647 return 1; 648 } 649 650 static char * 651 getline(void) 652 { 653 const char *cp; 654 655 tryagain: 656 if (fgets(line, maxlinelength, _gr_fp) == NULL) 657 return NULL; 658 if (index(line, '\n') == NULL) { 659 do { 660 if (feof(_gr_fp)) 661 return NULL; 662 if (MAXLINELENGTHLIMIT > 0 && 663 maxlinelength >= MAXLINELENGTHLIMIT) 664 return NULL; 665 line = (char *)reallocf(line, maxlinelength + 666 MAXLINELENGTH); 667 if (line == NULL) 668 return NULL; 669 if (fgets(line + maxlinelength - 1, 670 MAXLINELENGTH + 1, _gr_fp) == NULL) 671 return NULL; 672 maxlinelength += MAXLINELENGTH; 673 } while (index(line + maxlinelength - MAXLINELENGTH - 1, 674 '\n') == NULL); 675 } 676 677 678 /* 679 * Ignore comments: ^[ \t]*# 680 */ 681 for (cp = line; *cp != '\0'; cp++) 682 if (*cp != ' ' && *cp != '\t') 683 break; 684 if (*cp == '#' || *cp == '\0') 685 goto tryagain; 686 687 if (cp != line) /* skip white space at beginning of line */ 688 bcopy(cp, line, strlen(cp)); 689 690 return line; 691 } 692 693 static int 694 copyline(const char *src) 695 { 696 size_t sz; 697 698 sz = strlen(src); 699 if (sz > maxlinelength - 1) { 700 sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH; 701 if ((line = (char *) reallocf(line, sz)) == NULL) 702 return 0; 703 maxlinelength = sz; 704 } 705 strlcpy(line, src, maxlinelength); 706 return 1; 707 } 708 709