]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/bind9/lib/isc/unix/entropy.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / bind9 / lib / isc / unix / entropy.c
1 /*
2  * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: entropy.c,v 1.71.18.7 2006/12/07 04:53:03 marka Exp $ */
19
20 /* \file unix/entropy.c
21  * \brief
22  * This is the system dependent part of the ISC entropy API.
23  */
24
25 #include <config.h>
26
27 #include <sys/param.h>  /* Openserver 5.0.6A and FD_SETSIZE */
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33
34 #include <unistd.h>
35
36 #include <isc/platform.h>
37 #include <isc/strerror.h>
38
39 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
40 #include <sys/select.h>
41 #endif
42
43 #include "errno2result.h"
44
45 /*%
46  * There is only one variable in the entropy data structures that is not
47  * system independent, but pulling the structure that uses it into this file
48  * ultimately means pulling several other independent structures here also to
49  * resolve their interdependencies.  Thus only the problem variable's type
50  * is defined here.
51  */
52 #define FILESOURCE_HANDLE_TYPE  int
53
54 typedef struct {
55         int     handle;
56         enum    {
57                 isc_usocketsource_disconnected,
58                 isc_usocketsource_connecting,
59                 isc_usocketsource_connected,
60                 isc_usocketsource_ndesired,
61                 isc_usocketsource_wrote,
62                 isc_usocketsource_reading
63         } status;
64         size_t  sz_to_recv;
65 } isc_entropyusocketsource_t;
66
67 #include "../entropy.c"
68
69 static unsigned int
70 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
71         isc_entropy_t *ent = source->ent;
72         unsigned char buf[128];
73         int fd = source->sources.file.handle;
74         ssize_t n, ndesired;
75         unsigned int added;
76
77         if (source->bad)
78                 return (0);
79
80         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
81
82         added = 0;
83         while (desired > 0) {
84                 ndesired = ISC_MIN(desired, sizeof(buf));
85                 n = read(fd, buf, ndesired);
86                 if (n < 0) {
87                         if (errno == EAGAIN || errno == EINTR)
88                                 goto out;
89                         goto err;
90                 }
91                 if (n == 0)
92                         goto err;
93
94                 entropypool_adddata(ent, buf, n, n * 8);
95                 added += n * 8;
96                 desired -= n;
97         }
98         goto out;
99
100  err:
101         (void)close(fd);
102         source->sources.file.handle = -1;
103         source->bad = ISC_TRUE;
104
105  out:
106         return (added);
107 }
108
109 static unsigned int
110 get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
111         isc_entropy_t *ent = source->ent;
112         unsigned char buf[128];
113         int fd = source->sources.usocket.handle;
114         ssize_t n = 0, ndesired;
115         unsigned int added;
116         size_t sz_to_recv = source->sources.usocket.sz_to_recv;
117
118         if (source->bad)
119                 return (0);
120
121         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
122
123         added = 0;
124         while (desired > 0) {
125                 ndesired = ISC_MIN(desired, sizeof(buf));
126  eagain_loop:
127
128                 switch ( source->sources.usocket.status ) {
129                 case isc_usocketsource_ndesired:
130                         buf[0] = ndesired;
131                         if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
132                                 if (errno == EWOULDBLOCK || errno == EINTR ||
133                                     errno == ECONNRESET)
134                                         goto out;
135                                 goto err;
136                         }
137                         INSIST(n == 1);
138                         source->sources.usocket.status =
139                                                 isc_usocketsource_wrote;
140                         goto eagain_loop;
141
142                 case isc_usocketsource_connecting:
143                 case isc_usocketsource_connected:
144                         buf[0] = 1;
145                         buf[1] = ndesired;
146                         if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
147                                 if (errno == EWOULDBLOCK || errno == EINTR ||
148                                     errno == ECONNRESET)
149                                         goto out;
150                                 goto err;
151                         }
152                         if (n == 1) {
153                                 source->sources.usocket.status =
154                                         isc_usocketsource_ndesired;
155                                 goto eagain_loop;
156                         }       
157                         INSIST(n == 2);
158                         source->sources.usocket.status =
159                                                 isc_usocketsource_wrote;
160                         /*FALLTHROUGH*/
161                 
162                 case isc_usocketsource_wrote:
163                         if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
164                                 if (errno == EAGAIN) {
165                                         /*
166                                          * The problem of EAGAIN (try again
167                                          * later) is a major issue on HP-UX.
168                                          * Solaris actually tries the recvfrom
169                                          * call again, while HP-UX just dies. 
170                                          * This code is an attempt to let the
171                                          * entropy pool fill back up (at least
172                                          * that's what I think the problem is.)
173                                          * We go to eagain_loop because if we 
174                                          * just "break", then the "desired"
175                                          * amount gets borked.
176                                          */
177                                         usleep(1000);
178                                         goto eagain_loop;
179                                 }
180                                 if (errno == EWOULDBLOCK || errno == EINTR)
181                                         goto out;
182                                 goto err;
183                         }
184                         source->sources.usocket.status =
185                                         isc_usocketsource_reading;
186                         sz_to_recv = buf[0];
187                         source->sources.usocket.sz_to_recv = sz_to_recv;
188                         if (sz_to_recv > sizeof(buf))
189                                 goto err;
190                         /*FALLTHROUGH*/
191
192                 case isc_usocketsource_reading:
193                         if (sz_to_recv != 0U) {
194                                 n = recv(fd, buf, sz_to_recv, 0);
195                                 if (n < 0) {
196                                         if (errno == EWOULDBLOCK ||
197                                             errno == EINTR)
198                                                 goto out;
199                                         goto err;
200                                 }
201                         } else
202                                 n = 0;
203                         break;
204                 
205                 default:
206                         goto err;
207                 }
208
209                 if ((size_t)n != sz_to_recv)
210                         source->sources.usocket.sz_to_recv -= n;
211                 else
212                         source->sources.usocket.status =
213                                 isc_usocketsource_connected;
214
215                 if (n == 0)
216                         goto out;
217
218                 entropypool_adddata(ent, buf, n, n * 8);
219                 added += n * 8;
220                 desired -= n;
221         }
222         goto out;
223
224  err:
225         close(fd);
226         source->bad = ISC_TRUE;
227         source->sources.usocket.status = isc_usocketsource_disconnected;
228         source->sources.usocket.handle = -1;
229
230  out:
231         return (added);
232 }
233
234 /*
235  * Poll each source, trying to get data from it to stuff into the entropy
236  * pool.
237  */
238 static void
239 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
240         unsigned int added;
241         unsigned int remaining;
242         unsigned int needed;
243         unsigned int nsource;
244         isc_entropysource_t *source;
245
246         REQUIRE(VALID_ENTROPY(ent));
247
248         needed = desired;
249
250         /*
251          * This logic is a little strange, so an explanation is in order.
252          *
253          * If needed is 0, it means we are being asked to "fill to whatever
254          * we think is best."  This means that if we have at least a
255          * partially full pool (say, > 1/4th of the pool) we probably don't
256          * need to add anything.
257          *
258          * Also, we will check to see if the "pseudo" count is too high.
259          * If it is, try to mix in better data.  Too high is currently
260          * defined as 1/4th of the pool.
261          *
262          * Next, if we are asked to add a specific bit of entropy, make
263          * certain that we will do so.  Clamp how much we try to add to
264          * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
265          *
266          * Note that if we are in a blocking mode, we will only try to
267          * get as much data as we need, not as much as we might want
268          * to build up.
269          */
270         if (needed == 0) {
271                 REQUIRE(!blocking);
272
273                 if ((ent->pool.entropy >= RND_POOLBITS / 4)
274                     && (ent->pool.pseudo <= RND_POOLBITS / 4))
275                         return;
276
277                 needed = THRESHOLD_BITS * 4;
278         } else {
279                 needed = ISC_MAX(needed, THRESHOLD_BITS);
280                 needed = ISC_MIN(needed, RND_POOLBITS);
281         }
282
283         /*
284          * In any case, clamp how much we need to how much we can add.
285          */
286         needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
287
288         /*
289          * But wait!  If we're not yet initialized, we need at least
290          *      THRESHOLD_BITS
291          * of randomness.
292          */
293         if (ent->initialized < THRESHOLD_BITS)
294                 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
295
296         /*
297          * Poll each file source to see if we can read anything useful from
298          * it.  XXXMLG When where are multiple sources, we should keep a
299          * record of which one we last used so we can start from it (or the
300          * next one) to avoid letting some sources build up entropy while
301          * others are always drained.
302          */
303
304         added = 0;
305         remaining = needed;
306         if (ent->nextsource == NULL) {
307                 ent->nextsource = ISC_LIST_HEAD(ent->sources);
308                 if (ent->nextsource == NULL)
309                         return;
310         }
311         source = ent->nextsource;
312  again_file:
313         for (nsource = 0; nsource < ent->nsources; nsource++) {
314                 unsigned int got;
315
316                 if (remaining == 0)
317                         break;
318
319                 got = 0;
320
321                 switch ( source->type ) {
322                 case ENTROPY_SOURCETYPE_FILE:
323                         got = get_from_filesource(source, remaining);
324                         break;
325
326                 case ENTROPY_SOURCETYPE_USOCKET:
327                         got = get_from_usocketsource(source, remaining);
328                         break;
329                 }
330
331                 added += got;
332
333                 remaining -= ISC_MIN(remaining, got);
334
335                 source = ISC_LIST_NEXT(source, link);
336                 if (source == NULL)
337                         source = ISC_LIST_HEAD(ent->sources);
338         }
339         ent->nextsource = source;
340
341         if (blocking && remaining != 0) {
342                 int fds;
343
344                 fds = wait_for_sources(ent);
345                 if (fds > 0)
346                         goto again_file;
347         }
348
349         /*
350          * Here, if there are bits remaining to be had and we can block,
351          * check to see if we have a callback source.  If so, call them.
352          */
353         source = ISC_LIST_HEAD(ent->sources);
354         while ((remaining != 0) && (source != NULL)) {
355                 unsigned int got;
356
357                 got = 0;
358
359                 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
360                         got = get_from_callback(source, remaining, blocking);
361
362                 added += got;
363                 remaining -= ISC_MIN(remaining, got);
364
365                 if (added >= needed)
366                         break;
367
368                 source = ISC_LIST_NEXT(source, link);
369         }
370
371         /*
372          * Mark as initialized if we've added enough data.
373          */
374         if (ent->initialized < THRESHOLD_BITS)
375                 ent->initialized += added;
376 }
377
378 static int
379 wait_for_sources(isc_entropy_t *ent) {
380         isc_entropysource_t *source;
381         int maxfd, fd;
382         int cc;
383         fd_set reads;
384         fd_set writes;
385
386         maxfd = -1;
387         FD_ZERO(&reads);
388         FD_ZERO(&writes);
389
390         source = ISC_LIST_HEAD(ent->sources);
391         while (source != NULL) {
392                 if (source->type == ENTROPY_SOURCETYPE_FILE) {
393                         fd = source->sources.file.handle;
394                         if (fd >= 0) {
395                                 maxfd = ISC_MAX(maxfd, fd);
396                                 FD_SET(fd, &reads);
397                         }
398                 }
399                 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
400                         fd = source->sources.usocket.handle;
401                         if (fd >= 0) {
402                                 switch (source->sources.usocket.status) {
403                                 case isc_usocketsource_disconnected:
404                                         break;
405                                 case isc_usocketsource_connecting:
406                                 case isc_usocketsource_connected:
407                                 case isc_usocketsource_ndesired:
408                                         maxfd = ISC_MAX(maxfd, fd);
409                                         FD_SET(fd, &writes);
410                                         break;
411                                 case isc_usocketsource_wrote:
412                                 case isc_usocketsource_reading:
413                                         maxfd = ISC_MAX(maxfd, fd);
414                                         FD_SET(fd, &reads);
415                                         break;
416                                 }
417                         }
418                 }
419                 source = ISC_LIST_NEXT(source, link);
420         }
421
422         if (maxfd < 0)
423                 return (-1);
424
425         cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
426         if (cc < 0)
427                 return (-1);
428
429         return (cc);
430 }
431
432 static void
433 destroyfilesource(isc_entropyfilesource_t *source) {
434         (void)close(source->handle);
435 }
436
437 static void
438 destroyusocketsource(isc_entropyusocketsource_t *source) {
439         close(source->handle);
440 }
441
442 /*
443  * Make a fd non-blocking
444  */
445 static isc_result_t
446 make_nonblock(int fd) {
447         int ret;
448         int flags;
449         char strbuf[ISC_STRERRORSIZE];
450 #ifdef USE_FIONBIO_IOCTL
451         int on = 1;
452
453         ret = ioctl(fd, FIONBIO, (char *)&on);
454 #else
455         flags = fcntl(fd, F_GETFL, 0);
456         flags |= PORT_NONBLOCK;
457         ret = fcntl(fd, F_SETFL, flags);
458 #endif
459
460         if (ret == -1) {
461                 isc__strerror(errno, strbuf, sizeof(strbuf));
462                 UNEXPECTED_ERROR(__FILE__, __LINE__,
463 #ifdef USE_FIONBIO_IOCTL
464                                  "ioctl(%d, FIONBIO, &on): %s", fd,
465 #else
466                                  "fcntl(%d, F_SETFL, %d): %s", fd, flags,
467 #endif
468                                  strbuf);
469
470                 return (ISC_R_UNEXPECTED);
471         }
472
473         return (ISC_R_SUCCESS);
474 }
475
476 isc_result_t
477 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
478         int fd;
479         struct stat _stat;
480         isc_boolean_t is_usocket = ISC_FALSE;
481         isc_boolean_t is_connected = ISC_FALSE;
482         isc_result_t ret;
483         isc_entropysource_t *source;
484
485         REQUIRE(VALID_ENTROPY(ent));
486         REQUIRE(fname != NULL);
487
488         LOCK(&ent->lock);
489
490         if (stat(fname, &_stat) < 0) {
491                 ret = isc__errno2result(errno);
492                 goto errout;
493         }
494         /* 
495          * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
496          * but it does return type S_IFIFO (the OS believes that
497          * the socket is a fifo).  This may be an issue if we tell
498          * the program to look at an actual FIFO as its source of
499          * entropy.
500          */
501 #if defined(S_ISSOCK)
502         if (S_ISSOCK(_stat.st_mode))
503                 is_usocket = ISC_TRUE;
504 #endif
505 #if defined(S_ISFIFO) && defined(sun)
506         if (S_ISFIFO(_stat.st_mode))
507                 is_usocket = ISC_TRUE;
508 #endif
509         if (is_usocket)
510                 fd = socket(PF_UNIX, SOCK_STREAM, 0);
511         else
512                 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
513
514         if (fd < 0) {
515                 ret = isc__errno2result(errno);
516                 goto errout;
517         }
518
519         ret = make_nonblock(fd);
520         if (ret != ISC_R_SUCCESS)
521                 goto closefd;
522
523         if (is_usocket) {
524                 struct sockaddr_un sname;
525
526                 memset(&sname, 0, sizeof(sname));
527                 sname.sun_family = AF_UNIX;
528                 strncpy(sname.sun_path, fname, sizeof(sname.sun_path));
529                 sname.sun_path[sizeof(sname.sun_path)-1] = '0';
530 #ifdef ISC_PLATFORM_HAVESALEN
531 #if !defined(SUN_LEN)
532 #define SUN_LEN(su) \
533         (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
534 #endif
535                 sname.sun_len = SUN_LEN(&sname);
536 #endif
537
538                 if (connect(fd, (struct sockaddr *) &sname,
539                             sizeof(struct sockaddr_un)) < 0) {
540                         if (errno != EINPROGRESS) {
541                                 ret = isc__errno2result(errno);
542                                 goto closefd;
543                         }
544                 } else
545                         is_connected = ISC_TRUE;
546         }
547
548         source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
549         if (source == NULL) {
550                 ret = ISC_R_NOMEMORY;
551                 goto closefd;
552         }
553
554         /*
555          * From here down, no failures can occur.
556          */
557         source->magic = SOURCE_MAGIC;
558         source->ent = ent;
559         source->total = 0;
560         source->bad = ISC_FALSE;
561         memset(source->name, 0, sizeof(source->name));
562         ISC_LINK_INIT(source, link);
563         if (is_usocket) {
564                 source->sources.usocket.handle = fd;
565                 if (is_connected)
566                         source->sources.usocket.status =
567                                         isc_usocketsource_connected;
568                 else
569                         source->sources.usocket.status =
570                                         isc_usocketsource_connecting;
571                 source->sources.usocket.sz_to_recv = 0;
572                 source->type = ENTROPY_SOURCETYPE_USOCKET;
573         } else {
574                 source->sources.file.handle = fd;
575                 source->type = ENTROPY_SOURCETYPE_FILE;
576         }
577
578         /*
579          * Hook it into the entropy system.
580          */
581         ISC_LIST_APPEND(ent->sources, source, link);
582         ent->nsources++;
583
584         UNLOCK(&ent->lock);
585         return (ISC_R_SUCCESS);
586
587  closefd:
588         (void)close(fd);
589
590  errout:
591         UNLOCK(&ent->lock);
592
593         return (ret);
594 }