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
initgroups(const char * uname,gid_t agroup)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
getgrouplist(const char * uname,gid_t agroup,gid_t * groups,int * ngroups)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