1 /* Common code for file-based databases in nss_files module.
2    Copyright (C) 1996-2022 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    <https://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 #include <nss_files.h>
26 
27 #include <kernel-features.h>
28 
29 /* These symbols are defined by the including source file:
30 
31    ENTNAME -- database name of the structure and functions (hostent, pwent).
32    STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
33    DATABASE -- string of the database file's name ("hosts", "passwd").
34 
35    NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
36 
37    Also see files-parse.c.
38 */
39 
40 #define ENTNAME_r	CONCAT(ENTNAME,_r)
41 
42 #define DATAFILE	"/etc/" DATABASE
43 
44 #ifdef NEED_H_ERRNO
45 # include <netdb.h>
46 # define H_ERRNO_PROTO	, int *herrnop
47 # define H_ERRNO_ARG	, herrnop
48 # define H_ERRNO_ARG_OR_NULL herrnop
49 # define H_ERRNO_SET(val) (*herrnop = (val))
50 #else
51 # define H_ERRNO_PROTO
52 # define H_ERRNO_ARG
53 # define H_ERRNO_ARG_OR_NULL NULL
54 # define H_ERRNO_SET(val) ((void) 0)
55 #endif
56 
57 #ifndef EXTRA_ARGS
58 # define EXTRA_ARGS
59 # define EXTRA_ARGS_DECL
60 # define EXTRA_ARGS_VALUE
61 #endif
62 
63 
64 /* Maintenance of the stream open on the database file.  For getXXent
65    operations the stream needs to be held open across calls, the other
66    getXXbyYY operations all use their own stream.  */
67 
68 /* Open database file if not already opened.  */
69 static enum nss_status
internal_setent(FILE ** stream)70 internal_setent (FILE **stream)
71 {
72   enum nss_status status = NSS_STATUS_SUCCESS;
73 
74   if (*stream == NULL)
75     {
76       *stream = __nss_files_fopen (DATAFILE);
77 
78       if (*stream == NULL)
79 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
80     }
81   else
82     rewind (*stream);
83 
84   return status;
85 }
86 
87 
88 /* Thread-safe, exported version of that.  */
89 enum nss_status
CONCAT(_nss_files_set,ENTNAME)90 CONCAT(_nss_files_set,ENTNAME) (int stayopen)
91 {
92   return __nss_files_data_setent (CONCAT (nss_file_, ENTNAME), DATAFILE);
93 }
libc_hidden_def(CONCAT (_nss_files_set,ENTNAME))94 libc_hidden_def (CONCAT (_nss_files_set,ENTNAME))
95 
96 enum nss_status
97 CONCAT(_nss_files_end,ENTNAME) (void)
98 {
99   return __nss_files_data_endent (CONCAT (nss_file_, ENTNAME));
100 }
libc_hidden_def(CONCAT (_nss_files_end,ENTNAME))101 libc_hidden_def (CONCAT (_nss_files_end,ENTNAME))
102 
103 
104 /* Parsing the database file into `struct STRUCTURE' data structures.  */
105 static enum nss_status
106 internal_getent (FILE *stream, struct STRUCTURE *result,
107 		 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
108 		 EXTRA_ARGS_DECL)
109 {
110   struct parser_data *data = (void *) buffer;
111   size_t linebuflen = buffer + buflen - data->linebuffer;
112   int saved_errno = errno;	/* Do not clobber errno on success.  */
113 
114   if (buflen < sizeof *data + 2)
115     {
116       *errnop = ERANGE;
117       H_ERRNO_SET (NETDB_INTERNAL);
118       return NSS_STATUS_TRYAGAIN;
119     }
120 
121   while (true)
122     {
123       off64_t original_offset;
124       int ret = __nss_readline (stream, data->linebuffer, linebuflen,
125 				&original_offset);
126       if (ret == ENOENT)
127 	{
128 	  /* End of file.  */
129 	  H_ERRNO_SET (HOST_NOT_FOUND);
130 	  __set_errno (saved_errno);
131 	  return NSS_STATUS_NOTFOUND;
132 	}
133       else if (ret == 0)
134 	{
135 	  ret = __nss_parse_line_result (stream, original_offset,
136 					 parse_line (data->linebuffer,
137 						     result, data, buflen,
138 						     errnop EXTRA_ARGS));
139 	  if (ret == 0)
140 	    {
141 	      /* Line has been parsed successfully.  */
142 	      __set_errno (saved_errno);
143 	      return NSS_STATUS_SUCCESS;
144 	    }
145 	  else if (ret == EINVAL)
146 	    /* If it is invalid, loop to get the next line of the file
147 	       to parse.  */
148 	    continue;
149 	}
150 
151       *errnop = ret;
152       H_ERRNO_SET (NETDB_INTERNAL);
153       if (ret == ERANGE)
154 	/* Request larger buffer.  */
155 	return NSS_STATUS_TRYAGAIN;
156       else
157 	/* Other read failure.  */
158 	return NSS_STATUS_UNAVAIL;
159     }
160 }
161 
162 
163 /* Return the next entry from the database file, doing locking.  */
164 enum nss_status
CONCAT(_nss_files_get,ENTNAME_r)165 CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
166 				  size_t buflen, int *errnop H_ERRNO_PROTO)
167 {
168   /* Return next entry in host file.  */
169 
170   struct nss_files_per_file_data *data;
171   enum nss_status status = __nss_files_data_open (&data,
172 						  CONCAT (nss_file_, ENTNAME),
173 						  DATAFILE,
174 						  errnop, H_ERRNO_ARG_OR_NULL);
175   if (status != NSS_STATUS_SUCCESS)
176     return status;
177 
178   status = internal_getent (data->stream, result, buffer, buflen, errnop
179 			    H_ERRNO_ARG EXTRA_ARGS_VALUE);
180 
181   __nss_files_data_put (data);
182   return status;
183 }
184 libc_hidden_def (CONCAT (_nss_files_get,ENTNAME_r))
185 
186 /* Macro for defining lookup functions for this file-based database.
187 
188    NAME is the name of the lookup; e.g. `hostbyname'.
189 
190    DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
191    e.g. `1 + sizeof (id) * 4'.
192 
193    PROTO is the potentially empty list of other parameters.
194 
195    BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
196    to the lookup key arguments and does `break;' if they match.  */
197 
198 #define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
199 enum nss_status								      \
200 _nss_files_get##name##_r (proto,					      \
201 			  struct STRUCTURE *result, char *buffer,	      \
202 			  size_t buflen, int *errnop H_ERRNO_PROTO)	      \
203 {									      \
204   enum nss_status status;						      \
205   FILE *stream = NULL;							      \
206 									      \
207   /* Open file.  */							      \
208   status = internal_setent (&stream);					      \
209 									      \
210   if (status == NSS_STATUS_SUCCESS)					      \
211     {									      \
212       while ((status = internal_getent (stream, result, buffer, buflen, errnop \
213 					H_ERRNO_ARG EXTRA_ARGS_VALUE))	      \
214 	     == NSS_STATUS_SUCCESS)					      \
215 	{ break_if_match }						      \
216 									      \
217       fclose (stream);							      \
218     }									      \
219 									      \
220   return status;							      \
221 }									      \
222 libc_hidden_def (_nss_files_get##name##_r)
223