"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/lib/isc/unix/entropy.c" (7 Sep 2020, 13703 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "entropy.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 /* \file unix/entropy.c
   13  * \brief
   14  * This is the system dependent part of the ISC entropy API.
   15  */
   16 
   17 #include <config.h>
   18 
   19 #include <stdbool.h>
   20 
   21 #include <sys/param.h>  /* Openserver 5.0.6A and FD_SETSIZE */
   22 #include <sys/types.h>
   23 #include <sys/time.h>
   24 #include <sys/stat.h>
   25 #include <sys/socket.h>
   26 #include <sys/un.h>
   27 
   28 #ifdef HAVE_NANOSLEEP
   29 #include <time.h>
   30 #endif
   31 #include <unistd.h>
   32 
   33 #include <isc/platform.h>
   34 #include <isc/print.h>
   35 #include <isc/strerror.h>
   36 #include <isc/string.h>
   37 
   38 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
   39 #include <sys/select.h>
   40 #endif
   41 
   42 #include "errno2result.h"
   43 
   44 /*%
   45  * There is only one variable in the entropy data structures that is not
   46  * system independent, but pulling the structure that uses it into this file
   47  * ultimately means pulling several other independent structures here also to
   48  * resolve their interdependencies.  Thus only the problem variable's type
   49  * is defined here.
   50  */
   51 #define FILESOURCE_HANDLE_TYPE  int
   52 
   53 typedef struct {
   54     int handle;
   55     enum    {
   56         isc_usocketsource_disconnected,
   57         isc_usocketsource_connecting,
   58         isc_usocketsource_connected,
   59         isc_usocketsource_ndesired,
   60         isc_usocketsource_wrote,
   61         isc_usocketsource_reading
   62     } status;
   63     size_t  sz_to_recv;
   64 } isc_entropyusocketsource_t;
   65 
   66 #include "../entropy.c"
   67 
   68 static unsigned int
   69 get_from_filesource(isc_entropysource_t *source, uint32_t desired) {
   70     isc_entropy_t *ent = source->ent;
   71     unsigned char buf[128];
   72     int fd = source->sources.file.handle;
   73     ssize_t n, ndesired;
   74     unsigned int added;
   75 
   76     if (source->bad)
   77         return (0);
   78 
   79     desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
   80 
   81     added = 0;
   82     while (desired > 0) {
   83         ndesired = ISC_MIN(desired, sizeof(buf));
   84         n = read(fd, buf, ndesired);
   85         if (n < 0) {
   86             if (errno == EAGAIN || errno == EINTR)
   87                 goto out;
   88             goto err;
   89         }
   90         if (n == 0)
   91             goto err;
   92 
   93         entropypool_adddata(ent, buf, n, n * 8);
   94         added += n * 8;
   95         desired -= n;
   96     }
   97     goto out;
   98 
   99  err:
  100     (void)close(fd);
  101     source->sources.file.handle = -1;
  102     source->bad = true;
  103 
  104  out:
  105     return (added);
  106 }
  107 
  108 static unsigned int
  109 get_from_usocketsource(isc_entropysource_t *source, uint32_t desired) {
  110     isc_entropy_t *ent = source->ent;
  111     unsigned char buf[128];
  112     int fd = source->sources.usocket.handle;
  113     ssize_t n = 0, ndesired;
  114     unsigned int added;
  115     size_t sz_to_recv = source->sources.usocket.sz_to_recv;
  116 
  117     if (source->bad)
  118         return (0);
  119 
  120     desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
  121 
  122     added = 0;
  123     while (desired > 0) {
  124         ndesired = ISC_MIN(desired, sizeof(buf));
  125  eagain_loop:
  126 
  127         switch ( source->sources.usocket.status ) {
  128         case isc_usocketsource_ndesired:
  129             buf[0] = ndesired;
  130             if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
  131                 if (errno == EWOULDBLOCK || errno == EINTR ||
  132                     errno == ECONNRESET)
  133                     goto out;
  134                 goto err;
  135             }
  136             INSIST(n == 1);
  137             source->sources.usocket.status =
  138                         isc_usocketsource_wrote;
  139             goto eagain_loop;
  140 
  141         case isc_usocketsource_connecting:
  142         case isc_usocketsource_connected:
  143             buf[0] = 1;
  144             buf[1] = ndesired;
  145             if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
  146                 if (errno == EWOULDBLOCK || errno == EINTR ||
  147                     errno == ECONNRESET)
  148                     goto out;
  149                 goto err;
  150             }
  151             if (n == 1) {
  152                 source->sources.usocket.status =
  153                     isc_usocketsource_ndesired;
  154                 goto eagain_loop;
  155             }
  156             INSIST(n == 2);
  157             source->sources.usocket.status =
  158                         isc_usocketsource_wrote;
  159             /* FALLTHROUGH */
  160 
  161         case isc_usocketsource_wrote:
  162             if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
  163                 if (errno == EAGAIN) {
  164                     /*
  165                      * The problem of EAGAIN (try again
  166                      * later) is a major issue on HP-UX.
  167                      * Solaris actually tries the recvfrom
  168                      * call again, while HP-UX just dies.
  169                      * This code is an attempt to let the
  170                      * entropy pool fill back up (at least
  171                      * that's what I think the problem is.)
  172                      * We go to eagain_loop because if we
  173                      * just "break", then the "desired"
  174                      * amount gets borked.
  175                      */
  176 #ifdef HAVE_NANOSLEEP
  177                     struct timespec ts;
  178 
  179                     ts.tv_sec = 0;
  180                     ts.tv_nsec = 1000000;
  181                     nanosleep(&ts, NULL);
  182 #else
  183                     usleep(1000);
  184 #endif
  185                     goto eagain_loop;
  186                 }
  187                 if (errno == EWOULDBLOCK || errno == EINTR)
  188                     goto out;
  189                 goto err;
  190             }
  191             source->sources.usocket.status =
  192                     isc_usocketsource_reading;
  193             sz_to_recv = buf[0];
  194             source->sources.usocket.sz_to_recv = sz_to_recv;
  195             if (sz_to_recv > sizeof(buf))
  196                 goto err;
  197             /* FALLTHROUGH */
  198 
  199         case isc_usocketsource_reading:
  200             if (sz_to_recv != 0U) {
  201                 n = recv(fd, buf, sz_to_recv, 0);
  202                 if (n < 0) {
  203                     if (errno == EWOULDBLOCK ||
  204                         errno == EINTR)
  205                         goto out;
  206                     goto err;
  207                 }
  208             } else
  209                 n = 0;
  210             break;
  211 
  212         default:
  213             goto err;
  214         }
  215 
  216         if ((size_t)n != sz_to_recv)
  217             source->sources.usocket.sz_to_recv -= n;
  218         else
  219             source->sources.usocket.status =
  220                 isc_usocketsource_connected;
  221 
  222         if (n == 0)
  223             goto out;
  224 
  225         entropypool_adddata(ent, buf, n, n * 8);
  226         added += n * 8;
  227         desired -= n;
  228     }
  229     goto out;
  230 
  231  err:
  232     close(fd);
  233     source->bad = true;
  234     source->sources.usocket.status = isc_usocketsource_disconnected;
  235     source->sources.usocket.handle = -1;
  236 
  237  out:
  238     return (added);
  239 }
  240 
  241 /*
  242  * Poll each source, trying to get data from it to stuff into the entropy
  243  * pool.
  244  */
  245 static void
  246 fillpool(isc_entropy_t *ent, unsigned int desired, bool blocking) {
  247     unsigned int added;
  248     unsigned int remaining;
  249     unsigned int needed;
  250     unsigned int nsource;
  251     isc_entropysource_t *source;
  252 
  253     REQUIRE(VALID_ENTROPY(ent));
  254 
  255     needed = desired;
  256 
  257     /*
  258      * This logic is a little strange, so an explanation is in order.
  259      *
  260      * If needed is 0, it means we are being asked to "fill to whatever
  261      * we think is best."  This means that if we have at least a
  262      * partially full pool (say, > 1/4th of the pool) we probably don't
  263      * need to add anything.
  264      *
  265      * Also, we will check to see if the "pseudo" count is too high.
  266      * If it is, try to mix in better data.  Too high is currently
  267      * defined as 1/4th of the pool.
  268      *
  269      * Next, if we are asked to add a specific bit of entropy, make
  270      * certain that we will do so.  Clamp how much we try to add to
  271      * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
  272      *
  273      * Note that if we are in a blocking mode, we will only try to
  274      * get as much data as we need, not as much as we might want
  275      * to build up.
  276      */
  277     if (needed == 0) {
  278         REQUIRE(!blocking);
  279 
  280         if ((ent->pool.entropy >= RND_POOLBITS / 4)
  281             && (ent->pool.pseudo <= RND_POOLBITS / 4))
  282             return;
  283 
  284         needed = THRESHOLD_BITS * 4;
  285     } else {
  286         needed = ISC_MAX(needed, THRESHOLD_BITS);
  287         needed = ISC_MIN(needed, RND_POOLBITS);
  288     }
  289 
  290     /*
  291      * In any case, clamp how much we need to how much we can add.
  292      */
  293     needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
  294 
  295     /*
  296      * But wait!  If we're not yet initialized, we need at least
  297      *  THRESHOLD_BITS
  298      * of randomness.
  299      */
  300     if (ent->initialized < THRESHOLD_BITS)
  301         needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
  302 
  303     /*
  304      * Poll each file source to see if we can read anything useful from
  305      * it.  XXXMLG When where are multiple sources, we should keep a
  306      * record of which one we last used so we can start from it (or the
  307      * next one) to avoid letting some sources build up entropy while
  308      * others are always drained.
  309      */
  310 
  311     added = 0;
  312     remaining = needed;
  313     if (ent->nextsource == NULL) {
  314         ent->nextsource = ISC_LIST_HEAD(ent->sources);
  315         if (ent->nextsource == NULL)
  316             return;
  317     }
  318     source = ent->nextsource;
  319  again_file:
  320     for (nsource = 0; nsource < ent->nsources; nsource++) {
  321         unsigned int got;
  322 
  323         if (remaining == 0)
  324             break;
  325 
  326         got = 0;
  327 
  328         switch ( source->type ) {
  329         case ENTROPY_SOURCETYPE_FILE:
  330             got = get_from_filesource(source, remaining);
  331             break;
  332 
  333         case ENTROPY_SOURCETYPE_USOCKET:
  334             got = get_from_usocketsource(source, remaining);
  335             break;
  336         }
  337 
  338         added += got;
  339 
  340         remaining -= ISC_MIN(remaining, got);
  341 
  342         source = ISC_LIST_NEXT(source, link);
  343         if (source == NULL)
  344             source = ISC_LIST_HEAD(ent->sources);
  345     }
  346     ent->nextsource = source;
  347 
  348     if (blocking && remaining != 0) {
  349         int fds;
  350 
  351         fds = wait_for_sources(ent);
  352         if (fds > 0)
  353             goto again_file;
  354     }
  355 
  356     /*
  357      * Here, if there are bits remaining to be had and we can block,
  358      * check to see if we have a callback source.  If so, call them.
  359      */
  360     source = ISC_LIST_HEAD(ent->sources);
  361     while ((remaining != 0) && (source != NULL)) {
  362         unsigned int got;
  363 
  364         got = 0;
  365 
  366         if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
  367             got = get_from_callback(source, remaining, blocking);
  368 
  369         added += got;
  370         remaining -= ISC_MIN(remaining, got);
  371 
  372         if (added >= needed)
  373             break;
  374 
  375         source = ISC_LIST_NEXT(source, link);
  376     }
  377 
  378     /*
  379      * Mark as initialized if we've added enough data.
  380      */
  381     if (ent->initialized < THRESHOLD_BITS)
  382         ent->initialized += added;
  383 }
  384 
  385 static int
  386 wait_for_sources(isc_entropy_t *ent) {
  387     isc_entropysource_t *source;
  388     int maxfd, fd;
  389     int cc;
  390     fd_set reads;
  391     fd_set writes;
  392 
  393     maxfd = -1;
  394     FD_ZERO(&reads);
  395     FD_ZERO(&writes);
  396 
  397     source = ISC_LIST_HEAD(ent->sources);
  398     while (source != NULL) {
  399         if (source->type == ENTROPY_SOURCETYPE_FILE) {
  400             fd = source->sources.file.handle;
  401             if (fd >= 0) {
  402                 maxfd = ISC_MAX(maxfd, fd);
  403                 FD_SET(fd, &reads);
  404             }
  405         }
  406         if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
  407             fd = source->sources.usocket.handle;
  408             if (fd >= 0) {
  409                 switch (source->sources.usocket.status) {
  410                 case isc_usocketsource_disconnected:
  411                     break;
  412                 case isc_usocketsource_connecting:
  413                 case isc_usocketsource_connected:
  414                 case isc_usocketsource_ndesired:
  415                     maxfd = ISC_MAX(maxfd, fd);
  416                     FD_SET(fd, &writes);
  417                     break;
  418                 case isc_usocketsource_wrote:
  419                 case isc_usocketsource_reading:
  420                     maxfd = ISC_MAX(maxfd, fd);
  421                     FD_SET(fd, &reads);
  422                     break;
  423                 }
  424             }
  425         }
  426         source = ISC_LIST_NEXT(source, link);
  427     }
  428 
  429     if (maxfd < 0)
  430         return (-1);
  431 
  432     cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
  433     if (cc < 0)
  434         return (-1);
  435 
  436     return (cc);
  437 }
  438 
  439 static void
  440 destroyfilesource(isc_entropyfilesource_t *source) {
  441     (void)close(source->handle);
  442 }
  443 
  444 static void
  445 destroyusocketsource(isc_entropyusocketsource_t *source) {
  446     close(source->handle);
  447 }
  448 
  449 /*
  450  * Make a fd non-blocking
  451  */
  452 static isc_result_t
  453 make_nonblock(int fd) {
  454     int ret;
  455     char strbuf[ISC_STRERRORSIZE];
  456 #ifdef USE_FIONBIO_IOCTL
  457     int on = 1;
  458 #else
  459     int flags;
  460 #endif
  461 
  462 #ifdef USE_FIONBIO_IOCTL
  463     ret = ioctl(fd, FIONBIO, (char *)&on);
  464 #else
  465     flags = fcntl(fd, F_GETFL, 0);
  466     flags |= PORT_NONBLOCK;
  467     ret = fcntl(fd, F_SETFL, flags);
  468 #endif
  469 
  470     if (ret == -1) {
  471         isc__strerror(errno, strbuf, sizeof(strbuf));
  472         UNEXPECTED_ERROR(__FILE__, __LINE__,
  473 #ifdef USE_FIONBIO_IOCTL
  474                  "ioctl(%d, FIONBIO, &on): %s", fd,
  475 #else
  476                  "fcntl(%d, F_SETFL, %d): %s", fd, flags,
  477 #endif
  478                  strbuf);
  479 
  480         return (ISC_R_UNEXPECTED);
  481     }
  482 
  483     return (ISC_R_SUCCESS);
  484 }
  485 
  486 isc_result_t
  487 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
  488     int fd;
  489     struct stat _stat;
  490     bool is_usocket = false;
  491     bool is_connected = false;
  492     isc_result_t ret;
  493     isc_entropysource_t *source;
  494 
  495     REQUIRE(VALID_ENTROPY(ent));
  496     REQUIRE(fname != NULL);
  497 
  498     LOCK(&ent->lock);
  499 
  500     if (stat(fname, &_stat) < 0) {
  501         ret = isc__errno2result(errno);
  502         goto errout;
  503     }
  504     /*
  505      * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
  506      * but it does return type S_IFIFO (the OS believes that
  507      * the socket is a fifo).  This may be an issue if we tell
  508      * the program to look at an actual FIFO as its source of
  509      * entropy.
  510      */
  511 #if defined(S_ISSOCK)
  512     if (S_ISSOCK(_stat.st_mode))
  513         is_usocket = true;
  514 #endif
  515 #if defined(S_ISFIFO) && defined(sun)
  516     if (S_ISFIFO(_stat.st_mode))
  517         is_usocket = true;
  518 #endif
  519     if (is_usocket)
  520         fd = socket(PF_UNIX, SOCK_STREAM, 0);
  521     else
  522         fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
  523 
  524     if (fd < 0) {
  525         ret = isc__errno2result(errno);
  526         goto errout;
  527     }
  528 
  529     ret = make_nonblock(fd);
  530     if (ret != ISC_R_SUCCESS)
  531         goto closefd;
  532 
  533     if (is_usocket) {
  534         struct sockaddr_un sname;
  535 
  536         memset(&sname, 0, sizeof(sname));
  537         sname.sun_family = AF_UNIX;
  538         strlcpy(sname.sun_path, fname, sizeof(sname.sun_path));
  539 #ifdef ISC_PLATFORM_HAVESALEN
  540 #if !defined(SUN_LEN)
  541 #define SUN_LEN(su) \
  542     (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
  543 #endif
  544         sname.sun_len = SUN_LEN(&sname);
  545 #endif
  546 
  547         if (connect(fd, (struct sockaddr *) &sname,
  548                 sizeof(struct sockaddr_un)) < 0) {
  549             if (errno != EINPROGRESS) {
  550                 ret = isc__errno2result(errno);
  551                 goto closefd;
  552             }
  553         } else
  554             is_connected = true;
  555     }
  556 
  557     source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
  558     if (source == NULL) {
  559         ret = ISC_R_NOMEMORY;
  560         goto closefd;
  561     }
  562 
  563     /*
  564      * From here down, no failures can occur.
  565      */
  566     source->magic = SOURCE_MAGIC;
  567     source->ent = ent;
  568     source->total = 0;
  569     source->bad = false;
  570     memset(source->name, 0, sizeof(source->name));
  571     ISC_LINK_INIT(source, link);
  572     if (is_usocket) {
  573         source->sources.usocket.handle = fd;
  574         if (is_connected)
  575             source->sources.usocket.status =
  576                     isc_usocketsource_connected;
  577         else
  578             source->sources.usocket.status =
  579                     isc_usocketsource_connecting;
  580         source->sources.usocket.sz_to_recv = 0;
  581         source->type = ENTROPY_SOURCETYPE_USOCKET;
  582     } else {
  583         source->sources.file.handle = fd;
  584         source->type = ENTROPY_SOURCETYPE_FILE;
  585     }
  586 
  587     /*
  588      * Hook it into the entropy system.
  589      */
  590     ISC_LIST_APPEND(ent->sources, source, link);
  591     ent->nsources++;
  592 
  593     UNLOCK(&ent->lock);
  594     return (ISC_R_SUCCESS);
  595 
  596  closefd:
  597     (void)close(fd);
  598 
  599  errout:
  600     UNLOCK(&ent->lock);
  601 
  602     return (ret);
  603 }