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