Imported Upstream version 1.7.6p1
[debian/sudo] / pwutil.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2011
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * Sponsored in part by the Defense Advanced Research Projects
18  * Agency (DARPA) and Air Force Research Laboratory, Air Force
19  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20  */
21
22 #include <config.h>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
27 #include <stdio.h>
28 #ifdef STDC_HEADERS
29 # include <stdlib.h>
30 # include <stddef.h>
31 #else
32 # ifdef HAVE_STDLIB_H
33 #  include <stdlib.h>
34 # endif
35 #endif /* STDC_HEADERS */
36 #ifdef HAVE_STRING_H
37 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
38 #  include <memory.h>
39 # endif
40 # include <string.h>
41 #endif /* HAVE_STRING_H */
42 #ifdef HAVE_STRINGS_H
43 # include <strings.h>
44 #endif /* HAVE_STRINGS_H */
45 #ifdef HAVE_UNISTD_H
46 # include <unistd.h>
47 #endif /* HAVE_UNISTD_H */
48 #ifdef HAVE_SETAUTHDB
49 # include <usersec.h>
50 #endif /* HAVE_SETAUTHDB */
51 #include <pwd.h>
52 #include <grp.h>
53
54 #include "sudo.h"
55 #include "redblack.h"
56
57 /*
58  * The passwd and group caches.
59  */
60 static struct rbtree *pwcache_byuid, *pwcache_byname;
61 static struct rbtree *grcache_bygid, *grcache_byname;
62
63 static int  cmp_pwuid   __P((const void *, const void *));
64 static int  cmp_pwnam   __P((const void *, const void *));
65 static int  cmp_grgid   __P((const void *, const void *));
66
67 #define cmp_grnam       cmp_pwnam
68
69 #define ptr_to_item(p) ((struct cache_item *)((char *)(p) - sizeof(struct cache_item)))
70
71 struct cache_item {
72     unsigned int refcnt;
73     /* key */
74     union {
75         uid_t uid;
76         gid_t gid;
77         char *name;
78     } k;
79     /* datum */
80     union {
81         struct passwd *pw;
82         struct group *gr;
83     } d;
84 };
85
86 /*
87  * Compare by uid.
88  */
89 static int
90 cmp_pwuid(v1, v2)
91     const void *v1;
92     const void *v2;
93 {
94     const struct cache_item *ci1 = (const struct cache_item *) v1;
95     const struct cache_item *ci2 = (const struct cache_item *) v2;
96     return ci1->k.uid - ci2->k.uid;
97 }
98
99 /*
100  * Compare by user name.
101  */
102 static int
103 cmp_pwnam(v1, v2)
104     const void *v1;
105     const void *v2;
106 {
107     const struct cache_item *ci1 = (const struct cache_item *) v1;
108     const struct cache_item *ci2 = (const struct cache_item *) v2;
109     return strcmp(ci1->k.name, ci2->k.name);
110 }
111
112 #define FIELD_SIZE(src, name, size)                     \
113 do {                                                    \
114         if (src->name) {                                \
115                 size = strlen(src->name) + 1;           \
116                 total += size;                          \
117         }                                               \
118 } while (0)
119
120 #define FIELD_COPY(src, dst, name, size)                \
121 do {                                                    \
122         if (src->name) {                                \
123                 memcpy(cp, src->name, size);            \
124                 dst->name = cp;                         \
125                 cp += size;                             \
126         }                                               \
127 } while (0)
128
129 /*
130  * Dynamically allocate space for a struct item plus the key and data
131  * elements.  If name is non-NULL it is used as the key, else the
132  * uid is the key.  Fills in datum from struct password.
133  *
134  * We would like to fill in the encrypted password too but the
135  * call to the shadow function could overwrite the pw buffer (NIS).
136  */
137 static struct cache_item *
138 make_pwitem(pw, name)
139     const struct passwd *pw;
140     const char *name;
141 {
142     char *cp;
143     const char *pw_shell;
144     size_t nsize, psize, csize, gsize, dsize, ssize, total;
145     struct cache_item *item;
146     struct passwd *newpw;
147
148     /* If shell field is empty, expand to _PATH_BSHELL. */
149     pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
150         ? _PATH_BSHELL : pw->pw_shell;
151
152     /* Allocate in one big chunk for easy freeing. */
153     nsize = psize = csize = gsize = dsize = ssize = 0;
154     total = sizeof(struct cache_item) + sizeof(struct passwd);
155     FIELD_SIZE(pw, pw_name, nsize);
156     FIELD_SIZE(pw, pw_passwd, psize);
157 #ifdef HAVE_LOGIN_CAP_H
158     FIELD_SIZE(pw, pw_class, csize);
159 #endif
160     FIELD_SIZE(pw, pw_gecos, gsize);
161     FIELD_SIZE(pw, pw_dir, dsize);
162     /* Treat shell specially since we expand "" -> _PATH_BSHELL */
163     ssize = strlen(pw_shell) + 1;
164     total += ssize;
165     if (name != NULL)
166         total += strlen(name) + 1;
167
168     /* Allocate space for struct item, struct passwd and the strings. */
169     if ((item = malloc(total)) == NULL)
170             return NULL;
171     cp = (char *) item + sizeof(struct cache_item);
172
173     /*
174      * Copy in passwd contents and make strings relative to space
175      * at the end of the buffer.
176      */
177     newpw = (struct passwd *) cp;
178     memcpy(newpw, pw, sizeof(struct passwd));
179     cp += sizeof(struct passwd);
180     FIELD_COPY(pw, newpw, pw_name, nsize);
181     FIELD_COPY(pw, newpw, pw_passwd, psize);
182 #ifdef HAVE_LOGIN_CAP_H
183     FIELD_COPY(pw, newpw, pw_class, csize);
184 #endif
185     FIELD_COPY(pw, newpw, pw_gecos, gsize);
186     FIELD_COPY(pw, newpw, pw_dir, dsize);
187     /* Treat shell specially since we expand "" -> _PATH_BSHELL */
188     memcpy(cp, pw_shell, ssize);
189     newpw->pw_shell = cp;
190     cp += ssize;
191
192     /* Set key and datum. */
193     if (name != NULL) {
194         memcpy(cp, name, strlen(name) + 1);
195         item->k.name = cp;
196     } else {
197         item->k.uid = pw->pw_uid;
198     }
199     item->d.pw = newpw;
200     item->refcnt = 1;
201
202     return item;
203 }
204
205 void
206 pw_addref(pw)
207     struct passwd *pw;
208 {
209     ptr_to_item(pw)->refcnt++;
210 }
211
212 static void
213 pw_delref_item(v)
214     void *v;
215 {
216     struct cache_item *item = v;
217
218     if (--item->refcnt == 0)
219         efree(item);
220 }
221
222 void
223 pw_delref(pw)
224     struct passwd *pw;
225 {
226     pw_delref_item(ptr_to_item(pw));
227 }
228
229 /*
230  * Get a password entry by uid and allocate space for it.
231  * Fills in pw_passwd from shadow file if necessary.
232  */
233 struct passwd *
234 sudo_getpwuid(uid)
235     uid_t uid;
236 {
237     struct cache_item key, *item;
238     struct rbnode *node;
239
240     key.k.uid = uid;
241     if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
242         item = (struct cache_item *) node->data;
243         goto done;
244     }
245     /*
246      * Cache passwd db entry if it exists or a negative response if not.
247      */
248 #ifdef HAVE_SETAUTHDB
249     aix_setauthdb(IDtouser(uid));
250 #endif
251     if ((key.d.pw = getpwuid(uid)) != NULL) {
252         item = make_pwitem(key.d.pw, NULL);
253         if (rbinsert(pwcache_byuid, item) != NULL)
254             errorx(1, "unable to cache uid %u (%s), already exists",
255                 (unsigned int) uid, item->d.pw->pw_name);
256     } else {
257         item = emalloc(sizeof(*item));
258         item->refcnt = 1;
259         item->k.uid = uid;
260         item->d.pw = NULL;
261         if (rbinsert(pwcache_byuid, item) != NULL)
262             errorx(1, "unable to cache uid %u, already exists",
263                 (unsigned int) uid);
264     }
265 #ifdef HAVE_SETAUTHDB
266     aix_restoreauthdb();
267 #endif
268 done:
269     item->refcnt++;
270     return item->d.pw;
271 }
272
273 /*
274  * Get a password entry by name and allocate space for it.
275  * Fills in pw_passwd from shadow file if necessary.
276  */
277 struct passwd *
278 sudo_getpwnam(name)
279     const char *name;
280 {
281     struct cache_item key, *item;
282     struct rbnode *node;
283     size_t len;
284
285     key.k.name = (char *) name;
286     if ((node = rbfind(pwcache_byname, &key)) != NULL) {
287         item = (struct cache_item *) node->data;
288         goto done;
289     }
290     /*
291      * Cache passwd db entry if it exists or a negative response if not.
292      */
293 #ifdef HAVE_SETAUTHDB
294     aix_setauthdb((char *) name);
295 #endif
296     if ((key.d.pw = getpwnam(name)) != NULL) {
297         item = make_pwitem(key.d.pw, name);
298         if (rbinsert(pwcache_byname, item) != NULL)
299             errorx(1, "unable to cache user %s, already exists", name);
300     } else {
301         len = strlen(name) + 1;
302         item = emalloc(sizeof(*item) + len);
303         item->refcnt = 1;
304         item->k.name = (char *) item + sizeof(*item);
305         memcpy(item->k.name, name, len);
306         item->d.pw = NULL;
307         if (rbinsert(pwcache_byname, item) != NULL)
308             errorx(1, "unable to cache user %s, already exists", name);
309     }
310 #ifdef HAVE_SETAUTHDB
311     aix_restoreauthdb();
312 #endif
313 done:
314     item->refcnt++;
315     return item->d.pw;
316 }
317
318 /*
319  * Take a uid in string form "#123" and return a faked up passwd struct.
320  */
321 struct passwd *
322 sudo_fakepwnam(user, gid)
323     const char *user;
324     gid_t gid;
325 {
326     struct cache_item *item;
327     struct passwd *pw;
328     struct rbnode *node;
329     size_t len, namelen;
330     int i;
331
332     namelen = strlen(user);
333     len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ +
334         sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
335         sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);
336
337     for (i = 0; i < 2; i++) {
338         item = emalloc(len);
339         zero_bytes(item, sizeof(*item) + sizeof(*pw));
340         pw = (struct passwd *) ((char *)item + sizeof(*item));
341         pw->pw_uid = (uid_t) atoi(user + 1);
342         pw->pw_gid = gid;
343         pw->pw_name = (char *)pw + sizeof(struct passwd);
344         memcpy(pw->pw_name, user, namelen + 1);
345         pw->pw_passwd = pw->pw_name + namelen + 1;
346         memcpy(pw->pw_passwd, "*", 2);
347         pw->pw_gecos = pw->pw_passwd + 2;
348         pw->pw_gecos[0] = '\0';
349         pw->pw_dir = pw->pw_gecos + 1;
350         memcpy(pw->pw_dir, "/", 2);
351         pw->pw_shell = pw->pw_dir + 2;
352         memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
353
354         item->refcnt = 1;
355         item->d.pw = pw;
356         if (i == 0) {
357             /* Store by uid, overwriting cached version. */
358             item->k.uid = pw->pw_uid;
359             if ((node = rbinsert(pwcache_byuid, item)) != NULL) {
360                 pw_delref_item(node->data);
361                 node->data = item;
362             }
363         } else {
364             /* Store by name, overwriting cached version. */
365             item->k.name = pw->pw_name;
366             if ((node = rbinsert(pwcache_byname, item)) != NULL) {
367                 pw_delref_item(node->data);
368                 node->data = item;
369             }
370         }
371     }
372     item->refcnt++;
373     return pw;
374 }
375
376 void
377 sudo_setpwent()
378 {
379     setpwent();
380     if (pwcache_byuid == NULL)
381         pwcache_byuid = rbcreate(cmp_pwuid);
382     if (pwcache_byname == NULL)
383         pwcache_byname = rbcreate(cmp_pwnam);
384 }
385
386 void
387 sudo_freepwcache()
388 {
389     if (pwcache_byuid != NULL) {
390         rbdestroy(pwcache_byuid, pw_delref_item);
391         pwcache_byuid = NULL;
392     }
393     if (pwcache_byname != NULL) {
394         rbdestroy(pwcache_byname, pw_delref_item);
395         pwcache_byname = NULL;
396     }
397 }
398
399 void
400 sudo_endpwent()
401 {
402     endpwent();
403     sudo_freepwcache();
404 }
405
406 /*
407  * Compare by gid.
408  */
409 static int
410 cmp_grgid(v1, v2)
411     const void *v1;
412     const void *v2;
413 {
414     const struct cache_item *ci1 = (const struct cache_item *) v1;
415     const struct cache_item *ci2 = (const struct cache_item *) v2;
416     return ci1->k.gid - ci2->k.gid;
417 }
418
419 /*
420  * Dynamically allocate space for a struct item plus the key and data
421  * elements.  If name is non-NULL it is used as the key, else the
422  * gid is the key.  Fills in datum from struct group.
423  */
424 static struct cache_item *
425 make_gritem(gr, name)
426     const struct group *gr;
427     const char *name;
428 {
429     char *cp;
430     size_t nsize, psize, nmem, total, len;
431     struct cache_item *item;
432     struct group *newgr;
433
434     /* Allocate in one big chunk for easy freeing. */
435     nsize = psize = nmem = 0;
436     total = sizeof(struct cache_item) + sizeof(struct group);
437     FIELD_SIZE(gr, gr_name, nsize);
438     FIELD_SIZE(gr, gr_passwd, psize);
439     if (gr->gr_mem) {
440         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
441             total += strlen(gr->gr_mem[nmem]) + 1;
442         nmem++;
443         total += sizeof(char *) * nmem;
444     }
445     if (name != NULL)
446         total += strlen(name) + 1;
447
448     if ((item = malloc(total)) == NULL)
449             return NULL;
450     cp = (char *) item + sizeof(struct cache_item);
451
452     /*
453      * Copy in group contents and make strings relative to space
454      * at the end of the buffer.  Note that gr_mem must come
455      * immediately after struct group to guarantee proper alignment.
456      */
457     newgr = (struct group *)cp;
458     memcpy(newgr, gr, sizeof(struct group));
459     cp += sizeof(struct group);
460     if (gr->gr_mem) {
461         newgr->gr_mem = (char **)cp;
462         cp += sizeof(char *) * nmem;
463         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
464             len = strlen(gr->gr_mem[nmem]) + 1;
465             memcpy(cp, gr->gr_mem[nmem], len);
466             newgr->gr_mem[nmem] = cp;
467             cp += len;
468         }
469         newgr->gr_mem[nmem] = NULL;
470     }
471     FIELD_COPY(gr, newgr, gr_passwd, psize);
472     FIELD_COPY(gr, newgr, gr_name, nsize);
473
474     /* Set key and datum. */
475     if (name != NULL) {
476         memcpy(cp, name, strlen(name) + 1);
477         item->k.name = cp;
478     } else {
479         item->k.gid = gr->gr_gid;
480     }
481     item->d.gr = newgr;
482     item->refcnt = 1;
483
484     return item;
485 }
486
487 void
488 gr_addref(gr)
489     struct group *gr;
490 {
491     ptr_to_item(gr)->refcnt++;
492 }
493
494 static void
495 gr_delref_item(v)
496     void *v;
497 {
498     struct cache_item *item = v;
499
500     if (--item->refcnt == 0)
501         efree(item);
502 }
503
504 void
505 gr_delref(gr)
506     struct group *gr;
507 {
508     gr_delref_item(ptr_to_item(gr));
509 }
510
511 /*
512  * Get a group entry by gid and allocate space for it.
513  */
514 struct group *
515 sudo_getgrgid(gid)
516     gid_t gid;
517 {
518     struct cache_item key, *item;
519     struct rbnode *node;
520
521     key.k.gid = gid;
522     if ((node = rbfind(grcache_bygid, &key)) != NULL) {
523         item = (struct cache_item *) node->data;
524         goto done;
525     }
526     /*
527      * Cache group db entry if it exists or a negative response if not.
528      */
529     if ((key.d.gr = getgrgid(gid)) != NULL) {
530         item = make_gritem(key.d.gr, NULL);
531         if (rbinsert(grcache_bygid, item) != NULL)
532             errorx(1, "unable to cache gid %u (%s), already exists",
533                 (unsigned int) gid, key.d.gr->gr_name);
534     } else {
535         item = emalloc(sizeof(*item));
536         item->refcnt = 1;
537         item->k.gid = gid;
538         item->d.gr = NULL;
539         if (rbinsert(grcache_bygid, item) != NULL)
540             errorx(1, "unable to cache gid %u, already exists",
541                 (unsigned int) gid);
542     }
543 done:
544     item->refcnt++;
545     return item->d.gr;
546 }
547
548 /*
549  * Get a group entry by name and allocate space for it.
550  */
551 struct group *
552 sudo_getgrnam(name)
553     const char *name;
554 {
555     struct cache_item key, *item;
556     struct rbnode *node;
557     size_t len;
558
559     key.k.name = (char *) name;
560     if ((node = rbfind(grcache_byname, &key)) != NULL) {
561         item = (struct cache_item *) node->data;
562         goto done;
563     }
564     /*
565      * Cache group db entry if it exists or a negative response if not.
566      */
567     if ((key.d.gr = getgrnam(name)) != NULL) {
568         item = make_gritem(key.d.gr, name);
569         if (rbinsert(grcache_byname, item) != NULL)
570             errorx(1, "unable to cache group %s, already exists", name);
571     } else {
572         len = strlen(name) + 1;
573         item = emalloc(sizeof(*item) + len);
574         item->refcnt = 1;
575         item->k.name = (char *) item + sizeof(*item);
576         memcpy(item->k.name, name, len);
577         item->d.gr = NULL;
578         if (rbinsert(grcache_byname, item) != NULL)
579             errorx(1, "unable to cache group %s, already exists", name);
580     }
581 done:
582     item->refcnt++;
583     return item->d.gr;
584 }
585
586 /*
587  * Take a gid in string form "#123" and return a faked up group struct.
588  */
589 struct group *
590 sudo_fakegrnam(group)
591     const char *group;
592 {
593     struct cache_item *item;
594     struct group *gr;
595     struct rbnode *node;
596     size_t len, namelen;
597     int i;
598
599     namelen = strlen(group);
600     len = sizeof(*item) + sizeof(*gr) + namelen + 1;
601
602     for (i = 0; i < 2; i++) {
603         item = emalloc(len);
604         zero_bytes(item, sizeof(*item) + sizeof(*gr));
605         gr = (struct group *) ((char *)item + sizeof(*item));
606         gr->gr_gid = (gid_t) atoi(group + 1);
607         gr->gr_name = (char *)gr + sizeof(struct group);
608         memcpy(gr->gr_name, group, namelen + 1);
609
610         item->refcnt = 1;
611         item->d.gr = gr;
612         if (i == 0) {
613             /* Store by gid, overwriting cached version. */
614             item->k.gid = gr->gr_gid;
615             if ((node = rbinsert(grcache_bygid, item)) != NULL) {
616                 gr_delref_item(node->data);
617                 node->data = item;
618             }
619         } else {
620             /* Store by name, overwriting cached version. */
621             item->k.name = gr->gr_name;
622             if ((node = rbinsert(grcache_byname, item)) != NULL) {
623                 gr_delref_item(node->data);
624                 node->data = item;
625             }
626         }
627     }
628     item->refcnt++;
629     return gr;
630 }
631
632 void
633 sudo_setgrent()
634 {
635     setgrent();
636     if (grcache_bygid == NULL)
637         grcache_bygid = rbcreate(cmp_grgid);
638     if (grcache_byname == NULL)
639         grcache_byname = rbcreate(cmp_grnam);
640 }
641
642 void
643 sudo_freegrcache()
644 {
645     if (grcache_bygid != NULL) {
646         rbdestroy(grcache_bygid, gr_delref_item);
647         grcache_bygid = NULL;
648     }
649     if (grcache_byname != NULL) {
650         rbdestroy(grcache_byname, gr_delref_item);
651         grcache_byname = NULL;
652     }
653 }
654
655 void
656 sudo_endgrent()
657 {
658     endgrent();
659     sudo_freegrcache();
660 }
661
662 int
663 user_in_group(pw, group)
664     struct passwd *pw;
665     const char *group;
666 {
667 #ifdef HAVE_MBR_CHECK_MEMBERSHIP
668     uuid_t gu, uu;
669     int ismember;
670 #else
671     char **gr_mem;
672     int i;
673 #endif
674     struct group *grp;
675     int retval = FALSE;
676
677 #ifdef HAVE_SETAUTHDB
678     aix_setauthdb(pw->pw_name);
679 #endif
680     /* A group name that begins with a '#' may be a gid. */
681     if ((grp = sudo_getgrnam(group)) == NULL && *group == '#')
682         grp = sudo_getgrgid(atoi(group + 1));
683 #ifdef HAVE_SETAUTHDB
684     aix_restoreauthdb();
685 #endif
686     if (grp == NULL)
687         goto done;
688
689     /* check against user's primary (passwd file) gid */
690     if (grp->gr_gid == pw->pw_gid) {
691         retval = TRUE;
692         goto done;
693     }
694
695 #ifdef HAVE_MBR_CHECK_MEMBERSHIP
696     /* If we are matching the invoking user use the stashed uuid. */
697     if (strcmp(pw->pw_name, user_name) == 0) {
698         if (mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
699             mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember) {
700             retval = TRUE;
701             goto done;
702         }
703     } else {
704         if (mbr_uid_to_uuid(pw->pw_uid, uu) == 0 &&
705             mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
706             mbr_check_membership(uu, gu, &ismember) == 0 && ismember) {
707             retval = TRUE;
708             goto done;
709         }
710     }
711 #else /* HAVE_MBR_CHECK_MEMBERSHIP */
712 # ifdef HAVE_GETGROUPS
713     /*
714      * If we are matching the invoking or list user and that user has a
715      * supplementary group vector, check it.
716      */
717     if (user_ngroups > 0 &&
718         strcmp(pw->pw_name, list_pw ? list_pw->pw_name : user_name) == 0) {
719         for (i = 0; i < user_ngroups; i++) {
720             if (grp->gr_gid == user_groups[i]) {
721                 retval = TRUE;
722                 goto done;
723             }
724         }
725     } else
726 # endif /* HAVE_GETGROUPS */
727     {
728         if (grp != NULL && grp->gr_mem != NULL) {
729             for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) {
730                 if (strcmp(*gr_mem, pw->pw_name) == 0) {
731                     retval = TRUE;
732                     goto done;
733                 }
734             }
735         }
736     }
737 #endif /* HAVE_MBR_CHECK_MEMBERSHIP */
738
739 done:
740     if (grp != NULL)
741         gr_delref(grp);
742     return retval;
743 }