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