]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
initgroups_dyn: Refactor adding a group into an add_group function
[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 #include <sys/types.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <string.h>
33 #include <dlfcn.h>
34 #include <stdio.h>
35 #include <syslog.h>
36 #include <errno.h>
37 #include <pwd.h>
38 #include <grp.h>
39 #include <nss.h>
40 #include "nsswitch-internal.h"
41 #include "nonlocal.h"
42
43
44 enum nss_status
45 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
46                          char *buffer, size_t buflen, int *errnop);
47 enum nss_status
48 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
49                          char *buffer, size_t buflen, int *errnop);
50
51
52 static service_user *__nss_passwd_nonlocal_database;
53
54 static int
55 internal_function
56 __nss_passwd_nonlocal_lookup(service_user **ni, const char *fct_name,
57                              void **fctp)
58 {
59     if (__nss_passwd_nonlocal_database == NULL
60         && __nss_database_lookup("passwd_nonlocal", NULL, NULL,
61                                  &__nss_passwd_nonlocal_database) < 0)
62         return -1;
63
64     *ni = __nss_passwd_nonlocal_database;
65
66     *fctp = __nss_lookup_function(*ni, fct_name);
67     return 0;
68 }
69
70
71 enum nss_status
72 check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
73 {
74     enum nss_status status;
75     struct passwd pwbuf;
76     char *buf;
77     size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
78     const struct walk_nss w = {
79         .lookup = &__nss_passwd_lookup, .fct_name = "getpwuid_r",
80         .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
81     };
82     const __typeof__(&_nss_nonlocal_getpwuid_r) self = &_nss_nonlocal_getpwuid_r;
83 #define args (uid, &pwbuf, buf, buflen, errnop)
84 #include "walk_nss.h"
85 #undef args
86
87     if (status == NSS_STATUS_SUCCESS) {
88         syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
89         free(buf);
90         status = NSS_STATUS_NOTFOUND;
91     } else if (status != NSS_STATUS_TRYAGAIN) {
92         status = NSS_STATUS_SUCCESS;
93     }
94
95     return status;
96 }
97
98 enum nss_status
99 check_nonlocal_passwd(const char *user, struct passwd *pwd, int *errnop)
100 {
101     enum nss_status status = NSS_STATUS_SUCCESS;
102     int old_errno = errno;
103     char *end;
104     unsigned long uid;
105
106     errno = 0;
107     uid = strtoul(pwd->pw_name, &end, 10);
108     if (errno == 0 && *end == '\0' && (uid_t)uid == uid) {
109         errno = old_errno;
110         status = check_nonlocal_uid(user, uid, errnop);
111     } else {
112         errno = old_errno;
113     }
114     if (status != NSS_STATUS_SUCCESS)
115         return status;
116
117     return check_nonlocal_uid(user, pwd->pw_uid, errnop);
118 }
119
120 enum nss_status
121 check_nonlocal_user(const char *user, int *errnop)
122 {
123     enum nss_status status;
124     struct passwd pwbuf;
125     char *buf;
126     size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
127     const struct walk_nss w = {
128         .lookup = __nss_passwd_lookup, .fct_name = "getpwnam_r",
129         .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
130     };
131     const __typeof__(&_nss_nonlocal_getpwnam_r) self = &_nss_nonlocal_getpwnam_r;
132 #define args (user, &pwbuf, buf, buflen, errnop)
133 #include "walk_nss.h"
134 #undef args
135
136     if (status == NSS_STATUS_SUCCESS) {
137         free(buf);
138         status = NSS_STATUS_NOTFOUND;
139     } else if (status != NSS_STATUS_TRYAGAIN) {
140         status = NSS_STATUS_SUCCESS;
141     }
142
143     return status;
144 }
145
146
147 static service_user *pwent_startp, *pwent_nip;
148 static void *pwent_fct_start;
149 static union {
150     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
151                          int *errnop);
152     void *ptr;
153 } pwent_fct;
154 static const char *pwent_fct_name = "getpwent_r";
155
156 enum nss_status
157 _nss_nonlocal_setpwent(int stayopen)
158 {
159     enum nss_status status;
160     const struct walk_nss w = {
161         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "setpwent",
162         .status = &status
163     };
164     const __typeof__(&_nss_nonlocal_setpwent) self = NULL;
165 #define args (stayopen)
166 #include "walk_nss.h"
167 #undef args
168     if (status != NSS_STATUS_SUCCESS)
169         return status;
170
171     if (pwent_fct_start == NULL)
172         __nss_passwd_nonlocal_lookup(&pwent_startp, pwent_fct_name,
173                                      &pwent_fct_start);
174     pwent_nip = pwent_startp;
175     pwent_fct.ptr = pwent_fct_start;
176     return NSS_STATUS_SUCCESS;
177 }
178
179 enum nss_status
180 _nss_nonlocal_endpwent(void)
181 {
182     enum nss_status status;
183     const struct walk_nss w = {
184         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "endpwent",
185         .status = &status
186     };
187     const __typeof__(&_nss_nonlocal_endpwent) self = NULL;
188
189     pwent_nip = NULL;
190
191 #define args ()
192 #include "walk_nss.h"
193 #undef args
194     return status;
195 }
196
197 enum nss_status
198 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
199                          int *errnop)
200 {
201     enum nss_status status;
202
203     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
204     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
205         return NSS_STATUS_UNAVAIL;
206
207     if (pwent_nip == NULL) {
208         status = _nss_nonlocal_setpwent(0);
209         if (status != NSS_STATUS_SUCCESS)
210             return status;
211     }
212     do {
213         if (pwent_fct.ptr == NULL)
214             status = NSS_STATUS_UNAVAIL;
215         else {
216             int nonlocal_errno;
217             do
218                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
219             while (status == NSS_STATUS_SUCCESS &&
220                    check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
221         }
222         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
223             return status;
224
225         if (status == NSS_STATUS_SUCCESS)
226             return NSS_STATUS_SUCCESS;
227     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
228
229     pwent_nip = NULL;
230     return NSS_STATUS_NOTFOUND;
231 }
232
233
234 enum nss_status
235 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
236                          char *buffer, size_t buflen, int *errnop)
237 {
238     enum nss_status status;
239     int group_errno;
240     const struct walk_nss w = {
241         .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
242         .status = &status, .errnop = errnop
243     };
244     const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
245
246     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
247     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
248         return NSS_STATUS_UNAVAIL;
249
250 #define args (name, pwd, buffer, buflen, errnop)
251 #include "walk_nss.h"
252 #undef args
253     if (status != NSS_STATUS_SUCCESS)
254         return status;
255
256     if (strcmp(name, pwd->pw_name) != 0) {
257         syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
258         return NSS_STATUS_NOTFOUND;
259     }
260
261     status = check_nonlocal_passwd(name, pwd, errnop);
262     if (status != NSS_STATUS_SUCCESS)
263         return status;
264
265     if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
266         NSS_STATUS_SUCCESS)
267         pwd->pw_gid = 65534 /* nogroup */;
268     return NSS_STATUS_SUCCESS;
269 }
270
271 enum nss_status
272 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
273                          char *buffer, size_t buflen, int *errnop)
274 {
275     enum nss_status status;
276     int group_errno;
277     const struct walk_nss w = {
278         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "getpwuid_r",
279         .status = &status, .errnop = errnop
280     };
281     const __typeof__(&_nss_nonlocal_getpwuid_r) self = NULL;
282
283     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
284     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
285         return NSS_STATUS_UNAVAIL;
286
287 #define args (uid, pwd, buffer, buflen, errnop)
288 #include "walk_nss.h"
289 #undef args
290     if (status != NSS_STATUS_SUCCESS)
291         return status;
292
293     if (uid != pwd->pw_uid) {
294         syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
295         return NSS_STATUS_NOTFOUND;
296     }
297
298     status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
299     if (status != NSS_STATUS_SUCCESS)
300         return status;
301
302     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
303         NSS_STATUS_SUCCESS)
304         pwd->pw_gid = 65534 /* nogroup */;
305     return NSS_STATUS_SUCCESS;
306 }
This page took 0.052412 seconds and 5 git commands to generate.