]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Link with pthread to be safe.
[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>
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation
9  * files (the "Software"), to deal in the Software without
10  * restriction, including without limitation the rights to use, copy,
11  * modify, merge, publish, distribute, sublicense, and/or sell copies
12  * of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27
28
29 #define _GNU_SOURCE
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <dlfcn.h>
36 #include <stdio.h>
37 #include <syslog.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <grp.h>
41 #include <nss.h>
42 #include "nsswitch-internal.h"
43 #include "nonlocal.h"
44
45
46 static service_user *
47 nss_passwd_nonlocal_database(void)
48 {
49     static service_user *nip = NULL;
50     if (nip == NULL)
51         __nss_database_lookup("passwd_nonlocal", NULL, "", &nip);
52
53     return nip;
54 }
55
56
57 static __thread int local_only = 0;
58
59 enum nss_status
60 local_getpwuid_r(uid_t uid, struct passwd *pwd,
61                  char *buffer, size_t buflen, int *errnop)
62 {
63     int old_local_only = local_only;
64     int old_errno = errno;
65     int ret;
66     errno = *errnop;
67     local_only = 1;
68
69     ret = getpwuid_r(uid, pwd, buffer, buflen, &pwd);
70
71     local_only = old_local_only;
72     *errnop = errno;
73     errno = old_errno;
74
75     if (pwd != NULL)
76         return NSS_STATUS_SUCCESS;
77     else if (ret == 0)
78         return NSS_STATUS_NOTFOUND;
79     else
80         return NSS_STATUS_TRYAGAIN;
81 }
82
83 enum nss_status
84 check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
85 {
86     struct passwd local_pwd;
87     int local_errno = errno;
88     enum nss_status local_status, status = NSS_STATUS_SUCCESS;
89     int local_buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
90     char *local_buffer = malloc(local_buflen);
91     if (local_buffer == NULL) {
92         *errnop = ENOMEM;
93         errno = local_errno;
94         return NSS_STATUS_TRYAGAIN;
95     }
96     local_errno = 0;
97     local_status = local_getpwuid_r(uid, &local_pwd, local_buffer,
98                                     local_buflen, &local_errno);
99     if (local_status == NSS_STATUS_SUCCESS) {
100         syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, local_pwd.pw_name);
101         status = NSS_STATUS_NOTFOUND;
102     } else if (local_status != NSS_STATUS_NOTFOUND &&
103                local_status != NSS_STATUS_UNAVAIL) {
104         *errnop = local_errno;
105         status = local_status;
106     }
107     free(local_buffer);
108     return status;
109 }
110
111
112 static service_user *pwent_nip = NULL;
113 static void *pwent_fct_start;
114 static union {
115     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
116                          int *errnop);
117     void *ptr;
118 } pwent_fct;
119 static const char *pwent_fct_name = "getpwent_r";
120
121 enum nss_status
122 _nss_nonlocal_setpwent(int stayopen)
123 {
124     static const char *fct_name = "setpwent";
125     static void *fct_start = NULL;
126     enum nss_status status;
127     service_user *nip;
128     union {
129         enum nss_status (*l)(int stayopen);
130         void *ptr;
131     } fct;
132
133     nip = nss_passwd_nonlocal_database();
134     if (nip == NULL)
135         return NSS_STATUS_UNAVAIL;
136     if (fct_start == NULL)
137         fct_start = __nss_lookup_function(nip, fct_name);
138     fct.ptr = fct_start;
139     do {
140         if (fct.ptr == NULL)
141             status = NSS_STATUS_UNAVAIL;
142         else
143             status = DL_CALL_FCT(fct.l, (stayopen));
144     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
145     if (status != NSS_STATUS_SUCCESS)
146         return status;
147
148     pwent_nip = nip;
149     if (pwent_fct_start == NULL)
150         pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
151     pwent_fct.ptr = pwent_fct_start;
152     return NSS_STATUS_SUCCESS;
153 }
154
155 enum nss_status
156 _nss_nonlocal_endpwent(void)
157 {
158     static const char *fct_name = "endpwent";
159     static void *fct_start = NULL;
160     enum nss_status status;
161     service_user *nip;
162     union {
163         enum nss_status (*l)(void);
164         void *ptr;
165     } fct;
166
167     pwent_nip = NULL;
168
169     nip = nss_passwd_nonlocal_database();
170     if (nip == NULL)
171         return NSS_STATUS_UNAVAIL;
172     if (fct_start == NULL)
173         fct_start = __nss_lookup_function(nip, fct_name);
174     fct.ptr = fct_start;
175     do {
176         if (fct.ptr == NULL)
177             status = NSS_STATUS_UNAVAIL;
178         else
179             status = DL_CALL_FCT(fct.l, ());
180     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
181     return status;
182 }
183
184 enum nss_status
185 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
186                          int *errnop)
187 {
188     enum nss_status status;
189     if (pwent_nip == NULL) {
190         status = _nss_nonlocal_setpwent(0);
191         if (status != NSS_STATUS_SUCCESS)
192             return status;
193     }
194     do {
195         if (pwent_fct.ptr == NULL)
196             status = NSS_STATUS_UNAVAIL;
197         else {
198             int nonlocal_errno;
199             do
200                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));       
201             while (status == NSS_STATUS_SUCCESS &&
202                    check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
203         }
204         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
205             return status;
206
207         if (status == NSS_STATUS_SUCCESS)
208             return NSS_STATUS_SUCCESS;
209     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
210
211     pwent_nip = NULL;
212     return NSS_STATUS_NOTFOUND;
213 }
214
215
216 enum nss_status
217 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
218                          char *buffer, size_t buflen, int *errnop)
219 {
220     static const char *fct_name = "getpwnam_r";
221     static void *fct_start = NULL;
222     enum nss_status status;
223     service_user *nip;
224     union {
225         enum nss_status (*l)(const char *name, struct passwd *pwd,
226                              char *buffer, size_t buflen, int *errnop);
227         void *ptr;
228     } fct;
229     int group_errno;
230
231     nip = nss_passwd_nonlocal_database();
232     if (nip == NULL)
233         return NSS_STATUS_UNAVAIL;
234     if (fct_start == NULL)
235         fct_start = __nss_lookup_function(nip, fct_name);
236     fct.ptr = fct_start;
237     do {
238         if (fct.ptr == NULL)
239             status = NSS_STATUS_UNAVAIL;
240         else
241             status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
242         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
243             break;
244     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
245     if (status != NSS_STATUS_SUCCESS)
246         return status;
247
248     status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
249     if (status != NSS_STATUS_SUCCESS)
250         return status;
251
252     if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
253         NSS_STATUS_SUCCESS)
254         pwd->pw_gid = 65534 /* nogroup */;
255     return NSS_STATUS_SUCCESS;
256 }
257
258 enum nss_status
259 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
260                          char *buffer, size_t buflen, int *errnop)
261 {
262     static const char *fct_name = "getpwuid_r";
263     static void *fct_start = NULL;
264     enum nss_status status;
265     service_user *nip;
266     union {
267         enum nss_status (*l)(uid_t uid, struct passwd *pwd,
268                              char *buffer, size_t buflen, int *errnop);
269         void *ptr;
270     } fct;
271     int group_errno;
272
273     if (local_only == 1)
274         return NSS_STATUS_UNAVAIL;
275
276     nip = nss_passwd_nonlocal_database();
277     if (nip == NULL)
278         return NSS_STATUS_UNAVAIL;
279     if (fct_start == NULL)
280         fct_start = __nss_lookup_function(nip, fct_name);
281     fct.ptr = fct_start;
282     do {
283         if (fct.ptr == NULL)
284             status = NSS_STATUS_UNAVAIL;
285         else
286             status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
287         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
288             break;
289     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
290     if (status != NSS_STATUS_SUCCESS)
291         return status;
292
293     status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
294     if (status != NSS_STATUS_SUCCESS)
295         return status;
296
297     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
298         NSS_STATUS_SUCCESS)
299         pwd->pw_gid = 65534 /* nogroup */;
300     return NSS_STATUS_SUCCESS;
301 }
This page took 0.06683 seconds and 5 git commands to generate.