1 /* $NetBSD: getgrent.c,v 1.42 2002/02/02 15:21:29 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.42 2002/02/02 15:21:29 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 #include <stdarg.h> 61 62 #ifdef HESIOD 63 #include <hesiod.h> 64 #endif 65 #ifdef YP 66 #include <rpc/rpc.h> 67 #include <rpcsvc/yp_prot.h> 68 #include <rpcsvc/ypclnt.h> 69 #endif 70 71 #if defined(YP) || defined(HESIOD) 72 #define _GROUP_COMPAT 73 #endif 74 75 #ifdef __weak_alias 76 __weak_alias(endgrent,_endgrent) 77 __weak_alias(getgrent,_getgrent) 78 __weak_alias(getgrgid,_getgrgid) 79 __weak_alias(getgrnam,_getgrnam) 80 __weak_alias(setgrent,_setgrent) 81 __weak_alias(setgroupent,_setgroupent) 82 #endif 83 84 static FILE *_gr_fp; 85 static struct group _gr_group; 86 static int _gr_stayopen; 87 static int _gr_filesdone; 88 89 static void grcleanup(void); 90 static int grscan(int, gid_t, const char *); 91 static int grstart(void); 92 static int grmatchline(int, gid_t, const char *); 93 94 #define MAXGRP 200 95 #define MAXLINELENGTH 1024 96 97 static __aconst char *members[MAXGRP]; 98 static char line[MAXLINELENGTH]; 99 100 #ifdef YP 101 static char *__ypcurrent, *__ypdomain; 102 static int __ypcurrentlen; 103 static int _gr_ypdone; 104 #endif 105 106 #ifdef HESIOD 107 static int _gr_hesnum; 108 #endif 109 110 #ifdef _GROUP_COMPAT 111 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME }; 112 static enum _grmode __grmode; 113 #endif 114 115 struct group * 116 getgrent(void) 117 { 118 119 if ((!_gr_fp && !grstart()) || !grscan(0, 0, NULL)) 120 return (NULL); 121 return &_gr_group; 122 } 123 124 struct group * 125 getgrnam(const char *name) 126 { 127 int rval; 128 129 _DIAGASSERT(name != NULL); 130 131 if (!grstart()) 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_t gid) 141 { 142 int rval; 143 144 if (!grstart()) 145 return NULL; 146 rval = grscan(1, gid, NULL); 147 if (!_gr_stayopen) 148 endgrent(); 149 return (rval) ? &_gr_group : NULL; 150 } 151 152 void 153 grcleanup(void) 154 { 155 156 _gr_filesdone = 0; 157 #ifdef YP 158 if (__ypcurrent) 159 free(__ypcurrent); 160 __ypcurrent = NULL; 161 _gr_ypdone = 0; 162 #endif 163 #ifdef HESIOD 164 _gr_hesnum = 0; 165 #endif 166 #ifdef _GROUP_COMPAT 167 __grmode = GRMODE_NONE; 168 #endif 169 } 170 171 static int 172 grstart(void) 173 { 174 175 grcleanup(); 176 if (_gr_fp) { 177 rewind(_gr_fp); 178 return 1; 179 } 180 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0; 181 } 182 183 void 184 setgrent(void) 185 { 186 187 (void) setgroupent(0); 188 } 189 190 int 191 setgroupent(int stayopen) 192 { 193 194 if (!grstart()) 195 return 0; 196 _gr_stayopen = stayopen; 197 return 1; 198 } 199 200 void 201 endgrent(void) 202 { 203 204 grcleanup(); 205 if (_gr_fp) { 206 (void)fclose(_gr_fp); 207 _gr_fp = NULL; 208 } 209 } 210 211 212 static int _local_grscan(void *, void *, va_list); 213 214 /*ARGSUSED*/ 215 static int 216 _local_grscan(void *rv, void *cb_data, va_list ap) 217 { 218 int search = va_arg(ap, int); 219 gid_t gid = va_arg(ap, gid_t); 220 const char *name = va_arg(ap, const char *); 221 222 if (_gr_filesdone) 223 return NS_NOTFOUND; 224 for (;;) { 225 if (!fgets(line, sizeof(line), _gr_fp)) { 226 if (!search) 227 _gr_filesdone = 1; 228 return NS_NOTFOUND; 229 } 230 /* skip lines that are too big */ 231 if (!strchr(line, '\n')) { 232 int ch; 233 234 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 235 ; 236 continue; 237 } 238 if (grmatchline(search, gid, name)) 239 return NS_SUCCESS; 240 } 241 /* NOTREACHED */ 242 } 243 244 #ifdef HESIOD 245 static int _dns_grscan(void *, void *, va_list); 246 247 /*ARGSUSED*/ 248 static int 249 _dns_grscan(void *rv, void *cb_data, va_list ap) 250 { 251 int search = va_arg(ap, int); 252 gid_t gid = va_arg(ap, gid_t); 253 const char *name = va_arg(ap, const char *); 254 255 char **hp; 256 void *context; 257 int r; 258 259 r = NS_UNAVAIL; 260 if (!search && _gr_hesnum == -1) 261 return NS_NOTFOUND; 262 if (hesiod_init(&context) == -1) 263 return (r); 264 265 for (;;) { 266 if (search) { 267 if (name) 268 strncpy(line, name, sizeof(line)); 269 else 270 snprintf(line, sizeof(line), "%u", 271 (unsigned int)gid); 272 } else { 273 snprintf(line, sizeof(line), "group-%u", _gr_hesnum); 274 _gr_hesnum++; 275 } 276 277 line[sizeof(line) - 1] = '\0'; 278 hp = hesiod_resolve(context, line, "group"); 279 if (hp == NULL) { 280 if (errno == ENOENT) { 281 if (!search) 282 _gr_hesnum = -1; 283 r = NS_NOTFOUND; 284 } 285 break; 286 } 287 288 /* only check first elem */ 289 strncpy(line, hp[0], sizeof(line)); 290 line[sizeof(line) - 1] = '\0'; 291 hesiod_free_list(context, hp); 292 if (grmatchline(search, gid, name)) { 293 r = NS_SUCCESS; 294 break; 295 } else if (search) { 296 r = NS_NOTFOUND; 297 break; 298 } 299 } 300 hesiod_end(context); 301 return (r); 302 } 303 #endif /* HESIOD */ 304 305 #ifdef YP 306 static int _nis_grscan(void *, void *, va_list); 307 308 /*ARGSUSED*/ 309 static int 310 _nis_grscan(void *rv, void *cb_data, va_list ap) 311 { 312 int search = va_arg(ap, int); 313 gid_t gid = va_arg(ap, gid_t); 314 const char *name = va_arg(ap, const char *); 315 316 char *key, *data; 317 int keylen, datalen; 318 int r; 319 320 if(__ypdomain == NULL) { 321 switch (yp_get_default_domain(&__ypdomain)) { 322 case 0: 323 break; 324 case YPERR_RESRC: 325 return NS_TRYAGAIN; 326 default: 327 return NS_UNAVAIL; 328 } 329 } 330 331 if (search) { /* specific group or gid */ 332 if (name) 333 strncpy(line, name, sizeof(line)); 334 else 335 snprintf(line, sizeof(line), "%u", (unsigned int)gid); 336 line[sizeof(line) - 1] = '\0'; 337 data = NULL; 338 r = yp_match(__ypdomain, 339 (name) ? "group.byname" : "group.bygid", 340 line, (int)strlen(line), &data, &datalen); 341 switch (r) { 342 case 0: 343 break; 344 case YPERR_KEY: 345 if (data) 346 free(data); 347 return NS_NOTFOUND; 348 default: 349 if (data) 350 free(data); 351 return NS_UNAVAIL; 352 } 353 data[datalen] = '\0'; /* clear trailing \n */ 354 strncpy(line, data, sizeof(line)); 355 line[sizeof(line) - 1] = '\0'; 356 free(data); 357 if (grmatchline(search, gid, name)) 358 return NS_SUCCESS; 359 else 360 return NS_NOTFOUND; 361 } 362 363 /* ! search */ 364 if (_gr_ypdone) 365 return NS_NOTFOUND; 366 for (;;) { 367 data = NULL; 368 if(__ypcurrent) { 369 key = NULL; 370 r = yp_next(__ypdomain, "group.byname", 371 __ypcurrent, __ypcurrentlen, 372 &key, &keylen, &data, &datalen); 373 free(__ypcurrent); 374 switch (r) { 375 case 0: 376 break; 377 case YPERR_NOMORE: 378 __ypcurrent = NULL; 379 if (key) 380 free(key); 381 if (data) 382 free(data); 383 _gr_ypdone = 1; 384 return NS_NOTFOUND; 385 default: 386 if (key) 387 free(key); 388 if (data) 389 free(data); 390 return NS_UNAVAIL; 391 } 392 __ypcurrent = key; 393 __ypcurrentlen = keylen; 394 } else { 395 if (yp_first(__ypdomain, "group.byname", 396 &__ypcurrent, &__ypcurrentlen, 397 &data, &datalen)) { 398 if (data) 399 free(data); 400 return NS_UNAVAIL; 401 } 402 } 403 data[datalen] = '\0'; /* clear trailing \n */ 404 strncpy(line, data, sizeof(line)); 405 line[sizeof(line) - 1] = '\0'; 406 free(data); 407 if (grmatchline(search, gid, name)) 408 return NS_SUCCESS; 409 } 410 /* NOTREACHED */ 411 } 412 #endif /* YP */ 413 414 #ifdef _GROUP_COMPAT 415 /* 416 * log an error if "files" or "compat" is specified in group_compat database 417 */ 418 static int _bad_grscan(void *, void *, va_list); 419 420 /*ARGSUSED*/ 421 static int 422 _bad_grscan(void *rv, void *cb_data, va_list ap) 423 { 424 static int warned; 425 426 _DIAGASSERT(cb_data != NULL); 427 428 if (!warned) { 429 syslog(LOG_ERR, 430 "nsswitch.conf group_compat database can't use '%s'", 431 (char *)cb_data); 432 } 433 warned = 1; 434 return NS_UNAVAIL; 435 } 436 437 /* 438 * when a name lookup in compat mode is required, look it up in group_compat 439 * nsswitch database. only Hesiod and NIS is supported - it doesn't make 440 * sense to lookup compat names from 'files' or 'compat' 441 */ 442 443 static int __grscancompat(int, gid_t, const char *); 444 445 static int 446 __grscancompat(int search, gid_t gid, const char *name) 447 { 448 static const ns_dtab dtab[] = { 449 NS_FILES_CB(_bad_grscan, "files") 450 NS_DNS_CB(_dns_grscan, NULL) 451 NS_NIS_CB(_nis_grscan, NULL) 452 NS_COMPAT_CB(_bad_grscan, "compat") 453 { 0 } 454 }; 455 static const ns_src defaultnis[] = { 456 { NSSRC_NIS, NS_SUCCESS }, 457 { 0 } 458 }; 459 460 _DIAGASSERT(name != NULL); 461 462 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", 463 defaultnis, search, gid, name)); 464 } 465 #endif /* GROUP_COMPAT */ 466 467 468 static int _compat_grscan(void *, void *, va_list); 469 470 /*ARGSUSED*/ 471 static int 472 _compat_grscan(void *rv, void *cb_data, va_list ap) 473 { 474 int search = va_arg(ap, int); 475 gid_t gid = va_arg(ap, gid_t); 476 const char *name = va_arg(ap, const char *); 477 478 #ifdef _GROUP_COMPAT 479 static char *grname = NULL; 480 #endif 481 482 for (;;) { 483 #ifdef _GROUP_COMPAT 484 if(__grmode != GRMODE_NONE) { 485 int r; 486 487 switch(__grmode) { 488 case GRMODE_FULL: 489 r = __grscancompat(search, gid, name); 490 if (r == NS_SUCCESS) 491 return r; 492 __grmode = GRMODE_NONE; 493 break; 494 case GRMODE_NAME: 495 if(grname == (char *)NULL) { 496 __grmode = GRMODE_NONE; 497 break; 498 } 499 r = __grscancompat(1, 0, grname); 500 free(grname); 501 grname = (char *)NULL; 502 if (r != NS_SUCCESS) 503 break; 504 if (!search) 505 return NS_SUCCESS; 506 if (name) { 507 if (! strcmp(_gr_group.gr_name, name)) 508 return NS_SUCCESS; 509 } else { 510 if (_gr_group.gr_gid == gid) 511 return NS_SUCCESS; 512 } 513 break; 514 case GRMODE_NONE: 515 abort(); 516 } 517 continue; 518 } 519 #endif /* _GROUP_COMPAT */ 520 521 if (!fgets(line, sizeof(line), _gr_fp)) 522 return NS_NOTFOUND; 523 /* skip lines that are too big */ 524 if (!strchr(line, '\n')) { 525 int ch; 526 527 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 528 ; 529 continue; 530 } 531 532 #ifdef _GROUP_COMPAT 533 if (line[0] == '+') { 534 char *tptr, *bp; 535 536 switch(line[1]) { 537 case ':': 538 case '\0': 539 case '\n': 540 __grmode = GRMODE_FULL; 541 break; 542 default: 543 __grmode = GRMODE_NAME; 544 bp = line; 545 tptr = strsep(&bp, ":\n"); 546 grname = strdup(tptr + 1); 547 break; 548 } 549 continue; 550 } 551 #endif /* _GROUP_COMPAT */ 552 if (grmatchline(search, gid, name)) 553 return NS_SUCCESS; 554 } 555 /* NOTREACHED */ 556 } 557 558 static int 559 grscan(int search, gid_t gid, const char *name) 560 { 561 int r; 562 static const ns_dtab dtab[] = { 563 NS_FILES_CB(_local_grscan, NULL) 564 NS_DNS_CB(_dns_grscan, NULL) 565 NS_NIS_CB(_nis_grscan, NULL) 566 NS_COMPAT_CB(_compat_grscan, NULL) 567 { 0 } 568 }; 569 static const ns_src compatsrc[] = { 570 { NSSRC_COMPAT, NS_SUCCESS }, 571 { 0 } 572 }; 573 574 /* name may be NULL if search is nonzero */ 575 576 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, 577 search, gid, name); 578 return (r == NS_SUCCESS) ? 1 : 0; 579 } 580 581 static int 582 grmatchline(int search, gid_t gid, const char *name) 583 { 584 unsigned long id; 585 __aconst char **m; 586 char *cp, *bp, *ep; 587 588 /* name may be NULL if search is nonzero */ 589 590 if (line[0] == '+') 591 return 0; /* sanity check to prevent recursion */ 592 bp = line; 593 _gr_group.gr_name = strsep(&bp, ":\n"); 594 if (search && name && strcmp(_gr_group.gr_name, name)) 595 return 0; 596 _gr_group.gr_passwd = strsep(&bp, ":\n"); 597 if (!(cp = strsep(&bp, ":\n"))) 598 return 0; 599 id = strtoul(cp, &ep, 10); 600 if (id > GID_MAX || *ep != '\0') 601 return 0; 602 _gr_group.gr_gid = (gid_t)id; 603 if (search && name == NULL && _gr_group.gr_gid != gid) 604 return 0; 605 cp = NULL; 606 if (bp == NULL) 607 return 0; 608 for (_gr_group.gr_mem = m = members;; bp++) { 609 if (m == &members[MAXGRP - 1]) 610 break; 611 if (*bp == ',') { 612 if (cp) { 613 *bp = '\0'; 614 *m++ = cp; 615 cp = NULL; 616 } 617 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 618 if (cp) { 619 *bp = '\0'; 620 *m++ = cp; 621 } 622 break; 623 } else if (cp == NULL) 624 cp = bp; 625 } 626 *m = NULL; 627 return 1; 628 } 629