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