]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Skip the nonlocal handler, so we can expunge the magic buflen hack.
[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     int 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         if (fct.l == _nss_nonlocal_getpwuid_r)
99             status = NSS_STATUS_NOTFOUND;
100         else
101             status = DL_CALL_FCT(fct.l, (uid, &pwbuf, buf, buflen, errnop));
102         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
103             break;
104     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
105
106     if (status == NSS_STATUS_SUCCESS) {
107         syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
108         status = NSS_STATUS_NOTFOUND;
109     } else if (status != NSS_STATUS_TRYAGAIN) {
110         status = NSS_STATUS_SUCCESS;
111     }
112
113     free(buf);
114     return status;
115 }
116
117 enum nss_status
118 check_nonlocal_user(const char *user, int *errnop)
119 {
120     static const char *fct_name = "getpwnam_r";
121     static service_user *startp = NULL;
122     static void *fct_start = NULL;
123     enum nss_status status;
124     service_user *nip;
125     union {
126         enum nss_status (*l)(const char *name, struct passwd *pwd,
127                              char *buffer, size_t buflen, int *errnop);
128         void *ptr;
129     } fct;
130     struct passwd pwbuf;
131     int old_errno = errno;
132
133     int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
134     char *buf = malloc(buflen);
135     if (buf == NULL) {
136         *errnop = ENOMEM;
137         errno = old_errno;
138         return NSS_STATUS_TRYAGAIN;
139     }
140
141     if (fct_start == NULL &&
142         __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
143         free(buf);
144         return NSS_STATUS_UNAVAIL;
145     }
146     nip = startp;
147     fct.ptr = fct_start;
148     do {
149         if (fct.l == _nss_nonlocal_getpwnam_r)
150             status = NSS_STATUS_NOTFOUND;
151         else
152             status = DL_CALL_FCT(fct.l, (user, &pwbuf, buf, buflen, errnop));
153         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
154             break;
155     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
156
157     if (status == NSS_STATUS_SUCCESS)
158         status = NSS_STATUS_NOTFOUND;
159     else if (status != NSS_STATUS_TRYAGAIN)
160         status = NSS_STATUS_SUCCESS;
161
162     free(buf);
163     return status;
164 }
165
166
167 static service_user *pwent_nip = NULL;
168 static void *pwent_fct_start;
169 static union {
170     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
171                          int *errnop);
172     void *ptr;
173 } pwent_fct;
174 static const char *pwent_fct_name = "getpwent_r";
175
176 enum nss_status
177 _nss_nonlocal_setpwent(int stayopen)
178 {
179     static const char *fct_name = "setpwent";
180     static void *fct_start = NULL;
181     enum nss_status status;
182     service_user *nip;
183     union {
184         enum nss_status (*l)(int stayopen);
185         void *ptr;
186     } fct;
187
188     nip = nss_passwd_nonlocal_database();
189     if (nip == NULL)
190         return NSS_STATUS_UNAVAIL;
191     if (fct_start == NULL)
192         fct_start = __nss_lookup_function(nip, fct_name);
193     fct.ptr = fct_start;
194     do {
195         if (fct.ptr == NULL)
196             status = NSS_STATUS_UNAVAIL;
197         else
198             status = DL_CALL_FCT(fct.l, (stayopen));
199     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
200     if (status != NSS_STATUS_SUCCESS)
201         return status;
202
203     pwent_nip = nip;
204     if (pwent_fct_start == NULL)
205         pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
206     pwent_fct.ptr = pwent_fct_start;
207     return NSS_STATUS_SUCCESS;
208 }
209
210 enum nss_status
211 _nss_nonlocal_endpwent(void)
212 {
213     static const char *fct_name = "endpwent";
214     static void *fct_start = NULL;
215     enum nss_status status;
216     service_user *nip;
217     union {
218         enum nss_status (*l)(void);
219         void *ptr;
220     } fct;
221
222     pwent_nip = NULL;
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, ());
235     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
236     return status;
237 }
238
239 enum nss_status
240 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
241                          int *errnop)
242 {
243     enum nss_status status;
244
245     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
246     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
247         return NSS_STATUS_UNAVAIL;
248
249     if (pwent_nip == NULL) {
250         status = _nss_nonlocal_setpwent(0);
251         if (status != NSS_STATUS_SUCCESS)
252             return status;
253     }
254     do {
255         if (pwent_fct.ptr == NULL)
256             status = NSS_STATUS_UNAVAIL;
257         else {
258             int nonlocal_errno;
259             do
260                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
261             while (status == NSS_STATUS_SUCCESS &&
262                    check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
263         }
264         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
265             return status;
266
267         if (status == NSS_STATUS_SUCCESS)
268             return NSS_STATUS_SUCCESS;
269     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
270
271     pwent_nip = NULL;
272     return NSS_STATUS_NOTFOUND;
273 }
274
275
276 enum nss_status
277 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
278                          char *buffer, size_t buflen, int *errnop)
279 {
280     static const char *fct_name = "getpwnam_r";
281     static void *fct_start = NULL;
282     enum nss_status status;
283     service_user *nip;
284     union {
285         enum nss_status (*l)(const char *name, struct passwd *pwd,
286                              char *buffer, size_t buflen, int *errnop);
287         void *ptr;
288     } fct;
289     int group_errno;
290
291     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
292     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
293         return NSS_STATUS_UNAVAIL;
294
295     nip = nss_passwd_nonlocal_database();
296     if (nip == NULL)
297         return NSS_STATUS_UNAVAIL;
298     if (fct_start == NULL)
299         fct_start = __nss_lookup_function(nip, fct_name);
300     fct.ptr = fct_start;
301     do {
302         if (fct.ptr == NULL)
303             status = NSS_STATUS_UNAVAIL;
304         else
305             status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
306         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
307             break;
308     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
309     if (status != NSS_STATUS_SUCCESS)
310         return status;
311
312     status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
313     if (status != NSS_STATUS_SUCCESS)
314         return status;
315
316     if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
317         NSS_STATUS_SUCCESS)
318         pwd->pw_gid = 65534 /* nogroup */;
319     return NSS_STATUS_SUCCESS;
320 }
321
322 enum nss_status
323 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
324                          char *buffer, size_t buflen, int *errnop)
325 {
326     static const char *fct_name = "getpwuid_r";
327     static void *fct_start = NULL;
328     enum nss_status status;
329     service_user *nip;
330     union {
331         enum nss_status (*l)(uid_t uid, struct passwd *pwd,
332                              char *buffer, size_t buflen, int *errnop);
333         void *ptr;
334     } fct;
335     int group_errno;
336
337     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
338     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
339         return NSS_STATUS_UNAVAIL;
340
341     nip = nss_passwd_nonlocal_database();
342     if (nip == NULL)
343         return NSS_STATUS_UNAVAIL;
344     if (fct_start == NULL)
345         fct_start = __nss_lookup_function(nip, fct_name);
346     fct.ptr = fct_start;
347     do {
348         if (fct.ptr == NULL)
349             status = NSS_STATUS_UNAVAIL;
350         else
351             status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
352         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
353             break;
354     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
355     if (status != NSS_STATUS_SUCCESS)
356         return status;
357
358     status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
359     if (status != NSS_STATUS_SUCCESS)
360         return status;
361
362     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
363         NSS_STATUS_SUCCESS)
364         pwd->pw_gid = 65534 /* nogroup */;
365     return NSS_STATUS_SUCCESS;
366 }
This page took 0.555705 seconds and 5 git commands to generate.