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

Last change on this file since 2740 was 2432, checked in by andersk, 11 years ago
Update nss_nonlocal to 2.1 - Support Automake 1.12. - Guard one-time initialization with memory barriers. - Make initgroups_dyn succeed when adding only magic groups.
File size: 13.0 KB
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
28#include <sys/types.h>
29#include <dlfcn.h>
30#include <errno.h>
31#include <grp.h>
32#include <nss.h>
33#include <pwd.h>
34#include <stdbool.h>
35#include <stddef.h>
36#include <stdlib.h>
37#include <string.h>
38#include <syslog.h>
39#include <unistd.h>
40
41#include "nsswitch-internal.h"
42#include "nonlocal.h"
43
44/*
45 * If the MAGIC_NONLOCAL_GROUPNAME local group exists, then nonlocal
46 * users will be automatically added to it.  Furthermore, if a local
47 * user is added to this group, then that user will inherit any
48 * nonlocal gids from a nonlocal user of the same name, as
49 * supplementary gids.
50 */
51#define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
52
53/*
54 * If the MAGIC_LOCAL_GROUPNAME local group exists, then local users
55 * will be automatically added to it.
56 */
57#define MAGIC_LOCAL_GROUPNAME "nss-local-users"
58
59/*
60 * If the MAGIC_NONLOCAL_USERNAME local user is added to a local
61 * group, then the local group will inherit the nonlocal membership of
62 * a group of the same gid.
63 */
64#define MAGIC_NONLOCAL_USERNAME "nss-nonlocal-users"
65
66
67enum nss_status
68_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
69                         char *buffer, size_t buflen, int *errnop);
70
71enum nss_status
72_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
73                         char *buffer, size_t buflen, int *errnop);
74
75
76static service_user *__nss_group_nonlocal_database;
77
78static int
79internal_function
80__nss_group_nonlocal_lookup(service_user **ni, const char *fct_name,
81                            void **fctp)
82{
83    if (__nss_group_nonlocal_database == NULL
84        && __nss_database_lookup("group_nonlocal", NULL, NULL,
85                                 &__nss_group_nonlocal_database) < 0)
86        return -1;
87
88    *ni = __nss_group_nonlocal_database;
89
90    *fctp = __nss_lookup_function(*ni, fct_name);
91    return 0;
92}
93
94
95enum nss_status
96check_nonlocal_gid(const char *user, const char *group, gid_t gid, int *errnop)
97{
98    enum nss_status status;
99    struct group gbuf;
100    char *buf;
101    size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
102    const struct walk_nss w = {
103        .lookup = &__nss_group_lookup, .fct_name = "getgrgid_r",
104        .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
105    };
106    const __typeof__(&_nss_nonlocal_getgrgid_r) self = &_nss_nonlocal_getgrgid_r;
107#define args (gid, &gbuf, buf, buflen, errnop)
108#include "walk_nss.h"
109#undef args
110
111    if (status == NSS_STATUS_TRYAGAIN)
112        return status;
113    else if (status != NSS_STATUS_SUCCESS)
114        return NSS_STATUS_SUCCESS;
115
116    if (group == NULL || strcmp(gbuf.gr_name, group) == 0) {
117        char *const *mem;
118        for (mem = gbuf.gr_mem; *mem != NULL; mem++)
119            if (strcmp(*mem, MAGIC_NONLOCAL_USERNAME) == 0) {
120                status = check_nonlocal_user(*mem, errnop);
121                if (status == NSS_STATUS_TRYAGAIN) {
122                    free(buf);
123                    return status;
124                } else if (status == NSS_STATUS_NOTFOUND) {
125                    free(buf);
126                    return NSS_STATUS_SUCCESS;
127                }
128                break;
129            }
130    }
131
132    syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
133    free(buf);
134    return NSS_STATUS_NOTFOUND;
135}
136
137enum nss_status
138check_nonlocal_group(const char *user, struct group *grp, int *errnop)
139{
140    enum nss_status status = NSS_STATUS_SUCCESS;
141    int old_errno = errno;
142    char *end;
143    unsigned long gid;
144
145    errno = 0;
146    gid = strtoul(grp->gr_name, &end, 10);
147    if (errno == 0 && *end == '\0' && (gid_t)gid == gid) {
148        errno = old_errno;
149        status = check_nonlocal_gid(user, grp->gr_name, gid, errnop);
150    } else
151        errno = old_errno;
152    if (status != NSS_STATUS_SUCCESS)
153        return status;
154
155    return check_nonlocal_gid(user, grp->gr_name, grp->gr_gid, errnop);
156}
157
158enum nss_status
159get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
160{
161    enum nss_status status;
162    size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
163    const struct walk_nss w = {
164        .lookup = &__nss_group_lookup, .fct_name = "getgrnam_r",
165        .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
166    };
167    const __typeof__(&_nss_nonlocal_getgrnam_r) self = &_nss_nonlocal_getgrnam_r;
168#define args (name, grp, *buffer, buflen, errnop)
169#include "walk_nss.h"
170#undef args
171    return status;
172}
173
174static bool grent_initialized = false;
175static service_user *grent_startp, *grent_nip;
176static void *grent_fct_start;
177static union {
178    enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
179                         int *errnop);
180    void *ptr;
181} grent_fct;
182static const char *grent_fct_name = "getgrent_r";
183
184enum nss_status
185_nss_nonlocal_setgrent(int stayopen)
186{
187    enum nss_status status;
188    const struct walk_nss w = {
189        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "setgrent",
190        .status = &status
191    };
192    const __typeof__(&_nss_nonlocal_setgrent) self = NULL;
193#define args (stayopen)
194#include "walk_nss.h"
195#undef args
196    if (status != NSS_STATUS_SUCCESS)
197        return status;
198
199    if (!grent_initialized) {
200        __nss_group_nonlocal_lookup(&grent_startp, grent_fct_name,
201                                    &grent_fct_start);
202        __sync_synchronize();
203        grent_initialized = true;
204    }
205    grent_nip = grent_startp;
206    grent_fct.ptr = grent_fct_start;
207    return NSS_STATUS_SUCCESS;
208}
209
210enum nss_status
211_nss_nonlocal_endgrent(void)
212{
213    enum nss_status status;
214    const struct walk_nss w = {
215        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "endgrent",
216        .status = &status, .all_values = 1,
217    };
218    const __typeof__(&_nss_nonlocal_endgrent) self = NULL;
219
220    grent_nip = NULL;
221
222#define args ()
223#include "walk_nss.h"
224#undef args
225    return status;
226}
227
228enum nss_status
229_nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
230                         int *errnop)
231{
232    enum nss_status status;
233
234    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
235    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
236        return NSS_STATUS_UNAVAIL;
237
238    if (grent_nip == NULL) {
239        status = _nss_nonlocal_setgrent(0);
240        if (status != NSS_STATUS_SUCCESS)
241            return status;
242    }
243    do {
244        if (grent_fct.ptr == NULL)
245            status = NSS_STATUS_UNAVAIL;
246        else {
247            int nonlocal_errno;
248            do
249                status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
250            while (status == NSS_STATUS_SUCCESS &&
251                   check_nonlocal_group("(unknown)", grp, &nonlocal_errno) != NSS_STATUS_SUCCESS);
252        }
253        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
254            return status;
255
256        if (status == NSS_STATUS_SUCCESS)
257            return NSS_STATUS_SUCCESS;
258    } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
259
260    grent_nip = NULL;
261    return NSS_STATUS_NOTFOUND;
262}
263
264
265enum nss_status
266_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
267                         char *buffer, size_t buflen, int *errnop)
268{
269    enum nss_status status;
270    const struct walk_nss w = {
271        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "getgrnam_r",
272        .status = &status, .errnop = errnop
273    };
274    const __typeof__(&_nss_nonlocal_getgrnam_r) self = NULL;
275
276    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
277    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
278        return NSS_STATUS_UNAVAIL;
279
280#define args (name, grp, buffer, buflen, errnop)
281#include "walk_nss.h"
282#undef args
283    if (status != NSS_STATUS_SUCCESS)
284        return status;
285
286    if (strcmp(name, grp->gr_name) != 0) {
287        syslog(LOG_ERR, "nss_nonlocal: discarding group %s from lookup for group %s\n", grp->gr_name, name);
288        return NSS_STATUS_NOTFOUND;
289    }
290
291    return check_nonlocal_group(name, grp, errnop);
292}
293
294enum nss_status
295_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
296                         char *buffer, size_t buflen, int *errnop)
297{
298    enum nss_status status;
299    const struct walk_nss w = {
300        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "getgrgid_r",
301        .status = &status, .errnop = errnop
302    };
303    const __typeof__(&_nss_nonlocal_getgrgid_r) self = NULL;
304
305    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
306    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
307        return NSS_STATUS_UNAVAIL;
308
309#define args (gid, grp, buffer, buflen, errnop)
310#include "walk_nss.h"
311#undef args
312    if (status != NSS_STATUS_SUCCESS)
313        return status;
314
315    if (gid != grp->gr_gid) {
316        syslog(LOG_ERR, "nss_nonlocal: discarding gid %d from lookup for gid %d\n", grp->gr_gid, gid);
317        return NSS_STATUS_NOTFOUND;
318    }
319
320    return check_nonlocal_group(grp->gr_name, grp, errnop);
321}
322
323static bool
324add_group(gid_t group, long int *start, long int *size, gid_t **groupsp,
325          long int limit, int *errnop, enum nss_status *status)
326{
327    int i, old_errno = errno;
328    for (i = 0; i < *start; ++i)
329        if ((*groupsp)[i] == group)
330            return true;
331    if (*start + 1 > *size) {
332        gid_t *newgroups;
333        long int newsize = 2 * *size;
334        if (limit > 0) {
335            if (*size >= limit) {
336                *status = NSS_STATUS_SUCCESS;
337                return false;
338            }
339            if (newsize > limit)
340                newsize = limit;
341        }
342        newgroups = realloc(*groupsp, newsize * sizeof((*groupsp)[0]));
343        errno = old_errno;
344        if (newgroups == NULL) {
345            *errnop = ENOMEM;
346            *status = NSS_STATUS_TRYAGAIN;
347            return false;
348        }
349        *groupsp = newgroups;
350        *size = newsize;
351    }
352    (*groupsp)[(*start)++] = group;
353    return true;
354}
355
356enum nss_status
357_nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
358                             long int *size, gid_t **groupsp, long int limit,
359                             int *errnop)
360{
361    enum nss_status status;
362    const struct walk_nss w = {
363        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "initgroups_dyn",
364        .status = &status, .all_values = 1, .errnop = errnop
365    };
366    const __typeof__(&_nss_nonlocal_initgroups_dyn) self = NULL;
367
368    struct group local_users_group, nonlocal_users_group;
369    bool is_nonlocal = true;
370    char *buffer;
371    int in, out, i;
372
373    /* Check that the user is a nonlocal user, or a member of the
374     * MAGIC_NONLOCAL_GROUPNAME group, before adding any groups. */
375    status = check_nonlocal_user(user, errnop);
376    if (status == NSS_STATUS_TRYAGAIN) {
377        return status;
378    } else if (status != NSS_STATUS_SUCCESS) {
379        is_nonlocal = false;
380
381        status = get_local_group(MAGIC_LOCAL_GROUPNAME,
382                                 &local_users_group, &buffer, errnop);
383        if (status == NSS_STATUS_SUCCESS) {
384            free(buffer);
385            if (!add_group(local_users_group.gr_gid, start, size, groupsp,
386                           limit, errnop, &status))
387                return status;
388        } else if (status == NSS_STATUS_TRYAGAIN) {
389            return status;
390        } else {
391            syslog(LOG_WARNING,
392                   "nss_nonlocal: Group %s does not exist locally!",
393                   MAGIC_LOCAL_GROUPNAME);
394        }
395    }
396
397    status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
398                             &nonlocal_users_group, &buffer, errnop);
399    if (status == NSS_STATUS_SUCCESS) {
400        free(buffer);
401        if (is_nonlocal) {
402            if (!add_group(nonlocal_users_group.gr_gid, start, size, groupsp,
403                           limit, errnop, &status))
404                return status;
405        } else {
406            int i;
407            for (i = 0; i < *start; ++i) {
408                if ((*groupsp)[i] == nonlocal_users_group.gr_gid) {
409                    is_nonlocal = true;
410                    break;
411                }
412            }
413
414            if (is_nonlocal) {
415                struct passwd pwbuf;
416                char *buf;
417                int nonlocal_errno = *errnop;
418                status = get_nonlocal_passwd(user, &pwbuf, &buf, errnop);
419
420                if (status == NSS_STATUS_SUCCESS) {
421                    nonlocal_errno = *errnop;
422                    status = check_nonlocal_gid(user, NULL, pwbuf.pw_gid,
423                                                &nonlocal_errno);
424                    free(buf);
425                }
426
427                if (status == NSS_STATUS_SUCCESS) {
428                    if (!add_group(pwbuf.pw_gid, start, size, groupsp, limit,
429                                   errnop, &status))
430                        return status;
431                } else if (status == NSS_STATUS_TRYAGAIN) {
432                    *errnop = nonlocal_errno;
433                    return status;
434                }
435            }
436        }
437    } else if (status == NSS_STATUS_TRYAGAIN) {
438        if (is_nonlocal)
439            return status;
440    } else {
441        syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
442               MAGIC_NONLOCAL_GROUPNAME);
443    }
444
445    if (!is_nonlocal)
446        return NSS_STATUS_SUCCESS;
447
448    in = out = *start;
449
450#define args (user, group, start, size, groupsp, limit, errnop)
451#include "walk_nss.h"
452#undef args
453    if (status == NSS_STATUS_NOTFOUND || status == NSS_STATUS_UNAVAIL)
454        return NSS_STATUS_SUCCESS;
455    else if (status != NSS_STATUS_SUCCESS)
456        return status;
457
458    for (; in < *start; ++in) {
459        int nonlocal_errno = *errnop;
460
461        for (i = 0; i < out; ++i)
462            if ((*groupsp)[i] == (*groupsp)[in])
463                break;
464        if (i < out)
465            continue;
466
467        status = check_nonlocal_gid(user, NULL, (*groupsp)[in],
468                                    &nonlocal_errno);
469        if (status == NSS_STATUS_SUCCESS) {
470            (*groupsp)[out++] = (*groupsp)[in];
471        } else if (status == NSS_STATUS_TRYAGAIN) {
472            *start = out;
473            *errnop = nonlocal_errno;
474            return status;
475        }
476    }
477
478    *start = out;
479    return NSS_STATUS_SUCCESS;
480}
Note: See TracBrowser for help on using the repository browser.