]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
bd9d9ad206e0b700b334fa2ca606c7e9ba9a5445
[nss_nonlocal.git] / nonlocal-passwd.c
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
44 enum nss_status
45 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
46                          char *buffer, size_t buflen, int *errnop);
47 enum nss_status
48 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
49                          char *buffer, size_t buflen, int *errnop);
50
51
52 static service_user *
53 nss_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
63 enum nss_status
64 check_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     errno = old_errno;
82     if (buf == NULL) {
83         *errnop = ENOMEM;
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             errno = old_errno;
105             if (buf == NULL) {
106                 *errnop = ENOMEM;
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
124 enum nss_status
125 check_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         errno = old_errno;
136         status = check_nonlocal_uid(user, uid, errnop);
137     } else {
138         errno = old_errno;
139     }
140     if (status != NSS_STATUS_SUCCESS)
141         return status;
142
143     return check_nonlocal_uid(user, pwd->pw_uid, errnop);
144 }
145
146 enum nss_status
147 check_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     errno = old_errno;
165     if (buf == NULL) {
166         *errnop = ENOMEM;
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             errno = old_errno;
188             if (buf == NULL) {
189                 *errnop = ENOMEM;
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
206 static service_user *pwent_nip = NULL;
207 static void *pwent_fct_start;
208 static union {
209     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
210                          int *errnop);
211     void *ptr;
212 } pwent_fct;
213 static const char *pwent_fct_name = "getpwent_r";
214
215 enum 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
249 enum 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
278 enum 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
315 enum 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
366 enum 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     if (uid != pwd->pw_uid) {
403         syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
404         return NSS_STATUS_NOTFOUND;
405     }
406
407     status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
408     if (status != NSS_STATUS_SUCCESS)
409         return status;
410
411     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
412         NSS_STATUS_SUCCESS)
413         pwd->pw_gid = 65534 /* nogroup */;
414     return NSS_STATUS_SUCCESS;
415 }
This page took 0.059236 seconds and 3 git commands to generate.