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