source: branches/fc11-dev/server/common/oursrc/nss_nonlocal/nonlocal-group.c @ 1179

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