]> git.ipfire.org Git - thirdparty/glibc.git/blame - inet/getnetgrent_r.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / inet / getnetgrent_r.c
CommitLineData
2b778ceb 1/* Copyright (C) 1996-2021 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 15 License along with the GNU C Library; if not, see
5a82c748 16 <https://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
ff3cacc5 39/* Set up NIP to run through the services. Return nonzero if there are no
a68b0d31 40 services (left). */
ff3cacc5 41static int
f4f3b091 42setup (void **fctp, nss_action_list *nipp)
a68b0d31
UD
43{
44 int no_more;
2303f5fd 45
f4f3b091
DD
46 no_more = __nss_netgroup_lookup2 (nipp, "setnetgrent", NULL, fctp);
47
a68b0d31
UD
48 return no_more;
49}
50\f
11336c16
UD
51/* Free used memory. */
52static void
2303f5fd 53free_memory (struct __netgrent *data)
11336c16 54{
2303f5fd 55 while (data->known_groups != NULL)
11336c16 56 {
2303f5fd
UD
57 struct name_list *tmp = data->known_groups;
58 data->known_groups = data->known_groups->next;
11336c16
UD
59 free (tmp);
60 }
61
2303f5fd 62 while (data->needed_groups != NULL)
11336c16 63 {
2303f5fd
UD
64 struct name_list *tmp = data->needed_groups;
65 data->needed_groups = data->needed_groups->next;
11336c16
UD
66 free (tmp);
67 }
68}
69\f
ff3cacc5
UD
70static void
71endnetgrent_hook (struct __netgrent *datap)
72{
73 enum nss_status (*endfct) (struct __netgrent *);
74
f4f3b091 75 if (datap->nip == NULL || datap->nip == (nss_action_list) -1l)
ff3cacc5
UD
76 return;
77
78 endfct = __nss_lookup_function (datap->nip, "endnetgrent");
79 if (endfct != NULL)
80 (void) (*endfct) (datap);
81 datap->nip = NULL;
82}
83
11336c16 84static int
d71b808a
UD
85__internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
86 int *errnop)
a68b0d31 87{
fb776f3e
AJ
88 union
89 {
90 enum nss_status (*f) (const char *, struct __netgrent *);
91 void *ptr;
92 } fct;
a68b0d31 93 enum nss_status status = NSS_STATUS_UNAVAIL;
11336c16 94 struct name_list *new_elem;
a68b0d31 95
ff3cacc5
UD
96 /* Free data from previous service. */
97 endnetgrent_hook (datap);
98
a68b0d31 99 /* Cycle through all the services and run their setnetgrent functions. */
ff3cacc5 100 int no_more = setup (&fct.ptr, &datap->nip);
a68b0d31
UD
101 while (! no_more)
102 {
ff3cacc5
UD
103 assert (datap->data == NULL);
104
384ca551 105 /* Ignore status, we force check in `__nss_next2'. */
75d39ff2 106 status = DL_CALL_FCT (*fct.f, (group, datap));
a68b0d31 107
f4f3b091 108 nss_action_list old_nip = datap->nip;
384ca551
UD
109 no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
110 status, 0);
ff3cacc5
UD
111
112 if (status == NSS_STATUS_SUCCESS && ! no_more)
113 {
114 enum nss_status (*endfct) (struct __netgrent *);
115
116 endfct = __nss_lookup_function (old_nip, "endnetgrent");
117 if (endfct != NULL)
75d39ff2 118 (void) DL_CALL_FCT (*endfct, (datap));
ff3cacc5 119 }
a68b0d31
UD
120 }
121
11336c16 122 /* Add the current group to the list of known groups. */
2486480e
UD
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)
11336c16 127 {
d71b808a
UD
128 *errnop = errno;
129 status = NSS_STATUS_TRYAGAIN;
11336c16
UD
130 }
131 else
132 {
2303f5fd 133 new_elem->next = datap->known_groups;
2486480e 134 memcpy (new_elem->name, group, group_len);
2303f5fd 135 datap->known_groups = new_elem;
11336c16 136 }
a68b0d31
UD
137
138 return status == NSS_STATUS_SUCCESS;
139}
140
ee600e3f 141int
86466cd9 142__internal_setnetgrent (const char *group, struct __netgrent *datap)
2303f5fd
UD
143{
144 /* Free list of all netgroup names from last run. */
145 free_memory (datap);
146
7ba4fcfc 147 return __internal_setnetgrent_reuse (group, datap, &errno);
2303f5fd 148}
86466cd9 149libc_hidden_def (__internal_setnetgrent)
2303f5fd 150
3cc3ef96
RM
151static int
152nscd_setnetgrent (const char *group)
11336c16 153{
3cc3ef96 154#ifdef USE_NSCD
684ae515
UD
155 if (__nss_not_use_nscd_netgroup > 0
156 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
157 __nss_not_use_nscd_netgroup = 0;
158
159 if (!__nss_not_use_nscd_netgroup
160 && !__nss_database_custom[NSS_DBSIDX_netgroup])
3cc3ef96
RM
161 return __nscd_setnetgrent (group, &dataset);
162#endif
163 return -1;
164}
684ae515 165
3cc3ef96
RM
166int
167setnetgrent (const char *group)
168{
169 int result;
170
171 __libc_lock_lock (lock);
172
173 result = nscd_setnetgrent (group);
174 if (result < 0)
175 result = __internal_setnetgrent (group, &dataset);
11336c16
UD
176
177 __libc_lock_unlock (lock);
178
179 return result;
180}
181
ee600e3f 182void
86466cd9 183__internal_endnetgrent (struct __netgrent *datap)
a68b0d31 184{
ff3cacc5 185 endnetgrent_hook (datap);
11336c16 186 /* Now free list of all netgroup names from last run. */
2303f5fd
UD
187 free_memory (datap);
188}
86466cd9 189libc_hidden_def (__internal_endnetgrent)
2303f5fd
UD
190
191
192void
193endnetgrent (void)
194{
195 __libc_lock_lock (lock);
196
86466cd9 197 __internal_endnetgrent (&dataset);
11336c16 198
a68b0d31
UD
199 __libc_lock_unlock (lock);
200}
201
3cc3ef96 202#ifdef USE_NSCD
dd3022d7
SP
203static const char *
204get_nonempty_val (const char *in)
205{
206 if (*in == '\0')
207 return NULL;
208 return in;
209}
210
684ae515
UD
211static enum nss_status
212nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
213 int *errnop)
214{
215 if (datap->cursor >= datap->data + datap->data_size)
216 return NSS_STATUS_UNAVAIL;
217
218 datap->type = triple_val;
dd3022d7 219 datap->val.triple.host = get_nonempty_val (datap->cursor);
a843a204 220 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
dd3022d7 221 datap->val.triple.user = get_nonempty_val (datap->cursor);
a843a204 222 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
dd3022d7 223 datap->val.triple.domain = get_nonempty_val (datap->cursor);
a843a204 224 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
684ae515
UD
225
226 return NSS_STATUS_SUCCESS;
227}
3cc3ef96 228#endif
684ae515 229
ee600e3f 230int
86466cd9 231__internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
01c901a5 232 struct __netgrent *datap,
d71b808a 233 char *buffer, size_t buflen, int *errnop)
a68b0d31 234{
ff3cacc5 235 enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
a68b0d31
UD
236
237 /* Initialize status to return if no more functions are found. */
238 enum nss_status status = NSS_STATUS_NOTFOUND;
239
a68b0d31
UD
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. */
684ae515
UD
243 int no_more = datap->nip == NULL;
244 if (! no_more)
245 {
3cc3ef96
RM
246#ifdef USE_NSCD
247 /* This bogus function pointer is a special marker left by
66ce3cb1
SE
248 __nscd_setnetgrent to tell us to use the data it left
249 before considering any modules. */
f4f3b091 250 if (datap->nip == (nss_action_list) -1l)
684ae515
UD
251 fct = nscd_getnetgrent;
252 else
3cc3ef96 253#endif
684ae515
UD
254 {
255 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
256 no_more = fct == NULL;
257 }
a68b0d31 258
66ce3cb1 259 while (! no_more)
11336c16 260 {
66ce3cb1 261 status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
11336c16 262
66ce3cb1
SE
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))
ff3cacc5 267 {
66ce3cb1
SE
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)
272 {
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;
277
278 found = __internal_setnetgrent_reuse (datap->known_groups->name,
279 datap, errnop);
280 }
281
282 if (found && datap->nip != NULL)
283 {
284 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
285 if (fct != NULL)
286 continue;
287 }
ff3cacc5 288 }
66ce3cb1 289 else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
11336c16 290 {
66ce3cb1
SE
291 /* The last entry was a name of another netgroup. */
292 struct name_list *namep;
293
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;
307
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
315 {
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;
321 }
11336c16 322 }
66ce3cb1 323 break;
11336c16 324 }
a68b0d31
UD
325 }
326
327 if (status == NSS_STATUS_SUCCESS)
328 {
afd4eb37
UD
329 *hostp = (char *) datap->val.triple.host;
330 *userp = (char *) datap->val.triple.user;
331 *domainp = (char *) datap->val.triple.domain;
a68b0d31
UD
332 }
333
2303f5fd
UD
334 return status == NSS_STATUS_SUCCESS ? 1 : 0;
335}
86466cd9 336libc_hidden_def (__internal_getnetgrent_r)
2303f5fd
UD
337
338/* The real entry point. */
339int
340__getnetgrent_r (char **hostp, char **userp, char **domainp,
341 char *buffer, size_t buflen)
342{
343 enum nss_status status;
344
345 __libc_lock_lock (lock);
346
86466cd9 347 status = __internal_getnetgrent_r (hostp, userp, domainp, &dataset,
66ce3cb1 348 buffer, buflen, &errno);
2303f5fd 349
a68b0d31
UD
350 __libc_lock_unlock (lock);
351
2303f5fd 352 return status;
a68b0d31
UD
353}
354weak_alias (__getnetgrent_r, getnetgrent_r)
355\f
356/* Test whether given (host,user,domain) triple is in NETGROUP. */
357int
358innetgr (const char *netgroup, const char *host, const char *user,
359 const char *domain)
360{
3cc3ef96 361#ifdef USE_NSCD
684ae515
UD
362 if (__nss_not_use_nscd_netgroup > 0
363 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
364 __nss_not_use_nscd_netgroup = 0;
365
366 if (!__nss_not_use_nscd_netgroup
367 && !__nss_database_custom[NSS_DBSIDX_netgroup])
368 {
369 int result = __nscd_innetgr (netgroup, host, user, domain);
370 if (result >= 0)
371 return result;
372 }
3cc3ef96 373#endif
684ae515 374
fb776f3e
AJ
375 union
376 {
5ce33a62 377 enum nss_status (*f) (const char *, struct __netgrent *);
fb776f3e
AJ
378 void *ptr;
379 } setfct;
ff3cacc5
UD
380 void (*endfct) (struct __netgrent *);
381 int (*getfct) (struct __netgrent *, char *, size_t, int *);
8500b0ca 382 struct __netgrent entry;
a68b0d31 383 int result = 0;
11336c16 384 const char *current_group = netgroup;
a68b0d31 385
8500b0ca 386 memset (&entry, '\0', sizeof (entry));
a5753206 387
a68b0d31
UD
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. */
11336c16 392 while (1)
a68b0d31 393 {
ff3cacc5 394 int no_more = setup (&setfct.ptr, &entry.nip);
11336c16 395 while (! no_more)
a68b0d31 396 {
ff3cacc5
UD
397 assert (entry.data == NULL);
398
11336c16 399 /* Open netgroup. */
75d39ff2
UD
400 enum nss_status status = DL_CALL_FCT (*setfct.f,
401 (current_group, &entry));
8500b0ca 402
11336c16 403 if (status == NSS_STATUS_SUCCESS
ff3cacc5
UD
404 && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
405 != NULL)
a68b0d31 406 {
11336c16 407 char buffer[1024];
11336c16 408
75d39ff2
UD
409 while (DL_CALL_FCT (*getfct,
410 (&entry, buffer, sizeof buffer, &errno))
11336c16 411 == NSS_STATUS_SUCCESS)
a68b0d31 412 {
11336c16
UD
413 if (entry.type == group_val)
414 {
415 /* Make sure we haven't seen the name before. */
416 struct name_list *namep;
417
8500b0ca
UD
418 for (namep = entry.known_groups; namep != NULL;
419 namep = namep->next)
11336c16
UD
420 if (strcmp (entry.val.group, namep->name) == 0)
421 break;
960e5535
UD
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;
11336c16
UD
427 if (namep == NULL
428 && strcmp (netgroup, entry.val.group) != 0)
429 {
2486480e 430 size_t group_len = strlen (entry.val.group) + 1;
11336c16 431 namep =
2486480e
UD
432 (struct name_list *) malloc (sizeof (*namep)
433 + group_len);
434 if (namep == NULL)
11336c16
UD
435 {
436 /* Out of memory, simply return. */
11336c16
UD
437 result = -1;
438 break;
439 }
440
8500b0ca 441 namep->next = entry.needed_groups;
2486480e 442 memcpy (namep->name, entry.val.group, group_len);
8500b0ca 443 entry.needed_groups = namep;
11336c16
UD
444 }
445 }
446 else
447 {
11336c16 448 if ((entry.val.triple.host == NULL || host == NULL
b77e6cd6 449 || __strcasecmp (entry.val.triple.host, host) == 0)
11336c16
UD
450 && (entry.val.triple.user == NULL || user == NULL
451 || strcmp (entry.val.triple.user, user) == 0)
452 && (entry.val.triple.domain == NULL || domain == NULL
b77e6cd6
UD
453 || __strcasecmp (entry.val.triple.domain,
454 domain) == 0))
11336c16
UD
455 {
456 result = 1;
457 break;
458 }
459 }
a68b0d31 460 }
11336c16 461
11336c16
UD
462 /* If we found one service which does know the given
463 netgroup we don't try further. */
464 status = NSS_STATUS_RETURN;
a68b0d31
UD
465 }
466
11336c16 467 /* Free all resources of the service. */
ff3cacc5
UD
468 endfct = __nss_lookup_function (entry.nip, "endnetgrent");
469 if (endfct != NULL)
75d39ff2 470 DL_CALL_FCT (*endfct, (&entry));
a68b0d31 471
9c5a7904
UD
472 if (result != 0)
473 break;
474
11336c16 475 /* Look for the next service. */
384ca551
UD
476 no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
477 &setfct.ptr, status, 0);
a68b0d31
UD
478 }
479
8500b0ca 480 if (result == 0 && entry.needed_groups != NULL)
11336c16 481 {
8500b0ca
UD
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;
684ae515 486 current_group = tmp->name;
11336c16
UD
487 continue;
488 }
a68b0d31 489
11336c16
UD
490 /* No way out. */
491 break;
a68b0d31
UD
492 }
493
11336c16 494 /* Free the memory. */
8500b0ca 495 free_memory (&entry);
11336c16 496
9c5a7904 497 return result == 1;
a68b0d31 498}
a585ba22 499libc_hidden_def (innetgr)