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