]> andersk Git - nss_nonlocal.git/blob - nonlocal-group.c
Use a magic buflen instead of thread-local variables, to avoid strange
[nss_nonlocal.git] / nonlocal-group.c
1 /*
2  * nonlocal-group.c
3  * group database for nss_nonlocal proxy
4  *
5  * Copyright © 2007 Anders Kaseorg <andersk@mit.edu>
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation
9  * files (the "Software"), to deal in the Software without
10  * restriction, including without limitation the rights to use, copy,
11  * modify, merge, publish, distribute, sublicense, and/or sell copies
12  * of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27
28 #define _GNU_SOURCE
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <dlfcn.h>
35 #include <stdio.h>
36 #include <syslog.h>
37 #include <errno.h>
38 #include <grp.h>
39 #include <nss.h>
40 #include "nsswitch-internal.h"
41 #include "nonlocal.h"
42
43 #define MAGIC_LOCAL_GR_BUFLEN (sysconf(_SC_GETGR_R_SIZE_MAX) + 7)
44
45
46 static service_user *
47 nss_group_nonlocal_database(void)
48 {
49     static service_user *nip = NULL;
50     if (nip == NULL)
51         __nss_database_lookup("group_nonlocal", NULL, "", &nip);
52     
53     return nip;
54 }
55
56
57 enum nss_status
58 check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
59 {
60     enum nss_status status = NSS_STATUS_SUCCESS;
61     struct group gbuf;
62     struct group *gbufp = &gbuf;
63     int ret;
64     int old_errno = errno;
65     int buflen = MAGIC_LOCAL_GR_BUFLEN;
66     char *buf = malloc(buflen);
67     if (buf == NULL) {
68         *errnop = ENOMEM;
69         errno = old_errno;
70         return NSS_STATUS_TRYAGAIN;
71     }
72     errno = 0;
73     ret = getgrgid_r(gid, gbufp, buf, buflen, &gbufp);
74     if (ret != 0) {
75         *errnop = old_errno;
76         status = NSS_STATUS_TRYAGAIN;
77     } else if (gbufp != NULL) {
78         syslog(LOG_WARNING, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
79         status = NSS_STATUS_NOTFOUND;
80     }
81     free(buf);
82     errno = old_errno;
83     return status;
84 }
85
86
87 static service_user *grent_nip = NULL;
88 static void *grent_fct_start;
89 static union {
90     enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
91                          int *errnop);
92     void *ptr;
93 } grent_fct;
94 static const char *grent_fct_name = "getgrent_r";
95
96 enum nss_status
97 _nss_nonlocal_setgrent(int stayopen)
98 {
99     static const char *fct_name = "setgrent";
100     static void *fct_start = NULL;
101     enum nss_status status;
102     service_user *nip;
103     union {
104         enum nss_status (*l)(int stayopen);
105         void *ptr;
106     } fct;
107
108     nip = nss_group_nonlocal_database();
109     if (nip == NULL)
110         return NSS_STATUS_UNAVAIL;
111     if (fct_start == NULL)
112         fct_start = __nss_lookup_function(nip, fct_name);
113     fct.ptr = fct_start;
114     do {
115         if (fct.ptr == NULL)
116             status = NSS_STATUS_UNAVAIL;
117         else
118             status = DL_CALL_FCT(fct.l, (stayopen));
119     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
120     if (status != NSS_STATUS_SUCCESS)
121         return status;
122
123     grent_nip = nip;
124     if (grent_fct_start == NULL)
125         grent_fct_start = __nss_lookup_function(nip, grent_fct_name);
126     grent_fct.ptr = grent_fct_start;
127     return NSS_STATUS_SUCCESS;
128 }
129
130 enum nss_status
131 _nss_nonlocal_endgrent(void)
132 {
133     static const char *fct_name = "endgrent";
134     static void *fct_start = NULL;
135     enum nss_status status;
136     service_user *nip;
137     union {
138         enum nss_status (*l)(void);
139         void *ptr;
140     } fct;
141
142     grent_nip = NULL;
143
144     nip = nss_group_nonlocal_database();
145     if (nip == NULL)
146         return NSS_STATUS_UNAVAIL;
147     if (fct_start == NULL)
148         fct_start = __nss_lookup_function(nip, fct_name);
149     fct.ptr = fct_start;
150     do {
151         if (fct.ptr == NULL)
152             status = NSS_STATUS_UNAVAIL;
153         else
154             status = DL_CALL_FCT(fct.l, ());
155     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
156     return status;
157 }
158
159 enum nss_status
160 _nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
161                          int *errnop)
162 {
163     enum nss_status status;
164     if (grent_nip == NULL) {
165         status = _nss_nonlocal_setgrent(0);
166         if (status != NSS_STATUS_SUCCESS)
167             return status;
168     }
169     do {
170         if (grent_fct.ptr == NULL)
171             status = NSS_STATUS_UNAVAIL;
172         else {
173             int nonlocal_errno;
174             do
175                 status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
176             while (status == NSS_STATUS_SUCCESS &&
177                    check_nonlocal_gid("(unknown)", grp->gr_gid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
178         }
179         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
180             return status;
181
182         if (status == NSS_STATUS_SUCCESS)
183             return NSS_STATUS_SUCCESS;
184     } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
185
186     grent_nip = NULL;
187     return NSS_STATUS_NOTFOUND;
188 }
189
190
191 enum nss_status
192 _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
193                          char *buffer, size_t buflen, int *errnop)
194 {
195     static const char *fct_name = "getgrnam_r";
196     static void *fct_start = NULL;
197     enum nss_status status;
198     service_user *nip;
199     union {
200         enum nss_status (*l)(const char *name, struct group *grp,
201                              char *buffer, size_t buflen, int *errnop);
202         void *ptr;
203     } fct;
204
205     nip = nss_group_nonlocal_database();
206     if (nip == NULL)
207         return NSS_STATUS_UNAVAIL;
208     if (fct_start == NULL)
209         fct_start = __nss_lookup_function(nip, fct_name);
210     fct.ptr = fct_start;
211     do {
212         if (fct.ptr == NULL)
213             status = NSS_STATUS_UNAVAIL;
214         else
215             status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
216         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
217             break;
218     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
219     if (status != NSS_STATUS_SUCCESS)
220         return status;
221
222     return check_nonlocal_gid(name, grp->gr_gid, errnop);
223 }
224
225 enum nss_status
226 _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
227                          char *buffer, size_t buflen, int *errnop)
228 {
229     static const char *fct_name = "getgrgid_r";
230     static void *fct_start = NULL;
231     enum nss_status status;
232     service_user *nip;
233     union {
234         enum nss_status (*l)(gid_t gid, struct group *grp,
235                              char *buffer, size_t buflen, int *errnop);
236         void *ptr;
237     } fct;
238
239     if (buflen == MAGIC_LOCAL_GR_BUFLEN)
240         return NSS_STATUS_UNAVAIL;
241
242     nip = nss_group_nonlocal_database();
243     if (nip == NULL)
244         return NSS_STATUS_UNAVAIL;
245     if (fct_start == NULL)
246         fct_start = __nss_lookup_function(nip, fct_name);
247     fct.ptr = fct_start;
248     do {
249         if (fct.ptr == NULL)
250             status = NSS_STATUS_UNAVAIL;
251         else
252             status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
253         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
254             break;
255     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
256     if (status != NSS_STATUS_SUCCESS)
257         return status;
258
259     return check_nonlocal_gid(grp->gr_name, grp->gr_gid, errnop);
260 }
261
262 enum nss_status
263 _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
264                              long int *size, gid_t **groupsp, long int limit,
265                              int *errnop)
266 {
267     static const char *fct_name = "initgroups_dyn";
268     static void *fct_start = NULL;
269     enum nss_status status;
270     service_user *nip;
271     union {
272         enum nss_status (*l)(const char *user, gid_t group, long int *start,
273                              long int *size, gid_t **groupsp, long int limit,
274                              int *errnop);
275         void *ptr;
276     } fct;
277     int in = *start, out = *start, i;
278
279     nip = nss_group_nonlocal_database();
280     if (nip == NULL)
281         return NSS_STATUS_UNAVAIL;
282     if (fct_start == NULL)
283         fct_start = __nss_lookup_function(nip, fct_name);
284     fct.ptr = fct_start;
285
286     do {
287         if (fct.ptr == NULL)
288             status = NSS_STATUS_UNAVAIL;
289         else
290             status = DL_CALL_FCT(fct.l, (user, group, start, size, groupsp, limit, errnop));
291         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
292             break;
293     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
294     if (status != NSS_STATUS_SUCCESS)
295         return status;
296
297     for (; in < *start; ++in) {
298         int nonlocal_errno = *errnop;
299
300         for (i = 0; i < out; ++i)
301             if ((*groupsp)[i] == (*groupsp)[in])
302                 break;
303         if (i < out)
304             continue;
305
306         status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
307         if (status == NSS_STATUS_SUCCESS) {
308             (*groupsp)[out++] = (*groupsp)[in];
309         } else if (status != NSS_STATUS_NOTFOUND) {
310             *start = out;
311             *errnop = nonlocal_errno;
312             return status;
313         }
314     }
315
316     *start = out;
317     return NSS_STATUS_SUCCESS;
318 }
This page took 0.235524 seconds and 5 git commands to generate.