2 * Copyright (c) 2010, 2011, 2013 Todd C. Miller <Todd.Miller@courtesan.com>
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.
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.
19 #ifndef HAVE_GETGROUPLIST
21 #include <sys/types.h>
30 #endif /* STDC_HEADERS */
33 #endif /* HAVE_STRING_H */
36 #endif /* HAVE_STRINGS_H */
38 #ifdef HAVE_NSS_SEARCH
40 # include <nsswitch.h>
41 # ifdef HAVE_NSS_DBDEFS_H
42 # include <nss_dbdefs.h>
44 # include "compat/nss_dbdefs.h"
50 #if defined(HAVE_GETGRSET)
52 * BSD-compatible getgrouplist(3) using getgrset(3)
55 getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
57 char *cp, *grset = NULL;
59 int grpsize = *ngroupsp;
63 /* We support BSD semantics where the first element is the base gid */
69 aix_setauthdb((char *) name);
71 if ((grset = getgrset(name)) != NULL) {
72 for (cp = strtok(grset, ","); cp != NULL; cp = strtok(NULL, ",")) {
75 if (ngroups == grpsize)
77 groups[ngroups++] = gid;
93 #elif defined(HAVE_NSS_SEARCH)
96 # define GID_MAX UID_MAX
100 # define ALIGNBYTES (sizeof(long) - 1L)
103 # define ALIGN(p) (((unsigned long)(p) + ALIGNBYTES) & ~ALIGNBYTES)
106 extern void _nss_initf_group(nss_db_params_t *);
109 * Convert a groups file string (instr) to a struct group (ent) using
113 str2grp(const char *instr, int inlen, void *ent, char *buf, int buflen)
115 struct group *grp = ent;
116 char *cp, *ep, *fieldsep = buf;
117 char **gr_mem, **gr_end;
121 /* Must at least have space to copy instr -> buf. */
123 return NSS_STR_PARSE_ERANGE;
125 /* Paranoia: buf and instr should be distinct. */
127 memmove(buf, instr, inlen);
131 if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
132 return NSS_STR_PARSE_PARSE;
136 /* Check for YP inclusion/exclusion entries. */
137 if (*cp == '+' || *cp == '-') {
138 /* Only the name is required for YP inclusion/exclusion entries. */
145 if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
146 return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
150 if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
151 return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
153 gid = strtoul(cp, &ep, 10);
154 if (*cp == '\0' || *ep != '\0')
155 return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
157 if (*cp == '-' && gid != 0) {
158 /* Negative gids get mapped to nobody on Solaris. */
159 grp->gr_gid = GID_NOBODY;
162 if ((errno == ERANGE && gid == ULONG_MAX) ||
163 gid > GID_MAX || gid != (gid_t)gid) {
164 return NSS_STR_PARSE_ERANGE;
166 grp->gr_gid = (gid_t)gid;
169 /* Store group members, taking care to use proper alignment. */
171 if (*fieldsep != '\0') {
172 grp->gr_mem = gr_mem = (char **)ALIGN(buf + inlen + 1);
173 gr_end = (char **)((unsigned long)(buf + buflen) & ~ALIGNBYTES);
175 if (gr_mem == gr_end)
176 return NSS_STR_PARSE_ERANGE; /* out of space! */
178 if (fieldsep == NULL)
180 if ((fieldsep = strchr(cp = fieldsep, ',')) != NULL)
185 return NSS_STR_PARSE_SUCCESS;
189 process_cstr(const char *instr, int inlen, struct nss_groupsbymem *gbm)
191 const char *user = gbm->username;
192 nss_status_t rval = NSS_NOTFOUND;
198 buf = _nss_XbyY_buf_alloc(sizeof(struct group), NSS_BUFLEN_GROUP);
202 /* Parse groups file string -> struct group. */
204 error = (*gbm->str2ent)(instr, inlen, grp, buf->buffer, buf->buflen);
205 if (error || grp->gr_mem == NULL)
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 */
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. */
224 _nss_XbyY_buf_free(buf);
229 * BSD-compatible getgrouplist(3) using nss_search(3)
232 getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
234 struct nss_groupsbymem gbm;
235 static DEFINE_NSS_DB_ROOT(db_root);
237 /* We support BSD semantics where the first element is the base gid */
242 memset(&gbm, 0, sizeof(gbm));
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;
252 * Can't use nss_search return value since it may return NSS_UNAVAIL
253 * when no nsswitch.conf entry (e.g. compat mode).
255 (void)nss_search(&db_root, _nss_initf_group, NSS_DBOP_GROUP_BYMEMBER, &gbm);
257 if (gbm.numgids <= gbm.maxgids) {
258 *ngroupsp = gbm.numgids;
261 *ngroupsp = gbm.maxgids;
265 #else /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */
268 * BSD-compatible getgrouplist(3) using getgrent(3)
271 getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
274 int grpsize = *ngroupsp;
278 /* We support BSD semantics where the first element is the base gid */
284 while ((grp = getgrent()) != NULL) {
285 if (grp->gr_gid == basegid)
288 for (i = 0; grp->gr_mem[i] != NULL; i++) {
289 if (strcmp(name, grp->gr_mem[i]) == 0)
292 if (grp->gr_mem[i] == NULL)
293 continue; /* user not found */
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])
301 if (ngroups == grpsize)
303 groups[ngroups++] = grp->gr_gid;
314 #endif /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */
315 #endif /* HAVE_GETGROUPLIST */