|
|
1.1 misho 1: /*
2: * Copyright (C) 2004-2007, 2009 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.80.332.2 2009/02/16 23:47:15 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
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
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
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
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
444: destroyfilesource(isc_entropyfilesource_t *source) {
445: (void)close(source->handle);
446: }
447:
448: static void
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
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
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: }