]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Use a magic buflen instead of thread-local variables, to avoid strange
[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 #define MAGIC_LOCAL_PW_BUFLEN (sysconf(_SC_GETPW_R_SIZE_MAX) + 7)
46
47
48 static service_user *
49 nss_passwd_nonlocal_database(void)
50 {
51     static service_user *nip = NULL;
52     if (nip == NULL)
53         __nss_database_lookup("passwd_nonlocal", NULL, "", &nip);
54
55     return nip;
56 }
57
58
59 enum nss_status
60 check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
61 {
62     enum nss_status status = NSS_STATUS_SUCCESS;
63     struct passwd pwbuf;
64     struct passwd *pwbufp = &pwbuf;
65     int ret;
66     int old_errno = errno;
67     int buflen = MAGIC_LOCAL_PW_BUFLEN;
68     char *buf = malloc(buflen);
69     if (buf == NULL) {
70         *errnop = ENOMEM;
71         errno = old_errno;
72         return NSS_STATUS_TRYAGAIN;
73     }
74     errno = 0;
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
89 static service_user *pwent_nip = NULL;
90 static void *pwent_fct_start;
91 static union {
92     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
93                          int *errnop);
94     void *ptr;
95 } pwent_fct;
96 static const char *pwent_fct_name = "getpwent_r";
97
98 enum nss_status
99 _nss_nonlocal_setpwent(int stayopen)
100 {
101     static const char *fct_name = "setpwent";
102     static void *fct_start = NULL;
103     enum nss_status status;
104     service_user *nip;
105     union {
106         enum nss_status (*l)(int stayopen);
107         void *ptr;
108     } fct;
109
110     nip = nss_passwd_nonlocal_database();
111     if (nip == NULL)
112         return NSS_STATUS_UNAVAIL;
113     if (fct_start == NULL)
114         fct_start = __nss_lookup_function(nip, fct_name);
115     fct.ptr = fct_start;
116     do {
117         if (fct.ptr == NULL)
118             status = NSS_STATUS_UNAVAIL;
119         else
120             status = DL_CALL_FCT(fct.l, (stayopen));
121     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
122     if (status != NSS_STATUS_SUCCESS)
123         return status;
124
125     pwent_nip = nip;
126     if (pwent_fct_start == NULL)
127         pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
128     pwent_fct.ptr = pwent_fct_start;
129     return NSS_STATUS_SUCCESS;
130 }
131
132 enum nss_status
133 _nss_nonlocal_endpwent(void)
134 {
135     static const char *fct_name = "endpwent";
136     static void *fct_start = NULL;
137     enum nss_status status;
138     service_user *nip;
139     union {
140         enum nss_status (*l)(void);
141         void *ptr;
142     } fct;
143
144     pwent_nip = NULL;
145
146     nip = nss_passwd_nonlocal_database();
147     if (nip == NULL)
148         return NSS_STATUS_UNAVAIL;
149     if (fct_start == NULL)
150         fct_start = __nss_lookup_function(nip, fct_name);
151     fct.ptr = fct_start;
152     do {
153         if (fct.ptr == NULL)
154             status = NSS_STATUS_UNAVAIL;
155         else
156             status = DL_CALL_FCT(fct.l, ());
157     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
158     return status;
159 }
160
161 enum nss_status
162 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
163                          int *errnop)
164 {
165     enum nss_status status;
166     if (pwent_nip == NULL) {
167         status = _nss_nonlocal_setpwent(0);
168         if (status != NSS_STATUS_SUCCESS)
169             return status;
170     }
171     do {
172         if (pwent_fct.ptr == NULL)
173             status = NSS_STATUS_UNAVAIL;
174         else {
175             int nonlocal_errno;
176             do
177                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));       
178             while (status == NSS_STATUS_SUCCESS &&
179                    check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
180         }
181         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
182             return status;
183
184         if (status == NSS_STATUS_SUCCESS)
185             return NSS_STATUS_SUCCESS;
186     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
187
188     pwent_nip = NULL;
189     return NSS_STATUS_NOTFOUND;
190 }
191
192
193 enum nss_status
194 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
195                          char *buffer, size_t buflen, int *errnop)
196 {
197     static const char *fct_name = "getpwnam_r";
198     static void *fct_start = NULL;
199     enum nss_status status;
200     service_user *nip;
201     union {
202         enum nss_status (*l)(const char *name, struct passwd *pwd,
203                              char *buffer, size_t buflen, int *errnop);
204         void *ptr;
205     } fct;
206     int group_errno;
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, (name, pwd, buffer, buflen, errnop));
219         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
220             break;
221     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
222     if (status != NSS_STATUS_SUCCESS)
223         return status;
224
225     status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
226     if (status != NSS_STATUS_SUCCESS)
227         return status;
228
229     if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
230         NSS_STATUS_SUCCESS)
231         pwd->pw_gid = 65534 /* nogroup */;
232     return NSS_STATUS_SUCCESS;
233 }
234
235 enum nss_status
236 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
237                          char *buffer, size_t buflen, int *errnop)
238 {
239     static const char *fct_name = "getpwuid_r";
240     static void *fct_start = NULL;
241     enum nss_status status;
242     service_user *nip;
243     union {
244         enum nss_status (*l)(uid_t uid, struct passwd *pwd,
245                              char *buffer, size_t buflen, int *errnop);
246         void *ptr;
247     } fct;
248     int group_errno;
249
250     if (buflen == MAGIC_LOCAL_PW_BUFLEN)
251         return NSS_STATUS_UNAVAIL;
252
253     nip = nss_passwd_nonlocal_database();
254     if (nip == NULL)
255         return NSS_STATUS_UNAVAIL;
256     if (fct_start == NULL)
257         fct_start = __nss_lookup_function(nip, fct_name);
258     fct.ptr = fct_start;
259     do {
260         if (fct.ptr == NULL)
261             status = NSS_STATUS_UNAVAIL;
262         else
263             status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
264         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
265             break;
266     } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
267     if (status != NSS_STATUS_SUCCESS)
268         return status;
269
270     status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
271     if (status != NSS_STATUS_SUCCESS)
272         return status;
273
274     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
275         NSS_STATUS_SUCCESS)
276         pwd->pw_gid = 65534 /* nogroup */;
277     return NSS_STATUS_SUCCESS;
278 }
This page took 0.07442 seconds and 5 git commands to generate.