mips: FIx clone3 implementation (BZ 31325)
[glibc.git] / nss / getnetgrent_r.c
blobaf5fb7ad53e3b642107f0d351790a29042f24f02
1 /* Copyright (C) 1996-2024 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 <assert.h>
19 #include <atomic.h>
20 #include <libc-lock.h>
21 #include <errno.h>
22 #include <netdb.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include "netgroup.h"
27 #include "nsswitch.h"
28 #include <sysdep.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
40 services (left). */
41 static int
42 setup (void **fctp, nss_action_list *nipp)
44 int no_more;
46 no_more = __nss_netgroup_lookup2 (nipp, "setnetgrent", NULL, fctp);
48 return no_more;
51 /* Free used memory. */
52 static void
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;
59 free (tmp);
62 while (data->needed_groups != NULL)
64 struct name_list *tmp = data->needed_groups;
65 data->needed_groups = data->needed_groups->next;
66 free (tmp);
70 static void
71 endnetgrent_hook (struct __netgrent *datap)
73 enum nss_status (*endfct) (struct __netgrent *);
75 if (datap->nip == NULL || datap->nip == (nss_action_list) -1l)
76 return;
78 endfct = __nss_lookup_function (datap->nip, "endnetgrent");
79 if (endfct != NULL)
80 (void) (*endfct) (datap);
81 datap->nip = NULL;
84 static int
85 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
86 int *errnop)
88 union
90 enum nss_status (*f) (const char *, struct __netgrent *);
91 void *ptr;
92 } fct;
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);
101 while (! no_more)
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,
110 status, 0);
112 if (status == NSS_STATUS_SUCCESS && ! no_more)
114 enum nss_status (*endfct) (struct __netgrent *);
116 endfct = __nss_lookup_function (old_nip, "endnetgrent");
117 if (endfct != NULL)
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)
125 + group_len);
126 if (new_elem == NULL)
128 *errnop = errno;
129 status = NSS_STATUS_TRYAGAIN;
131 else
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. */
145 free_memory (datap);
147 return __internal_setnetgrent_reuse (group, datap, &errno);
149 libc_hidden_def (__internal_setnetgrent)
151 static int
152 nscd_setnetgrent (const char *group)
154 #ifdef USE_NSCD
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);
162 #endif
163 return -1;
167 setnetgrent (const char *group)
169 int result;
171 __libc_lock_lock (lock);
173 result = nscd_setnetgrent (group);
174 if (result < 0)
175 result = __internal_setnetgrent (group, &dataset);
177 __libc_lock_unlock (lock);
179 return result;
182 void
183 __internal_endnetgrent (struct __netgrent *datap)
185 endnetgrent_hook (datap);
186 /* Now free list of all netgroup names from last run. */
187 free_memory (datap);
189 libc_hidden_def (__internal_endnetgrent)
192 void
193 endnetgrent (void)
195 __libc_lock_lock (lock);
197 __internal_endnetgrent (&dataset);
199 __libc_lock_unlock (lock);
202 #ifdef USE_NSCD
203 static const char *
204 get_nonempty_val (const char *in)
206 if (*in == '\0')
207 return NULL;
208 return in;
211 static enum nss_status
212 nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
213 int *errnop)
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 = strchr (datap->cursor, '\0') + 1;
221 datap->val.triple.user = get_nonempty_val (datap->cursor);
222 datap->cursor = strchr (datap->cursor, '\0') + 1;
223 datap->val.triple.domain = get_nonempty_val (datap->cursor);
224 datap->cursor = strchr (datap->cursor, '\0') + 1;
226 return NSS_STATUS_SUCCESS;
228 #endif
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;
244 if (! no_more)
246 #ifdef USE_NSCD
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;
252 else
253 #endif
255 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
256 no_more = fct == NULL;
259 while (! no_more)
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
269 if available. */
270 int found = 0;
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,
279 datap, errnop);
282 if (found && datap->nip != NULL)
284 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
285 if (fct != NULL)
286 continue;
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;
296 namep = namep->next)
297 if (strcmp (datap->val.group, namep->name) == 0)
298 break;
299 if (namep == NULL)
300 for (namep = datap->needed_groups; namep != NULL;
301 namep = namep->next)
302 if (strcmp (datap->val.group, namep->name) == 0)
303 break;
304 if (namep != NULL)
305 /* Really ignore. */
306 continue;
308 size_t group_len = strlen (datap->val.group) + 1;
309 namep = (struct name_list *) malloc (sizeof (struct name_list)
310 + group_len);
311 if (namep == NULL)
312 /* We are out of memory. */
313 status = NSS_STATUS_RETURN;
314 else
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. */
320 continue;
323 break;
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);
352 return status;
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,
359 const char *domain)
361 #ifdef USE_NSCD
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);
370 if (result >= 0)
371 return result;
373 #endif
375 union
377 enum nss_status (*f) (const char *, struct __netgrent *);
378 void *ptr;
379 } setfct;
380 void (*endfct) (struct __netgrent *);
381 int (*getfct) (struct __netgrent *, char *, size_t, int *);
382 struct __netgrent entry;
383 int result = 0;
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. */
392 while (1)
394 int no_more = setup (&setfct.ptr, &entry.nip);
395 while (! no_more)
397 assert (entry.data == NULL);
399 /* Open netgroup. */
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"))
405 != NULL)
407 char buffer[1024];
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;
419 namep = namep->next)
420 if (strcmp (entry.val.group, namep->name) == 0)
421 break;
422 if (namep == NULL)
423 for (namep = entry.needed_groups; namep != NULL;
424 namep = namep->next)
425 if (strcmp (entry.val.group, namep->name) == 0)
426 break;
427 if (namep == NULL
428 && strcmp (netgroup, entry.val.group) != 0)
430 size_t group_len = strlen (entry.val.group) + 1;
431 namep =
432 (struct name_list *) malloc (sizeof (*namep)
433 + group_len);
434 if (namep == NULL)
436 /* Out of memory, simply return. */
437 result = -1;
438 break;
441 namep->next = entry.needed_groups;
442 memcpy (namep->name, entry.val.group, group_len);
443 entry.needed_groups = namep;
446 else
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,
454 domain) == 0))
456 result = 1;
457 break;
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");
469 if (endfct != NULL)
470 DL_CALL_FCT (*endfct, (&entry));
472 if (result != 0)
473 break;
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;
487 continue;
490 /* No way out. */
491 break;
494 /* Free the memory. */
495 free_memory (&entry);
497 return result == 1;
499 libc_hidden_def (innetgr)