17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5f48205beScasper * Common Development and Distribution License (the "License").
6f48205beScasper * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217257d1b4Sraf
227c478bd9Sstevel@tonic-gate /*
237257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
25*f2c438c5SJason King * Copyright 2020 Joyent, Inc.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
297c478bd9Sstevel@tonic-gate /* All Rights Reserved */
307c478bd9Sstevel@tonic-gate
317257d1b4Sraf #pragma weak _initgroups = initgroups
327c478bd9Sstevel@tonic-gate
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
34*f2c438c5SJason King #include <string.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <grp.h>
37*f2c438c5SJason King #include <limits.h>
38*f2c438c5SJason King #include <sys/debug.h>
397c478bd9Sstevel@tonic-gate #include <sys/types.h>
40f48205beScasper #include <sys/param.h>
417c478bd9Sstevel@tonic-gate #include <unistd.h>
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate /* Private interface to the groups code in getgrnam.c */
447c478bd9Sstevel@tonic-gate extern int _getgroupsbymember(const char *, gid_t[], int, int);
457c478bd9Sstevel@tonic-gate
467c478bd9Sstevel@tonic-gate int
initgroups(const char * uname,gid_t agroup)477c478bd9Sstevel@tonic-gate initgroups(const char *uname, gid_t agroup)
487c478bd9Sstevel@tonic-gate {
497c478bd9Sstevel@tonic-gate gid_t *groups;
507c478bd9Sstevel@tonic-gate long ngroups_max;
517c478bd9Sstevel@tonic-gate int ngroups;
527c478bd9Sstevel@tonic-gate int errsave, retsave;
537c478bd9Sstevel@tonic-gate
547c478bd9Sstevel@tonic-gate if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) < 0) {
557c478bd9Sstevel@tonic-gate /* ==== Hope sysconf() set errno to something sensible */
567c478bd9Sstevel@tonic-gate return (-1);
577c478bd9Sstevel@tonic-gate }
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate * ngroups_max is the maximum number of supplemental groups per
607c478bd9Sstevel@tonic-gate * process. if no supplemental groups are allowed, we're done.
617c478bd9Sstevel@tonic-gate */
627c478bd9Sstevel@tonic-gate if (ngroups_max == 0)
637c478bd9Sstevel@tonic-gate return (0);
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate if ((groups = (gid_t *)calloc(ngroups_max, sizeof (gid_t))) == 0) {
667c478bd9Sstevel@tonic-gate errno = ENOMEM;
677c478bd9Sstevel@tonic-gate return (-1);
687c478bd9Sstevel@tonic-gate }
697c478bd9Sstevel@tonic-gate groups[0] = agroup;
707c478bd9Sstevel@tonic-gate
717c478bd9Sstevel@tonic-gate ngroups = _getgroupsbymember(uname, groups, (int)ngroups_max,
727257d1b4Sraf (agroup <= MAXUID) ? 1 : 0);
737c478bd9Sstevel@tonic-gate if (ngroups < 0) {
747c478bd9Sstevel@tonic-gate /* XXX -- man page does not define a value for errno in */
757c478bd9Sstevel@tonic-gate /* this case. Should be looked into sometime. */
767c478bd9Sstevel@tonic-gate free(groups);
777c478bd9Sstevel@tonic-gate return (-1);
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate
807c478bd9Sstevel@tonic-gate retsave = setgroups(ngroups, groups);
817c478bd9Sstevel@tonic-gate errsave = errno;
827c478bd9Sstevel@tonic-gate
837c478bd9Sstevel@tonic-gate free(groups);
847c478bd9Sstevel@tonic-gate
857c478bd9Sstevel@tonic-gate errno = errsave;
867c478bd9Sstevel@tonic-gate return (retsave);
877c478bd9Sstevel@tonic-gate }
88*f2c438c5SJason King
89*f2c438c5SJason King int
getgrouplist(const char * uname,gid_t agroup,gid_t * groups,int * ngroups)90*f2c438c5SJason King getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *ngroups)
91*f2c438c5SJason King {
92*f2c438c5SJason King gid_t *grouplist = NULL;
93*f2c438c5SJason King gid_t *grpptr;
94*f2c438c5SJason King long ngroups_max;
95*f2c438c5SJason King int sz, ret;
96*f2c438c5SJason King
97*f2c438c5SJason King /*
98*f2c438c5SJason King * We require sysconf(_SC_NGROUPS_MAX) either returns a sane value (>0)
99*f2c438c5SJason King * or fails. If it returns 0, something has gone horribly, horribly
100*f2c438c5SJason King * wrong.
101*f2c438c5SJason King */
102*f2c438c5SJason King ngroups_max = sysconf(_SC_NGROUPS_MAX);
103*f2c438c5SJason King if (ngroups_max > INT_MAX)
104*f2c438c5SJason King ngroups_max = INT_MAX;
105*f2c438c5SJason King else if (ngroups_max < 0)
106*f2c438c5SJason King return (-1);
107*f2c438c5SJason King VERIFY3S(ngroups_max, >, 0);
108*f2c438c5SJason King
109*f2c438c5SJason King /*
110*f2c438c5SJason King * The documented behavior of getgrouplist(3C) on other platforms
111*f2c438c5SJason King * (e.g. Linux and FreeBSD) do not list any failures other than
112*f2c438c5SJason King * 'groups is too small'. However, examination of some popular
113*f2c438c5SJason King * implementations of getgrouplist on those platforms (e.g. glibc and
114*f2c438c5SJason King * musl -- both appear to share the same man page for getgrouplist(3))
115*f2c438c5SJason King * show that they can in fact fail for other reasons (e.g. ENOMEM,
116*f2c438c5SJason King * EIO).
117*f2c438c5SJason King *
118*f2c438c5SJason King * As such, we don't attempt to catch and deal with any underlying
119*f2c438c5SJason King * errors here. Instead, any underlying errors cause getgrouplist(3C)
120*f2c438c5SJason King * to fail, and any errno value set is left unmodified for examination
121*f2c438c5SJason King * by the caller.
122*f2c438c5SJason King *
123*f2c438c5SJason King * One small complication is that the internal _getgroupsbymember()
124*f2c438c5SJason King * itself doesn't provide any way to report back if the buffer supplied
125*f2c438c5SJason King * to _getgroupsbymember() is too small. Instead, we always supply
126*f2c438c5SJason King * a buffer large enough to hold _SC_NGROUPS_MAX entries -- either
127*f2c438c5SJason King * by allocating one ourselves or using the user supplied buffer if
128*f2c438c5SJason King * sufficiently large.
129*f2c438c5SJason King *
130*f2c438c5SJason King * The system behavior is undefined for any user in more groups than
131*f2c438c5SJason King * _SC_NGROUPS_MAX -- initgroups(3C) for example just ignores any
132*f2c438c5SJason King * excess groups (and which _SC_NGROUPS_MAX sized subset of groups end
133*f2c438c5SJason King * up being set as the secondary groups is non-deterministic), so this
134*f2c438c5SJason King * seems reasonable. Modifying _getgroupsbymember() would require
135*f2c438c5SJason King * modification of the NSS code (due to the pervasive special handling
136*f2c438c5SJason King * of _getgroupsbymember() in the NSS code) as well as modification of
137*f2c438c5SJason King * all NSS backends that implement it. As there are at least a few
138*f2c438c5SJason King * known third party NSS backends, we've opted to avoid doing this
139*f2c438c5SJason King * for now.
140*f2c438c5SJason King */
141*f2c438c5SJason King
142*f2c438c5SJason King if ((ngroups == NULL) || (*ngroups <= 0) || (groups == NULL)) {
143*f2c438c5SJason King *ngroups = ngroups_max;
144*f2c438c5SJason King errno = EINVAL;
145*f2c438c5SJason King return (-1);
146*f2c438c5SJason King }
147*f2c438c5SJason King
148*f2c438c5SJason King if (*ngroups < ngroups_max) {
149*f2c438c5SJason King /*
150*f2c438c5SJason King * The caller's buffer might be too small, try to use our own
151*f2c438c5SJason King * buffer instead.
152*f2c438c5SJason King */
153*f2c438c5SJason King grouplist = calloc(ngroups_max, sizeof (gid_t));
154*f2c438c5SJason King if (grouplist == NULL)
155*f2c438c5SJason King return (-1);
156*f2c438c5SJason King
157*f2c438c5SJason King grpptr = grouplist;
158*f2c438c5SJason King sz = ngroups_max;
159*f2c438c5SJason King } else {
160*f2c438c5SJason King /* The caller's buffer is large enough, so use it */
161*f2c438c5SJason King grpptr = groups;
162*f2c438c5SJason King sz = *ngroups;
163*f2c438c5SJason King }
164*f2c438c5SJason King
165*f2c438c5SJason King /*
166*f2c438c5SJason King * Always add agroup as the first member -- it should always appear
167*f2c438c5SJason King * in the resulting list of groups, and this allows the backends to
168*f2c438c5SJason King * skip adding it.
169*f2c438c5SJason King */
170*f2c438c5SJason King grpptr[0] = agroup;
171*f2c438c5SJason King
172*f2c438c5SJason King ret = _getgroupsbymember(uname, grpptr, sz, 1);
173*f2c438c5SJason King
174*f2c438c5SJason King /*
175*f2c438c5SJason King * We passed in 1 group entry. We should at minimum get 1 entry back
176*f2c438c5SJason King * from _getgroupsbymember(). If we don't, there is a bug in the NSS
177*f2c438c5SJason King * code or a backend. Since the return value is used to size a copy
178*f2c438c5SJason King * further below, we hard fail (abort) here if we get back an
179*f2c438c5SJason King * impossible value so we're not traipsing all over memory (which would
180*f2c438c5SJason King * just make debugging any such problem all the more difficult).
181*f2c438c5SJason King */
182*f2c438c5SJason King VERIFY3S(ret, >, 0);
183*f2c438c5SJason King
184*f2c438c5SJason King /*
185*f2c438c5SJason King * If we used the caller's buffer, it means its size was >= ngroups_max
186*f2c438c5SJason King * entries, and we're done.
187*f2c438c5SJason King */
188*f2c438c5SJason King if (grpptr == groups) {
189*f2c438c5SJason King /* Set *ngroups to the number of entries in groups */
190*f2c438c5SJason King *ngroups = ret;
191*f2c438c5SJason King return (ret);
192*f2c438c5SJason King }
193*f2c438c5SJason King
194*f2c438c5SJason King /* We verified earlier *ngroups > 0 */
195*f2c438c5SJason King if (ret < *ngroups) {
196*f2c438c5SJason King /* Copy as many gids that will fit */
197*f2c438c5SJason King (void) memcpy(groups, grpptr, *ngroups * sizeof (gid_t));
198*f2c438c5SJason King
199*f2c438c5SJason King *ngroups = ret;
200*f2c438c5SJason King ret = -1;
201*f2c438c5SJason King errno = ERANGE;
202*f2c438c5SJason King } else {
203*f2c438c5SJason King (void) memcpy(groups, grpptr, ret * sizeof (gid_t));
204*f2c438c5SJason King *ngroups = ret;
205*f2c438c5SJason King }
206*f2c438c5SJason King
207*f2c438c5SJason King free(grouplist);
208*f2c438c5SJason King return (ret);
209*f2c438c5SJason King }
210