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