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