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