elf: Fix use-after-free in ldconfig [BZ #26779]
[glibc.git] / nss / nss_files / files-netgrp.c
blob9ef50d4555008aa1c601fe3cb2bef97f94424865
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.
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.
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.
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/>. */
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>
30 #define DATAFILE "/etc/netgroup"
32 libc_hidden_proto (_nss_files_endnetgrent)
34 #define EXPAND(needed) \
35 do \
36 { \
37 size_t old_cursor = result->cursor - result->data; \
38 void *old_data = result->data; \
40 result->data_size += 512 > 2 * needed ? 512 : 2 * needed; \
41 result->data = realloc (result->data, result->data_size); \
43 if (result->data == NULL) \
44 { \
45 free (old_data); \
46 status = NSS_STATUS_UNAVAIL; \
47 goto the_end; \
48 } \
50 result->cursor = result->data + old_cursor; \
51 } \
52 while (0)
55 enum nss_status
56 _nss_files_setnetgrent (const char *group, struct __netgrent *result)
58 FILE *fp;
59 enum nss_status status;
61 if (group[0] == '\0')
62 return NSS_STATUS_UNAVAIL;
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
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);
76 status = NSS_STATUS_NOTFOUND;
77 result->cursor = result->data;
79 while (!__feof_unlocked (fp))
81 ssize_t curlen = __getline (&line, &line_len, fp);
82 int found;
84 if (curlen < 0)
86 status = NSS_STATUS_NOTFOUND;
87 break;
90 found = (curlen > group_len && strncmp (line, group, group_len) == 0
91 && isspace (line[group_len]));
93 /* Read the whole line (including continuation) and store it
94 if FOUND in nonzero. Otherwise we don't need it. */
95 if (found)
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;
104 while (curlen > 1 && line[curlen - 1] == '\n'
105 && line[curlen - 2] == '\\')
107 /* Yes, we have a continuation line. */
108 if (found)
109 /* Remove these characters from the stored line. */
110 result->cursor -= 2;
112 /* Get next line. */
113 curlen = __getline (&line, &line_len, fp);
114 if (curlen <= 0)
115 break;
117 if (found)
119 /* Make sure we have enough room. */
120 EXPAND (1 + curlen + 1);
122 /* Add separator in case next line starts immediately. */
123 *result->cursor++ = ' ';
125 /* Copy new line. */
126 memcpy (result->cursor, line, curlen + 1);
127 result->cursor += curlen;
131 if (found)
133 /* Now we have read the line. */
134 status = NSS_STATUS_SUCCESS;
135 result->cursor = result->data;
136 result->first = 1;
137 break;
141 the_end:
142 /* We don't need the file and the line buffer anymore. */
143 free (line);
144 fclose (fp);
146 if (status != NSS_STATUS_SUCCESS)
147 _nss_files_endnetgrent (result);
150 return status;
152 libc_hidden_def (_nss_files_setnetgrent)
154 enum nss_status
155 _nss_files_endnetgrent (struct __netgrent *result)
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;
164 libc_hidden_def (_nss_files_endnetgrent)
166 static char *
167 strip_whitespace (char *str)
169 char *cp = str;
171 /* Skip leading spaces. */
172 while (isspace (*cp))
173 cp++;
175 str = cp;
176 while (*cp != '\0' && ! isspace(*cp))
177 cp++;
179 /* Null-terminate, stripping off any trailing spaces. */
180 *cp = '\0';
182 return *str == '\0' ? NULL : str;
185 enum nss_status
186 _nss_netgroup_parseline (char **cursor, struct __netgrent *result,
187 char *buffer, size_t buflen, int *errnop)
189 enum nss_status status;
190 const char *host, *user, *domain;
191 char *cp = *cursor;
193 /* Some sanity checks. */
194 if (cp == NULL)
195 return NSS_STATUS_NOTFOUND;
197 /* First skip leading spaces. */
198 while (isspace (*cp))
199 ++cp;
201 if (*cp != '(')
203 /* We have a list of other netgroups. */
204 char *name = cp;
206 while (*cp != '\0' && ! isspace (*cp))
207 ++cp;
209 if (name != cp)
211 /* It is another netgroup name. */
212 int last = *cp == '\0';
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;
222 return NSS_STATUS_SUCCESS;
225 return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
228 /* Match host name. */
229 host = ++cp;
230 while (*cp != ',')
231 if (*cp++ == '\0')
232 return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
234 /* Match user name. */
235 user = ++cp;
236 while (*cp != ',')
237 if (*cp++ == '\0')
238 return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
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;
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)
252 *errnop = ERANGE;
253 status = NSS_STATUS_TRYAGAIN;
255 else
257 memcpy (buffer, host, cp - host);
258 result->type = triple_val;
260 buffer[(user - host) - 1] = '\0'; /* Replace ',' with '\0'. */
261 result->val.triple.host = strip_whitespace (buffer);
263 buffer[(domain - host) - 1] = '\0'; /* Replace ',' with '\0'. */
264 result->val.triple.user = strip_whitespace (buffer + (user - host));
266 buffer[(cp - host) - 1] = '\0'; /* Replace ')' with '\0'. */
267 result->val.triple.domain = strip_whitespace (buffer + (domain - host));
269 status = NSS_STATUS_SUCCESS;
271 /* Remember where we stopped reading. */
272 *cursor = cp;
274 result->first = 0;
277 return status;
279 libc_hidden_def (_nss_netgroup_parseline)
282 enum nss_status
283 _nss_files_getnetgrent_r (struct __netgrent *result, char *buffer,
284 size_t buflen, int *errnop)
286 enum nss_status status;
288 status = _nss_netgroup_parseline (&result->cursor, result, buffer, buflen,
289 errnop);
291 return status;
293 libc_hidden_def (_nss_files_getnetgrent_r)