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