]> andersk Git - nss_nonlocal.git/blob - nonlocal-passwd.c
Add the primary nonlocal gid to local users in MAGIC_NONLOCAL_GROUPNAME
[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 enum nss_status
147 get_nonlocal_passwd(const char *name, struct passwd *pwd, char **buffer,
148                     int *errnop)
149 {
150     enum nss_status status;
151     size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
152     const struct walk_nss w = {
153         .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
154         .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
155     };
156     const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
157 #define args (name, pwd, *buffer, buflen, errnop)
158 #include "walk_nss.h"
159 #undef args
160     return status;
161 }
162
163
164 static service_user *pwent_startp, *pwent_nip;
165 static void *pwent_fct_start;
166 static union {
167     enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
168                          int *errnop);
169     void *ptr;
170 } pwent_fct;
171 static const char *pwent_fct_name = "getpwent_r";
172
173 enum nss_status
174 _nss_nonlocal_setpwent(int stayopen)
175 {
176     enum nss_status status;
177     const struct walk_nss w = {
178         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "setpwent",
179         .status = &status
180     };
181     const __typeof__(&_nss_nonlocal_setpwent) self = NULL;
182 #define args (stayopen)
183 #include "walk_nss.h"
184 #undef args
185     if (status != NSS_STATUS_SUCCESS)
186         return status;
187
188     if (pwent_fct_start == NULL)
189         __nss_passwd_nonlocal_lookup(&pwent_startp, pwent_fct_name,
190                                      &pwent_fct_start);
191     pwent_nip = pwent_startp;
192     pwent_fct.ptr = pwent_fct_start;
193     return NSS_STATUS_SUCCESS;
194 }
195
196 enum nss_status
197 _nss_nonlocal_endpwent(void)
198 {
199     enum nss_status status;
200     const struct walk_nss w = {
201         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "endpwent",
202         .status = &status
203     };
204     const __typeof__(&_nss_nonlocal_endpwent) self = NULL;
205
206     pwent_nip = NULL;
207
208 #define args ()
209 #include "walk_nss.h"
210 #undef args
211     return status;
212 }
213
214 enum nss_status
215 _nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
216                          int *errnop)
217 {
218     enum nss_status status;
219
220     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
221     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
222         return NSS_STATUS_UNAVAIL;
223
224     if (pwent_nip == NULL) {
225         status = _nss_nonlocal_setpwent(0);
226         if (status != NSS_STATUS_SUCCESS)
227             return status;
228     }
229     do {
230         if (pwent_fct.ptr == NULL)
231             status = NSS_STATUS_UNAVAIL;
232         else {
233             int nonlocal_errno;
234             do
235                 status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
236             while (status == NSS_STATUS_SUCCESS &&
237                    check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
238         }
239         if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
240             return status;
241
242         if (status == NSS_STATUS_SUCCESS)
243             return NSS_STATUS_SUCCESS;
244     } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
245
246     pwent_nip = NULL;
247     return NSS_STATUS_NOTFOUND;
248 }
249
250
251 enum nss_status
252 _nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
253                          char *buffer, size_t buflen, int *errnop)
254 {
255     enum nss_status status;
256     int group_errno;
257     const struct walk_nss w = {
258         .lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
259         .status = &status, .errnop = errnop
260     };
261     const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
262
263     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
264     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
265         return NSS_STATUS_UNAVAIL;
266
267 #define args (name, pwd, buffer, buflen, errnop)
268 #include "walk_nss.h"
269 #undef args
270     if (status != NSS_STATUS_SUCCESS)
271         return status;
272
273     if (strcmp(name, pwd->pw_name) != 0) {
274         syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
275         return NSS_STATUS_NOTFOUND;
276     }
277
278     status = check_nonlocal_passwd(name, pwd, errnop);
279     if (status != NSS_STATUS_SUCCESS)
280         return status;
281
282     if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
283         NSS_STATUS_SUCCESS)
284         pwd->pw_gid = 65534 /* nogroup */;
285     return NSS_STATUS_SUCCESS;
286 }
287
288 enum nss_status
289 _nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
290                          char *buffer, size_t buflen, int *errnop)
291 {
292     enum nss_status status;
293     int group_errno;
294     const struct walk_nss w = {
295         .lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "getpwuid_r",
296         .status = &status, .errnop = errnop
297     };
298     const __typeof__(&_nss_nonlocal_getpwuid_r) self = NULL;
299
300     char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
301     if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
302         return NSS_STATUS_UNAVAIL;
303
304 #define args (uid, pwd, buffer, buflen, errnop)
305 #include "walk_nss.h"
306 #undef args
307     if (status != NSS_STATUS_SUCCESS)
308         return status;
309
310     if (uid != pwd->pw_uid) {
311         syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
312         return NSS_STATUS_NOTFOUND;
313     }
314
315     status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
316     if (status != NSS_STATUS_SUCCESS)
317         return status;
318
319     if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
320         NSS_STATUS_SUCCESS)
321         pwd->pw_gid = 65534 /* nogroup */;
322     return NSS_STATUS_SUCCESS;
323 }
This page took 0.053248 seconds and 5 git commands to generate.