]> git.ipfire.org Git - thirdparty/glibc.git/blame - nis/nss_compat/compat-initgroups.c
(CFLAGS-tst-align.c): Add -mpreferred-stack-boundary=4.
[thirdparty/glibc.git] / nis / nss_compat / compat-initgroups.c
CommitLineData
a334319f 1/* Copyright (C) 1998-2003, 2004 Free Software Foundation, Inc.
899d423e 2 This file is part of the GNU C Library.
3b965a7d 3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
899d423e
UD
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
899d423e
UD
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 13 Lesser General Public License for more details.
899d423e 14
41bdb6e2
AJ
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
899d423e 19
bbca27a4
UD
20#include <alloca.h>
21#include <ctype.h>
899d423e
UD
22#include <errno.h>
23#include <fcntl.h>
899d423e 24#include <grp.h>
bbca27a4
UD
25#include <nss.h>
26#include <stdio_ext.h>
899d423e
UD
27#include <string.h>
28#include <unistd.h>
bbca27a4 29#include <rpc/types.h>
7603ea28 30#include <sys/param.h>
899d423e 31#include <nsswitch.h>
bbca27a4 32#include <bits/libc-lock.h>
899d423e 33
fc9f33e3 34static service_user *ni;
bbca27a4
UD
35/* Type of the lookup function. */
36static enum nss_status (*nss_initgroups_dyn) (const char *, gid_t,
37 long int *, long int *,
38 gid_t **, long int, int *);
a334319f 39static enum nss_status (*nss_setgrent) (int stayopen);
bbca27a4
UD
40static enum nss_status (*nss_getgrnam_r) (const char *name,
41 struct group * grp, char *buffer,
42 size_t buflen, int *errnop);
43static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp,
44 char *buffer, size_t buflen,
45 int *errnop);
46static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer,
47 size_t buflen, int *errnop);
a334319f 48static enum nss_status (*nss_endgrent) (void);
bbca27a4
UD
49
50/* Protect global state against multiple changers. */
51__libc_lock_define_initialized (static, lock)
52
899d423e
UD
53
54/* Get the declaration of the parser function. */
55#define ENTNAME grent
56#define STRUCTURE group
57#define EXTERN_PARSER
58#include <nss/nss_files/files-parse.c>
59
60/* Structure for remembering -group members ... */
61#define BLACKLIST_INITIAL_SIZE 512
62#define BLACKLIST_INCREMENT 256
63struct blacklist_t
e36b0b57 64{
bbca27a4
UD
65 char *data;
66 int current;
67 int size;
e36b0b57
UD
68};
69
899d423e 70struct ent_t
bbca27a4
UD
71{
72 bool_t files;
73 FILE *stream;
74 struct blacklist_t blacklist;
899d423e
UD
75};
76typedef struct ent_t ent_t;
77
78
79/* Prototypes for local functions. */
80static void blacklist_store_name (const char *, ent_t *);
81static int in_blacklist (const char *, int, ent_t *);
82
bbca27a4
UD
83/* Initialize the NSS interface/functions. The calling function must
84 hold the lock. */
85static void
86init_nss_interface (void)
899d423e 87{
bbca27a4 88 __libc_lock_lock (lock);
899d423e 89
bbca27a4
UD
90 /* Retest. */
91 if (ni == NULL
92 && __nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0)
899d423e 93 {
bbca27a4 94 nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn");
a334319f 95 nss_setgrent = __nss_lookup_function (ni, "setgrent");
bbca27a4
UD
96 nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r");
97 nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r");
98 nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r");
a334319f 99 nss_endgrent = __nss_lookup_function (ni, "endgrent");
899d423e
UD
100 }
101
bbca27a4 102 __libc_lock_unlock (lock);
899d423e
UD
103}
104
105static enum nss_status
106internal_setgrent (ent_t *ent)
107{
108 enum nss_status status = NSS_STATUS_SUCCESS;
109
bbca27a4 110 ent->files = TRUE;
899d423e 111
bbca27a4
UD
112 if (ni == NULL)
113 init_nss_interface ();
899d423e
UD
114
115 if (ent->blacklist.data != NULL)
116 {
117 ent->blacklist.current = 1;
118 ent->blacklist.data[0] = '|';
119 ent->blacklist.data[1] = '\0';
120 }
121 else
122 ent->blacklist.current = 0;
123
bbca27a4
UD
124 ent->stream = fopen ("/etc/group", "rm");
125
899d423e 126 if (ent->stream == NULL)
bbca27a4
UD
127 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
128 else
899d423e 129 {
bbca27a4
UD
130 /* We have to make sure the file is `closed on exec'. */
131 int result, flags;
899d423e 132
bbca27a4
UD
133 result = flags = fcntl (fileno_unlocked (ent->stream), F_GETFD, 0);
134 if (result >= 0)
899d423e 135 {
bbca27a4
UD
136 flags |= FD_CLOEXEC;
137 result = fcntl (fileno_unlocked (ent->stream), F_SETFD, flags);
138 }
139 if (result < 0)
140 {
141 /* Something went wrong. Close the stream and return a
142 failure. */
143 fclose (ent->stream);
144 ent->stream = NULL;
145 status = NSS_STATUS_UNAVAIL;
899d423e 146 }
bbca27a4
UD
147 else
148 /* We take care of locking ourself. */
149 __fsetlocking (ent->stream, FSETLOCKING_BYCALLER);
899d423e 150 }
899d423e
UD
151
152 return status;
153}
154
155
156static enum nss_status
157internal_endgrent (ent_t *ent)
158{
159 if (ent->stream != NULL)
160 {
161 fclose (ent->stream);
162 ent->stream = NULL;
163 }
164
899d423e
UD
165 if (ent->blacklist.data != NULL)
166 {
167 ent->blacklist.current = 1;
168 ent->blacklist.data[0] = '|';
169 ent->blacklist.data[1] = '\0';
170 }
171 else
172 ent->blacklist.current = 0;
173
174 return NSS_STATUS_SUCCESS;
175}
176
bbca27a4
UD
177/* This function checks, if the user is a member of this group and if
178 yes, add the group id to the list. */
179static void
180check_and_add_group (const char *user, gid_t group, long int *start,
181 long int *size, gid_t **groupsp, long int limit,
182 struct group *grp)
899d423e 183{
bbca27a4
UD
184 gid_t *groups = *groupsp;
185 char **member;
186
187 /* Don't add main group to list of groups. */
188 if (grp->gr_gid == group)
189 return;
190
191 for (member = grp->gr_mem; *member != NULL; ++member)
192 if (strcmp (*member, user) == 0)
193 {
194 /* Matches user. Insert this group. */
195 if (*start == *size)
196 {
197 /* Need a bigger buffer. */
198 gid_t *newgroups;
199 long int newsize;
200
201 if (limit > 0 && *size == limit)
202 /* We reached the maximum. */
203 return;
899d423e 204
bbca27a4
UD
205 if (limit <= 0)
206 newsize = 2 * *size;
207 else
208 newsize = MIN (limit, 2 * *size);
e36b0b57 209
bbca27a4
UD
210 newgroups = realloc (groups, newsize * sizeof (*groups));
211 if (newgroups == NULL)
212 return;
213 *groupsp = groups = newgroups;
214 *size = newsize;
215 }
899d423e 216
bbca27a4
UD
217 groups[*start] = grp->gr_gid;
218 *start += 1;
899d423e 219
bbca27a4
UD
220 break;
221 }
899d423e
UD
222}
223
3426e770 224/* Get the next group from NSS (+ entry). If the NSS module supports
bbca27a4 225 initgroups_dyn, get all entries at once. */
899d423e 226static enum nss_status
bbca27a4
UD
227getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
228 gid_t group, long int *start, long int *size,
229 gid_t **groupsp, long int limit, int *errnop)
899d423e 230{
bbca27a4
UD
231 enum nss_status status;
232 struct group grpbuf;
899d423e 233
bbca27a4
UD
234 /* if this module does not support getgrent_r and initgroups_dyn,
235 abort. We cannot find the needed group entries. */
236 if (nss_getgrent_r == NULL && nss_initgroups_dyn == NULL)
237 return NSS_STATUS_UNAVAIL;
238
239 /* Try nss_initgroups_dyn if supported. We also need getgrgid_r.
240 If this function is not supported, step through the whole group
241 database with getgrent_r. */
242 if (nss_initgroups_dyn && nss_getgrgid_r)
899d423e 243 {
3426e770 244 long int mystart = 0;
8583671d 245 long int mysize = limit <= 0 ? *size : limit;
afd7b703 246 gid_t *mygroups = malloc (mysize * sizeof (gid_t));
3426e770 247
afd7b703 248 if (mygroups == NULL)
3426e770 249 return NSS_STATUS_TRYAGAIN;
bbca27a4
UD
250
251 /* For every gid in the list we get from the NSS module,
252 get the whole group entry. We need to do this, since we
253 need the group name to check if it is in the blacklist.
254 In worst case, this is as twice as slow as stepping with
255 getgrent_r through the whole group database. But for large
256 group databases this is faster, since the user can only be
257 in a limited number of groups. */
afd7b703 258 if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups,
bbca27a4 259 limit, errnop) == NSS_STATUS_SUCCESS)
899d423e 260 {
8583671d 261 /* A temporary buffer. We use the normal buffer, until we find
bbca27a4
UD
262 an entry, for which this buffer is to small. In this case, we
263 overwrite the pointer with one to a bigger buffer. */
264 char *tmpbuf = buffer;
265 size_t tmplen = buflen;
266 int i;
267
268 for (i = 0; i < mystart; i++)
269 {
afd7b703 270 while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, tmpbuf,
bbca27a4
UD
271 tmplen,
272 errnop)) == NSS_STATUS_TRYAGAIN
273 && *errnop == ERANGE)
274 if (tmpbuf == buffer)
275 {
276 tmplen *= 2;
277 tmpbuf = __alloca (tmplen);
278 }
279 else
280 tmpbuf = extend_alloca (tmpbuf, tmplen, 2 * tmplen);
281
282 if (!in_blacklist (grpbuf.gr_name,
283 strlen (grpbuf.gr_name), ent))
284 check_and_add_group (user, group, start, size, groupsp,
285 limit, &grpbuf);
286 }
3426e770 287
afd7b703 288 free (mygroups);
3426e770 289
bbca27a4 290 return NSS_STATUS_NOTFOUND;
899d423e 291 }
3426e770 292
afd7b703 293 free (mygroups);
bbca27a4 294 }
899d423e 295
bbca27a4
UD
296 /* If we come here, the NSS module does not support initgroups_dyn
297 and we have to step through the whole list ourself. */
298 do
299 {
300 if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) !=
301 NSS_STATUS_SUCCESS)
302 return status;
899d423e 303 }
bbca27a4 304 while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent));
899d423e 305
bbca27a4 306 check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
899d423e
UD
307 return NSS_STATUS_SUCCESS;
308}
309
899d423e 310static enum nss_status
bbca27a4
UD
311internal_getgrent_r (ent_t *ent, char *buffer, size_t buflen, const char *user,
312 gid_t group, long int *start, long int *size,
313 gid_t **groupsp, long int limit, int *errnop)
899d423e
UD
314{
315 struct parser_data *data = (void *) buffer;
bbca27a4 316 struct group grpbuf;
899d423e 317
bbca27a4
UD
318 if (!ent->files)
319 return getgrent_next_nss (ent, buffer, buflen, user, group,
320 start, size, groupsp, limit, errnop);
899d423e 321
899d423e
UD
322 while (1)
323 {
324 fpos_t pos;
325 int parse_res = 0;
326 char *p;
327
328 do
329 {
20f8e666
UD
330 /* We need at least 3 characters for one line. */
331 if (__builtin_expect (buflen < 3, 0))
332 {
333 erange:
334 *errnop = ERANGE;
335 return NSS_STATUS_TRYAGAIN;
336 }
337
899d423e
UD
338 fgetpos (ent->stream, &pos);
339 buffer[buflen - 1] = '\xff';
bbca27a4
UD
340 p = fgets_unlocked (buffer, buflen, ent->stream);
341 if (p == NULL && feof_unlocked (ent->stream))
34816665
UD
342 return NSS_STATUS_NOTFOUND;
343
20f8e666 344 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
899d423e 345 {
20f8e666 346 erange_reset:
899d423e 347 fsetpos (ent->stream, &pos);
20f8e666 348 goto erange;
899d423e
UD
349 }
350
351 /* Terminate the line for any case. */
352 buffer[buflen - 1] = '\0';
353
354 /* Skip leading blanks. */
355 while (isspace (*p))
356 ++p;
357 }
bbca27a4
UD
358 while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
359 /* Parse the line. If it is invalid, loop to
360 get the next line of the file to parse. */
361 !(parse_res = _nss_files_parse_grent (p, &grpbuf, data, buflen,
899d423e
UD
362 errnop)));
363
20f8e666
UD
364 if (__builtin_expect (parse_res == -1, 0))
365 /* The parser ran out of space. */
366 goto erange_reset;
899d423e 367
bbca27a4 368 if (grpbuf.gr_name[0] != '+' && grpbuf.gr_name[0] != '-')
899d423e
UD
369 /* This is a real entry. */
370 break;
371
372 /* -group */
bbca27a4
UD
373 if (grpbuf.gr_name[0] == '-' && grpbuf.gr_name[1] != '\0'
374 && grpbuf.gr_name[1] != '@')
899d423e 375 {
bbca27a4 376 blacklist_store_name (&grpbuf.gr_name[1], ent);
899d423e
UD
377 continue;
378 }
379
380 /* +group */
bbca27a4
UD
381 if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] != '\0'
382 && grpbuf.gr_name[1] != '@')
899d423e 383 {
bbca27a4
UD
384 if (in_blacklist (&grpbuf.gr_name[1],
385 strlen (&grpbuf.gr_name[1]), ent))
386 continue;
387 /* Store the group in the blacklist for the "+" at the end of
899d423e 388 /etc/group */
bbca27a4
UD
389 blacklist_store_name (&grpbuf.gr_name[1], ent);
390 if (nss_getgrnam_r == NULL)
391 return NSS_STATUS_UNAVAIL;
392 else if (nss_getgrnam_r (&grpbuf.gr_name[1], &grpbuf, buffer,
393 buflen, errnop) != NSS_STATUS_SUCCESS)
394 continue;
395
396 check_and_add_group (user, group, start, size, groupsp,
397 limit, &grpbuf);
398
399 return NSS_STATUS_SUCCESS;
899d423e
UD
400 }
401
402 /* +:... */
bbca27a4 403 if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0')
899d423e 404 {
bbca27a4
UD
405 ent->files = FALSE;
406 return getgrent_next_nss (ent, buffer, buflen, user, group,
407 start, size, groupsp, limit, errnop);
899d423e
UD
408 }
409 }
410
bbca27a4
UD
411 check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
412
899d423e
UD
413 return NSS_STATUS_SUCCESS;
414}
415
416
899d423e 417enum nss_status
cf9e9ad9 418_nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
7603ea28
UD
419 long int *size, gid_t **groupsp, long int limit,
420 int *errnop)
899d423e 421{
899d423e
UD
422 size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
423 char *tmpbuf;
424 enum nss_status status;
bbca27a4 425 ent_t intern = { TRUE, NULL, {NULL, 0, 0} };
899d423e
UD
426
427 status = internal_setgrent (&intern);
428 if (status != NSS_STATUS_SUCCESS)
429 return status;
430
431 tmpbuf = __alloca (buflen);
432
433 do
434 {
bbca27a4
UD
435 while ((status = internal_getgrent_r (&intern, tmpbuf, buflen,
436 user, group, start, size,
437 groupsp, limit, errnop))
438 == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
439 tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
899d423e
UD
440 }
441 while (status == NSS_STATUS_SUCCESS);
442
899d423e
UD
443 internal_endgrent (&intern);
444
445 return NSS_STATUS_SUCCESS;
446}
447
448
449/* Support routines for remembering -@netgroup and -user entries.
450 The names are stored in a single string with `|' as separator. */
451static void
452blacklist_store_name (const char *name, ent_t *ent)
453{
454 int namelen = strlen (name);
455 char *tmp;
456
bbca27a4 457 /* First call, setup cache. */
899d423e
UD
458 if (ent->blacklist.size == 0)
459 {
460 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
461 ent->blacklist.data = malloc (ent->blacklist.size);
462 if (ent->blacklist.data == NULL)
463 return;
464 ent->blacklist.data[0] = '|';
465 ent->blacklist.data[1] = '\0';
466 ent->blacklist.current = 1;
467 }
468 else
469 {
470 if (in_blacklist (name, namelen, ent))
471 return; /* no duplicates */
472
473 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
474 {
475 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
476 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
477 if (tmp == NULL)
478 {
479 free (ent->blacklist.data);
480 ent->blacklist.size = 0;
481 return;
482 }
483 ent->blacklist.data = tmp;
484 }
485 }
486
487 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
488 *tmp++ = '|';
489 *tmp = '\0';
490 ent->blacklist.current += namelen + 1;
491
492 return;
493}
494
495/* returns TRUE if ent->blacklist contains name, else FALSE */
496static bool_t
497in_blacklist (const char *name, int namelen, ent_t *ent)
498{
499 char buf[namelen + 3];
500 char *cp;
501
502 if (ent->blacklist.data == NULL)
503 return FALSE;
504
505 buf[0] = '|';
506 cp = stpcpy (&buf[1], name);
bbca27a4 507 *cp++ = '|';
899d423e
UD
508 *cp = '\0';
509 return strstr (ent->blacklist.data, buf) != NULL;
510}