* inet/getnetgrent_r.c: Include assert.
[platform/upstream/glibc.git] / nss / nss_files / files-netgrp.c
1 /* Netgroup file parser in nss_files modules.
2    Copyright (C) 1996, 1997, 2000, 2004, 2005 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 <stdio_ext.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "nsswitch.h"
29 #include "netgroup.h"
30
31 #define DATAFILE        "/etc/netgroup"
32
33 libnss_files_hidden_proto (_nss_files_endnetgrent)
34
35 #define EXPAND(needed)                                                        \
36   do                                                                          \
37     {                                                                         \
38       size_t old_cursor = result->cursor - result->data;                      \
39       void *old_data = result->data;                                          \
40                                                                               \
41       result->data_size += 512 > 2 * needed ? 512 : 2 * needed;               \
42       result->data = realloc (result->data, result->data_size);               \
43                                                                               \
44       if (result->data == NULL)                                               \
45         {                                                                     \
46           free (old_data);                                                    \
47           status = NSS_STATUS_UNAVAIL;                                        \
48           goto the_end;                                                       \
49         }                                                                     \
50                                                                               \
51       result->cursor = result->data + old_cursor;                             \
52     }                                                                         \
53   while (0)
54
55
56 enum nss_status
57 _nss_files_setnetgrent (const char *group, struct __netgrent *result)
58 {
59   FILE *fp;
60   enum nss_status status;
61
62   if (group[0] == '\0')
63     return NSS_STATUS_UNAVAIL;
64
65   /* Find the netgroups file and open it.  */
66   fp = fopen (DATAFILE, "r");
67   if (fp == NULL)
68     status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
69   else
70     {
71       /* Read the file line by line and try to find the description
72          GROUP.  We must take care for long lines.  */
73       char *line = NULL;
74       size_t line_len = 0;
75       const ssize_t group_len = strlen (group);
76
77       status = NSS_STATUS_NOTFOUND;
78       result->cursor = result->data;
79
80       __fsetlocking (fp, FSETLOCKING_BYCALLER);
81
82       while (!feof_unlocked (fp))
83         {
84           ssize_t curlen = getline (&line, &line_len, fp);
85           int found;
86
87           if (curlen < 0)
88             {
89               status = NSS_STATUS_NOTFOUND;
90               break;
91             }
92
93           found = (curlen > group_len && strncmp (line, group, group_len) == 0
94                    && isspace (line[group_len]));
95
96           /* Read the whole line (including continuation) and store it
97              if FOUND in nonzero.  Otherwise we don't need it.  */
98           if (found)
99             {
100               /* Store the data from the first line.  */
101               EXPAND (curlen - group_len);
102               memcpy (result->cursor, &line[group_len + 1],
103                       curlen - group_len);
104               result->cursor += (curlen - group_len) - 1;
105             }
106
107           while (line[curlen - 1] == '\n' && line[curlen - 2] == '\\')
108             {
109               /* Yes, we have a continuation line.  */
110               if (found)
111                 /* Remove these characters from the stored line.  */
112                 result->cursor -= 2;
113
114               /* Get next line.  */
115               curlen = getline (&line, &line_len, fp);
116               if (curlen <= 0)
117                 break;
118
119               if (found)
120                 {
121                   /* Make sure we have enough room.  */
122                   EXPAND (1 + curlen + 1);
123
124                   /* Add separator in case next line starts immediately.  */
125                   *result->cursor++ = ' ';
126
127                   /* Copy new line.  */
128                   memcpy (result->cursor, line, curlen + 1);
129                   result->cursor += curlen;
130                 }
131             }
132
133           if (found)
134             {
135               /* Now we have read the line.  */
136               status = NSS_STATUS_SUCCESS;
137               result->cursor = result->data;
138               result->first = 1;
139               break;
140             }
141         }
142
143     the_end:
144       /* We don't need the file and the line buffer anymore.  */
145       free (line);
146       fclose (fp);
147
148       if (status != NSS_STATUS_SUCCESS)
149         _nss_files_endnetgrent (result);
150     }
151
152   return status;
153 }
154
155
156 int
157 _nss_files_endnetgrent (struct __netgrent *result)
158 {
159   /* Free allocated memory for data if some is present.  */
160   free (result->data);
161   result->data = NULL;
162   result->data_size = 0;
163   result->cursor = NULL;
164   return NSS_STATUS_SUCCESS;
165 }
166 libnss_files_hidden_def (_nss_files_endnetgrent)
167
168 static char *
169 strip_whitespace (char *str)
170 {
171   char *cp = str;
172
173   /* Skip leading spaces.  */
174   while (isspace (*cp))
175     cp++;
176
177   str = cp;
178   while (*cp != '\0' && ! isspace(*cp))
179     cp++;
180
181   /* Null-terminate, stripping off any trailing spaces.  */
182   *cp = '\0';
183
184   return *str == '\0' ? NULL : str;
185 }
186
187 enum nss_status
188 _nss_netgroup_parseline (char **cursor, struct __netgrent *result,
189                          char *buffer, size_t buflen, int *errnop)
190 {
191   enum nss_status status;
192   const char *host, *user, *domain;
193   char *cp = *cursor;
194
195   /* Some sanity checks.  */
196   if (cp == NULL)
197     return NSS_STATUS_NOTFOUND;
198
199   /* First skip leading spaces.  */
200   while (isspace (*cp))
201     ++cp;
202
203   if (*cp != '(')
204     {
205       /* We have a list of other netgroups.  */
206       char *name = cp;
207
208       while (*cp != '\0' && ! isspace (*cp))
209         ++cp;
210
211       if (name != cp)
212         {
213           /* It is another netgroup name.  */
214           int last = *cp == '\0';
215
216           result->type = group_val;
217           result->val.group = name;
218           *cp = '\0';
219           if (! last)
220             ++cp;
221           *cursor = cp;
222           result->first = 0;
223
224           return NSS_STATUS_SUCCESS;
225         }
226
227       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
228     }
229
230   /* Match host name.  */
231   host = ++cp;
232   while (*cp != ',')
233     if (*cp++ == '\0')
234       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
235
236   /* Match user name.  */
237   user = ++cp;
238   while (*cp != ',')
239     if (*cp++ == '\0')
240       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
241
242   /* Match domain name.  */
243   domain = ++cp;
244   while (*cp != ')')
245     if (*cp++ == '\0')
246       return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
247   ++cp;
248
249
250   /* When we got here we have found an entry.  Before we can copy it
251      to the private buffer we have to make sure it is big enough.  */
252   if (cp - host > buflen)
253     {
254       *errnop = ERANGE;
255       status = NSS_STATUS_UNAVAIL;
256     }
257   else
258     {
259       memcpy (buffer, host, cp - host);
260       result->type = triple_val;
261
262       buffer[(user - host) - 1] = '\0'; /* Replace ',' with '\0'.  */
263       result->val.triple.host = strip_whitespace (buffer);
264
265       buffer[(domain - host) - 1] = '\0'; /* Replace ',' with '\0'.  */
266       result->val.triple.user = strip_whitespace (buffer + (user - host));
267
268       buffer[(cp - host) - 1] = '\0'; /* Replace ')' with '\0'.  */
269       result->val.triple.domain = strip_whitespace (buffer + (domain - host));
270
271       status = NSS_STATUS_SUCCESS;
272
273       /* Remember where we stopped reading.  */
274       *cursor = cp;
275
276       result->first = 0;
277     }
278
279   return status;
280 }
281 libnss_files_hidden_def (_nss_netgroup_parseline)
282
283
284 enum nss_status
285 _nss_files_getnetgrent_r (struct __netgrent *result, char *buffer,
286                           size_t buflen, int *errnop)
287 {
288   enum nss_status status;
289
290   status = _nss_netgroup_parseline (&result->cursor, result, buffer, buflen,
291                                     errnop);
292
293   return status;
294 }