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

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