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