]> andersk Git - nss_nonlocal.git/blame - nonlocal-group.c
Guard one-time initialization with memory barriers
[nss_nonlocal.git] / nonlocal-group.c
CommitLineData
f6903667
AK
1/*
2 * nonlocal-group.c
3 * group database for nss_nonlocal proxy
4 *
96a1ee0f
AK
5 * Copyright © 2007–2010 Anders Kaseorg <andersk@mit.edu> and Tim
6 * Abbott <tabbott@mit.edu>
f6903667 7 *
96a1ee0f 8 * This file is part of nss_nonlocal.
f6903667 9 *
96a1ee0f
AK
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.
f6903667 14 *
96a1ee0f
AK
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
f6903667
AK
24 */
25
26#define _GNU_SOURCE
dc397f8f 27
f6903667 28#include <sys/types.h>
f6903667 29#include <dlfcn.h>
f6903667
AK
30#include <errno.h>
31#include <grp.h>
32#include <nss.h>
dc397f8f
AK
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
f6903667
AK
41#include "nsswitch-internal.h"
42#include "nonlocal.h"
43
6ca16423
AK
44/*
45 * If the MAGIC_NONLOCAL_GROUPNAME local group exists, then nonlocal
34cfeb28
AK
46 * users will be automatically added to it. Furthermore, if a local
47 * user is added to this group, then that user will inherit any
775f7dc3
AK
48 * nonlocal gids from a nonlocal user of the same name, as
49 * supplementary gids.
6ca16423 50 */
48479fc7 51#define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
6ca16423
AK
52
53/*
54 * If the MAGIC_LOCAL_GROUPNAME local group exists, then local users
55 * will be automatically added to it.
56 */
48479fc7 57#define MAGIC_LOCAL_GROUPNAME "nss-local-users"
f7293a54 58
25468b96
AK
59/*
60 * If the MAGIC_NONLOCAL_USERNAME local user is added to a local
61 * group, then the local group will inherit the nonlocal membership of
62 * a group of the same gid.
63 */
64#define MAGIC_NONLOCAL_USERNAME "nss-nonlocal-users"
65
f6903667 66
c1812233
AK
67enum nss_status
68_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
69 char *buffer, size_t buflen, int *errnop);
70
71enum nss_status
72_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
73 char *buffer, size_t buflen, int *errnop);
74
75
cbb0e3ea
AK
76static service_user *__nss_group_nonlocal_database;
77
78static int
79internal_function
80__nss_group_nonlocal_lookup(service_user **ni, const char *fct_name,
81 void **fctp)
f6903667 82{
cbb0e3ea
AK
83 if (__nss_group_nonlocal_database == NULL
84 && __nss_database_lookup("group_nonlocal", NULL, NULL,
85 &__nss_group_nonlocal_database) < 0)
86 return -1;
87
88 *ni = __nss_group_nonlocal_database;
48479fc7 89
cbb0e3ea
AK
90 *fctp = __nss_lookup_function(*ni, fct_name);
91 return 0;
f6903667
AK
92}
93
94
f6903667 95enum nss_status
25468b96 96check_nonlocal_gid(const char *user, const char *group, gid_t gid, int *errnop)
f6903667 97{
30fb3957 98 enum nss_status status;
f7293a54 99 struct group gbuf;
cbb0e3ea 100 char *buf;
8870ee9c 101 size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
cbb0e3ea
AK
102 const struct walk_nss w = {
103 .lookup = &__nss_group_lookup, .fct_name = "getgrgid_r",
104 .status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
105 };
106 const __typeof__(&_nss_nonlocal_getgrgid_r) self = &_nss_nonlocal_getgrgid_r;
107#define args (gid, &gbuf, buf, buflen, errnop)
108#include "walk_nss.h"
109#undef args
30fb3957 110
25468b96
AK
111 if (status == NSS_STATUS_TRYAGAIN)
112 return status;
113 else if (status != NSS_STATUS_SUCCESS)
114 return NSS_STATUS_SUCCESS;
115
116 if (group == NULL || strcmp(gbuf.gr_name, group) == 0) {
117 char *const *mem;
118 for (mem = gbuf.gr_mem; *mem != NULL; mem++)
119 if (strcmp(*mem, MAGIC_NONLOCAL_USERNAME) == 0) {
120 status = check_nonlocal_user(*mem, errnop);
121 if (status == NSS_STATUS_TRYAGAIN) {
122 free(buf);
123 return status;
124 } else if (status == NSS_STATUS_NOTFOUND) {
125 free(buf);
126 return NSS_STATUS_SUCCESS;
127 }
128 break;
129 }
f6903667 130 }
30fb3957 131
25468b96
AK
132 syslog(LOG_DEBUG, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
133 free(buf);
134 return NSS_STATUS_NOTFOUND;
f6903667
AK
135}
136
cf338316
AK
137enum nss_status
138check_nonlocal_group(const char *user, struct group *grp, int *errnop)
139{
a07a7616
AK
140 enum nss_status status = NSS_STATUS_SUCCESS;
141 int old_errno = errno;
142 char *end;
143 unsigned long gid;
144
145 errno = 0;
146 gid = strtoul(grp->gr_name, &end, 10);
f4061d47
AK
147 if (errno == 0 && *end == '\0' && (gid_t)gid == gid) {
148 errno = old_errno;
25468b96 149 status = check_nonlocal_gid(user, grp->gr_name, gid, errnop);
f4061d47
AK
150 } else
151 errno = old_errno;
a07a7616
AK
152 if (status != NSS_STATUS_SUCCESS)
153 return status;
154
25468b96 155 return check_nonlocal_gid(user, grp->gr_name, grp->gr_gid, errnop);
cf338316
AK
156}
157
48479fc7 158enum nss_status
8aa9014e 159get_local_group(const char *name, struct group *grp, char **buffer, int *errnop)
48479fc7 160{
30fb3957 161 enum nss_status status;
cbb0e3ea
AK
162 size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
163 const struct walk_nss w = {
164 .lookup = &__nss_group_lookup, .fct_name = "getgrnam_r",
165 .status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
166 };
167 const __typeof__(&_nss_nonlocal_getgrnam_r) self = &_nss_nonlocal_getgrnam_r;
168#define args (name, grp, *buffer, buflen, errnop)
169#include "walk_nss.h"
170#undef args
48479fc7
TA
171 return status;
172}
f6903667 173
48939704 174static bool grent_initialized = false;
cbb0e3ea 175static service_user *grent_startp, *grent_nip;
f6903667
AK
176static void *grent_fct_start;
177static union {
178 enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
179 int *errnop);
180 void *ptr;
181} grent_fct;
182static const char *grent_fct_name = "getgrent_r";
183
184enum nss_status
185_nss_nonlocal_setgrent(int stayopen)
186{
f6903667 187 enum nss_status status;
cbb0e3ea
AK
188 const struct walk_nss w = {
189 .lookup = &__nss_group_nonlocal_lookup, .fct_name = "setgrent",
190 .status = &status
191 };
192 const __typeof__(&_nss_nonlocal_setgrent) self = NULL;
193#define args (stayopen)
194#include "walk_nss.h"
195#undef args
f6903667
AK
196 if (status != NSS_STATUS_SUCCESS)
197 return status;
198
48939704 199 if (!grent_initialized) {
cbb0e3ea
AK
200 __nss_group_nonlocal_lookup(&grent_startp, grent_fct_name,
201 &grent_fct_start);
48939704
AK
202 __sync_synchronize();
203 grent_initialized = true;
204 }
cbb0e3ea 205 grent_nip = grent_startp;
f6903667
AK
206 grent_fct.ptr = grent_fct_start;
207 return NSS_STATUS_SUCCESS;
208}
209
210enum nss_status
211_nss_nonlocal_endgrent(void)
212{
f6903667 213 enum nss_status status;
cbb0e3ea
AK
214 const struct walk_nss w = {
215 .lookup = &__nss_group_nonlocal_lookup, .fct_name = "endgrent",
216 .status = &status
217 };
218 const __typeof__(&_nss_nonlocal_endgrent) self = NULL;
f6903667
AK
219
220 grent_nip = NULL;
221
cbb0e3ea
AK
222#define args ()
223#include "walk_nss.h"
224#undef args
f6903667
AK
225 return status;
226}
227
228enum nss_status
229_nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
230 int *errnop)
231{
232 enum nss_status status;
a360549e
TA
233
234 char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
c1812233 235 if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
a360549e
TA
236 return NSS_STATUS_UNAVAIL;
237
f6903667
AK
238 if (grent_nip == NULL) {
239 status = _nss_nonlocal_setgrent(0);
240 if (status != NSS_STATUS_SUCCESS)
241 return status;
242 }
243 do {
244 if (grent_fct.ptr == NULL)
245 status = NSS_STATUS_UNAVAIL;
246 else {
247 int nonlocal_errno;
248 do
249 status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
250 while (status == NSS_STATUS_SUCCESS &&
cf338316 251 check_nonlocal_group("(unknown)", grp, &nonlocal_errno) != NSS_STATUS_SUCCESS);
f6903667
AK
252 }
253 if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
254 return status;
255
256 if (status == NSS_STATUS_SUCCESS)
257 return NSS_STATUS_SUCCESS;
258 } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
259
260 grent_nip = NULL;
261 return NSS_STATUS_NOTFOUND;
262}
263
264
265enum nss_status
266_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
267 char *buffer, size_t buflen, int *errnop)
268{
f6903667 269 enum nss_status status;
cbb0e3ea
AK
270 const struct walk_nss w = {
271 .lookup = &__nss_group_nonlocal_lookup, .fct_name = "getgrnam_r",
272 .status = &status, .errnop = errnop
273 };
274 const __typeof__(&_nss_nonlocal_getgrnam_r) self = NULL;
f6903667 275
a360549e 276 char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
c1812233 277 if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
48479fc7
TA
278 return NSS_STATUS_UNAVAIL;
279
cbb0e3ea
AK
280#define args (name, grp, buffer, buflen, errnop)
281#include "walk_nss.h"
282#undef args
f6903667
AK
283 if (status != NSS_STATUS_SUCCESS)
284 return status;
285
22562df0
AK
286 if (strcmp(name, grp->gr_name) != 0) {
287 syslog(LOG_ERR, "nss_nonlocal: discarding group %s from lookup for group %s\n", grp->gr_name, name);
288 return NSS_STATUS_NOTFOUND;
289 }
290
cf338316 291 return check_nonlocal_group(name, grp, errnop);
f6903667
AK
292}
293
294enum nss_status
295_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
296 char *buffer, size_t buflen, int *errnop)
297{
f6903667 298 enum nss_status status;
cbb0e3ea
AK
299 const struct walk_nss w = {
300 .lookup = &__nss_group_nonlocal_lookup, .fct_name = "getgrgid_r",
301 .status = &status, .errnop = errnop
302 };
303 const __typeof__(&_nss_nonlocal_getgrgid_r) self = NULL;
f6903667 304
a360549e 305 char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
c1812233 306 if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
f6903667
AK
307 return NSS_STATUS_UNAVAIL;
308
cbb0e3ea
AK
309#define args (gid, grp, buffer, buflen, errnop)
310#include "walk_nss.h"
311#undef args
f6903667
AK
312 if (status != NSS_STATUS_SUCCESS)
313 return status;
314
8027fdc4
AK
315 if (gid != grp->gr_gid) {
316 syslog(LOG_ERR, "nss_nonlocal: discarding gid %d from lookup for gid %d\n", grp->gr_gid, gid);
317 return NSS_STATUS_NOTFOUND;
318 }
319
cf338316 320 return check_nonlocal_group(grp->gr_name, grp, errnop);
f6903667
AK
321}
322
dfc6e292
AK
323static bool
324add_group(gid_t group, long int *start, long int *size, gid_t **groupsp,
325 long int limit, int *errnop, enum nss_status *status)
326{
327 int i, old_errno = errno;
328 for (i = 0; i < *start; ++i)
329 if ((*groupsp)[i] == group)
330 return true;
331 if (*start + 1 > *size) {
332 gid_t *newgroups;
333 long int newsize = 2 * *size;
334 if (limit > 0) {
335 if (*size >= limit) {
336 *status = NSS_STATUS_SUCCESS;
337 return false;
338 }
339 if (newsize > limit)
340 newsize = limit;
341 }
342 newgroups = realloc(*groupsp, newsize * sizeof((*groupsp)[0]));
343 errno = old_errno;
344 if (newgroups == NULL) {
345 *errnop = ENOMEM;
346 *status = NSS_STATUS_TRYAGAIN;
347 return false;
348 }
349 *groupsp = newgroups;
350 *size = newsize;
351 }
352 (*groupsp)[(*start)++] = group;
353 return true;
354}
355
f6903667
AK
356enum nss_status
357_nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
358 long int *size, gid_t **groupsp, long int limit,
359 int *errnop)
360{
f6903667 361 enum nss_status status;
cbb0e3ea
AK
362 const struct walk_nss w = {
363 .lookup = &__nss_group_nonlocal_lookup, .fct_name = "initgroups_dyn",
364 .status = &status, .errnop = errnop
365 };
366 const __typeof__(&_nss_nonlocal_initgroups_dyn) self = NULL;
1e78305d
AK
367
368 struct group local_users_group, nonlocal_users_group;
b3831d60 369 bool is_nonlocal = true;
1e78305d 370 char *buffer;
d7bf1d11 371 int in, out, i;
48479fc7 372
34cfeb28
AK
373 /* Check that the user is a nonlocal user, or a member of the
374 * MAGIC_NONLOCAL_GROUPNAME group, before adding any groups. */
48479fc7 375 status = check_nonlocal_user(user, errnop);
3010a54b 376 if (status == NSS_STATUS_TRYAGAIN) {
48479fc7 377 return status;
3010a54b 378 } else if (status != NSS_STATUS_SUCCESS) {
b3831d60 379 is_nonlocal = false;
48479fc7 380
3010a54b
AK
381 status = get_local_group(MAGIC_LOCAL_GROUPNAME,
382 &local_users_group, &buffer, errnop);
383 if (status == NSS_STATUS_SUCCESS) {
384 free(buffer);
385 if (!add_group(local_users_group.gr_gid, start, size, groupsp,
386 limit, errnop, &status))
387 return status;
388 } else if (status == NSS_STATUS_TRYAGAIN) {
dfc6e292 389 return status;
3010a54b
AK
390 } else {
391 syslog(LOG_WARNING,
392 "nss_nonlocal: Group %s does not exist locally!",
393 MAGIC_LOCAL_GROUPNAME);
394 }
a4e1e153
AK
395 }
396
397 status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
398 &nonlocal_users_group, &buffer, errnop);
399 if (status == NSS_STATUS_SUCCESS) {
400 free(buffer);
401 if (is_nonlocal) {
dfc6e292
AK
402 if (!add_group(nonlocal_users_group.gr_gid, start, size, groupsp,
403 limit, errnop, &status))
404 return status;
34cfeb28
AK
405 } else {
406 int i;
407 for (i = 0; i < *start; ++i) {
408 if ((*groupsp)[i] == nonlocal_users_group.gr_gid) {
409 is_nonlocal = true;
410 break;
411 }
412 }
775f7dc3
AK
413
414 if (is_nonlocal) {
415 struct passwd pwbuf;
416 char *buf;
417 int nonlocal_errno = *errnop;
418 status = get_nonlocal_passwd(user, &pwbuf, &buf, errnop);
419
420 if (status == NSS_STATUS_SUCCESS) {
421 nonlocal_errno = *errnop;
25468b96 422 status = check_nonlocal_gid(user, NULL, pwbuf.pw_gid,
775f7dc3
AK
423 &nonlocal_errno);
424 free(buf);
425 }
426
427 if (status == NSS_STATUS_SUCCESS) {
428 if (!add_group(pwbuf.pw_gid, start, size, groupsp, limit,
429 errnop, &status))
430 return status;
431 } else if (status == NSS_STATUS_TRYAGAIN) {
432 *errnop = nonlocal_errno;
433 return status;
434 }
435 }
1e78305d 436 }
a4e1e153
AK
437 } else if (status == NSS_STATUS_TRYAGAIN) {
438 if (is_nonlocal)
439 return status;
440 } else {
441 syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
442 MAGIC_NONLOCAL_GROUPNAME);
48479fc7 443 }
f6903667 444
b3831d60 445 if (!is_nonlocal)
1e78305d
AK
446 return NSS_STATUS_SUCCESS;
447
d7bf1d11 448 in = out = *start;
1e78305d 449
cbb0e3ea
AK
450#define args (user, group, start, size, groupsp, limit, errnop)
451#include "walk_nss.h"
452#undef args
f6903667
AK
453 if (status != NSS_STATUS_SUCCESS)
454 return status;
455
456 for (; in < *start; ++in) {
457 int nonlocal_errno = *errnop;
458
459 for (i = 0; i < out; ++i)
460 if ((*groupsp)[i] == (*groupsp)[in])
461 break;
462 if (i < out)
463 continue;
464
25468b96
AK
465 status = check_nonlocal_gid(user, NULL, (*groupsp)[in],
466 &nonlocal_errno);
f6903667 467 if (status == NSS_STATUS_SUCCESS) {
1e78305d 468 (*groupsp)[out++] = (*groupsp)[in];
5879a696 469 } else if (status == NSS_STATUS_TRYAGAIN) {
f6903667
AK
470 *start = out;
471 *errnop = nonlocal_errno;
472 return status;
473 }
474 }
475
476 *start = out;
477 return NSS_STATUS_SUCCESS;
478}
This page took 0.118658 seconds and 5 git commands to generate.