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