source: trunk/debathena/debathena/libnss-nonlocal/nonlocal-group.c @ 24585

Revision 24585, 14.2 KB checked in by andersk, 15 years ago (diff)
Line 
1/*
2 * nonlocal-group.c
3 * group database for nss_nonlocal proxy
4 *
5 * Copyright © 2007–2010 Anders Kaseorg <andersk@mit.edu> and Tim
6 * Abbott <tabbott@mit.edu>
7 *
8 * This file is part of nss_nonlocal.
9 *
10 * nss_nonlocal is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1 of
13 * the License, or (at your option) any later version.
14 *
15 * nss_nonlocal is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with nss_nonlocal; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * 02110-1301  USA
24 */
25
26#define _GNU_SOURCE
27#include <sys/types.h>
28#include <unistd.h>
29#include <stdlib.h>
30#include <stdint.h>
31#include <string.h>
32#include <dlfcn.h>
33#include <stdio.h>
34#include <syslog.h>
35#include <errno.h>
36#include <grp.h>
37#include <nss.h>
38#include "nsswitch-internal.h"
39#include "nonlocal.h"
40
41#define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
42#define MAGIC_LOCAL_GROUPNAME "nss-local-users"
43
44
45enum nss_status
46_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
47                         char *buffer, size_t buflen, int *errnop);
48
49enum nss_status
50_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
51                         char *buffer, size_t buflen, int *errnop);
52
53
54static service_user *
55nss_group_nonlocal_database(void)
56{
57    static service_user *nip = NULL;
58    if (nip == NULL)
59        __nss_database_lookup("group_nonlocal", NULL, "", &nip);
60
61    return nip;
62}
63
64
65enum nss_status
66check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
67{
68    static const char *fct_name = "getgrgid_r";
69    static service_user *startp = NULL;
70    static void *fct_start = NULL;
71    enum nss_status status;
72    service_user *nip;
73    union {
74        enum nss_status (*l)(gid_t gid, struct group *grp,
75                             char *buffer, size_t buflen, int *errnop);
76        void *ptr;
77    } fct;
78    struct group gbuf;
79    int old_errno = errno;
80
81    size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
82    char *buf = malloc(buflen);
83    if (buf == NULL) {
84        *errnop = ENOMEM;
85        errno = old_errno;
86        return NSS_STATUS_TRYAGAIN;
87    }
88
89    if (fct_start == NULL &&
90        __nss_group_lookup(&startp, fct_name, &fct_start) != 0) {
91        free(buf);
92        return NSS_STATUS_UNAVAIL;
93    }
94    nip = startp;
95    fct.ptr = fct_start;
96    do {
97    morebuf:
98        if (fct.l == _nss_nonlocal_getgrgid_r)
99            status = NSS_STATUS_NOTFOUND;
100        else
101            status = DL_CALL_FCT(fct.l, (gid, &gbuf, buf, buflen, errnop));
102        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
103            free(buf);
104            buflen *= 2;
105            buf = malloc(buflen);
106            if (buf == NULL) {
107                *errnop = ENOMEM;
108                errno = old_errno;
109                return NSS_STATUS_TRYAGAIN;
110            }
111            goto morebuf;
112        }
113    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
114
115    if (status == NSS_STATUS_SUCCESS) {
116        syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
117        status = NSS_STATUS_NOTFOUND;
118    } else if (status != NSS_STATUS_TRYAGAIN) {
119        status = NSS_STATUS_SUCCESS;
120    }
121
122    free(buf);
123    return status;
124}
125
126enum nss_status
127check_nonlocal_group(const char *user, struct group *grp, int *errnop)
128{
129    enum nss_status status = NSS_STATUS_SUCCESS;
130    int old_errno = errno;
131    char *end;
132    unsigned long gid;
133
134    errno = 0;
135    gid = strtoul(grp->gr_name, &end, 10);
136    if (errno == 0 && *end == '\0' && (gid_t)gid == gid)
137        status = check_nonlocal_gid(user, gid, errnop);
138    errno = old_errno;
139    if (status != NSS_STATUS_SUCCESS)
140        return status;
141
142    return check_nonlocal_gid(user, grp->gr_gid, errnop);
143}
144
145enum nss_status
146get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
147{
148    static const char *fct_name = "getgrnam_r";
149    static service_user *startp = NULL;
150    static void *fct_start = NULL;
151    enum nss_status status;
152    service_user *nip;
153    union {
154        enum nss_status (*l)(const char *name, struct group *grp,
155                             char *buffer, size_t buflen, int *errnop);
156        void *ptr;
157    } fct;
158    size_t buflen;
159    int old_errno = errno;
160
161    buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
162    *buffer = malloc(buflen);
163    if (*buffer == NULL) {
164        *errnop = ENOMEM;
165        errno = old_errno;
166        return NSS_STATUS_TRYAGAIN;
167    }
168
169    if (fct_start == NULL &&
170        __nss_group_lookup(&startp, fct_name, &fct_start) != 0) {
171        free(*buffer);
172        *buffer = NULL;
173        return NSS_STATUS_UNAVAIL;
174    }
175    nip = startp;
176    fct.ptr = fct_start;
177    do {
178    morebuf:
179        if (fct.l == _nss_nonlocal_getgrnam_r)
180            status = NSS_STATUS_NOTFOUND;
181        else
182            status = DL_CALL_FCT(fct.l, (name, grp, *buffer, buflen, errnop));
183        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
184            free(*buffer);
185            buflen *= 2;
186            *buffer = malloc(buflen);
187            if (*buffer == NULL) {
188                *errnop = ENOMEM;
189                errno = old_errno;
190                return NSS_STATUS_TRYAGAIN;
191            }
192            goto morebuf;
193        }
194    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
195
196    if (status != NSS_STATUS_SUCCESS) {
197        free(*buffer);
198        *buffer = NULL;
199    }
200
201    return status;
202}
203
204static service_user *grent_nip = NULL;
205static void *grent_fct_start;
206static union {
207    enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
208                         int *errnop);
209    void *ptr;
210} grent_fct;
211static const char *grent_fct_name = "getgrent_r";
212
213enum nss_status
214_nss_nonlocal_setgrent(int stayopen)
215{
216    static const char *fct_name = "setgrent";
217    static void *fct_start = NULL;
218    enum nss_status status;
219    service_user *nip;
220    union {
221        enum nss_status (*l)(int stayopen);
222        void *ptr;
223    } fct;
224
225    nip = nss_group_nonlocal_database();
226    if (nip == NULL)
227        return NSS_STATUS_UNAVAIL;
228    if (fct_start == NULL)
229        fct_start = __nss_lookup_function(nip, fct_name);
230    fct.ptr = fct_start;
231    do {
232        if (fct.ptr == NULL)
233            status = NSS_STATUS_UNAVAIL;
234        else
235            status = DL_CALL_FCT(fct.l, (stayopen));
236    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
237    if (status != NSS_STATUS_SUCCESS)
238        return status;
239
240    grent_nip = nip;
241    if (grent_fct_start == NULL)
242        grent_fct_start = __nss_lookup_function(nip, grent_fct_name);
243    grent_fct.ptr = grent_fct_start;
244    return NSS_STATUS_SUCCESS;
245}
246
247enum nss_status
248_nss_nonlocal_endgrent(void)
249{
250    static const char *fct_name = "endgrent";
251    static void *fct_start = NULL;
252    enum nss_status status;
253    service_user *nip;
254    union {
255        enum nss_status (*l)(void);
256        void *ptr;
257    } fct;
258
259    grent_nip = NULL;
260
261    nip = nss_group_nonlocal_database();
262    if (nip == NULL)
263        return NSS_STATUS_UNAVAIL;
264    if (fct_start == NULL)
265        fct_start = __nss_lookup_function(nip, fct_name);
266    fct.ptr = fct_start;
267    do {
268        if (fct.ptr == NULL)
269            status = NSS_STATUS_UNAVAIL;
270        else
271            status = DL_CALL_FCT(fct.l, ());
272    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
273    return status;
274}
275
276enum nss_status
277_nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
278                         int *errnop)
279{
280    enum nss_status status;
281
282    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
283    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
284        return NSS_STATUS_UNAVAIL;
285
286    if (grent_nip == NULL) {
287        status = _nss_nonlocal_setgrent(0);
288        if (status != NSS_STATUS_SUCCESS)
289            return status;
290    }
291    do {
292        if (grent_fct.ptr == NULL)
293            status = NSS_STATUS_UNAVAIL;
294        else {
295            int nonlocal_errno;
296            do
297                status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
298            while (status == NSS_STATUS_SUCCESS &&
299                   check_nonlocal_group("(unknown)", grp, &nonlocal_errno) != NSS_STATUS_SUCCESS);
300        }
301        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
302            return status;
303
304        if (status == NSS_STATUS_SUCCESS)
305            return NSS_STATUS_SUCCESS;
306    } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
307
308    grent_nip = NULL;
309    return NSS_STATUS_NOTFOUND;
310}
311
312
313enum nss_status
314_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
315                         char *buffer, size_t buflen, int *errnop)
316{
317    static const char *fct_name = "getgrnam_r";
318    static void *fct_start = NULL;
319    enum nss_status status;
320    service_user *nip;
321    union {
322        enum nss_status (*l)(const char *name, struct group *grp,
323                             char *buffer, size_t buflen, int *errnop);
324        void *ptr;
325    } fct;
326
327    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
328    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
329        return NSS_STATUS_UNAVAIL;
330
331    nip = nss_group_nonlocal_database();
332    if (nip == NULL)
333        return NSS_STATUS_UNAVAIL;
334    if (fct_start == NULL)
335        fct_start = __nss_lookup_function(nip, fct_name);
336    fct.ptr = fct_start;
337    do {
338        if (fct.ptr == NULL)
339            status = NSS_STATUS_UNAVAIL;
340        else
341            status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
342        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
343            break;
344    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
345    if (status != NSS_STATUS_SUCCESS)
346        return status;
347
348    if (strcmp(name, grp->gr_name) != 0) {
349        syslog(LOG_ERR, "nss_nonlocal: discarding group %s from lookup for group %s\n", grp->gr_name, name);
350        return NSS_STATUS_NOTFOUND;
351    }
352
353    return check_nonlocal_group(name, grp, errnop);
354}
355
356enum nss_status
357_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
358                         char *buffer, size_t buflen, int *errnop)
359{
360    static const char *fct_name = "getgrgid_r";
361    static void *fct_start = NULL;
362    enum nss_status status;
363    service_user *nip;
364    union {
365        enum nss_status (*l)(gid_t gid, struct group *grp,
366                             char *buffer, size_t buflen, int *errnop);
367        void *ptr;
368    } fct;
369
370    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
371    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
372        return NSS_STATUS_UNAVAIL;
373
374    nip = nss_group_nonlocal_database();
375    if (nip == NULL)
376        return NSS_STATUS_UNAVAIL;
377    if (fct_start == NULL)
378        fct_start = __nss_lookup_function(nip, fct_name);
379    fct.ptr = fct_start;
380    do {
381        if (fct.ptr == NULL)
382            status = NSS_STATUS_UNAVAIL;
383        else
384            status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
385        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
386            break;
387    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
388    if (status != NSS_STATUS_SUCCESS)
389        return status;
390
391    if (gid != grp->gr_gid) {
392        syslog(LOG_ERR, "nss_nonlocal: discarding gid %d from lookup for gid %d\n", grp->gr_gid, gid);
393        return NSS_STATUS_NOTFOUND;
394    }
395
396    return check_nonlocal_group(grp->gr_name, grp, errnop);
397}
398
399enum nss_status
400_nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
401                             long int *size, gid_t **groupsp, long int limit,
402                             int *errnop)
403{
404    static const char *fct_name = "initgroups_dyn";
405    static void *fct_start = NULL;
406    enum nss_status status;
407    service_user *nip;
408    union {
409        enum nss_status (*l)(const char *user, gid_t group, long int *start,
410                             long int *size, gid_t **groupsp, long int limit,
411                             int *errnop);
412        void *ptr;
413    } fct;
414
415    struct group local_users_group, nonlocal_users_group;
416    gid_t local_users_gid, gid;
417    int is_local = 0;
418    char *buffer;
419    int old_errno;
420    int in, out, i;
421
422    /* Check that the user is a nonlocal user before adding any groups. */
423    status = check_nonlocal_user(user, errnop);
424    if (status == NSS_STATUS_TRYAGAIN)
425        return status;
426    else if (status != NSS_STATUS_SUCCESS)
427        is_local = 1;
428
429    old_errno = errno;
430
431    status = get_local_group(MAGIC_LOCAL_GROUPNAME,
432                             &local_users_group, &buffer, errnop);
433    if (status == NSS_STATUS_SUCCESS) {
434        local_users_gid = local_users_group.gr_gid;
435        free(buffer);
436    } else if (status == NSS_STATUS_TRYAGAIN) {
437        return status;
438    } else {
439        syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
440               MAGIC_LOCAL_GROUPNAME);
441        local_users_gid = -1;
442    }
443
444    if (is_local) {
445        gid = local_users_gid;
446    } else {
447        status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
448                                 &nonlocal_users_group, &buffer, errnop);
449        if (status == NSS_STATUS_SUCCESS) {
450            gid = nonlocal_users_group.gr_gid;
451            free(buffer);
452        } else if (status == NSS_STATUS_TRYAGAIN) {
453            return status;
454        } else {
455            syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
456                   MAGIC_NONLOCAL_GROUPNAME);
457            gid = -1;
458        }
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    in = out = *start;
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}
Note: See TracBrowser for help on using the repository browser.