]>
Commit | Line | Data |
---|---|---|
d4697bc9 | 1 | /* Copyright (C) 1998-2014 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 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
899d423e | 18 | |
bbca27a4 UD |
19 | #include <alloca.h> |
20 | #include <ctype.h> | |
899d423e UD |
21 | #include <errno.h> |
22 | #include <fcntl.h> | |
899d423e | 23 | #include <grp.h> |
bbca27a4 UD |
24 | #include <nss.h> |
25 | #include <stdio_ext.h> | |
899d423e UD |
26 | #include <string.h> |
27 | #include <unistd.h> | |
bbca27a4 | 28 | #include <rpc/types.h> |
7603ea28 | 29 | #include <sys/param.h> |
899d423e | 30 | #include <nsswitch.h> |
bbca27a4 | 31 | #include <bits/libc-lock.h> |
cc783763 | 32 | #include <kernel-features.h> |
899d423e | 33 | |
fc9f33e3 | 34 | static service_user *ni; |
bbca27a4 UD |
35 | /* Type of the lookup function. */ |
36 | static enum nss_status (*nss_initgroups_dyn) (const char *, gid_t, | |
37 | long int *, long int *, | |
38 | gid_t **, long int, int *); | |
bbca27a4 UD |
39 | static enum nss_status (*nss_getgrnam_r) (const char *name, |
40 | struct group * grp, char *buffer, | |
41 | size_t buflen, int *errnop); | |
42 | static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp, | |
43 | char *buffer, size_t buflen, | |
44 | int *errnop); | |
ccab6d8f | 45 | static enum nss_status (*nss_setgrent) (int stayopen); |
bbca27a4 UD |
46 | static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer, |
47 | size_t buflen, int *errnop); | |
ccab6d8f | 48 | static 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 | |
63 | struct blacklist_t | |
e36b0b57 | 64 | { |
bbca27a4 UD |
65 | char *data; |
66 | int current; | |
67 | int size; | |
e36b0b57 UD |
68 | }; |
69 | ||
899d423e | 70 | struct ent_t |
bbca27a4 | 71 | { |
ccab6d8f UD |
72 | bool files; |
73 | bool need_endgrent; | |
74 | bool skip_initgroups_dyn; | |
bbca27a4 UD |
75 | FILE *stream; |
76 | struct blacklist_t blacklist; | |
899d423e UD |
77 | }; |
78 | typedef struct ent_t ent_t; | |
79 | ||
80 | ||
cc783763 UD |
81 | /* Positive if O_CLOEXEC is supported, negative if it is not supported, |
82 | zero if it is still undecided. This variable is shared with the | |
83 | other compat functions. */ | |
84 | #ifdef __ASSUME_O_CLOEXEC | |
85 | # define __compat_have_cloexec 1 | |
86 | #else | |
87 | # ifdef O_CLOEXEC | |
88 | extern int __compat_have_cloexec; | |
89 | # else | |
90 | # define __compat_have_cloexec -1 | |
91 | # endif | |
92 | #endif | |
93 | ||
899d423e UD |
94 | /* Prototypes for local functions. */ |
95 | static void blacklist_store_name (const char *, ent_t *); | |
96 | static int in_blacklist (const char *, int, ent_t *); | |
97 | ||
bbca27a4 UD |
98 | /* Initialize the NSS interface/functions. The calling function must |
99 | hold the lock. */ | |
100 | static void | |
101 | init_nss_interface (void) | |
899d423e | 102 | { |
bbca27a4 | 103 | __libc_lock_lock (lock); |
899d423e | 104 | |
bbca27a4 UD |
105 | /* Retest. */ |
106 | if (ni == NULL | |
107 | && __nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0) | |
899d423e | 108 | { |
bbca27a4 | 109 | nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn"); |
bbca27a4 UD |
110 | nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r"); |
111 | nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r"); | |
ccab6d8f | 112 | nss_setgrent = __nss_lookup_function (ni, "setgrent"); |
bbca27a4 | 113 | nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r"); |
ccab6d8f | 114 | nss_endgrent = __nss_lookup_function (ni, "endgrent"); |
899d423e UD |
115 | } |
116 | ||
bbca27a4 | 117 | __libc_lock_unlock (lock); |
899d423e UD |
118 | } |
119 | ||
120 | static enum nss_status | |
121 | internal_setgrent (ent_t *ent) | |
122 | { | |
123 | enum nss_status status = NSS_STATUS_SUCCESS; | |
124 | ||
ccab6d8f | 125 | ent->files = true; |
899d423e | 126 | |
bbca27a4 UD |
127 | if (ni == NULL) |
128 | init_nss_interface (); | |
899d423e UD |
129 | |
130 | if (ent->blacklist.data != NULL) | |
131 | { | |
132 | ent->blacklist.current = 1; | |
133 | ent->blacklist.data[0] = '|'; | |
134 | ent->blacklist.data[1] = '\0'; | |
135 | } | |
136 | else | |
137 | ent->blacklist.current = 0; | |
138 | ||
cc783763 | 139 | ent->stream = fopen ("/etc/group", "rme"); |
bbca27a4 | 140 | |
899d423e | 141 | if (ent->stream == NULL) |
bbca27a4 UD |
142 | status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; |
143 | else | |
899d423e | 144 | { |
bbca27a4 | 145 | /* We have to make sure the file is `closed on exec'. */ |
cc783763 | 146 | int result = 0; |
899d423e | 147 | |
cc783763 | 148 | if (__compat_have_cloexec <= 0) |
899d423e | 149 | { |
cc783763 UD |
150 | int flags; |
151 | result = flags = fcntl (fileno_unlocked (ent->stream), F_GETFD, 0); | |
152 | if (result >= 0) | |
153 | { | |
154 | #if defined O_CLOEXEC && !defined __ASSUME_O_CLOEXEC | |
155 | if (__compat_have_cloexec == 0) | |
156 | __compat_have_cloexec = (flags & FD_CLOEXEC) ? 1 : -1; | |
157 | ||
158 | if (__compat_have_cloexec < 0) | |
159 | #endif | |
160 | { | |
161 | flags |= FD_CLOEXEC; | |
162 | result = fcntl (fileno_unlocked (ent->stream), F_SETFD, | |
163 | flags); | |
164 | } | |
165 | } | |
bbca27a4 | 166 | } |
cc783763 | 167 | |
bbca27a4 UD |
168 | if (result < 0) |
169 | { | |
170 | /* Something went wrong. Close the stream and return a | |
171 | failure. */ | |
172 | fclose (ent->stream); | |
173 | ent->stream = NULL; | |
174 | status = NSS_STATUS_UNAVAIL; | |
899d423e | 175 | } |
bbca27a4 UD |
176 | else |
177 | /* We take care of locking ourself. */ | |
178 | __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); | |
899d423e | 179 | } |
899d423e UD |
180 | |
181 | return status; | |
182 | } | |
183 | ||
184 | ||
185 | static enum nss_status | |
186 | internal_endgrent (ent_t *ent) | |
187 | { | |
188 | if (ent->stream != NULL) | |
189 | { | |
190 | fclose (ent->stream); | |
191 | ent->stream = NULL; | |
192 | } | |
193 | ||
899d423e UD |
194 | if (ent->blacklist.data != NULL) |
195 | { | |
196 | ent->blacklist.current = 1; | |
197 | ent->blacklist.data[0] = '|'; | |
198 | ent->blacklist.data[1] = '\0'; | |
199 | } | |
200 | else | |
201 | ent->blacklist.current = 0; | |
202 | ||
ccab6d8f UD |
203 | if (ent->need_endgrent && nss_endgrent != NULL) |
204 | nss_endgrent (); | |
205 | ||
899d423e UD |
206 | return NSS_STATUS_SUCCESS; |
207 | } | |
208 | ||
ccab6d8f | 209 | /* Add new group record. */ |
bbca27a4 | 210 | static void |
ccab6d8f UD |
211 | add_group (long int *start, long int *size, gid_t **groupsp, long int limit, |
212 | gid_t gid) | |
213 | { | |
214 | gid_t *groups = *groupsp; | |
215 | ||
216 | /* Matches user. Insert this group. */ | |
a1ffb40e | 217 | if (__glibc_unlikely (*start == *size)) |
ccab6d8f UD |
218 | { |
219 | /* Need a bigger buffer. */ | |
220 | gid_t *newgroups; | |
221 | long int newsize; | |
222 | ||
223 | if (limit > 0 && *size == limit) | |
224 | /* We reached the maximum. */ | |
225 | return; | |
226 | ||
227 | if (limit <= 0) | |
228 | newsize = 2 * *size; | |
229 | else | |
230 | newsize = MIN (limit, 2 * *size); | |
231 | ||
232 | newgroups = realloc (groups, newsize * sizeof (*groups)); | |
233 | if (newgroups == NULL) | |
234 | return; | |
235 | *groupsp = groups = newgroups; | |
236 | *size = newsize; | |
237 | } | |
238 | ||
239 | groups[*start] = gid; | |
240 | *start += 1; | |
241 | } | |
242 | ||
243 | /* This function checks, if the user is a member of this group and if | |
244 | yes, add the group id to the list. Return nonzero is we couldn't | |
245 | handle the group because the user is not in the member list. */ | |
246 | static int | |
bbca27a4 UD |
247 | check_and_add_group (const char *user, gid_t group, long int *start, |
248 | long int *size, gid_t **groupsp, long int limit, | |
249 | struct group *grp) | |
899d423e | 250 | { |
bbca27a4 UD |
251 | char **member; |
252 | ||
253 | /* Don't add main group to list of groups. */ | |
254 | if (grp->gr_gid == group) | |
ccab6d8f | 255 | return 0; |
bbca27a4 UD |
256 | |
257 | for (member = grp->gr_mem; *member != NULL; ++member) | |
258 | if (strcmp (*member, user) == 0) | |
259 | { | |
ccab6d8f UD |
260 | add_group (start, size, groupsp, limit, grp->gr_gid); |
261 | return 0; | |
bbca27a4 | 262 | } |
ccab6d8f UD |
263 | |
264 | return 1; | |
899d423e UD |
265 | } |
266 | ||
3426e770 | 267 | /* Get the next group from NSS (+ entry). If the NSS module supports |
bbca27a4 | 268 | initgroups_dyn, get all entries at once. */ |
899d423e | 269 | static enum nss_status |
bbca27a4 UD |
270 | getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user, |
271 | gid_t group, long int *start, long int *size, | |
272 | gid_t **groupsp, long int limit, int *errnop) | |
899d423e | 273 | { |
bbca27a4 UD |
274 | enum nss_status status; |
275 | struct group grpbuf; | |
899d423e | 276 | |
bbca27a4 UD |
277 | /* Try nss_initgroups_dyn if supported. We also need getgrgid_r. |
278 | If this function is not supported, step through the whole group | |
279 | database with getgrent_r. */ | |
ccab6d8f | 280 | if (! ent->skip_initgroups_dyn) |
899d423e | 281 | { |
3426e770 | 282 | long int mystart = 0; |
8583671d | 283 | long int mysize = limit <= 0 ? *size : limit; |
afd7b703 | 284 | gid_t *mygroups = malloc (mysize * sizeof (gid_t)); |
3426e770 | 285 | |
afd7b703 | 286 | if (mygroups == NULL) |
3426e770 | 287 | return NSS_STATUS_TRYAGAIN; |
bbca27a4 UD |
288 | |
289 | /* For every gid in the list we get from the NSS module, | |
eaca7569 UD |
290 | get the whole group entry. We need to do this, since we |
291 | need the group name to check if it is in the blacklist. | |
292 | In worst case, this is as twice as slow as stepping with | |
293 | getgrent_r through the whole group database. But for large | |
294 | group databases this is faster, since the user can only be | |
295 | in a limited number of groups. */ | |
afd7b703 | 296 | if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups, |
bbca27a4 | 297 | limit, errnop) == NSS_STATUS_SUCCESS) |
899d423e | 298 | { |
984a4237 JL |
299 | status = NSS_STATUS_NOTFOUND; |
300 | ||
ccab6d8f UD |
301 | /* If there is no blacklist we can trust the underlying |
302 | initgroups implementation. */ | |
303 | if (ent->blacklist.current <= 1) | |
304 | for (int i = 0; i < mystart; i++) | |
305 | add_group (start, size, groupsp, limit, mygroups[i]); | |
306 | else | |
bbca27a4 | 307 | { |
ccab6d8f UD |
308 | /* A temporary buffer. We use the normal buffer, until we find |
309 | an entry, for which this buffer is to small. In this case, we | |
310 | overwrite the pointer with one to a bigger buffer. */ | |
311 | char *tmpbuf = buffer; | |
312 | size_t tmplen = buflen; | |
984a4237 | 313 | bool use_malloc = false; |
ccab6d8f UD |
314 | |
315 | for (int i = 0; i < mystart; i++) | |
85883123 | 316 | { |
ccab6d8f UD |
317 | while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, |
318 | tmpbuf, tmplen, errnop)) | |
319 | == NSS_STATUS_TRYAGAIN | |
320 | && *errnop == ERANGE) | |
984a4237 JL |
321 | { |
322 | if (__libc_use_alloca (tmplen * 2)) | |
323 | { | |
324 | if (tmpbuf == buffer) | |
325 | { | |
326 | tmplen *= 2; | |
327 | tmpbuf = __alloca (tmplen); | |
328 | } | |
329 | else | |
330 | tmpbuf = extend_alloca (tmpbuf, tmplen, tmplen * 2); | |
331 | } | |
332 | else | |
333 | { | |
334 | tmplen *= 2; | |
335 | char *newbuf = realloc (use_malloc ? tmpbuf : NULL, tmplen); | |
336 | ||
337 | if (newbuf == NULL) | |
338 | { | |
339 | status = NSS_STATUS_TRYAGAIN; | |
340 | goto done; | |
341 | } | |
342 | use_malloc = true; | |
343 | tmpbuf = newbuf; | |
344 | } | |
345 | } | |
ccab6d8f UD |
346 | |
347 | if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1)) | |
85883123 | 348 | { |
ccab6d8f | 349 | if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0)) |
984a4237 | 350 | goto done; |
ccab6d8f UD |
351 | |
352 | if (!in_blacklist (grpbuf.gr_name, | |
353 | strlen (grpbuf.gr_name), ent) | |
354 | && check_and_add_group (user, group, start, size, | |
355 | groupsp, limit, &grpbuf)) | |
356 | { | |
357 | if (nss_setgrent != NULL) | |
358 | { | |
359 | nss_setgrent (1); | |
360 | ent->need_endgrent = true; | |
361 | } | |
362 | ent->skip_initgroups_dyn = true; | |
363 | ||
364 | goto iter; | |
365 | } | |
85883123 | 366 | } |
85883123 | 367 | } |
984a4237 JL |
368 | |
369 | status = NSS_STATUS_NOTFOUND; | |
370 | ||
371 | done: | |
372 | if (use_malloc) | |
373 | free (tmpbuf); | |
bbca27a4 | 374 | } |
3426e770 | 375 | |
afd7b703 | 376 | free (mygroups); |
3426e770 | 377 | |
984a4237 | 378 | return status; |
899d423e | 379 | } |
3426e770 | 380 | |
afd7b703 | 381 | free (mygroups); |
bbca27a4 | 382 | } |
899d423e | 383 | |
bbca27a4 | 384 | /* If we come here, the NSS module does not support initgroups_dyn |
ccab6d8f UD |
385 | or we were confronted with a split group. In these cases we have |
386 | to step through the whole list ourself. */ | |
387 | iter: | |
bbca27a4 UD |
388 | do |
389 | { | |
390 | if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) != | |
391 | NSS_STATUS_SUCCESS) | |
ccab6d8f | 392 | break; |
899d423e | 393 | } |
bbca27a4 | 394 | while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)); |
899d423e | 395 | |
ccab6d8f UD |
396 | if (status == NSS_STATUS_SUCCESS) |
397 | check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); | |
398 | ||
399 | return status; | |
899d423e UD |
400 | } |
401 | ||
899d423e | 402 | static enum nss_status |
bbca27a4 UD |
403 | internal_getgrent_r (ent_t *ent, char *buffer, size_t buflen, const char *user, |
404 | gid_t group, long int *start, long int *size, | |
405 | gid_t **groupsp, long int limit, int *errnop) | |
899d423e UD |
406 | { |
407 | struct parser_data *data = (void *) buffer; | |
bbca27a4 | 408 | struct group grpbuf; |
899d423e | 409 | |
bbca27a4 UD |
410 | if (!ent->files) |
411 | return getgrent_next_nss (ent, buffer, buflen, user, group, | |
412 | start, size, groupsp, limit, errnop); | |
899d423e | 413 | |
899d423e UD |
414 | while (1) |
415 | { | |
416 | fpos_t pos; | |
417 | int parse_res = 0; | |
418 | char *p; | |
419 | ||
420 | do | |
421 | { | |
20f8e666 | 422 | /* We need at least 3 characters for one line. */ |
a1ffb40e | 423 | if (__glibc_unlikely (buflen < 3)) |
20f8e666 UD |
424 | { |
425 | erange: | |
426 | *errnop = ERANGE; | |
427 | return NSS_STATUS_TRYAGAIN; | |
428 | } | |
429 | ||
899d423e UD |
430 | fgetpos (ent->stream, &pos); |
431 | buffer[buflen - 1] = '\xff'; | |
bbca27a4 UD |
432 | p = fgets_unlocked (buffer, buflen, ent->stream); |
433 | if (p == NULL && feof_unlocked (ent->stream)) | |
34816665 UD |
434 | return NSS_STATUS_NOTFOUND; |
435 | ||
20f8e666 | 436 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
899d423e | 437 | { |
20f8e666 | 438 | erange_reset: |
899d423e | 439 | fsetpos (ent->stream, &pos); |
20f8e666 | 440 | goto erange; |
899d423e UD |
441 | } |
442 | ||
443 | /* Terminate the line for any case. */ | |
444 | buffer[buflen - 1] = '\0'; | |
445 | ||
446 | /* Skip leading blanks. */ | |
447 | while (isspace (*p)) | |
448 | ++p; | |
449 | } | |
bbca27a4 UD |
450 | while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ |
451 | /* Parse the line. If it is invalid, loop to | |
eaca7569 | 452 | get the next line of the file to parse. */ |
bbca27a4 | 453 | !(parse_res = _nss_files_parse_grent (p, &grpbuf, data, buflen, |
899d423e UD |
454 | errnop))); |
455 | ||
a1ffb40e | 456 | if (__glibc_unlikely (parse_res == -1)) |
20f8e666 UD |
457 | /* The parser ran out of space. */ |
458 | goto erange_reset; | |
899d423e | 459 | |
bbca27a4 | 460 | if (grpbuf.gr_name[0] != '+' && grpbuf.gr_name[0] != '-') |
899d423e UD |
461 | /* This is a real entry. */ |
462 | break; | |
463 | ||
464 | /* -group */ | |
bbca27a4 UD |
465 | if (grpbuf.gr_name[0] == '-' && grpbuf.gr_name[1] != '\0' |
466 | && grpbuf.gr_name[1] != '@') | |
899d423e | 467 | { |
bbca27a4 | 468 | blacklist_store_name (&grpbuf.gr_name[1], ent); |
899d423e UD |
469 | continue; |
470 | } | |
471 | ||
472 | /* +group */ | |
bbca27a4 UD |
473 | if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] != '\0' |
474 | && grpbuf.gr_name[1] != '@') | |
899d423e | 475 | { |
bbca27a4 UD |
476 | if (in_blacklist (&grpbuf.gr_name[1], |
477 | strlen (&grpbuf.gr_name[1]), ent)) | |
478 | continue; | |
479 | /* Store the group in the blacklist for the "+" at the end of | |
899d423e | 480 | /etc/group */ |
bbca27a4 UD |
481 | blacklist_store_name (&grpbuf.gr_name[1], ent); |
482 | if (nss_getgrnam_r == NULL) | |
483 | return NSS_STATUS_UNAVAIL; | |
484 | else if (nss_getgrnam_r (&grpbuf.gr_name[1], &grpbuf, buffer, | |
485 | buflen, errnop) != NSS_STATUS_SUCCESS) | |
486 | continue; | |
487 | ||
488 | check_and_add_group (user, group, start, size, groupsp, | |
489 | limit, &grpbuf); | |
490 | ||
491 | return NSS_STATUS_SUCCESS; | |
899d423e UD |
492 | } |
493 | ||
494 | /* +:... */ | |
bbca27a4 | 495 | if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0') |
899d423e | 496 | { |
ccab6d8f UD |
497 | /* If the selected module does not support getgrent_r or |
498 | initgroups_dyn, abort. We cannot find the needed group | |
499 | entries. */ | |
3056dcdb UD |
500 | if (nss_initgroups_dyn == NULL || nss_getgrgid_r == NULL) |
501 | { | |
502 | if (nss_setgrent != NULL) | |
eaca7569 | 503 | { |
3056dcdb UD |
504 | nss_setgrent (1); |
505 | ent->need_endgrent = true; | |
506 | } | |
507 | ent->skip_initgroups_dyn = true; | |
3056dcdb | 508 | |
eaca7569 UD |
509 | if (nss_getgrent_r == NULL) |
510 | return NSS_STATUS_UNAVAIL; | |
511 | } | |
ccab6d8f UD |
512 | |
513 | ent->files = false; | |
514 | ||
bbca27a4 UD |
515 | return getgrent_next_nss (ent, buffer, buflen, user, group, |
516 | start, size, groupsp, limit, errnop); | |
899d423e UD |
517 | } |
518 | } | |
519 | ||
bbca27a4 UD |
520 | check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); |
521 | ||
899d423e UD |
522 | return NSS_STATUS_SUCCESS; |
523 | } | |
524 | ||
525 | ||
899d423e | 526 | enum nss_status |
cf9e9ad9 | 527 | _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start, |
7603ea28 UD |
528 | long int *size, gid_t **groupsp, long int limit, |
529 | int *errnop) | |
899d423e | 530 | { |
899d423e UD |
531 | size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); |
532 | char *tmpbuf; | |
533 | enum nss_status status; | |
ccab6d8f | 534 | ent_t intern = { true, false, false, NULL, {NULL, 0, 0} }; |
984a4237 | 535 | bool use_malloc = false; |
899d423e UD |
536 | |
537 | status = internal_setgrent (&intern); | |
538 | if (status != NSS_STATUS_SUCCESS) | |
539 | return status; | |
540 | ||
541 | tmpbuf = __alloca (buflen); | |
542 | ||
543 | do | |
544 | { | |
bbca27a4 UD |
545 | while ((status = internal_getgrent_r (&intern, tmpbuf, buflen, |
546 | user, group, start, size, | |
547 | groupsp, limit, errnop)) | |
548 | == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) | |
984a4237 JL |
549 | if (__libc_use_alloca (buflen * 2)) |
550 | tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen); | |
551 | else | |
552 | { | |
553 | buflen *= 2; | |
554 | char *newbuf = realloc (use_malloc ? tmpbuf : NULL, buflen); | |
555 | if (newbuf == NULL) | |
556 | { | |
557 | status = NSS_STATUS_TRYAGAIN; | |
558 | goto done; | |
559 | } | |
560 | use_malloc = true; | |
561 | tmpbuf = newbuf; | |
562 | } | |
899d423e UD |
563 | } |
564 | while (status == NSS_STATUS_SUCCESS); | |
565 | ||
984a4237 JL |
566 | status = NSS_STATUS_SUCCESS; |
567 | ||
568 | done: | |
569 | if (use_malloc) | |
570 | free (tmpbuf); | |
571 | ||
899d423e UD |
572 | internal_endgrent (&intern); |
573 | ||
984a4237 | 574 | return status; |
899d423e UD |
575 | } |
576 | ||
577 | ||
578 | /* Support routines for remembering -@netgroup and -user entries. | |
579 | The names are stored in a single string with `|' as separator. */ | |
580 | static void | |
581 | blacklist_store_name (const char *name, ent_t *ent) | |
582 | { | |
583 | int namelen = strlen (name); | |
584 | char *tmp; | |
585 | ||
bbca27a4 | 586 | /* First call, setup cache. */ |
899d423e UD |
587 | if (ent->blacklist.size == 0) |
588 | { | |
589 | ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); | |
590 | ent->blacklist.data = malloc (ent->blacklist.size); | |
591 | if (ent->blacklist.data == NULL) | |
592 | return; | |
593 | ent->blacklist.data[0] = '|'; | |
594 | ent->blacklist.data[1] = '\0'; | |
595 | ent->blacklist.current = 1; | |
596 | } | |
597 | else | |
598 | { | |
599 | if (in_blacklist (name, namelen, ent)) | |
600 | return; /* no duplicates */ | |
601 | ||
602 | if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) | |
603 | { | |
604 | ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); | |
605 | tmp = realloc (ent->blacklist.data, ent->blacklist.size); | |
606 | if (tmp == NULL) | |
607 | { | |
608 | free (ent->blacklist.data); | |
609 | ent->blacklist.size = 0; | |
610 | return; | |
611 | } | |
612 | ent->blacklist.data = tmp; | |
613 | } | |
614 | } | |
615 | ||
616 | tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); | |
617 | *tmp++ = '|'; | |
618 | *tmp = '\0'; | |
619 | ent->blacklist.current += namelen + 1; | |
620 | ||
621 | return; | |
622 | } | |
623 | ||
624 | /* returns TRUE if ent->blacklist contains name, else FALSE */ | |
625 | static bool_t | |
626 | in_blacklist (const char *name, int namelen, ent_t *ent) | |
627 | { | |
628 | char buf[namelen + 3]; | |
629 | char *cp; | |
630 | ||
631 | if (ent->blacklist.data == NULL) | |
632 | return FALSE; | |
633 | ||
634 | buf[0] = '|'; | |
635 | cp = stpcpy (&buf[1], name); | |
bbca27a4 | 636 | *cp++ = '|'; |
899d423e UD |
637 | *cp = '\0'; |
638 | return strstr (ent->blacklist.data, buf) != NULL; | |
639 | } |