1 /* $NetBSD: getgrent.c,v 1.43 2002/11/17 01:51:24 itojun 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.43 2002/11/17 01:51:24 itojun 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 strlcpy(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 hp = hesiod_resolve(context, line, "group"); 278 if (hp == NULL) { 279 if (errno == ENOENT) { 280 if (!search) 281 _gr_hesnum = -1; 282 r = NS_NOTFOUND; 283 } 284 break; 285 } 286 287 /* only check first elem */ 288 strlcpy(line, hp[0], sizeof(line)); 289 hesiod_free_list(context, hp); 290 if (grmatchline(search, gid, name)) { 291 r = NS_SUCCESS; 292 break; 293 } else if (search) { 294 r = NS_NOTFOUND; 295 break; 296 } 297 } 298 hesiod_end(context); 299 return (r); 300 } 301 #endif /* HESIOD */ 302 303 #ifdef YP 304 static int _nis_grscan(void *, void *, va_list); 305 306 /*ARGSUSED*/ 307 static int 308 _nis_grscan(void *rv, void *cb_data, va_list ap) 309 { 310 int search = va_arg(ap, int); 311 gid_t gid = va_arg(ap, gid_t); 312 const char *name = va_arg(ap, const char *); 313 314 char *key, *data; 315 int keylen, datalen; 316 int r; 317 318 if(__ypdomain == NULL) { 319 switch (yp_get_default_domain(&__ypdomain)) { 320 case 0: 321 break; 322 case YPERR_RESRC: 323 return NS_TRYAGAIN; 324 default: 325 return NS_UNAVAIL; 326 } 327 } 328 329 if (search) { /* specific group or gid */ 330 if (name) 331 strlcpy(line, name, sizeof(line)); 332 else 333 snprintf(line, sizeof(line), "%u", (unsigned int)gid); 334 data = NULL; 335 r = yp_match(__ypdomain, 336 (name) ? "group.byname" : "group.bygid", 337 line, (int)strlen(line), &data, &datalen); 338 switch (r) { 339 case 0: 340 break; 341 case YPERR_KEY: 342 if (data) 343 free(data); 344 return NS_NOTFOUND; 345 default: 346 if (data) 347 free(data); 348 return NS_UNAVAIL; 349 } 350 data[datalen] = '\0'; /* clear trailing \n */ 351 strlcpy(line, data, sizeof(line)); 352 free(data); 353 if (grmatchline(search, gid, name)) 354 return NS_SUCCESS; 355 else 356 return NS_NOTFOUND; 357 } 358 359 /* ! search */ 360 if (_gr_ypdone) 361 return NS_NOTFOUND; 362 for (;;) { 363 data = NULL; 364 if(__ypcurrent) { 365 key = NULL; 366 r = yp_next(__ypdomain, "group.byname", 367 __ypcurrent, __ypcurrentlen, 368 &key, &keylen, &data, &datalen); 369 free(__ypcurrent); 370 switch (r) { 371 case 0: 372 break; 373 case YPERR_NOMORE: 374 __ypcurrent = NULL; 375 if (key) 376 free(key); 377 if (data) 378 free(data); 379 _gr_ypdone = 1; 380 return NS_NOTFOUND; 381 default: 382 if (key) 383 free(key); 384 if (data) 385 free(data); 386 return NS_UNAVAIL; 387 } 388 __ypcurrent = key; 389 __ypcurrentlen = keylen; 390 } else { 391 if (yp_first(__ypdomain, "group.byname", 392 &__ypcurrent, &__ypcurrentlen, 393 &data, &datalen)) { 394 if (data) 395 free(data); 396 return NS_UNAVAIL; 397 } 398 } 399 data[datalen] = '\0'; /* clear trailing \n */ 400 strlcpy(line, data, sizeof(line)); 401 free(data); 402 if (grmatchline(search, gid, name)) 403 return NS_SUCCESS; 404 } 405 /* NOTREACHED */ 406 } 407 #endif /* YP */ 408 409 #ifdef _GROUP_COMPAT 410 /* 411 * log an error if "files" or "compat" is specified in group_compat database 412 */ 413 static int _bad_grscan(void *, void *, va_list); 414 415 /*ARGSUSED*/ 416 static int 417 _bad_grscan(void *rv, void *cb_data, va_list ap) 418 { 419 static int warned; 420 421 _DIAGASSERT(cb_data != NULL); 422 423 if (!warned) { 424 syslog(LOG_ERR, 425 "nsswitch.conf group_compat database can't use '%s'", 426 (char *)cb_data); 427 } 428 warned = 1; 429 return NS_UNAVAIL; 430 } 431 432 /* 433 * when a name lookup in compat mode is required, look it up in group_compat 434 * nsswitch database. only Hesiod and NIS is supported - it doesn't make 435 * sense to lookup compat names from 'files' or 'compat' 436 */ 437 438 static int __grscancompat(int, gid_t, const char *); 439 440 static int 441 __grscancompat(int search, gid_t gid, const char *name) 442 { 443 static const ns_dtab dtab[] = { 444 NS_FILES_CB(_bad_grscan, "files") 445 NS_DNS_CB(_dns_grscan, NULL) 446 NS_NIS_CB(_nis_grscan, NULL) 447 NS_COMPAT_CB(_bad_grscan, "compat") 448 { 0 } 449 }; 450 static const ns_src defaultnis[] = { 451 { NSSRC_NIS, NS_SUCCESS }, 452 { 0 } 453 }; 454 455 _DIAGASSERT(name != NULL); 456 457 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", 458 defaultnis, search, gid, name)); 459 } 460 #endif /* GROUP_COMPAT */ 461 462 463 static int _compat_grscan(void *, void *, va_list); 464 465 /*ARGSUSED*/ 466 static int 467 _compat_grscan(void *rv, void *cb_data, va_list ap) 468 { 469 int search = va_arg(ap, int); 470 gid_t gid = va_arg(ap, gid_t); 471 const char *name = va_arg(ap, const char *); 472 473 #ifdef _GROUP_COMPAT 474 static char *grname = NULL; 475 #endif 476 477 for (;;) { 478 #ifdef _GROUP_COMPAT 479 if(__grmode != GRMODE_NONE) { 480 int r; 481 482 switch(__grmode) { 483 case GRMODE_FULL: 484 r = __grscancompat(search, gid, name); 485 if (r == NS_SUCCESS) 486 return r; 487 __grmode = GRMODE_NONE; 488 break; 489 case GRMODE_NAME: 490 if(grname == (char *)NULL) { 491 __grmode = GRMODE_NONE; 492 break; 493 } 494 r = __grscancompat(1, 0, grname); 495 free(grname); 496 grname = (char *)NULL; 497 if (r != NS_SUCCESS) 498 break; 499 if (!search) 500 return NS_SUCCESS; 501 if (name) { 502 if (! strcmp(_gr_group.gr_name, name)) 503 return NS_SUCCESS; 504 } else { 505 if (_gr_group.gr_gid == gid) 506 return NS_SUCCESS; 507 } 508 break; 509 case GRMODE_NONE: 510 abort(); 511 } 512 continue; 513 } 514 #endif /* _GROUP_COMPAT */ 515 516 if (!fgets(line, sizeof(line), _gr_fp)) 517 return NS_NOTFOUND; 518 /* skip lines that are too big */ 519 if (!strchr(line, '\n')) { 520 int ch; 521 522 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 523 ; 524 continue; 525 } 526 527 #ifdef _GROUP_COMPAT 528 if (line[0] == '+') { 529 char *tptr, *bp; 530 531 switch(line[1]) { 532 case ':': 533 case '\0': 534 case '\n': 535 __grmode = GRMODE_FULL; 536 break; 537 default: 538 __grmode = GRMODE_NAME; 539 bp = line; 540 tptr = strsep(&bp, ":\n"); 541 grname = strdup(tptr + 1); 542 break; 543 } 544 continue; 545 } 546 #endif /* _GROUP_COMPAT */ 547 if (grmatchline(search, gid, name)) 548 return NS_SUCCESS; 549 } 550 /* NOTREACHED */ 551 } 552 553 static int 554 grscan(int search, gid_t gid, const char *name) 555 { 556 int r; 557 static const ns_dtab dtab[] = { 558 NS_FILES_CB(_local_grscan, NULL) 559 NS_DNS_CB(_dns_grscan, NULL) 560 NS_NIS_CB(_nis_grscan, NULL) 561 NS_COMPAT_CB(_compat_grscan, NULL) 562 { 0 } 563 }; 564 static const ns_src compatsrc[] = { 565 { NSSRC_COMPAT, NS_SUCCESS }, 566 { 0 } 567 }; 568 569 /* name may be NULL if search is nonzero */ 570 571 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, 572 search, gid, name); 573 return (r == NS_SUCCESS) ? 1 : 0; 574 } 575 576 static int 577 grmatchline(int search, gid_t gid, const char *name) 578 { 579 unsigned long id; 580 __aconst char **m; 581 char *cp, *bp, *ep; 582 583 /* name may be NULL if search is nonzero */ 584 585 if (line[0] == '+') 586 return 0; /* sanity check to prevent recursion */ 587 bp = line; 588 _gr_group.gr_name = strsep(&bp, ":\n"); 589 if (search && name && strcmp(_gr_group.gr_name, name)) 590 return 0; 591 _gr_group.gr_passwd = strsep(&bp, ":\n"); 592 if (!(cp = strsep(&bp, ":\n"))) 593 return 0; 594 id = strtoul(cp, &ep, 10); 595 if (id > GID_MAX || *ep != '\0') 596 return 0; 597 _gr_group.gr_gid = (gid_t)id; 598 if (search && name == NULL && _gr_group.gr_gid != gid) 599 return 0; 600 cp = NULL; 601 if (bp == NULL) 602 return 0; 603 for (_gr_group.gr_mem = m = members;; bp++) { 604 if (m == &members[MAXGRP - 1]) 605 break; 606 if (*bp == ',') { 607 if (cp) { 608 *bp = '\0'; 609 *m++ = cp; 610 cp = NULL; 611 } 612 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 613 if (cp) { 614 *bp = '\0'; 615 *m++ = cp; 616 } 617 break; 618 } else if (cp == NULL) 619 cp = bp; 620 } 621 *m = NULL; 622 return 1; 623 } 624