]> andersk Git - nss_nonlocal.git/blob - nonlocal-group.c
Initial release.
[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
44 static service_user *
45 nss_group_nonlocal_database(void)
46 {
47     static service_user *nip = NULL;
48     if (nip == NULL)
49         __nss_database_lookup("group_nonlocal", NULL, "", &nip);
50     
51     return nip;
52 }
53
54
55 static __thread int local_only = 0;
56
57 enum nss_status
58 local_getgrgid_r(gid_t gid, struct group *grp,
59                  char *buffer, size_t buflen, int *errnop)
60 {
61     int old_local_only = local_only;
62     int old_errno = errno;
63     int ret;
64     errno = *errnop;
65     local_only = 1;
66
67     ret = getgrgid_r(gid, grp, buffer, buflen, &grp);
68
69     local_only = old_local_only;
70     *errnop = errno;
71     errno = old_errno;
72
73     if (grp != NULL)
74         return NSS_STATUS_SUCCESS;
75     else if (ret == 0)
76         return NSS_STATUS_NOTFOUND;
77     else
78         return NSS_STATUS_TRYAGAIN;
79 }
80
81 enum nss_status
82 check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
83 {
84     struct group local_grp;
85     int local_errno = errno;
86     enum nss_status local_status, status = NSS_STATUS_SUCCESS;
87     int local_buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
88     char *local_buffer = malloc(local_buflen);
89     if (local_buffer == NULL) {
90         *errnop = ENOMEM;
91         errno = local_errno;
92         return NSS_STATUS_TRYAGAIN;
93     }
94     local_errno = 0;
95     local_status = local_getgrgid_r(gid, &local_grp, local_buffer,
96                                     local_buflen, &local_errno);
97     if (local_status == NSS_STATUS_SUCCESS) {
98         syslog(LOG_WARNING, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", local_grp.gr_gid, local_grp.gr_name, user);
99         status = NSS_STATUS_NOTFOUND;
100     } else if (local_status != NSS_STATUS_NOTFOUND &&
101                local_status != NSS_STATUS_UNAVAIL) {
102         *errnop = local_errno;
103         status = local_status;
104     }
105     free(local_buffer);
106     return status;
107 }
108
109
110 static service_user *grent_nip = NULL;
111 static void *grent_fct_start;
112 static union {
113     enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
114                          int *errnop);
115     void *ptr;
116 } grent_fct;
117 static const char *grent_fct_name = "getgrent_r";
118
119 enum nss_status
120 _nss_nonlocal_setgrent(int stayopen)
121 {
122     static const char *fct_name = "setgrent";
123     static void *fct_start = NULL;
124     enum nss_status status;
125     service_user *nip;
126     union {
127         enum nss_status (*l)(int stayopen);
128         void *ptr;
129     } fct;
130
131     nip = nss_group_nonlocal_database();
132     if (nip == NULL)
133         return NSS_STATUS_UNAVAIL;
134     if (fct_start == NULL)
135         fct_start = __nss_lookup_function(nip, fct_name);
136     fct.ptr = fct_start;
137     do {
138         if (fct.ptr == NULL)
139             status = NSS_STATUS_UNAVAIL;
140         else
141             status = DL_CALL_FCT(fct.l, (stayopen));
142     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
143     if (status != NSS_STATUS_SUCCESS)
144         return status;
145
146     grent_nip = nip;
147     if (grent_fct_start == NULL)
148         grent_fct_start = __nss_lookup_function(nip, grent_fct_name);
149     grent_fct.ptr = grent_fct_start;
150     return NSS_STATUS_SUCCESS;
151 }
152
153 enum nss_status
154 _nss_nonlocal_endgrent(void)
155 {
156     static const char *fct_name = "endgrent";
157     static void *fct_start = NULL;
158     enum nss_status status;
159     service_user *nip;
160     union {
161         enum nss_status (*l)(void);
162         void *ptr;
163     } fct;
164
165     grent_nip = NULL;
166
167     nip = nss_group_nonlocal_database();
168     if (nip == NULL)
169         return NSS_STATUS_UNAVAIL;
170     if (fct_start == NULL)
171         fct_start = __nss_lookup_function(nip, fct_name);
172     fct.ptr = fct_start;
173     do {
174         if (fct.ptr == NULL)
175             status = NSS_STATUS_UNAVAIL;
176         else
177             status = DL_CALL_FCT(fct.l, ());
178     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
179     return status;
180 }
181
182 enum nss_status
183 _nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
184                          int *errnop)
185 {
186     enum nss_status status;
187     if (grent_nip == NULL) {
188         status = _nss_nonlocal_setgrent(0);
189         if (status != NSS_STATUS_SUCCESS)
190             return status;
191     }
192     do {
193         if (grent_fct.ptr == NULL)
194             status = NSS_STATUS_UNAVAIL;
195         else {
196             int nonlocal_errno;
197             do
198                 status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
199             while (status == NSS_STATUS_SUCCESS &&
200                    check_nonlocal_gid("(unknown)", grp->gr_gid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
201         }
202         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
203             return status;
204
205         if (status == NSS_STATUS_SUCCESS)
206             return NSS_STATUS_SUCCESS;
207     } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
208
209     grent_nip = NULL;
210     return NSS_STATUS_NOTFOUND;
211 }
212
213
214 enum nss_status
215 _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
216                          char *buffer, size_t buflen, int *errnop)
217 {
218     static const char *fct_name = "getgrnam_r";
219     static void *fct_start = NULL;
220     enum nss_status status;
221     service_user *nip;
222     union {
223         enum nss_status (*l)(const char *name, struct group *grp,
224                              char *buffer, size_t buflen, int *errnop);
225         void *ptr;
226     } fct;
227
228     nip = nss_group_nonlocal_database();
229     if (nip == NULL)
230         return NSS_STATUS_UNAVAIL;
231     if (fct_start == NULL)
232         fct_start = __nss_lookup_function(nip, fct_name);
233     fct.ptr = fct_start;
234     do {
235         if (fct.ptr == NULL)
236             status = NSS_STATUS_UNAVAIL;
237         else
238             status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
239         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
240             break;
241     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
242     if (status != NSS_STATUS_SUCCESS)
243         return status;
244
245     return check_nonlocal_gid(name, grp->gr_gid, errnop);
246 }
247
248 enum nss_status
249 _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
250                          char *buffer, size_t buflen, int *errnop)
251 {
252     static const char *fct_name = "getgrgid_r";
253     static void *fct_start = NULL;
254     enum nss_status status;
255     service_user *nip;
256     union {
257         enum nss_status (*l)(gid_t gid, struct group *grp,
258                              char *buffer, size_t buflen, int *errnop);
259         void *ptr;
260     } fct;
261
262     if (local_only == 1)
263         return NSS_STATUS_UNAVAIL;
264
265     nip = nss_group_nonlocal_database();
266     if (nip == NULL)
267         return NSS_STATUS_UNAVAIL;
268     if (fct_start == NULL)
269         fct_start = __nss_lookup_function(nip, fct_name);
270     fct.ptr = fct_start;
271     do {
272         if (fct.ptr == NULL)
273             status = NSS_STATUS_UNAVAIL;
274         else
275             status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
276         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
277             break;
278     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
279     if (status != NSS_STATUS_SUCCESS)
280         return status;
281
282     return check_nonlocal_gid(grp->gr_name, grp->gr_gid, errnop);
283 }
284
285 enum nss_status
286 _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
287                              long int *size, gid_t **groupsp, long int limit,
288                              int *errnop)
289 {
290     static const char *fct_name = "initgroups_dyn";
291     static void *fct_start = NULL;
292     enum nss_status status;
293     service_user *nip;
294     union {
295         enum nss_status (*l)(const char *user, gid_t group, long int *start,
296                              long int *size, gid_t **groupsp, long int limit,
297                              int *errnop);
298         void *ptr;
299     } fct;
300     int in = *start, out = *start, i;
301
302     nip = nss_group_nonlocal_database();
303     if (nip == NULL)
304         return NSS_STATUS_UNAVAIL;
305     if (fct_start == NULL)
306         fct_start = __nss_lookup_function(nip, fct_name);
307     fct.ptr = fct_start;
308
309     do {
310         if (fct.ptr == NULL)
311             status = NSS_STATUS_UNAVAIL;
312         else
313             status = DL_CALL_FCT(fct.l, (user, group, start, size, groupsp, limit, errnop));
314         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
315             break;
316     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
317     if (status != NSS_STATUS_SUCCESS)
318         return status;
319
320     for (; in < *start; ++in) {
321         int nonlocal_errno = *errnop;
322
323         for (i = 0; i < out; ++i)
324             if ((*groupsp)[i] == (*groupsp)[in])
325                 break;
326         if (i < out)
327             continue;
328
329         status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
330         if (status == NSS_STATUS_SUCCESS) {
331             (*groupsp)[out++] = (*groupsp)[in];
332         } else if (status != NSS_STATUS_NOTFOUND) {
333             *start = out;
334             *errnop = nonlocal_errno;
335             return status;
336         }
337     }
338
339     *start = out;
340     return NSS_STATUS_SUCCESS;
341 }
This page took 0.054218 seconds and 5 git commands to generate.