Adjust code for change in name_list layout. Numerous strdup and free
[kopensolaris-gnu/glibc.git] / inet / getnetgrent_r.c
1 /* Copyright (C) 1996,1997,1998,1999,2002,2004 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 (tmp);
82     }
83
84   while (data->needed_groups != NULL)
85     {
86       struct name_list *tmp = data->needed_groups;
87       data->needed_groups = data->needed_groups->next;
88       free (tmp);
89     }
90 }
91 \f
92 static int
93 internal_function
94 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
95                               int *errnop)
96 {
97   union
98   {
99     enum nss_status (*f) (const char *, struct __netgrent *);
100     void *ptr;
101   } fct;
102   enum nss_status status = NSS_STATUS_UNAVAIL;
103   struct name_list *new_elem;
104   int no_more;
105
106   /* Cycle through all the services and run their setnetgrent functions.  */
107   no_more = setup (&fct.ptr, "setnetgrent", 1);
108   while (! no_more)
109     {
110       /* Ignore status, we force check in `__nss_next'.  */
111       status = (*fct.f) (group, datap);
112
113       no_more = __nss_next (&nip, "setnetgrent", &fct.ptr, status, 0);
114     }
115
116   /* Add the current group to the list of known groups.  */
117   size_t group_len = strlen (group) + 1;
118   new_elem = (struct name_list *) malloc (sizeof (struct name_list)
119                                           + group_len);
120   if (new_elem == NULL)
121     {
122       *errnop = errno;
123       status = NSS_STATUS_TRYAGAIN;
124     }
125   else
126     {
127       new_elem->next = datap->known_groups;
128       memcpy (new_elem->name, group, group_len);
129       datap->known_groups = new_elem;
130     }
131
132   return status == NSS_STATUS_SUCCESS;
133 }
134
135 int internal_setnetgrent (const char *group, struct __netgrent *datap);
136 libc_hidden_proto (internal_setnetgrent)
137
138 int
139 internal_setnetgrent (const char *group, struct __netgrent *datap)
140 {
141   /* Free list of all netgroup names from last run.  */
142   free_memory (datap);
143
144   return __internal_setnetgrent_reuse (group, datap, &errno);
145 }
146 libc_hidden_def (internal_setnetgrent)
147 strong_alias (internal_setnetgrent, __internal_setnetgrent)
148
149 int
150 setnetgrent (const char *group)
151 {
152   int result;
153
154   __libc_lock_lock (lock);
155
156   result = internal_setnetgrent (group, &dataset);
157
158   __libc_lock_unlock (lock);
159
160   return result;
161 }
162
163
164 void internal_endnetgrent (struct __netgrent *datap);
165 libc_hidden_proto (internal_endnetgrent)
166
167 void
168 internal_endnetgrent (struct __netgrent *datap)
169 {
170   service_user *old_nip;
171   union
172   {
173     enum nss_status (*f) (struct __netgrent *);
174     void *ptr;
175   } fct;
176   int no_more;
177
178   /* Remember which was the last used service.  */
179   old_nip = nip;
180
181   /* Cycle through all the services and run their endnetgrent functions.  */
182   no_more = setup (&fct.ptr, "endnetgrent", 1);
183   while (! no_more)
184     {
185       /* Ignore status, we force check in `__nss_next'.  */
186       (void) (*fct.f) (datap);
187
188       no_more = (nip == old_nip
189                  || __nss_next (&nip, "endnetgrent", &fct.ptr, 0, 1));
190     }
191
192   /* Now free list of all netgroup names from last run.  */
193   free_memory (datap);
194 }
195 libc_hidden_def (internal_endnetgrent)
196 strong_alias (internal_endnetgrent, __internal_endnetgrent)
197
198
199 void
200 endnetgrent (void)
201 {
202   __libc_lock_lock (lock);
203
204   internal_endnetgrent (&dataset);
205
206   __libc_lock_unlock (lock);
207 }
208
209
210 int internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
211                             struct __netgrent *datap,
212                             char *buffer, size_t buflen, int *errnop);
213 libc_hidden_proto (internal_getnetgrent_r)
214
215 int
216 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
217                           struct __netgrent *datap,
218                           char *buffer, size_t buflen, int *errnop)
219 {
220   union
221   {
222     enum nss_status (*f) (struct __netgrent *, char *, size_t, int *);
223     void *ptr;
224   } fct;
225   int no_more;
226
227   /* Initialize status to return if no more functions are found.  */
228   enum nss_status status = NSS_STATUS_NOTFOUND;
229
230   /* Run through available functions, starting with the same function last
231      run.  We will repeat each function as long as it succeeds, and then go
232      on to the next service action.  */
233   no_more = setup (&fct.ptr, "getnetgrent_r", 0);
234   while (! no_more)
235     {
236       status = (*fct.f) (datap, buffer, buflen, &errno);
237
238       if (status == NSS_STATUS_RETURN)
239         {
240           /* This was the last one for this group.  Look at next group
241              if available.  */
242           int found = 0;
243           while (datap->needed_groups != NULL && ! found)
244             {
245               struct name_list *tmp = datap->needed_groups;
246               datap->needed_groups = datap->needed_groups->next;
247               tmp->next = datap->known_groups;
248               datap->known_groups = tmp;
249
250               found = __internal_setnetgrent_reuse (datap->known_groups->name,
251                                                     datap, errnop);
252             }
253
254           if (found)
255             continue;
256         }
257       else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
258         {
259           /* The last entry was a name of another netgroup.  */
260           struct name_list *namep;
261
262           /* Ignore if we've seen the name before.  */
263           for (namep = datap->known_groups; namep != NULL;
264                namep = namep->next)
265             if (strcmp (datap->val.group, namep->name) == 0)
266               break;
267           if (namep != NULL)
268             /* Really ignore.  */
269             continue;
270
271           size_t group_len = strlen (datap->val.group) + 1;
272           namep = (struct name_list *) malloc (sizeof (struct name_list)
273                                                + group_len);
274           if (namep == NULL)
275             /* We are out of memory.  */
276             status = NSS_STATUS_RETURN;
277           else
278             {
279               namep->next = datap->needed_groups;
280               memcpy (namep->name, datap->val.group, group_len);
281               datap->needed_groups = namep;
282               /* And get the next entry.  */
283               continue;
284             }
285         }
286
287       no_more = __nss_next (&nip, "getnetgrent_r", &fct.ptr, status, 0);
288     }
289
290   if (status == NSS_STATUS_SUCCESS)
291     {
292       *hostp = (char *) datap->val.triple.host;
293       *userp = (char *) datap->val.triple.user;
294       *domainp = (char *) datap->val.triple.domain;
295     }
296
297   return status == NSS_STATUS_SUCCESS ? 1 : 0;
298 }
299 libc_hidden_def (internal_getnetgrent_r)
300 strong_alias (internal_getnetgrent_r, __internal_getnetgrent_r)
301
302 /* The real entry point.  */
303 int
304 __getnetgrent_r (char **hostp, char **userp, char **domainp,
305                  char *buffer, size_t buflen)
306 {
307   enum nss_status status;
308
309   __libc_lock_lock (lock);
310
311   status = internal_getnetgrent_r (hostp, userp, domainp, &dataset,
312                                    buffer, buflen, &errno);
313
314   __libc_lock_unlock (lock);
315
316   return status;
317 }
318 weak_alias (__getnetgrent_r, getnetgrent_r)
319 \f
320 /* Test whether given (host,user,domain) triple is in NETGROUP.  */
321 int
322 innetgr (const char *netgroup, const char *host, const char *user,
323          const char *domain)
324 {
325   union
326   {
327     int (*f) (const char *, struct __netgrent *);
328     void *ptr;
329   } setfct;
330   union
331   {
332     void (*f) (struct __netgrent *);
333     void *ptr;
334   } endfct;
335   union
336   {
337     int (*f) (struct __netgrent *, char *, size_t, int *);
338     void *ptr;
339   } getfct;
340   struct name_list *known = NULL;
341   struct name_list *needed = NULL;
342   int result = 0;
343   int no_more;
344   const char *current_group = netgroup;
345   int real_entry = 0;
346
347   /* Walk through the services until we found an answer or we shall
348      not work further.  We can do some optimization here.  Since all
349      services must provide the `setnetgrent' function we can do all
350      the work during one walk through the service list.  */
351   while (1)
352     {
353       no_more = setup (&setfct.ptr, "setnetgrent", 1);
354       while (! no_more)
355         {
356           enum nss_status status;
357           struct __netgrent entry;
358
359           /* Clear the space for the netgroup data.  */
360           __bzero (&entry, sizeof (entry));
361
362           /* Open netgroup.  */
363           status = (*setfct.f) (current_group, &entry);
364           if (status == NSS_STATUS_SUCCESS
365               && __nss_lookup (&nip, "getnetgrent_r", &getfct.ptr) == 0)
366             {
367               char buffer[1024];
368
369               while ((*getfct.f) (&entry, buffer, sizeof buffer, &errno)
370                      == NSS_STATUS_SUCCESS)
371                 {
372                   if (entry.type == group_val)
373                     {
374                       /* Make sure we haven't seen the name before.  */
375                       struct name_list *namep;
376
377                       for (namep = known; namep != NULL; namep = namep->next)
378                         if (strcmp (entry.val.group, namep->name) == 0)
379                           break;
380                       if (namep == NULL
381                           && strcmp (netgroup, entry.val.group) != 0)
382                         {
383                           size_t group_len = strlen (entry.val.group) + 1;
384                           namep =
385                             (struct name_list *) malloc (sizeof (*namep)
386                                                          + group_len);
387                           if (namep == NULL)
388                             {
389                               /* Out of memory, simply return.  */
390                               result = -1;
391                               break;
392                             }
393
394                           namep->next = needed;
395                           memcpy (namep->name, entry.val.group, group_len);
396                           needed = namep;
397                         }
398                     }
399                   else
400                     {
401                       real_entry = 1;
402
403                       if ((entry.val.triple.host == NULL || host == NULL
404                            || __strcasecmp (entry.val.triple.host, host) == 0)
405                           && (entry.val.triple.user == NULL || user == NULL
406                               || strcmp (entry.val.triple.user, user) == 0)
407                           && (entry.val.triple.domain == NULL || domain == NULL
408                               || __strcasecmp (entry.val.triple.domain,
409                                                domain) == 0))
410                         {
411                           result = 1;
412                           break;
413                         }
414                     }
415                 }
416
417               if (result != 0)
418                 break;
419
420               /* If we found one service which does know the given
421                  netgroup we don't try further.  */
422               status = NSS_STATUS_RETURN;
423             }
424
425           /* Free all resources of the service.  */
426           if (__nss_lookup (&nip, "endnetgrent", &endfct.ptr) == 0)
427             (*endfct.f) (&entry);
428
429           /* Look for the next service.  */
430           no_more = __nss_next (&nip, "setnetgrent",
431                                 &setfct.ptr, status, 0);
432         }
433
434       if (result == 0 && needed != NULL)
435         {
436           struct name_list *tmp = needed;
437           needed = tmp->next;
438           tmp->next = known;
439           known = tmp;
440           current_group = known->name;
441           continue;
442         }
443
444       /* No way out.  */
445       break;
446     }
447
448   /* Free the memory.  */
449   while (known != NULL)
450     {
451       struct name_list *tmp = known;
452       known = known->next;
453       free (tmp);
454     }
455   while (needed != NULL)
456     {
457       struct name_list *tmp = needed;
458       needed = needed->next;
459       free (tmp);
460     }
461
462   return result == 1;
463 }
464 libc_hidden_def (innetgr)