entropy.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2008, 2012  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2000-2003  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id: entropy.c,v 1.82 2008/12/01 23:47:45 tbox Exp $ */
00019 
00020 /* \file unix/entropy.c
00021  * \brief
00022  * This is the system dependent part of the ISC entropy API.
00023  */
00024 
00025 #include <config.h>
00026 
00027 #include <sys/param.h>  /* Openserver 5.0.6A and FD_SETSIZE */
00028 #include <sys/types.h>
00029 #include <sys/time.h>
00030 #include <sys/stat.h>
00031 #include <sys/socket.h>
00032 #include <sys/un.h>
00033 
00034 #ifdef HAVE_NANOSLEEP
00035 #include <time.h>
00036 #endif
00037 #include <unistd.h>
00038 
00039 #include <isc/platform.h>
00040 #include <isc/strerror.h>
00041 
00042 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
00043 #include <sys/select.h>
00044 #endif
00045 
00046 #include "errno2result.h"
00047 
00048 /*%
00049  * There is only one variable in the entropy data structures that is not
00050  * system independent, but pulling the structure that uses it into this file
00051  * ultimately means pulling several other independent structures here also to
00052  * resolve their interdependencies.  Thus only the problem variable's type
00053  * is defined here.
00054  */
00055 #define FILESOURCE_HANDLE_TYPE  int
00056 
00057 typedef struct {
00058         int     handle;
00059         enum    {
00060                 isc_usocketsource_disconnected,
00061                 isc_usocketsource_connecting,
00062                 isc_usocketsource_connected,
00063                 isc_usocketsource_ndesired,
00064                 isc_usocketsource_wrote,
00065                 isc_usocketsource_reading
00066         } status;
00067         size_t  sz_to_recv;
00068 } isc_entropyusocketsource_t;
00069 
00070 #include "../entropy.c"
00071 
00072 static unsigned int
00073 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
00074         isc_entropy_t *ent = source->ent;
00075         unsigned char buf[128];
00076         int fd = source->sources.file.handle;
00077         ssize_t n, ndesired;
00078         unsigned int added;
00079 
00080         if (source->bad)
00081                 return (0);
00082 
00083         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
00084 
00085         added = 0;
00086         while (desired > 0) {
00087                 ndesired = ISC_MIN(desired, sizeof(buf));
00088                 n = read(fd, buf, ndesired);
00089                 if (n < 0) {
00090                         if (errno == EAGAIN || errno == EINTR)
00091                                 goto out;
00092                         goto err;
00093                 }
00094                 if (n == 0)
00095                         goto err;
00096 
00097                 entropypool_adddata(ent, buf, n, n * 8);
00098                 added += n * 8;
00099                 desired -= n;
00100         }
00101         goto out;
00102 
00103  err:
00104         (void)close(fd);
00105         source->sources.file.handle = -1;
00106         source->bad = ISC_TRUE;
00107 
00108  out:
00109         return (added);
00110 }
00111 
00112 static unsigned int
00113 get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
00114         isc_entropy_t *ent = source->ent;
00115         unsigned char buf[128];
00116         int fd = source->sources.usocket.handle;
00117         ssize_t n = 0, ndesired;
00118         unsigned int added;
00119         size_t sz_to_recv = source->sources.usocket.sz_to_recv;
00120 
00121         if (source->bad)
00122                 return (0);
00123 
00124         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
00125 
00126         added = 0;
00127         while (desired > 0) {
00128                 ndesired = ISC_MIN(desired, sizeof(buf));
00129  eagain_loop:
00130 
00131                 switch ( source->sources.usocket.status ) {
00132                 case isc_usocketsource_ndesired:
00133                         buf[0] = ndesired;
00134                         if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
00135                                 if (errno == EWOULDBLOCK || errno == EINTR ||
00136                                     errno == ECONNRESET)
00137                                         goto out;
00138                                 goto err;
00139                         }
00140                         INSIST(n == 1);
00141                         source->sources.usocket.status =
00142                                                 isc_usocketsource_wrote;
00143                         goto eagain_loop;
00144 
00145                 case isc_usocketsource_connecting:
00146                 case isc_usocketsource_connected:
00147                         buf[0] = 1;
00148                         buf[1] = ndesired;
00149                         if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
00150                                 if (errno == EWOULDBLOCK || errno == EINTR ||
00151                                     errno == ECONNRESET)
00152                                         goto out;
00153                                 goto err;
00154                         }
00155                         if (n == 1) {
00156                                 source->sources.usocket.status =
00157                                         isc_usocketsource_ndesired;
00158                                 goto eagain_loop;
00159                         }
00160                         INSIST(n == 2);
00161                         source->sources.usocket.status =
00162                                                 isc_usocketsource_wrote;
00163                         /*FALLTHROUGH*/
00164 
00165                 case isc_usocketsource_wrote:
00166                         if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
00167                                 if (errno == EAGAIN) {
00168                                         /*
00169                                          * The problem of EAGAIN (try again
00170                                          * later) is a major issue on HP-UX.
00171                                          * Solaris actually tries the recvfrom
00172                                          * call again, while HP-UX just dies.
00173                                          * This code is an attempt to let the
00174                                          * entropy pool fill back up (at least
00175                                          * that's what I think the problem is.)
00176                                          * We go to eagain_loop because if we
00177                                          * just "break", then the "desired"
00178                                          * amount gets borked.
00179                                          */
00180 #ifdef HAVE_NANOSLEEP
00181                                         struct timespec ts;
00182 
00183                                         ts.tv_sec = 0;
00184                                         ts.tv_nsec = 1000000;
00185                                         nanosleep(&ts, NULL);
00186 #else
00187                                         usleep(1000);
00188 #endif
00189                                         goto eagain_loop;
00190                                 }
00191                                 if (errno == EWOULDBLOCK || errno == EINTR)
00192                                         goto out;
00193                                 goto err;
00194                         }
00195                         source->sources.usocket.status =
00196                                         isc_usocketsource_reading;
00197                         sz_to_recv = buf[0];
00198                         source->sources.usocket.sz_to_recv = sz_to_recv;
00199                         if (sz_to_recv > sizeof(buf))
00200                                 goto err;
00201                         /*FALLTHROUGH*/
00202 
00203                 case isc_usocketsource_reading:
00204                         if (sz_to_recv != 0U) {
00205                                 n = recv(fd, buf, sz_to_recv, 0);
00206                                 if (n < 0) {
00207                                         if (errno == EWOULDBLOCK ||
00208                                             errno == EINTR)
00209                                                 goto out;
00210                                         goto err;
00211                                 }
00212                         } else
00213                                 n = 0;
00214                         break;
00215 
00216                 default:
00217                         goto err;
00218                 }
00219 
00220                 if ((size_t)n != sz_to_recv)
00221                         source->sources.usocket.sz_to_recv -= n;
00222                 else
00223                         source->sources.usocket.status =
00224                                 isc_usocketsource_connected;
00225 
00226                 if (n == 0)
00227                         goto out;
00228 
00229                 entropypool_adddata(ent, buf, n, n * 8);
00230                 added += n * 8;
00231                 desired -= n;
00232         }
00233         goto out;
00234 
00235  err:
00236         close(fd);
00237         source->bad = ISC_TRUE;
00238         source->sources.usocket.status = isc_usocketsource_disconnected;
00239         source->sources.usocket.handle = -1;
00240 
00241  out:
00242         return (added);
00243 }
00244 
00245 /*
00246  * Poll each source, trying to get data from it to stuff into the entropy
00247  * pool.
00248  */
00249 static void
00250 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
00251         unsigned int added;
00252         unsigned int remaining;
00253         unsigned int needed;
00254         unsigned int nsource;
00255         isc_entropysource_t *source;
00256 
00257         REQUIRE(VALID_ENTROPY(ent));
00258 
00259         needed = desired;
00260 
00261         /*
00262          * This logic is a little strange, so an explanation is in order.
00263          *
00264          * If needed is 0, it means we are being asked to "fill to whatever
00265          * we think is best."  This means that if we have at least a
00266          * partially full pool (say, > 1/4th of the pool) we probably don't
00267          * need to add anything.
00268          *
00269          * Also, we will check to see if the "pseudo" count is too high.
00270          * If it is, try to mix in better data.  Too high is currently
00271          * defined as 1/4th of the pool.
00272          *
00273          * Next, if we are asked to add a specific bit of entropy, make
00274          * certain that we will do so.  Clamp how much we try to add to
00275          * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
00276          *
00277          * Note that if we are in a blocking mode, we will only try to
00278          * get as much data as we need, not as much as we might want
00279          * to build up.
00280          */
00281         if (needed == 0) {
00282                 REQUIRE(!blocking);
00283 
00284                 if ((ent->pool.entropy >= RND_POOLBITS / 4)
00285                     && (ent->pool.pseudo <= RND_POOLBITS / 4))
00286                         return;
00287 
00288                 needed = THRESHOLD_BITS * 4;
00289         } else {
00290                 needed = ISC_MAX(needed, THRESHOLD_BITS);
00291                 needed = ISC_MIN(needed, RND_POOLBITS);
00292         }
00293 
00294         /*
00295          * In any case, clamp how much we need to how much we can add.
00296          */
00297         needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
00298 
00299         /*
00300          * But wait!  If we're not yet initialized, we need at least
00301          *      THRESHOLD_BITS
00302          * of randomness.
00303          */
00304         if (ent->initialized < THRESHOLD_BITS)
00305                 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
00306 
00307         /*
00308          * Poll each file source to see if we can read anything useful from
00309          * it.  XXXMLG When where are multiple sources, we should keep a
00310          * record of which one we last used so we can start from it (or the
00311          * next one) to avoid letting some sources build up entropy while
00312          * others are always drained.
00313          */
00314 
00315         added = 0;
00316         remaining = needed;
00317         if (ent->nextsource == NULL) {
00318                 ent->nextsource = ISC_LIST_HEAD(ent->sources);
00319                 if (ent->nextsource == NULL)
00320                         return;
00321         }
00322         source = ent->nextsource;
00323  again_file:
00324         for (nsource = 0; nsource < ent->nsources; nsource++) {
00325                 unsigned int got;
00326 
00327                 if (remaining == 0)
00328                         break;
00329 
00330                 got = 0;
00331 
00332                 switch ( source->type ) {
00333                 case ENTROPY_SOURCETYPE_FILE:
00334                         got = get_from_filesource(source, remaining);
00335                         break;
00336 
00337                 case ENTROPY_SOURCETYPE_USOCKET:
00338                         got = get_from_usocketsource(source, remaining);
00339                         break;
00340                 }
00341 
00342                 added += got;
00343 
00344                 remaining -= ISC_MIN(remaining, got);
00345 
00346                 source = ISC_LIST_NEXT(source, link);
00347                 if (source == NULL)
00348                         source = ISC_LIST_HEAD(ent->sources);
00349         }
00350         ent->nextsource = source;
00351 
00352         if (blocking && remaining != 0) {
00353                 int fds;
00354 
00355                 fds = wait_for_sources(ent);
00356                 if (fds > 0)
00357                         goto again_file;
00358         }
00359 
00360         /*
00361          * Here, if there are bits remaining to be had and we can block,
00362          * check to see if we have a callback source.  If so, call them.
00363          */
00364         source = ISC_LIST_HEAD(ent->sources);
00365         while ((remaining != 0) && (source != NULL)) {
00366                 unsigned int got;
00367 
00368                 got = 0;
00369 
00370                 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
00371                         got = get_from_callback(source, remaining, blocking);
00372 
00373                 added += got;
00374                 remaining -= ISC_MIN(remaining, got);
00375 
00376                 if (added >= needed)
00377                         break;
00378 
00379                 source = ISC_LIST_NEXT(source, link);
00380         }
00381 
00382         /*
00383          * Mark as initialized if we've added enough data.
00384          */
00385         if (ent->initialized < THRESHOLD_BITS)
00386                 ent->initialized += added;
00387 }
00388 
00389 static int
00390 wait_for_sources(isc_entropy_t *ent) {
00391         isc_entropysource_t *source;
00392         int maxfd, fd;
00393         int cc;
00394         fd_set reads;
00395         fd_set writes;
00396 
00397         maxfd = -1;
00398         FD_ZERO(&reads);
00399         FD_ZERO(&writes);
00400 
00401         source = ISC_LIST_HEAD(ent->sources);
00402         while (source != NULL) {
00403                 if (source->type == ENTROPY_SOURCETYPE_FILE) {
00404                         fd = source->sources.file.handle;
00405                         if (fd >= 0) {
00406                                 maxfd = ISC_MAX(maxfd, fd);
00407                                 FD_SET(fd, &reads);
00408                         }
00409                 }
00410                 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
00411                         fd = source->sources.usocket.handle;
00412                         if (fd >= 0) {
00413                                 switch (source->sources.usocket.status) {
00414                                 case isc_usocketsource_disconnected:
00415                                         break;
00416                                 case isc_usocketsource_connecting:
00417                                 case isc_usocketsource_connected:
00418                                 case isc_usocketsource_ndesired:
00419                                         maxfd = ISC_MAX(maxfd, fd);
00420                                         FD_SET(fd, &writes);
00421                                         break;
00422                                 case isc_usocketsource_wrote:
00423                                 case isc_usocketsource_reading:
00424                                         maxfd = ISC_MAX(maxfd, fd);
00425                                         FD_SET(fd, &reads);
00426                                         break;
00427                                 }
00428                         }
00429                 }
00430                 source = ISC_LIST_NEXT(source, link);
00431         }
00432 
00433         if (maxfd < 0)
00434                 return (-1);
00435 
00436         cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
00437         if (cc < 0)
00438                 return (-1);
00439 
00440         return (cc);
00441 }
00442 
00443 static void
00444 destroyfilesource(isc_entropyfilesource_t *source) {
00445         (void)close(source->handle);
00446 }
00447 
00448 static void
00449 destroyusocketsource(isc_entropyusocketsource_t *source) {
00450         close(source->handle);
00451 }
00452 
00453 /*
00454  * Make a fd non-blocking
00455  */
00456 static isc_result_t
00457 make_nonblock(int fd) {
00458         int ret;
00459         int flags;
00460         char strbuf[ISC_STRERRORSIZE];
00461 #ifdef USE_FIONBIO_IOCTL
00462         int on = 1;
00463 
00464         ret = ioctl(fd, FIONBIO, (char *)&on);
00465 #else
00466         flags = fcntl(fd, F_GETFL, 0);
00467         flags |= PORT_NONBLOCK;
00468         ret = fcntl(fd, F_SETFL, flags);
00469 #endif
00470 
00471         if (ret == -1) {
00472                 isc__strerror(errno, strbuf, sizeof(strbuf));
00473                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00474 #ifdef USE_FIONBIO_IOCTL
00475                                  "ioctl(%d, FIONBIO, &on): %s", fd,
00476 #else
00477                                  "fcntl(%d, F_SETFL, %d): %s", fd, flags,
00478 #endif
00479                                  strbuf);
00480 
00481                 return (ISC_R_UNEXPECTED);
00482         }
00483 
00484         return (ISC_R_SUCCESS);
00485 }
00486 
00487 isc_result_t
00488 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
00489         int fd;
00490         struct stat _stat;
00491         isc_boolean_t is_usocket = ISC_FALSE;
00492         isc_boolean_t is_connected = ISC_FALSE;
00493         isc_result_t ret;
00494         isc_entropysource_t *source;
00495 
00496         REQUIRE(VALID_ENTROPY(ent));
00497         REQUIRE(fname != NULL);
00498 
00499         LOCK(&ent->lock);
00500 
00501         if (stat(fname, &_stat) < 0) {
00502                 ret = isc__errno2result(errno);
00503                 goto errout;
00504         }
00505         /*
00506          * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
00507          * but it does return type S_IFIFO (the OS believes that
00508          * the socket is a fifo).  This may be an issue if we tell
00509          * the program to look at an actual FIFO as its source of
00510          * entropy.
00511          */
00512 #if defined(S_ISSOCK)
00513         if (S_ISSOCK(_stat.st_mode))
00514                 is_usocket = ISC_TRUE;
00515 #endif
00516 #if defined(S_ISFIFO) && defined(sun)
00517         if (S_ISFIFO(_stat.st_mode))
00518                 is_usocket = ISC_TRUE;
00519 #endif
00520         if (is_usocket)
00521                 fd = socket(PF_UNIX, SOCK_STREAM, 0);
00522         else
00523                 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
00524 
00525         if (fd < 0) {
00526                 ret = isc__errno2result(errno);
00527                 goto errout;
00528         }
00529 
00530         ret = make_nonblock(fd);
00531         if (ret != ISC_R_SUCCESS)
00532                 goto closefd;
00533 
00534         if (is_usocket) {
00535                 struct sockaddr_un sname;
00536 
00537                 memset(&sname, 0, sizeof(sname));
00538                 sname.sun_family = AF_UNIX;
00539                 strlcpy(sname.sun_path, fname, sizeof(sname.sun_path));
00540 #ifdef ISC_PLATFORM_HAVESALEN
00541 #if !defined(SUN_LEN)
00542 #define SUN_LEN(su) \
00543         (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
00544 #endif
00545                 sname.sun_len = SUN_LEN(&sname);
00546 #endif
00547 
00548                 if (connect(fd, (struct sockaddr *) &sname,
00549                             sizeof(struct sockaddr_un)) < 0) {
00550                         if (errno != EINPROGRESS) {
00551                                 ret = isc__errno2result(errno);
00552                                 goto closefd;
00553                         }
00554                 } else
00555                         is_connected = ISC_TRUE;
00556         }
00557 
00558         source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
00559         if (source == NULL) {
00560                 ret = ISC_R_NOMEMORY;
00561                 goto closefd;
00562         }
00563 
00564         /*
00565          * From here down, no failures can occur.
00566          */
00567         source->magic = SOURCE_MAGIC;
00568         source->ent = ent;
00569         source->total = 0;
00570         source->bad = ISC_FALSE;
00571         memset(source->name, 0, sizeof(source->name));
00572         ISC_LINK_INIT(source, link);
00573         if (is_usocket) {
00574                 source->sources.usocket.handle = fd;
00575                 if (is_connected)
00576                         source->sources.usocket.status =
00577                                         isc_usocketsource_connected;
00578                 else
00579                         source->sources.usocket.status =
00580                                         isc_usocketsource_connecting;
00581                 source->sources.usocket.sz_to_recv = 0;
00582                 source->type = ENTROPY_SOURCETYPE_USOCKET;
00583         } else {
00584                 source->sources.file.handle = fd;
00585                 source->type = ENTROPY_SOURCETYPE_FILE;
00586         }
00587 
00588         /*
00589          * Hook it into the entropy system.
00590          */
00591         ISC_LIST_APPEND(ent->sources, source, link);
00592         ent->nsources++;
00593 
00594         UNLOCK(&ent->lock);
00595         return (ISC_R_SUCCESS);
00596 
00597  closefd:
00598         (void)close(fd);
00599 
00600  errout:
00601         UNLOCK(&ent->lock);
00602 
00603         return (ret);
00604 }

Generated on Tue Apr 28 17:41:04 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha