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