]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Add copyright notice to nonlocal.h
[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     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
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         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
143 enum nss_status
144 check_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
203 static service_user *pwent_nip = NULL;
204 static void *pwent_fct_start;
205 static union {
206     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
207                          int *errnop);
208     void *ptr;
209 } pwent_fct;
210 static const char *pwent_fct_name = "getpwent_r";
211
212 enum 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
246 enum 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
275 enum 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
312 enum 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
363 enum 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 }
This page took 0.078075 seconds and 5 git commands to generate.