1 /* $NetBSD: getgrent.c,v 1.48 2003/10/13 15:36:33 agc Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 45 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 46 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 47 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, 48 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 49 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 50 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 51 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54 * SUCH DAMAGE. 55 */ 56 57 #include <sys/cdefs.h> 58 #if defined(LIBC_SCCS) && !defined(lint) 59 #if 0 60 static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; 61 #else 62 __RCSID("$NetBSD: getgrent.c,v 1.48 2003/10/13 15:36:33 agc Exp $"); 63 #endif 64 #endif /* LIBC_SCCS and not lint */ 65 66 #include "namespace.h" 67 68 #include <sys/types.h> 69 70 #include <assert.h> 71 #include <errno.h> 72 #include <grp.h> 73 #include <limits.h> 74 #include <nsswitch.h> 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <string.h> 78 #include <syslog.h> 79 80 #include <stdarg.h> 81 82 #ifdef HESIOD 83 #include <hesiod.h> 84 #endif 85 #ifdef YP 86 #include <rpc/rpc.h> 87 #include <rpcsvc/yp_prot.h> 88 #include <rpcsvc/ypclnt.h> 89 #endif 90 91 #if defined(YP) || defined(HESIOD) 92 #define _GROUP_COMPAT 93 #endif 94 95 struct group *_getgrent_user(const char *); 96 97 #ifdef __weak_alias 98 __weak_alias(endgrent,_endgrent) 99 __weak_alias(getgrent,_getgrent) 100 __weak_alias(getgrgid,_getgrgid) 101 __weak_alias(getgrnam,_getgrnam) 102 __weak_alias(setgrent,_setgrent) 103 __weak_alias(setgroupent,_setgroupent) 104 #endif 105 106 static FILE *_gr_fp; 107 static struct group _gr_group; 108 static int _gr_stayopen; 109 static int _gr_filesdone; 110 111 static void grcleanup(void); 112 static int grscan(int, gid_t, const char *, const char *); 113 static int grstart(void); 114 static int grmatchline(int, gid_t, const char *, const char *); 115 116 #define MAXGRP 200 117 #define MAXLINELENGTH 1024 118 119 static __aconst char *members[MAXGRP]; 120 static char line[MAXLINELENGTH]; 121 122 #ifdef YP 123 static char *__ypcurrent, *__ypdomain; 124 static int __ypcurrentlen; 125 static int _gr_ypdone; 126 #endif 127 128 #ifdef HESIOD 129 static int _gr_hesnum; 130 static struct group *_gr_hesgrplist = NULL; 131 static int _gr_hesgrplistnum; 132 static int _gr_hesgrplistmax; 133 #endif 134 135 #ifdef _GROUP_COMPAT 136 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME }; 137 static enum _grmode __grmode; 138 #endif 139 140 struct group * 141 getgrent(void) 142 { 143 144 if ((!_gr_fp && !grstart()) || !grscan(0, 0, NULL, NULL)) 145 return (NULL); 146 return &_gr_group; 147 } 148 149 /* 150 * _getgrent_user() is designed only to be called by getgrouplist(3) and 151 * hence makes no guarantees about filling the entire structure that it 152 * returns. It may only fill in the group name and gid fields. 153 */ 154 155 struct group * 156 _getgrent_user(const char *user) 157 { 158 159 if ((!_gr_fp && !grstart()) || !grscan(0, 0, NULL, user)) 160 return (NULL); 161 return &_gr_group; 162 } 163 164 struct group * 165 getgrnam(const char *name) 166 { 167 int rval; 168 169 _DIAGASSERT(name != NULL); 170 171 if (!grstart()) 172 return NULL; 173 rval = grscan(1, 0, name, NULL); 174 if (!_gr_stayopen) 175 endgrent(); 176 return (rval) ? &_gr_group : NULL; 177 } 178 179 struct group * 180 getgrgid(gid_t gid) 181 { 182 int rval; 183 184 if (!grstart()) 185 return NULL; 186 rval = grscan(1, gid, NULL, NULL); 187 if (!_gr_stayopen) 188 endgrent(); 189 return (rval) ? &_gr_group : NULL; 190 } 191 192 void 193 grcleanup(void) 194 { 195 196 _gr_filesdone = 0; 197 #ifdef YP 198 if (__ypcurrent) 199 free(__ypcurrent); 200 __ypcurrent = NULL; 201 _gr_ypdone = 0; 202 #endif 203 #ifdef HESIOD 204 _gr_hesnum = 0; 205 if (!_gr_hesgrplist) 206 free(_gr_hesgrplist); 207 _gr_hesgrplist = NULL; 208 _gr_hesgrplistnum = -1; 209 _gr_hesgrplistmax = 0; 210 #endif 211 #ifdef _GROUP_COMPAT 212 __grmode = GRMODE_NONE; 213 #endif 214 } 215 216 static int 217 grstart(void) 218 { 219 220 grcleanup(); 221 if (_gr_fp) { 222 rewind(_gr_fp); 223 return 1; 224 } 225 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0; 226 } 227 228 void 229 setgrent(void) 230 { 231 232 (void) setgroupent(0); 233 } 234 235 int 236 setgroupent(int stayopen) 237 { 238 239 if (!grstart()) 240 return 0; 241 _gr_stayopen = stayopen; 242 return 1; 243 } 244 245 void 246 endgrent(void) 247 { 248 249 grcleanup(); 250 if (_gr_fp) { 251 (void)fclose(_gr_fp); 252 _gr_fp = NULL; 253 } 254 } 255 256 257 static int _local_grscan(void *, void *, va_list); 258 259 /*ARGSUSED*/ 260 static int 261 _local_grscan(void *rv, void *cb_data, va_list ap) 262 { 263 int search = va_arg(ap, int); 264 gid_t gid = va_arg(ap, gid_t); 265 const char *name = va_arg(ap, const char *); 266 const char *user = va_arg(ap, const char *); 267 268 if (_gr_filesdone) 269 return NS_NOTFOUND; 270 for (;;) { 271 if (!fgets(line, sizeof(line), _gr_fp)) { 272 if (!search) 273 _gr_filesdone = 1; 274 return NS_NOTFOUND; 275 } 276 /* skip lines that are too big */ 277 if (!strchr(line, '\n')) { 278 int ch; 279 280 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 281 ; 282 continue; 283 } 284 if (grmatchline(search, gid, name, user)) 285 return NS_SUCCESS; 286 } 287 /* NOTREACHED */ 288 } 289 290 #ifdef HESIOD 291 static int _dns_grscan(void *, void *, va_list); 292 static int _dns_grplist(const char *); 293 294 /*ARGSUSED*/ 295 static int 296 _dns_grscan(void *rv, void *cb_data, va_list ap) 297 { 298 int search = va_arg(ap, int); 299 gid_t gid = va_arg(ap, gid_t); 300 const char *name = va_arg(ap, const char *); 301 const char *user = va_arg(ap, const char *); 302 303 char **hp; 304 void *context; 305 int r; 306 307 r = NS_UNAVAIL; 308 if (!search && user && _gr_hesgrplistmax != -1) { 309 r = _dns_grplist(user); 310 /* if we did not find user.grplist, just iterate */ 311 if (!_gr_hesgrplist) { 312 _gr_hesgrplistmax = -1; 313 if (r != NS_NOTFOUND) 314 return r; 315 } else 316 return r; 317 } 318 if (!search && _gr_hesnum == -1) 319 return NS_NOTFOUND; 320 if (hesiod_init(&context) == -1) 321 return (r); 322 323 for (;;) { 324 if (search) { 325 if (name) 326 strlcpy(line, name, sizeof(line)); 327 else 328 snprintf(line, sizeof(line), "%u", 329 (unsigned int)gid); 330 } else { 331 snprintf(line, sizeof(line), "group-%u", _gr_hesnum); 332 _gr_hesnum++; 333 } 334 335 hp = NULL; 336 if (search && !name) { 337 hp = hesiod_resolve(context, line, "gid"); 338 if (hp == NULL && errno != ENOENT) 339 break; 340 } 341 if (hp == NULL) 342 hp = hesiod_resolve(context, line, "group"); 343 if (hp == NULL) { 344 if (errno == ENOENT) { 345 if (!search) 346 _gr_hesnum = -1; 347 r = NS_NOTFOUND; 348 } 349 break; 350 } 351 352 /* only check first elem */ 353 strlcpy(line, hp[0], sizeof(line)); 354 hesiod_free_list(context, hp); 355 if (grmatchline(search, gid, name, user)) { 356 r = NS_SUCCESS; 357 break; 358 } else if (search) { 359 r = NS_NOTFOUND; 360 break; 361 } 362 } 363 hesiod_end(context); 364 return (r); 365 } 366 367 static int 368 _dns_grplist(const char *user) 369 { 370 void *context; 371 int r; 372 char **hp; 373 char *cp; 374 375 r = NS_UNAVAIL; 376 if (!_gr_hesgrplist) { 377 if (hesiod_init(&context) == -1) 378 return r; 379 380 _gr_hesgrplistnum = -1; 381 hp = hesiod_resolve(context, user, "grplist"); 382 if (!hp) { 383 if (errno == ENOENT) 384 r = NS_NOTFOUND; 385 hesiod_end(context); 386 return r; 387 } 388 389 strlcpy(line, hp[0], sizeof(line)); 390 hesiod_free_list(context, hp); 391 392 _gr_hesgrplistmax = 0; 393 for (cp=line; *cp; cp++) 394 if (*cp == ':') 395 _gr_hesgrplistmax++; 396 _gr_hesgrplistmax /= 2; 397 _gr_hesgrplistmax++; 398 399 _gr_hesgrplist = malloc(_gr_hesgrplistmax * 400 sizeof(*_gr_hesgrplist)); 401 if (!_gr_hesgrplist) { 402 hesiod_end(context); 403 return NS_UNAVAIL; 404 } 405 406 cp = line; 407 _gr_hesgrplistmax = 0; 408 for (;;) { 409 char *name; 410 char *num; 411 gid_t gid; 412 char *ep; 413 414 /* XXXrcd: error handling */ 415 if (!(name = strsep(&cp, ":"))) 416 break; 417 if (!(num = strsep(&cp, ":"))) 418 break; 419 gid = (gid_t) strtoul(num, &ep, 10); 420 if (gid > GID_MAX || *ep != '\0') 421 break; 422 423 _gr_hesgrplist[_gr_hesgrplistmax].gr_name = name; 424 _gr_hesgrplist[_gr_hesgrplistmax].gr_gid = gid; 425 _gr_hesgrplistmax++; 426 } 427 428 hesiod_end(context); 429 } 430 431 /* we assume that _gr_hesgrplist is now defined */ 432 if (++_gr_hesgrplistnum >= _gr_hesgrplistmax) 433 return NS_NOTFOUND; 434 435 /* 436 * Now we copy the relevant information into _gr_group, so that 437 * it can be returned. Note that we only fill in the bare necessities 438 * as this will be used exclusively by getgrouplist(3) and we do 439 * not want to have to look up all of the information. 440 */ 441 _gr_group.gr_name = _gr_hesgrplist[_gr_hesgrplistnum].gr_name; 442 _gr_group.gr_passwd = NULL; 443 _gr_group.gr_gid = _gr_hesgrplist[_gr_hesgrplistnum].gr_gid; 444 _gr_group.gr_mem = NULL; 445 446 return NS_SUCCESS; 447 } 448 #endif /* HESIOD */ 449 450 #ifdef YP 451 static int _nis_grscan(void *, void *, va_list); 452 453 /*ARGSUSED*/ 454 static int 455 _nis_grscan(void *rv, void *cb_data, va_list ap) 456 { 457 int search = va_arg(ap, int); 458 gid_t gid = va_arg(ap, gid_t); 459 const char *name = va_arg(ap, const char *); 460 const char *user = va_arg(ap, const char *); 461 462 char *key, *data; 463 int keylen, datalen; 464 int r; 465 466 if(__ypdomain == NULL) { 467 switch (yp_get_default_domain(&__ypdomain)) { 468 case 0: 469 break; 470 case YPERR_RESRC: 471 return NS_TRYAGAIN; 472 default: 473 return NS_UNAVAIL; 474 } 475 } 476 477 if (search) { /* specific group or gid */ 478 if (name) 479 strlcpy(line, name, sizeof(line)); 480 else 481 snprintf(line, sizeof(line), "%u", (unsigned int)gid); 482 data = NULL; 483 r = yp_match(__ypdomain, 484 (name) ? "group.byname" : "group.bygid", 485 line, (int)strlen(line), &data, &datalen); 486 switch (r) { 487 case 0: 488 break; 489 case YPERR_KEY: 490 if (data) 491 free(data); 492 return NS_NOTFOUND; 493 default: 494 if (data) 495 free(data); 496 return NS_UNAVAIL; 497 } 498 data[datalen] = '\0'; /* clear trailing \n */ 499 strlcpy(line, data, sizeof(line)); 500 free(data); 501 if (grmatchline(search, gid, name, user)) 502 return NS_SUCCESS; 503 else 504 return NS_NOTFOUND; 505 } 506 507 /* ! search */ 508 if (_gr_ypdone) 509 return NS_NOTFOUND; 510 for (;;) { 511 data = NULL; 512 if(__ypcurrent) { 513 key = NULL; 514 r = yp_next(__ypdomain, "group.byname", 515 __ypcurrent, __ypcurrentlen, 516 &key, &keylen, &data, &datalen); 517 free(__ypcurrent); 518 switch (r) { 519 case 0: 520 break; 521 case YPERR_NOMORE: 522 __ypcurrent = NULL; 523 if (key) 524 free(key); 525 if (data) 526 free(data); 527 _gr_ypdone = 1; 528 return NS_NOTFOUND; 529 default: 530 if (key) 531 free(key); 532 if (data) 533 free(data); 534 return NS_UNAVAIL; 535 } 536 __ypcurrent = key; 537 __ypcurrentlen = keylen; 538 } else { 539 if (yp_first(__ypdomain, "group.byname", 540 &__ypcurrent, &__ypcurrentlen, 541 &data, &datalen)) { 542 if (data) 543 free(data); 544 return NS_UNAVAIL; 545 } 546 } 547 data[datalen] = '\0'; /* clear trailing \n */ 548 strlcpy(line, data, sizeof(line)); 549 free(data); 550 if (grmatchline(search, gid, name, user)) 551 return NS_SUCCESS; 552 } 553 /* NOTREACHED */ 554 } 555 #endif /* YP */ 556 557 #ifdef _GROUP_COMPAT 558 /* 559 * log an error if "files" or "compat" is specified in group_compat database 560 */ 561 static int _bad_grscan(void *, void *, va_list); 562 563 /*ARGSUSED*/ 564 static int 565 _bad_grscan(void *rv, void *cb_data, va_list ap) 566 { 567 static int warned; 568 569 _DIAGASSERT(cb_data != NULL); 570 571 if (!warned) { 572 syslog(LOG_ERR, 573 "nsswitch.conf group_compat database can't use '%s'", 574 (char *)cb_data); 575 } 576 warned = 1; 577 return NS_UNAVAIL; 578 } 579 580 /* 581 * when a name lookup in compat mode is required, look it up in group_compat 582 * nsswitch database. only Hesiod and NIS is supported - it doesn't make 583 * sense to lookup compat names from 'files' or 'compat' 584 */ 585 586 static int __grscancompat(int, gid_t, const char *, const char *); 587 588 static int 589 __grscancompat(int search, gid_t gid, const char *name, const char *user) 590 { 591 static const ns_dtab dtab[] = { 592 NS_FILES_CB(_bad_grscan, "files") 593 NS_DNS_CB(_dns_grscan, NULL) 594 NS_NIS_CB(_nis_grscan, NULL) 595 NS_COMPAT_CB(_bad_grscan, "compat") 596 { 0 } 597 }; 598 static const ns_src defaultnis[] = { 599 { NSSRC_NIS, NS_SUCCESS }, 600 { 0 } 601 }; 602 603 _DIAGASSERT(name != NULL); 604 605 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", 606 defaultnis, search, gid, name, user)); 607 } 608 #endif /* GROUP_COMPAT */ 609 610 611 static int _compat_grscan(void *, void *, va_list); 612 613 /*ARGSUSED*/ 614 static int 615 _compat_grscan(void *rv, void *cb_data, va_list ap) 616 { 617 int search = va_arg(ap, int); 618 gid_t gid = va_arg(ap, gid_t); 619 const char *name = va_arg(ap, const char *); 620 const char *user = va_arg(ap, const char *); 621 622 #ifdef _GROUP_COMPAT 623 static char *grname = NULL; 624 #endif 625 626 for (;;) { 627 #ifdef _GROUP_COMPAT 628 if(__grmode != GRMODE_NONE) { 629 int r; 630 631 switch(__grmode) { 632 case GRMODE_FULL: 633 r = __grscancompat(search, gid, name, user); 634 if (r == NS_SUCCESS) 635 return r; 636 __grmode = GRMODE_NONE; 637 break; 638 case GRMODE_NAME: 639 if(grname == (char *)NULL) { 640 __grmode = GRMODE_NONE; 641 break; 642 } 643 r = __grscancompat(1, 0, grname, user); 644 free(grname); 645 grname = (char *)NULL; 646 if (r != NS_SUCCESS) 647 break; 648 if (!search) 649 return NS_SUCCESS; 650 if (name) { 651 if (! strcmp(_gr_group.gr_name, name)) 652 return NS_SUCCESS; 653 } else { 654 if (_gr_group.gr_gid == gid) 655 return NS_SUCCESS; 656 } 657 break; 658 case GRMODE_NONE: 659 abort(); 660 } 661 continue; 662 } 663 #endif /* _GROUP_COMPAT */ 664 665 if (!fgets(line, sizeof(line), _gr_fp)) 666 return NS_NOTFOUND; 667 /* skip lines that are too big */ 668 if (!strchr(line, '\n')) { 669 int ch; 670 671 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) 672 ; 673 continue; 674 } 675 676 #ifdef _GROUP_COMPAT 677 if (line[0] == '+') { 678 char *tptr, *bp; 679 680 switch(line[1]) { 681 case ':': 682 case '\0': 683 case '\n': 684 __grmode = GRMODE_FULL; 685 break; 686 default: 687 __grmode = GRMODE_NAME; 688 bp = line; 689 tptr = strsep(&bp, ":\n"); 690 grname = strdup(tptr + 1); 691 break; 692 } 693 continue; 694 } 695 #endif /* _GROUP_COMPAT */ 696 if (grmatchline(search, gid, name, user)) 697 return NS_SUCCESS; 698 } 699 /* NOTREACHED */ 700 } 701 702 static int 703 grscan(int search, gid_t gid, const char *name, const char *user) 704 { 705 int r; 706 static const ns_dtab dtab[] = { 707 NS_FILES_CB(_local_grscan, NULL) 708 NS_DNS_CB(_dns_grscan, NULL) 709 NS_NIS_CB(_nis_grscan, NULL) 710 NS_COMPAT_CB(_compat_grscan, NULL) 711 { 0 } 712 }; 713 static const ns_src compatsrc[] = { 714 { NSSRC_COMPAT, NS_SUCCESS }, 715 { 0 } 716 }; 717 718 /* name may be NULL if search is nonzero */ 719 720 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, 721 search, gid, name, user); 722 return (r == NS_SUCCESS) ? 1 : 0; 723 } 724 725 static int 726 grmatchline(int search, gid_t gid, const char *name, const char *user) 727 { 728 unsigned long id; 729 __aconst char **m; 730 char *cp, *bp, *ep; 731 732 /* name may be NULL if search is nonzero */ 733 734 if (line[0] == '+') 735 return 0; /* sanity check to prevent recursion */ 736 bp = line; 737 _gr_group.gr_name = strsep(&bp, ":\n"); 738 if (search && name && strcmp(_gr_group.gr_name, name)) 739 return 0; 740 _gr_group.gr_passwd = strsep(&bp, ":\n"); 741 if (!(cp = strsep(&bp, ":\n"))) 742 return 0; 743 id = strtoul(cp, &ep, 10); 744 if (id > GID_MAX || *ep != '\0') 745 return 0; 746 _gr_group.gr_gid = (gid_t)id; 747 if (search && name == NULL && _gr_group.gr_gid != gid) 748 return 0; 749 cp = NULL; 750 if (bp == NULL) 751 return 0; 752 for (_gr_group.gr_mem = m = members;; bp++) { 753 if (m == &members[MAXGRP - 1]) 754 break; 755 if (*bp == ',') { 756 if (cp) { 757 *bp = '\0'; 758 *m++ = cp; 759 cp = NULL; 760 } 761 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 762 if (cp) { 763 *bp = '\0'; 764 *m++ = cp; 765 } 766 break; 767 } else if (cp == NULL) 768 cp = bp; 769 } 770 *m = NULL; 771 if (user) { 772 for (m = members; *m; m++) 773 if (!strcmp(user, *m)) 774 return 1; 775 return 0; 776 } 777 return 1; 778 } 779