]> git.ipfire.org Git - thirdparty/glibc.git/blame - inet/getnetgrent_r.c
Enhance --enable-tunables to select tunables frontend at build time
[thirdparty/glibc.git] / inet / getnetgrent_r.c
CommitLineData
f7a9f785 1/* Copyright (C) 1996-2016 Free Software Foundation, Inc.
afd4eb37 2 This file is part of the GNU C Library.
a68b0d31 3
afd4eb37 4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
a68b0d31 8
afd4eb37
UD
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
41bdb6e2 12 Lesser General Public License for more details.
a68b0d31 13
41bdb6e2 14 You should have received a copy of the GNU Lesser General Public
59ba27a6
PE
15 License along with the GNU C Library; if not, see
16 <http://www.gnu.org/licenses/>. */
a68b0d31 17
ff3cacc5 18#include <assert.h>
384ca551 19#include <atomic.h>
ec999b8e 20#include <libc-lock.h>
6591c335 21#include <errno.h>
a68b0d31 22#include <netdb.h>
384ca551 23#include <stdbool.h>
afd4eb37 24#include <stdlib.h>
2303f5fd 25#include <string.h>
a68b0d31
UD
26#include "netgroup.h"
27#include "nsswitch.h"
384ca551 28#include <sysdep.h>
684ae515 29#include <nscd/nscd_proto.h>
a68b0d31
UD
30
31
32/* Protect above variable against multiple uses at the same time. */
33__libc_lock_define_initialized (static, lock)
34
2303f5fd
UD
35/* The whole information for the set/get/endnetgrent functions are
36 kept in this structure. */
37static struct __netgrent dataset;
11336c16 38
a68b0d31 39/* The lookup function for the first entry of this service. */
ff3cacc5 40extern int __nss_netgroup_lookup (service_user **nipp, const char *name,
f7ddf3d3 41 void **fctp) internal_function;
a68b0d31 42
ff3cacc5 43/* Set up NIP to run through the services. Return nonzero if there are no
a68b0d31 44 services (left). */
ff3cacc5
UD
45static int
46setup (void **fctp, service_user **nipp)
a68b0d31 47{
2303f5fd 48 /* Remember the first service_entry, it's always the same. */
384ca551 49 static bool startp_initialized;
390500b1 50 static service_user *startp;
a68b0d31 51 int no_more;
2303f5fd 52
384ca551 53 if (!startp_initialized)
a68b0d31 54 {
8500b0ca
UD
55 /* Executing this more than once at the same time must yield the
56 same result every time. So we need no locking. */
ff3cacc5 57 no_more = __nss_netgroup_lookup (nipp, "setnetgrent", fctp);
8500b0ca 58 startp = no_more ? (service_user *) -1 : *nipp;
40ce302d 59#ifdef PTR_MANGLE
384ca551 60 PTR_MANGLE (startp);
40ce302d 61#endif
384ca551
UD
62 atomic_write_barrier ();
63 startp_initialized = true;
a68b0d31 64 }
a68b0d31
UD
65 else
66 {
384ca551 67 service_user *nip = startp;
40ce302d 68#ifdef PTR_DEMANGLE
384ca551 69 PTR_DEMANGLE (nip);
40ce302d 70#endif
384ca551
UD
71 if (nip == (service_user *) -1)
72 /* No services at all. */
73 return 1;
74
ff3cacc5 75 /* Reset to the beginning of the service list. */
384ca551 76 *nipp = nip;
a68b0d31 77 /* Look up the first function. */
384ca551 78 no_more = __nss_lookup (nipp, "setnetgrent", NULL, fctp);
a68b0d31
UD
79 }
80 return no_more;
81}
82\f
11336c16
UD
83/* Free used memory. */
84static void
2303f5fd 85free_memory (struct __netgrent *data)
11336c16 86{
2303f5fd 87 while (data->known_groups != NULL)
11336c16 88 {
2303f5fd
UD
89 struct name_list *tmp = data->known_groups;
90 data->known_groups = data->known_groups->next;
11336c16
UD
91 free (tmp);
92 }
93
2303f5fd 94 while (data->needed_groups != NULL)
11336c16 95 {
2303f5fd
UD
96 struct name_list *tmp = data->needed_groups;
97 data->needed_groups = data->needed_groups->next;
11336c16
UD
98 free (tmp);
99 }
100}
101\f
ff3cacc5
UD
102static void
103endnetgrent_hook (struct __netgrent *datap)
104{
105 enum nss_status (*endfct) (struct __netgrent *);
106
684ae515 107 if (datap->nip == NULL || datap->nip == (service_user *) -1l)
ff3cacc5
UD
108 return;
109
110 endfct = __nss_lookup_function (datap->nip, "endnetgrent");
111 if (endfct != NULL)
112 (void) (*endfct) (datap);
113 datap->nip = NULL;
114}
115
11336c16 116static int
dfd2257a 117internal_function
d71b808a
UD
118__internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
119 int *errnop)
a68b0d31 120{
fb776f3e
AJ
121 union
122 {
123 enum nss_status (*f) (const char *, struct __netgrent *);
124 void *ptr;
125 } fct;
a68b0d31 126 enum nss_status status = NSS_STATUS_UNAVAIL;
11336c16 127 struct name_list *new_elem;
a68b0d31 128
ff3cacc5
UD
129 /* Free data from previous service. */
130 endnetgrent_hook (datap);
131
a68b0d31 132 /* Cycle through all the services and run their setnetgrent functions. */
ff3cacc5 133 int no_more = setup (&fct.ptr, &datap->nip);
a68b0d31
UD
134 while (! no_more)
135 {
ff3cacc5
UD
136 assert (datap->data == NULL);
137
384ca551 138 /* Ignore status, we force check in `__nss_next2'. */
75d39ff2 139 status = DL_CALL_FCT (*fct.f, (group, datap));
a68b0d31 140
ff3cacc5 141 service_user *old_nip = datap->nip;
384ca551
UD
142 no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
143 status, 0);
ff3cacc5
UD
144
145 if (status == NSS_STATUS_SUCCESS && ! no_more)
146 {
147 enum nss_status (*endfct) (struct __netgrent *);
148
149 endfct = __nss_lookup_function (old_nip, "endnetgrent");
150 if (endfct != NULL)
75d39ff2 151 (void) DL_CALL_FCT (*endfct, (datap));
ff3cacc5 152 }
a68b0d31
UD
153 }
154
11336c16 155 /* Add the current group to the list of known groups. */
2486480e
UD
156 size_t group_len = strlen (group) + 1;
157 new_elem = (struct name_list *) malloc (sizeof (struct name_list)
158 + group_len);
159 if (new_elem == NULL)
11336c16 160 {
d71b808a
UD
161 *errnop = errno;
162 status = NSS_STATUS_TRYAGAIN;
11336c16
UD
163 }
164 else
165 {
2303f5fd 166 new_elem->next = datap->known_groups;
2486480e 167 memcpy (new_elem->name, group, group_len);
2303f5fd 168 datap->known_groups = new_elem;
11336c16 169 }
a68b0d31
UD
170
171 return status == NSS_STATUS_SUCCESS;
172}
173
ee600e3f 174int
86466cd9
RM
175internal_function
176__internal_setnetgrent (const char *group, struct __netgrent *datap)
2303f5fd
UD
177{
178 /* Free list of all netgroup names from last run. */
179 free_memory (datap);
180
7ba4fcfc 181 return __internal_setnetgrent_reuse (group, datap, &errno);
2303f5fd 182}
86466cd9 183libc_hidden_def (__internal_setnetgrent)
2303f5fd 184
3cc3ef96
RM
185static int
186nscd_setnetgrent (const char *group)
11336c16 187{
3cc3ef96 188#ifdef USE_NSCD
684ae515
UD
189 if (__nss_not_use_nscd_netgroup > 0
190 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
191 __nss_not_use_nscd_netgroup = 0;
192
193 if (!__nss_not_use_nscd_netgroup
194 && !__nss_database_custom[NSS_DBSIDX_netgroup])
3cc3ef96
RM
195 return __nscd_setnetgrent (group, &dataset);
196#endif
197 return -1;
198}
684ae515 199
3cc3ef96
RM
200int
201setnetgrent (const char *group)
202{
203 int result;
204
205 __libc_lock_lock (lock);
206
207 result = nscd_setnetgrent (group);
208 if (result < 0)
209 result = __internal_setnetgrent (group, &dataset);
11336c16
UD
210
211 __libc_lock_unlock (lock);
212
213 return result;
214}
215
ee600e3f 216void
86466cd9
RM
217internal_function
218__internal_endnetgrent (struct __netgrent *datap)
a68b0d31 219{
ff3cacc5 220 endnetgrent_hook (datap);
11336c16 221 /* Now free list of all netgroup names from last run. */
2303f5fd
UD
222 free_memory (datap);
223}
86466cd9 224libc_hidden_def (__internal_endnetgrent)
2303f5fd
UD
225
226
227void
228endnetgrent (void)
229{
230 __libc_lock_lock (lock);
231
86466cd9 232 __internal_endnetgrent (&dataset);
11336c16 233
a68b0d31
UD
234 __libc_lock_unlock (lock);
235}
236
3cc3ef96 237#ifdef USE_NSCD
dd3022d7
SP
238static const char *
239get_nonempty_val (const char *in)
240{
241 if (*in == '\0')
242 return NULL;
243 return in;
244}
245
684ae515
UD
246static enum nss_status
247nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
248 int *errnop)
249{
250 if (datap->cursor >= datap->data + datap->data_size)
251 return NSS_STATUS_UNAVAIL;
252
253 datap->type = triple_val;
dd3022d7 254 datap->val.triple.host = get_nonempty_val (datap->cursor);
a843a204 255 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
dd3022d7 256 datap->val.triple.user = get_nonempty_val (datap->cursor);
a843a204 257 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
dd3022d7 258 datap->val.triple.domain = get_nonempty_val (datap->cursor);
a843a204 259 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
684ae515
UD
260
261 return NSS_STATUS_SUCCESS;
262}
3cc3ef96 263#endif
684ae515 264
ee600e3f 265int
86466cd9
RM
266internal_function
267__internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
01c901a5 268 struct __netgrent *datap,
d71b808a 269 char *buffer, size_t buflen, int *errnop)
a68b0d31 270{
ff3cacc5 271 enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
a68b0d31
UD
272
273 /* Initialize status to return if no more functions are found. */
274 enum nss_status status = NSS_STATUS_NOTFOUND;
275
a68b0d31
UD
276 /* Run through available functions, starting with the same function last
277 run. We will repeat each function as long as it succeeds, and then go
278 on to the next service action. */
684ae515
UD
279 int no_more = datap->nip == NULL;
280 if (! no_more)
281 {
3cc3ef96
RM
282#ifdef USE_NSCD
283 /* This bogus function pointer is a special marker left by
66ce3cb1
SE
284 __nscd_setnetgrent to tell us to use the data it left
285 before considering any modules. */
684ae515
UD
286 if (datap->nip == (service_user *) -1l)
287 fct = nscd_getnetgrent;
288 else
3cc3ef96 289#endif
684ae515
UD
290 {
291 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
292 no_more = fct == NULL;
293 }
a68b0d31 294
66ce3cb1 295 while (! no_more)
11336c16 296 {
66ce3cb1 297 status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
11336c16 298
66ce3cb1
SE
299 if (status == NSS_STATUS_RETURN
300 /* The service returned a NOTFOUND, but there are more groups that
301 we need to resolve before we give up. */
302 || (status == NSS_STATUS_NOTFOUND && datap->needed_groups != NULL))
ff3cacc5 303 {
66ce3cb1
SE
304 /* This was the last one for this group. Look at next group
305 if available. */
306 int found = 0;
307 while (datap->needed_groups != NULL && ! found)
308 {
309 struct name_list *tmp = datap->needed_groups;
310 datap->needed_groups = datap->needed_groups->next;
311 tmp->next = datap->known_groups;
312 datap->known_groups = tmp;
313
314 found = __internal_setnetgrent_reuse (datap->known_groups->name,
315 datap, errnop);
316 }
317
318 if (found && datap->nip != NULL)
319 {
320 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
321 if (fct != NULL)
322 continue;
323 }
ff3cacc5 324 }
66ce3cb1 325 else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
11336c16 326 {
66ce3cb1
SE
327 /* The last entry was a name of another netgroup. */
328 struct name_list *namep;
329
330 /* Ignore if we've seen the name before. */
331 for (namep = datap->known_groups; namep != NULL;
332 namep = namep->next)
333 if (strcmp (datap->val.group, namep->name) == 0)
334 break;
335 if (namep == NULL)
336 for (namep = datap->needed_groups; namep != NULL;
337 namep = namep->next)
338 if (strcmp (datap->val.group, namep->name) == 0)
339 break;
340 if (namep != NULL)
341 /* Really ignore. */
342 continue;
343
344 size_t group_len = strlen (datap->val.group) + 1;
345 namep = (struct name_list *) malloc (sizeof (struct name_list)
346 + group_len);
347 if (namep == NULL)
348 /* We are out of memory. */
349 status = NSS_STATUS_RETURN;
350 else
351 {
352 namep->next = datap->needed_groups;
353 memcpy (namep->name, datap->val.group, group_len);
354 datap->needed_groups = namep;
355 /* And get the next entry. */
356 continue;
357 }
11336c16 358 }
66ce3cb1 359 break;
11336c16 360 }
a68b0d31
UD
361 }
362
363 if (status == NSS_STATUS_SUCCESS)
364 {
afd4eb37
UD
365 *hostp = (char *) datap->val.triple.host;
366 *userp = (char *) datap->val.triple.user;
367 *domainp = (char *) datap->val.triple.domain;
a68b0d31
UD
368 }
369
2303f5fd
UD
370 return status == NSS_STATUS_SUCCESS ? 1 : 0;
371}
86466cd9 372libc_hidden_def (__internal_getnetgrent_r)
2303f5fd
UD
373
374/* The real entry point. */
375int
376__getnetgrent_r (char **hostp, char **userp, char **domainp,
377 char *buffer, size_t buflen)
378{
379 enum nss_status status;
380
381 __libc_lock_lock (lock);
382
86466cd9 383 status = __internal_getnetgrent_r (hostp, userp, domainp, &dataset,
66ce3cb1 384 buffer, buflen, &errno);
2303f5fd 385
a68b0d31
UD
386 __libc_lock_unlock (lock);
387
2303f5fd 388 return status;
a68b0d31
UD
389}
390weak_alias (__getnetgrent_r, getnetgrent_r)
391\f
392/* Test whether given (host,user,domain) triple is in NETGROUP. */
393int
394innetgr (const char *netgroup, const char *host, const char *user,
395 const char *domain)
396{
3cc3ef96 397#ifdef USE_NSCD
684ae515
UD
398 if (__nss_not_use_nscd_netgroup > 0
399 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
400 __nss_not_use_nscd_netgroup = 0;
401
402 if (!__nss_not_use_nscd_netgroup
403 && !__nss_database_custom[NSS_DBSIDX_netgroup])
404 {
405 int result = __nscd_innetgr (netgroup, host, user, domain);
406 if (result >= 0)
407 return result;
408 }
3cc3ef96 409#endif
684ae515 410
fb776f3e
AJ
411 union
412 {
5ce33a62 413 enum nss_status (*f) (const char *, struct __netgrent *);
fb776f3e
AJ
414 void *ptr;
415 } setfct;
ff3cacc5
UD
416 void (*endfct) (struct __netgrent *);
417 int (*getfct) (struct __netgrent *, char *, size_t, int *);
8500b0ca 418 struct __netgrent entry;
a68b0d31 419 int result = 0;
11336c16 420 const char *current_group = netgroup;
a68b0d31 421
8500b0ca 422 memset (&entry, '\0', sizeof (entry));
a5753206 423
a68b0d31
UD
424 /* Walk through the services until we found an answer or we shall
425 not work further. We can do some optimization here. Since all
426 services must provide the `setnetgrent' function we can do all
427 the work during one walk through the service list. */
11336c16 428 while (1)
a68b0d31 429 {
ff3cacc5 430 int no_more = setup (&setfct.ptr, &entry.nip);
11336c16 431 while (! no_more)
a68b0d31 432 {
ff3cacc5
UD
433 assert (entry.data == NULL);
434
11336c16 435 /* Open netgroup. */
75d39ff2
UD
436 enum nss_status status = DL_CALL_FCT (*setfct.f,
437 (current_group, &entry));
8500b0ca 438
11336c16 439 if (status == NSS_STATUS_SUCCESS
ff3cacc5
UD
440 && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
441 != NULL)
a68b0d31 442 {
11336c16 443 char buffer[1024];
11336c16 444
75d39ff2
UD
445 while (DL_CALL_FCT (*getfct,
446 (&entry, buffer, sizeof buffer, &errno))
11336c16 447 == NSS_STATUS_SUCCESS)
a68b0d31 448 {
11336c16
UD
449 if (entry.type == group_val)
450 {
451 /* Make sure we haven't seen the name before. */
452 struct name_list *namep;
453
8500b0ca
UD
454 for (namep = entry.known_groups; namep != NULL;
455 namep = namep->next)
11336c16
UD
456 if (strcmp (entry.val.group, namep->name) == 0)
457 break;
960e5535
UD
458 if (namep == NULL)
459 for (namep = entry.needed_groups; namep != NULL;
460 namep = namep->next)
461 if (strcmp (entry.val.group, namep->name) == 0)
462 break;
11336c16
UD
463 if (namep == NULL
464 && strcmp (netgroup, entry.val.group) != 0)
465 {
2486480e 466 size_t group_len = strlen (entry.val.group) + 1;
11336c16 467 namep =
2486480e
UD
468 (struct name_list *) malloc (sizeof (*namep)
469 + group_len);
470 if (namep == NULL)
11336c16
UD
471 {
472 /* Out of memory, simply return. */
11336c16
UD
473 result = -1;
474 break;
475 }
476
8500b0ca 477 namep->next = entry.needed_groups;
2486480e 478 memcpy (namep->name, entry.val.group, group_len);
8500b0ca 479 entry.needed_groups = namep;
11336c16
UD
480 }
481 }
482 else
483 {
11336c16 484 if ((entry.val.triple.host == NULL || host == NULL
b77e6cd6 485 || __strcasecmp (entry.val.triple.host, host) == 0)
11336c16
UD
486 && (entry.val.triple.user == NULL || user == NULL
487 || strcmp (entry.val.triple.user, user) == 0)
488 && (entry.val.triple.domain == NULL || domain == NULL
b77e6cd6
UD
489 || __strcasecmp (entry.val.triple.domain,
490 domain) == 0))
11336c16
UD
491 {
492 result = 1;
493 break;
494 }
495 }
a68b0d31 496 }
11336c16 497
11336c16
UD
498 /* If we found one service which does know the given
499 netgroup we don't try further. */
500 status = NSS_STATUS_RETURN;
a68b0d31
UD
501 }
502
11336c16 503 /* Free all resources of the service. */
ff3cacc5
UD
504 endfct = __nss_lookup_function (entry.nip, "endnetgrent");
505 if (endfct != NULL)
75d39ff2 506 DL_CALL_FCT (*endfct, (&entry));
a68b0d31 507
9c5a7904
UD
508 if (result != 0)
509 break;
510
11336c16 511 /* Look for the next service. */
384ca551
UD
512 no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
513 &setfct.ptr, status, 0);
a68b0d31
UD
514 }
515
8500b0ca 516 if (result == 0 && entry.needed_groups != NULL)
11336c16 517 {
8500b0ca
UD
518 struct name_list *tmp = entry.needed_groups;
519 entry.needed_groups = tmp->next;
520 tmp->next = entry.known_groups;
521 entry.known_groups = tmp;
684ae515 522 current_group = tmp->name;
11336c16
UD
523 continue;
524 }
a68b0d31 525
11336c16
UD
526 /* No way out. */
527 break;
a68b0d31
UD
528 }
529
11336c16 530 /* Free the memory. */
8500b0ca 531 free_memory (&entry);
11336c16 532
9c5a7904 533 return result == 1;
a68b0d31 534}
a585ba22 535libc_hidden_def (innetgr)