source: trunk/server/common/oursrc/nss_nonlocal/nonlocal-passwd.c @ 2432

Last change on this file since 2432 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: 8.9 KB
Line 
1/*
2 * nonlocal-passwd.c
3 * passwd 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
27#define _GNU_SOURCE
28
29#include <sys/types.h>
30#include <dlfcn.h>
31#include <errno.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
45enum nss_status
46_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
47                         char *buffer, size_t buflen, int *errnop);
48enum nss_status
49_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
50                         char *buffer, size_t buflen, int *errnop);
51
52
53static service_user *__nss_passwd_nonlocal_database;
54
55static int
56internal_function
57__nss_passwd_nonlocal_lookup(service_user **ni, const char *fct_name,
58                             void **fctp)
59{
60    if (__nss_passwd_nonlocal_database == NULL
61        && __nss_database_lookup("passwd_nonlocal", NULL, NULL,
62                                 &__nss_passwd_nonlocal_database) < 0)
63        return -1;
64
65    *ni = __nss_passwd_nonlocal_database;
66
67    *fctp = __nss_lookup_function(*ni, fct_name);
68    return 0;
69}
70
71
72enum nss_status
73check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
74{
75    enum nss_status status;
76    struct passwd pwbuf;
77    char *buf;
78    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
79    const struct walk_nss w = {
80        .lookup = &__nss_passwd_lookup, .fct_name = "getpwuid_r",
81        .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
82    };
83    const __typeof__(&_nss_nonlocal_getpwuid_r) self = &_nss_nonlocal_getpwuid_r;
84#define args (uid, &pwbuf, buf, buflen, errnop)
85#include "walk_nss.h"
86#undef args
87
88    if (status == NSS_STATUS_SUCCESS) {
89        syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
90        free(buf);
91        status = NSS_STATUS_NOTFOUND;
92    } else if (status != NSS_STATUS_TRYAGAIN) {
93        status = NSS_STATUS_SUCCESS;
94    }
95
96    return status;
97}
98
99enum nss_status
100check_nonlocal_passwd(const char *user, struct passwd *pwd, int *errnop)
101{
102    enum nss_status status = NSS_STATUS_SUCCESS;
103    int old_errno = errno;
104    char *end;
105    unsigned long uid;
106
107    errno = 0;
108    uid = strtoul(pwd->pw_name, &end, 10);
109    if (errno == 0 && *end == '\0' && (uid_t)uid == uid) {
110        errno = old_errno;
111        status = check_nonlocal_uid(user, uid, errnop);
112    } else {
113        errno = old_errno;
114    }
115    if (status != NSS_STATUS_SUCCESS)
116        return status;
117
118    return check_nonlocal_uid(user, pwd->pw_uid, errnop);
119}
120
121enum nss_status
122check_nonlocal_user(const char *user, int *errnop)
123{
124    enum nss_status status;
125    struct passwd pwbuf;
126    char *buf;
127    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
128    const struct walk_nss w = {
129        .lookup = __nss_passwd_lookup, .fct_name = "getpwnam_r",
130        .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
131    };
132    const __typeof__(&_nss_nonlocal_getpwnam_r) self = &_nss_nonlocal_getpwnam_r;
133#define args (user, &pwbuf, buf, buflen, errnop)
134#include "walk_nss.h"
135#undef args
136
137    if (status == NSS_STATUS_SUCCESS) {
138        free(buf);
139        status = NSS_STATUS_NOTFOUND;
140    } else if (status != NSS_STATUS_TRYAGAIN) {
141        status = NSS_STATUS_SUCCESS;
142    }
143
144    return status;
145}
146
147enum nss_status
148get_nonlocal_passwd(const char *name, struct passwd *pwd, char **buffer,
149                    int *errnop)
150{
151    enum nss_status status;
152    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
153    const struct walk_nss w = {
154        .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
155        .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
156    };
157    const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
158#define args (name, pwd, *buffer, buflen, errnop)
159#include "walk_nss.h"
160#undef args
161    return status;
162}
163
164
165static bool pwent_initialized = false;
166static service_user *pwent_startp, *pwent_nip;
167static void *pwent_fct_start;
168static union {
169    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
170                         int *errnop);
171    void *ptr;
172} pwent_fct;
173static const char *pwent_fct_name = "getpwent_r";
174
175enum nss_status
176_nss_nonlocal_setpwent(int stayopen)
177{
178    enum nss_status status;
179    const struct walk_nss w = {
180        .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "setpwent",
181        .status = &status
182    };
183    const __typeof__(&_nss_nonlocal_setpwent) self = NULL;
184#define args (stayopen)
185#include "walk_nss.h"
186#undef args
187    if (status != NSS_STATUS_SUCCESS)
188        return status;
189
190    if (!pwent_initialized) {
191        __nss_passwd_nonlocal_lookup(&pwent_startp, pwent_fct_name,
192                                     &pwent_fct_start);
193        __sync_synchronize();
194        pwent_initialized = true;
195    }
196    pwent_nip = pwent_startp;
197    pwent_fct.ptr = pwent_fct_start;
198    return NSS_STATUS_SUCCESS;
199}
200
201enum nss_status
202_nss_nonlocal_endpwent(void)
203{
204    enum nss_status status;
205    const struct walk_nss w = {
206        .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "endpwent",
207        .status = &status, .all_values = 1,
208    };
209    const __typeof__(&_nss_nonlocal_endpwent) self = NULL;
210
211    pwent_nip = NULL;
212
213#define args ()
214#include "walk_nss.h"
215#undef args
216    return status;
217}
218
219enum nss_status
220_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
221                         int *errnop)
222{
223    enum nss_status status;
224
225    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
226    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
227        return NSS_STATUS_UNAVAIL;
228
229    if (pwent_nip == NULL) {
230        status = _nss_nonlocal_setpwent(0);
231        if (status != NSS_STATUS_SUCCESS)
232            return status;
233    }
234    do {
235        if (pwent_fct.ptr == NULL)
236            status = NSS_STATUS_UNAVAIL;
237        else {
238            int nonlocal_errno;
239            do
240                status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
241            while (status == NSS_STATUS_SUCCESS &&
242                   check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
243        }
244        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
245            return status;
246
247        if (status == NSS_STATUS_SUCCESS)
248            return NSS_STATUS_SUCCESS;
249    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
250
251    pwent_nip = NULL;
252    return NSS_STATUS_NOTFOUND;
253}
254
255
256enum nss_status
257_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
258                         char *buffer, size_t buflen, int *errnop)
259{
260    enum nss_status status;
261    int group_errno;
262    const struct walk_nss w = {
263        .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
264        .status = &status, .errnop = errnop
265    };
266    const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
267
268    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
269    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
270        return NSS_STATUS_UNAVAIL;
271
272#define args (name, pwd, buffer, buflen, errnop)
273#include "walk_nss.h"
274#undef args
275    if (status != NSS_STATUS_SUCCESS)
276        return status;
277
278    if (strcmp(name, pwd->pw_name) != 0) {
279        syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
280        return NSS_STATUS_NOTFOUND;
281    }
282
283    status = check_nonlocal_passwd(name, pwd, errnop);
284    if (status != NSS_STATUS_SUCCESS)
285        return status;
286
287    if (check_nonlocal_gid(name, NULL, pwd->pw_gid, &group_errno) !=
288        NSS_STATUS_SUCCESS)
289        pwd->pw_gid = 65534 /* nogroup */;
290    return NSS_STATUS_SUCCESS;
291}
292
293enum nss_status
294_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
295                         char *buffer, size_t buflen, int *errnop)
296{
297    enum nss_status status;
298    int group_errno;
299    const struct walk_nss w = {
300        .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "getpwuid_r",
301        .status = &status, .errnop = errnop
302    };
303    const __typeof__(&_nss_nonlocal_getpwuid_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 (uid, pwd, buffer, buflen, errnop)
310#include "walk_nss.h"
311#undef args
312    if (status != NSS_STATUS_SUCCESS)
313        return status;
314
315    if (uid != pwd->pw_uid) {
316        syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
317        return NSS_STATUS_NOTFOUND;
318    }
319
320    status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
321    if (status != NSS_STATUS_SUCCESS)
322        return status;
323
324    if (check_nonlocal_gid(pwd->pw_name, NULL, pwd->pw_gid, &group_errno) !=
325        NSS_STATUS_SUCCESS)
326        pwd->pw_gid = 65534 /* nogroup */;
327    return NSS_STATUS_SUCCESS;
328}
Note: See TracBrowser for help on using the repository browser.