Annotation of embedaddon/ntp/lib/isc/unix/entropy.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>