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

Revision 26035, 13.0 KB checked in by andersk, 11 years ago (diff)
In libnss-nonlocal: * New upstream release. - Support Automake 1.12. - Guard one-time initialization with memory barriers. - Make initgroups_dyn succeed when adding only magic groups. * Rewrite packaging with Debhelper 7. * Move magic user and group creation to separate libnss-nonlocal-common package. Full history: git://andersk.mit.edu/nss_nonlocal.git debian/2.1-0debathena1
RevLine 
[22686]1/*
2 * nonlocal-group.c
3 * group database for nss_nonlocal proxy
4 *
[24585]5 * Copyright © 2007–2010 Anders Kaseorg <andersk@mit.edu> and Tim
6 * Abbott <tabbott@mit.edu>
[22686]7 *
[24585]8 * This file is part of nss_nonlocal.
[22686]9 *
[24585]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.
[22686]14 *
[24585]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
[22686]24 */
25
26#define _GNU_SOURCE
[26035]27
[22686]28#include <sys/types.h>
29#include <dlfcn.h>
30#include <errno.h>
31#include <grp.h>
32#include <nss.h>
[26035]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
[22686]41#include "nsswitch-internal.h"
42#include "nonlocal.h"
43
[25083]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 */
[22714]51#define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
[25083]52
53/*
54 * If the MAGIC_LOCAL_GROUPNAME local group exists, then local users
55 * will be automatically added to it.
56 */
[22714]57#define MAGIC_LOCAL_GROUPNAME "nss-local-users"
[22686]58
[25083]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"
[22686]65
[25083]66
[23113]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
[25083]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)
[22686]82{
[25083]83    if (__nss_group_nonlocal_database == NULL
84        && __nss_database_lookup("group_nonlocal", NULL, NULL,
85                                 &__nss_group_nonlocal_database) < 0)
86        return -1;
[22714]87
[25083]88    *ni = __nss_group_nonlocal_database;
89
90    *fctp = __nss_lookup_function(*ni, fct_name);
91    return 0;
[22686]92}
93
94
95enum nss_status
[25083]96check_nonlocal_gid(const char *user, const char *group, gid_t gid, int *errnop)
[22686]97{
[23113]98    enum nss_status status;
[22686]99    struct group gbuf;
[25083]100    char *buf;
[24122]101    size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
[25083]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
[23113]110
[25083]111    if (status == NSS_STATUS_TRYAGAIN)
112        return status;
113    else if (status != NSS_STATUS_SUCCESS)
114        return NSS_STATUS_SUCCESS;
[23113]115
[25083]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            }
[22686]130    }
[23113]131
[25083]132    syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
[22686]133    free(buf);
[25083]134    return NSS_STATUS_NOTFOUND;
[22686]135}
136
[22714]137enum nss_status
[24122]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);
[25083]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;
[24122]152    if (status != NSS_STATUS_SUCCESS)
153        return status;
154
[25083]155    return check_nonlocal_gid(user, grp->gr_name, grp->gr_gid, errnop);
[24122]156}
157
158enum nss_status
[23829]159get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
[22714]160{
[23113]161    enum nss_status status;
[25083]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
[22714]171    return status;
172}
[22686]173
[26035]174static bool grent_initialized = false;
[25083]175static service_user *grent_startp, *grent_nip;
[22686]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;
[25083]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
[22686]196    if (status != NSS_STATUS_SUCCESS)
197        return status;
198
[26035]199    if (!grent_initialized) {
[25083]200        __nss_group_nonlocal_lookup(&grent_startp, grent_fct_name,
201                                    &grent_fct_start);
[26035]202        __sync_synchronize();
203        grent_initialized = true;
204    }
[25083]205    grent_nip = grent_startp;
[22686]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;
[25083]214    const struct walk_nss w = {
215        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "endgrent",
[26035]216        .status = &status, .all_values = 1,
[25083]217    };
218    const __typeof__(&_nss_nonlocal_endgrent) self = NULL;
[22686]219
220    grent_nip = NULL;
221
[25083]222#define args ()
223#include "walk_nss.h"
224#undef args
[22686]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;
[22768]233
234    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
[23113]235    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
[22768]236        return NSS_STATUS_UNAVAIL;
237
[22686]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 &&
[24122]251                   check_nonlocal_group("(unknown)", grp, &nonlocal_errno) != NSS_STATUS_SUCCESS);
[22686]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;
[25083]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;
[22686]275
[22768]276    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
[23113]277    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
[22714]278        return NSS_STATUS_UNAVAIL;
279
[25083]280#define args (name, grp, buffer, buflen, errnop)
281#include "walk_nss.h"
282#undef args
[22686]283    if (status != NSS_STATUS_SUCCESS)
284        return status;
285
[24122]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);
[22686]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;
[25083]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;
[22686]304
[22768]305    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
[23113]306    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
[22686]307        return NSS_STATUS_UNAVAIL;
308
[25083]309#define args (gid, grp, buffer, buflen, errnop)
310#include "walk_nss.h"
311#undef args
[22686]312    if (status != NSS_STATUS_SUCCESS)
313        return status;
314
[24585]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
[24122]320    return check_nonlocal_group(grp->gr_name, grp, errnop);
[22686]321}
322
[25083]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
[22686]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;
[25083]362    const struct walk_nss w = {
363        .lookup = &__nss_group_nonlocal_lookup, .fct_name = "initgroups_dyn",
[26035]364        .status = &status, .all_values = 1, .errnop = errnop
[25083]365    };
366    const __typeof__(&_nss_nonlocal_initgroups_dyn) self = NULL;
[22755]367
368    struct group local_users_group, nonlocal_users_group;
[25083]369    bool is_nonlocal = true;
[22755]370    char *buffer;
[24122]371    int in, out, i;
[22686]372
[25083]373    /* Check that the user is a nonlocal user, or a member of the
374     * MAGIC_NONLOCAL_GROUPNAME group, before adding any groups. */
[22714]375    status = check_nonlocal_user(user, errnop);
[25083]376    if (status == NSS_STATUS_TRYAGAIN) {
[23113]377        return status;
[25083]378    } else if (status != NSS_STATUS_SUCCESS) {
379        is_nonlocal = false;
[22714]380
[25083]381        status = get_local_group(MAGIC_LOCAL_GROUPNAME,
382                                 &local_users_group, &buffer, errnop);
[23113]383        if (status == NSS_STATUS_SUCCESS) {
384            free(buffer);
[25083]385            if (!add_group(local_users_group.gr_gid, start, size, groupsp,
386                           limit, errnop, &status))
387                return status;
[23829]388        } else if (status == NSS_STATUS_TRYAGAIN) {
[23113]389            return status;
390        } else {
[25083]391            syslog(LOG_WARNING,
392                   "nss_nonlocal: Group %s does not exist locally!",
393                   MAGIC_LOCAL_GROUPNAME);
[23113]394        }
[22714]395    }
[22755]396
[25083]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;
[22755]411                }
[25083]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);
[22755]425                }
[25083]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                }
[22755]435            }
[22714]436        }
[25083]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);
[22714]443    }
444
[25083]445    if (!is_nonlocal)
[22755]446        return NSS_STATUS_SUCCESS;
447
[24122]448    in = out = *start;
[22755]449
[25083]450#define args (user, group, start, size, groupsp, limit, errnop)
451#include "walk_nss.h"
452#undef args
[26035]453    if (status == NSS_STATUS_NOTFOUND || status == NSS_STATUS_UNAVAIL)
454        return NSS_STATUS_SUCCESS;
455    else if (status != NSS_STATUS_SUCCESS)
[22686]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
[25083]467        status = check_nonlocal_gid(user, NULL, (*groupsp)[in],
468                                    &nonlocal_errno);
[22686]469        if (status == NSS_STATUS_SUCCESS) {
[22755]470            (*groupsp)[out++] = (*groupsp)[in];
[23113]471        } else if (status == NSS_STATUS_TRYAGAIN) {
[22686]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.