1 /* $NetBSD: entropy.c,v 1.1.1.1 2009/12/13 16:54:33 kardel Exp $ */ 2 3 /* 4 * Copyright (C) 2004-2007, 2009 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.80.332.2 2009/02/16 23:47:15 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 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 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 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 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 446 destroyfilesource(isc_entropyfilesource_t *source) { 447 (void)close(source->handle); 448 } 449 450 static void 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 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 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 strncpy(sname.sun_path, fname, sizeof(sname.sun_path)); 542 sname.sun_path[sizeof(sname.sun_path)-1] = '0'; 543 #ifdef ISC_PLATFORM_HAVESALEN 544 #if !defined(SUN_LEN) 545 #define SUN_LEN(su) \ 546 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) 547 #endif 548 sname.sun_len = SUN_LEN(&sname); 549 #endif 550 551 if (connect(fd, (struct sockaddr *) &sname, 552 sizeof(struct sockaddr_un)) < 0) { 553 if (errno != EINPROGRESS) { 554 ret = isc__errno2result(errno); 555 goto closefd; 556 } 557 } else 558 is_connected = ISC_TRUE; 559 } 560 561 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); 562 if (source == NULL) { 563 ret = ISC_R_NOMEMORY; 564 goto closefd; 565 } 566 567 /* 568 * From here down, no failures can occur. 569 */ 570 source->magic = SOURCE_MAGIC; 571 source->ent = ent; 572 source->total = 0; 573 source->bad = ISC_FALSE; 574 memset(source->name, 0, sizeof(source->name)); 575 ISC_LINK_INIT(source, link); 576 if (is_usocket) { 577 source->sources.usocket.handle = fd; 578 if (is_connected) 579 source->sources.usocket.status = 580 isc_usocketsource_connected; 581 else 582 source->sources.usocket.status = 583 isc_usocketsource_connecting; 584 source->sources.usocket.sz_to_recv = 0; 585 source->type = ENTROPY_SOURCETYPE_USOCKET; 586 } else { 587 source->sources.file.handle = fd; 588 source->type = ENTROPY_SOURCETYPE_FILE; 589 } 590 591 /* 592 * Hook it into the entropy system. 593 */ 594 ISC_LIST_APPEND(ent->sources, source, link); 595 ent->nsources++; 596 597 UNLOCK(&ent->lock); 598 return (ISC_R_SUCCESS); 599 600 closefd: 601 (void)close(fd); 602 603 errout: 604 UNLOCK(&ent->lock); 605 606 return (ret); 607 } 608