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