1 /* $NetBSD: getgrent.c,v 1.34.2.1 1999/04/27 14:10:58 perry Exp $ */ 2 /* 3 * Copyright (c) 1989, 1993 4 * The Regents of the University of California. All rights reserved. 5 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)getgrent.c 8.2 (Berkeley) 3/21/94 36 * $FreeBSD: src/lib/libc/gen/getgrent.c,v 1.17.6.1 2001/03/05 08:56:02 obrien Exp $ 37 * $DragonFly: src/lib/libc/gen/getgrent.c,v 1.4 2005/04/27 12:36:31 joerg Exp $ 38 */ 39 40 #include <errno.h> 41 #include <limits.h> 42 #include <sys/types.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <grp.h> 47 #include <syslog.h> 48 49 static FILE *_gr_fp; 50 static struct group _gr_group; 51 static int _gr_stayopen; 52 53 static int grscan(int, gid_t, const char *); 54 static int start_gr(void); 55 56 #ifdef YP 57 #include <rpc/rpc.h> 58 #include <rpcsvc/yp_prot.h> 59 #include <rpcsvc/ypclnt.h> 60 static int _gr_stepping_yp; 61 static int _gr_yp_enabled; 62 static int _getypgroup(struct group *, const char *, const char *); 63 static int _nextypgroup(struct group *); 64 #endif 65 66 /* initial size for malloc and increase steps for realloc */ 67 #define MAXGRP 64 68 #define MAXLINELENGTH 256 69 70 static char **members; /* list of group members */ 71 static int maxgrp; /* current length of **mebers */ 72 static char *line; /* temp buffer for group line */ 73 static int maxlinelength; /* current length of *line */ 74 75 /* 76 * Lines longer than MAXLINELENGTHLIMIT will be counted as an error. 77 * <= 0 disable check for maximum line length 78 * 256K is enough for 64,000 uids 79 */ 80 #define MAXLINELENGTHLIMIT (256 * 1024) 81 #define GROUP_IGNORE_COMMENTS 1 /* allow comments in /etc/group */ 82 83 struct group * 84 getgrent(void) 85 { 86 if (!_gr_fp && !start_gr()) { 87 return NULL; 88 } 89 90 #ifdef YP 91 if (_gr_stepping_yp) { 92 if (_nextypgroup(&_gr_group)) 93 return(&_gr_group); 94 } 95 tryagain: 96 #endif 97 98 if (!grscan(0, 0, NULL)) 99 return(NULL); 100 #ifdef YP 101 if(_gr_group.gr_name[0] == '+' && _gr_group.gr_name[1]) { 102 _getypgroup(&_gr_group, &_gr_group.gr_name[1], 103 "group.byname"); 104 } else if(_gr_group.gr_name[0] == '+') { 105 if (!_nextypgroup(&_gr_group)) 106 goto tryagain; 107 else 108 return(&_gr_group); 109 } 110 #endif 111 return(&_gr_group); 112 } 113 114 struct group * 115 getgrnam(const char *name) 116 { 117 int rval; 118 119 if (!start_gr()) 120 return(NULL); 121 #ifdef YP 122 tryagain: 123 #endif 124 rval = grscan(1, 0, name); 125 #ifdef YP 126 if(rval == -1 && (_gr_yp_enabled < 0 || (_gr_yp_enabled && 127 _gr_group.gr_name[0] == '+'))) { 128 if (!(rval = _getypgroup(&_gr_group, name, "group.byname"))) 129 goto tryagain; 130 } 131 #endif 132 if (!_gr_stayopen) 133 endgrent(); 134 return (rval) ? &_gr_group : NULL; 135 } 136 137 struct group * 138 getgrgid(gid_t gid) 139 { 140 int rval; 141 142 if (!start_gr()) 143 return(NULL); 144 #ifdef YP 145 tryagain: 146 #endif 147 rval = grscan(1, gid, NULL); 148 #ifdef YP 149 if(rval == -1 && _gr_yp_enabled) { 150 char buf[16]; 151 snprintf(buf, sizeof buf, "%d", (unsigned)gid); 152 if (!(rval = _getypgroup(&_gr_group, buf, "group.bygid"))) 153 goto tryagain; 154 } 155 #endif 156 if (!_gr_stayopen) 157 endgrent(); 158 return (rval) ? &_gr_group : NULL; 159 } 160 161 static int 162 start_gr(void) 163 { 164 if (_gr_fp) { 165 rewind(_gr_fp); 166 return(1); 167 } 168 _gr_fp = fopen(_PATH_GROUP, "r"); 169 if(!_gr_fp) return 0; 170 #ifdef YP 171 /* 172 * This is a disgusting hack, used to determine when YP is enabled. 173 * This would be easier if we had a group database to go along with 174 * the password database. 175 */ 176 { 177 char *my_line; 178 size_t linelen; 179 _gr_yp_enabled = 0; 180 while((my_line = fgetln(_gr_fp, &linelen)) != NULL) { 181 if(my_line[0] == '+') { 182 if(my_line[1] && my_line[1] != ':' && !_gr_yp_enabled) { 183 _gr_yp_enabled = 1; 184 } else { 185 _gr_yp_enabled = -1; 186 break; 187 } 188 } 189 } 190 rewind(_gr_fp); 191 } 192 #endif 193 194 if (maxlinelength == 0) { 195 if ((line = (char *)malloc(MAXLINELENGTH)) == NULL) 196 return 0; 197 maxlinelength += MAXLINELENGTH; 198 } 199 200 if (maxgrp == 0) { 201 if ((members = (char **) malloc(sizeof(char **) * 202 MAXGRP)) == NULL) 203 return 0; 204 maxgrp += MAXGRP; 205 } 206 207 return 1; 208 } 209 210 int 211 setgrent(void) 212 { 213 return setgroupent(0); 214 } 215 216 int 217 setgroupent(int stayopen) 218 { 219 if (!start_gr()) 220 return 0; 221 _gr_stayopen = stayopen; 222 #ifdef YP 223 _gr_stepping_yp = 0; 224 #endif 225 return 1; 226 } 227 228 void 229 endgrent(void) 230 { 231 #ifdef YP 232 _gr_stepping_yp = 0; 233 #endif 234 if (_gr_fp) { 235 (void)fclose(_gr_fp); 236 _gr_fp = NULL; 237 } 238 } 239 240 static int 241 grscan(int search, gid_t gid, const char *name) 242 { 243 char *cp, **m; 244 char *bp; 245 246 247 #ifdef YP 248 int _ypfound; 249 #endif 250 for (;;) { 251 #ifdef YP 252 _ypfound = 0; 253 #endif 254 if (fgets(line, maxlinelength, _gr_fp) == NULL) 255 return(0); 256 257 if (!index(line, '\n')) { 258 do { 259 if (feof(_gr_fp)) 260 return(0); 261 262 /* don't allocate infinite memory */ 263 if (MAXLINELENGTHLIMIT > 0 && 264 maxlinelength >= MAXLINELENGTHLIMIT) 265 return(0); 266 267 if ((line = reallocf(line, 268 (maxlinelength + MAXLINELENGTH))) == NULL) 269 return(0); 270 271 if (fgets(line + maxlinelength - 1, 272 MAXLINELENGTH + 1, _gr_fp) == NULL) 273 return(0); 274 275 maxlinelength += MAXLINELENGTH; 276 } while (!index(line + maxlinelength - 277 MAXLINELENGTH - 1, '\n')); 278 } 279 280 #ifdef GROUP_IGNORE_COMMENTS 281 /* 282 * Ignore comments: ^[ \t]*# 283 */ 284 for (cp = line; *cp != '\0'; cp++) 285 if (*cp != ' ' && *cp != '\t') 286 break; 287 if (*cp == '#' || *cp == '\0') 288 continue; 289 #endif 290 291 bp = line; 292 293 if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL) 294 break; 295 #ifdef YP 296 /* 297 * XXX We need to be careful to avoid proceeding 298 * past this point under certain circumstances or 299 * we risk dereferencing null pointers down below. 300 */ 301 if (_gr_group.gr_name[0] == '+') { 302 if (strlen(_gr_group.gr_name) == 1) { 303 switch(search) { 304 case 0: 305 return(1); 306 case 1: 307 return(-1); 308 default: 309 return(0); 310 } 311 } else { 312 cp = &_gr_group.gr_name[1]; 313 if (search && name != NULL) 314 if (strcmp(cp, name)) 315 continue; 316 if (!_getypgroup(&_gr_group, cp, 317 "group.byname")) 318 continue; 319 if (search && name == NULL) 320 if (gid != _gr_group.gr_gid) 321 continue; 322 /* We're going to override -- tell the world. */ 323 _ypfound++; 324 } 325 } 326 #else 327 if (_gr_group.gr_name[0] == '+') 328 continue; 329 #endif /* YP */ 330 if (search && name) { 331 if(strcmp(_gr_group.gr_name, name)) { 332 continue; 333 } 334 } 335 #ifdef YP 336 if ((cp = strsep(&bp, ":\n")) == NULL) { 337 if (_ypfound) 338 return(1); 339 else 340 break; 341 } 342 if (strlen(cp) || !_ypfound) 343 _gr_group.gr_passwd = cp; 344 #else 345 if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL) 346 break; 347 #endif 348 if (!(cp = strsep(&bp, ":\n"))) { 349 #ifdef YP 350 if (_ypfound) 351 return(1); 352 else 353 #endif 354 continue; 355 } 356 #ifdef YP 357 /* 358 * Hurm. Should we be doing this? We allow UIDs to 359 * be overridden -- what about GIDs? 360 */ 361 if (!_ypfound) 362 #endif 363 _gr_group.gr_gid = atoi(cp); 364 if (search && name == NULL && _gr_group.gr_gid != gid) 365 continue; 366 cp = NULL; 367 if (bp == NULL) /* !!! Must check for this! */ 368 break; 369 #ifdef YP 370 if ((cp = strsep(&bp, ":\n")) == NULL) 371 break; 372 373 if (!strlen(cp) && _ypfound) 374 return(1); 375 else 376 members[0] = NULL; 377 bp = cp; 378 cp = NULL; 379 #endif 380 for (m = members; ; bp++) { 381 if (m == (members + maxgrp - 1)) { 382 if ((members = (char **) 383 reallocf(members, 384 sizeof(char **) * 385 (maxgrp + MAXGRP))) == NULL) 386 return(0); 387 m = members + maxgrp - 1; 388 maxgrp += MAXGRP; 389 } 390 if (*bp == ',') { 391 if (cp) { 392 *bp = '\0'; 393 *m++ = cp; 394 cp = NULL; 395 } 396 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 397 if (cp) { 398 *bp = '\0'; 399 *m++ = cp; 400 } 401 break; 402 } else if (cp == NULL) 403 cp = bp; 404 405 } 406 _gr_group.gr_mem = members; 407 *m = NULL; 408 return(1); 409 } 410 /* NOTREACHED */ 411 return (0); 412 } 413 414 #ifdef YP 415 416 static int 417 _gr_breakout_yp(struct group *gr, char *result) 418 { 419 char *s, *cp; 420 char **m; 421 422 /* 423 * XXX If 's' ends up being a NULL pointer, punt on this group. 424 * It means the NIS group entry is badly formatted and should 425 * be skipped. 426 */ 427 if ((s = strsep(&result, ":")) == NULL) return 0; /* name */ 428 gr->gr_name = s; 429 430 if ((s = strsep(&result, ":")) == NULL) return 0; /* password */ 431 gr->gr_passwd = s; 432 433 if ((s = strsep(&result, ":")) == NULL) return 0; /* gid */ 434 gr->gr_gid = atoi(s); 435 436 if ((s = result) == NULL) return 0; 437 cp = 0; 438 439 for (m = members; ; s++) { 440 if (m == members + maxgrp - 1) { 441 if ((members = (char **)reallocf(members, 442 sizeof(char **) * (maxgrp + MAXGRP))) == NULL) 443 return(0); 444 m = members + maxgrp - 1; 445 maxgrp += MAXGRP; 446 } 447 if (*s == ',') { 448 if (cp) { 449 *s = '\0'; 450 *m++ = cp; 451 cp = NULL; 452 } 453 } else if (*s == '\0' || *s == '\n' || *s == ' ') { 454 if (cp) { 455 *s = '\0'; 456 *m++ = cp; 457 } 458 break; 459 } else if (cp == NULL) { 460 cp = s; 461 } 462 } 463 _gr_group.gr_mem = members; 464 *m = NULL; 465 466 return 1; 467 } 468 469 static char *_gr_yp_domain; 470 471 static int 472 _getypgroup(struct group *gr, const char *name, const char *map) 473 { 474 char *result, *s; 475 static char resultbuf[YPMAXRECORD + 2]; 476 int resultlen; 477 478 if(!_gr_yp_domain) { 479 if(yp_get_default_domain(&_gr_yp_domain)) 480 return 0; 481 } 482 483 if(yp_match(_gr_yp_domain, map, name, strlen(name), 484 &result, &resultlen)) 485 return 0; 486 487 s = strchr(result, '\n'); 488 if(s) *s = '\0'; 489 490 if (strlcpy(resultbuf, result, sizeof(resultbuf)) >= sizeof(resultbuf)) 491 return(0); 492 free(result); 493 return(_gr_breakout_yp(gr, resultbuf)); 494 495 } 496 497 498 static int 499 _nextypgroup(struct group *gr) 500 { 501 static char *key; 502 static int keylen; 503 char *lastkey, *result; 504 static char resultbuf[YPMAXRECORD + 2]; 505 size_t resultlen; 506 int rv; 507 508 if(!_gr_yp_domain) { 509 if(yp_get_default_domain(&_gr_yp_domain)) 510 return 0; 511 } 512 513 if(!_gr_stepping_yp) { 514 if(key) free(key); 515 rv = yp_first(_gr_yp_domain, "group.byname", 516 &key, &keylen, &result, &resultlen); 517 if(rv) { 518 return 0; 519 } 520 _gr_stepping_yp = 1; 521 goto unpack; 522 } else { 523 tryagain: 524 lastkey = key; 525 rv = yp_next(_gr_yp_domain, "group.byname", key, keylen, 526 &key, &keylen, &result, &resultlen); 527 free(lastkey); 528 unpack: 529 if(rv) { 530 _gr_stepping_yp = 0; 531 return 0; 532 } 533 534 if(resultlen > sizeof(resultbuf)) { 535 free(result); 536 goto tryagain; 537 } 538 539 strncpy(resultbuf, result, resultlen); 540 resultbuf[resultlen] = '\0'; 541 free(result); 542 if((result = strchr(resultbuf, '\n')) != NULL) 543 *result = '\0'; 544 if (_gr_breakout_yp(gr, resultbuf)) 545 return(1); 546 else 547 goto tryagain; 548 } 549 } 550 551 #endif /* YP */ 552