1 /*        $NetBSD: getgroupmembership.c,v 1.4 2008/04/28 20:22:59 martin Exp $  */
2 
3 /*-
4  * Copyright (c) 2004-2005 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Luke Mewburn.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: getgroupmembership.c,v 1.4 2008/04/28 20:22:59 martin Exp $");
35 #endif /* LIBC_SCCS and not lint */
36 
37 /*
38  * calculate group access list
39  */
40 
41 #include "namespace.h"
42 #include "reentrant.h"
43 
44 #include <sys/param.h>
45 
46 #include <assert.h>
47 #include <errno.h>
48 #include <grp.h>
49 #include <limits.h>
50 #include <nsswitch.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 #ifdef HESIOD
58 #include <hesiod.h>
59 #endif
60 
61 #include "gr_private.h"
62 
63 #ifdef __weak_alias
__weak_alias(getgroupmembership,_getgroupmembership)64 __weak_alias(getgroupmembership,_getgroupmembership)
65 #endif
66 
67 /*
68  * __gr_addgid
69  *        Add gid to the groups array (of maxgrp size) at the position
70  *        indicated by *groupc, unless it already exists or *groupc is
71  *        past &groups[maxgrp].
72  *        Returns 1 upon success (including duplicate suppression), 0 otherwise.
73  */
74 static int
75 __gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *groupc)
76 {
77           int       ret, dupc;
78 
79           _DIAGASSERT(groupc != NULL);
80           _DIAGASSERT(groups != NULL);
81 
82                                                             /* skip duplicates */
83           for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
84                     if (groups[dupc] == gid)
85                               return 1;
86           }
87 
88           ret = 1;
89           if (*groupc < maxgrp)                             /* add this gid */
90                     groups[*groupc] = gid;
91           else
92                     ret = 0;
93           (*groupc)++;
94           return ret;
95 }
96 
97 
98 /*ARGSUSED*/
99 static int
_files_getgroupmembership(void * retval,void * cb_data,va_list ap)100 _files_getgroupmembership(void *retval, void *cb_data, va_list ap)
101 {
102           int                 *result   = va_arg(ap, int *);
103           const char          *uname    = va_arg(ap, const char *);
104           gid_t                agroup   = va_arg(ap, gid_t);
105           gid_t               *groups   = va_arg(ap, gid_t *);
106           int                  maxgrp   = va_arg(ap, int);
107           int                 *groupc   = va_arg(ap, int *);
108 
109           struct __grstate_files        state;
110           struct group                  grp;
111           char                          grpbuf[_GETGR_R_SIZE_MAX];
112           int                           rv, i;
113 
114           _DIAGASSERT(result != NULL);
115           _DIAGASSERT(uname != NULL);
116           /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
117           _DIAGASSERT(groupc != NULL);
118 
119                                                             /* install primary group */
120           (void) __gr_addgid(agroup, groups, maxgrp, groupc);
121 
122           memset(&state, 0, sizeof(state));
123           while (__grscan_files(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
124                                         0, NULL, 0) == NS_SUCCESS) {
125                                                             /* scan members */
126                     for (i = 0; grp.gr_mem[i]; i++) {
127                               if (strcmp(grp.gr_mem[i], uname) != 0)
128                                         continue;
129                               if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
130                                         *result = -1;
131                               break;
132                     }
133           }
134           __grend_files(&state);
135           return NS_NOTFOUND;
136 }
137 
138 
139 #ifdef HESIOD
140 
141 /*ARGSUSED*/
142 static int
_dns_getgroupmembership(void * retval,void * cb_data,va_list ap)143 _dns_getgroupmembership(void *retval, void *cb_data, va_list ap)
144 {
145           int                 *result   = va_arg(ap, int *);
146           const char          *uname    = va_arg(ap, const char *);
147           gid_t                agroup   = va_arg(ap, gid_t);
148           gid_t               *groups   = va_arg(ap, gid_t *);
149           int                  maxgrp   = va_arg(ap, int);
150           int                 *groupc   = va_arg(ap, int *);
151 
152           struct __grstate_dns          state;
153           struct group                  grp;
154           char                          grpbuf[_GETGR_R_SIZE_MAX];
155           unsigned long                 id;
156           void                          *context;
157           char                          **hp, *cp, *ep;
158           int                           rv, i;
159 
160           _DIAGASSERT(result != NULL);
161           _DIAGASSERT(uname != NULL);
162           /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
163           _DIAGASSERT(groupc != NULL);
164 
165                                                             /* install primary group */
166           (void) __gr_addgid(agroup, groups, maxgrp, groupc);
167 
168           hp = NULL;
169           rv = NS_NOTFOUND;
170 
171           if (hesiod_init(&context) == -1)                  /* setup hesiod */
172                     return NS_UNAVAIL;
173 
174           hp = hesiod_resolve(context, uname, "grplist");   /* find grplist */
175           if (hp == NULL) {
176                     if (errno != ENOENT) {                            /* wasn't "not found"*/
177                               rv = NS_UNAVAIL;
178                               goto dnsgroupmembers_out;
179                     }
180                               /* grplist not found, fallback to _dns_grscan */
181                     memset(&state, 0, sizeof(state));
182                     while (__grscan_dns(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
183                                                   0, NULL, 0) == NS_SUCCESS) {
184                                                                       /* scan members */
185                               for (i = 0; grp.gr_mem[i]; i++) {
186                                         if (strcmp(grp.gr_mem[i], uname) != 0)
187                                                   continue;
188                                         if (! __gr_addgid(grp.gr_gid, groups, maxgrp,
189                                             groupc))
190                                                   *result = -1;
191                                         break;
192                               }
193                     }
194                     __grend_dns(&state);
195                     rv = NS_NOTFOUND;
196                     goto dnsgroupmembers_out;
197           }
198 
199           if ((ep = strchr(hp[0], '\n')) != NULL)
200                     *ep = '\0';                                       /* clear trailing \n */
201 
202           for (cp = hp[0]; *cp != '\0'; ) {                 /* parse grplist */
203                     if ((cp = strchr(cp, ':')) == NULL)     /* skip grpname */
204                               break;
205                     cp++;
206                     id = strtoul(cp, &ep, 10);              /* parse gid */
207                     if (id > GID_MAX || (*ep != ':' && *ep != '\0')) {
208                               rv = NS_UNAVAIL;
209                               goto dnsgroupmembers_out;
210                     }
211                     cp = ep;
212                     if (*cp == ':')
213                               cp++;
214 
215                                                                       /* add gid */
216                     if (! __gr_addgid((gid_t)id, groups, maxgrp, groupc))
217                               *result = -1;
218           }
219 
220           rv = NS_NOTFOUND;
221 
222  dnsgroupmembers_out:
223           if (hp)
224                     hesiod_free_list(context, hp);
225           hesiod_end(context);
226           return rv;
227 }
228 
229 #endif /* HESIOD */
230 
231 
232 #ifdef YP
233 
234 /*ARGSUSED*/
235 static int
_nis_getgroupmembership(void * retval,void * cb_data,va_list ap)236 _nis_getgroupmembership(void *retval, void *cb_data, va_list ap)
237 {
238           int                 *result   = va_arg(ap, int *);
239           const char          *uname    = va_arg(ap, const char *);
240           gid_t                agroup   = va_arg(ap, gid_t);
241           gid_t               *groups   = va_arg(ap, gid_t *);
242           int                  maxgrp   = va_arg(ap, int);
243           int                 *groupc   = va_arg(ap, int *);
244 
245           struct __grstate_nis          state;
246           struct group                  grp;
247           char                          grpbuf[_GETGR_R_SIZE_MAX];
248           int                           rv, i;
249 
250           _DIAGASSERT(result != NULL);
251           _DIAGASSERT(uname != NULL);
252           /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
253           _DIAGASSERT(groupc != NULL);
254 
255                                                             /* install primary group */
256           (void) __gr_addgid(agroup, groups, maxgrp, groupc);
257 
258           memset(&state, 0, sizeof(state));
259           while (__grscan_nis(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
260                                         0, NULL, 0) == NS_SUCCESS) {
261                                                             /* scan members */
262                     for (i = 0; grp.gr_mem[i]; i++) {
263                               if (strcmp(grp.gr_mem[i], uname) != 0)
264                                         continue;
265                               if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
266                                         *result = -1;
267                               break;
268                     }
269           }
270           __grend_nis(&state);
271 
272           return NS_NOTFOUND;
273 }
274 
275 #endif /* YP */
276 
277 
278 #ifdef _GROUP_COMPAT
279 
280 struct __compatggm {
281           const char          *uname;             /* user to search for */
282           gid_t               *groups;
283           gid_t                agroup;
284           int                  maxgrp;
285           int                 *groupc;
286 };
287 
288 static int
_compat_ggm_search(void * cookie,struct group ** groupres)289 _compat_ggm_search(void *cookie, struct group **groupres)
290 {
291           struct __compatggm  *cp;
292           int                           rerror, crv;
293 
294           static const ns_dtab dtab[] = {
295                     NS_FILES_CB(__grbad_compat, "files")
296                     NS_DNS_CB(_dns_getgroupmembership, NULL)
297                     NS_NIS_CB(_nis_getgroupmembership, NULL)
298                     NS_COMPAT_CB(__grbad_compat, "compat")
299                     NS_NULL_CB
300           };
301 
302           *groupres = NULL;   /* we don't care about this */
303           cp = (struct __compatggm *)cookie;
304 
305           crv = nsdispatch(NULL, dtab,
306               NSDB_GROUP_COMPAT, "getgroupmembership",
307               __nsdefaultnis,
308               &rerror, cp->uname, cp->agroup, cp->groups, cp->maxgrp, cp->groupc);
309 
310           if (crv == NS_SUCCESS)
311                     crv = NS_NOTFOUND;  /* indicate "no more +: entries" */
312 
313           return crv;
314 }
315 
316 /* ARGSUSED */
317 static int
_compat_getgroupmembership(void * retval,void * cb_data,va_list ap)318 _compat_getgroupmembership(void *retval, void *cb_data, va_list ap)
319 {
320           int                 *result   = va_arg(ap, int *);
321           const char          *uname    = va_arg(ap, const char *);
322           gid_t                agroup   = va_arg(ap, gid_t);
323           gid_t               *groups   = va_arg(ap, gid_t *);
324           int                  maxgrp   = va_arg(ap, int);
325           int                 *groupc   = va_arg(ap, int *);
326 
327           struct __grstate_compat       state;
328           struct __compatggm  ggmstate;
329           struct group                  grp;
330           char                          grpbuf[_GETGR_R_SIZE_MAX];
331           int                           rv, i;
332 
333           _DIAGASSERT(result != NULL);
334           _DIAGASSERT(uname != NULL);
335           /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
336           _DIAGASSERT(groupc != NULL);
337 
338                                                             /* install primary group */
339           (void) __gr_addgid(agroup, groups, maxgrp, groupc);
340 
341           memset(&state, 0, sizeof(state));
342           memset(&ggmstate, 0, sizeof(ggmstate));
343           ggmstate.uname = uname;
344           ggmstate.groups = groups;
345           ggmstate.agroup = agroup;
346           ggmstate.maxgrp = maxgrp;
347           ggmstate.groupc = groupc;
348 
349           while (__grscan_compat(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
350                                         0, NULL, 0, _compat_ggm_search, &ggmstate)
351                     == NS_SUCCESS) {
352                                                             /* scan members */
353                     for (i = 0; grp.gr_mem[i]; i++) {
354                               if (strcmp(grp.gr_mem[i], uname) != 0)
355                                         continue;
356                               if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
357                                         *result = -1;
358                               break;
359                     }
360           }
361 
362           __grend_compat(&state);
363           return NS_NOTFOUND;
364 }
365 
366 #endif    /* _GROUP_COMPAT */
367 
368 
369 int
getgroupmembership(const char * uname,gid_t agroup,gid_t * groups,int maxgrp,int * groupc)370 getgroupmembership(const char *uname, gid_t agroup,
371     gid_t *groups, int maxgrp, int *groupc)
372 {
373           int       rerror;
374 
375           static const ns_dtab dtab[] = {
376                     NS_FILES_CB(_files_getgroupmembership, NULL)
377                     NS_DNS_CB(_dns_getgroupmembership, NULL)
378                     NS_NIS_CB(_nis_getgroupmembership, NULL)
379                     NS_COMPAT_CB(_compat_getgroupmembership, NULL)
380                     NS_NULL_CB
381           };
382 
383           _DIAGASSERT(uname != NULL);
384           /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
385           _DIAGASSERT(groupc != NULL);
386 
387           *groupc = 0;
388 
389           mutex_lock(&__grmutex);
390                               /*
391                                * Call each backend.
392                                * For compatibility with getgrent(3) semantics,
393                                * a backend should return NS_NOTFOUND even upon
394                                * completion, to allow result merging to occur.
395                                */
396           (void) nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
397               __nsdefaultcompat,
398               &rerror, uname, agroup, groups, maxgrp, groupc);
399           mutex_unlock(&__grmutex);
400 
401           if (*groupc > maxgrp)                             /* too many groups found */
402                     return -1;
403           else
404                     return 0;
405 }
406