]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Guard one-time initialization with memory barriers
[nss_nonlocal.git] / nonlocal-passwd.c
1 /*
2  * nonlocal-passwd.c
3  * passwd database for nss_nonlocal proxy.
4  *
5  * Copyright © 2007–2010 Anders Kaseorg <andersk@mit.edu> and Tim
6  * Abbott <tabbott@mit.edu>
7  *
8  * This file is part of nss_nonlocal.
9  *
10  * nss_nonlocal is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1 of
13  * the License, or (at your option) any later version.
14  *
15  * nss_nonlocal is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with nss_nonlocal; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23  * 02110-1301  USA
24  */
25
26
27 #define _GNU_SOURCE
28
29 #include <sys/types.h>
30 #include <dlfcn.h>
31 #include <errno.h>
32 #include <nss.h>
33 #include <pwd.h>
34 #include <stdbool.h>
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <unistd.h>
40
41 #include "nsswitch-internal.h"
42 #include "nonlocal.h"
43
44
45 enum nss_status
46 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
47                          char *buffer, size_t buflen, int *errnop);
48 enum nss_status
49 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
50                          char *buffer, size_t buflen, int *errnop);
51
52
53 static service_user *__nss_passwd_nonlocal_database;
54
55 static int
56 internal_function
57 __nss_passwd_nonlocal_lookup(service_user **ni, const char *fct_name,
58                              void **fctp)
59 {
60     if (__nss_passwd_nonlocal_database == NULL
61         && __nss_database_lookup("passwd_nonlocal", NULL, NULL,
62                                  &__nss_passwd_nonlocal_database) < 0)
63         return -1;
64
65     *ni = __nss_passwd_nonlocal_database;
66
67     *fctp = __nss_lookup_function(*ni, fct_name);
68     return 0;
69 }
70
71
72 enum nss_status
73 check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
74 {
75     enum nss_status status;
76     struct passwd pwbuf;
77     char *buf;
78     size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
79     const struct walk_nss w = {
80         .lookup = &__nss_passwd_lookup, .fct_name = "getpwuid_r",
81         .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
82     };
83     const __typeof__(&_nss_nonlocal_getpwuid_r) self = &_nss_nonlocal_getpwuid_r;
84 #define args (uid, &pwbuf, buf, buflen, errnop)
85 #include "walk_nss.h"
86 #undef args
87
88     if (status == NSS_STATUS_SUCCESS) {
89         syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
90         free(buf);
91         status = NSS_STATUS_NOTFOUND;
92     } else if (status != NSS_STATUS_TRYAGAIN) {
93         status = NSS_STATUS_SUCCESS;
94     }
95
96     return status;
97 }
98
99 enum nss_status
100 check_nonlocal_passwd(const char *user, struct passwd *pwd, int *errnop)
101 {
102     enum nss_status status = NSS_STATUS_SUCCESS;
103     int old_errno = errno;
104     char *end;
105     unsigned long uid;
106
107     errno = 0;
108     uid = strtoul(pwd->pw_name, &end, 10);
109     if (errno == 0 && *end == '\0' && (uid_t)uid == uid) {
110         errno = old_errno;
111         status = check_nonlocal_uid(user, uid, errnop);
112     } else {
113         errno = old_errno;
114     }
115     if (status != NSS_STATUS_SUCCESS)
116         return status;
117
118     return check_nonlocal_uid(user, pwd->pw_uid, errnop);
119 }
120
121 enum nss_status
122 check_nonlocal_user(const char *user, int *errnop)
123 {
124     enum nss_status status;
125     struct passwd pwbuf;
126     char *buf;
127     size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
128     const struct walk_nss w = {
129         .lookup = __nss_passwd_lookup, .fct_name = "getpwnam_r",
130         .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
131     };
132     const __typeof__(&_nss_nonlocal_getpwnam_r) self = &_nss_nonlocal_getpwnam_r;
133 #define args (user, &pwbuf, buf, buflen, errnop)
134 #include "walk_nss.h"
135 #undef args
136
137     if (status == NSS_STATUS_SUCCESS) {
138         free(buf);
139         status = NSS_STATUS_NOTFOUND;
140     } else if (status != NSS_STATUS_TRYAGAIN) {
141         status = NSS_STATUS_SUCCESS;
142     }
143
144     return status;
145 }
146
147 enum nss_status
148 get_nonlocal_passwd(const char *name, struct passwd *pwd, char **buffer,
149                     int *errnop)
150 {
151     enum nss_status status;
152     size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
153     const struct walk_nss w = {
154         .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
155         .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
156     };
157     const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
158 #define args (name, pwd, *buffer, buflen, errnop)
159 #include "walk_nss.h"
160 #undef args
161     return status;
162 }
163
164
165 static bool pwent_initialized = false;
166 static service_user *pwent_startp, *pwent_nip;
167 static void *pwent_fct_start;
168 static union {
169     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
170                          int *errnop);
171     void *ptr;
172 } pwent_fct;
173 static const char *pwent_fct_name = "getpwent_r";
174
175 enum nss_status
176 _nss_nonlocal_setpwent(int stayopen)
177 {
178     enum nss_status status;
179     const struct walk_nss w = {
180         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "setpwent",
181         .status = &status
182     };
183     const __typeof__(&_nss_nonlocal_setpwent) self = NULL;
184 #define args (stayopen)
185 #include "walk_nss.h"
186 #undef args
187     if (status != NSS_STATUS_SUCCESS)
188         return status;
189
190     if (!pwent_initialized) {
191         __nss_passwd_nonlocal_lookup(&pwent_startp, pwent_fct_name,
192                                      &pwent_fct_start);
193         __sync_synchronize();
194         pwent_initialized = true;
195     }
196     pwent_nip = pwent_startp;
197     pwent_fct.ptr = pwent_fct_start;
198     return NSS_STATUS_SUCCESS;
199 }
200
201 enum nss_status
202 _nss_nonlocal_endpwent(void)
203 {
204     enum nss_status status;
205     const struct walk_nss w = {
206         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "endpwent",
207         .status = &status
208     };
209     const __typeof__(&_nss_nonlocal_endpwent) self = NULL;
210
211     pwent_nip = NULL;
212
213 #define args ()
214 #include "walk_nss.h"
215 #undef args
216     return status;
217 }
218
219 enum nss_status
220 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
221                          int *errnop)
222 {
223     enum nss_status status;
224
225     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
226     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
227         return NSS_STATUS_UNAVAIL;
228
229     if (pwent_nip == NULL) {
230         status = _nss_nonlocal_setpwent(0);
231         if (status != NSS_STATUS_SUCCESS)
232             return status;
233     }
234     do {
235         if (pwent_fct.ptr == NULL)
236             status = NSS_STATUS_UNAVAIL;
237         else {
238             int nonlocal_errno;
239             do
240                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
241             while (status == NSS_STATUS_SUCCESS &&
242                    check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
243         }
244         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
245             return status;
246
247         if (status == NSS_STATUS_SUCCESS)
248             return NSS_STATUS_SUCCESS;
249     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
250
251     pwent_nip = NULL;
252     return NSS_STATUS_NOTFOUND;
253 }
254
255
256 enum nss_status
257 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
258                          char *buffer, size_t buflen, int *errnop)
259 {
260     enum nss_status status;
261     int group_errno;
262     const struct walk_nss w = {
263         .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
264         .status = &status, .errnop = errnop
265     };
266     const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
267
268     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
269     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
270         return NSS_STATUS_UNAVAIL;
271
272 #define args (name, pwd, buffer, buflen, errnop)
273 #include "walk_nss.h"
274 #undef args
275     if (status != NSS_STATUS_SUCCESS)
276         return status;
277
278     if (strcmp(name, pwd->pw_name) != 0) {
279         syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
280         return NSS_STATUS_NOTFOUND;
281     }
282
283     status = check_nonlocal_passwd(name, pwd, errnop);
284     if (status != NSS_STATUS_SUCCESS)
285         return status;
286
287     if (check_nonlocal_gid(name, NULL, pwd->pw_gid, &group_errno) !=
288         NSS_STATUS_SUCCESS)
289         pwd->pw_gid = 65534 /* nogroup */;
290     return NSS_STATUS_SUCCESS;
291 }
292
293 enum nss_status
294 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
295                          char *buffer, size_t buflen, int *errnop)
296 {
297     enum nss_status status;
298     int group_errno;
299     const struct walk_nss w = {
300         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "getpwuid_r",
301         .status = &status, .errnop = errnop
302     };
303     const __typeof__(&_nss_nonlocal_getpwuid_r) self = NULL;
304
305     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
306     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
307         return NSS_STATUS_UNAVAIL;
308
309 #define args (uid, pwd, buffer, buflen, errnop)
310 #include "walk_nss.h"
311 #undef args
312     if (status != NSS_STATUS_SUCCESS)
313         return status;
314
315     if (uid != pwd->pw_uid) {
316         syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
317         return NSS_STATUS_NOTFOUND;
318     }
319
320     status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
321     if (status != NSS_STATUS_SUCCESS)
322         return status;
323
324     if (check_nonlocal_gid(pwd->pw_name, NULL, pwd->pw_gid, &group_errno) !=
325         NSS_STATUS_SUCCESS)
326         pwd->pw_gid = 65534 /* nogroup */;
327     return NSS_STATUS_SUCCESS;
328 }
This page took 0.305216 seconds and 5 git commands to generate.