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