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