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