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/>. */
24 #include <stdio_ext.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
,
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. */
46 #define STRUCTURE group
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
63 enum nss_status setent_status
;
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
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
;
100 if (ent
->blacklist
.data
!= NULL
)
102 ent
->blacklist
.current
= 1;
103 ent
->blacklist
.data
[0] = '|';
104 ent
->blacklist
.data
[1] = '\0';
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
;
117 rewind (ent
->stream
);
119 if (needent
&& status
== NSS_STATUS_SUCCESS
&& setgrent_impl
)
120 ent
->setent_status
= setgrent_impl (stayopen
);
127 _nss_compat_setgrent (int stayopen
)
129 enum nss_status result
;
131 __libc_lock_lock (lock
);
134 init_nss_interface ();
136 result
= internal_setgrent (&ext_ent
, stayopen
, 1);
138 __libc_lock_unlock (lock
);
144 static enum nss_status __attribute_warn_unused_result__
145 internal_endgrent (ent_t
*ent
)
147 if (ent
->stream
!= NULL
)
149 fclose (ent
->stream
);
153 if (ent
->blacklist
.data
!= NULL
)
155 ent
->blacklist
.current
= 1;
156 ent
->blacklist
.data
[0] = '|';
157 ent
->blacklist
.data
[1] = '\0';
160 ent
->blacklist
.current
= 0;
162 return NSS_STATUS_SUCCESS
;
165 /* Like internal_endgrent, but preserve errno in all cases. */
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
);
175 _nss_compat_endgrent (void)
177 enum nss_status result
;
179 __libc_lock_lock (lock
);
184 result
= internal_endgrent (&ext_ent
);
186 __libc_lock_unlock (lock
);
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
)
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
,
226 if (status
!= NSS_STATUS_SUCCESS
)
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
;
249 /* We need at least 3 characters for one line. */
250 if (__glibc_unlikely (buflen
< 3))
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))
266 fsetpos (ent
->stream
, &pos
);
270 /* Terminate the line for any case. */
271 buffer
[buflen
- 1] = '\0';
273 /* Skip leading blanks. */
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
,
283 if (__glibc_unlikely (parse_res
== -1))
284 /* The parser ran out of space. */
287 if (result
->gr_name
[0] != '+' && result
->gr_name
[0] != '-')
288 /* This is a real entry. */
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
);
300 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] != '\0'
301 && result
->gr_name
[1] != '@')
303 size_t len
= strlen (result
->gr_name
);
305 enum nss_status status
;
307 /* Store the group in the blacklist for the "+" at the end of
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. */
315 else if (status
== NSS_STATUS_RETURN
/* We couldn't parse the entry*/
316 || status
== NSS_STATUS_NOTFOUND
) /* No group in NIS */
320 if (status
== NSS_STATUS_TRYAGAIN
)
321 /* The parser ran out of space. */
329 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] == '\0')
333 return getgrent_next_nss (result
, ent
, buffer
, buflen
, errnop
);
337 return NSS_STATUS_SUCCESS
;
342 _nss_compat_getgrent_r (struct group
*grp
, char *buffer
, size_t buflen
,
345 enum nss_status result
= NSS_STATUS_SUCCESS
;
347 __libc_lock_lock (lock
);
349 /* Be prepared that the setgrent function was not called before. */
351 init_nss_interface ();
353 if (ext_ent
.stream
== NULL
)
354 result
= internal_setgrent (&ext_ent
, 1, 1);
356 if (result
== NSS_STATUS_SUCCESS
)
359 result
= getgrent_next_file (grp
, &ext_ent
, buffer
, buflen
, errnop
);
361 result
= getgrent_next_nss (grp
, &ext_ent
, buffer
, buflen
, errnop
);
363 __libc_lock_unlock (lock
);
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
;
382 /* We need at least 3 characters for one line. */
383 if (__glibc_unlikely (buflen
< 3))
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))
399 fsetpos (ent
->stream
, &pos
);
403 /* Terminate the line for any case. */
404 buffer
[buflen
- 1] = '\0';
406 /* Skip leading blanks. */
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
,
416 if (__glibc_unlikely (parse_res
== -1))
417 /* The parser ran out of space. */
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
;
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
;
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 */
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 */
469 return NSS_STATUS_SUCCESS
;
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
);
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
);
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
;
513 /* We need at least 3 characters for one line. */
514 if (__glibc_unlikely (buflen
< 3))
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))
530 fsetpos (ent
->stream
, &pos
);
534 /* Terminate the line for any case. */
535 buffer
[buflen
- 1] = '\0';
537 /* Skip leading blanks. */
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
,
547 if (__glibc_unlikely (parse_res
== -1))
548 /* The parser ran out of space. */
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
;
561 if (result
->gr_name
[0] == '-' && result
->gr_name
[1] != '\0')
563 blacklist_store_name (&result
->gr_name
[1], ent
);
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
);
573 enum nss_status status
;
575 /* Store the group in the blacklist for the "+" at the end of
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
)
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
;
601 return NSS_STATUS_SUCCESS
;
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
);
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
);
629 /* Support routines for remembering -@netgroup and -user entries.
630 The names are stored in a single string with `|' as separator. */
632 blacklist_store_name (const char *name
, ent_t
*ent
)
634 int namelen
= strlen (name
);
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
)
644 ent
->blacklist
.data
[0] = '|';
645 ent
->blacklist
.data
[1] = '\0';
646 ent
->blacklist
.current
= 1;
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
);
659 free (ent
->blacklist
.data
);
660 ent
->blacklist
.size
= 0;
663 ent
->blacklist
.data
= tmp
;
667 tmp
= stpcpy (ent
->blacklist
.data
+ ent
->blacklist
.current
, name
);
670 ent
->blacklist
.current
+= namelen
+ 1;
675 /* Return whether ent->blacklist contains name. */
677 in_blacklist (const char *name
, int namelen
, ent_t
*ent
)
679 char buf
[namelen
+ 3];
682 if (ent
->blacklist
.data
== NULL
)
686 cp
= stpcpy (&buf
[1], name
);
689 return strstr (ent
->blacklist
.data
, buf
) != NULL
;