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

Last change on this file since 1825 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
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 <pwd.h>
37#include <grp.h>
38#include <nss.h>
39#include "nsswitch-internal.h"
40#include "nonlocal.h"
41
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 */
49#define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
50
51/*
52 * If the MAGIC_LOCAL_GROUPNAME local group exists, then local users
53 * will be automatically added to it.
54 */
55#define MAGIC_LOCAL_GROUPNAME "nss-local-users"
56
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"
63
64
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
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)
80{
81    if (__nss_group_nonlocal_database == NULL
82        && __nss_database_lookup("group_nonlocal", NULL, NULL,
83                                 &__nss_group_nonlocal_database) < 0)
84        return -1;
85
86    *ni = __nss_group_nonlocal_database;
87
88    *fctp = __nss_lookup_function(*ni, fct_name);
89    return 0;
90}
91
92
93enum nss_status
94check_nonlocal_gid(const char *user, const char *group, gid_t gid, int *errnop)
95{
96    enum nss_status status;
97    struct group gbuf;
98    char *buf;
99    size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
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
108
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;
127            }
128    }
129
130    syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
131    free(buf);
132    return NSS_STATUS_NOTFOUND;
133}
134
135enum nss_status
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);
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;
150    if (status != NSS_STATUS_SUCCESS)
151        return status;
152
153    return check_nonlocal_gid(user, grp->gr_name, grp->gr_gid, errnop);
154}
155
156enum nss_status
157get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
158{
159    enum nss_status status;
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
169    return status;
170}
171
172static service_user *grent_startp, *grent_nip;
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;
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
193    if (status != NSS_STATUS_SUCCESS)
194        return status;
195
196    if (grent_fct_start == NULL)
197        __nss_group_nonlocal_lookup(&grent_startp, grent_fct_name,
198                                    &grent_fct_start);
199    grent_nip = grent_startp;
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;
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;
213
214    grent_nip = NULL;
215
216#define args ()
217#include "walk_nss.h"
218#undef args
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);
229    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
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 &&
245                   check_nonlocal_group("(unknown)", grp, &nonlocal_errno) != NSS_STATUS_SUCCESS);
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;
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;
269
270    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
271    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
272        return NSS_STATUS_UNAVAIL;
273
274#define args (name, grp, buffer, buflen, errnop)
275#include "walk_nss.h"
276#undef args
277    if (status != NSS_STATUS_SUCCESS)
278        return status;
279
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);
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;
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;
298
299    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
300    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
301        return NSS_STATUS_UNAVAIL;
302
303#define args (gid, grp, buffer, buflen, errnop)
304#include "walk_nss.h"
305#undef args
306    if (status != NSS_STATUS_SUCCESS)
307        return status;
308
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);
315}
316
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
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;
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;
361
362    struct group local_users_group, nonlocal_users_group;
363    bool is_nonlocal = true;
364    char *buffer;
365    int in, out, i;
366
367    /* Check that the user is a nonlocal user, or a member of the
368     * MAGIC_NONLOCAL_GROUPNAME group, before adding any groups. */
369    status = check_nonlocal_user(user, errnop);
370    if (status == NSS_STATUS_TRYAGAIN) {
371        return status;
372    } else if (status != NSS_STATUS_SUCCESS) {
373        is_nonlocal = false;
374
375        status = get_local_group(MAGIC_LOCAL_GROUPNAME,
376                                 &local_users_group, &buffer, errnop);
377        if (status == NSS_STATUS_SUCCESS) {
378            free(buffer);
379            if (!add_group(local_users_group.gr_gid, start, size, groupsp,
380                           limit, errnop, &status))
381                return status;
382        } else if (status == NSS_STATUS_TRYAGAIN) {
383            return status;
384        } else {
385            syslog(LOG_WARNING,
386                   "nss_nonlocal: Group %s does not exist locally!",
387                   MAGIC_LOCAL_GROUPNAME);
388        }
389    }
390
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;
405                }
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);
419                }
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                }
429            }
430        }
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);
437    }
438
439    if (!is_nonlocal)
440        return NSS_STATUS_SUCCESS;
441
442    in = out = *start;
443
444#define args (user, group, start, size, groupsp, limit, errnop)
445#include "walk_nss.h"
446#undef args
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
459        status = check_nonlocal_gid(user, NULL, (*groupsp)[in],
460                                    &nonlocal_errno);
461        if (status == NSS_STATUS_SUCCESS) {
462            (*groupsp)[out++] = (*groupsp)[in];
463        } else if (status == NSS_STATUS_TRYAGAIN) {
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.