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