Imported Upstream version 1.8.7
[debian/sudo] / compat / getgrouplist.c
1 /*
2  * Copyright (c) 2010, 2011, 2013 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <config.h>
18
19 #ifndef HAVE_GETGROUPLIST
20
21 #include <sys/types.h>
22 #include <stdio.h>
23 #ifdef STDC_HEADERS
24 # include <stdlib.h>
25 # include <stddef.h>
26 #else
27 # ifdef HAVE_STDLIB_H
28 #  include <stdlib.h>
29 # endif
30 #endif /* STDC_HEADERS */
31 #ifdef HAVE_STRING_H
32 # include <string.h>
33 #endif /* HAVE_STRING_H */
34 #ifdef HAVE_STRINGS_H
35 # include <strings.h>
36 #endif /* HAVE_STRINGS_H */
37 #include <grp.h>
38 #ifdef HAVE_NSS_SEARCH
39 # include <limits.h>
40 # include <nsswitch.h>
41 # ifdef HAVE_NSS_DBDEFS_H
42 #  include <nss_dbdefs.h>
43 # else
44 #  include "compat/nss_dbdefs.h"
45 # endif
46 #endif
47
48 #include "missing.h"
49
50 #if defined(HAVE_GETGRSET)
51 /*
52  * BSD-compatible getgrouplist(3) using getgrset(3)
53  */
54 int
55 getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
56 {
57     char *cp, *grset = NULL;
58     int i, ngroups = 1;
59     int grpsize = *ngroupsp;
60     int rval = -1;
61     gid_t gid;
62
63     /* We support BSD semantics where the first element is the base gid */
64     if (grpsize <= 0)
65         return -1;
66     groups[0] = basegid;
67
68 #ifdef HAVE_SETAUTHDB
69     aix_setauthdb((char *) name);
70 #endif
71     if ((grset = getgrset(name)) != NULL) {
72         for (cp = strtok(grset, ","); cp != NULL; cp = strtok(NULL, ",")) {
73             gid = atoi(cp);
74             if (gid != basegid) {
75                 if (ngroups == grpsize)
76                     goto done;
77                 groups[ngroups++] = gid;
78             }
79         }
80     }
81     rval = 0;
82
83 done:
84     free(grset);
85 #ifdef HAVE_SETAUTHDB
86     aix_restoreauthdb();
87 #endif
88     *ngroupsp = ngroups;
89
90     return rval;
91 }
92
93 #elif defined(HAVE_NSS_SEARCH)
94
95 #ifndef GID_MAX
96 # define GID_MAX        UID_MAX
97 #endif
98
99 #ifndef ALIGNBYTES
100 # define ALIGNBYTES     (sizeof(long) - 1L)
101 #endif
102 #ifndef ALIGN
103 # define ALIGN(p)       (((unsigned long)(p) + ALIGNBYTES) & ~ALIGNBYTES)
104 #endif
105
106 extern void _nss_initf_group(nss_db_params_t *);
107
108 /*
109  * Convert a groups file string (instr) to a struct group (ent) using
110  * buf for storage.  
111  */
112 static int
113 str2grp(const char *instr, int inlen, void *ent, char *buf, int buflen)
114 {
115     struct group *grp = ent;
116     char *cp, *ep, *fieldsep = buf;
117     char **gr_mem, **gr_end;
118     int yp = 0;
119     unsigned long gid;
120
121     /* Must at least have space to copy instr -> buf. */
122     if (inlen >= buflen)
123         return NSS_STR_PARSE_ERANGE;
124
125     /* Paranoia: buf and instr should be distinct. */
126     if (buf != instr) {
127         memmove(buf, instr, inlen);
128         buf[inlen] = '\0';
129     }
130
131     if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
132         return NSS_STR_PARSE_PARSE;
133     *fieldsep++ = '\0';
134     grp->gr_name = cp;
135
136     /* Check for YP inclusion/exclusion entries. */
137     if (*cp == '+' || *cp == '-') {
138         /* Only the name is required for YP inclusion/exclusion entries. */
139         grp->gr_passwd = "";
140         grp->gr_gid = 0;
141         grp->gr_mem = NULL;
142         yp = 1;
143     }
144
145     if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
146         return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
147     *fieldsep++ = '\0';
148     grp->gr_passwd = cp;
149
150     if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
151         return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
152     *fieldsep++ = '\0';
153     gid = strtoul(cp, &ep, 10);
154     if (*cp == '\0' || *ep != '\0')
155         return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
156 #ifdef GID_NOBODY
157     if (*cp == '-' && gid != 0) {
158         /* Negative gids get mapped to nobody on Solaris. */
159         grp->gr_gid = GID_NOBODY;
160     } else
161 #endif
162     if ((errno == ERANGE && gid == ULONG_MAX) ||
163         gid > GID_MAX || gid != (gid_t)gid) {
164         return NSS_STR_PARSE_ERANGE;
165     } else {
166         grp->gr_gid = (gid_t)gid;
167     }
168
169     /* Store group members, taking care to use proper alignment. */
170     grp->gr_mem = NULL;
171     if (*fieldsep != '\0') {
172         grp->gr_mem = gr_mem = (char **)ALIGN(buf + inlen + 1);
173         gr_end = (char **)((unsigned long)(buf + buflen) & ~ALIGNBYTES);
174         for (;;) {
175             if (gr_mem == gr_end)
176                 return NSS_STR_PARSE_ERANGE;    /* out of space! */
177             *gr_mem++ = cp;
178             if (fieldsep == NULL)
179                 break;
180             if ((fieldsep = strchr(cp = fieldsep, ',')) != NULL)
181                 *fieldsep++ = '\0';
182         }
183         *gr_mem = NULL;
184     }
185     return NSS_STR_PARSE_SUCCESS;
186 }
187
188 static nss_status_t
189 process_cstr(const char *instr, int inlen, struct nss_groupsbymem *gbm)
190 {
191     const char *user = gbm->username;
192     nss_status_t rval = NSS_NOTFOUND;
193     nss_XbyY_buf_t *buf;
194     struct group *grp;
195     char **gr_mem;
196     int error, i;
197
198     buf = _nss_XbyY_buf_alloc(sizeof(struct group), NSS_BUFLEN_GROUP);
199     if (buf == NULL)
200         return NSS_UNAVAIL;
201
202     /* Parse groups file string -> struct group. */
203     grp = buf->result;
204     error = (*gbm->str2ent)(instr, inlen, grp, buf->buffer, buf->buflen);
205     if (error || grp->gr_mem == NULL)
206         goto done;
207
208     for (gr_mem = grp->gr_mem; *gr_mem != NULL; gr_mem++) {
209         if (strcmp(*gr_mem, user) == 0) {
210             /* Append to gid_array unless gr_gid is a dupe. */
211             for (i = 0; i < gbm->numgids; i++) {
212                 if (gbm->gid_array[i] == grp->gr_gid)
213                     goto done;                  /* already present */
214             }
215             /* Store gid if there is space. */
216             if (i < gbm->maxgids)
217                 gbm->gid_array[i] = grp->gr_gid;
218             /* Always increment numgids so we can detect when out of space. */
219             gbm->numgids++;
220             goto done;
221         }
222     }
223 done:
224     _nss_XbyY_buf_free(buf);
225     return rval;
226 }
227
228 /*
229  * BSD-compatible getgrouplist(3) using nss_search(3)
230  */
231 int
232 getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
233 {
234     struct nss_groupsbymem gbm;
235     static DEFINE_NSS_DB_ROOT(db_root);
236
237     /* We support BSD semantics where the first element is the base gid */
238     if (*ngroupsp <= 0)
239         return -1;
240     groups[0] = basegid;
241
242     memset(&gbm, 0, sizeof(gbm));
243     gbm.username = name;
244     gbm.gid_array = groups;
245     gbm.maxgids = *ngroupsp;
246     gbm.numgids = 1; /* for basegid */
247     gbm.force_slow_way = 1;
248     gbm.str2ent = str2grp;
249     gbm.process_cstr = process_cstr;
250
251     /*
252      * Can't use nss_search return value since it may return NSS_UNAVAIL
253      * when no nsswitch.conf entry (e.g. compat mode).
254      */
255     (void)nss_search(&db_root, _nss_initf_group, NSS_DBOP_GROUP_BYMEMBER, &gbm);
256
257     if (gbm.numgids <= gbm.maxgids) {
258         *ngroupsp = gbm.numgids;
259         return 0;
260     }
261     *ngroupsp = gbm.maxgids;
262     return -1;
263 }
264
265 #else /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */
266
267 /*
268  * BSD-compatible getgrouplist(3) using getgrent(3)
269  */
270 int
271 getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
272 {
273     int i, ngroups = 1;
274     int grpsize = *ngroupsp;
275     int rval = -1;
276     struct group *grp;
277
278     /* We support BSD semantics where the first element is the base gid */
279     if (grpsize <= 0)
280         return -1;
281     groups[0] = basegid;
282
283     setgrent();
284     while ((grp = getgrent()) != NULL) {
285         if (grp->gr_gid == basegid)
286             continue;
287
288         for (i = 0; grp->gr_mem[i] != NULL; i++) {
289             if (strcmp(name, grp->gr_mem[i]) == 0)
290                 break;
291         }
292         if (grp->gr_mem[i] == NULL)
293             continue; /* user not found */
294
295         /* Only add if it is not the same as an existing gid */
296         for (i = 0; i < ngroups; i++) {
297             if (grp->gr_gid == groups[i])
298                 break;
299         }
300         if (i == ngroups) {
301             if (ngroups == grpsize)
302                 goto done;
303             groups[ngroups++] = grp->gr_gid;
304         }
305     }
306     rval = 0;
307
308 done:
309     endgrent();
310     *ngroupsp = ngroups;
311
312     return rval;
313 }
314 #endif /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */
315 #endif /* HAVE_GETGROUPLIST */