1/* Common code for file-based databases in nss_files module.
2 Copyright (C) 1996-2018 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 }
82 else
83 rewind (*stream);
84
85 return status;
86}
87
88
89/* Thread-safe, exported version of that. */
90enum nss_status
91CONCAT(_nss_files_set,ENTNAME) (int stayopen)
92{
93 enum nss_status status;
94
95 __libc_lock_lock (lock);
96
97 status = internal_setent (&stream);
98
99 __libc_lock_unlock (lock);
100
101 return status;
102}
103
104
105/* Close the database file. */
106static void
107internal_endent (FILE **stream)
108{
109 if (*stream != NULL)
110 {
111 fclose (*stream);
112 *stream = NULL;
113 }
114}
115
116
117/* Thread-safe, exported version of that. */
118enum nss_status
119CONCAT(_nss_files_end,ENTNAME) (void)
120{
121 __libc_lock_lock (lock);
122
123 internal_endent (&stream);
124
125 __libc_lock_unlock (lock);
126
127 return NSS_STATUS_SUCCESS;
128}
129
130
131typedef enum
132{
133 gcr_ok = 0,
134 gcr_error = -1,
135 gcr_overflow = -2
136} get_contents_ret;
137
138/* Hack around the fact that fgets only accepts int sizes. */
139static get_contents_ret
140get_contents (char *linebuf, size_t len, FILE *stream)
141{
142 size_t remaining_len = len;
143 char *curbuf = linebuf;
144
145 do
146 {
147 int curlen = ((remaining_len > (size_t) INT_MAX) ? INT_MAX
148 : remaining_len);
149
150 /* Terminate the line so that we can test for overflow. */
151 ((unsigned char *) curbuf)[curlen - 1] = 0xff;
152
153 char *p = fgets_unlocked (curbuf, curlen, stream);
154
155 /* EOF or read error. */
156 if (p == NULL)
157 return gcr_error;
158
159 /* Done reading in the line. */
160 if (((unsigned char *) curbuf)[curlen - 1] == 0xff)
161 return gcr_ok;
162
163 /* Drop the terminating '\0'. */
164 remaining_len -= curlen - 1;
165 curbuf += curlen - 1;
166 }
167 /* fgets copies one less than the input length. Our last iteration is of
168 REMAINING_LEN and once that is done, REMAINING_LEN is decremented by
169 REMAINING_LEN - 1, leaving the result as 1. */
170 while (remaining_len > 1);
171
172 /* This means that the current buffer was not large enough. */
173 return gcr_overflow;
174}
175
176/* Parsing the database file into `struct STRUCTURE' data structures. */
177static enum nss_status
178internal_getent (FILE *stream, struct STRUCTURE *result,
179 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
180 EXTRA_ARGS_DECL)
181{
182 char *p;
183 struct parser_data *data = (void *) buffer;
184 size_t linebuflen = buffer + buflen - data->linebuffer;
185 int parse_result;
186
187 if (buflen < sizeof *data + 2)
188 {
189 *errnop = ERANGE;
190 H_ERRNO_SET (NETDB_INTERNAL);
191 return NSS_STATUS_TRYAGAIN;
192 }
193
194 do
195 {
196 get_contents_ret r = get_contents (data->linebuffer, linebuflen, stream);
197
198 if (r == gcr_error)
199 {
200 /* End of file or read error. */
201 H_ERRNO_SET (HOST_NOT_FOUND);
202 return NSS_STATUS_NOTFOUND;
203 }
204
205 if (r == gcr_overflow)
206 {
207 /* The line is too long. Give the user the opportunity to
208 enlarge the buffer. */
209 *errnop = ERANGE;
210 H_ERRNO_SET (NETDB_INTERNAL);
211 return NSS_STATUS_TRYAGAIN;
212 }
213
214 /* Everything OK. Now skip leading blanks. */
215 p = data->linebuffer;
216 while (isspace (*p))
217 ++p;
218 }
219 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
220 /* Parse the line. If it is invalid, loop to get the next
221 line of the file to parse. */
222 || ! (parse_result = parse_line (p, result, data, buflen, errnop
223 EXTRA_ARGS)));
224
225 if (__glibc_unlikely (parse_result == -1))
226 {
227 H_ERRNO_SET (NETDB_INTERNAL);
228 return NSS_STATUS_TRYAGAIN;
229 }
230
231 /* Filled in RESULT with the next entry from the database file. */
232 return NSS_STATUS_SUCCESS;
233}
234
235
236/* Return the next entry from the database file, doing locking. */
237enum nss_status
238CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
239 size_t buflen, int *errnop H_ERRNO_PROTO)
240{
241 /* Return next entry in host file. */
242 enum nss_status status = NSS_STATUS_SUCCESS;
243
244 __libc_lock_lock (lock);
245
246 /* Be prepared that the set*ent function was not called before. */
247 if (stream == NULL)
248 {
249 int save_errno = errno;
250
251 status = internal_setent (&stream);
252
253 __set_errno (save_errno);
254 }
255
256 if (status == NSS_STATUS_SUCCESS)
257 status = internal_getent (stream, result, buffer, buflen, errnop
258 H_ERRNO_ARG EXTRA_ARGS_VALUE);
259
260 __libc_lock_unlock (lock);
261
262 return status;
263}
264
265/* Macro for defining lookup functions for this file-based database.
266
267 NAME is the name of the lookup; e.g. `hostbyname'.
268
269 DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
270 e.g. `1 + sizeof (id) * 4'.
271
272 PROTO is the potentially empty list of other parameters.
273
274 BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
275 to the lookup key arguments and does `break;' if they match. */
276
277#define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
278enum nss_status \
279_nss_files_get##name##_r (proto, \
280 struct STRUCTURE *result, char *buffer, \
281 size_t buflen, int *errnop H_ERRNO_PROTO) \
282{ \
283 enum nss_status status; \
284 FILE *stream = NULL; \
285 \
286 /* Open file. */ \
287 status = internal_setent (&stream); \
288 \
289 if (status == NSS_STATUS_SUCCESS) \
290 { \
291 while ((status = internal_getent (stream, result, buffer, buflen, errnop \
292 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
293 == NSS_STATUS_SUCCESS) \
294 { break_if_match } \
295 \
296 internal_endent (&stream); \
297 } \
298 \
299 return status; \
300}
301