source: trunk/debathena/debathena/libnss-nonlocal/nonlocal-passwd.c @ 23113

Revision 23113, 10.0 KB checked in by andersk, 16 years ago (diff)
In libnss-nonlocal: * Replace the magic buflen hack with explicit iteration over the nss chain, so that getpwent/getgrent works with nscd enabled. * Fix some memory leaks. * Autotoolfiscate. * Code cleanup.
Line 
1/*
2 * nonlocal-passwd.c
3 * passwd database for nss_nonlocal proxy.
4 *
5 * Copyright © 2007 Anders Kaseorg <andersk@mit.edu> and Tim Abbott
6 * <tabbott@mit.edu>
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use, copy,
12 * modify, merge, publish, distribute, sublicense, and/or sell copies
13 * of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29
30#define _GNU_SOURCE
31#include <sys/types.h>
32#include <unistd.h>
33#include <stdlib.h>
34#include <stdint.h>
35#include <string.h>
36#include <dlfcn.h>
37#include <stdio.h>
38#include <syslog.h>
39#include <errno.h>
40#include <pwd.h>
41#include <grp.h>
42#include <nss.h>
43#include "nsswitch-internal.h"
44#include "nonlocal.h"
45
46
47enum nss_status
48_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
49                         char *buffer, size_t buflen, int *errnop);
50enum nss_status
51_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
52                         char *buffer, size_t buflen, int *errnop);
53
54
55static service_user *
56nss_passwd_nonlocal_database(void)
57{
58    static service_user *nip = NULL;
59    if (nip == NULL)
60        __nss_database_lookup("passwd_nonlocal", NULL, "", &nip);
61
62    return nip;
63}
64
65
66enum nss_status
67check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
68{
69    static const char *fct_name = "getpwuid_r";
70    static service_user *startp = NULL;
71    static void *fct_start = NULL;
72    enum nss_status status;
73    service_user *nip;
74    union {
75        enum nss_status (*l)(uid_t uid, struct passwd *pwd,
76                             char *buffer, size_t buflen, int *errnop);
77        void *ptr;
78    } fct;
79    struct passwd pwbuf;
80    int old_errno = errno;
81
82    int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
83    char *buf = malloc(buflen);
84    if (buf == NULL) {
85        *errnop = ENOMEM;
86        errno = old_errno;
87        return NSS_STATUS_TRYAGAIN;
88    }
89
90    if (fct_start == NULL &&
91        __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
92        free(buf);
93        return NSS_STATUS_UNAVAIL;
94    }
95    nip = startp;
96    fct.ptr = fct_start;
97    do {
98        if (fct.l == _nss_nonlocal_getpwuid_r)
99            status = NSS_STATUS_NOTFOUND;
100        else
101            status = DL_CALL_FCT(fct.l, (uid, &pwbuf, buf, buflen, errnop));
102        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
103            break;
104    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
105
106    if (status == NSS_STATUS_SUCCESS) {
107        syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
108        status = NSS_STATUS_NOTFOUND;
109    } else if (status != NSS_STATUS_TRYAGAIN) {
110        status = NSS_STATUS_SUCCESS;
111    }
112
113    free(buf);
114    return status;
115}
116
117enum nss_status
118check_nonlocal_user(const char *user, int *errnop)
119{
120    static const char *fct_name = "getpwnam_r";
121    static service_user *startp = NULL;
122    static void *fct_start = NULL;
123    enum nss_status status;
124    service_user *nip;
125    union {
126        enum nss_status (*l)(const char *name, struct passwd *pwd,
127                             char *buffer, size_t buflen, int *errnop);
128        void *ptr;
129    } fct;
130    struct passwd pwbuf;
131    int old_errno = errno;
132
133    int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
134    char *buf = malloc(buflen);
135    if (buf == NULL) {
136        *errnop = ENOMEM;
137        errno = old_errno;
138        return NSS_STATUS_TRYAGAIN;
139    }
140
141    if (fct_start == NULL &&
142        __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
143        free(buf);
144        return NSS_STATUS_UNAVAIL;
145    }
146    nip = startp;
147    fct.ptr = fct_start;
148    do {
149        if (fct.l == _nss_nonlocal_getpwnam_r)
150            status = NSS_STATUS_NOTFOUND;
151        else
152            status = DL_CALL_FCT(fct.l, (user, &pwbuf, buf, buflen, errnop));
153        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
154            break;
155    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
156
157    if (status == NSS_STATUS_SUCCESS)
158        status = NSS_STATUS_NOTFOUND;
159    else if (status != NSS_STATUS_TRYAGAIN)
160        status = NSS_STATUS_SUCCESS;
161
162    free(buf);
163    return status;
164}
165
166
167static service_user *pwent_nip = NULL;
168static void *pwent_fct_start;
169static union {
170    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
171                         int *errnop);
172    void *ptr;
173} pwent_fct;
174static const char *pwent_fct_name = "getpwent_r";
175
176enum nss_status
177_nss_nonlocal_setpwent(int stayopen)
178{
179    static const char *fct_name = "setpwent";
180    static void *fct_start = NULL;
181    enum nss_status status;
182    service_user *nip;
183    union {
184        enum nss_status (*l)(int stayopen);
185        void *ptr;
186    } fct;
187
188    nip = nss_passwd_nonlocal_database();
189    if (nip == NULL)
190        return NSS_STATUS_UNAVAIL;
191    if (fct_start == NULL)
192        fct_start = __nss_lookup_function(nip, fct_name);
193    fct.ptr = fct_start;
194    do {
195        if (fct.ptr == NULL)
196            status = NSS_STATUS_UNAVAIL;
197        else
198            status = DL_CALL_FCT(fct.l, (stayopen));
199    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
200    if (status != NSS_STATUS_SUCCESS)
201        return status;
202
203    pwent_nip = nip;
204    if (pwent_fct_start == NULL)
205        pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
206    pwent_fct.ptr = pwent_fct_start;
207    return NSS_STATUS_SUCCESS;
208}
209
210enum nss_status
211_nss_nonlocal_endpwent(void)
212{
213    static const char *fct_name = "endpwent";
214    static void *fct_start = NULL;
215    enum nss_status status;
216    service_user *nip;
217    union {
218        enum nss_status (*l)(void);
219        void *ptr;
220    } fct;
221
222    pwent_nip = NULL;
223
224    nip = nss_passwd_nonlocal_database();
225    if (nip == NULL)
226        return NSS_STATUS_UNAVAIL;
227    if (fct_start == NULL)
228        fct_start = __nss_lookup_function(nip, fct_name);
229    fct.ptr = fct_start;
230    do {
231        if (fct.ptr == NULL)
232            status = NSS_STATUS_UNAVAIL;
233        else
234            status = DL_CALL_FCT(fct.l, ());
235    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
236    return status;
237}
238
239enum nss_status
240_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
241                         int *errnop)
242{
243    enum nss_status status;
244
245    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
246    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
247        return NSS_STATUS_UNAVAIL;
248
249    if (pwent_nip == NULL) {
250        status = _nss_nonlocal_setpwent(0);
251        if (status != NSS_STATUS_SUCCESS)
252            return status;
253    }
254    do {
255        if (pwent_fct.ptr == NULL)
256            status = NSS_STATUS_UNAVAIL;
257        else {
258            int nonlocal_errno;
259            do
260                status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
261            while (status == NSS_STATUS_SUCCESS &&
262                   check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
263        }
264        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
265            return status;
266
267        if (status == NSS_STATUS_SUCCESS)
268            return NSS_STATUS_SUCCESS;
269    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
270
271    pwent_nip = NULL;
272    return NSS_STATUS_NOTFOUND;
273}
274
275
276enum nss_status
277_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
278                         char *buffer, size_t buflen, int *errnop)
279{
280    static const char *fct_name = "getpwnam_r";
281    static void *fct_start = NULL;
282    enum nss_status status;
283    service_user *nip;
284    union {
285        enum nss_status (*l)(const char *name, struct passwd *pwd,
286                             char *buffer, size_t buflen, int *errnop);
287        void *ptr;
288    } fct;
289    int group_errno;
290
291    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
292    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
293        return NSS_STATUS_UNAVAIL;
294
295    nip = nss_passwd_nonlocal_database();
296    if (nip == NULL)
297        return NSS_STATUS_UNAVAIL;
298    if (fct_start == NULL)
299        fct_start = __nss_lookup_function(nip, fct_name);
300    fct.ptr = fct_start;
301    do {
302        if (fct.ptr == NULL)
303            status = NSS_STATUS_UNAVAIL;
304        else
305            status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
306        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
307            break;
308    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
309    if (status != NSS_STATUS_SUCCESS)
310        return status;
311
312    status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
313    if (status != NSS_STATUS_SUCCESS)
314        return status;
315
316    if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
317        NSS_STATUS_SUCCESS)
318        pwd->pw_gid = 65534 /* nogroup */;
319    return NSS_STATUS_SUCCESS;
320}
321
322enum nss_status
323_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
324                         char *buffer, size_t buflen, int *errnop)
325{
326    static const char *fct_name = "getpwuid_r";
327    static void *fct_start = NULL;
328    enum nss_status status;
329    service_user *nip;
330    union {
331        enum nss_status (*l)(uid_t uid, struct passwd *pwd,
332                             char *buffer, size_t buflen, int *errnop);
333        void *ptr;
334    } fct;
335    int group_errno;
336
337    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
338    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
339        return NSS_STATUS_UNAVAIL;
340
341    nip = nss_passwd_nonlocal_database();
342    if (nip == NULL)
343        return NSS_STATUS_UNAVAIL;
344    if (fct_start == NULL)
345        fct_start = __nss_lookup_function(nip, fct_name);
346    fct.ptr = fct_start;
347    do {
348        if (fct.ptr == NULL)
349            status = NSS_STATUS_UNAVAIL;
350        else
351            status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
352        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
353            break;
354    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
355    if (status != NSS_STATUS_SUCCESS)
356        return status;
357
358    status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
359    if (status != NSS_STATUS_SUCCESS)
360        return status;
361
362    if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
363        NSS_STATUS_SUCCESS)
364        pwd->pw_gid = 65534 /* nogroup */;
365    return NSS_STATUS_SUCCESS;
366}
Note: See TracBrowser for help on using the repository browser.