Allow AF_PACKET.
[kopensolaris-gnu/glibc.git] / inet / getnetgrent_r.c
1 /* Copyright (C) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
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.
8
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.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <bits/libc-lock.h>
20 #include <errno.h>
21 #include <netdb.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "netgroup.h"
25 #include "nsswitch.h"
26
27
28 /* Protect above variable against multiple uses at the same time.  */
29 __libc_lock_define_initialized (static, lock)
30
31 /* This handle for the NSS data base is shared between all
32    set/get/endXXXent functions.  */
33 static service_user *nip;
34
35 /* The whole information for the set/get/endnetgrent functions are
36    kept in this structure.  */
37 static struct __netgrent dataset;
38
39 /* The lookup function for the first entry of this service.  */
40 extern int __nss_netgroup_lookup (service_user **nip, const char *name,
41                                   void **fctp) internal_function;
42
43
44 /* Set up NIP to run through the services.  If ALL is zero, use NIP's
45    current location if it's not nil.  Return nonzero if there are no
46    services (left).  */
47 static enum nss_status
48 setup (void **fctp, const char *func_name, int all)
49 {
50   /* Remember the first service_entry, it's always the same.  */
51   static service_user *startp;
52   int no_more;
53
54   if (startp == NULL)
55     {
56       no_more = __nss_netgroup_lookup (&nip, func_name, fctp);
57       startp = no_more ? (service_user *) -1 : nip;
58     }
59   else if (startp == (service_user *) -1)
60     /* No services at all.  */
61     return 1;
62   else
63     {
64       if (all || !nip)
65         /* Reset to the beginning of the service list.  */
66         nip = startp;
67       /* Look up the first function.  */
68       no_more = __nss_lookup (&nip, func_name, fctp);
69     }
70   return no_more;
71 }
72 \f
73 /* Free used memory.  */
74 static void
75 free_memory (struct __netgrent *data)
76 {
77   while (data->known_groups != NULL)
78     {
79       struct name_list *tmp = data->known_groups;
80       data->known_groups = data->known_groups->next;
81       free ((void *) tmp->name);
82       free (tmp);
83     }
84
85   while (data->needed_groups != NULL)
86     {
87       struct name_list *tmp = data->needed_groups;
88       data->needed_groups = data->needed_groups->next;
89       free ((void *) tmp->name);
90       free (tmp);
91     }
92 }
93 \f
94 static int
95 internal_function
96 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
97                               int *errnop)
98 {
99   union
100   {
101     enum nss_status (*f) (const char *, struct __netgrent *);
102     void *ptr;
103   } fct;
104   enum nss_status status = NSS_STATUS_UNAVAIL;
105   struct name_list *new_elem;
106   int no_more;
107
108   /* Cycle through all the services and run their setnetgrent functions.  */
109   no_more = setup (&fct.ptr, "setnetgrent", 1);
110   while (! no_more)
111     {
112       /* Ignore status, we force check in `__nss_next'.  */
113       status = (*fct.f) (group, datap);
114
115       no_more = __nss_next (&nip, "setnetgrent", &fct.ptr, status, 0);
116     }
117
118   /* Add the current group to the list of known groups.  */
119   new_elem = (struct name_list *) malloc (sizeof (struct name_list));
120   if (new_elem == NULL || (new_elem->name = __strdup (group)) == NULL)
121     {
122       if (new_elem != NULL)
123         free (new_elem);
124       *errnop = errno;
125       status = NSS_STATUS_TRYAGAIN;
126     }
127   else
128     {
129       new_elem->next = datap->known_groups;
130       datap->known_groups = new_elem;
131     }
132
133   return status == NSS_STATUS_SUCCESS;
134 }
135
136 static int
137 internal_setnetgrent (const char *group, struct __netgrent *datap)
138 {
139   /* Free list of all netgroup names from last run.  */
140   free_memory (datap);
141
142   return __internal_setnetgrent_reuse (group, datap, &errno);
143 }
144 strong_alias (internal_setnetgrent, __internal_setnetgrent)
145
146 int
147 setnetgrent (const char *group)
148 {
149   int result;
150
151   __libc_lock_lock (lock);
152
153   result = internal_setnetgrent (group, &dataset);
154
155   __libc_lock_unlock (lock);
156
157   return result;
158 }
159
160
161 static void
162 internal_endnetgrent (struct __netgrent *datap)
163 {
164   service_user *old_nip;
165   union
166   {
167     enum nss_status (*f) (struct __netgrent *);
168     void *ptr;
169   } fct;
170   int no_more;
171
172   /* Remember which was the last used service.  */
173   old_nip = nip;
174
175   /* Cycle through all the services and run their endnetgrent functions.  */
176   no_more = setup (&fct.ptr, "endnetgrent", 1);
177   while (! no_more)
178     {
179       /* Ignore status, we force check in `__nss_next'.  */
180       (void) (*fct.f) (datap);
181
182       no_more = (nip == old_nip
183                  || __nss_next (&nip, "endnetgrent", &fct.ptr, 0, 1));
184     }
185
186   /* Now free list of all netgroup names from last run.  */
187   free_memory (datap);
188 }
189 strong_alias (internal_endnetgrent, __internal_endnetgrent)
190
191
192 void
193 endnetgrent (void)
194 {
195   __libc_lock_lock (lock);
196
197   internal_endnetgrent (&dataset);
198
199   __libc_lock_unlock (lock);
200 }
201
202
203 static int
204 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
205                           struct __netgrent *datap,
206                           char *buffer, size_t buflen, int *errnop)
207 {
208   union
209   {
210     enum nss_status (*f) (struct __netgrent *, char *, size_t, int *);
211     void *ptr;
212   } fct;
213   int no_more;
214
215   /* Initialize status to return if no more functions are found.  */
216   enum nss_status status = NSS_STATUS_NOTFOUND;
217
218   /* Run through available functions, starting with the same function last
219      run.  We will repeat each function as long as it succeeds, and then go
220      on to the next service action.  */
221   no_more = setup (&fct.ptr, "getnetgrent_r", 0);
222   while (! no_more)
223     {
224       status = (*fct.f) (datap, buffer, buflen, &errno);
225
226       if (status == NSS_STATUS_RETURN)
227         {
228           /* This was the last one for this group.  Look at next group
229              if available.  */
230           int found = 0;
231           while (datap->needed_groups != NULL && ! found)
232             {
233               struct name_list *tmp = datap->needed_groups;
234               datap->needed_groups = datap->needed_groups->next;
235               tmp->next = datap->known_groups;
236               datap->known_groups = tmp;
237
238               found = __internal_setnetgrent_reuse (datap->known_groups->name,
239                                                     datap, errnop);
240             }
241
242           if (found)
243             continue;
244         }
245       else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
246         {
247           /* The last entry was a name of another netgroup.  */
248           struct name_list *namep;
249
250           /* Ignore if we've seen the name before.  */
251           for (namep = datap->known_groups; namep != NULL;
252                namep = namep->next)
253             if (strcmp (datap->val.group, namep->name) == 0)
254               break;
255           if (namep != NULL)
256             /* Really ignore.  */
257             continue;
258
259           namep = (struct name_list *) malloc (sizeof (struct name_list));
260           if (namep == NULL
261               || (namep->name = __strdup (datap->val.group)) == NULL)
262             {
263               /* We are out of memory.  */
264               if (namep != NULL)
265                 free (namep);
266               status = NSS_STATUS_RETURN;
267             }
268           else
269             {
270               namep->next = datap->needed_groups;
271               datap->needed_groups = namep;
272               /* And get the next entry.  */
273               continue;
274             }
275         }
276
277       no_more = __nss_next (&nip, "getnetgrent_r", &fct.ptr, status, 0);
278     }
279
280   if (status == NSS_STATUS_SUCCESS)
281     {
282       *hostp = (char *) datap->val.triple.host;
283       *userp = (char *) datap->val.triple.user;
284       *domainp = (char *) datap->val.triple.domain;
285     }
286
287   return status == NSS_STATUS_SUCCESS ? 1 : 0;
288 }
289 strong_alias (internal_getnetgrent_r, __internal_getnetgrent_r)
290
291 /* The real entry point.  */
292 int
293 __getnetgrent_r (char **hostp, char **userp, char **domainp,
294                  char *buffer, size_t buflen)
295 {
296   enum nss_status status;
297
298   __libc_lock_lock (lock);
299
300   status = internal_getnetgrent_r (hostp, userp, domainp, &dataset,
301                                    buffer, buflen, &errno);
302
303   __libc_lock_unlock (lock);
304
305   return status;
306 }
307 weak_alias (__getnetgrent_r, getnetgrent_r)
308 \f
309 /* Test whether given (host,user,domain) triple is in NETGROUP.  */
310 int
311 innetgr (const char *netgroup, const char *host, const char *user,
312          const char *domain)
313 {
314   union
315   {
316     int (*f) (const char *, struct __netgrent *);
317     void *ptr;
318   } setfct;
319   union
320   {
321     void (*f) (struct __netgrent *);
322     void *ptr;
323   } endfct;
324   union
325   {
326     int (*f) (struct __netgrent *, char *, size_t, int *);
327     void *ptr;
328   } getfct;
329   struct name_list *known = NULL;
330   struct name_list *needed = NULL;
331   int result = 0;
332   int no_more;
333   const char *current_group = netgroup;
334   int real_entry = 0;
335
336   /* Walk through the services until we found an answer or we shall
337      not work further.  We can do some optimization here.  Since all
338      services must provide the `setnetgrent' function we can do all
339      the work during one walk through the service list.  */
340   while (1)
341     {
342       no_more = setup (&setfct.ptr, "setnetgrent", 1);
343       while (! no_more)
344         {
345           enum nss_status status;
346           struct __netgrent entry;
347
348           /* Clear the space for the netgroup data.  */
349           __bzero (&entry, sizeof (entry));
350
351           /* Open netgroup.  */
352           status = (*setfct.f) (current_group, &entry);
353           if (status == NSS_STATUS_SUCCESS
354               && __nss_lookup (&nip, "getnetgrent_r", &getfct.ptr) == 0)
355             {
356               char buffer[1024];
357
358               while ((*getfct.f) (&entry, buffer, sizeof buffer, &errno)
359                      == NSS_STATUS_SUCCESS)
360                 {
361                   if (entry.type == group_val)
362                     {
363                       /* Make sure we haven't seen the name before.  */
364                       struct name_list *namep;
365
366                       for (namep = known; namep != NULL; namep = namep->next)
367                         if (strcmp (entry.val.group, namep->name) == 0)
368                           break;
369                       if (namep == NULL
370                           && strcmp (netgroup, entry.val.group) != 0)
371                         {
372                           namep =
373                             (struct name_list *) malloc (sizeof (*namep));
374                           if (namep == NULL
375                               || ((namep->name = __strdup (entry.val.group))
376                                   == NULL))
377                             {
378                               /* Out of memory, simply return.  */
379                               if (namep != NULL)
380                                 free (namep);
381                               result = -1;
382                               break;
383                             }
384
385                           namep->next = needed;
386                           needed = namep;
387                         }
388                     }
389                   else
390                     {
391                       real_entry = 1;
392
393                       if ((entry.val.triple.host == NULL || host == NULL
394                            || __strcasecmp (entry.val.triple.host, host) == 0)
395                           && (entry.val.triple.user == NULL || user == NULL
396                               || strcmp (entry.val.triple.user, user) == 0)
397                           && (entry.val.triple.domain == NULL || domain == NULL
398                               || __strcasecmp (entry.val.triple.domain,
399                                                domain) == 0))
400                         {
401                           result = 1;
402                           break;
403                         }
404                     }
405                 }
406
407               if (result != 0)
408                 break;
409
410               /* If we found one service which does know the given
411                  netgroup we don't try further.  */
412               status = NSS_STATUS_RETURN;
413             }
414
415           /* Free all resources of the service.  */
416           if (__nss_lookup (&nip, "endnetgrent", &endfct.ptr) == 0)
417             (*endfct.f) (&entry);
418
419           /* Look for the next service.  */
420           no_more = __nss_next (&nip, "setnetgrent",
421                                 &setfct.ptr, status, 0);
422         }
423
424       if (result == 0 && needed != NULL)
425         {
426           struct name_list *tmp = needed;
427           needed = tmp->next;
428           tmp->next = known;
429           known = tmp;
430           current_group = known->name;
431           continue;
432         }
433
434       /* No way out.  */
435       break;
436     }
437
438   /* Free the memory.  */
439   while (known != NULL)
440     {
441       struct name_list *tmp = known;
442       known = known->next;
443       free ((void *) tmp->name);
444       free (tmp);
445     }
446   while (needed != NULL)
447     {
448       struct name_list *tmp = needed;
449       needed = needed->next;
450       free ((void *) tmp->name);
451       free (tmp);
452     }
453
454   return result == 1;
455 }
456 libc_hidden_def (innetgr)