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