1/* Common code for file-based databases in nss_files module.
2 Copyright (C) 1996-2017 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 <http://www.gnu.org/licenses/>. */
18
19#include <stdio.h>
20#include <ctype.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <libc-lock.h>
24#include "nsswitch.h"
25
26#include <kernel-features.h>
27
28/* These symbols are defined by the including source file:
29
30 ENTNAME -- database name of the structure and functions (hostent, pwent).
31 STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
32 DATABASE -- string of the database file's name ("hosts", "passwd").
33
34 NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
35
36 Also see files-parse.c.
37*/
38
39#define ENTNAME_r CONCAT(ENTNAME,_r)
40
41#define DATAFILE "/etc/" DATABASE
42
43#ifdef NEED_H_ERRNO
44# include <netdb.h>
45# define H_ERRNO_PROTO , int *herrnop
46# define H_ERRNO_ARG , herrnop
47# define H_ERRNO_SET(val) (*herrnop = (val))
48#else
49# define H_ERRNO_PROTO
50# define H_ERRNO_ARG
51# define H_ERRNO_SET(val) ((void) 0)
52#endif
53
54#ifndef EXTRA_ARGS
55# define EXTRA_ARGS
56# define EXTRA_ARGS_DECL
57# define EXTRA_ARGS_VALUE
58#endif
59
60/* Locks the static variables in this file. */
61__libc_lock_define_initialized (static, lock)
62
63/* Maintenance of the stream open on the database file. For getXXent
64 operations the stream needs to be held open across calls, the other
65 getXXbyYY operations all use their own stream. */
66
67static FILE *stream;
68
69/* Open database file if not already opened. */
70static enum nss_status
71internal_setent (FILE **stream)
72{
73 enum nss_status status = NSS_STATUS_SUCCESS;
74
75 if (*stream == NULL)
76 {
77 *stream = fopen (DATAFILE, "rce");
78
79 if (*stream == NULL)
80 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
81 else
82 {
83#if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
84# ifdef O_CLOEXEC
85 if (__have_o_cloexec <= 0)
86# endif
87 {
88 /* We have to make sure the file is `closed on exec'. */
89 int result;
90 int flags;
91
92 result = flags = fcntl (fileno (*stream), F_GETFD, 0);
93 if (result >= 0)
94 {
95# ifdef O_CLOEXEC
96 if (__have_o_cloexec == 0)
97 __have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
98 if (__have_o_cloexec < 0)
99# endif
100 {
101 flags |= FD_CLOEXEC;
102 result = fcntl (fileno (*stream), F_SETFD, flags);
103 }
104 }
105 if (result < 0)
106 {
107 /* Something went wrong. Close the stream and return a
108 failure. */
109 fclose (*stream);
110 *stream = NULL;
111 status = NSS_STATUS_UNAVAIL;
112 }
113 }
114#endif
115 }
116 }
117 else
118 rewind (*stream);
119
120 return status;
121}
122
123
124/* Thread-safe, exported version of that. */
125enum nss_status
126CONCAT(_nss_files_set,ENTNAME) (int stayopen)
127{
128 enum nss_status status;
129
130 __libc_lock_lock (lock);
131
132 status = internal_setent (&stream);
133
134 __libc_lock_unlock (lock);
135
136 return status;
137}
138
139
140/* Close the database file. */
141static void
142internal_endent (FILE **stream)
143{
144 if (*stream != NULL)
145 {
146 fclose (*stream);
147 *stream = NULL;
148 }
149}
150
151
152/* Thread-safe, exported version of that. */
153enum nss_status
154CONCAT(_nss_files_end,ENTNAME) (void)
155{
156 __libc_lock_lock (lock);
157
158 internal_endent (&stream);
159
160 __libc_lock_unlock (lock);
161
162 return NSS_STATUS_SUCCESS;
163}
164
165
166typedef enum
167{
168 gcr_ok = 0,
169 gcr_error = -1,
170 gcr_overflow = -2
171} get_contents_ret;
172
173/* Hack around the fact that fgets only accepts int sizes. */
174static get_contents_ret
175get_contents (char *linebuf, size_t len, FILE *stream)
176{
177 size_t remaining_len = len;
178 char *curbuf = linebuf;
179
180 do
181 {
182 int curlen = ((remaining_len > (size_t) INT_MAX) ? INT_MAX
183 : remaining_len);
184
185 /* Terminate the line so that we can test for overflow. */
186 ((unsigned char *) curbuf)[curlen - 1] = 0xff;
187
188 char *p = fgets_unlocked (curbuf, curlen, stream);
189
190 /* EOF or read error. */
191 if (p == NULL)
192 return gcr_error;
193
194 /* Done reading in the line. */
195 if (((unsigned char *) curbuf)[curlen - 1] == 0xff)
196 return gcr_ok;
197
198 /* Drop the terminating '\0'. */
199 remaining_len -= curlen - 1;
200 curbuf += curlen - 1;
201 }
202 /* fgets copies one less than the input length. Our last iteration is of
203 REMAINING_LEN and once that is done, REMAINING_LEN is decremented by
204 REMAINING_LEN - 1, leaving the result as 1. */
205 while (remaining_len > 1);
206
207 /* This means that the current buffer was not large enough. */
208 return gcr_overflow;
209}
210
211/* Parsing the database file into `struct STRUCTURE' data structures. */
212static enum nss_status
213internal_getent (FILE *stream, struct STRUCTURE *result,
214 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
215 EXTRA_ARGS_DECL)
216{
217 char *p;
218 struct parser_data *data = (void *) buffer;
219 size_t linebuflen = buffer + buflen - data->linebuffer;
220 int parse_result;
221
222 if (buflen < sizeof *data + 2)
223 {
224 *errnop = ERANGE;
225 H_ERRNO_SET (NETDB_INTERNAL);
226 return NSS_STATUS_TRYAGAIN;
227 }
228
229 do
230 {
231 get_contents_ret r = get_contents (data->linebuffer, linebuflen, stream);
232
233 if (r == gcr_error)
234 {
235 /* End of file or read error. */
236 H_ERRNO_SET (HOST_NOT_FOUND);
237 return NSS_STATUS_NOTFOUND;
238 }
239
240 if (r == gcr_overflow)
241 {
242 /* The line is too long. Give the user the opportunity to
243 enlarge the buffer. */
244 *errnop = ERANGE;
245 H_ERRNO_SET (NETDB_INTERNAL);
246 return NSS_STATUS_TRYAGAIN;
247 }
248
249 /* Everything OK. Now skip leading blanks. */
250 p = data->linebuffer;
251 while (isspace (*p))
252 ++p;
253 }
254 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
255 /* Parse the line. If it is invalid, loop to get the next
256 line of the file to parse. */
257 || ! (parse_result = parse_line (p, result, data, buflen, errnop
258 EXTRA_ARGS)));
259
260 if (__glibc_unlikely (parse_result == -1))
261 {
262 H_ERRNO_SET (NETDB_INTERNAL);
263 return NSS_STATUS_TRYAGAIN;
264 }
265
266 /* Filled in RESULT with the next entry from the database file. */
267 return NSS_STATUS_SUCCESS;
268}
269
270
271/* Return the next entry from the database file, doing locking. */
272enum nss_status
273CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
274 size_t buflen, int *errnop H_ERRNO_PROTO)
275{
276 /* Return next entry in host file. */
277 enum nss_status status = NSS_STATUS_SUCCESS;
278
279 __libc_lock_lock (lock);
280
281 /* Be prepared that the set*ent function was not called before. */
282 if (stream == NULL)
283 {
284 int save_errno = errno;
285
286 status = internal_setent (&stream);
287
288 __set_errno (save_errno);
289 }
290
291 if (status == NSS_STATUS_SUCCESS)
292 status = internal_getent (stream, result, buffer, buflen, errnop
293 H_ERRNO_ARG EXTRA_ARGS_VALUE);
294
295 __libc_lock_unlock (lock);
296
297 return status;
298}
299
300/* Macro for defining lookup functions for this file-based database.
301
302 NAME is the name of the lookup; e.g. `hostbyname'.
303
304 DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
305 e.g. `1 + sizeof (id) * 4'.
306
307 PROTO is the potentially empty list of other parameters.
308
309 BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
310 to the lookup key arguments and does `break;' if they match. */
311
312#define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
313enum nss_status \
314_nss_files_get##name##_r (proto, \
315 struct STRUCTURE *result, char *buffer, \
316 size_t buflen, int *errnop H_ERRNO_PROTO) \
317{ \
318 enum nss_status status; \
319 FILE *stream = NULL; \
320 \
321 /* Open file. */ \
322 status = internal_setent (&stream); \
323 \
324 if (status == NSS_STATUS_SUCCESS) \
325 { \
326 while ((status = internal_getent (stream, result, buffer, buflen, errnop \
327 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
328 == NSS_STATUS_SUCCESS) \
329 { break_if_match } \
330 \
331 internal_endent (&stream); \
332 } \
333 \
334 return status; \
335}
336