]> andersk Git - nss_nonlocal.git/blame - nonlocal-passwd.c
Initial release.
[nss_nonlocal.git] / nonlocal-passwd.c
CommitLineData
f6903667
AK
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
46static service_user *
47nss_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
57static __thread int local_only = 0;
58
59enum nss_status
60local_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
83enum nss_status
84check_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
112static service_user *pwent_nip = NULL;
113static void *pwent_fct_start;
114static union {
115 enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
116 int *errnop);
117 void *ptr;
118} pwent_fct;
119static const char *pwent_fct_name = "getpwent_r";
120
121enum 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
155enum 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
184enum 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
216enum 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
258enum 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 1.948738 seconds and 5 git commands to generate.