]> git.ipfire.org Git - thirdparty/glibc.git/blob - inet/getnetgrent_r.c
Implement caching of nscd
[thirdparty/glibc.git] / inet / getnetgrent_r.c
1 /* Copyright (C) 1996,1997,1998,1999,2002,2004,2005,2007,2011
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
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.
9
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
13 Lesser General Public License for more details.
14
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. */
19
20 #include <assert.h>
21 #include <atomic.h>
22 #include <bits/libc-lock.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "netgroup.h"
29 #include "nsswitch.h"
30 #include <sysdep.h>
31 #include <nscd/nscd_proto.h>
32
33
34 /* Protect above variable against multiple uses at the same time. */
35 __libc_lock_define_initialized (static, lock)
36
37 /* The whole information for the set/get/endnetgrent functions are
38 kept in this structure. */
39 static struct __netgrent dataset;
40
41 /* The lookup function for the first entry of this service. */
42 extern int __nss_netgroup_lookup (service_user **nipp, const char *name,
43 void **fctp) internal_function;
44
45 /* Set up NIP to run through the services. Return nonzero if there are no
46 services (left). */
47 static int
48 setup (void **fctp, service_user **nipp)
49 {
50 /* Remember the first service_entry, it's always the same. */
51 static bool startp_initialized;
52 static service_user *startp;
53 int no_more;
54
55 if (!startp_initialized)
56 {
57 /* Executing this more than once at the same time must yield the
58 same result every time. So we need no locking. */
59 no_more = __nss_netgroup_lookup (nipp, "setnetgrent", fctp);
60 startp = no_more ? (service_user *) -1 : *nipp;
61 PTR_MANGLE (startp);
62 atomic_write_barrier ();
63 startp_initialized = true;
64 }
65 else
66 {
67 service_user *nip = startp;
68 PTR_DEMANGLE (nip);
69 if (nip == (service_user *) -1)
70 /* No services at all. */
71 return 1;
72
73 /* Reset to the beginning of the service list. */
74 *nipp = nip;
75 /* Look up the first function. */
76 no_more = __nss_lookup (nipp, "setnetgrent", NULL, fctp);
77 }
78 return no_more;
79 }
80 \f
81 /* Free used memory. */
82 static void
83 free_memory (struct __netgrent *data)
84 {
85 while (data->known_groups != NULL)
86 {
87 struct name_list *tmp = data->known_groups;
88 data->known_groups = data->known_groups->next;
89 free (tmp);
90 }
91
92 while (data->needed_groups != NULL)
93 {
94 struct name_list *tmp = data->needed_groups;
95 data->needed_groups = data->needed_groups->next;
96 free (tmp);
97 }
98 }
99 \f
100 static void
101 endnetgrent_hook (struct __netgrent *datap)
102 {
103 enum nss_status (*endfct) (struct __netgrent *);
104
105 if (datap->nip == NULL || datap->nip == (service_user *) -1l)
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
114 static int
115 internal_function
116 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
117 int *errnop)
118 {
119 union
120 {
121 enum nss_status (*f) (const char *, struct __netgrent *);
122 void *ptr;
123 } fct;
124 enum nss_status status = NSS_STATUS_UNAVAIL;
125 struct name_list *new_elem;
126
127 /* Free data from previous service. */
128 endnetgrent_hook (datap);
129
130 /* Cycle through all the services and run their setnetgrent functions. */
131 int no_more = setup (&fct.ptr, &datap->nip);
132 while (! no_more)
133 {
134 assert (datap->data == NULL);
135
136 /* Ignore status, we force check in `__nss_next2'. */
137 status = DL_CALL_FCT (*fct.f, (group, datap));
138
139 service_user *old_nip = datap->nip;
140 no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
141 status, 0);
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)
149 (void) DL_CALL_FCT (*endfct, (datap));
150 }
151 }
152
153 /* Add the current group to the list of known groups. */
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)
158 {
159 *errnop = errno;
160 status = NSS_STATUS_TRYAGAIN;
161 }
162 else
163 {
164 new_elem->next = datap->known_groups;
165 memcpy (new_elem->name, group, group_len);
166 datap->known_groups = new_elem;
167 }
168
169 return status == NSS_STATUS_SUCCESS;
170 }
171
172 int internal_setnetgrent (const char *group, struct __netgrent *datap);
173 libc_hidden_proto (internal_setnetgrent)
174
175 int
176 internal_setnetgrent (const char *group, struct __netgrent *datap)
177 {
178 /* Free list of all netgroup names from last run. */
179 free_memory (datap);
180
181 return __internal_setnetgrent_reuse (group, datap, &errno);
182 }
183 libc_hidden_def (internal_setnetgrent)
184 strong_alias (internal_setnetgrent, __internal_setnetgrent)
185
186 int
187 setnetgrent (const char *group)
188 {
189 int result;
190
191 __libc_lock_lock (lock);
192
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
205 result = internal_setnetgrent (group, &dataset);
206
207 out:
208 __libc_lock_unlock (lock);
209
210 return result;
211 }
212
213 void internal_endnetgrent (struct __netgrent *datap);
214 libc_hidden_proto (internal_endnetgrent)
215
216 void
217 internal_endnetgrent (struct __netgrent *datap)
218 {
219 endnetgrent_hook (datap);
220 /* Now free list of all netgroup names from last run. */
221 free_memory (datap);
222 }
223 libc_hidden_def (internal_endnetgrent)
224 strong_alias (internal_endnetgrent, __internal_endnetgrent)
225
226
227 void
228 endnetgrent (void)
229 {
230 __libc_lock_lock (lock);
231
232 internal_endnetgrent (&dataset);
233
234 __libc_lock_unlock (lock);
235 }
236
237
238 int internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
239 struct __netgrent *datap,
240 char *buffer, size_t buflen, int *errnop);
241 libc_hidden_proto (internal_getnetgrent_r)
242
243
244 static enum nss_status
245 nscd_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;
253 datap->cursor = (char *) rawmemchr (datap->cursor, '\0') + 1;
254 datap->val.triple.user = datap->cursor;
255 datap->cursor = (char *) rawmemchr (datap->cursor, '\0') + 1;
256 datap->val.triple.domain = datap->cursor;
257 datap->cursor = (char *) rawmemchr (datap->cursor, '\0') + 1;
258
259 return NSS_STATUS_SUCCESS;
260 }
261
262
263 int
264 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
265 struct __netgrent *datap,
266 char *buffer, size_t buflen, int *errnop)
267 {
268 enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
269
270 /* Initialize status to return if no more functions are found. */
271 enum nss_status status = NSS_STATUS_NOTFOUND;
272
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. */
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
288 while (! no_more)
289 {
290 status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
291
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;
297 while (datap->needed_groups != NULL && ! found)
298 {
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;
303
304 found = __internal_setnetgrent_reuse (datap->known_groups->name,
305 datap, errnop);
306 }
307
308 if (found && datap->nip != NULL)
309 {
310 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
311 if (fct != NULL)
312 continue;
313 }
314 }
315 else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
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. */
321 for (namep = datap->known_groups; namep != NULL;
322 namep = namep->next)
323 if (strcmp (datap->val.group, namep->name) == 0)
324 break;
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)
329 break;
330 if (namep != NULL)
331 /* Really ignore. */
332 continue;
333
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;
340 else
341 {
342 namep->next = datap->needed_groups;
343 memcpy (namep->name, datap->val.group, group_len);
344 datap->needed_groups = namep;
345 /* And get the next entry. */
346 continue;
347 }
348 }
349
350 break;
351 }
352
353 if (status == NSS_STATUS_SUCCESS)
354 {
355 *hostp = (char *) datap->val.triple.host;
356 *userp = (char *) datap->val.triple.user;
357 *domainp = (char *) datap->val.triple.domain;
358 }
359
360 return status == NSS_STATUS_SUCCESS ? 1 : 0;
361 }
362 libc_hidden_def (internal_getnetgrent_r)
363 strong_alias (internal_getnetgrent_r, __internal_getnetgrent_r)
364
365 /* The real entry point. */
366 int
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
374 status = internal_getnetgrent_r (hostp, userp, domainp, &dataset,
375 buffer, buflen, &errno);
376
377 __libc_lock_unlock (lock);
378
379 return status;
380 }
381 weak_alias (__getnetgrent_r, getnetgrent_r)
382 \f
383 /* Test whether given (host,user,domain) triple is in NETGROUP. */
384 int
385 innetgr (const char *netgroup, const char *host, const char *user,
386 const char *domain)
387 {
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
400 union
401 {
402 enum nss_status (*f) (const char *, struct __netgrent *);
403 void *ptr;
404 } setfct;
405 void (*endfct) (struct __netgrent *);
406 int (*getfct) (struct __netgrent *, char *, size_t, int *);
407 struct __netgrent entry;
408 int result = 0;
409 const char *current_group = netgroup;
410
411 memset (&entry, '\0', sizeof (entry));
412
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. */
417 while (1)
418 {
419 int no_more = setup (&setfct.ptr, &entry.nip);
420 while (! no_more)
421 {
422 assert (entry.data == NULL);
423
424 /* Open netgroup. */
425 enum nss_status status = DL_CALL_FCT (*setfct.f,
426 (current_group, &entry));
427
428 if (status == NSS_STATUS_SUCCESS
429 && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
430 != NULL)
431 {
432 char buffer[1024];
433
434 while (DL_CALL_FCT (*getfct,
435 (&entry, buffer, sizeof buffer, &errno))
436 == NSS_STATUS_SUCCESS)
437 {
438 if (entry.type == group_val)
439 {
440 /* Make sure we haven't seen the name before. */
441 struct name_list *namep;
442
443 for (namep = entry.known_groups; namep != NULL;
444 namep = namep->next)
445 if (strcmp (entry.val.group, namep->name) == 0)
446 break;
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;
452 if (namep == NULL
453 && strcmp (netgroup, entry.val.group) != 0)
454 {
455 size_t group_len = strlen (entry.val.group) + 1;
456 namep =
457 (struct name_list *) malloc (sizeof (*namep)
458 + group_len);
459 if (namep == NULL)
460 {
461 /* Out of memory, simply return. */
462 result = -1;
463 break;
464 }
465
466 namep->next = entry.needed_groups;
467 memcpy (namep->name, entry.val.group, group_len);
468 entry.needed_groups = namep;
469 }
470 }
471 else
472 {
473 if ((entry.val.triple.host == NULL || host == NULL
474 || __strcasecmp (entry.val.triple.host, host) == 0)
475 && (entry.val.triple.user == NULL || user == NULL
476 || strcmp (entry.val.triple.user, user) == 0)
477 && (entry.val.triple.domain == NULL || domain == NULL
478 || __strcasecmp (entry.val.triple.domain,
479 domain) == 0))
480 {
481 result = 1;
482 break;
483 }
484 }
485 }
486
487 /* If we found one service which does know the given
488 netgroup we don't try further. */
489 status = NSS_STATUS_RETURN;
490 }
491
492 /* Free all resources of the service. */
493 endfct = __nss_lookup_function (entry.nip, "endnetgrent");
494 if (endfct != NULL)
495 DL_CALL_FCT (*endfct, (&entry));
496
497 if (result != 0)
498 break;
499
500 /* Look for the next service. */
501 no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
502 &setfct.ptr, status, 0);
503 }
504
505 if (result == 0 && entry.needed_groups != NULL)
506 {
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;
511 current_group = tmp->name;
512 continue;
513 }
514
515 /* No way out. */
516 break;
517 }
518
519 /* Free the memory. */
520 free_memory (&entry);
521
522 return result == 1;
523 }
524 libc_hidden_def (innetgr)