1/* Mail alias file parser in nss_files module.
2 Copyright (C) 1996-2021 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, see
18 <https://www.gnu.org/licenses/>. */
19
20#include <aliases.h>
21#include <ctype.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <libc-lock.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28
29#include <kernel-features.h>
30
31#include "nsswitch.h"
32#include <nss_files.h>
33
34
35/* Maintenance of the stream open on the database file. For getXXent
36 operations the stream needs to be held open across calls, the other
37 getXXbyYY operations all use their own stream. */
38
39static enum nss_status
40internal_setent (FILE **stream)
41{
42 enum nss_status status = NSS_STATUS_SUCCESS;
43
44 if (*stream == NULL)
45 {
46 *stream = __nss_files_fopen ("/etc/aliases");
47
48 if (*stream == NULL)
49 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
50 }
51 else
52 rewind (*stream);
53
54 return status;
55}
56
57
58/* Thread-safe, exported version of that. */
59enum nss_status
60_nss_files_setaliasent (void)
61{
62 return __nss_files_data_setent (nss_file_aliasent, "/etc/aliases");
63}
64libc_hidden_def (_nss_files_setaliasent)
65
66enum nss_status
67_nss_files_endaliasent (void)
68{
69 return __nss_files_data_endent (nss_file_aliasent);
70}
71libc_hidden_def (_nss_files_endaliasent)
72
73/* Parsing the database file into `struct aliasent' data structures. */
74static enum nss_status
75get_next_alias (FILE *stream, const char *match, struct aliasent *result,
76 char *buffer, size_t buflen, int *errnop)
77{
78 enum nss_status status = NSS_STATUS_NOTFOUND;
79 int ignore = 0;
80
81 result->alias_members_len = 0;
82
83 while (1)
84 {
85 /* Now we are ready to process the input. We have to read a
86 line and all its continuations and construct the array of
87 string pointers. This pointers and the names itself have to
88 be placed in BUFFER. */
89 char *first_unused = buffer;
90 size_t room_left = buflen - (buflen % __alignof__ (char *));
91 char *line;
92
93 /* Check whether the buffer is large enough for even trying to
94 read something. */
95 if (room_left < 2)
96 goto no_more_room;
97
98 /* Read the first line. It must contain the alias name and
99 possibly some alias names. */
100 first_unused[room_left - 1] = '\xff';
101 line = __fgets_unlocked (first_unused, room_left, stream);
102 if (line == NULL)
103 /* Nothing to read. */
104 break;
105 else if (first_unused[room_left - 1] != '\xff')
106 {
107 /* The line is too long for our buffer. */
108 no_more_room:
109 *errnop = ERANGE;
110 status = NSS_STATUS_TRYAGAIN;
111 break;
112 }
113 else
114 {
115 char *cp;
116
117 /* If we are in IGNORE mode and the first character in the
118 line is a white space we ignore the line and start
119 reading the next. */
120 if (ignore && isspace (*first_unused))
121 continue;
122
123 /* Terminate the line for any case. */
124 cp = strpbrk (first_unused, "#\n");
125 if (cp != NULL)
126 *cp = '\0';
127
128 /* Skip leading blanks. */
129 while (isspace (*line))
130 ++line;
131
132 result->alias_name = first_unused;
133 while (*line != '\0' && *line != ':')
134 *first_unused++ = *line++;
135 if (*line == '\0' || result->alias_name == first_unused)
136 /* No valid name. Ignore the line. */
137 continue;
138
139 *first_unused++ = '\0';
140 if (room_left < (size_t) (first_unused - result->alias_name))
141 goto no_more_room;
142 room_left -= first_unused - result->alias_name;
143 ++line;
144
145 /* When we search for a specific alias we can avoid all the
146 difficult parts and compare now with the name we are
147 looking for. If it does not match we simply ignore all
148 lines until the next line containing the start of a new
149 alias is found. */
150 ignore = (match != NULL
151 && __strcasecmp (result->alias_name, match) != 0);
152
153 while (! ignore)
154 {
155 while (isspace (*line))
156 ++line;
157
158 cp = first_unused;
159 while (*line != '\0' && *line != ',')
160 *first_unused++ = *line++;
161
162 if (first_unused != cp)
163 {
164 /* OK, we can have a regular entry or an include
165 request. */
166 if (*line != '\0')
167 ++line;
168 *first_unused++ = '\0';
169
170 if (strncmp (cp, ":include:", 9) != 0)
171 {
172 if (room_left < (first_unused - cp) + sizeof (char *))
173 goto no_more_room;
174 room_left -= (first_unused - cp) + sizeof (char *);
175
176 ++result->alias_members_len;
177 }
178 else
179 {
180 /* Oh well, we have to read the addressed file. */
181 FILE *listfile;
182 char *old_line = NULL;
183
184 first_unused = cp;
185
186 listfile = __nss_files_fopen (&cp[9]);
187 /* If the file does not exist we simply ignore
188 the statement. */
189 if (listfile != NULL
190 && (old_line = __strdup (line)) != NULL)
191 {
192 while (! __feof_unlocked (listfile))
193 {
194 if (room_left < 2)
195 {
196 free (old_line);
197 fclose (listfile);
198 goto no_more_room;
199 }
200
201 first_unused[room_left - 1] = '\xff';
202 line = __fgets_unlocked (first_unused, room_left,
203 listfile);
204 if (line == NULL)
205 break;
206 if (first_unused[room_left - 1] != '\xff')
207 {
208 free (old_line);
209 fclose (listfile);
210 goto no_more_room;
211 }
212
213 /* Parse the line. */
214 cp = strpbrk (line, "#\n");
215 if (cp != NULL)
216 *cp = '\0';
217
218 do
219 {
220 while (isspace (*line))
221 ++line;
222
223 cp = first_unused;
224 while (*line != '\0' && *line != ',')
225 *first_unused++ = *line++;
226
227 if (*line != '\0')
228 ++line;
229
230 if (first_unused != cp)
231 {
232 *first_unused++ = '\0';
233 if (room_left < ((first_unused - cp)
234 + __alignof__ (char *)))
235 {
236 free (old_line);
237 fclose (listfile);
238 goto no_more_room;
239 }
240 room_left -= ((first_unused - cp)
241 + __alignof__ (char *));
242 ++result->alias_members_len;
243 }
244 }
245 while (*line != '\0');
246 }
247 fclose (listfile);
248
249 first_unused[room_left - 1] = '\0';
250 strncpy (first_unused, old_line, room_left);
251
252 free (old_line);
253 line = first_unused;
254
255 if (first_unused[room_left - 1] != '\0')
256 goto no_more_room;
257 }
258 }
259 }
260
261 if (*line == '\0')
262 {
263 /* Get the next line. But we must be careful. We
264 must not read the whole line at once since it
265 might belong to the current alias. Simply read
266 the first character. If it is a white space we
267 have a continuation line. Otherwise it is the
268 beginning of a new alias and we can push back the
269 just read character. */
270 int ch;
271
272 ch = __getc_unlocked (stream);
273 if (ch == EOF || ch == '\n' || !isspace (ch))
274 {
275 size_t cnt;
276
277 /* Now prepare the return. Provide string
278 pointers for the currently selected aliases. */
279 if (ch != EOF)
280 ungetc (ch, stream);
281
282 /* Adjust the pointer so it is aligned for
283 storing pointers. */
284 first_unused += __alignof__ (char *) - 1;
285 first_unused -= ((first_unused - (char *) 0)
286 % __alignof__ (char *));
287 result->alias_members = (char **) first_unused;
288
289 /* Compute addresses of alias entry strings. */
290 cp = result->alias_name;
291 for (cnt = 0; cnt < result->alias_members_len; ++cnt)
292 {
293 cp = strchr (cp, '\0') + 1;
294 result->alias_members[cnt] = cp;
295 }
296
297 status = (result->alias_members_len == 0
298 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
299 break;
300 }
301
302 /* The just read character is a white space and so
303 can be ignored. */
304 first_unused[room_left - 1] = '\xff';
305 line = __fgets_unlocked (first_unused, room_left, stream);
306 if (line == NULL)
307 {
308 /* Continuation line without any data and
309 without a newline at the end. Treat it as an
310 empty line and retry, reaching EOF once
311 more. */
312 line = first_unused;
313 *line = '\0';
314 continue;
315 }
316 if (first_unused[room_left - 1] != '\xff')
317 goto no_more_room;
318 cp = strpbrk (line, "#\n");
319 if (cp != NULL)
320 *cp = '\0';
321 }
322 }
323 }
324
325 if (status != NSS_STATUS_NOTFOUND)
326 /* We read something. In any case break here. */
327 break;
328 }
329
330 return status;
331}
332
333
334enum nss_status
335_nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
336 int *errnop)
337{
338 /* Return next entry in host file. */
339
340 struct nss_files_per_file_data *data;
341 enum nss_status status = __nss_files_data_open (&data, nss_file_aliasent,
342 "/etc/aliases", errnop, NULL);
343 if (status != NSS_STATUS_SUCCESS)
344 return status;
345
346 result->alias_local = 1;
347
348 /* Read lines until we get a definite result. */
349 do
350 status = get_next_alias (data->stream, NULL, result, buffer, buflen,
351 errnop);
352 while (status == NSS_STATUS_RETURN);
353
354 __nss_files_data_put (data);
355 return status;
356}
357libc_hidden_def (_nss_files_getaliasent_r)
358
359enum nss_status
360_nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
361 char *buffer, size_t buflen, int *errnop)
362{
363 /* Return next entry in host file. */
364 enum nss_status status = NSS_STATUS_SUCCESS;
365 FILE *stream = NULL;
366
367 if (name == NULL)
368 {
369 __set_errno (EINVAL);
370 return NSS_STATUS_UNAVAIL;
371 }
372
373 /* Open the stream. */
374 status = internal_setent (&stream);
375
376 if (status == NSS_STATUS_SUCCESS)
377 {
378 result->alias_local = 1;
379
380 /* Read lines until we get a definite result. */
381 do
382 status = get_next_alias (stream, name, result, buffer, buflen, errnop);
383 while (status == NSS_STATUS_RETURN);
384
385 fclose (stream);
386 }
387
388 return status;
389}
390libc_hidden_def (_nss_files_getaliasbyname_r)
391