]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Replace recursive nss calls with explicit iteration over the nss chain.
[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 #define MAGIC_LOCAL_PW_BUFLEN (sysconf(_SC_GETPW_R_SIZE_MAX) + 7)
47
48
49 static service_user *
50 nss_passwd_nonlocal_database(void)
51 {
52     static service_user *nip = NULL;
53     if (nip == NULL)
54         __nss_database_lookup("passwd_nonlocal", NULL, "", &nip);
55
56     return nip;
57 }
58
59
60 enum nss_status
61 check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
62 {
63     static const char *fct_name = "getpwuid_r";
64     static service_user *startp = NULL;
65     static void *fct_start = NULL;
66     enum nss_status status;
67     service_user *nip;
68     union {
69         enum nss_status (*l)(uid_t uid, struct passwd *pwd,
70                              char *buffer, size_t buflen, int *errnop);
71         void *ptr;
72     } fct;
73     struct passwd pwbuf;
74     int old_errno = errno;
75     int buflen = MAGIC_LOCAL_PW_BUFLEN;
76     char *buf = malloc(buflen);
77     if (buf == NULL) {
78         *errnop = ENOMEM;
79         errno = old_errno;
80         return NSS_STATUS_TRYAGAIN;
81     }
82
83     if (fct_start == NULL &&
84         __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
85         free(buf);
86         return NSS_STATUS_UNAVAIL;
87     }
88     nip = startp;
89     fct.ptr = fct_start;
90     do {
91         status = DL_CALL_FCT(fct.l, (uid, &pwbuf, buf, buflen, errnop));
92         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
93             break;
94     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
95
96     if (status == NSS_STATUS_SUCCESS) {
97         syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
98         status = NSS_STATUS_NOTFOUND;
99     } else if (status != NSS_STATUS_TRYAGAIN) {
100         status = NSS_STATUS_SUCCESS;
101     }
102
103     free(buf);
104     return status;
105 }
106
107 enum nss_status
108 check_nonlocal_user(const char *user, int *errnop)
109 {
110     static const char *fct_name = "getpwnam_r";
111     static service_user *startp = NULL;
112     static void *fct_start = NULL;
113     enum nss_status status;
114     service_user *nip;
115     union {
116         enum nss_status (*l)(const char *name, struct passwd *pwd,
117                              char *buffer, size_t buflen, int *errnop);
118         void *ptr;
119     } fct;
120     struct passwd pwbuf;
121     int old_errno = errno;
122     int buflen = MAGIC_LOCAL_PW_BUFLEN;
123     char *buf = malloc(buflen);
124     if (buf == NULL) {
125         *errnop = ENOMEM;
126         errno = old_errno;
127         return NSS_STATUS_TRYAGAIN;
128     }
129
130     if (fct_start == NULL &&
131         __nss_passwd_lookup(&startp, fct_name, &fct_start) != 0) {
132         free(buf);
133         return NSS_STATUS_UNAVAIL;
134     }
135     nip = startp;
136     fct.ptr = fct_start;
137     do {
138         status = DL_CALL_FCT(fct.l, (user, &pwbuf, buf, buflen, errnop));
139         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
140             break;
141     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
142
143     if (status == NSS_STATUS_SUCCESS)
144         status = NSS_STATUS_NOTFOUND;
145     else if (status != NSS_STATUS_TRYAGAIN)
146         status = NSS_STATUS_SUCCESS;
147
148     free(buf);
149     return status;
150 }
151
152
153 static service_user *pwent_nip = NULL;
154 static void *pwent_fct_start;
155 static union {
156     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
157                          int *errnop);
158     void *ptr;
159 } pwent_fct;
160 static const char *pwent_fct_name = "getpwent_r";
161
162 enum nss_status
163 _nss_nonlocal_setpwent(int stayopen)
164 {
165     static const char *fct_name = "setpwent";
166     static void *fct_start = NULL;
167     enum nss_status status;
168     service_user *nip;
169     union {
170         enum nss_status (*l)(int stayopen);
171         void *ptr;
172     } fct;
173
174     nip = nss_passwd_nonlocal_database();
175     if (nip == NULL)
176         return NSS_STATUS_UNAVAIL;
177     if (fct_start == NULL)
178         fct_start = __nss_lookup_function(nip, fct_name);
179     fct.ptr = fct_start;
180     do {
181         if (fct.ptr == NULL)
182             status = NSS_STATUS_UNAVAIL;
183         else
184             status = DL_CALL_FCT(fct.l, (stayopen));
185     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
186     if (status != NSS_STATUS_SUCCESS)
187         return status;
188
189     pwent_nip = nip;
190     if (pwent_fct_start == NULL)
191         pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
192     pwent_fct.ptr = pwent_fct_start;
193     return NSS_STATUS_SUCCESS;
194 }
195
196 enum nss_status
197 _nss_nonlocal_endpwent(void)
198 {
199     static const char *fct_name = "endpwent";
200     static void *fct_start = NULL;
201     enum nss_status status;
202     service_user *nip;
203     union {
204         enum nss_status (*l)(void);
205         void *ptr;
206     } fct;
207
208     pwent_nip = NULL;
209
210     nip = nss_passwd_nonlocal_database();
211     if (nip == NULL)
212         return NSS_STATUS_UNAVAIL;
213     if (fct_start == NULL)
214         fct_start = __nss_lookup_function(nip, fct_name);
215     fct.ptr = fct_start;
216     do {
217         if (fct.ptr == NULL)
218             status = NSS_STATUS_UNAVAIL;
219         else
220             status = DL_CALL_FCT(fct.l, ());
221     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
222     return status;
223 }
224
225 enum nss_status
226 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
227                          int *errnop)
228 {
229     enum nss_status status;
230
231     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
232     if (buflen == MAGIC_LOCAL_PW_BUFLEN ||
233         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
234         return NSS_STATUS_UNAVAIL;
235
236     if (pwent_nip == NULL) {
237         status = _nss_nonlocal_setpwent(0);
238         if (status != NSS_STATUS_SUCCESS)
239             return status;
240     }
241     do {
242         if (pwent_fct.ptr == NULL)
243             status = NSS_STATUS_UNAVAIL;
244         else {
245             int nonlocal_errno;
246             do
247                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
248             while (status == NSS_STATUS_SUCCESS &&
249                    check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
250         }
251         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
252             return status;
253
254         if (status == NSS_STATUS_SUCCESS)
255             return NSS_STATUS_SUCCESS;
256     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
257
258     pwent_nip = NULL;
259     return NSS_STATUS_NOTFOUND;
260 }
261
262
263 enum nss_status
264 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
265                          char *buffer, size_t buflen, int *errnop)
266 {
267     static const char *fct_name = "getpwnam_r";
268     static void *fct_start = NULL;
269     enum nss_status status;
270     service_user *nip;
271     union {
272         enum nss_status (*l)(const char *name, struct passwd *pwd,
273                              char *buffer, size_t buflen, int *errnop);
274         void *ptr;
275     } fct;
276     int group_errno;
277
278     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
279     if (buflen == MAGIC_LOCAL_PW_BUFLEN ||
280         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
281         return NSS_STATUS_UNAVAIL;
282
283     nip = nss_passwd_nonlocal_database();
284     if (nip == NULL)
285         return NSS_STATUS_UNAVAIL;
286     if (fct_start == NULL)
287         fct_start = __nss_lookup_function(nip, fct_name);
288     fct.ptr = fct_start;
289     do {
290         if (fct.ptr == NULL)
291             status = NSS_STATUS_UNAVAIL;
292         else
293             status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
294         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
295             break;
296     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
297     if (status != NSS_STATUS_SUCCESS)
298         return status;
299
300     status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
301     if (status != NSS_STATUS_SUCCESS)
302         return status;
303
304     if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
305         NSS_STATUS_SUCCESS)
306         pwd->pw_gid = 65534 /* nogroup */;
307     return NSS_STATUS_SUCCESS;
308 }
309
310 enum nss_status
311 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
312                          char *buffer, size_t buflen, int *errnop)
313 {
314     static const char *fct_name = "getpwuid_r";
315     static void *fct_start = NULL;
316     enum nss_status status;
317     service_user *nip;
318     union {
319         enum nss_status (*l)(uid_t uid, struct passwd *pwd,
320                              char *buffer, size_t buflen, int *errnop);
321         void *ptr;
322     } fct;
323     int group_errno;
324
325     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
326     if (buflen == MAGIC_LOCAL_PW_BUFLEN ||
327         (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
328         return NSS_STATUS_UNAVAIL;
329
330     nip = nss_passwd_nonlocal_database();
331     if (nip == NULL)
332         return NSS_STATUS_UNAVAIL;
333     if (fct_start == NULL)
334         fct_start = __nss_lookup_function(nip, fct_name);
335     fct.ptr = fct_start;
336     do {
337         if (fct.ptr == NULL)
338             status = NSS_STATUS_UNAVAIL;
339         else
340             status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
341         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
342             break;
343     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
344     if (status != NSS_STATUS_SUCCESS)
345         return status;
346
347     status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
348     if (status != NSS_STATUS_SUCCESS)
349         return status;
350
351     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
352         NSS_STATUS_SUCCESS)
353         pwd->pw_gid = 65534 /* nogroup */;
354     return NSS_STATUS_SUCCESS;
355 }
This page took 0.340934 seconds and 5 git commands to generate.