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