getgroupmembership.c revision 1.1 1 /* $NetBSD: getgroupmembership.c,v 1.1 2005/01/06 15:10:45 lukem 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.1 2005/01/06 15:10:45 lukem 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(grpcnt != NULL);
87
88 /* skip duplicates */
89 for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
90 if (groups[dupc] == gid)
91 return 1;
92 }
93
94 ret = 1;
95 if (*groupc < maxgrp) /* add this gid */
96 groups[*groupc] = gid;
97 else
98 ret = 0;
99 (*groupc)++;
100 return ret;
101 }
102
103
104 /*ARGSUSED*/
105 static int
106 _files_getgroupmembership(void *retval, void *cb_data, va_list ap)
107 {
108 int *result = va_arg(ap, int *);
109 const char *uname = va_arg(ap, const char *);
110 gid_t agroup = va_arg(ap, gid_t);
111 gid_t *groups = va_arg(ap, gid_t *);
112 int maxgrp = va_arg(ap, int);
113 int *groupc = va_arg(ap, int *);
114
115 struct __grstate_files state;
116 struct group grp;
117 char grpbuf[_GETGR_R_SIZE_MAX];
118 int rv, i;
119
120 _DIAGASSERT(result != NULL);
121 _DIAGASSERT(uname != NULL);
122 /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
123 _DIAGASSERT(groupc != NULL);
124
125 /* install primary group */
126 (void) __gr_addgid(agroup, groups, maxgrp, groupc);
127
128 memset(&state, 0, sizeof(state));
129 while (__grscan_files(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
130 0, NULL, 0) == NS_SUCCESS) {
131 /* scan members */
132 for (i = 0; grp.gr_mem[i]; i++) {
133 if (strcmp(grp.gr_mem[i], uname) != 0)
134 continue;
135 if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
136 *result = -1;
137 break;
138 }
139 }
140 __grend_files(&state);
141 return NS_NOTFOUND;
142 }
143
144
145 #ifdef HESIOD
146
147 /*ARGSUSED*/
148 static int
149 _dns_getgroupmembership(void *retval, void *cb_data, va_list ap)
150 {
151 int *result = va_arg(ap, int *);
152 const char *uname = va_arg(ap, const char *);
153 gid_t agroup = va_arg(ap, gid_t);
154 gid_t *groups = va_arg(ap, gid_t *);
155 int maxgrp = va_arg(ap, int);
156 int *groupc = va_arg(ap, int *);
157
158 struct __grstate_dns state;
159 struct group grp;
160 char grpbuf[_GETGR_R_SIZE_MAX];
161 unsigned long id;
162 void *context;
163 char **hp, *cp, *ep;
164 int rv, i;
165
166 _DIAGASSERT(result != NULL);
167 _DIAGASSERT(uname != NULL);
168 /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
169 _DIAGASSERT(groupc != NULL);
170
171 /* install primary group */
172 (void) __gr_addgid(agroup, groups, maxgrp, groupc);
173
174 hp = NULL;
175 rv = NS_NOTFOUND;
176
177 if (hesiod_init(&context) == -1) /* setup hesiod */
178 return NS_UNAVAIL;
179
180 hp = hesiod_resolve(context, uname, "grplist"); /* find grplist */
181 if (hp == NULL) {
182 if (errno != ENOENT) { /* wasn't "not found"*/
183 rv = NS_UNAVAIL;
184 goto dnsgroupmembers_out;
185 }
186 /* grplist not found, fallback to _dns_grscan */
187 memset(&state, 0, sizeof(state));
188 while (__grscan_dns(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
189 0, NULL, 0) == NS_SUCCESS) {
190 /* scan members */
191 for (i = 0; grp.gr_mem[i]; i++) {
192 if (strcmp(grp.gr_mem[i], uname) != 0)
193 continue;
194 if (! __gr_addgid(grp.gr_gid, groups, maxgrp,
195 groupc))
196 *result = -1;
197 break;
198 }
199 }
200 __grend_dns(&state);
201 rv = NS_NOTFOUND;
202 goto dnsgroupmembers_out;
203 }
204
205 if ((ep = strchr(hp[0], '\n')) != NULL)
206 *ep = '\0'; /* clear trailing \n */
207
208 for (cp = hp[0]; *cp != '\0'; ) { /* parse grplist */
209 if ((cp = strchr(cp, ':')) == NULL) /* skip grpname */
210 break;
211 cp++;
212 id = strtoul(cp, &ep, 10); /* parse gid */
213 if (id > GID_MAX || (*ep != ':' && *ep != '\0')) {
214 rv = NS_UNAVAIL;
215 goto dnsgroupmembers_out;
216 }
217 cp = ep;
218 if (*cp == ':')
219 cp++;
220
221 /* add gid */
222 if (! __gr_addgid((gid_t)id, groups, maxgrp, groupc))
223 *result = -1;
224 }
225
226 rv = NS_NOTFOUND;
227
228 dnsgroupmembers_out:
229 if (hp)
230 hesiod_free_list(context, hp);
231 hesiod_end(context);
232 return rv;
233 }
234
235 #endif /* HESIOD */
236
237
238 #ifdef YP
239
240 /*ARGSUSED*/
241 static int
242 _nis_getgroupmembership(void *retval, void *cb_data, va_list ap)
243 {
244 int *result = va_arg(ap, int *);
245 const char *uname = va_arg(ap, const char *);
246 gid_t agroup = va_arg(ap, gid_t);
247 gid_t *groups = va_arg(ap, gid_t *);
248 int maxgrp = va_arg(ap, int);
249 int *groupc = va_arg(ap, int *);
250
251 struct __grstate_nis state;
252 struct group grp;
253 char grpbuf[_GETGR_R_SIZE_MAX];
254 int rv, i;
255
256 _DIAGASSERT(result != NULL);
257 _DIAGASSERT(uname != NULL);
258 /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
259 _DIAGASSERT(groupc != NULL);
260
261 /* install primary group */
262 (void) __gr_addgid(agroup, groups, maxgrp, groupc);
263
264 memset(&state, 0, sizeof(state));
265 while (__grscan_nis(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
266 0, NULL, 0) == NS_SUCCESS) {
267 /* scan members */
268 for (i = 0; grp.gr_mem[i]; i++) {
269 if (strcmp(grp.gr_mem[i], uname) != 0)
270 continue;
271 if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
272 *result = -1;
273 break;
274 }
275 }
276 __grend_nis(&state);
277
278 return NS_NOTFOUND;
279 }
280
281 #endif /* YP */
282
283
284 #ifdef _GROUP_COMPAT
285
286 struct __compatggm {
287 const char *uname; /* user to search for */
288 gid_t *groups;
289 gid_t agroup;
290 int maxgrp;
291 int *groupc;
292 };
293
294 static int
295 _compat_ggm_search(void *cookie, struct group **groupres)
296 {
297 struct __compatggm *cp;
298 int rerror, crv;
299
300 static const ns_dtab dtab[] = {
301 NS_FILES_CB(__grbad_compat, "files")
302 NS_DNS_CB(_dns_getgroupmembership, NULL)
303 NS_NIS_CB(_nis_getgroupmembership, NULL)
304 NS_COMPAT_CB(__grbad_compat, "compat")
305 { 0 }
306 };
307
308 *groupres = NULL; /* we don't care about this */
309 cp = (struct __compatggm *)cookie;
310
311 crv = nsdispatch(NULL, dtab,
312 NSDB_GROUP_COMPAT, "getgroupmembership",
313 __nsdefaultnis,
314 &rerror, cp->uname, cp->agroup, cp->groups, cp->maxgrp, cp->groupc);
315
316 if (crv == NS_SUCCESS)
317 crv = NS_NOTFOUND; /* indicate "no more +: entries" */
318
319 return crv;
320 }
321
322 /* ARGSUSED */
323 static int
324 _compat_getgroupmembership(void *retval, void *cb_data, va_list ap)
325 {
326 int *result = va_arg(ap, int *);
327 const char *uname = va_arg(ap, const char *);
328 gid_t agroup = va_arg(ap, gid_t);
329 gid_t *groups = va_arg(ap, gid_t *);
330 int maxgrp = va_arg(ap, int);
331 int *groupc = va_arg(ap, int *);
332
333 struct __grstate_compat state;
334 struct __compatggm ggmstate;
335 struct group grp;
336 char grpbuf[_GETGR_R_SIZE_MAX];
337 int rv, i;
338
339 _DIAGASSERT(result != NULL);
340 _DIAGASSERT(uname != NULL);
341 /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
342 _DIAGASSERT(groupc != NULL);
343
344 /* install primary group */
345 (void) __gr_addgid(agroup, groups, maxgrp, groupc);
346
347 memset(&state, 0, sizeof(state));
348 memset(&ggmstate, 0, sizeof(ggmstate));
349 ggmstate.uname = uname;
350 ggmstate.groups = groups;
351 ggmstate.agroup = agroup;
352 ggmstate.maxgrp = maxgrp;
353 ggmstate.groupc = groupc;
354
355 while (__grscan_compat(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
356 0, NULL, 0, _compat_ggm_search, &ggmstate)
357 == NS_SUCCESS) {
358 /* scan members */
359 for (i = 0; grp.gr_mem[i]; i++) {
360 if (strcmp(grp.gr_mem[i], uname) != 0)
361 continue;
362 if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
363 *result = -1;
364 break;
365 }
366 }
367
368 __grend_compat(&state);
369 return NS_NOTFOUND;
370 }
371
372 #endif /* _GROUP_COMPAT */
373
374
375 int
376 getgroupmembership(const char *uname, gid_t agroup,
377 gid_t *groups, int maxgrp, int *groupc)
378 {
379 int rerror;
380
381 static const ns_dtab dtab[] = {
382 NS_FILES_CB(_files_getgroupmembership, NULL)
383 NS_DNS_CB(_dns_getgroupmembership, NULL)
384 NS_NIS_CB(_nis_getgroupmembership, NULL)
385 NS_COMPAT_CB(_compat_getgroupmembership, NULL)
386 { 0 }
387 };
388
389 _DIAGASSERT(uname != NULL);
390 /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
391 _DIAGASSERT(groupc != NULL);
392
393 *groupc = 0;
394
395 mutex_lock(&__grmutex);
396 /*
397 * Call each backend.
398 * For compatibility with getgrent(3) semantics,
399 * a backend should return NS_NOTFOUND even upon
400 * completion, to allow result merging to occur.
401 */
402 (void) nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
403 __nsdefaultcompat,
404 &rerror, uname, agroup, groups, maxgrp, groupc);
405 mutex_unlock(&__grmutex);
406
407 if (*groupc > maxgrp) /* too many groups found */
408 return -1;
409 else
410 return 0;
411 }
412