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

Revision 24585, 10.9 KB checked in by andersk, 14 years ago (diff)
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#include <sys/types.h>
29#include <unistd.h>
30#include <stdlib.h>
31#include <stdint.h>
32#include <string.h>
33#include <dlfcn.h>
34#include <stdio.h>
35#include <syslog.h>
36#include <errno.h>
37#include <pwd.h>
38#include <grp.h>
39#include <nss.h>
40#include "nsswitch-internal.h"
41#include "nonlocal.h"
42
43
44enum nss_status
45_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
46                         char *buffer, size_t buflen, int *errnop);
47enum nss_status
48_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
49                         char *buffer, size_t buflen, int *errnop);
50
51
52static service_user *
53nss_passwd_nonlocal_database(void)
54{
55    static service_user *nip = NULL;
56    if (nip == NULL)
57        __nss_database_lookup("passwd_nonlocal", NULL, "", &nip);
58
59    return nip;
60}
61
62
63enum nss_status
64check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
65{
66    static const char *fct_name = "getpwuid_r";
67    static service_user *startp = NULL;
68    static void *fct_start = NULL;
69    enum nss_status status;
70    service_user *nip;
71    union {
72        enum nss_status (*l)(uid_t uid, struct passwd *pwd,
73                             char *buffer, size_t buflen, int *errnop);
74        void *ptr;
75    } fct;
76    struct passwd pwbuf;
77    int old_errno = errno;
78
79    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
80    char *buf = malloc(buflen);
81    if (buf == NULL) {
82        *errnop = ENOMEM;
83        errno = old_errno;
84        return NSS_STATUS_TRYAGAIN;
85    }
86
87    if (fct_start == NULL &&
88        __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
89        free(buf);
90        return NSS_STATUS_UNAVAIL;
91    }
92    nip = startp;
93    fct.ptr = fct_start;
94    do {
95    morebuf:
96        if (fct.l == _nss_nonlocal_getpwuid_r)
97            status = NSS_STATUS_NOTFOUND;
98        else
99            status = DL_CALL_FCT(fct.l, (uid, &pwbuf, buf, buflen, errnop));
100        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
101            free(buf);
102            buflen *= 2;
103            buf = malloc(buflen);
104            if (buf == NULL) {
105                *errnop = ENOMEM;
106                errno = old_errno;
107                return NSS_STATUS_TRYAGAIN;
108            }
109            goto morebuf;
110        }
111    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
112
113    if (status == NSS_STATUS_SUCCESS) {
114        syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
115        status = NSS_STATUS_NOTFOUND;
116    } else if (status != NSS_STATUS_TRYAGAIN) {
117        status = NSS_STATUS_SUCCESS;
118    }
119
120    free(buf);
121    return status;
122}
123
124enum nss_status
125check_nonlocal_passwd(const char *user, struct passwd *pwd, int *errnop)
126{
127    enum nss_status status = NSS_STATUS_SUCCESS;
128    int old_errno = errno;
129    char *end;
130    unsigned long uid;
131
132    errno = 0;
133    uid = strtoul(pwd->pw_name, &end, 10);
134    if (errno == 0 && *end == '\0' && (uid_t)uid == uid)
135        status = check_nonlocal_uid(user, uid, errnop);
136    errno = old_errno;
137    if (status != NSS_STATUS_SUCCESS)
138        return status;
139
140    return check_nonlocal_uid(user, pwd->pw_uid, errnop);
141}
142
143enum nss_status
144check_nonlocal_user(const char *user, int *errnop)
145{
146    static const char *fct_name = "getpwnam_r";
147    static service_user *startp = NULL;
148    static void *fct_start = NULL;
149    enum nss_status status;
150    service_user *nip;
151    union {
152        enum nss_status (*l)(const char *name, struct passwd *pwd,
153                             char *buffer, size_t buflen, int *errnop);
154        void *ptr;
155    } fct;
156    struct passwd pwbuf;
157    int old_errno = errno;
158
159    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
160    char *buf = malloc(buflen);
161    if (buf == NULL) {
162        *errnop = ENOMEM;
163        errno = old_errno;
164        return NSS_STATUS_TRYAGAIN;
165    }
166
167    if (fct_start == NULL &&
168        __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
169        free(buf);
170        return NSS_STATUS_UNAVAIL;
171    }
172    nip = startp;
173    fct.ptr = fct_start;
174    do {
175    morebuf:
176        if (fct.l == _nss_nonlocal_getpwnam_r)
177            status = NSS_STATUS_NOTFOUND;
178        else
179            status = DL_CALL_FCT(fct.l, (user, &pwbuf, buf, buflen, errnop));
180        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) {
181            free(buf);
182            buflen *= 2;
183            buf = malloc(buflen);
184            if (buf == NULL) {
185                *errnop = ENOMEM;
186                errno = old_errno;
187                return NSS_STATUS_TRYAGAIN;
188            }
189            goto morebuf;
190        }
191    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
192
193    if (status == NSS_STATUS_SUCCESS)
194        status = NSS_STATUS_NOTFOUND;
195    else if (status != NSS_STATUS_TRYAGAIN)
196        status = NSS_STATUS_SUCCESS;
197
198    free(buf);
199    return status;
200}
201
202
203static service_user *pwent_nip = NULL;
204static void *pwent_fct_start;
205static union {
206    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
207                         int *errnop);
208    void *ptr;
209} pwent_fct;
210static const char *pwent_fct_name = "getpwent_r";
211
212enum nss_status
213_nss_nonlocal_setpwent(int stayopen)
214{
215    static const char *fct_name = "setpwent";
216    static void *fct_start = NULL;
217    enum nss_status status;
218    service_user *nip;
219    union {
220        enum nss_status (*l)(int stayopen);
221        void *ptr;
222    } fct;
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, (stayopen));
235    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
236    if (status != NSS_STATUS_SUCCESS)
237        return status;
238
239    pwent_nip = nip;
240    if (pwent_fct_start == NULL)
241        pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
242    pwent_fct.ptr = pwent_fct_start;
243    return NSS_STATUS_SUCCESS;
244}
245
246enum nss_status
247_nss_nonlocal_endpwent(void)
248{
249    static const char *fct_name = "endpwent";
250    static void *fct_start = NULL;
251    enum nss_status status;
252    service_user *nip;
253    union {
254        enum nss_status (*l)(void);
255        void *ptr;
256    } fct;
257
258    pwent_nip = NULL;
259
260    nip = nss_passwd_nonlocal_database();
261    if (nip == NULL)
262        return NSS_STATUS_UNAVAIL;
263    if (fct_start == NULL)
264        fct_start = __nss_lookup_function(nip, fct_name);
265    fct.ptr = fct_start;
266    do {
267        if (fct.ptr == NULL)
268            status = NSS_STATUS_UNAVAIL;
269        else
270            status = DL_CALL_FCT(fct.l, ());
271    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
272    return status;
273}
274
275enum nss_status
276_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
277                         int *errnop)
278{
279    enum nss_status status;
280
281    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
282    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
283        return NSS_STATUS_UNAVAIL;
284
285    if (pwent_nip == NULL) {
286        status = _nss_nonlocal_setpwent(0);
287        if (status != NSS_STATUS_SUCCESS)
288            return status;
289    }
290    do {
291        if (pwent_fct.ptr == NULL)
292            status = NSS_STATUS_UNAVAIL;
293        else {
294            int nonlocal_errno;
295            do
296                status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
297            while (status == NSS_STATUS_SUCCESS &&
298                   check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
299        }
300        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
301            return status;
302
303        if (status == NSS_STATUS_SUCCESS)
304            return NSS_STATUS_SUCCESS;
305    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
306
307    pwent_nip = NULL;
308    return NSS_STATUS_NOTFOUND;
309}
310
311
312enum nss_status
313_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
314                         char *buffer, size_t buflen, int *errnop)
315{
316    static const char *fct_name = "getpwnam_r";
317    static void *fct_start = NULL;
318    enum nss_status status;
319    service_user *nip;
320    union {
321        enum nss_status (*l)(const char *name, struct passwd *pwd,
322                             char *buffer, size_t buflen, int *errnop);
323        void *ptr;
324    } fct;
325    int group_errno;
326
327    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
328    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
329        return NSS_STATUS_UNAVAIL;
330
331    nip = nss_passwd_nonlocal_database();
332    if (nip == NULL)
333        return NSS_STATUS_UNAVAIL;
334    if (fct_start == NULL)
335        fct_start = __nss_lookup_function(nip, fct_name);
336    fct.ptr = fct_start;
337    do {
338        if (fct.ptr == NULL)
339            status = NSS_STATUS_UNAVAIL;
340        else
341            status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
342        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
343            break;
344    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
345    if (status != NSS_STATUS_SUCCESS)
346        return status;
347
348    if (strcmp(name, pwd->pw_name) != 0) {
349        syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
350        return NSS_STATUS_NOTFOUND;
351    }
352
353    status = check_nonlocal_passwd(name, pwd, errnop);
354    if (status != NSS_STATUS_SUCCESS)
355        return status;
356
357    if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
358        NSS_STATUS_SUCCESS)
359        pwd->pw_gid = 65534 /* nogroup */;
360    return NSS_STATUS_SUCCESS;
361}
362
363enum nss_status
364_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
365                         char *buffer, size_t buflen, int *errnop)
366{
367    static const char *fct_name = "getpwuid_r";
368    static void *fct_start = NULL;
369    enum nss_status status;
370    service_user *nip;
371    union {
372        enum nss_status (*l)(uid_t uid, struct passwd *pwd,
373                             char *buffer, size_t buflen, int *errnop);
374        void *ptr;
375    } fct;
376    int group_errno;
377
378    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
379    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
380        return NSS_STATUS_UNAVAIL;
381
382    nip = nss_passwd_nonlocal_database();
383    if (nip == NULL)
384        return NSS_STATUS_UNAVAIL;
385    if (fct_start == NULL)
386        fct_start = __nss_lookup_function(nip, fct_name);
387    fct.ptr = fct_start;
388    do {
389        if (fct.ptr == NULL)
390            status = NSS_STATUS_UNAVAIL;
391        else
392            status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
393        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
394            break;
395    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
396    if (status != NSS_STATUS_SUCCESS)
397        return status;
398
399    if (uid != pwd->pw_uid) {
400        syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
401        return NSS_STATUS_NOTFOUND;
402    }
403
404    status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
405    if (status != NSS_STATUS_SUCCESS)
406        return status;
407
408    if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
409        NSS_STATUS_SUCCESS)
410        pwd->pw_gid = 65534 /* nogroup */;
411    return NSS_STATUS_SUCCESS;
412}
Note: See TracBrowser for help on using the repository browser.