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