(EXPAND): Free buffer which cannot be expanded.
[kopensolaris-gnu/glibc.git] / nss / nss_files / files-netgrp.c
1 /* Netgroup file parser in nss_files modules.
2    Copyright (C) 1996, 1997, 2000, 2004 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "nsswitch.h"
28 #include "netgroup.h"
29
30 #define DATAFILE        "/etc/netgroup"
31
32
33 #define EXPAND(needed)                                                        \
34   do                                                                          \
35     {                                                                         \
36       size_t old_cursor = result->cursor - result->data;                      \
37       void *old_data = result->data;                                          \
38                                                                               \
39       result->data_size += 512 > 2 * needed ? 512 : 2 * needed;               \
40       result->data = realloc (result->data, result->data_size);               \
41                                                                               \
42       if (result->data == NULL)                                               \
43         {                                                                     \
44           free (old_data);                                                    \
45           status = NSS_STATUS_UNAVAIL;                                        \
46           goto the_end;                                                       \
47         }                                                                     \
48                                                                               \
49       result->cursor = result->data + old_cursor;                             \
50     }                                                                         \
51   while (0)
52
53
54 enum nss_status
55 _nss_files_setnetgrent (const char *group, struct __netgrent *result)
56 {
57   FILE *fp;
58   enum nss_status status;
59
60   if (group[0] == '\0')
61     return NSS_STATUS_UNAVAIL;
62
63   /* Find the netgroups file and open it.  */
64   fp = fopen (DATAFILE, "r");
65   if (fp == NULL)
66     status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
67   else
68     {
69       /* Read the file line by line and try to find the description
70          GROUP.  We must take care for long lines.  */
71       char *line = NULL;
72       size_t line_len = 0;
73       const ssize_t group_len = strlen (group);
74
75       status = NSS_STATUS_NOTFOUND;
76       result->cursor = result->data;
77
78       while (!feof (fp))
79         {
80           ssize_t curlen = getline (&line, &line_len, fp);
81           int found;
82
83           if (curlen < 0)
84             {
85               status = NSS_STATUS_NOTFOUND;
86               break;
87             }
88
89           found = (curlen > group_len && strncmp (line, group, group_len) == 0
90                    && isspace (line[group_len]));
91
92           /* Read the whole line (including continuation) and store it
93              if FOUND in nonzero.  Otherwise we don't need it.  */
94           if (found)
95             {
96               /* Store the data from the first line.  */
97               EXPAND (curlen - group_len);
98               memcpy (result->cursor, &line[group_len + 1],
99                       curlen - group_len);
100               result->cursor += (curlen - group_len) - 1;
101             }
102
103           while (line[curlen - 1] == '\n' && line[curlen - 2] == '\\')
104             {
105               /* Yes, we have a continuation line.  */
106               if (found)
107                 /* Remove these characters from the stored line.  */
108                 result->cursor -= 2;
109
110               /* Get next line.  */
111               curlen = getline (&line, &line_len, fp);
112               if (curlen <= 0)
113                 break;
114
115               if (found)
116                 {
117                   /* Make sure we have enough room.  */
118                   EXPAND (1 + curlen + 1);
119
120                   /* Add separator in case next line starts immediately.  */
121                   *result->cursor++ = ' ';
122
123                   /* Copy new line.  */
124                   memcpy (result->cursor, line, curlen + 1);
125                   result->cursor += curlen;
126                 }
127             }
128
129           if (found)
130             {
131               /* Now we have read the line.  */
132               status = NSS_STATUS_SUCCESS;
133               result->cursor = result->data;
134               result->first = 1;
135               break;
136             }
137         }
138
139     the_end:
140       /* We don't need the file and the line buffer anymore.  */
141       free (line);
142       fclose (fp);
143     }
144
145   return status;
146 }
147
148
149 int
150 _nss_files_endnetgrent (struct __netgrent *result)
151 {
152   /* Free allocated memory for data if some is present.  */
153   if (result->data != NULL)
154     {
155       free (result->data);
156       result->data = NULL;
157       result->data_size = 0;
158       result->cursor = NULL;
159     }
160
161   return NSS_STATUS_SUCCESS;
162 }
163
164 static char *
165 strip_whitespace (char *str)
166 {
167   char *cp = str;
168
169   /* Skip leading spaces.  */
170   while (isspace (*cp))
171     cp++;
172
173   str = cp;
174   while (*cp != '\0' && ! isspace(*cp))
175     cp++;
176
177   /* Null-terminate, stripping off any trailing spaces.  */
178   *cp = '\0';
179
180   return *str == '\0' ? NULL : str;
181 }
182
183 enum nss_status
184 _nss_netgroup_parseline (char **cursor, struct __netgrent *result,
185                          char *buffer, size_t buflen, int *errnop)
186 {
187   enum nss_status status;
188   const char *host, *user, *domain;
189   char *cp = *cursor;
190
191   /* Some sanity checks.  */
192   if (cp == NULL)
193     return NSS_STATUS_NOTFOUND;
194
195   /* First skip leading spaces.  */
196   while (isspace (*cp))
197     ++cp;
198
199   if (*cp != '(')
200     {
201       /* We have a list of other netgroups.  */
202       char *name = cp;
203
204       while (*cp != '\0' && ! isspace (*cp))
205         ++cp;
206
207       if (name != cp)
208         {
209           /* It is another netgroup name.  */
210           int last = *cp == '\0';
211
212           result->type = group_val;
213           result->val.group = name;
214           *cp = '\0';
215           if (! last)
216             ++cp;
217           *cursor = cp;
218           result->first = 0;
219
220           return NSS_STATUS_SUCCESS;
221         }
222
223       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
224     }
225
226   /* Match host name.  */
227   host = ++cp;
228   while (*cp != ',')
229     if (*cp++ == '\0')
230       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
231
232   /* Match user name.  */
233   user = ++cp;
234   while (*cp != ',')
235     if (*cp++ == '\0')
236       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
237
238   /* Match domain name.  */
239   domain = ++cp;
240   while (*cp != ')')
241     if (*cp++ == '\0')
242       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
243   ++cp;
244
245
246   /* When we got here we have found an entry.  Before we can copy it
247      to the private buffer we have to make sure it is big enough.  */
248   if (cp - host > buflen)
249     {
250       *errnop = ERANGE;
251       status = NSS_STATUS_UNAVAIL;
252     }
253   else
254     {
255       memcpy (buffer, host, cp - host);
256       result->type = triple_val;
257
258       buffer[(user - host) - 1] = '\0'; /* Replace ',' with '\0'.  */
259       result->val.triple.host = strip_whitespace (buffer);
260
261       buffer[(domain - host) - 1] = '\0'; /* Replace ',' with '\0'.  */
262       result->val.triple.user = strip_whitespace (buffer + (user - host));
263
264       buffer[(cp - host) - 1] = '\0'; /* Replace ')' with '\0'.  */
265       result->val.triple.domain = strip_whitespace (buffer + (domain - host));
266
267       status = NSS_STATUS_SUCCESS;
268
269       /* Remember where we stopped reading.  */
270       *cursor = cp;
271
272       result->first = 0;
273     }
274
275   return status;
276 }
277
278
279 enum nss_status
280 _nss_files_getnetgrent_r (struct __netgrent *result, char *buffer,
281                           size_t buflen, int *errnop)
282 {
283   enum nss_status status;
284
285   status = _nss_netgroup_parseline (&result->cursor, result, buffer, buflen,
286                                     errnop);
287
288   return status;
289 }