]> andersk Git - nss_nonlocal.git/blob - nonlocal-group.c
If NSS_NONLOCAL_IGNORE is set, pretend that nonlocal information doesn't exist.
[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     enum nss_status status = NSS_STATUS_SUCCESS;
64     struct group gbuf;
65     struct group *gbufp = &gbuf;
66     int ret;
67     int old_errno = errno;
68     int buflen = MAGIC_LOCAL_GR_BUFLEN;
69     char *buf = malloc(buflen);
70     if (buf == NULL) {
71         *errnop = ENOMEM;
72         errno = old_errno;
73         return NSS_STATUS_TRYAGAIN;
74     }
75     errno = 0;
76     ret = getgrgid_r(gid, gbufp, buf, buflen, &gbufp);
77     if (ret != 0) {
78         *errnop = old_errno;
79         status = NSS_STATUS_TRYAGAIN;
80     } else if (gbufp != NULL) {
81         syslog(LOG_WARNING, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
82         status = NSS_STATUS_NOTFOUND;
83     }
84     free(buf);
85     errno = old_errno;
86     return status;
87 }
88
89 enum nss_status
90 get_local_group(const char *name, struct group *grp, char *buffer, size_t buflen, int *errnop)
91 {
92     enum nss_status status = NSS_STATUS_NOTFOUND;
93     struct group gbuf;
94     struct group *gbufp = &gbuf;
95     int ret, n;
96     int old_errno = errno;
97     int len = MAGIC_LOCAL_GR_BUFLEN;
98     char *buf = malloc(len);
99     if (buf == NULL) {
100         *errnop = ENOMEM;
101         errno = old_errno;
102         return NSS_STATUS_TRYAGAIN;
103     }
104     errno = 0;
105     ret = getgrnam_r(name, gbufp, buf, len, &gbufp);
106     if (ret != 0) {
107         *errnop = old_errno;
108         status = NSS_STATUS_TRYAGAIN;
109     } else if (gbufp != NULL) {
110         status = NSS_STATUS_SUCCESS;
111
112         n = snprintf(buffer, buflen, "%s", gbufp->gr_name);
113         if (n < 0 || n >= buflen) {
114             *errnop = ERANGE;
115             status = NSS_STATUS_TRYAGAIN;
116             goto get_local_group_done;
117         }
118         grp->gr_name = buffer;
119         buffer += n;
120         buflen -= n;
121
122         n = snprintf(buffer, buflen, "%s", gbufp->gr_passwd);
123         if (n < 0 || n >= buflen) {
124             *errnop = ERANGE;
125             status = NSS_STATUS_TRYAGAIN;
126             goto get_local_group_done;
127         }
128         grp->gr_passwd = buffer;
129         buffer += n;
130         buflen -= n;
131
132         grp->gr_gid = gbufp->gr_gid;
133
134         if (buflen < sizeof(void *)) {
135             *errnop = ERANGE;
136             status = NSS_STATUS_TRYAGAIN;
137             goto get_local_group_done;
138         }
139         *(void **)buffer = NULL;
140         buffer += sizeof(void *);
141         buflen -= sizeof(void *);
142     }
143  get_local_group_done:
144     free(buf);
145     errno = old_errno;
146     return status;
147 }
148
149 static service_user *grent_nip = NULL;
150 static void *grent_fct_start;
151 static union {
152     enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
153                          int *errnop);
154     void *ptr;
155 } grent_fct;
156 static const char *grent_fct_name = "getgrent_r";
157
158 enum nss_status
159 _nss_nonlocal_setgrent(int stayopen)
160 {
161     static const char *fct_name = "setgrent";
162     static void *fct_start = NULL;
163     enum nss_status status;
164     service_user *nip;
165     union {
166         enum nss_status (*l)(int stayopen);
167         void *ptr;
168     } fct;
169
170     nip = nss_group_nonlocal_database();
171     if (nip == NULL)
172         return NSS_STATUS_UNAVAIL;
173     if (fct_start == NULL)
174         fct_start = __nss_lookup_function(nip, fct_name);
175     fct.ptr = fct_start;
176     do {
177         if (fct.ptr == NULL)
178             status = NSS_STATUS_UNAVAIL;
179         else
180             status = DL_CALL_FCT(fct.l, (stayopen));
181     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
182     if (status != NSS_STATUS_SUCCESS)
183         return status;
184
185     grent_nip = nip;
186     if (grent_fct_start == NULL)
187         grent_fct_start = __nss_lookup_function(nip, grent_fct_name);
188     grent_fct.ptr = grent_fct_start;
189     return NSS_STATUS_SUCCESS;
190 }
191
192 enum nss_status
193 _nss_nonlocal_endgrent(void)
194 {
195     static const char *fct_name = "endgrent";
196     static void *fct_start = NULL;
197     enum nss_status status;
198     service_user *nip;
199     union {
200         enum nss_status (*l)(void);
201         void *ptr;
202     } fct;
203
204     grent_nip = NULL;
205
206     nip = nss_group_nonlocal_database();
207     if (nip == NULL)
208         return NSS_STATUS_UNAVAIL;
209     if (fct_start == NULL)
210         fct_start = __nss_lookup_function(nip, fct_name);
211     fct.ptr = fct_start;
212     do {
213         if (fct.ptr == NULL)
214             status = NSS_STATUS_UNAVAIL;
215         else
216             status = DL_CALL_FCT(fct.l, ());
217     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
218     return status;
219 }
220
221 enum nss_status
222 _nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
223                          int *errnop)
224 {
225     enum nss_status status;
226
227     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
228     if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
229         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
230         return NSS_STATUS_UNAVAIL;
231
232     if (grent_nip == NULL) {
233         status = _nss_nonlocal_setgrent(0);
234         if (status != NSS_STATUS_SUCCESS)
235             return status;
236     }
237     do {
238         if (grent_fct.ptr == NULL)
239             status = NSS_STATUS_UNAVAIL;
240         else {
241             int nonlocal_errno;
242             do
243                 status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
244             while (status == NSS_STATUS_SUCCESS &&
245                    check_nonlocal_gid("(unknown)", grp->gr_gid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
246         }
247         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
248             return status;
249
250         if (status == NSS_STATUS_SUCCESS)
251             return NSS_STATUS_SUCCESS;
252     } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
253
254     grent_nip = NULL;
255     return NSS_STATUS_NOTFOUND;
256 }
257
258
259 enum nss_status
260 _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
261                          char *buffer, size_t buflen, int *errnop)
262 {
263     static const char *fct_name = "getgrnam_r";
264     static void *fct_start = NULL;
265     enum nss_status status;
266     service_user *nip;
267     union {
268         enum nss_status (*l)(const char *name, struct group *grp,
269                              char *buffer, size_t buflen, int *errnop);
270         void *ptr;
271     } fct;
272
273     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
274     if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
275         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
276         return NSS_STATUS_UNAVAIL;
277
278     nip = nss_group_nonlocal_database();
279     if (nip == NULL)
280         return NSS_STATUS_UNAVAIL;
281     if (fct_start == NULL)
282         fct_start = __nss_lookup_function(nip, fct_name);
283     fct.ptr = fct_start;
284     do {
285         if (fct.ptr == NULL)
286             status = NSS_STATUS_UNAVAIL;
287         else
288             status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
289         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
290             break;
291     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
292     if (status != NSS_STATUS_SUCCESS)
293         return status;
294
295     return check_nonlocal_gid(name, grp->gr_gid, errnop);
296 }
297
298 enum nss_status
299 _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
300                          char *buffer, size_t buflen, int *errnop)
301 {
302     static const char *fct_name = "getgrgid_r";
303     static void *fct_start = NULL;
304     enum nss_status status;
305     service_user *nip;
306     union {
307         enum nss_status (*l)(gid_t gid, struct group *grp,
308                              char *buffer, size_t buflen, int *errnop);
309         void *ptr;
310     } fct;
311
312     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
313     if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
314         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
315         return NSS_STATUS_UNAVAIL;
316
317     nip = nss_group_nonlocal_database();
318     if (nip == NULL)
319         return NSS_STATUS_UNAVAIL;
320     if (fct_start == NULL)
321         fct_start = __nss_lookup_function(nip, fct_name);
322     fct.ptr = fct_start;
323     do {
324         if (fct.ptr == NULL)
325             status = NSS_STATUS_UNAVAIL;
326         else
327             status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
328         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
329             break;
330     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
331     if (status != NSS_STATUS_SUCCESS)
332         return status;
333
334     return check_nonlocal_gid(grp->gr_name, grp->gr_gid, errnop);
335 }
336
337 enum nss_status
338 _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
339                              long int *size, gid_t **groupsp, long int limit,
340                              int *errnop)
341 {
342     static const char *fct_name = "initgroups_dyn";
343     static void *fct_start = NULL;
344     enum nss_status status;
345     service_user *nip;
346     union {
347         enum nss_status (*l)(const char *user, gid_t group, long int *start,
348                              long int *size, gid_t **groupsp, long int limit,
349                              int *errnop);
350         void *ptr;
351     } fct;
352
353     struct group local_users_group, nonlocal_users_group;
354     gid_t local_users_gid, gid;
355     int is_local = 0;
356     int buflen;
357     char *buffer;
358
359     /* Check that the user is a nonlocal user before adding any groups. */
360     status = check_nonlocal_user(user, errnop);
361     if (status == NSS_STATUS_NOTFOUND)
362         is_local = 1;
363     else if (status != NSS_STATUS_SUCCESS)
364         return status;
365
366     int old_errno = errno;
367
368     buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
369     buffer = malloc(buflen);
370     if (buffer == NULL) {
371         *errnop = ENOMEM;
372         errno = old_errno;
373         return NSS_STATUS_TRYAGAIN;
374     }
375     status = get_local_group(MAGIC_LOCAL_GROUPNAME,
376                              &local_users_group, buffer, buflen, errnop);
377     if (status == NSS_STATUS_NOTFOUND) {
378         syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
379                MAGIC_LOCAL_GROUPNAME);
380         local_users_gid = -1;
381     } else if (status != NSS_STATUS_SUCCESS) {
382         return status;
383     } else
384         local_users_gid = local_users_group.gr_gid;
385     free(buffer);
386
387     if (is_local) {
388         gid = local_users_gid;
389     } else {
390         buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
391         buffer = malloc(buflen);
392         if (buffer == NULL) {
393             *errnop = ENOMEM;
394             errno = old_errno;
395             return NSS_STATUS_TRYAGAIN;
396         }
397         status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
398                                  &nonlocal_users_group, buffer, buflen, errnop);
399         if (status == NSS_STATUS_NOTFOUND) {
400             syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
401                    MAGIC_NONLOCAL_GROUPNAME);
402             gid = -1;
403         } else if (status != NSS_STATUS_SUCCESS) {
404             errno = old_errno;
405             return status;
406         } else
407             gid = nonlocal_users_group.gr_gid;
408         free(buffer);
409     }
410
411     if (gid != -1) {
412         int i;
413         for (i = 0; i < *start; ++i)
414             if ((*groupsp)[i] == gid)
415                 break;
416         if (i >= *start) {
417             if (*start + 1 > *size) {
418                 gid_t *newgroups;
419                 long int newsize = 2 * *size;
420                 if (limit > 0) {
421                     if (*size >= limit)
422                         return NSS_STATUS_SUCCESS;
423                     if (newsize > limit)
424                         newsize = limit;
425                 }
426                 newgroups = realloc(*groupsp, *size * sizeof((*groupsp)[0]));
427                 if (newgroups == NULL) {
428                     *errnop = ENOMEM;
429                     errno = old_errno;
430                     return NSS_STATUS_TRYAGAIN;
431                 }
432                 *groupsp = newgroups;
433                 *size = newsize;
434             }
435             (*groupsp)[(*start)++] = gid;
436         }
437     }
438
439     errno = old_errno;
440
441     if (is_local)
442         return NSS_STATUS_SUCCESS;
443
444     int in = *start, out = *start, i;
445
446     nip = nss_group_nonlocal_database();
447     if (nip == NULL)
448         return NSS_STATUS_UNAVAIL;
449     if (fct_start == NULL)
450         fct_start = __nss_lookup_function(nip, fct_name);
451     fct.ptr = fct_start;
452
453     do {
454         if (fct.ptr == NULL)
455             status = NSS_STATUS_UNAVAIL;
456         else
457             status = DL_CALL_FCT(fct.l, (user, group, start, size, groupsp, limit, errnop));
458         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
459             break;
460     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
461     if (status != NSS_STATUS_SUCCESS)
462         return status;
463
464     for (; in < *start; ++in) {
465         int nonlocal_errno = *errnop;
466
467         for (i = 0; i < out; ++i)
468             if ((*groupsp)[i] == (*groupsp)[in])
469                 break;
470         if (i < out)
471             continue;
472
473         /* Don't let users get into MAGIC_LOCAL_GROUPNAME from nonlocal reasons. */
474         if (local_users_gid == (*groupsp)[in]) {
475             syslog(LOG_WARNING, "nss_nonlocal: Nonlocal user %s removed from special local users group %s",
476                    user, MAGIC_LOCAL_GROUPNAME);
477             continue;
478         }
479
480         status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
481         if (status == NSS_STATUS_SUCCESS) {
482             (*groupsp)[out++] = (*groupsp)[in];
483         } else if (status != NSS_STATUS_NOTFOUND) {
484             *start = out;
485             *errnop = nonlocal_errno;
486             return status;
487         }
488     }
489
490     *start = out;
491     return NSS_STATUS_SUCCESS;
492 }
This page took 0.228033 seconds and 5 git commands to generate.