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 57typedef 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 72static unsigned int 73get_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 112static unsigned int 113get_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 */ 249static void 250fillpool(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 389static int 390wait_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 443static void 444destroyfilesource(isc_entropyfilesource_t *source) { 445 (void)close(source->handle); 446} 447 448static void 449destroyusocketsource(isc_entropyusocketsource_t *source) { 450 close(source->handle); 451} 452 453/* 454 * Make a fd non-blocking 455 */ 456static isc_result_t 457make_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 487isc_result_t 488isc_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