Update.
[platform/upstream/glibc.git] / hesiod / nss_hesiod / hesiod-grp.c
1 /* Copyright (C) 1997, 1999, 2000 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <grp.h>
23 #include <hesiod.h>
24 #include <nss.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "nss_hesiod.h"
30
31 /* Get the declaration of the parser function.  */
32 #define ENTNAME grent
33 #define STRUCTURE group
34 #define EXTERN_PARSER
35 #include <nss/nss_files/files-parse.c>
36
37 enum nss_status
38 _nss_hesiod_setgrent (void)
39 {
40   return NSS_STATUS_SUCCESS;
41 }
42
43 enum nss_status
44 _nss_hesiod_endgrent (void)
45 {
46   return NSS_STATUS_SUCCESS;
47 }
48
49 static enum nss_status
50 lookup (const char *name, const char *type, struct group *grp,
51         char *buffer, size_t buflen, int *errnop)
52 {
53   struct parser_data *data = (void *) buffer;
54   size_t linebuflen;
55   void *context;
56   char **list;
57   int parse_res;
58   size_t len;
59
60   context = _nss_hesiod_init ();
61   if (context == NULL)
62     return NSS_STATUS_UNAVAIL;
63
64   list = hesiod_resolve (context, name, type);
65   if (list == NULL)
66     {
67       hesiod_end (context);
68       return errno == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL;
69     }
70
71   linebuflen = buffer + buflen - data->linebuffer;
72   len = strlen (*list) + 1;
73   if (linebuflen < len)
74     {
75       hesiod_free_list (context, list);
76       hesiod_end (context);
77       *errnop = ERANGE;
78       return NSS_STATUS_TRYAGAIN;
79     }
80
81   memcpy (data->linebuffer, *list, len);
82   hesiod_free_list (context, list);
83   hesiod_end (context);
84
85   parse_res = _nss_files_parse_grent (buffer, grp, data, buflen, errnop);
86   if (parse_res < 1)
87     return parse_res == -1 ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
88
89   return NSS_STATUS_SUCCESS;
90 }
91
92 enum nss_status
93 _nss_hesiod_getgrnam_r (const char *name, struct group *grp,
94                         char *buffer, size_t buflen, int *errnop)
95 {
96   return lookup (name, "group", grp, buffer, buflen, errnop);
97 }
98
99 enum nss_status
100 _nss_hesiod_getgrgid_r (gid_t gid, struct group *grp,
101                         char *buffer, size_t buflen, int *errnop)
102 {
103   char gidstr[21];      /* We will probably never have a gid_t with more
104                            than 64 bits.  */
105
106   snprintf (gidstr, sizeof gidstr, "%d", gid);
107
108   return lookup (gidstr, "gid", grp, buffer, buflen, errnop);
109 }
110
111 static int
112 internal_gid_in_list (const gid_t *list, const gid_t g, long int len)
113 {
114   while (len > 0)
115     {
116       if (*list == g)
117         return 1;
118       --len;
119       ++list;
120     }
121   return 0;
122 }
123
124 static enum nss_status
125 internal_gid_from_group (void *context, const char *groupname, gid_t *group)
126 {
127   char **grp_res;
128   enum nss_status status = NSS_STATUS_NOTFOUND;
129
130   grp_res = hesiod_resolve (context, groupname, "group");
131   if (grp_res != NULL && *grp_res != NULL)
132     {
133       char *p = *grp_res;
134
135       while (*p != '\0' && *p != ':')
136         ++p;
137       while (*p != '\0' && *p == ':')
138         ++p;
139       while (*p != '\0' && *p != ':')
140         ++p;
141       while (*p != '\0' && *p == ':')
142         ++p;
143       if (*p == ':')
144         {
145           char *endp;
146           char *q = ++p;
147           long int val;
148
149           q = p;
150           while (*q != '\0' && *q != ':')
151             ++q;
152
153           val = strtol (p, &endp, 10);
154           if (sizeof (gid_t) == sizeof (long int) || (gid_t) val == val)
155             {
156               *group = val;
157               if (endp == q && endp != p)
158                 status = NSS_STATUS_SUCCESS;
159             }
160         }
161       hesiod_free_list (context, grp_res);
162     }
163   return status;
164 }
165
166 enum nss_status
167 _nss_hesiod_initgroups_dyn (const char *user, gid_t group, long int *start,
168                             long int *size, gid_t **groupsp, int *errnop)
169 {
170   enum nss_status status = NSS_STATUS_SUCCESS;
171   char **list = NULL;
172   char *p;
173   void *context;
174   gid_t *groups = *groupsp;
175
176   context = _nss_hesiod_init ();
177   if (context == NULL)
178     return NSS_STATUS_UNAVAIL;
179
180   list = hesiod_resolve (context, user, "grplist");
181
182   if (list == NULL)
183     {
184       hesiod_end (context);
185       return errno == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL;
186     }
187
188   if (!internal_gid_in_list (groups, group, *start))
189     {
190       if (__builtin_expect (*start == *size, 0))
191         {
192           /* Need a bigger buffer.  */
193           gid_t *newgroups;
194           newgroups = realloc (groups, 2 * *size * sizeof (*groups));
195           if (newgroups == NULL)
196             goto done;
197           *groupsp = groups = newgroups;
198           *size *= 2;
199         }
200
201       groups[(*start)++] = group;
202     }
203
204   p = *list;
205   while (*p != '\0')
206     {
207       char *endp;
208       char *q;
209       long int val;
210
211       status = NSS_STATUS_NOTFOUND;
212
213       q = p;
214       while (*q != '\0' && *q != ':')
215         ++q;
216
217       if (*q != '\0')
218         *q++ = '\0';
219
220       val = strtol (p, &endp, 10);
221       if (sizeof (gid_t) == sizeof (long int) || (gid_t) val == val)
222         {
223           if (*endp == '\0' && endp != p)
224             status = NSS_STATUS_SUCCESS;
225           else
226             status = internal_gid_from_group (context, p, &group);
227
228           if (status == NSS_STATUS_SUCCESS
229               && !internal_gid_in_list (groups, group, *start))
230             {
231               if (__builtin_expect (*start == *size, 0))
232                 {
233                   /* Need a bigger buffer.  */
234                   gid_t *newgroups;
235                   newgroups = realloc (groups, 2 * *size * sizeof (*groups));
236                   if (newgroups == NULL)
237                     goto done;
238                   *groupsp = groups = newgroups;
239                   *size *= 2;
240                 }
241
242               groups[(*start)++] = group;
243             }
244         }
245
246       p = q;
247     }
248
249  done:
250   hesiod_free_list (context, list);
251   hesiod_end (context);
252
253   return NSS_STATUS_SUCCESS;
254 }