1 /* Copyright (C) 1996-2020 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
5 The GNU C Library is free software; you can redistribute it and/or
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.
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
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
25 #include <stdio_ext.h>
27 #include <libc-lock.h>
28 #include <kernel-features.h>
29 #include <nss_files.h>
31 NSS_DECLARE_MODULE_FUNCTIONS (compat
)
33 static service_user
*ni
;
34 static enum nss_status (*setgrent_impl
) (int stayopen
);
35 static enum nss_status (*getgrnam_r_impl
) (const char *name
,
36 struct group
* grp
, char *buffer
,
37 size_t buflen
, int *errnop
);
38 static enum nss_status (*getgrgid_r_impl
) (gid_t gid
, struct group
* grp
,
39 char *buffer
, size_t buflen
,
41 static enum nss_status (*getgrent_r_impl
) (struct group
* grp
, char *buffer
,
42 size_t buflen
, int *errnop
);
43 static enum nss_status (*endgrent_impl
) (void);
45 /* Get the declaration of the parser function. */
47 #define STRUCTURE group
49 #include <nss/nss_files/files-parse.c>
51 /* Structure for remembering -group members ... */
52 #define BLACKLIST_INITIAL_SIZE 512
53 #define BLACKLIST_INCREMENT 256
64 enum nss_status setent_status
;
66 struct blacklist_t blacklist
;
68 typedef struct ent_t ent_t
;
70 static ent_t ext_ent
= { true, NSS_STATUS_SUCCESS
, NULL
, { NULL
, 0, 0 }};
72 /* Protect global state against multiple changers. */
73 __libc_lock_define_initialized (static, lock
)
75 /* Prototypes for local functions. */
76 static void blacklist_store_name (const char *, ent_t
*);
77 static bool in_blacklist (const char *, int, ent_t
*);
79 /* Initialize the NSS interface/functions. The calling function must
82 init_nss_interface (void)
84 if (__nss_database_lookup2 ("group_compat", NULL
, "nis", &ni
) >= 0)
86 setgrent_impl
= __nss_lookup_function (ni
, "setgrent");
87 getgrnam_r_impl
= __nss_lookup_function (ni
, "getgrnam_r");
88 getgrgid_r_impl
= __nss_lookup_function (ni
, "getgrgid_r");
89 getgrent_r_impl
= __nss_lookup_function (ni
, "getgrent_r");
90 endgrent_impl
= __nss_lookup_function (ni
, "endgrent");
94 static enum nss_status
95 internal_setgrent (ent_t
*ent
, int stayopen
, int needent
)
97 enum nss_status status
= NSS_STATUS_SUCCESS
;
101 if (ent
->blacklist
.data
!= NULL
)
103 ent
->blacklist
.current
= 1;
104 ent
->blacklist
.data
[0] = '|';
105 ent
->blacklist
.data
[1] = '\0';
108 ent
->blacklist
.current
= 0;
110 if (ent
->stream
== NULL
)
112 ent
->stream
= __nss_files_fopen ("/etc/group");
114 if (ent
->stream
== NULL
)
115 status
= errno
== EAGAIN
? NSS_STATUS_TRYAGAIN
: NSS_STATUS_UNAVAIL
;
118 rewind (ent
->stream
);
120 if (needent
&& status
== NSS_STATUS_SUCCESS
&& setgrent_impl
)
121 ent
->setent_status
= setgrent_impl (stayopen
);
128 _nss_compat_setgrent (int stayopen
)
130 enum nss_status result
;
132 __libc_lock_lock (lock
);
135 init_nss_interface ();
137 result
= internal_setgrent (&ext_ent
, stayopen
, 1);
139 __libc_lock_unlock (lock
);
145 static enum nss_status __attribute_warn_unused_result__
146 internal_endgrent (ent_t
*ent
)
148 if (ent
->stream
!= NULL
)
150 fclose (ent
->stream
);
154 if (ent
->blacklist
.data
!= NULL
)
156 ent
->blacklist
.current
= 1;
157 ent
->blacklist
.data
[0] = '|';
158 ent
->blacklist
.data
[1] = '\0';
161 ent
->blacklist
.current
= 0;
163 return NSS_STATUS_SUCCESS
;
166 /* Like internal_endgrent, but preserve errno in all cases. */
168 internal_endgrent_noerror (ent_t
*ent
)
170 int saved_errno
= errno
;
171 enum nss_status unused
__attribute__ ((unused
)) = internal_endgrent (ent
);
172 __set_errno (saved_errno
);
176 _nss_compat_endgrent (void)
178 enum nss_status result
;
180 __libc_lock_lock (lock
);
185 result
= internal_endgrent (&ext_ent
);
187 __libc_lock_unlock (lock
);
192 /* get the next group from NSS (+ entry) */
193 static enum nss_status
194 getgrent_next_nss (struct group
*result
, ent_t
*ent
, char *buffer
,
195 size_t buflen
, int *errnop
)
197 if (!getgrent_r_impl
)
198 return NSS_STATUS_UNAVAIL
;
200 /* If the setgrent call failed, say so. */
201 if (ent
->setent_status
!= NSS_STATUS_SUCCESS
)
202 return ent
->setent_status
;
206 enum nss_status status
;
208 if ((status
= getgrent_r_impl (result
, buffer
, buflen
, errnop
))
209 != NSS_STATUS_SUCCESS
)
212 while (in_blacklist (result
->gr_name
, strlen (result
->gr_name
), ent
));
214 return NSS_STATUS_SUCCESS
;
217 /* This function handle the +group entrys in /etc/group */
218 static enum nss_status
219 getgrnam_plusgroup (const char *name
, struct group
*result
, ent_t
*ent
,
220 char *buffer
, size_t buflen
, int *errnop
)
222 if (!getgrnam_r_impl
)
223 return NSS_STATUS_UNAVAIL
;
225 enum nss_status status
= getgrnam_r_impl (name
, result
, buffer
, buflen
,
227 if (status
!= NSS_STATUS_SUCCESS
)
230 if (in_blacklist (result
->gr_name
, strlen (result
->gr_name
), ent
))
231 return NSS_STATUS_NOTFOUND
;
233 /* We found the entry. */
234 return NSS_STATUS_SUCCESS
;
237 static enum nss_status
238 getgrent_next_file (struct group
*result
, ent_t
*ent
,
239 char *buffer
, size_t buflen
, int *errnop
)
241 struct parser_data
*data
= (void *) buffer
;
250 /* We need at least 3 characters for one line. */
251 if (__glibc_unlikely (buflen
< 3))
255 return NSS_STATUS_TRYAGAIN
;
258 fgetpos (ent
->stream
, &pos
);
259 buffer
[buflen
- 1] = '\xff';
260 p
= fgets_unlocked (buffer
, buflen
, ent
->stream
);
261 if (p
== NULL
&& feof_unlocked (ent
->stream
))
262 return NSS_STATUS_NOTFOUND
;
264 if (p
== NULL
|| __builtin_expect (buffer
[buflen
- 1] != '\xff', 0))
267 fsetpos (ent
->stream
, &pos
);
271 /* Terminate the line for any case. */
272 buffer
[buflen
- 1] = '\0';
274 /* Skip leading blanks. */
278 while (*p
== '\0' || *p
== '#' /* Ignore empty and comment lines. */
279 /* Parse the line. If it is invalid, loop to
280 get the next line of the file to parse. */
281 || !(parse_res
= _nss_files_parse_grent (p
, result
, data
, buflen
,
284 if (__glibc_unlikely (parse_res
== -1))
285 /* The parser ran out of space. */
288 if (result
->gr_name
[0] != '+' && result
->gr_name
[0] != '-')
289 /* This is a real entry. */
293 if (result
->gr_name
[0] == '-' && result
->gr_name
[1] != '\0'
294 && result
->gr_name
[1] != '@')
296 blacklist_store_name (&result
->gr_name
[1], ent
);
301 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] != '\0'
302 && result
->gr_name
[1] != '@')
304 size_t len
= strlen (result
->gr_name
);
306 enum nss_status status
;
308 /* Store the group in the blacklist for the "+" at the end of
310 memcpy (buf
, &result
->gr_name
[1], len
);
311 status
= getgrnam_plusgroup (&result
->gr_name
[1], result
, ent
,
312 buffer
, buflen
, errnop
);
313 blacklist_store_name (buf
, ent
);
314 if (status
== NSS_STATUS_SUCCESS
) /* We found the entry. */
316 else if (status
== NSS_STATUS_RETURN
/* We couldn't parse the entry*/
317 || status
== NSS_STATUS_NOTFOUND
) /* No group in NIS */
321 if (status
== NSS_STATUS_TRYAGAIN
)
322 /* The parser ran out of space. */
330 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] == '\0')
334 return getgrent_next_nss (result
, ent
, buffer
, buflen
, errnop
);
338 return NSS_STATUS_SUCCESS
;
343 _nss_compat_getgrent_r (struct group
*grp
, char *buffer
, size_t buflen
,
346 enum nss_status result
= NSS_STATUS_SUCCESS
;
348 __libc_lock_lock (lock
);
350 /* Be prepared that the setgrent function was not called before. */
352 init_nss_interface ();
354 if (ext_ent
.stream
== NULL
)
355 result
= internal_setgrent (&ext_ent
, 1, 1);
357 if (result
== NSS_STATUS_SUCCESS
)
360 result
= getgrent_next_file (grp
, &ext_ent
, buffer
, buflen
, errnop
);
362 result
= getgrent_next_nss (grp
, &ext_ent
, buffer
, buflen
, errnop
);
364 __libc_lock_unlock (lock
);
369 /* Searches in /etc/group and the NIS/NIS+ map for a special group */
370 static enum nss_status
371 internal_getgrnam_r (const char *name
, struct group
*result
, ent_t
*ent
,
372 char *buffer
, size_t buflen
, int *errnop
)
374 struct parser_data
*data
= (void *) buffer
;
383 /* We need at least 3 characters for one line. */
384 if (__glibc_unlikely (buflen
< 3))
388 return NSS_STATUS_TRYAGAIN
;
391 fgetpos (ent
->stream
, &pos
);
392 buffer
[buflen
- 1] = '\xff';
393 p
= fgets_unlocked (buffer
, buflen
, ent
->stream
);
394 if (p
== NULL
&& feof_unlocked (ent
->stream
))
395 return NSS_STATUS_NOTFOUND
;
397 if (p
== NULL
|| __builtin_expect (buffer
[buflen
- 1] != '\xff', 0))
400 fsetpos (ent
->stream
, &pos
);
404 /* Terminate the line for any case. */
405 buffer
[buflen
- 1] = '\0';
407 /* Skip leading blanks. */
411 while (*p
== '\0' || *p
== '#' /* Ignore empty and comment lines. */
412 /* Parse the line. If it is invalid, loop to
413 get the next line of the file to parse. */
414 || !(parse_res
= _nss_files_parse_grent (p
, result
, data
, buflen
,
417 if (__glibc_unlikely (parse_res
== -1))
418 /* The parser ran out of space. */
421 /* This is a real entry. */
422 if (result
->gr_name
[0] != '+' && result
->gr_name
[0] != '-')
424 if (strcmp (result
->gr_name
, name
) == 0)
425 return NSS_STATUS_SUCCESS
;
431 if (result
->gr_name
[0] == '-' && result
->gr_name
[1] != '\0')
433 if (strcmp (&result
->gr_name
[1], name
) == 0)
434 return NSS_STATUS_NOTFOUND
;
440 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] != '\0')
442 if (strcmp (name
, &result
->gr_name
[1]) == 0)
444 enum nss_status status
;
446 status
= getgrnam_plusgroup (name
, result
, ent
,
447 buffer
, buflen
, errnop
);
448 if (status
== NSS_STATUS_RETURN
)
449 /* We couldn't parse the entry */
456 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] == '\0')
458 enum nss_status status
;
460 status
= getgrnam_plusgroup (name
, result
, ent
,
461 buffer
, buflen
, errnop
);
462 if (status
== NSS_STATUS_RETURN
)
463 /* We couldn't parse the entry */
470 return NSS_STATUS_SUCCESS
;
474 _nss_compat_getgrnam_r (const char *name
, struct group
*grp
,
475 char *buffer
, size_t buflen
, int *errnop
)
477 ent_t ent
= { true, NSS_STATUS_SUCCESS
, NULL
, { NULL
, 0, 0 }};
478 enum nss_status result
;
480 if (name
[0] == '-' || name
[0] == '+')
481 return NSS_STATUS_NOTFOUND
;
483 __libc_lock_lock (lock
);
486 init_nss_interface ();
488 __libc_lock_unlock (lock
);
490 result
= internal_setgrent (&ent
, 0, 0);
492 if (result
== NSS_STATUS_SUCCESS
)
493 result
= internal_getgrnam_r (name
, grp
, &ent
, buffer
, buflen
, errnop
);
495 internal_endgrent_noerror (&ent
);
500 /* Searches in /etc/group and the NIS/NIS+ map for a special group id */
501 static enum nss_status
502 internal_getgrgid_r (gid_t gid
, struct group
*result
, ent_t
*ent
,
503 char *buffer
, size_t buflen
, int *errnop
)
505 struct parser_data
*data
= (void *) buffer
;
514 /* We need at least 3 characters for one line. */
515 if (__glibc_unlikely (buflen
< 3))
519 return NSS_STATUS_TRYAGAIN
;
522 fgetpos (ent
->stream
, &pos
);
523 buffer
[buflen
- 1] = '\xff';
524 p
= fgets_unlocked (buffer
, buflen
, ent
->stream
);
525 if (p
== NULL
&& feof_unlocked (ent
->stream
))
526 return NSS_STATUS_NOTFOUND
;
528 if (p
== NULL
|| __builtin_expect (buffer
[buflen
- 1] != '\xff', 0))
531 fsetpos (ent
->stream
, &pos
);
535 /* Terminate the line for any case. */
536 buffer
[buflen
- 1] = '\0';
538 /* Skip leading blanks. */
542 while (*p
== '\0' || *p
== '#' /* Ignore empty and comment lines. */
543 /* Parse the line. If it is invalid, loop to
544 get the next line of the file to parse. */
545 || !(parse_res
= _nss_files_parse_grent (p
, result
, data
, buflen
,
548 if (__glibc_unlikely (parse_res
== -1))
549 /* The parser ran out of space. */
552 /* This is a real entry. */
553 if (result
->gr_name
[0] != '+' && result
->gr_name
[0] != '-')
555 if (result
->gr_gid
== gid
)
556 return NSS_STATUS_SUCCESS
;
562 if (result
->gr_name
[0] == '-' && result
->gr_name
[1] != '\0')
564 blacklist_store_name (&result
->gr_name
[1], ent
);
569 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] != '\0')
571 /* Yes, no +1, see the memcpy call below. */
572 size_t len
= strlen (result
->gr_name
);
574 enum nss_status status
;
576 /* Store the group in the blacklist for the "+" at the end of
578 memcpy (buf
, &result
->gr_name
[1], len
);
579 status
= getgrnam_plusgroup (&result
->gr_name
[1], result
, ent
,
580 buffer
, buflen
, errnop
);
581 blacklist_store_name (buf
, ent
);
582 if (status
== NSS_STATUS_SUCCESS
&& result
->gr_gid
== gid
)
588 if (result
->gr_name
[0] == '+' && result
->gr_name
[1] == '\0')
590 if (!getgrgid_r_impl
)
591 return NSS_STATUS_UNAVAIL
;
593 enum nss_status status
= getgrgid_r_impl (gid
, result
,
594 buffer
, buflen
, errnop
);
595 if (status
== NSS_STATUS_RETURN
) /* We couldn't parse the entry */
596 return NSS_STATUS_NOTFOUND
;
602 return NSS_STATUS_SUCCESS
;
606 _nss_compat_getgrgid_r (gid_t gid
, struct group
*grp
,
607 char *buffer
, size_t buflen
, int *errnop
)
609 ent_t ent
= { true, NSS_STATUS_SUCCESS
, NULL
, { NULL
, 0, 0 }};
610 enum nss_status result
;
612 __libc_lock_lock (lock
);
615 init_nss_interface ();
617 __libc_lock_unlock (lock
);
619 result
= internal_setgrent (&ent
, 0, 0);
621 if (result
== NSS_STATUS_SUCCESS
)
622 result
= internal_getgrgid_r (gid
, grp
, &ent
, buffer
, buflen
, errnop
);
624 internal_endgrent_noerror (&ent
);
630 /* Support routines for remembering -@netgroup and -user entries.
631 The names are stored in a single string with `|' as separator. */
633 blacklist_store_name (const char *name
, ent_t
*ent
)
635 int namelen
= strlen (name
);
638 /* first call, setup cache */
639 if (ent
->blacklist
.size
== 0)
641 ent
->blacklist
.size
= MAX (BLACKLIST_INITIAL_SIZE
, 2 * namelen
);
642 ent
->blacklist
.data
= malloc (ent
->blacklist
.size
);
643 if (ent
->blacklist
.data
== NULL
)
645 ent
->blacklist
.data
[0] = '|';
646 ent
->blacklist
.data
[1] = '\0';
647 ent
->blacklist
.current
= 1;
651 if (in_blacklist (name
, namelen
, ent
))
652 return; /* no duplicates */
654 if (ent
->blacklist
.current
+ namelen
+ 1 >= ent
->blacklist
.size
)
656 ent
->blacklist
.size
+= MAX (BLACKLIST_INCREMENT
, 2 * namelen
);
657 tmp
= realloc (ent
->blacklist
.data
, ent
->blacklist
.size
);
660 free (ent
->blacklist
.data
);
661 ent
->blacklist
.size
= 0;
664 ent
->blacklist
.data
= tmp
;
668 tmp
= stpcpy (ent
->blacklist
.data
+ ent
->blacklist
.current
, name
);
671 ent
->blacklist
.current
+= namelen
+ 1;
676 /* Return whether ent->blacklist contains name. */
678 in_blacklist (const char *name
, int namelen
, ent_t
*ent
)
680 char buf
[namelen
+ 3];
683 if (ent
->blacklist
.data
== NULL
)
687 cp
= stpcpy (&buf
[1], name
);
690 return strstr (ent
->blacklist
.data
, buf
) != NULL
;