]> andersk Git - nss_nonlocal.git/blob - nonlocal-group.c
Bug fixes and cleanups.
[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     if (grent_nip == NULL) {
227         status = _nss_nonlocal_setgrent(0);
228         if (status != NSS_STATUS_SUCCESS)
229             return status;
230     }
231     do {
232         if (grent_fct.ptr == NULL)
233             status = NSS_STATUS_UNAVAIL;
234         else {
235             int nonlocal_errno;
236             do
237                 status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
238             while (status == NSS_STATUS_SUCCESS &&
239                    check_nonlocal_gid("(unknown)", grp->gr_gid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
240         }
241         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
242             return status;
243
244         if (status == NSS_STATUS_SUCCESS)
245             return NSS_STATUS_SUCCESS;
246     } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
247
248     grent_nip = NULL;
249     return NSS_STATUS_NOTFOUND;
250 }
251
252
253 enum nss_status
254 _nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
255                          char *buffer, size_t buflen, int *errnop)
256 {
257     static const char *fct_name = "getgrnam_r";
258     static void *fct_start = NULL;
259     enum nss_status status;
260     service_user *nip;
261     union {
262         enum nss_status (*l)(const char *name, struct group *grp,
263                              char *buffer, size_t buflen, int *errnop);
264         void *ptr;
265     } fct;
266
267     if (buflen == MAGIC_LOCAL_GR_BUFLEN)
268         return NSS_STATUS_UNAVAIL;
269
270     nip = nss_group_nonlocal_database();
271     if (nip == NULL)
272         return NSS_STATUS_UNAVAIL;
273     if (fct_start == NULL)
274         fct_start = __nss_lookup_function(nip, fct_name);
275     fct.ptr = fct_start;
276     do {
277         if (fct.ptr == NULL)
278             status = NSS_STATUS_UNAVAIL;
279         else
280             status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
281         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
282             break;
283     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
284     if (status != NSS_STATUS_SUCCESS)
285         return status;
286
287     return check_nonlocal_gid(name, grp->gr_gid, errnop);
288 }
289
290 enum nss_status
291 _nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
292                          char *buffer, size_t buflen, int *errnop)
293 {
294     static const char *fct_name = "getgrgid_r";
295     static void *fct_start = NULL;
296     enum nss_status status;
297     service_user *nip;
298     union {
299         enum nss_status (*l)(gid_t gid, struct group *grp,
300                              char *buffer, size_t buflen, int *errnop);
301         void *ptr;
302     } fct;
303
304     if (buflen == MAGIC_LOCAL_GR_BUFLEN)
305         return NSS_STATUS_UNAVAIL;
306
307     nip = nss_group_nonlocal_database();
308     if (nip == NULL)
309         return NSS_STATUS_UNAVAIL;
310     if (fct_start == NULL)
311         fct_start = __nss_lookup_function(nip, fct_name);
312     fct.ptr = fct_start;
313     do {
314         if (fct.ptr == NULL)
315             status = NSS_STATUS_UNAVAIL;
316         else
317             status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
318         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
319             break;
320     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
321     if (status != NSS_STATUS_SUCCESS)
322         return status;
323
324     return check_nonlocal_gid(grp->gr_name, grp->gr_gid, errnop);
325 }
326
327 enum nss_status
328 _nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
329                              long int *size, gid_t **groupsp, long int limit,
330                              int *errnop)
331 {
332     static const char *fct_name = "initgroups_dyn";
333     static void *fct_start = NULL;
334     enum nss_status status;
335     service_user *nip;
336     union {
337         enum nss_status (*l)(const char *user, gid_t group, long int *start,
338                              long int *size, gid_t **groupsp, long int limit,
339                              int *errnop);
340         void *ptr;
341     } fct;
342
343     struct group local_users_group, nonlocal_users_group;
344     gid_t local_users_gid, gid;
345     int is_local = 0;
346     int buflen;
347     char *buffer;
348
349     /* Check that the user is a nonlocal user before adding any groups. */
350     status = check_nonlocal_user(user, errnop);
351     if (status == NSS_STATUS_NOTFOUND)
352         is_local = 1;
353     else if (status != NSS_STATUS_SUCCESS)
354         return status;
355
356     int old_errno = errno;
357
358     buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
359     buffer = malloc(buflen);
360     if (buffer == NULL) {
361         *errnop = ENOMEM;
362         errno = old_errno;
363         return NSS_STATUS_TRYAGAIN;
364     }
365     status = get_local_group(MAGIC_LOCAL_GROUPNAME,
366                              &local_users_group, buffer, buflen, errnop);
367     if (status == NSS_STATUS_NOTFOUND) {
368         syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
369                MAGIC_LOCAL_GROUPNAME);
370         local_users_gid = -1;
371     } else if (status != NSS_STATUS_SUCCESS) {
372         return status;
373     } else
374         local_users_gid = local_users_group.gr_gid;
375     free(buffer);
376
377     if (is_local) {
378         gid = local_users_gid;
379     } else {
380         buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
381         buffer = malloc(buflen);
382         if (buffer == NULL) {
383             *errnop = ENOMEM;
384             errno = old_errno;
385             return NSS_STATUS_TRYAGAIN;
386         }
387         status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
388                                  &nonlocal_users_group, buffer, buflen, errnop);
389         if (status == NSS_STATUS_NOTFOUND) {
390             syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
391                    MAGIC_NONLOCAL_GROUPNAME);
392             gid = -1;
393         } else if (status != NSS_STATUS_SUCCESS) {
394             errno = old_errno;
395             return status;
396         } else
397             gid = nonlocal_users_group.gr_gid;
398         free(buffer);
399     }
400
401     if (gid != -1) {
402         int i;
403         for (i = 0; i < *start; ++i)
404             if ((*groupsp)[i] == gid)
405                 break;
406         if (i >= *start) {
407             if (*start + 1 > *size) {
408                 gid_t *newgroups;
409                 long int newsize = 2 * *size;
410                 if (limit > 0) {
411                     if (*size >= limit)
412                         return NSS_STATUS_SUCCESS;
413                     if (newsize > limit)
414                         newsize = limit;
415                 }
416                 newgroups = realloc(*groupsp, *size * sizeof((*groupsp)[0]));
417                 if (newgroups == NULL) {
418                     *errnop = ENOMEM;
419                     errno = old_errno;
420                     return NSS_STATUS_TRYAGAIN;
421                 }
422                 *groupsp = newgroups;
423                 *size = newsize;
424             }
425             (*groupsp)[(*start)++] = gid;
426         }
427     }
428
429     errno = old_errno;
430
431     if (is_local)
432         return NSS_STATUS_SUCCESS;
433
434     int in = *start, out = *start, i;
435
436     nip = nss_group_nonlocal_database();
437     if (nip == NULL)
438         return NSS_STATUS_UNAVAIL;
439     if (fct_start == NULL)
440         fct_start = __nss_lookup_function(nip, fct_name);
441     fct.ptr = fct_start;
442
443     do {
444         if (fct.ptr == NULL)
445             status = NSS_STATUS_UNAVAIL;
446         else
447             status = DL_CALL_FCT(fct.l, (user, group, start, size, groupsp, limit, errnop));
448         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
449             break;
450     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
451     if (status != NSS_STATUS_SUCCESS)
452         return status;
453
454     for (; in < *start; ++in) {
455         int nonlocal_errno = *errnop;
456
457         for (i = 0; i < out; ++i)
458             if ((*groupsp)[i] == (*groupsp)[in])
459                 break;
460         if (i < out)
461             continue;
462
463         /* Don't let users get into MAGIC_LOCAL_GROUPNAME from nonlocal reasons. */
464         if (local_users_gid == (*groupsp)[in]) {
465             syslog(LOG_WARNING, "nss_nonlocal: Nonlocal user %s removed from special local users group %s",
466                    user, MAGIC_LOCAL_GROUPNAME);
467             continue;
468         }
469
470         status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
471         if (status == NSS_STATUS_SUCCESS) {
472             (*groupsp)[out++] = (*groupsp)[in];
473         } else if (status != NSS_STATUS_NOTFOUND) {
474             *start = out;
475             *errnop = nonlocal_errno;
476             return status;
477         }
478     }
479
480     *start = out;
481     return NSS_STATUS_SUCCESS;
482 }
This page took 0.370911 seconds and 5 git commands to generate.