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