1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2020 Joyent, Inc. 26 */ 27 28 /* Copyright (c) 1988 AT&T */ 29 /* All Rights Reserved */ 30 31 #pragma weak _initgroups = initgroups 32 33 #include <stdlib.h> 34 #include <string.h> 35 #include <errno.h> 36 #include <grp.h> 37 #include <limits.h> 38 #include <sys/debug.h> 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <unistd.h> 42 43 /* Private interface to the groups code in getgrnam.c */ 44 extern int _getgroupsbymember(const char *, gid_t[], int, int); 45 46 int 47 initgroups(const char *uname, gid_t agroup) 48 { 49 gid_t *groups; 50 long ngroups_max; 51 int ngroups; 52 int errsave, retsave; 53 54 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) < 0) { 55 /* ==== Hope sysconf() set errno to something sensible */ 56 return (-1); 57 } 58 /* 59 * ngroups_max is the maximum number of supplemental groups per 60 * process. if no supplemental groups are allowed, we're done. 61 */ 62 if (ngroups_max == 0) 63 return (0); 64 65 if ((groups = (gid_t *)calloc(ngroups_max, sizeof (gid_t))) == 0) { 66 errno = ENOMEM; 67 return (-1); 68 } 69 groups[0] = agroup; 70 71 ngroups = _getgroupsbymember(uname, groups, (int)ngroups_max, 72 (agroup <= MAXUID) ? 1 : 0); 73 if (ngroups < 0) { 74 /* XXX -- man page does not define a value for errno in */ 75 /* this case. Should be looked into sometime. */ 76 free(groups); 77 return (-1); 78 } 79 80 retsave = setgroups(ngroups, groups); 81 errsave = errno; 82 83 free(groups); 84 85 errno = errsave; 86 return (retsave); 87 } 88 89 int 90 getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *ngroups) 91 { 92 gid_t *grouplist = NULL; 93 gid_t *grpptr; 94 long ngroups_max; 95 int sz, ret; 96 97 /* 98 * We require sysconf(_SC_NGROUPS_MAX) either returns a sane value (>0) 99 * or fails. If it returns 0, something has gone horribly, horribly 100 * wrong. 101 */ 102 ngroups_max = sysconf(_SC_NGROUPS_MAX); 103 if (ngroups_max > INT_MAX) 104 ngroups_max = INT_MAX; 105 else if (ngroups_max < 0) 106 return (-1); 107 VERIFY3S(ngroups_max, >, 0); 108 109 /* 110 * The documented behavior of getgrouplist(3C) on other platforms 111 * (e.g. Linux and FreeBSD) do not list any failures other than 112 * 'groups is too small'. However, examination of some popular 113 * implementations of getgrouplist on those platforms (e.g. glibc and 114 * musl -- both appear to share the same man page for getgrouplist(3)) 115 * show that they can in fact fail for other reasons (e.g. ENOMEM, 116 * EIO). 117 * 118 * As such, we don't attempt to catch and deal with any underlying 119 * errors here. Instead, any underlying errors cause getgrouplist(3C) 120 * to fail, and any errno value set is left unmodified for examination 121 * by the caller. 122 * 123 * One small complication is that the internal _getgroupsbymember() 124 * itself doesn't provide any way to report back if the buffer supplied 125 * to _getgroupsbymember() is too small. Instead, we always supply 126 * a buffer large enough to hold _SC_NGROUPS_MAX entries -- either 127 * by allocating one ourselves or using the user supplied buffer if 128 * sufficiently large. 129 * 130 * The system behavior is undefined for any user in more groups than 131 * _SC_NGROUPS_MAX -- initgroups(3C) for example just ignores any 132 * excess groups (and which _SC_NGROUPS_MAX sized subset of groups end 133 * up being set as the secondary groups is non-deterministic), so this 134 * seems reasonable. Modifying _getgroupsbymember() would require 135 * modification of the NSS code (due to the pervasive special handling 136 * of _getgroupsbymember() in the NSS code) as well as modification of 137 * all NSS backends that implement it. As there are at least a few 138 * known third party NSS backends, we've opted to avoid doing this 139 * for now. 140 */ 141 142 if ((ngroups == NULL) || (*ngroups <= 0) || (groups == NULL)) { 143 *ngroups = ngroups_max; 144 errno = EINVAL; 145 return (-1); 146 } 147 148 if (*ngroups < ngroups_max) { 149 /* 150 * The caller's buffer might be too small, try to use our own 151 * buffer instead. 152 */ 153 grouplist = calloc(ngroups_max, sizeof (gid_t)); 154 if (grouplist == NULL) 155 return (-1); 156 157 grpptr = grouplist; 158 sz = ngroups_max; 159 } else { 160 /* The caller's buffer is large enough, so use it */ 161 grpptr = groups; 162 sz = *ngroups; 163 } 164 165 /* 166 * Always add agroup as the first member -- it should always appear 167 * in the resulting list of groups, and this allows the backends to 168 * skip adding it. 169 */ 170 grpptr[0] = agroup; 171 172 ret = _getgroupsbymember(uname, grpptr, sz, 1); 173 174 /* 175 * We passed in 1 group entry. We should at minimum get 1 entry back 176 * from _getgroupsbymember(). If we don't, there is a bug in the NSS 177 * code or a backend. Since the return value is used to size a copy 178 * further below, we hard fail (abort) here if we get back an 179 * impossible value so we're not traipsing all over memory (which would 180 * just make debugging any such problem all the more difficult). 181 */ 182 VERIFY3S(ret, >, 0); 183 184 /* 185 * If we used the caller's buffer, it means its size was >= ngroups_max 186 * entries, and we're done. 187 */ 188 if (grpptr == groups) { 189 /* Set *ngroups to the number of entries in groups */ 190 *ngroups = ret; 191 return (ret); 192 } 193 194 /* We verified earlier *ngroups > 0 */ 195 if (ret < *ngroups) { 196 /* Copy as many gids that will fit */ 197 (void) memcpy(groups, grpptr, *ngroups * sizeof (gid_t)); 198 199 *ngroups = ret; 200 ret = -1; 201 errno = ERANGE; 202 } else { 203 (void) memcpy(groups, grpptr, ret * sizeof (gid_t)); 204 *ngroups = ret; 205 } 206 207 free(grouplist); 208 return (ret); 209 } 210