elf: Remove -fno-tree-loop-distribute-patterns usage on dl-support
[glibc.git] / nss / nss_compat / compat-grp.c
blob0050f7f3402ed1dbdf99e325fff4cf0c905285f0
1 /* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <grp.h>
22 #include <nss.h>
23 #include <nsswitch.h>
24 #include <stdio_ext.h>
25 #include <string.h>
26 #include <libc-lock.h>
27 #include <kernel-features.h>
28 #include <nss_files.h>
30 NSS_DECLARE_MODULE_FUNCTIONS (compat)
32 static nss_action_list ni;
33 static enum nss_status (*setgrent_impl) (int stayopen);
34 static enum nss_status (*getgrnam_r_impl) (const char *name,
35 struct group * grp, char *buffer,
36 size_t buflen, int *errnop);
37 static enum nss_status (*getgrgid_r_impl) (gid_t gid, struct group * grp,
38 char *buffer, size_t buflen,
39 int *errnop);
40 static enum nss_status (*getgrent_r_impl) (struct group * grp, char *buffer,
41 size_t buflen, int *errnop);
42 static enum nss_status (*endgrent_impl) (void);
44 /* Get the declaration of the parser function. */
45 #define ENTNAME grent
46 #define STRUCTURE group
47 #define EXTERN_PARSER
48 #include <nss/nss_files/files-parse.c>
50 /* Structure for remembering -group members ... */
51 #define BLACKLIST_INITIAL_SIZE 512
52 #define BLACKLIST_INCREMENT 256
53 struct blacklist_t
55 char *data;
56 int current;
57 int size;
60 struct ent_t
62 bool files;
63 enum nss_status setent_status;
64 FILE *stream;
65 struct blacklist_t blacklist;
67 typedef struct ent_t ent_t;
69 static ent_t ext_ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }};
71 /* Protect global state against multiple changers. */
72 __libc_lock_define_initialized (static, lock)
74 /* Prototypes for local functions. */
75 static void blacklist_store_name (const char *, ent_t *);
76 static bool in_blacklist (const char *, int, ent_t *);
78 /* Initialize the NSS interface/functions. The calling function must
79 hold the lock. */
80 static void
81 init_nss_interface (void)
83 if (__nss_database_get (nss_database_group_compat, &ni))
85 setgrent_impl = __nss_lookup_function (ni, "setgrent");
86 getgrnam_r_impl = __nss_lookup_function (ni, "getgrnam_r");
87 getgrgid_r_impl = __nss_lookup_function (ni, "getgrgid_r");
88 getgrent_r_impl = __nss_lookup_function (ni, "getgrent_r");
89 endgrent_impl = __nss_lookup_function (ni, "endgrent");
93 static enum nss_status
94 internal_setgrent (ent_t *ent, int stayopen, int needent)
96 enum nss_status status = NSS_STATUS_SUCCESS;
98 ent->files = true;
100 if (ent->blacklist.data != NULL)
102 ent->blacklist.current = 1;
103 ent->blacklist.data[0] = '|';
104 ent->blacklist.data[1] = '\0';
106 else
107 ent->blacklist.current = 0;
109 if (ent->stream == NULL)
111 ent->stream = __nss_files_fopen ("/etc/group");
113 if (ent->stream == NULL)
114 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
116 else
117 rewind (ent->stream);
119 if (needent && status == NSS_STATUS_SUCCESS && setgrent_impl)
120 ent->setent_status = setgrent_impl (stayopen);
122 return status;
126 enum nss_status
127 _nss_compat_setgrent (int stayopen)
129 enum nss_status result;
131 __libc_lock_lock (lock);
133 if (ni == NULL)
134 init_nss_interface ();
136 result = internal_setgrent (&ext_ent, stayopen, 1);
138 __libc_lock_unlock (lock);
140 return result;
144 static enum nss_status __attribute_warn_unused_result__
145 internal_endgrent (ent_t *ent)
147 if (ent->stream != NULL)
149 fclose (ent->stream);
150 ent->stream = NULL;
153 if (ent->blacklist.data != NULL)
155 ent->blacklist.current = 1;
156 ent->blacklist.data[0] = '|';
157 ent->blacklist.data[1] = '\0';
159 else
160 ent->blacklist.current = 0;
162 return NSS_STATUS_SUCCESS;
165 /* Like internal_endgrent, but preserve errno in all cases. */
166 static void
167 internal_endgrent_noerror (ent_t *ent)
169 int saved_errno = errno;
170 enum nss_status unused __attribute__ ((unused)) = internal_endgrent (ent);
171 __set_errno (saved_errno);
174 enum nss_status
175 _nss_compat_endgrent (void)
177 enum nss_status result;
179 __libc_lock_lock (lock);
181 if (endgrent_impl)
182 endgrent_impl ();
184 result = internal_endgrent (&ext_ent);
186 __libc_lock_unlock (lock);
188 return result;
191 /* get the next group from NSS (+ entry) */
192 static enum nss_status
193 getgrent_next_nss (struct group *result, ent_t *ent, char *buffer,
194 size_t buflen, int *errnop)
196 if (!getgrent_r_impl)
197 return NSS_STATUS_UNAVAIL;
199 /* If the setgrent call failed, say so. */
200 if (ent->setent_status != NSS_STATUS_SUCCESS)
201 return ent->setent_status;
205 enum nss_status status;
207 if ((status = getgrent_r_impl (result, buffer, buflen, errnop))
208 != NSS_STATUS_SUCCESS)
209 return status;
211 while (in_blacklist (result->gr_name, strlen (result->gr_name), ent));
213 return NSS_STATUS_SUCCESS;
216 /* This function handle the +group entrys in /etc/group */
217 static enum nss_status
218 getgrnam_plusgroup (const char *name, struct group *result, ent_t *ent,
219 char *buffer, size_t buflen, int *errnop)
221 if (!getgrnam_r_impl)
222 return NSS_STATUS_UNAVAIL;
224 enum nss_status status = getgrnam_r_impl (name, result, buffer, buflen,
225 errnop);
226 if (status != NSS_STATUS_SUCCESS)
227 return status;
229 if (in_blacklist (result->gr_name, strlen (result->gr_name), ent))
230 return NSS_STATUS_NOTFOUND;
232 /* We found the entry. */
233 return NSS_STATUS_SUCCESS;
236 static enum nss_status
237 getgrent_next_file (struct group *result, ent_t *ent,
238 char *buffer, size_t buflen, int *errnop)
240 struct parser_data *data = (void *) buffer;
241 while (1)
243 fpos_t pos;
244 int parse_res = 0;
245 char *p;
249 /* We need at least 3 characters for one line. */
250 if (__glibc_unlikely (buflen < 3))
252 erange:
253 *errnop = ERANGE;
254 return NSS_STATUS_TRYAGAIN;
257 fgetpos (ent->stream, &pos);
258 buffer[buflen - 1] = '\xff';
259 p = fgets_unlocked (buffer, buflen, ent->stream);
260 if (p == NULL && feof_unlocked (ent->stream))
261 return NSS_STATUS_NOTFOUND;
263 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
265 erange_reset:
266 fsetpos (ent->stream, &pos);
267 goto erange;
270 /* Terminate the line for any case. */
271 buffer[buflen - 1] = '\0';
273 /* Skip leading blanks. */
274 while (isspace (*p))
275 ++p;
277 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
278 /* Parse the line. If it is invalid, loop to
279 get the next line of the file to parse. */
280 || !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
281 errnop)));
283 if (__glibc_unlikely (parse_res == -1))
284 /* The parser ran out of space. */
285 goto erange_reset;
287 if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
288 /* This is a real entry. */
289 break;
291 /* -group */
292 if (result->gr_name[0] == '-' && result->gr_name[1] != '\0'
293 && result->gr_name[1] != '@')
295 blacklist_store_name (&result->gr_name[1], ent);
296 continue;
299 /* +group */
300 if (result->gr_name[0] == '+' && result->gr_name[1] != '\0'
301 && result->gr_name[1] != '@')
303 size_t len = strlen (result->gr_name);
304 char buf[len];
305 enum nss_status status;
307 /* Store the group in the blacklist for the "+" at the end of
308 /etc/group */
309 memcpy (buf, &result->gr_name[1], len);
310 status = getgrnam_plusgroup (&result->gr_name[1], result, ent,
311 buffer, buflen, errnop);
312 blacklist_store_name (buf, ent);
313 if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
314 break;
315 else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry*/
316 || status == NSS_STATUS_NOTFOUND) /* No group in NIS */
317 continue;
318 else
320 if (status == NSS_STATUS_TRYAGAIN)
321 /* The parser ran out of space. */
322 goto erange_reset;
324 return status;
328 /* +:... */
329 if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
331 ent->files = false;
333 return getgrent_next_nss (result, ent, buffer, buflen, errnop);
337 return NSS_STATUS_SUCCESS;
341 enum nss_status
342 _nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen,
343 int *errnop)
345 enum nss_status result = NSS_STATUS_SUCCESS;
347 __libc_lock_lock (lock);
349 /* Be prepared that the setgrent function was not called before. */
350 if (ni == NULL)
351 init_nss_interface ();
353 if (ext_ent.stream == NULL)
354 result = internal_setgrent (&ext_ent, 1, 1);
356 if (result == NSS_STATUS_SUCCESS)
358 if (ext_ent.files)
359 result = getgrent_next_file (grp, &ext_ent, buffer, buflen, errnop);
360 else
361 result = getgrent_next_nss (grp, &ext_ent, buffer, buflen, errnop);
363 __libc_lock_unlock (lock);
365 return result;
368 /* Searches in /etc/group and the NIS/NIS+ map for a special group */
369 static enum nss_status
370 internal_getgrnam_r (const char *name, struct group *result, ent_t *ent,
371 char *buffer, size_t buflen, int *errnop)
373 struct parser_data *data = (void *) buffer;
374 while (1)
376 fpos_t pos;
377 int parse_res = 0;
378 char *p;
382 /* We need at least 3 characters for one line. */
383 if (__glibc_unlikely (buflen < 3))
385 erange:
386 *errnop = ERANGE;
387 return NSS_STATUS_TRYAGAIN;
390 fgetpos (ent->stream, &pos);
391 buffer[buflen - 1] = '\xff';
392 p = fgets_unlocked (buffer, buflen, ent->stream);
393 if (p == NULL && feof_unlocked (ent->stream))
394 return NSS_STATUS_NOTFOUND;
396 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
398 erange_reset:
399 fsetpos (ent->stream, &pos);
400 goto erange;
403 /* Terminate the line for any case. */
404 buffer[buflen - 1] = '\0';
406 /* Skip leading blanks. */
407 while (isspace (*p))
408 ++p;
410 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
411 /* Parse the line. If it is invalid, loop to
412 get the next line of the file to parse. */
413 || !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
414 errnop)));
416 if (__glibc_unlikely (parse_res == -1))
417 /* The parser ran out of space. */
418 goto erange_reset;
420 /* This is a real entry. */
421 if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
423 if (strcmp (result->gr_name, name) == 0)
424 return NSS_STATUS_SUCCESS;
425 else
426 continue;
429 /* -group */
430 if (result->gr_name[0] == '-' && result->gr_name[1] != '\0')
432 if (strcmp (&result->gr_name[1], name) == 0)
433 return NSS_STATUS_NOTFOUND;
434 else
435 continue;
438 /* +group */
439 if (result->gr_name[0] == '+' && result->gr_name[1] != '\0')
441 if (strcmp (name, &result->gr_name[1]) == 0)
443 enum nss_status status;
445 status = getgrnam_plusgroup (name, result, ent,
446 buffer, buflen, errnop);
447 if (status == NSS_STATUS_RETURN)
448 /* We couldn't parse the entry */
449 continue;
450 else
451 return status;
454 /* +:... */
455 if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
457 enum nss_status status;
459 status = getgrnam_plusgroup (name, result, ent,
460 buffer, buflen, errnop);
461 if (status == NSS_STATUS_RETURN)
462 /* We couldn't parse the entry */
463 continue;
464 else
465 return status;
469 return NSS_STATUS_SUCCESS;
472 enum nss_status
473 _nss_compat_getgrnam_r (const char *name, struct group *grp,
474 char *buffer, size_t buflen, int *errnop)
476 ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }};
477 enum nss_status result;
479 if (name[0] == '-' || name[0] == '+')
480 return NSS_STATUS_NOTFOUND;
482 __libc_lock_lock (lock);
484 if (ni == NULL)
485 init_nss_interface ();
487 __libc_lock_unlock (lock);
489 result = internal_setgrent (&ent, 0, 0);
491 if (result == NSS_STATUS_SUCCESS)
492 result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop);
494 internal_endgrent_noerror (&ent);
496 return result;
499 /* Searches in /etc/group and the NIS/NIS+ map for a special group id */
500 static enum nss_status
501 internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent,
502 char *buffer, size_t buflen, int *errnop)
504 struct parser_data *data = (void *) buffer;
505 while (1)
507 fpos_t pos;
508 int parse_res = 0;
509 char *p;
513 /* We need at least 3 characters for one line. */
514 if (__glibc_unlikely (buflen < 3))
516 erange:
517 *errnop = ERANGE;
518 return NSS_STATUS_TRYAGAIN;
521 fgetpos (ent->stream, &pos);
522 buffer[buflen - 1] = '\xff';
523 p = fgets_unlocked (buffer, buflen, ent->stream);
524 if (p == NULL && feof_unlocked (ent->stream))
525 return NSS_STATUS_NOTFOUND;
527 if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
529 erange_reset:
530 fsetpos (ent->stream, &pos);
531 goto erange;
534 /* Terminate the line for any case. */
535 buffer[buflen - 1] = '\0';
537 /* Skip leading blanks. */
538 while (isspace (*p))
539 ++p;
541 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
542 /* Parse the line. If it is invalid, loop to
543 get the next line of the file to parse. */
544 || !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
545 errnop)));
547 if (__glibc_unlikely (parse_res == -1))
548 /* The parser ran out of space. */
549 goto erange_reset;
551 /* This is a real entry. */
552 if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
554 if (result->gr_gid == gid)
555 return NSS_STATUS_SUCCESS;
556 else
557 continue;
560 /* -group */
561 if (result->gr_name[0] == '-' && result->gr_name[1] != '\0')
563 blacklist_store_name (&result->gr_name[1], ent);
564 continue;
567 /* +group */
568 if (result->gr_name[0] == '+' && result->gr_name[1] != '\0')
570 /* Yes, no +1, see the memcpy call below. */
571 size_t len = strlen (result->gr_name);
572 char buf[len];
573 enum nss_status status;
575 /* Store the group in the blacklist for the "+" at the end of
576 /etc/group */
577 memcpy (buf, &result->gr_name[1], len);
578 status = getgrnam_plusgroup (&result->gr_name[1], result, ent,
579 buffer, buflen, errnop);
580 blacklist_store_name (buf, ent);
581 if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid)
582 break;
583 else
584 continue;
586 /* +:... */
587 if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
589 if (!getgrgid_r_impl)
590 return NSS_STATUS_UNAVAIL;
592 enum nss_status status = getgrgid_r_impl (gid, result,
593 buffer, buflen, errnop);
594 if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
595 return NSS_STATUS_NOTFOUND;
596 else
597 return status;
601 return NSS_STATUS_SUCCESS;
604 enum nss_status
605 _nss_compat_getgrgid_r (gid_t gid, struct group *grp,
606 char *buffer, size_t buflen, int *errnop)
608 ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }};
609 enum nss_status result;
611 __libc_lock_lock (lock);
613 if (ni == NULL)
614 init_nss_interface ();
616 __libc_lock_unlock (lock);
618 result = internal_setgrent (&ent, 0, 0);
620 if (result == NSS_STATUS_SUCCESS)
621 result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop);
623 internal_endgrent_noerror (&ent);
625 return result;
629 /* Support routines for remembering -@netgroup and -user entries.
630 The names are stored in a single string with `|' as separator. */
631 static void
632 blacklist_store_name (const char *name, ent_t *ent)
634 int namelen = strlen (name);
635 char *tmp;
637 /* first call, setup cache */
638 if (ent->blacklist.size == 0)
640 ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
641 ent->blacklist.data = malloc (ent->blacklist.size);
642 if (ent->blacklist.data == NULL)
643 return;
644 ent->blacklist.data[0] = '|';
645 ent->blacklist.data[1] = '\0';
646 ent->blacklist.current = 1;
648 else
650 if (in_blacklist (name, namelen, ent))
651 return; /* no duplicates */
653 if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
655 ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
656 tmp = realloc (ent->blacklist.data, ent->blacklist.size);
657 if (tmp == NULL)
659 free (ent->blacklist.data);
660 ent->blacklist.size = 0;
661 return;
663 ent->blacklist.data = tmp;
667 tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
668 *tmp++ = '|';
669 *tmp = '\0';
670 ent->blacklist.current += namelen + 1;
672 return;
675 /* Return whether ent->blacklist contains name. */
676 static bool
677 in_blacklist (const char *name, int namelen, ent_t *ent)
679 char buf[namelen + 3];
680 char *cp;
682 if (ent->blacklist.data == NULL)
683 return false;
685 buf[0] = '|';
686 cp = stpcpy (&buf[1], name);
687 *cp++ = '|';
688 *cp = '\0';
689 return strstr (ent->blacklist.data, buf) != NULL;