source: server/common/oursrc/nss_nonlocal/nonlocal-group.c @ 846

Last change on this file since 846 was 800, checked in by andersk, 16 years ago
Update to nss_nonlocal 1.8.
File size: 14.4 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        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
119enum nss_status
120get_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
199static service_user *grent_nip = NULL;
200static void *grent_fct_start;
201static union {
202    enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
203                         int *errnop);
204    void *ptr;
205} grent_fct;
206static const char *grent_fct_name = "getgrent_r";
207
208enum 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
242enum 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
271enum 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
308enum 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
346enum 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
384enum 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}
Note: See TracBrowser for help on using the repository browser.