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

Revision 24122, 11.1 KB checked in by andersk, 15 years ago (diff)
In libnss-nonlocal: * New upstream version. - Disallow numeric nonlocal user/group names that look like local uid/gids.
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    size_t 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_passwd(const char *user, struct passwd *pwd, int *errnop)
129{
130    enum nss_status status = NSS_STATUS_SUCCESS;
131    int old_errno = errno;
132    char *end;
133    unsigned long uid;
134
135    errno = 0;
136    uid = strtoul(pwd->pw_name, &end, 10);
137    if (errno == 0 && *end == '\0' && (uid_t)uid == uid)
138        status = check_nonlocal_uid(user, uid, errnop);
139    errno = old_errno;
140    if (status != NSS_STATUS_SUCCESS)
141        return status;
142
143    return check_nonlocal_uid(user, pwd->pw_uid, errnop);
144}
145
146enum nss_status
147check_nonlocal_user(const char *user, int *errnop)
148{
149    static const char *fct_name = "getpwnam_r";
150    static service_user *startp = NULL;
151    static void *fct_start = NULL;
152    enum nss_status status;
153    service_user *nip;
154    union {
155        enum nss_status (*l)(const char *name, struct passwd *pwd,
156                             char *buffer, size_t buflen, int *errnop);
157        void *ptr;
158    } fct;
159    struct passwd pwbuf;
160    int old_errno = errno;
161
162    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
163    char *buf = malloc(buflen);
164    if (buf == NULL) {
165        *errnop = ENOMEM;
166        errno = old_errno;
167        return NSS_STATUS_TRYAGAIN;
168    }
169
170    if (fct_start == NULL &&
171        __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
172        free(buf);
173        return NSS_STATUS_UNAVAIL;
174    }
175    nip = startp;
176    fct.ptr = fct_start;
177    do {
178    morebuf:
179        if (fct.l == _nss_nonlocal_getpwnam_r)
180            status = NSS_STATUS_NOTFOUND;
181        else
182            status = DL_CALL_FCT(fct.l, (user, &pwbuf, buf, buflen, errnop));
183        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
184            free(buf);
185            buflen *= 2;
186            buf = malloc(buflen);
187            if (buf == NULL) {
188                *errnop = ENOMEM;
189                errno = old_errno;
190                return NSS_STATUS_TRYAGAIN;
191            }
192            goto morebuf;
193        }
194    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
195
196    if (status == NSS_STATUS_SUCCESS)
197        status = NSS_STATUS_NOTFOUND;
198    else if (status != NSS_STATUS_TRYAGAIN)
199        status = NSS_STATUS_SUCCESS;
200
201    free(buf);
202    return status;
203}
204
205
206static service_user *pwent_nip = NULL;
207static void *pwent_fct_start;
208static union {
209    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
210                         int *errnop);
211    void *ptr;
212} pwent_fct;
213static const char *pwent_fct_name = "getpwent_r";
214
215enum nss_status
216_nss_nonlocal_setpwent(int stayopen)
217{
218    static const char *fct_name = "setpwent";
219    static void *fct_start = NULL;
220    enum nss_status status;
221    service_user *nip;
222    union {
223        enum nss_status (*l)(int stayopen);
224        void *ptr;
225    } fct;
226
227    nip = nss_passwd_nonlocal_database();
228    if (nip == NULL)
229        return NSS_STATUS_UNAVAIL;
230    if (fct_start == NULL)
231        fct_start = __nss_lookup_function(nip, fct_name);
232    fct.ptr = fct_start;
233    do {
234        if (fct.ptr == NULL)
235            status = NSS_STATUS_UNAVAIL;
236        else
237            status = DL_CALL_FCT(fct.l, (stayopen));
238    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
239    if (status != NSS_STATUS_SUCCESS)
240        return status;
241
242    pwent_nip = nip;
243    if (pwent_fct_start == NULL)
244        pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
245    pwent_fct.ptr = pwent_fct_start;
246    return NSS_STATUS_SUCCESS;
247}
248
249enum nss_status
250_nss_nonlocal_endpwent(void)
251{
252    static const char *fct_name = "endpwent";
253    static void *fct_start = NULL;
254    enum nss_status status;
255    service_user *nip;
256    union {
257        enum nss_status (*l)(void);
258        void *ptr;
259    } fct;
260
261    pwent_nip = NULL;
262
263    nip = nss_passwd_nonlocal_database();
264    if (nip == NULL)
265        return NSS_STATUS_UNAVAIL;
266    if (fct_start == NULL)
267        fct_start = __nss_lookup_function(nip, fct_name);
268    fct.ptr = fct_start;
269    do {
270        if (fct.ptr == NULL)
271            status = NSS_STATUS_UNAVAIL;
272        else
273            status = DL_CALL_FCT(fct.l, ());
274    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
275    return status;
276}
277
278enum nss_status
279_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
280                         int *errnop)
281{
282    enum nss_status status;
283
284    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
285    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
286        return NSS_STATUS_UNAVAIL;
287
288    if (pwent_nip == NULL) {
289        status = _nss_nonlocal_setpwent(0);
290        if (status != NSS_STATUS_SUCCESS)
291            return status;
292    }
293    do {
294        if (pwent_fct.ptr == NULL)
295            status = NSS_STATUS_UNAVAIL;
296        else {
297            int nonlocal_errno;
298            do
299                status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
300            while (status == NSS_STATUS_SUCCESS &&
301                   check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
302        }
303        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
304            return status;
305
306        if (status == NSS_STATUS_SUCCESS)
307            return NSS_STATUS_SUCCESS;
308    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
309
310    pwent_nip = NULL;
311    return NSS_STATUS_NOTFOUND;
312}
313
314
315enum nss_status
316_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
317                         char *buffer, size_t buflen, int *errnop)
318{
319    static const char *fct_name = "getpwnam_r";
320    static void *fct_start = NULL;
321    enum nss_status status;
322    service_user *nip;
323    union {
324        enum nss_status (*l)(const char *name, struct passwd *pwd,
325                             char *buffer, size_t buflen, int *errnop);
326        void *ptr;
327    } fct;
328    int group_errno;
329
330    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
331    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
332        return NSS_STATUS_UNAVAIL;
333
334    nip = nss_passwd_nonlocal_database();
335    if (nip == NULL)
336        return NSS_STATUS_UNAVAIL;
337    if (fct_start == NULL)
338        fct_start = __nss_lookup_function(nip, fct_name);
339    fct.ptr = fct_start;
340    do {
341        if (fct.ptr == NULL)
342            status = NSS_STATUS_UNAVAIL;
343        else
344            status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
345        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
346            break;
347    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
348    if (status != NSS_STATUS_SUCCESS)
349        return status;
350
351    if (strcmp(name, pwd->pw_name) != 0) {
352        syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
353        return NSS_STATUS_NOTFOUND;
354    }
355
356    status = check_nonlocal_passwd(name, pwd, errnop);
357    if (status != NSS_STATUS_SUCCESS)
358        return status;
359
360    if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
361        NSS_STATUS_SUCCESS)
362        pwd->pw_gid = 65534 /* nogroup */;
363    return NSS_STATUS_SUCCESS;
364}
365
366enum nss_status
367_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
368                         char *buffer, size_t buflen, int *errnop)
369{
370    static const char *fct_name = "getpwuid_r";
371    static void *fct_start = NULL;
372    enum nss_status status;
373    service_user *nip;
374    union {
375        enum nss_status (*l)(uid_t uid, struct passwd *pwd,
376                             char *buffer, size_t buflen, int *errnop);
377        void *ptr;
378    } fct;
379    int group_errno;
380
381    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
382    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
383        return NSS_STATUS_UNAVAIL;
384
385    nip = nss_passwd_nonlocal_database();
386    if (nip == NULL)
387        return NSS_STATUS_UNAVAIL;
388    if (fct_start == NULL)
389        fct_start = __nss_lookup_function(nip, fct_name);
390    fct.ptr = fct_start;
391    do {
392        if (fct.ptr == NULL)
393            status = NSS_STATUS_UNAVAIL;
394        else
395            status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
396        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
397            break;
398    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
399    if (status != NSS_STATUS_SUCCESS)
400        return status;
401
402    status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
403    if (status != NSS_STATUS_SUCCESS)
404        return status;
405
406    if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
407        NSS_STATUS_SUCCESS)
408        pwd->pw_gid = 65534 /* nogroup */;
409    return NSS_STATUS_SUCCESS;
410}
Note: See TracBrowser for help on using the repository browser.