1 /* $NetBSD: getgrent.c,v 1.40 2000/12/17 22:09:12 lukem 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 #if 0 40 static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; 41 #else 42 __RCSID("$NetBSD: getgrent.c,v 1.40 2000/12/17 22:09:12 lukem Exp $"); 43 #endif 44 #endif /* LIBC_SCCS and not lint */ 45 46 #include "namespace.h" 47 48 #include <sys/types.h> 49 50 #include <assert.h> 51 #include <errno.h> 52 #include <grp.h> 53 #include <limits.h> 54 #include <nsswitch.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <syslog.h> 59 60 #ifdef __STDC__ 61 #include <stdarg.h> 62 #else 63 #include <varargs.h> 64 #endif 65 66 #ifdef HESIOD 67 #include <hesiod.h> 68 #endif 69 #ifdef YP 70 #include <rpc/rpc.h> 71 #include <rpcsvc/yp_prot.h> 72 #include <rpcsvc/ypclnt.h> 73 #endif 74 75 #if defined(YP) || defined(HESIOD) 76 #define _GROUP_COMPAT 77 #endif 78 79 #ifdef __weak_alias 80 __weak_alias(endgrent,_endgrent) 81 __weak_alias(getgrent,_getgrent) 82 __weak_alias(getgrgid,_getgrgid) 83 __weak_alias(getgrnam,_getgrnam) 84 __weak_alias(setgrent,_setgrent) 85 __weak_alias(setgroupent,_setgroupent) 86 #endif 87 88 static FILE *_gr_fp; 89 static struct group _gr_group; 90 static int _gr_stayopen; 91 static int _gr_filesdone; 92 93 static void grcleanup __P((void)); 94 static int grscan __P((int, gid_t, const char *)); 95 static int matchline __P((int, gid_t, const char *)); 96 static int start_gr __P((void)); 97 98 #define MAXGRP 200 99 #define MAXLINELENGTH 1024 100 101 static __aconst char *members[MAXGRP]; 102 static char line[MAXLINELENGTH]; 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 _DIAGASSERT(name != NULL); 134 135 if (!start_gr()) 136 return NULL; 137 rval = grscan(1, 0, name); 138 if (!_gr_stayopen) 139 endgrent(); 140 return (rval) ? &_gr_group : NULL; 141 } 142 143 struct group * 144 getgrgid(gid) 145 gid_t gid; 146 { 147 int rval; 148 149 if (!start_gr()) 150 return NULL; 151 rval = grscan(1, gid, NULL); 152 if (!_gr_stayopen) 153 endgrent(); 154 return (rval) ? &_gr_group : NULL; 155 } 156 157 void 158 grcleanup() 159 { 160 _gr_filesdone = 0; 161 #ifdef YP 162 if (__ypcurrent) 163 free(__ypcurrent); 164 __ypcurrent = NULL; 165 _gr_ypdone = 0; 166 #endif 167 #ifdef HESIOD 168 _gr_hesnum = 0; 169 #endif 170 #ifdef _GROUP_COMPAT 171 __grmode = GRMODE_NONE; 172 #endif 173 } 174 175 static int 176 start_gr() 177 { 178 grcleanup(); 179 if (_gr_fp) { 180 rewind(_gr_fp); 181 return 1; 182 } 183 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0; 184 } 185 186 void 187 setgrent() 188 { 189 (void) setgroupent(0); 190 } 191 192 int 193 setgroupent(stayopen) 194 int stayopen; 195 { 196 if (!start_gr()) 197 return 0; 198 _gr_stayopen = stayopen; 199 return 1; 200 } 201 202 void 203 endgrent() 204 { 205 grcleanup(); 206 if (_gr_fp) { 207 (void)fclose(_gr_fp); 208 _gr_fp = NULL; 209 } 210 } 211 212 213 static int _local_grscan __P((void *, void *, va_list)); 214 215 /*ARGSUSED*/ 216 static int 217 _local_grscan(rv, cb_data, ap) 218 void *rv; 219 void *cb_data; 220 va_list ap; 221 { 222 int search = va_arg(ap, int); 223 gid_t gid = va_arg(ap, gid_t); 224 const char *name = va_arg(ap, const char *); 225 226 if (_gr_filesdone) 227 return NS_NOTFOUND; 228 for (;;) { 229 if (!fgets(line, sizeof(line), _gr_fp)) { 230 if (!search) 231 _gr_filesdone = 1; 232 return NS_NOTFOUND; 233 } 234 /* skip lines that are too big */ 235 if (!strchr(line, '\n')) { 236 int ch; 237 238 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 239 ; 240 continue; 241 } 242 if (matchline(search, gid, name)) 243 return NS_SUCCESS; 244 } 245 /* NOTREACHED */ 246 } 247 248 #ifdef HESIOD 249 static int _dns_grscan __P((void *, void *, va_list)); 250 251 /*ARGSUSED*/ 252 static 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 266 r = NS_UNAVAIL; 267 if (!search && _gr_hesnum == -1) 268 return NS_NOTFOUND; 269 if (hesiod_init(&context) == -1) 270 return (r); 271 272 for (;;) { 273 if (search) { 274 if (name) 275 strncpy(line, name, sizeof(line)); 276 else 277 snprintf(line, sizeof(line), "%u", 278 (unsigned int)gid); 279 } else { 280 snprintf(line, sizeof(line), "group-%u", _gr_hesnum); 281 _gr_hesnum++; 282 } 283 284 line[sizeof(line) - 1] = '\0'; 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 strncpy(line, hp[0], sizeof(line)); 297 line[sizeof(line) - 1] = '\0'; 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 __P((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 strncpy(line, name, sizeof(line)); 344 else 345 snprintf(line, sizeof(line), "%u", (unsigned int)gid); 346 line[sizeof(line) - 1] = '\0'; 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 strncpy(line, data, sizeof(line)); 365 line[sizeof(line) - 1] = '\0'; 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 strncpy(line, data, sizeof(line)); 415 line[sizeof(line) - 1] = '\0'; 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 */ 428 static int _bad_grscan __P((void *, void *, va_list)); 429 430 /*ARGSUSED*/ 431 static 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 _DIAGASSERT(cb_data != NULL); 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 _DIAGASSERT(name != NULL); 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 __P((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 (!fgets(line, sizeof(line), _gr_fp)) 541 return NS_NOTFOUND; 542 /* skip lines that are too big */ 543 if (!strchr(line, '\n')) { 544 int ch; 545 546 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 547 ; 548 continue; 549 } 550 551 #ifdef _GROUP_COMPAT 552 if (line[0] == '+') { 553 char *tptr, *bp; 554 555 switch(line[1]) { 556 case ':': 557 case '\0': 558 case '\n': 559 __grmode = GRMODE_FULL; 560 break; 561 default: 562 __grmode = GRMODE_NAME; 563 bp = line; 564 tptr = strsep(&bp, ":\n"); 565 grname = strdup(tptr + 1); 566 break; 567 } 568 continue; 569 } 570 #endif /* _GROUP_COMPAT */ 571 if (matchline(search, gid, name)) 572 return NS_SUCCESS; 573 } 574 /* NOTREACHED */ 575 } 576 577 static int 578 grscan(search, gid, name) 579 int search; 580 gid_t gid; 581 const char *name; 582 { 583 int r; 584 static const ns_dtab dtab[] = { 585 NS_FILES_CB(_local_grscan, NULL) 586 NS_DNS_CB(_dns_grscan, NULL) 587 NS_NIS_CB(_nis_grscan, NULL) 588 NS_COMPAT_CB(_compat_grscan, NULL) 589 { 0 } 590 }; 591 static const ns_src compatsrc[] = { 592 { NSSRC_COMPAT, NS_SUCCESS }, 593 { 0 } 594 }; 595 596 /* name may be NULL if search is nonzero */ 597 598 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, 599 search, gid, name); 600 return (r == NS_SUCCESS) ? 1 : 0; 601 } 602 603 static int 604 matchline(search, gid, name) 605 int search; 606 gid_t gid; 607 const char *name; 608 { 609 unsigned long id; 610 __aconst char **m; 611 char *cp, *bp, *ep; 612 613 /* name may be NULL if search is nonzero */ 614 615 if (line[0] == '+') 616 return 0; /* sanity check to prevent recursion */ 617 bp = line; 618 _gr_group.gr_name = strsep(&bp, ":\n"); 619 if (search && name && strcmp(_gr_group.gr_name, name)) 620 return 0; 621 _gr_group.gr_passwd = strsep(&bp, ":\n"); 622 if (!(cp = strsep(&bp, ":\n"))) 623 return 0; 624 id = strtoul(cp, &ep, 10); 625 if (id > GID_MAX || *ep != '\0') 626 return 0; 627 _gr_group.gr_gid = (gid_t)id; 628 if (search && name == NULL && _gr_group.gr_gid != gid) 629 return 0; 630 cp = NULL; 631 if (bp == NULL) 632 return 0; 633 for (_gr_group.gr_mem = m = members;; bp++) { 634 if (m == &members[MAXGRP - 1]) 635 break; 636 if (*bp == ',') { 637 if (cp) { 638 *bp = '\0'; 639 *m++ = cp; 640 cp = NULL; 641 } 642 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 643 if (cp) { 644 *bp = '\0'; 645 *m++ = cp; 646 } 647 break; 648 } else if (cp == NULL) 649 cp = bp; 650 } 651 *m = NULL; 652 return 1; 653 } 654