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