1 /*
2 group.c - NSS lookup functions for group database
3
4 Copyright (C) 2006 West Consulting
5 Copyright (C) 2006-2015 Arthur de Jong
6 Copyright (C) 2010 Symas Corporation
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301 USA
22 */
23
24 #include "config.h"
25
26 #include <string.h>
27 #include <errno.h>
28 #include <stdlib.h>
29
30 #include "prototypes.h"
31 #include "common.h"
32 #include "compat/attrs.h"
33
34 /* read a single group entry from the stream */
read_group(TFILE * fp,struct group * result,char * buffer,size_t buflen,int * errnop)35 static nss_status_t read_group(TFILE *fp, struct group *result,
36 char *buffer, size_t buflen, int *errnop)
37 {
38 int32_t tmpint32, tmp2int32, tmp3int32;
39 size_t bufptr = 0;
40 memset(result, 0, sizeof(struct group));
41 READ_BUF_STRING(fp, result->gr_name);
42 READ_BUF_STRING(fp, result->gr_passwd);
43 READ_INT32(fp, result->gr_gid);
44 READ_BUF_STRINGLIST(fp, result->gr_mem);
45 return NSS_STATUS_SUCCESS;
46 }
47
48 /* read all group entries from the stream and add
49 gids of these groups to the list */
read_gids(TFILE * fp,gid_t skipgroup,long int * start,long int * size,gid_t ** groupsp,long int limit,int * errnop)50 static nss_status_t read_gids(TFILE *fp, gid_t skipgroup, long int *start,
51 long int *size, gid_t **groupsp,
52 long int limit, int *errnop)
53 {
54 int32_t res = (int32_t)NSLCD_RESULT_BEGIN;
55 int32_t tmpint32, tmp2int32, tmp3int32;
56 gid_t gid;
57 #ifdef NSS_FLAVOUR_GLIBC
58 gid_t *newgroups;
59 long int newsize;
60 #endif /* NSS_FLAVOUR_GLIBC */
61 /* loop over results */
62 while (res == (int32_t)NSLCD_RESULT_BEGIN)
63 {
64 /* skip group name */
65 SKIP_STRING(fp);
66 /* skip passwd entry */
67 SKIP_STRING(fp);
68 /* read gid */
69 READ_INT32(fp, gid);
70 /* skip members */
71 SKIP_STRINGLIST(fp);
72 /* only add the group to the list if it is not the specified group */
73 if (gid != skipgroup)
74 {
75 #ifdef NSS_FLAVOUR_GLIBC
76 /* check if we reached the limit */
77 if ((limit > 0) && (*start >= limit))
78 return NSS_STATUS_TRYAGAIN;
79 /* check if our buffer is large enough */
80 if ((*start) >= (*size))
81 {
82 /* for some reason Glibc expects us to grow the array (completely
83 different from all other NSS functions) */
84 /* calculate new size */
85 newsize = 2 * (*size);
86 if ((limit > 0) && (*start >= limit))
87 newsize = limit;
88 /* allocate new memory */
89 newgroups = realloc(*groupsp, newsize * sizeof(gid_t));
90 if (newgroups == NULL)
91 return NSS_STATUS_TRYAGAIN;
92 *groupsp = newgroups;
93 *size = newsize;
94 }
95 #endif /* NSS_FLAVOUR_GLIBC */
96 #ifdef NSS_FLAVOUR_SOLARIS
97 /* check if we reached the limit */
98 if ((limit > 0) && (*start >= limit))
99 {
100 *errnop = 1; /* this is args->erange */
101 return NSS_STATUS_NOTFOUND;
102 }
103 #endif /* NSS_FLAVOUR_SOLARIS */
104 /* add gid to list */
105 (*groupsp)[(*start)++] = gid;
106 }
107 /* read next response code (don't bail out on not success since we
108 just want to build up a list) */
109 READ_INT32(fp, res);
110 }
111 /* return the proper status code */
112 return NSS_STATUS_SUCCESS;
113 }
114
115 #ifdef NSS_FLAVOUR_GLIBC
116
117 /* get a group entry by name */
NSS_NAME(getgrnam_r)118 nss_status_t NSS_NAME(getgrnam_r)(const char *name, struct group *result,
119 char *buffer, size_t buflen, int *errnop)
120 {
121 NSS_GETONE(NSLCD_ACTION_GROUP_BYNAME,
122 WRITE_STRING(fp, name),
123 read_group(fp, result, buffer, buflen, errnop));
124 }
125
126 /* get a group entry by numeric gid */
NSS_NAME(getgrgid_r)127 nss_status_t NSS_NAME(getgrgid_r)(gid_t gid, struct group *result,
128 char *buffer, size_t buflen, int *errnop)
129 {
130 NSS_GETONE(NSLCD_ACTION_GROUP_BYGID,
131 WRITE_INT32(fp, gid),
132 read_group(fp, result, buffer, buflen, errnop));
133 }
134
135 /* thread-local file pointer to an ongoing request */
136 static TLS TFILE *grentfp;
137
138 /* start a request to read all groups */
NSS_NAME(setgrent)139 nss_status_t NSS_NAME(setgrent)(int UNUSED(stayopen))
140 {
141 NSS_SETENT(grentfp);
142 }
143
144 /* read a single group from the stream */
NSS_NAME(getgrent_r)145 nss_status_t NSS_NAME(getgrent_r)(struct group *result,
146 char *buffer, size_t buflen, int *errnop)
147 {
148 NSS_GETENT(grentfp, NSLCD_ACTION_GROUP_ALL,
149 read_group(grentfp, result, buffer, buflen, errnop));
150 }
151
152 /* close the stream opened with setgrent() above */
NSS_NAME(endgrent)153 nss_status_t NSS_NAME(endgrent)(void)
154 {
155 NSS_ENDENT(grentfp);
156 }
157
158 /* this function returns a list of groups, documentation for the
159 interface is scarce (any pointers are welcome) but this is
160 what is assumed the parameters mean:
161
162 user IN - the user name to find groups for
163 skipgroup IN - a group to not include in the list
164 *start IN/OUT - where to write in the array, is incremented
165 *size IN/OUT - the size of the supplied array (gid_t entries, not bytes)
166 **groupsp IN/OUT - pointer to the array of returned groupids
167 limit IN - the maxium size of the array
168 *errnop OUT - for returning errno
169 */
NSS_NAME(initgroups_dyn)170 nss_status_t NSS_NAME(initgroups_dyn)(const char *user, gid_t skipgroup,
171 long int *start, long int *size,
172 gid_t **groupsp, long int limit,
173 int *errnop)
174 {
175 /* temporarily map the buffer and buflen names so the check in NSS_GETONE
176 for validity of the buffer works (renaming the parameters may cause
177 confusion) */
178 #define buffer groupsp
179 #define buflen *size
180 NSS_GETONE(NSLCD_ACTION_GROUP_BYMEMBER,
181 WRITE_STRING(fp, user),
182 read_gids(fp, skipgroup, start, size, groupsp, limit, errnop));
183 #undef buffer
184 #undef buflen
185 }
186
187 #endif /* NSS_FLAVOUR_GLIBC */
188
189 #ifdef NSS_FLAVOUR_SOLARIS
190
191 #ifdef HAVE_STRUCT_NSS_XBYY_ARGS_RETURNLEN
group2str(struct group * result,char * buffer,size_t buflen)192 static char *group2str(struct group *result, char *buffer, size_t buflen)
193 {
194 int res, i;
195 res = snprintf(buffer, buflen, "%s:%s:%d:", result->gr_name,
196 result->gr_passwd, (int)result->gr_gid);
197 if ((res < 0) || (res >= (int)buflen))
198 return NULL;
199 if (result->gr_mem)
200 for (i = 0; result->gr_mem[i]; i++)
201 {
202 if (i)
203 strlcat(buffer, ",", buflen);
204 strlcat(buffer, result->gr_mem[i], buflen);
205 }
206 /* check if buffer overflowed */
207 if (strlen(buffer) >= buflen - 1)
208 return NULL;
209 return buffer;
210 }
211 #endif /* HAVE_STRUCT_NSS_XBYY_ARGS_RETURNLEN */
212
read_result(TFILE * fp,nss_XbyY_args_t * args)213 static nss_status_t read_result(TFILE *fp, nss_XbyY_args_t *args)
214 {
215 READ_RESULT(group, &args->erange);
216 }
217
group_getgrnam(nss_backend_t UNUSED (* be),void * args)218 static nss_status_t group_getgrnam(nss_backend_t UNUSED(*be), void *args)
219 {
220 NSS_GETONE(NSLCD_ACTION_GROUP_BYNAME,
221 WRITE_STRING(fp, NSS_ARGS(args)->key.name),
222 read_result(fp, args));
223 }
224
group_getgrgid(nss_backend_t UNUSED (* be),void * args)225 static nss_status_t group_getgrgid(nss_backend_t UNUSED(*be), void *args)
226 {
227 NSS_GETONE(NSLCD_ACTION_GROUP_BYGID,
228 WRITE_INT32(fp, NSS_ARGS(args)->key.gid),
229 read_result(fp, args));
230 }
231
group_setgrent(nss_backend_t * be,void UNUSED (* args))232 static nss_status_t group_setgrent(nss_backend_t *be, void UNUSED(*args))
233 {
234 NSS_SETENT(LDAP_BE(be)->fp);
235 }
236
group_getgrent(nss_backend_t * be,void * args)237 static nss_status_t group_getgrent(nss_backend_t *be, void *args)
238 {
239 NSS_GETENT(LDAP_BE(be)->fp, NSLCD_ACTION_GROUP_ALL,
240 read_result(LDAP_BE(be)->fp, args));
241 }
242
group_endgrent(nss_backend_t * be,void UNUSED (* args))243 static nss_status_t group_endgrent(nss_backend_t *be, void UNUSED(*args))
244 {
245 NSS_ENDENT(LDAP_BE(be)->fp);
246 }
247
group_getgroupsbymember(nss_backend_t UNUSED (* be),void * args)248 static nss_status_t group_getgroupsbymember(nss_backend_t UNUSED(*be), void *args)
249 {
250 struct nss_groupsbymem *argp = (struct nss_groupsbymem *)args;
251 long int start = (long int)argp->numgids;
252 gid_t skipgroup = (start > 0) ? argp->gid_array[0] : (gid_t)-1;
253 NSS_GETONE(NSLCD_ACTION_GROUP_BYMEMBER,
254 WRITE_STRING(fp, argp->username),
255 read_gids(fp, skipgroup, &start, NULL, (gid_t **)&argp->gid_array,
256 argp->maxgids, &NSS_ARGS(args)->erange);
257 argp->numgids = (int)start);
258 }
259
260 static nss_backend_op_t group_ops[] = {
261 nss_ldap_destructor,
262 group_endgrent,
263 group_setgrent,
264 group_getgrent,
265 group_getgrnam,
266 group_getgrgid,
267 group_getgroupsbymember
268 };
269
NSS_NAME(group_constr)270 nss_backend_t *NSS_NAME(group_constr)(const char UNUSED(*db_name),
271 const char UNUSED(*src_name),
272 const char UNUSED(*cfg_args))
273 {
274 return nss_ldap_constructor(group_ops, sizeof(group_ops));
275 }
276
277 #endif /* NSS_FLAVOUR_SOLARIS */
278