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