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