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