1 /* Copyright (C) 1996-2021 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/>. */
20 #include <libc-lock.h>
29 #include <nscd/nscd_proto.h>
32 /* Protect above variable against multiple uses at the same time. */
33 __libc_lock_define_initialized (static, lock
)
35 /* The whole information for the set/get/endnetgrent functions are
36 kept in this structure. */
37 static struct __netgrent dataset
;
39 /* Set up NIP to run through the services. Return nonzero if there are no
42 setup (void **fctp
, nss_action_list
*nipp
)
46 no_more
= __nss_netgroup_lookup2 (nipp
, "setnetgrent", NULL
, fctp
);
51 /* Free used memory. */
53 free_memory (struct __netgrent
*data
)
55 while (data
->known_groups
!= NULL
)
57 struct name_list
*tmp
= data
->known_groups
;
58 data
->known_groups
= data
->known_groups
->next
;
62 while (data
->needed_groups
!= NULL
)
64 struct name_list
*tmp
= data
->needed_groups
;
65 data
->needed_groups
= data
->needed_groups
->next
;
71 endnetgrent_hook (struct __netgrent
*datap
)
73 enum nss_status (*endfct
) (struct __netgrent
*);
75 if (datap
->nip
== NULL
|| datap
->nip
== (nss_action_list
) -1l)
78 endfct
= __nss_lookup_function (datap
->nip
, "endnetgrent");
80 (void) (*endfct
) (datap
);
85 __internal_setnetgrent_reuse (const char *group
, struct __netgrent
*datap
,
90 enum nss_status (*f
) (const char *, struct __netgrent
*);
93 enum nss_status status
= NSS_STATUS_UNAVAIL
;
94 struct name_list
*new_elem
;
96 /* Free data from previous service. */
97 endnetgrent_hook (datap
);
99 /* Cycle through all the services and run their setnetgrent functions. */
100 int no_more
= setup (&fct
.ptr
, &datap
->nip
);
103 assert (datap
->data
== NULL
);
105 /* Ignore status, we force check in `__nss_next2'. */
106 status
= DL_CALL_FCT (*fct
.f
, (group
, datap
));
108 nss_action_list old_nip
= datap
->nip
;
109 no_more
= __nss_next2 (&datap
->nip
, "setnetgrent", NULL
, &fct
.ptr
,
112 if (status
== NSS_STATUS_SUCCESS
&& ! no_more
)
114 enum nss_status (*endfct
) (struct __netgrent
*);
116 endfct
= __nss_lookup_function (old_nip
, "endnetgrent");
118 (void) DL_CALL_FCT (*endfct
, (datap
));
122 /* Add the current group to the list of known groups. */
123 size_t group_len
= strlen (group
) + 1;
124 new_elem
= (struct name_list
*) malloc (sizeof (struct name_list
)
126 if (new_elem
== NULL
)
129 status
= NSS_STATUS_TRYAGAIN
;
133 new_elem
->next
= datap
->known_groups
;
134 memcpy (new_elem
->name
, group
, group_len
);
135 datap
->known_groups
= new_elem
;
138 return status
== NSS_STATUS_SUCCESS
;
142 __internal_setnetgrent (const char *group
, struct __netgrent
*datap
)
144 /* Free list of all netgroup names from last run. */
147 return __internal_setnetgrent_reuse (group
, datap
, &errno
);
149 libc_hidden_def (__internal_setnetgrent
)
152 nscd_setnetgrent (const char *group
)
155 if (__nss_not_use_nscd_netgroup
> 0
156 && ++__nss_not_use_nscd_netgroup
> NSS_NSCD_RETRY
)
157 __nss_not_use_nscd_netgroup
= 0;
159 if (!__nss_not_use_nscd_netgroup
160 && !__nss_database_custom
[NSS_DBSIDX_netgroup
])
161 return __nscd_setnetgrent (group
, &dataset
);
167 setnetgrent (const char *group
)
171 __libc_lock_lock (lock
);
173 result
= nscd_setnetgrent (group
);
175 result
= __internal_setnetgrent (group
, &dataset
);
177 __libc_lock_unlock (lock
);
183 __internal_endnetgrent (struct __netgrent
*datap
)
185 endnetgrent_hook (datap
);
186 /* Now free list of all netgroup names from last run. */
189 libc_hidden_def (__internal_endnetgrent
)
195 __libc_lock_lock (lock
);
197 __internal_endnetgrent (&dataset
);
199 __libc_lock_unlock (lock
);
204 get_nonempty_val (const char *in
)
211 static enum nss_status
212 nscd_getnetgrent (struct __netgrent
*datap
, char *buffer
, size_t buflen
,
215 if (datap
->cursor
>= datap
->data
+ datap
->data_size
)
216 return NSS_STATUS_UNAVAIL
;
218 datap
->type
= triple_val
;
219 datap
->val
.triple
.host
= get_nonempty_val (datap
->cursor
);
220 datap
->cursor
= (char *) __rawmemchr (datap
->cursor
, '\0') + 1;
221 datap
->val
.triple
.user
= get_nonempty_val (datap
->cursor
);
222 datap
->cursor
= (char *) __rawmemchr (datap
->cursor
, '\0') + 1;
223 datap
->val
.triple
.domain
= get_nonempty_val (datap
->cursor
);
224 datap
->cursor
= (char *) __rawmemchr (datap
->cursor
, '\0') + 1;
226 return NSS_STATUS_SUCCESS
;
231 __internal_getnetgrent_r (char **hostp
, char **userp
, char **domainp
,
232 struct __netgrent
*datap
,
233 char *buffer
, size_t buflen
, int *errnop
)
235 enum nss_status (*fct
) (struct __netgrent
*, char *, size_t, int *);
237 /* Initialize status to return if no more functions are found. */
238 enum nss_status status
= NSS_STATUS_NOTFOUND
;
240 /* Run through available functions, starting with the same function last
241 run. We will repeat each function as long as it succeeds, and then go
242 on to the next service action. */
243 int no_more
= datap
->nip
== NULL
;
247 /* This bogus function pointer is a special marker left by
248 __nscd_setnetgrent to tell us to use the data it left
249 before considering any modules. */
250 if (datap
->nip
== (nss_action_list
) -1l)
251 fct
= nscd_getnetgrent
;
255 fct
= __nss_lookup_function (datap
->nip
, "getnetgrent_r");
256 no_more
= fct
== NULL
;
261 status
= DL_CALL_FCT (*fct
, (datap
, buffer
, buflen
, &errno
));
263 if (status
== NSS_STATUS_RETURN
264 /* The service returned a NOTFOUND, but there are more groups that
265 we need to resolve before we give up. */
266 || (status
== NSS_STATUS_NOTFOUND
&& datap
->needed_groups
!= NULL
))
268 /* This was the last one for this group. Look at next group
271 while (datap
->needed_groups
!= NULL
&& ! found
)
273 struct name_list
*tmp
= datap
->needed_groups
;
274 datap
->needed_groups
= datap
->needed_groups
->next
;
275 tmp
->next
= datap
->known_groups
;
276 datap
->known_groups
= tmp
;
278 found
= __internal_setnetgrent_reuse (datap
->known_groups
->name
,
282 if (found
&& datap
->nip
!= NULL
)
284 fct
= __nss_lookup_function (datap
->nip
, "getnetgrent_r");
289 else if (status
== NSS_STATUS_SUCCESS
&& datap
->type
== group_val
)
291 /* The last entry was a name of another netgroup. */
292 struct name_list
*namep
;
294 /* Ignore if we've seen the name before. */
295 for (namep
= datap
->known_groups
; namep
!= NULL
;
297 if (strcmp (datap
->val
.group
, namep
->name
) == 0)
300 for (namep
= datap
->needed_groups
; namep
!= NULL
;
302 if (strcmp (datap
->val
.group
, namep
->name
) == 0)
308 size_t group_len
= strlen (datap
->val
.group
) + 1;
309 namep
= (struct name_list
*) malloc (sizeof (struct name_list
)
312 /* We are out of memory. */
313 status
= NSS_STATUS_RETURN
;
316 namep
->next
= datap
->needed_groups
;
317 memcpy (namep
->name
, datap
->val
.group
, group_len
);
318 datap
->needed_groups
= namep
;
319 /* And get the next entry. */
327 if (status
== NSS_STATUS_SUCCESS
)
329 *hostp
= (char *) datap
->val
.triple
.host
;
330 *userp
= (char *) datap
->val
.triple
.user
;
331 *domainp
= (char *) datap
->val
.triple
.domain
;
334 return status
== NSS_STATUS_SUCCESS
? 1 : 0;
336 libc_hidden_def (__internal_getnetgrent_r
)
338 /* The real entry point. */
340 __getnetgrent_r (char **hostp
, char **userp
, char **domainp
,
341 char *buffer
, size_t buflen
)
343 enum nss_status status
;
345 __libc_lock_lock (lock
);
347 status
= __internal_getnetgrent_r (hostp
, userp
, domainp
, &dataset
,
348 buffer
, buflen
, &errno
);
350 __libc_lock_unlock (lock
);
354 weak_alias (__getnetgrent_r
, getnetgrent_r
)
356 /* Test whether given (host,user,domain) triple is in NETGROUP. */
358 innetgr (const char *netgroup
, const char *host
, const char *user
,
362 if (__nss_not_use_nscd_netgroup
> 0
363 && ++__nss_not_use_nscd_netgroup
> NSS_NSCD_RETRY
)
364 __nss_not_use_nscd_netgroup
= 0;
366 if (!__nss_not_use_nscd_netgroup
367 && !__nss_database_custom
[NSS_DBSIDX_netgroup
])
369 int result
= __nscd_innetgr (netgroup
, host
, user
, domain
);
377 enum nss_status (*f
) (const char *, struct __netgrent
*);
380 void (*endfct
) (struct __netgrent
*);
381 int (*getfct
) (struct __netgrent
*, char *, size_t, int *);
382 struct __netgrent entry
;
384 const char *current_group
= netgroup
;
386 memset (&entry
, '\0', sizeof (entry
));
388 /* Walk through the services until we found an answer or we shall
389 not work further. We can do some optimization here. Since all
390 services must provide the `setnetgrent' function we can do all
391 the work during one walk through the service list. */
394 int no_more
= setup (&setfct
.ptr
, &entry
.nip
);
397 assert (entry
.data
== NULL
);
400 enum nss_status status
= DL_CALL_FCT (*setfct
.f
,
401 (current_group
, &entry
));
403 if (status
== NSS_STATUS_SUCCESS
404 && (getfct
= __nss_lookup_function (entry
.nip
, "getnetgrent_r"))
409 while (DL_CALL_FCT (*getfct
,
410 (&entry
, buffer
, sizeof buffer
, &errno
))
411 == NSS_STATUS_SUCCESS
)
413 if (entry
.type
== group_val
)
415 /* Make sure we haven't seen the name before. */
416 struct name_list
*namep
;
418 for (namep
= entry
.known_groups
; namep
!= NULL
;
420 if (strcmp (entry
.val
.group
, namep
->name
) == 0)
423 for (namep
= entry
.needed_groups
; namep
!= NULL
;
425 if (strcmp (entry
.val
.group
, namep
->name
) == 0)
428 && strcmp (netgroup
, entry
.val
.group
) != 0)
430 size_t group_len
= strlen (entry
.val
.group
) + 1;
432 (struct name_list
*) malloc (sizeof (*namep
)
436 /* Out of memory, simply return. */
441 namep
->next
= entry
.needed_groups
;
442 memcpy (namep
->name
, entry
.val
.group
, group_len
);
443 entry
.needed_groups
= namep
;
448 if ((entry
.val
.triple
.host
== NULL
|| host
== NULL
449 || __strcasecmp (entry
.val
.triple
.host
, host
) == 0)
450 && (entry
.val
.triple
.user
== NULL
|| user
== NULL
451 || strcmp (entry
.val
.triple
.user
, user
) == 0)
452 && (entry
.val
.triple
.domain
== NULL
|| domain
== NULL
453 || __strcasecmp (entry
.val
.triple
.domain
,
462 /* If we found one service which does know the given
463 netgroup we don't try further. */
464 status
= NSS_STATUS_RETURN
;
467 /* Free all resources of the service. */
468 endfct
= __nss_lookup_function (entry
.nip
, "endnetgrent");
470 DL_CALL_FCT (*endfct
, (&entry
));
475 /* Look for the next service. */
476 no_more
= __nss_next2 (&entry
.nip
, "setnetgrent", NULL
,
477 &setfct
.ptr
, status
, 0);
480 if (result
== 0 && entry
.needed_groups
!= NULL
)
482 struct name_list
*tmp
= entry
.needed_groups
;
483 entry
.needed_groups
= tmp
->next
;
484 tmp
->next
= entry
.known_groups
;
485 entry
.known_groups
= tmp
;
486 current_group
= tmp
->name
;
494 /* Free the memory. */
495 free_memory (&entry
);
499 libc_hidden_def (innetgr
)