]> andersk Git - nss_nonlocal.git/blob - nonlocal-group.c
a09c0c3d2aaefc59c10c0ab2a97def134c3d59ba
[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> and Tim Abbott
6  * <tabbott@mit.edu>
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  */
28
29 #define _GNU_SOURCE
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <dlfcn.h>
36 #include <stdio.h>
37 #include <syslog.h>
38 #include <errno.h>
39 #include <grp.h>
40 #include <nss.h>
41 #include "nsswitch-internal.h"
42 #include "nonlocal.h"
43
44 #define MAGIC_LOCAL_GR_BUFLEN (sysconf(_SC_GETGR_R_SIZE_MAX) + 7)
45 #define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
46 #define MAGIC_LOCAL_GROUPNAME "nss-local-users"
47
48
49 static service_user *
50 nss_group_nonlocal_database(void)
51 {
52     static service_user *nip = NULL;
53     if (nip == NULL)
54         __nss_database_lookup("group_nonlocal", NULL, "", &nip);
55
56     return nip;
57 }
58
59
60 enum nss_status
61 check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
62 {
63     static const char *fct_name = "getgrgid_r";
64     static service_user *startp = NULL;
65     static void *fct_start = NULL;
66     enum nss_status status;
67     service_user *nip;
68     union {
69         enum nss_status (*l)(gid_t gid, struct group *grp,
70                              char *buffer, size_t buflen, int *errnop);
71         void *ptr;
72     } fct;
73     struct group gbuf;
74     int old_errno = errno;
75     int buflen = MAGIC_LOCAL_GR_BUFLEN;
76     char *buf = malloc(buflen);
77     if (buf == NULL) {
78         *errnop = ENOMEM;
79         errno = old_errno;
80         return NSS_STATUS_TRYAGAIN;
81     }
82
83     if (fct_start == NULL &&
84         __nss_group_lookup(&startp, fct_name, &fct_start) != 0) {
85         free(buf);
86         return NSS_STATUS_UNAVAIL;
87     }
88     nip = startp;
89     fct.ptr = fct_start;
90     do {
91         status = DL_CALL_FCT(fct.l, (gid, &gbuf, buf, buflen, errnop));
92         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
93             break;
94     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
95
96     if (status == NSS_STATUS_SUCCESS) {
97         syslog(LOG_WARNING, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
98         status = NSS_STATUS_NOTFOUND;
99     } else if (status != NSS_STATUS_TRYAGAIN) {
100         status = NSS_STATUS_SUCCESS;
101     }
102
103     free(buf);
104     return status;
105 }
106
107 enum nss_status
108 get_local_group(const char *name, struct group *grp, char *buffer, size_t buflen, int *errnop)
109 {
110     static const char *fct_name = "getgrnam_r";
111     static service_user *startp = NULL;
112     static void *fct_start = NULL;
113     enum nss_status status;
114     service_user *nip;
115     union {
116         enum nss_status (*l)(const char *name, struct group *grp,
117                              char *buffer, size_t buflen, int *errnop);
118         void *ptr;
119     } fct;
120     struct group gbuf;
121     int n;
122     int old_errno = errno;
123     int len = MAGIC_LOCAL_GR_BUFLEN;
124     char *buf = malloc(len);
125     if (buf == NULL) {
126         *errnop = ENOMEM;
127         errno = old_errno;
128         return NSS_STATUS_TRYAGAIN;
129     }
130
131     if (fct_start == NULL &&
132         __nss_group_lookup(&startp, fct_name, &fct_start) != 0) {
133         free(buf);
134         return NSS_STATUS_UNAVAIL;
135     }
136     nip = startp;
137     fct.ptr = fct_start;
138     do {
139         status = DL_CALL_FCT(fct.l, (name, &gbuf, buf, buflen, errnop));
140         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
141             break;
142     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
143
144     if (status == NSS_STATUS_SUCCESS) {
145         n = snprintf(buffer, buflen, "%s", gbuf.gr_name);
146         if (n < 0 || n >= buflen) {
147             *errnop = ERANGE;
148             status = NSS_STATUS_TRYAGAIN;
149             goto get_local_group_done;
150         }
151         grp->gr_name = buffer;
152         buffer += n;
153         buflen -= n;
154
155         n = snprintf(buffer, buflen, "%s", gbuf.gr_passwd);
156         if (n < 0 || n >= buflen) {
157             *errnop = ERANGE;
158             status = NSS_STATUS_TRYAGAIN;
159             goto get_local_group_done;
160         }
161         grp->gr_passwd = buffer;
162         buffer += n;
163         buflen -= n;
164
165         grp->gr_gid = gbuf.gr_gid;
166
167         if (buflen < sizeof(void *)) {
168             *errnop = ERANGE;
169             status = NSS_STATUS_TRYAGAIN;
170             goto get_local_group_done;
171         }
172         *(void **)buffer = NULL;
173         buffer += sizeof(void *);
174         buflen -= sizeof(void *);
175     }
176
177  get_local_group_done:
178     free(buf);
179     return status;
180 }
181
182 static service_user *grent_nip = NULL;
183 static void *grent_fct_start;
184 static union {
185     enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
186                          int *errnop);
187     void *ptr;
188 } grent_fct;
189 static const char *grent_fct_name = "getgrent_r";
190
191 enum nss_status
192 _nss_nonlocal_setgrent(int stayopen)
193 {
194     static const char *fct_name = "setgrent";
195     static void *fct_start = NULL;
196     enum nss_status status;
197     service_user *nip;
198     union {
199         enum nss_status (*l)(int stayopen);
200         void *ptr;
201     } fct;
202
203     nip = nss_group_nonlocal_database();
204     if (nip == NULL)
205         return NSS_STATUS_UNAVAIL;
206     if (fct_start == NULL)
207         fct_start = __nss_lookup_function(nip, fct_name);
208     fct.ptr = fct_start;
209     do {
210         if (fct.ptr == NULL)
211             status = NSS_STATUS_UNAVAIL;
212         else
213             status = DL_CALL_FCT(fct.l, (stayopen));
214     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
215     if (status != NSS_STATUS_SUCCESS)
216         return status;
217
218     grent_nip = nip;
219     if (grent_fct_start == NULL)
220         grent_fct_start = __nss_lookup_function(nip, grent_fct_name);
221     grent_fct.ptr = grent_fct_start;
222     return NSS_STATUS_SUCCESS;
223 }
224
225 enum nss_status
226 _nss_nonlocal_endgrent(void)
227 {
228     static const char *fct_name = "endgrent";
229     static void *fct_start = NULL;
230     enum nss_status status;
231     service_user *nip;
232     union {
233         enum nss_status (*l)(void);
234         void *ptr;
235     } fct;
236
237     grent_nip = NULL;
238
239     nip = nss_group_nonlocal_database();
240     if (nip == NULL)
241         return NSS_STATUS_UNAVAIL;
242     if (fct_start == NULL)
243         fct_start = __nss_lookup_function(nip, fct_name);
244     fct.ptr = fct_start;
245     do {
246         if (fct.ptr == NULL)
247             status = NSS_STATUS_UNAVAIL;
248         else
249             status = DL_CALL_FCT(fct.l, ());
250     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
251     return status;
252 }
253
254 enum nss_status
255 _nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
256                          int *errnop)
257 {
258     enum nss_status status;
259
260     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
261     if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
262         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
263         return NSS_STATUS_UNAVAIL;
264
265     if (grent_nip == NULL) {
266         status = _nss_nonlocal_setgrent(0);
267         if (status != NSS_STATUS_SUCCESS)
268             return status;
269     }
270     do {
271         if (grent_fct.ptr == NULL)
272             status = NSS_STATUS_UNAVAIL;
273         else {
274             int nonlocal_errno;
275             do
276                 status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
277             while (status == NSS_STATUS_SUCCESS &&
278                    check_nonlocal_gid("(unknown)", grp->gr_gid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
279         }
280         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
281             return status;
282
283         if (status == NSS_STATUS_SUCCESS)
284             return NSS_STATUS_SUCCESS;
285     } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
286
287     grent_nip = NULL;
288     return NSS_STATUS_NOTFOUND;
289 }
290
291
292 enum nss_status
293 _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
294                          char *buffer, size_t buflen, int *errnop)
295 {
296     static const char *fct_name = "getgrnam_r";
297     static void *fct_start = NULL;
298     enum nss_status status;
299     service_user *nip;
300     union {
301         enum nss_status (*l)(const char *name, struct group *grp,
302                              char *buffer, size_t buflen, int *errnop);
303         void *ptr;
304     } fct;
305
306     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
307     if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
308         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
309         return NSS_STATUS_UNAVAIL;
310
311     nip = nss_group_nonlocal_database();
312     if (nip == NULL)
313         return NSS_STATUS_UNAVAIL;
314     if (fct_start == NULL)
315         fct_start = __nss_lookup_function(nip, fct_name);
316     fct.ptr = fct_start;
317     do {
318         if (fct.ptr == NULL)
319             status = NSS_STATUS_UNAVAIL;
320         else
321             status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
322         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
323             break;
324     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
325     if (status != NSS_STATUS_SUCCESS)
326         return status;
327
328     return check_nonlocal_gid(name, grp->gr_gid, errnop);
329 }
330
331 enum nss_status
332 _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
333                          char *buffer, size_t buflen, int *errnop)
334 {
335     static const char *fct_name = "getgrgid_r";
336     static void *fct_start = NULL;
337     enum nss_status status;
338     service_user *nip;
339     union {
340         enum nss_status (*l)(gid_t gid, struct group *grp,
341                              char *buffer, size_t buflen, int *errnop);
342         void *ptr;
343     } fct;
344
345     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
346     if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
347         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
348         return NSS_STATUS_UNAVAIL;
349
350     nip = nss_group_nonlocal_database();
351     if (nip == NULL)
352         return NSS_STATUS_UNAVAIL;
353     if (fct_start == NULL)
354         fct_start = __nss_lookup_function(nip, fct_name);
355     fct.ptr = fct_start;
356     do {
357         if (fct.ptr == NULL)
358             status = NSS_STATUS_UNAVAIL;
359         else
360             status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
361         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
362             break;
363     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
364     if (status != NSS_STATUS_SUCCESS)
365         return status;
366
367     return check_nonlocal_gid(grp->gr_name, grp->gr_gid, errnop);
368 }
369
370 enum nss_status
371 _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
372                              long int *size, gid_t **groupsp, long int limit,
373                              int *errnop)
374 {
375     static const char *fct_name = "initgroups_dyn";
376     static void *fct_start = NULL;
377     enum nss_status status;
378     service_user *nip;
379     union {
380         enum nss_status (*l)(const char *user, gid_t group, long int *start,
381                              long int *size, gid_t **groupsp, long int limit,
382                              int *errnop);
383         void *ptr;
384     } fct;
385
386     struct group local_users_group, nonlocal_users_group;
387     gid_t local_users_gid, gid;
388     int is_local = 0;
389     int buflen;
390     char *buffer;
391
392     /* Check that the user is a nonlocal user before adding any groups. */
393     status = check_nonlocal_user(user, errnop);
394     if (status == NSS_STATUS_TRYAGAIN)
395         return status;
396     else if (status != NSS_STATUS_SUCCESS)
397         is_local = 1;
398
399     int old_errno = errno;
400
401     buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
402     buffer = malloc(buflen);
403     if (buffer == NULL) {
404         *errnop = ENOMEM;
405         errno = old_errno;
406         return NSS_STATUS_TRYAGAIN;
407     }
408     status = get_local_group(MAGIC_LOCAL_GROUPNAME,
409                              &local_users_group, buffer, buflen, errnop);
410     if (status == NSS_STATUS_SUCCESS) {
411         local_users_gid = local_users_group.gr_gid;
412     } else if (status == NSS_STATUS_TRYAGAIN) {
413         free(buffer);
414         return status;
415     } else {
416         syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
417                MAGIC_LOCAL_GROUPNAME);
418         local_users_gid = -1;
419     }
420     free(buffer);
421
422     if (is_local) {
423         gid = local_users_gid;
424     } else {
425         buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
426         buffer = malloc(buflen);
427         if (buffer == NULL) {
428             *errnop = ENOMEM;
429             errno = old_errno;
430             return NSS_STATUS_TRYAGAIN;
431         }
432         status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
433                                  &nonlocal_users_group, buffer, buflen, errnop);
434         if (status == NSS_STATUS_SUCCESS) {
435             gid = nonlocal_users_group.gr_gid;
436         } else if (status == NSS_STATUS_TRYAGAIN) {
437             free(buffer);
438             return status;
439         } else {
440             syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
441                    MAGIC_NONLOCAL_GROUPNAME);
442             gid = -1;
443         }
444         free(buffer);
445     }
446
447     if (gid != -1) {
448         int i;
449         for (i = 0; i < *start; ++i)
450             if ((*groupsp)[i] == gid)
451                 break;
452         if (i >= *start) {
453             if (*start + 1 > *size) {
454                 gid_t *newgroups;
455                 long int newsize = 2 * *size;
456                 if (limit > 0) {
457                     if (*size >= limit)
458                         return NSS_STATUS_SUCCESS;
459                     if (newsize > limit)
460                         newsize = limit;
461                 }
462                 newgroups = realloc(*groupsp, *size * sizeof((*groupsp)[0]));
463                 if (newgroups == NULL) {
464                     *errnop = ENOMEM;
465                     errno = old_errno;
466                     return NSS_STATUS_TRYAGAIN;
467                 }
468                 *groupsp = newgroups;
469                 *size = newsize;
470             }
471             (*groupsp)[(*start)++] = gid;
472         }
473     }
474
475     if (is_local)
476         return NSS_STATUS_SUCCESS;
477
478     int in = *start, out = *start, i;
479
480     nip = nss_group_nonlocal_database();
481     if (nip == NULL)
482         return NSS_STATUS_UNAVAIL;
483     if (fct_start == NULL)
484         fct_start = __nss_lookup_function(nip, fct_name);
485     fct.ptr = fct_start;
486
487     do {
488         if (fct.ptr == NULL)
489             status = NSS_STATUS_UNAVAIL;
490         else
491             status = DL_CALL_FCT(fct.l, (user, group, start, size, groupsp, limit, errnop));
492         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
493             break;
494     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
495     if (status != NSS_STATUS_SUCCESS)
496         return status;
497
498     for (; in < *start; ++in) {
499         int nonlocal_errno = *errnop;
500
501         for (i = 0; i < out; ++i)
502             if ((*groupsp)[i] == (*groupsp)[in])
503                 break;
504         if (i < out)
505             continue;
506
507         /* Don't let users get into MAGIC_LOCAL_GROUPNAME from nonlocal reasons. */
508         if (local_users_gid == (*groupsp)[in]) {
509             syslog(LOG_WARNING, "nss_nonlocal: Nonlocal user %s removed from special local users group %s",
510                    user, MAGIC_LOCAL_GROUPNAME);
511             continue;
512         }
513
514         status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
515         if (status == NSS_STATUS_SUCCESS) {
516             (*groupsp)[out++] = (*groupsp)[in];
517         } else if (status == NSS_STATUS_TRYAGAIN) {
518             *start = out;
519             *errnop = nonlocal_errno;
520             return status;
521         }
522     }
523
524     *start = out;
525     return NSS_STATUS_SUCCESS;
526 }
This page took 0.085145 seconds and 3 git commands to generate.