1 /* $NetBSD: getgrent.c,v 1.35 1999/04/18 02:04:04 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.35 1999/04/18 02:04:04 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 <errno.h> 51 #include <grp.h> 52 #include <limits.h> 53 #include <nsswitch.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <syslog.h> 58 59 #ifdef __STDC__ 60 #include <stdarg.h> 61 #else 62 #include <varargs.h> 63 #endif 64 65 #ifdef HESIOD 66 #include <hesiod.h> 67 #endif 68 #ifdef YP 69 #include <rpc/rpc.h> 70 #include <rpcsvc/yp_prot.h> 71 #include <rpcsvc/ypclnt.h> 72 #endif 73 74 #if defined(YP) || defined(HESIOD) 75 #define _GROUP_COMPAT 76 #endif 77 78 #ifdef __weak_alias 79 __weak_alias(endgrent,_endgrent); 80 __weak_alias(getgrent,_getgrent); 81 __weak_alias(getgrgid,_getgrgid); 82 __weak_alias(getgrnam,_getgrnam); 83 __weak_alias(setgrent,_setgrent); 84 __weak_alias(setgroupent,_setgroupent); 85 #endif 86 87 static FILE *_gr_fp; 88 static struct group _gr_group; 89 static int _gr_stayopen; 90 static int _gr_nomore; 91 92 static int grscan __P((int, gid_t, const char *)); 93 static int matchline __P((int, gid_t, const char *)); 94 static int start_gr __P((void)); 95 96 #define MAXGRP 200 97 #define MAXLINELENGTH 1024 98 99 static __aconst char *members[MAXGRP]; 100 static char line[MAXLINELENGTH]; 101 102 #ifdef YP 103 static char *__ypcurrent, *__ypdomain; 104 static int __ypcurrentlen; 105 #endif 106 107 #ifdef HESIOD 108 static int __gr_hesnum; 109 #endif 110 111 #ifdef _GROUP_COMPAT 112 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME }; 113 static enum _grmode __grmode; 114 #endif 115 116 struct group * 117 getgrent() 118 { 119 _gr_nomore = 0; 120 if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL) || _gr_nomore) 121 return(NULL); 122 return &_gr_group; 123 } 124 125 struct group * 126 getgrnam(name) 127 const char *name; 128 { 129 int rval; 130 131 if (!start_gr()) 132 return NULL; 133 rval = grscan(1, 0, name); 134 if (!_gr_stayopen) 135 endgrent(); 136 return (rval) ? &_gr_group : NULL; 137 } 138 139 struct group * 140 getgrgid(gid) 141 gid_t gid; 142 { 143 int rval; 144 145 if (!start_gr()) 146 return NULL; 147 rval = grscan(1, gid, NULL); 148 if (!_gr_stayopen) 149 endgrent(); 150 return (rval) ? &_gr_group : NULL; 151 } 152 153 static int 154 start_gr() 155 { 156 #ifdef YP 157 if (__ypcurrent) 158 free(__ypcurrent); 159 __ypcurrent = NULL; 160 #endif 161 #ifdef HESIOD 162 __gr_hesnum = 0; 163 #endif 164 #ifdef _GROUP_COMPAT 165 __grmode = GRMODE_NONE; 166 #endif 167 if (_gr_fp) { 168 rewind(_gr_fp); 169 return 1; 170 } 171 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0; 172 } 173 174 void 175 setgrent() 176 { 177 (void) setgroupent(0); 178 } 179 180 int 181 setgroupent(stayopen) 182 int stayopen; 183 { 184 if (!start_gr()) 185 return 0; 186 _gr_stayopen = stayopen; 187 return 1; 188 } 189 190 void 191 endgrent() 192 { 193 #ifdef YP 194 if (__ypcurrent) 195 free(__ypcurrent); 196 __ypcurrent = NULL; 197 #endif 198 #ifdef HESIOD 199 __gr_hesnum = 0; 200 #endif 201 #ifdef _GROUP_COMPAT 202 __grmode = GRMODE_NONE; 203 #endif 204 if (_gr_fp) { 205 (void)fclose(_gr_fp); 206 _gr_fp = NULL; 207 } 208 } 209 210 211 static int _local_grscan __P((void *, void *, va_list)); 212 213 /*ARGSUSED*/ 214 static int 215 _local_grscan(rv, cb_data, ap) 216 void *rv; 217 void *cb_data; 218 va_list ap; 219 { 220 int search = va_arg(ap, int); 221 gid_t gid = va_arg(ap, gid_t); 222 const char *name = va_arg(ap, const char *); 223 224 for (;;) { 225 if (!fgets(line, sizeof(line), _gr_fp)) { 226 if (!search) { 227 _gr_nomore = 1; 228 return NS_SUCCESS; 229 } 230 return NS_NOTFOUND; 231 } 232 /* skip lines that are too big */ 233 if (!strchr(line, '\n')) { 234 int ch; 235 236 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 237 ; 238 continue; 239 } 240 if (matchline(search, gid, name)) 241 return NS_SUCCESS; 242 } 243 /* NOTREACHED */ 244 } 245 246 #ifdef HESIOD 247 static int _dns_grscan __P((void *, void *, va_list)); 248 249 /*ARGSUSED*/ 250 static int 251 _dns_grscan(rv, cb_data, ap) 252 void *rv; 253 void *cb_data; 254 va_list ap; 255 { 256 int search = va_arg(ap, int); 257 gid_t gid = va_arg(ap, gid_t); 258 const char *name = va_arg(ap, const char *); 259 260 char **hp; 261 void *context; 262 int r; 263 264 r = NS_UNAVAIL; 265 if (hesiod_init(&context) == -1) 266 return (r); 267 268 for (;;) { 269 if (search) { 270 if (name) 271 strncpy(line, name, sizeof(line)); 272 else 273 snprintf(line, sizeof(line), "%u", 274 (unsigned int)gid); 275 } else { 276 snprintf(line, sizeof(line), "group-%u", __gr_hesnum); 277 __gr_hesnum++; 278 } 279 280 line[sizeof(line) - 1] = '\0'; 281 hp = hesiod_resolve(context, line, "group"); 282 if (hp == NULL) { 283 if (errno == ENOENT) { 284 if (!search) { 285 __gr_hesnum = 0; 286 _gr_nomore = 1; 287 r = NS_SUCCESS; 288 } else 289 r = NS_NOTFOUND; 290 } 291 break; 292 } 293 294 /* only check first elem */ 295 strncpy(line, hp[0], sizeof(line)); 296 line[sizeof(line) - 1] = '\0'; 297 hesiod_free_list(context, hp); 298 if (matchline(search, gid, name)) { 299 r = NS_SUCCESS; 300 break; 301 } else if (search) { 302 r = NS_NOTFOUND; 303 break; 304 } 305 } 306 hesiod_end(context); 307 return (r); 308 } 309 #endif 310 311 #ifdef YP 312 static int _nis_grscan __P((void *, void *, va_list)); 313 314 /*ARGSUSED*/ 315 static int 316 _nis_grscan(rv, cb_data, ap) 317 void *rv; 318 void *cb_data; 319 va_list ap; 320 { 321 int search = va_arg(ap, int); 322 gid_t gid = va_arg(ap, gid_t); 323 const char *name = va_arg(ap, const char *); 324 325 char *key, *data; 326 int keylen, datalen; 327 int r; 328 329 if(__ypdomain == NULL) { 330 switch (yp_get_default_domain(&__ypdomain)) { 331 case 0: 332 break; 333 case YPERR_RESRC: 334 return NS_TRYAGAIN; 335 default: 336 return NS_UNAVAIL; 337 } 338 } 339 340 if (search) { /* specific group or gid */ 341 if (name) 342 strncpy(line, name, sizeof(line)); 343 else 344 snprintf(line, sizeof(line), "%u", (unsigned int)gid); 345 line[sizeof(line) - 1] = '\0'; 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 strncpy(line, data, sizeof(line)); 364 line[sizeof(line) - 1] = '\0'; 365 free(data); 366 if (matchline(search, gid, name)) 367 return NS_SUCCESS; 368 else 369 return NS_NOTFOUND; 370 } 371 372 for (;;) { /* ! search */ 373 data = NULL; 374 if(__ypcurrent) { 375 key = NULL; 376 r = yp_next(__ypdomain, "group.byname", 377 __ypcurrent, __ypcurrentlen, 378 &key, &keylen, &data, &datalen); 379 free(__ypcurrent); 380 switch (r) { 381 case 0: 382 break; 383 case YPERR_NOMORE: 384 __ypcurrent = NULL; 385 if (key) 386 free(key); 387 if (data) 388 free(data); 389 _gr_nomore = 1; 390 return NS_SUCCESS; 391 default: 392 if (key) 393 free(key); 394 if (data) 395 free(data); 396 return NS_UNAVAIL; 397 } 398 __ypcurrent = key; 399 __ypcurrentlen = keylen; 400 } else { 401 if (yp_first(__ypdomain, "group.byname", 402 &__ypcurrent, &__ypcurrentlen, 403 &data, &datalen)) { 404 if (data); 405 free(data); 406 return NS_UNAVAIL; 407 } 408 } 409 data[datalen] = '\0'; /* clear trailing \n */ 410 strncpy(line, data, sizeof(line)); 411 line[sizeof(line) - 1] = '\0'; 412 free(data); 413 if (matchline(search, gid, name)) 414 return NS_SUCCESS; 415 } 416 /* NOTREACHED */ 417 } 418 #endif 419 420 #ifdef _GROUP_COMPAT 421 /* 422 * log an error if "files" or "compat" is specified in group_compat database 423 */ 424 static int _bad_grscan __P((void *, void *, va_list)); 425 426 /*ARGSUSED*/ 427 static int 428 _bad_grscan(rv, cb_data, ap) 429 void *rv; 430 void *cb_data; 431 va_list ap; 432 { 433 static int warned; 434 435 if (!warned) { 436 syslog(LOG_ERR, 437 "nsswitch.conf group_compat database can't use '%s'", 438 (char *)cb_data); 439 } 440 warned = 1; 441 return NS_UNAVAIL; 442 } 443 444 /* 445 * when a name lookup in compat mode is required, look it up in group_compat 446 * nsswitch database. only Hesiod and NIS is supported - it doesn't make 447 * sense to lookup compat names from 'files' or 'compat' 448 */ 449 450 static int __grscancompat __P((int, gid_t, const char *)); 451 452 static int 453 __grscancompat(search, gid, name) 454 int search; 455 gid_t gid; 456 const char *name; 457 { 458 static const ns_dtab dtab[] = { 459 NS_FILES_CB(_bad_grscan, "files") 460 NS_DNS_CB(_dns_grscan, NULL) 461 NS_NIS_CB(_nis_grscan, NULL) 462 NS_COMPAT_CB(_bad_grscan, "compat") 463 { 0 } 464 }; 465 static const ns_src defaultnis[] = { 466 { NSSRC_NIS, NS_SUCCESS }, 467 { 0 } 468 }; 469 470 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", 471 defaultnis, search, gid, name)); 472 } 473 #endif 474 475 476 static int _compat_grscan __P((void *, void *, va_list)); 477 478 /*ARGSUSED*/ 479 static int 480 _compat_grscan(rv, cb_data, ap) 481 void *rv; 482 void *cb_data; 483 va_list ap; 484 { 485 int search = va_arg(ap, int); 486 gid_t gid = va_arg(ap, gid_t); 487 const char *name = va_arg(ap, const char *); 488 489 #ifdef _GROUP_COMPAT 490 static char *grname = NULL; 491 #endif 492 493 for (;;) { 494 #ifdef _GROUP_COMPAT 495 if(__grmode != GRMODE_NONE) { 496 int r; 497 498 switch(__grmode) { 499 case GRMODE_FULL: 500 r = __grscancompat(search, gid, name); 501 if (r == NS_SUCCESS) 502 return r; 503 __grmode = GRMODE_NONE; 504 break; 505 case GRMODE_NAME: 506 if(grname == (char *)NULL) { 507 __grmode = GRMODE_NONE; 508 break; 509 } 510 r = __grscancompat(1, 0, grname); 511 free(grname); 512 grname = (char *)NULL; 513 if (r != NS_SUCCESS) 514 break; 515 if (!search) 516 return NS_SUCCESS; 517 if (name) { 518 if (! strcmp(_gr_group.gr_name, name)) 519 return NS_SUCCESS; 520 } else { 521 if (_gr_group.gr_gid == gid) 522 return NS_SUCCESS; 523 } 524 break; 525 case GRMODE_NONE: 526 abort(); 527 } 528 continue; 529 } 530 #endif /* _GROUP_COMPAT */ 531 532 if (!fgets(line, sizeof(line), _gr_fp)) 533 return NS_NOTFOUND; 534 /* skip lines that are too big */ 535 if (!strchr(line, '\n')) { 536 int ch; 537 538 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 539 ; 540 continue; 541 } 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 __aconst 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 (id > GID_MAX || *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 break; 624 if (*bp == ',') { 625 if (cp) { 626 *bp = '\0'; 627 *m++ = cp; 628 cp = NULL; 629 } 630 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 631 if (cp) { 632 *bp = '\0'; 633 *m++ = cp; 634 } 635 break; 636 } else if (cp == NULL) 637 cp = bp; 638 } 639 *m = NULL; 640 return 1; 641 } 642