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