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