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