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