Add support for new secure hosts mode.
[kopensolaris-gnu/glibc.git] / nscd / hstcache.c
1 /* Cache handling for host lookup.
2    Copyright (C) 1998, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <error.h>
24 #include <netdb.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <arpa/inet.h>
32
33 #include "nscd.h"
34 #include "dbg_log.h"
35
36 /* Get implementation for some internal functions.  */
37 #include "../resolv/mapv4v6addr.h"
38
39
40 /* This is the standard reply in case the service is disabled.  */
41 static const hst_response_header disabled =
42 {
43   version: NSCD_VERSION,
44   found: -1,
45   h_name_len: 0,
46   h_aliases_cnt: 0,
47   h_addrtype: -1,
48   h_length: -1,
49   h_addr_list_cnt: 0,
50   error: NETDB_INTERNAL
51 };
52
53 /* This is the struct describing how to write this record.  */
54 const struct iovec hst_iov_disabled =
55 {
56   iov_base: (void *) &disabled,
57   iov_len: sizeof (disabled)
58 };
59
60
61 /* This is the standard reply in case we haven't found the dataset.  */
62 static const hst_response_header notfound =
63 {
64   version: NSCD_VERSION,
65   found: 0,
66   h_name_len: 0,
67   h_aliases_cnt: 0,
68   h_addrtype: -1,
69   h_length: -1,
70   h_addr_list_cnt: 0,
71   error: HOST_NOT_FOUND
72 };
73
74 /* This is the struct describing how to write this record.  */
75 static const struct iovec iov_notfound =
76 {
77   iov_base: (void *) &notfound,
78   iov_len: sizeof (notfound)
79 };
80
81
82 struct hostdata
83 {
84   hst_response_header resp;
85   char strdata[0];
86 };
87
88
89 static void
90 cache_addhst (struct database *db, int fd, request_header *req, void *key,
91               struct hostent *hst, uid_t owner)
92 {
93   ssize_t total;
94   ssize_t written;
95   time_t t = time (NULL);
96
97   if (hst == NULL)
98     {
99       /* We have no data.  This means we send the standard reply for this
100          case.  */
101       void *copy;
102
103       total = sizeof (notfound);
104
105       written = writev (fd, &iov_notfound, 1);
106
107       copy = malloc (req->key_len);
108       if (copy == NULL)
109         error (EXIT_FAILURE, errno, _("while allocating key copy"));
110       memcpy (copy, key, req->key_len);
111
112       /* Compute the timeout time.  */
113       t += db->negtimeout;
114
115       /* Now get the lock to safely insert the records.  */
116       pthread_rwlock_rdlock (&db->lock);
117
118       cache_add (req->type, copy, req->key_len, &iov_notfound,
119                  sizeof (notfound), (void *) -1, 0, t, db, owner);
120
121       pthread_rwlock_unlock (&db->lock);
122     }
123   else
124     {
125       /* Determine the I/O structure.  */
126       struct hostdata *data;
127       size_t h_name_len = strlen (hst->h_name) + 1;
128       size_t h_aliases_cnt;
129       size_t *h_aliases_len;
130       size_t h_addr_list_cnt;
131       int addr_list_type;
132       char *addresses;
133       char *aliases;
134       char *key_copy = NULL;
135       char *cp;
136       size_t cnt;
137
138       /* Determine the number of aliases.  */
139       h_aliases_cnt = 0;
140       for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
141         ++h_aliases_cnt;
142       /* Determine the length of all aliases.  */
143       h_aliases_len = alloca (h_aliases_cnt * sizeof (size_t));
144       total = 0;
145       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
146         {
147           h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
148           total += h_aliases_len[cnt];
149         }
150
151       /* Determine the number of addresses.  */
152       h_addr_list_cnt = 0;
153       for (cnt = 0; hst->h_addr_list[cnt]; ++cnt)
154         ++h_addr_list_cnt;
155
156       /* We allocate all data in one memory block: the iov vector,
157          the response header and the dataset itself.  */
158       total += (sizeof (struct hostdata)
159                 + h_name_len
160                 + h_aliases_cnt * sizeof (size_t)
161                 + h_addr_list_cnt * (hst->h_length
162                                      + (hst->h_length == INADDRSZ
163                                         ? IN6ADDRSZ : 0)));
164
165       data = (struct hostdata *) malloc (total + req->key_len);
166       if (data == NULL)
167         /* There is no reason to go on.  */
168         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
169
170       data->resp.found = 1;
171       data->resp.h_name_len = h_name_len;
172       data->resp.h_aliases_cnt = h_aliases_cnt;
173       data->resp.h_addrtype = hst->h_addrtype;
174       data->resp.h_length = hst->h_length;
175       data->resp.h_addr_list_cnt = h_addr_list_cnt;
176       data->resp.error = NETDB_SUCCESS;
177
178       cp = data->strdata;
179
180       cp = mempcpy (cp, hst->h_name, h_name_len);
181       cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (size_t));
182
183       /* The normal addresses first.  */
184       addresses = cp;
185       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
186         cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
187
188       /* And the generated IPv6 addresses if necessary.  */
189       if (hst->h_length == INADDRSZ)
190         {
191           /* Generate the IPv6 addresses.  */
192           for (cnt = 0; cnt < h_addr_list_cnt; cp += IN6ADDRSZ, ++cnt)
193             map_v4v6_address (hst->h_addr_list[cnt], cp);
194         }
195
196       /* Then the aliases.  */
197       aliases = cp;
198       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
199         cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
200
201       assert (cp == data->strdata + total - sizeof (hst_response_header));
202
203       /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
204          that the answer we get from the NSS does not contain the key
205          itself.  This is the case if the resolver is used and the name
206          is extended by the domainnames from /etc/resolv.conf.  Therefore
207          we explicitly add the name here.  */
208       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
209         key_copy = memcpy (cp, key, req->key_len);
210
211       /* We write the dataset before inserting it to the database
212          since while inserting this thread might block and so would
213          unnecessarily let the receiver wait.  */
214       written = write (fd, data, total);
215
216       addr_list_type = (hst->h_length == INADDRSZ
217                         ? GETHOSTBYADDR : GETHOSTBYADDRv6);
218
219       /* Compute the timeout time.  */
220       t += db->postimeout;
221
222       /* Now get the lock to safely insert the records.  */
223       pthread_rwlock_rdlock (&db->lock);
224
225       /* First add all the aliases.  */
226       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
227         {
228           if (addr_list_type == GETHOSTBYADDR)
229             cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
230                        data, 0, t, db, owner);
231
232           cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
233                      data, 0, t, db, owner);
234
235           aliases += h_aliases_len[cnt];
236         }
237
238       /* Next the normal addresses.  */
239       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
240         {
241           cache_add (addr_list_type, addresses, hst->h_length, data, total,
242                      data, 0, t, db, owner);
243           addresses += hst->h_length;
244         }
245
246       /* If necessary the IPv6 addresses.  */
247       if (addr_list_type == GETHOSTBYADDR)
248         for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
249           {
250             cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
251                        data, 0, t, db, owner);
252             addresses += IN6ADDRSZ;
253           }
254
255       /* If necessary add the key for this request.  */
256       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
257         {
258           if (addr_list_type == GETHOSTBYADDR)
259             cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
260                        data, 0, t, db, owner);
261           cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
262                      total, data, 0, t, db, owner);
263         }
264
265       /* And finally the name.  We mark this as the last entry.  */
266       if (addr_list_type == GETHOSTBYADDR)
267         cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
268                    0, t, db, owner);
269       cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
270                  total, data, 1, t, db, owner);
271
272       pthread_rwlock_unlock (&db->lock);
273     }
274
275   if (written != total)
276     {
277       char buf[256];
278       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
279                strerror_r (errno, buf, sizeof (buf)));
280     }
281 }
282
283
284 void
285 addhstbyname (struct database *db, int fd, request_header *req,
286               void *key, uid_t uid)
287 {
288   /* Search for the entry matching the key.  Please note that we don't
289      look again in the table whether the dataset is now available.  We
290      simply insert it.  It does not matter if it is in there twice.  The
291      pruning function only will look at the timestamp.  */
292   int buflen = 512;
293   char *buffer = alloca (buflen);
294   struct hostent resultbuf;
295   struct hostent *hst;
296   uid_t oldeuid = 0;
297
298   if (debug_level > 0)
299     dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
300
301   if (secure[hstdb])
302     {
303       oldeuid = geteuid ();
304       seteuid (uid);
305     }
306
307   while (gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen, &hst,
308                            &h_errno) != 0
309          && h_errno == NETDB_INTERNAL
310          && errno == ERANGE)
311     {
312       errno = 0;
313       buflen += 256;
314       buffer = alloca (buflen);
315     }
316
317   if (secure[hstdb])
318     seteuid (uid);
319
320   cache_addhst (db, fd, req, key, hst, uid);
321 }
322
323
324 void
325 addhstbyaddr (struct database *db, int fd, request_header *req,
326               void *key, uid_t uid)
327 {
328   /* Search for the entry matching the key.  Please note that we don't
329      look again in the table whether the dataset is now available.  We
330      simply insert it.  It does not matter if it is in there twice.  The
331      pruning function only will look at the timestamp.  */
332   int buflen = 512;
333   char *buffer = alloca (buflen);
334   struct hostent resultbuf;
335   struct hostent *hst;
336   uid_t oldeuid = 0;
337
338   if (debug_level > 0)
339     {
340       char buf[INET_ADDRSTRLEN];
341       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
342                inet_ntop (AF_INET, key, buf, sizeof (buf)));
343     }
344
345   if (secure[hstdb])
346     {
347       oldeuid = geteuid ();
348       seteuid (uid);
349     }
350
351   while (gethostbyaddr_r (key, INADDRSZ, AF_INET, &resultbuf, buffer, buflen,
352                           &hst, &h_errno) != 0
353          && h_errno == NETDB_INTERNAL
354          && errno == ERANGE)
355     {
356       errno = 0;
357       buflen += 256;
358       buffer = alloca (buflen);
359     }
360
361   if (secure[hstdb])
362     seteuid (oldeuid);
363
364   cache_addhst (db, fd, req, key, hst, uid);
365 }
366
367
368 void
369 addhstbynamev6 (struct database *db, int fd, request_header *req,
370                 void *key, uid_t uid)
371 {
372   /* Search for the entry matching the key.  Please note that we don't
373      look again in the table whether the dataset is now available.  We
374      simply insert it.  It does not matter if it is in there twice.  The
375      pruning function only will look at the timestamp.  */
376   int buflen = 512;
377   char *buffer = alloca (buflen);
378   struct hostent resultbuf;
379   struct hostent *hst;
380   uid_t oldeuid = 0;
381
382   if (debug_level > 0)
383     {
384       char buf[INET6_ADDRSTRLEN];
385
386       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
387                inet_ntop (AF_INET6, key, buf, sizeof (buf)));
388     }
389
390   if (secure[hstdb])
391     {
392       oldeuid = geteuid ();
393       seteuid (uid);
394     }
395
396   while (gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen, &hst,
397                            &h_errno) != 0
398          && h_errno == NETDB_INTERNAL
399          && errno == ERANGE)
400     {
401       errno = 0;
402       buflen += 256;
403       buffer = alloca (buflen);
404     }
405
406   if (secure[hstdb])
407     seteuid (oldeuid);
408
409   cache_addhst (db, fd, req, key, hst, uid);
410 }
411
412
413 void
414 addhstbyaddrv6 (struct database *db, int fd, request_header *req,
415                 void *key, uid_t uid)
416 {
417   /* Search for the entry matching the key.  Please note that we don't
418      look again in the table whether the dataset is now available.  We
419      simply insert it.  It does not matter if it is in there twice.  The
420      pruning function only will look at the timestamp.  */
421   int buflen = 512;
422   char *buffer = alloca (buflen);
423   struct hostent resultbuf;
424   struct hostent *hst;
425   uid_t oldeuid = 0;
426
427   if (debug_level > 0)
428     {
429       char buf[INET6_ADDRSTRLEN];
430       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
431                inet_ntop (AF_INET6, key, buf, sizeof (buf)));
432     }
433
434   if (secure[hstdb])
435     {
436       oldeuid = geteuid ();
437       seteuid (uid);
438     }
439
440   while (gethostbyaddr_r (key, IN6ADDRSZ, AF_INET6, &resultbuf, buffer, buflen,
441                           &hst, &h_errno) != 0
442          && h_errno == NETDB_INTERNAL
443          && errno == ERANGE)
444     {
445       errno = 0;
446       buflen += 256;
447       buffer = alloca (buflen);
448     }
449
450   if (secure[hstdb])
451     seteuid (oldeuid);
452
453   cache_addhst (db, fd, req, key, hst, uid);
454 }