1 /* Common code for DB-based databases in nss_db 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 <dlfcn.h>
20 #include <fcntl.h>
21 #include <stdint.h>
22 #include <sys/mman.h>
23 #include <libc-lock.h>
24 #include "nsswitch.h"
25 #include "nss_db.h"
26 
27 /* The hashing function we use.  */
28 #include "../intl/hash-string.h"
29 
30 /* These symbols are defined by the including source file:
31 
32    ENTNAME -- database name of the structure and functions (hostent, pwent).
33    STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
34    DATABASE -- database file name, ("hosts", "passwd")
35 
36    NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
37 */
38 
39 #define ENTNAME_r	CONCAT(ENTNAME,_r)
40 
41 #include <paths.h>
42 #define	DBFILE		_PATH_VARDB DATABASE ".db"
43 
44 #ifdef NEED_H_ERRNO
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 /* State for this database.  */
55 static struct nss_db_map state;
56 /* Lock to protect the state and global variables.  */
57 __libc_lock_define (static , lock);
58 
59 /* Maintenance of the shared handle open on the database.  */
60 static int keep_db;
61 static const char *entidx;
62 
63 
64 /* Open the database.  */
65 enum nss_status
CONCAT(_nss_db_set,ENTNAME)66 CONCAT(_nss_db_set,ENTNAME) (int stayopen)
67 {
68   enum nss_status status;
69 
70   __libc_lock_lock (lock);
71 
72   status = internal_setent (DBFILE, &state);
73 
74   if (status == NSS_STATUS_SUCCESS)
75     {
76       /* Remember STAYOPEN flag.  */
77       keep_db |= stayopen;
78 
79       /* Reset the sequential index.  */
80       entidx  = NULL;
81     }
82 
83   __libc_lock_unlock (lock);
84 
85   return status;
86 }
87 
88 
89 /* Close it again.  */
90 enum nss_status
CONCAT(_nss_db_end,ENTNAME)91 CONCAT(_nss_db_end,ENTNAME) (void)
92 {
93   __libc_lock_lock (lock);
94 
95   internal_endent (&state);
96 
97   /* Reset STAYOPEN flag.  */
98   keep_db = 0;
99 
100   __libc_lock_unlock (lock);
101 
102   return NSS_STATUS_SUCCESS;
103 }
104 
105 
106 /* Macro for defining lookup functions for this DB-based database.
107 
108    NAME is the name of the lookup; e.g. `pwnam'.
109 
110    DB_CHAR is index indicator for the database.
111 
112    KEYPATTERN gives `printf' args to construct a key string;
113    e.g. `("%d", id)'.
114 
115    KEYSIZE gives the allocation size of a buffer to construct it in;
116    e.g. `1 + sizeof (id) * 4'.
117 
118    PROTO is the potentially empty list of other parameters.
119 
120    BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
121    to the lookup key arguments and does `break;' if they match.  */
122 
123 #define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
124 enum nss_status								      \
125  _nss_db_get##name##_r (proto, struct STRUCTURE *result,		      \
126 			char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO)\
127 {									      \
128   struct parser_data *data = (void *) buffer;				      \
129 									      \
130   if (buflen < sizeof *data)						      \
131     {									      \
132       *errnop = ERANGE;							      \
133       H_ERRNO_SET (NETDB_INTERNAL);					      \
134       return NSS_STATUS_TRYAGAIN;					      \
135     }									      \
136 									      \
137   struct nss_db_map state = { NULL, 0 };				      \
138   enum nss_status status = internal_setent (DBFILE, &state);		      \
139   if (status != NSS_STATUS_SUCCESS)					      \
140     {									      \
141       *errnop = errno;							      \
142       H_ERRNO_SET (NETDB_INTERNAL);					      \
143       return status;							      \
144     }									      \
145 									      \
146   const struct nss_db_header *header = state.header;			      \
147   int i;								      \
148   for (i = 0; i < header->ndbs; ++i)					      \
149     if (header->dbs[i].id == db_char)					      \
150       break;								      \
151   if (i == header->ndbs)						      \
152     {									      \
153       status = NSS_STATUS_UNAVAIL;					      \
154       goto out;								      \
155     }									      \
156 									      \
157   char *key;								      \
158   if (db_char == '.')							      \
159     key = (char *) IGNOREPATTERN keypattern;				      \
160   else									      \
161     {									      \
162       const size_t size = (keysize) + 1;				      \
163       key = alloca (size);						      \
164 									      \
165       KEYPRINTF keypattern;						      \
166     }									      \
167 									      \
168   const stridx_t *hashtable						      \
169     = (const stridx_t *) ((const char *) header				      \
170 			  + header->dbs[i].hashoffset);			      \
171   const char *valstrtab = (const char *) header + header->valstroffset;	      \
172   uint32_t hashval = __hash_string (key);				      \
173   size_t hidx = hashval % header->dbs[i].hashsize;			      \
174   size_t hval2 = 1 + hashval % (header->dbs[i].hashsize - 2);		      \
175 									      \
176   status = NSS_STATUS_NOTFOUND;						      \
177   while (hashtable[hidx] != ~((stridx_t) 0))				      \
178     {									      \
179       const char *valstr = valstrtab + hashtable[hidx];			      \
180       size_t len = strlen (valstr) + 1;					      \
181       if (len > buflen)							      \
182 	{								      \
183 	  /* No room to copy the data to.  */				      \
184 	  *errnop = ERANGE;						      \
185 	  H_ERRNO_SET (NETDB_INTERNAL);					      \
186 	  status = NSS_STATUS_TRYAGAIN;					      \
187 	  break;							      \
188 	}								      \
189 									      \
190       /* Copy the string to a place where it can be modified.  */	      \
191       char *p = memcpy (buffer, valstr, len);				      \
192 									      \
193       int err = parse_line (p, result, data, buflen, errnop EXTRA_ARGS);      \
194 									      \
195       /* Advance before break_if_match, lest it uses continue to skip
196 	 to the next entry.  */						      \
197       if ((hidx += hval2) >= header->dbs[i].hashsize)			      \
198 	hidx -= header->dbs[i].hashsize;				      \
199 									      \
200       if (err > 0)							      \
201 	{								      \
202 	  status = NSS_STATUS_SUCCESS;					      \
203 	  break_if_match;						      \
204 	  status = NSS_STATUS_NOTFOUND;					      \
205 	}								      \
206       else if (err == -1)						      \
207 	{								      \
208 	  H_ERRNO_SET (NETDB_INTERNAL);					      \
209 	  status = NSS_STATUS_TRYAGAIN;					      \
210 	  break;							      \
211 	}								      \
212     }									      \
213 									      \
214   if (status == NSS_STATUS_NOTFOUND)					      \
215     H_ERRNO_SET (HOST_NOT_FOUND);					      \
216 									      \
217  out:									      \
218   internal_endent (&state);						      \
219 									      \
220   return status;							      \
221 }
222 
223 #define KEYPRINTF(pattern, args...) snprintf (key, size, pattern ,##args)
224 #define IGNOREPATTERN(pattern, arg1, args...) (char *) (uintptr_t) arg1
225 
226 
227 
228 
229 /* Return the next entry from the database file, doing locking.  */
230 enum nss_status
CONCAT(_nss_db_get,ENTNAME_r)231 CONCAT(_nss_db_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
232 			       size_t buflen, int *errnop H_ERRNO_PROTO)
233 {
234   /* Return next entry in host file.  */
235   enum nss_status status;
236   struct parser_data *data = (void *) buffer;
237 
238   if (buflen < sizeof *data)
239     {
240       *errnop = ERANGE;
241       H_ERRNO_SET (NETDB_INTERNAL);
242       return NSS_STATUS_TRYAGAIN;
243     }
244 
245   __libc_lock_lock (lock);
246 
247   if (state.header == NULL)
248     {
249       status = internal_setent (DBFILE, &state);
250       if (status != NSS_STATUS_SUCCESS)
251 	{
252 	  *errnop = errno;
253 	  H_ERRNO_SET (NETDB_INTERNAL);
254 	  goto out;
255 	}
256       entidx = NULL;
257     }
258 
259   /* Start from the beginning if freshly initialized or reset
260      requested by set*ent.  */
261   if (entidx == NULL)
262     entidx = (const char *) state.header + state.header->valstroffset;
263 
264   status = NSS_STATUS_UNAVAIL;
265   if (state.header != MAP_FAILED)
266     {
267       const char *const end = ((const char *) state.header
268 			       + state.header->valstroffset
269 			       + state.header->valstrlen);
270       while (entidx < end)
271 	{
272 	  const char *next = rawmemchr (entidx, '\0') + 1;
273 	  size_t len = next - entidx;
274 
275 	  if (len > buflen)
276 	    {
277 	      /* No room to copy the data to.  */
278 	      *errnop = ERANGE;
279 	      H_ERRNO_SET (NETDB_INTERNAL);
280 	      status = NSS_STATUS_TRYAGAIN;
281 	      break;
282 	    }
283 
284 	  /* Copy the string to a place where it can be modified.  */
285 	  char *p = memcpy (buffer, entidx, len);
286 
287 	  int err = parse_line (p, result, data, buflen, errnop EXTRA_ARGS);
288 
289 	  if (err > 0)
290 	    {
291 	      status = NSS_STATUS_SUCCESS;
292 	      entidx = next;
293 	      break;
294 	    }
295 	  if (err < 0)
296 	    {
297 	      H_ERRNO_SET (NETDB_INTERNAL);
298 	      status = NSS_STATUS_TRYAGAIN;
299 	      break;
300 	    }
301 
302 	  /* Continue with the next record, this one is ill-formed.  */
303 	  entidx = next;
304 	}
305     }
306 
307  out:
308   __libc_lock_unlock (lock);
309 
310   return status;
311 }
312