source: branches/fc11-dev/server/common/oursrc/nss_nonlocal/nonlocal-passwd.c @ 1179

Last change on this file since 1179 was 1179, checked in by mitchb, 16 years ago
Merge r1121:1178 from trunk to branches/fc11-dev
File size: 10.4 KB
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    morebuf:
99        if (fct.l == _nss_nonlocal_getpwuid_r)
100            status = NSS_STATUS_NOTFOUND;
101        else
102            status = DL_CALL_FCT(fct.l, (uid, &pwbuf, buf, buflen, errnop));
103        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
104            free(buf);
105            buflen *= 2;
106            buf = malloc(buflen);
107            if (buf == NULL) {
108                *errnop = ENOMEM;
109                errno = old_errno;
110                return NSS_STATUS_TRYAGAIN;
111            }
112            goto morebuf;
113        }
114    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
115
116    if (status == NSS_STATUS_SUCCESS) {
117        syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
118        status = NSS_STATUS_NOTFOUND;
119    } else if (status != NSS_STATUS_TRYAGAIN) {
120        status = NSS_STATUS_SUCCESS;
121    }
122
123    free(buf);
124    return status;
125}
126
127enum nss_status
128check_nonlocal_user(const char *user, int *errnop)
129{
130    static const char *fct_name = "getpwnam_r";
131    static service_user *startp = NULL;
132    static void *fct_start = NULL;
133    enum nss_status status;
134    service_user *nip;
135    union {
136        enum nss_status (*l)(const char *name, struct passwd *pwd,
137                             char *buffer, size_t buflen, int *errnop);
138        void *ptr;
139    } fct;
140    struct passwd pwbuf;
141    int old_errno = errno;
142
143    int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
144    char *buf = malloc(buflen);
145    if (buf == NULL) {
146        *errnop = ENOMEM;
147        errno = old_errno;
148        return NSS_STATUS_TRYAGAIN;
149    }
150
151    if (fct_start == NULL &&
152        __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
153        free(buf);
154        return NSS_STATUS_UNAVAIL;
155    }
156    nip = startp;
157    fct.ptr = fct_start;
158    do {
159    morebuf:
160        if (fct.l == _nss_nonlocal_getpwnam_r)
161            status = NSS_STATUS_NOTFOUND;
162        else
163            status = DL_CALL_FCT(fct.l, (user, &pwbuf, buf, buflen, errnop));
164        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
165            free(buf);
166            buflen *= 2;
167            buf = malloc(buflen);
168            if (buf == NULL) {
169                *errnop = ENOMEM;
170                errno = old_errno;
171                return NSS_STATUS_TRYAGAIN;
172            }
173            goto morebuf;
174        }
175    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
176
177    if (status == NSS_STATUS_SUCCESS)
178        status = NSS_STATUS_NOTFOUND;
179    else if (status != NSS_STATUS_TRYAGAIN)
180        status = NSS_STATUS_SUCCESS;
181
182    free(buf);
183    return status;
184}
185
186
187static service_user *pwent_nip = NULL;
188static void *pwent_fct_start;
189static union {
190    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
191                         int *errnop);
192    void *ptr;
193} pwent_fct;
194static const char *pwent_fct_name = "getpwent_r";
195
196enum nss_status
197_nss_nonlocal_setpwent(int stayopen)
198{
199    static const char *fct_name = "setpwent";
200    static void *fct_start = NULL;
201    enum nss_status status;
202    service_user *nip;
203    union {
204        enum nss_status (*l)(int stayopen);
205        void *ptr;
206    } fct;
207
208    nip = nss_passwd_nonlocal_database();
209    if (nip == NULL)
210        return NSS_STATUS_UNAVAIL;
211    if (fct_start == NULL)
212        fct_start = __nss_lookup_function(nip, fct_name);
213    fct.ptr = fct_start;
214    do {
215        if (fct.ptr == NULL)
216            status = NSS_STATUS_UNAVAIL;
217        else
218            status = DL_CALL_FCT(fct.l, (stayopen));
219    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
220    if (status != NSS_STATUS_SUCCESS)
221        return status;
222
223    pwent_nip = nip;
224    if (pwent_fct_start == NULL)
225        pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
226    pwent_fct.ptr = pwent_fct_start;
227    return NSS_STATUS_SUCCESS;
228}
229
230enum nss_status
231_nss_nonlocal_endpwent(void)
232{
233    static const char *fct_name = "endpwent";
234    static void *fct_start = NULL;
235    enum nss_status status;
236    service_user *nip;
237    union {
238        enum nss_status (*l)(void);
239        void *ptr;
240    } fct;
241
242    pwent_nip = NULL;
243
244    nip = nss_passwd_nonlocal_database();
245    if (nip == NULL)
246        return NSS_STATUS_UNAVAIL;
247    if (fct_start == NULL)
248        fct_start = __nss_lookup_function(nip, fct_name);
249    fct.ptr = fct_start;
250    do {
251        if (fct.ptr == NULL)
252            status = NSS_STATUS_UNAVAIL;
253        else
254            status = DL_CALL_FCT(fct.l, ());
255    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
256    return status;
257}
258
259enum nss_status
260_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
261                         int *errnop)
262{
263    enum nss_status status;
264
265    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
266    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
267        return NSS_STATUS_UNAVAIL;
268
269    if (pwent_nip == NULL) {
270        status = _nss_nonlocal_setpwent(0);
271        if (status != NSS_STATUS_SUCCESS)
272            return status;
273    }
274    do {
275        if (pwent_fct.ptr == NULL)
276            status = NSS_STATUS_UNAVAIL;
277        else {
278            int nonlocal_errno;
279            do
280                status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
281            while (status == NSS_STATUS_SUCCESS &&
282                   check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
283        }
284        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
285            return status;
286
287        if (status == NSS_STATUS_SUCCESS)
288            return NSS_STATUS_SUCCESS;
289    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
290
291    pwent_nip = NULL;
292    return NSS_STATUS_NOTFOUND;
293}
294
295
296enum nss_status
297_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
298                         char *buffer, size_t buflen, int *errnop)
299{
300    static const char *fct_name = "getpwnam_r";
301    static void *fct_start = NULL;
302    enum nss_status status;
303    service_user *nip;
304    union {
305        enum nss_status (*l)(const char *name, struct passwd *pwd,
306                             char *buffer, size_t buflen, int *errnop);
307        void *ptr;
308    } fct;
309    int group_errno;
310
311    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
312    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
313        return NSS_STATUS_UNAVAIL;
314
315    nip = nss_passwd_nonlocal_database();
316    if (nip == NULL)
317        return NSS_STATUS_UNAVAIL;
318    if (fct_start == NULL)
319        fct_start = __nss_lookup_function(nip, fct_name);
320    fct.ptr = fct_start;
321    do {
322        if (fct.ptr == NULL)
323            status = NSS_STATUS_UNAVAIL;
324        else
325            status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
326        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
327            break;
328    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
329    if (status != NSS_STATUS_SUCCESS)
330        return status;
331
332    status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
333    if (status != NSS_STATUS_SUCCESS)
334        return status;
335
336    if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
337        NSS_STATUS_SUCCESS)
338        pwd->pw_gid = 65534 /* nogroup */;
339    return NSS_STATUS_SUCCESS;
340}
341
342enum nss_status
343_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
344                         char *buffer, size_t buflen, int *errnop)
345{
346    static const char *fct_name = "getpwuid_r";
347    static void *fct_start = NULL;
348    enum nss_status status;
349    service_user *nip;
350    union {
351        enum nss_status (*l)(uid_t uid, struct passwd *pwd,
352                             char *buffer, size_t buflen, int *errnop);
353        void *ptr;
354    } fct;
355    int group_errno;
356
357    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
358    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
359        return NSS_STATUS_UNAVAIL;
360
361    nip = nss_passwd_nonlocal_database();
362    if (nip == NULL)
363        return NSS_STATUS_UNAVAIL;
364    if (fct_start == NULL)
365        fct_start = __nss_lookup_function(nip, fct_name);
366    fct.ptr = fct_start;
367    do {
368        if (fct.ptr == NULL)
369            status = NSS_STATUS_UNAVAIL;
370        else
371            status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
372        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
373            break;
374    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
375    if (status != NSS_STATUS_SUCCESS)
376        return status;
377
378    status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
379    if (status != NSS_STATUS_SUCCESS)
380        return status;
381
382    if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
383        NSS_STATUS_SUCCESS)
384        pwd->pw_gid = 65534 /* nogroup */;
385    return NSS_STATUS_SUCCESS;
386}
Note: See TracBrowser for help on using the repository browser.