blocxx

SecureRand.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00039 #include "blocxx/BLOCXX_config.h"
00040 
00041 #ifdef BLOCXX_HAVE_OPENSSL
00042 // If you don't have SSL, you don't have cryptographically secure random
00043 // numbers.  Don't try to fall back to a weaker PRNG, as this violates the
00044 // security principle of "fail safe".
00045 
00046 #include "blocxx/Array.hpp"
00047 #include "blocxx/Assertion.hpp"
00048 #include "blocxx/Exec.hpp"
00049 #include "blocxx/FileSystem.hpp"
00050 #include "blocxx/Mutex.hpp"
00051 #include "blocxx/MutexLock.hpp"
00052 #include "blocxx/GlobalMutex.hpp"
00053 #include "blocxx/Secure.hpp"
00054 #include "blocxx/SecureRand.hpp"
00055 #include "blocxx/SSLCtxMgr.hpp"
00056 #include "blocxx/String.hpp"
00057 #include "blocxx/Thread.hpp"
00058 #include "blocxx/ThreadOnce.hpp"
00059 #include "blocxx/UnnamedPipe.hpp"
00060 #include "blocxx/UserUtils.hpp"
00061 #include "blocxx/Process.hpp"
00062 
00063 #include <cmath>
00064 #include <csignal>
00065 #include <cstring>  // for std::memset
00066 #include <limits>
00067 #ifdef BLOCXX_HAVE_UNISTD_H
00068 #include <unistd.h>
00069 #endif
00070 
00071 #include <fcntl.h>
00072 #include <openssl/rand.h>
00073 #include <openssl/err.h>
00074 #ifdef BLOCXX_HAVE_SYS_RESOURCE_H
00075 #include <sys/resource.h>
00076 #endif
00077 #ifndef BLOCXX_WIN32
00078 #include <sys/time.h>
00079 #endif
00080 
00081 #ifdef BLOCXX_WIN32
00082 #include <wincrypt.h>
00083 #endif
00084 
00085 using namespace blocxx;
00086 
00087 namespace
00088 {
00089    unsigned const RESEED_BYTES = 16; // 128 bits
00090    unsigned const SEED_BYTES   = 16; // 128 bits
00091 
00092    template <typename T> struct unsigned_equivalent
00093    {
00094       typedef T type;
00095    };
00096 
00097    template <> struct unsigned_equivalent<char>
00098    {
00099       typedef unsigned char type;
00100    };
00101 
00102    template <> struct unsigned_equivalent<signed char>
00103    {
00104       typedef unsigned char type;
00105    };
00106 
00107    template <> struct unsigned_equivalent<short>
00108    {
00109       typedef unsigned short type;
00110    };
00111 
00112    template <> struct unsigned_equivalent<int>
00113    {
00114       typedef unsigned int type;
00115    };
00116 
00117    template <> struct unsigned_equivalent<long>
00118    {
00119       typedef unsigned long type;
00120    };
00121 
00122    template <> struct unsigned_equivalent<long long>
00123    {
00124       typedef unsigned long long type;
00125    };
00126 
00127    blocxx::OnceFlag guard = BLOCXX_ONCE_INIT;
00128 
00129    void rand_init_impl();
00130 }
00131 
00132 namespace BLOCXX_NAMESPACE
00133 {
00134 BLOCXX_DEFINE_EXCEPTION(SecureRand);
00135 
00136 namespace Secure
00137 {
00138    void rand_init()
00139    {
00140       callOnce(guard, &rand_init_impl);
00141    }
00142 
00143    unsigned char * rand(unsigned char * buf, std::size_t n)
00144    {
00145       callOnce(guard, &rand_init_impl);
00146       ERR_clear_error();
00147       if (!RAND_bytes(buf, n))
00148       {
00149          BLOCXX_THROW(SecureRandException,
00150             SSLCtxMgr::getOpenSSLErrorDescription().c_str());
00151       }
00152       return buf;
00153    }
00154 
00155    ::pid_t fork_reseed()
00156    {
00157 #ifdef BLOCXX_WIN32
00158 #pragma message(Reminder "TODO: implement it for Win!")
00159 
00160       return BLOCXX_INVALID_HANDLE;
00161 #else
00162       unsigned char seed[2][RESEED_BYTES];
00163       rand(seed[0], sizeof(seed[0]));
00164       rand(seed[1], sizeof(seed[1]));
00165 
00166       ::pid_t rv = ::fork();
00167       if (rv < 0)
00168       {
00169          return rv;
00170       }
00171 
00172       std::size_t idx = rv > 0;  // 0 or 1
00173       RAND_seed(seed[idx], sizeof(seed[idx]));
00174       // forget other process's seed
00175       std::memset(seed[1 - idx], 0, sizeof(seed[1- idx]));
00176 
00177       return rv;
00178 #endif
00179    }
00180 
00181    namespace Impl
00182    {
00183       template <typename UnsignedInt>
00184       UnsignedInt rand_uint_lt(UnsignedInt n)
00185       {
00186          BLOCXX_ASSERT(n > 0);
00187          if ((n & (n - 1)) == 0) // n is a power of two
00188          {
00189             return rand_uint<UnsignedInt>() % n;
00190          }
00191          UnsignedInt const uint_max = static_cast<UnsignedInt>(-1);
00192          UnsignedInt const bound = uint_max - (uint_max % n);
00193          UnsignedInt rn;
00194          do
00195          {
00196             rn = rand_uint<UnsignedInt>();
00197          } while (rn >= bound);
00198          return rn % n;
00199       }
00200 
00201       // Explicit instantiation
00202       template unsigned char
00203          BLOCXX_COMMON_API rand_uint_lt<unsigned char>(unsigned char);
00204       template unsigned short
00205          BLOCXX_COMMON_API rand_uint_lt<unsigned short>(unsigned short);
00206       template unsigned int
00207          BLOCXX_COMMON_API rand_uint_lt<unsigned int>(unsigned int);
00208       template unsigned long
00209          BLOCXX_COMMON_API rand_uint_lt<unsigned long>(unsigned long);
00210       template unsigned long long
00211          BLOCXX_COMMON_API rand_uint_lt<unsigned long long>(unsigned long long);
00212 
00213       template <typename Integer>
00214       Integer rand_range(Integer min_value, Integer max_value)
00215       {
00216          BLOCXX_ASSERT(max_value >= min_value);
00217 
00218          // The following code uses these properties of C++:
00219          // - Conversions from a signed integer to an unsigned integer
00220          //   of the same size are always well-defined.
00221          // - Arithmetic for unsigned integers is module 2^n, where n
00222          //   is the number of bits.
00223          // - If signed integer k is negative, then converting it to
00224          //   the equivalent unsigned integer yields 2^n + k.
00225 
00226          typedef typename unsigned_equivalent<Integer>::type UnsignedInt;
00227          UnsignedInt const umax = static_cast<UnsignedInt>(max_value);
00228          UnsignedInt const umin = static_cast<UnsignedInt>(min_value);
00229          UnsignedInt const diff = umax - umin;
00230 
00231          // diff is the mathematical difference between max_value
00232          // and min_value, which may not be representable as an Integer,
00233          // but is guaranteed to be representable as an UnsignedInt.
00234 
00235          UnsignedInt const range = diff + static_cast<UnsignedInt>(1);
00236 
00237          // range == 0 iff every UnsignedInt value corresponds to
00238          // a unique Integer value (e.g., two's complement representation
00239          // instead of sign-magnitude), min_value is the smallest possible
00240          // Integer value, and max_value is the largest possible Integer
00241          // value.
00242 
00243          UnsignedInt rv;
00244          if (range == 0)
00245          {
00246             // All Integer values are allowed return values, and there
00247             // is a one-to-one mapping from UnsignedInt values to
00248             // Integer values.
00249             rv = rand_uint<UnsignedInt>();
00250          }
00251          else
00252          {
00253             // Compute the UnsignedInt value corresponding to the desired
00254             // Integer value.  This works even if min_value < 0 and
00255             // max_value >= 0, because the arithmetic is module 2^n.
00256             rv = umin + rand_uint_lt(range);
00257          }
00258          return static_cast<Integer>(rv);
00259       }
00260 
00261       // explicit instantiations
00262       template char
00263          BLOCXX_COMMON_API rand_range(char, char);
00264       template signed char
00265          BLOCXX_COMMON_API rand_range(signed char, signed char);
00266       template unsigned char
00267          BLOCXX_COMMON_API rand_range(unsigned char, unsigned char);
00268       template short
00269          BLOCXX_COMMON_API rand_range(short, short);
00270       template unsigned short
00271          BLOCXX_COMMON_API rand_range(unsigned short, unsigned short);
00272       template int
00273          BLOCXX_COMMON_API rand_range(int, int);
00274       template unsigned int
00275          BLOCXX_COMMON_API rand_range(unsigned int, unsigned int);
00276       template long
00277          BLOCXX_COMMON_API rand_range(long, long);
00278       template unsigned long
00279          BLOCXX_COMMON_API rand_range(unsigned long, unsigned long);
00280       template long long
00281          BLOCXX_COMMON_API rand_range(long long, long long);
00282       template unsigned long long
00283          BLOCXX_COMMON_API rand_range(unsigned long long, unsigned long long);
00284       
00285       template <unsigned int N>
00286       struct log2
00287       {
00288          enum { value = 1 + log2<N/2>::value };
00289       };
00290 
00291       template <>
00292       struct log2<1>
00293       {
00294          enum { value = 0 };
00295       };
00296       
00297       // # of mantissa bits if Number is a floating-point type.
00298       template <typename Number>
00299       struct bits_precision
00300       {
00301          typedef std::numeric_limits<Number> lim_t;
00302          enum { value = lim_t::digits * log2<lim_t::radix>::value };
00303       };
00304 
00305       template <typename Real>
00306       Real rand_unit_interval()
00307       {
00308          typedef UInt32 uint_t;
00309          int const UINT_BITS = 32;
00310          int const NUINT =
00311             (bits_precision<Real>::value + UINT_BITS - 1) / UINT_BITS;
00312          Real rv = 0.0;
00313          for (int i = 1; i <= NUINT; ++i)
00314          {
00315             Real r = static_cast<Real>(rand_uint<uint_t>());
00316             rv += std::ldexp(r, -UINT_BITS * i);
00317          }
00318          return rv;
00319       }
00320 
00321       // explicit instantiations
00322       template float rand_unit_interval<float>();
00323       template double rand_unit_interval<double>();
00324       template long double rand_unit_interval<long double>();
00325 
00326    } // namespace Impl
00327 
00328    void rand_save_state()
00329    {
00330       char randFile[MAXPATHLEN];
00331       char const * rval = RAND_file_name(randFile, MAXPATHLEN);
00332       if (rval)
00333       {
00334          // we only create this file is there's no chance an attacker
00335          // could read or write it. see Network Security with OpenSSL p. 101
00336          using namespace FileSystem::Path;
00337          if (security(dirname(randFile)).first == E_SECURE_DIR)
00338          {
00339             if (RAND_write_file(randFile) <= 0)
00340             {
00341                // in case "the bytes written were generated without
00342                // appropriate seed.", we don't want to load it up next
00343                // time.
00344                FileSystem::removeFile(randFile);
00345             }
00346          }
00347       }
00348 
00349    }
00350 
00351 } // namespace Secure
00352 } // namespace BLOCXX_NAMESPACE
00353 
00354 namespace
00355 {
00356    // These are used to generate random data via signal delivery timing
00357    // differences.  We have to use global data since it's modified from a
00358    // signal handler.
00359    volatile sig_atomic_t g_counter;
00360    volatile unsigned char* g_data;
00361    volatile sig_atomic_t g_dataIdx;
00362    int g_dataSize;
00363 }
00364 
00365 extern "C"
00366 {
00367    // this needs to still be static, since it gets exported because of
00368    // extern "C"
00369 #ifdef BLOCXX_NCR
00370    static void randomALRMHandler()
00371 #else
00372    static void randomALRMHandler(int sig)
00373 #endif
00374    {
00375       if (g_dataIdx < g_dataSize)
00376       {
00377          g_data[g_dataIdx++] ^= g_counter & 0xFF;
00378       }
00379    }
00380 }
00381 
00382 namespace
00383 {
00384    GlobalMutex g_randomTimerGuard = BLOCXX_GLOBAL_MUTEX_INIT();
00385 
00386 #ifndef BLOCXX_WIN32
00387    // This function will continue to iterate until *iterations <= 0.
00388    // *iterations may be set by another thread. *iterations should not be < 8.
00389    void generateRandomTimerData(unsigned char* data, int size, int* iterations)
00390    {
00391       BLOCXX_ASSERT(data != 0);
00392       BLOCXX_ASSERT(size > 0);
00393       BLOCXX_ASSERT(iterations != 0);
00394 
00395       // make sure we only have one thread running this at a time.
00396       MutexLock l(g_randomTimerGuard);
00397 
00398       // set up the global data for the signal handler
00399       g_data = data;
00400       g_dataSize = size;
00401       g_dataIdx = 0;
00402 
00403       // install our ALRM handler
00404       struct sigaction sa, osa;
00405       sa.sa_handler = randomALRMHandler;
00406       sa.sa_flags = 0;
00407       sigemptyset(&sa.sa_mask);
00408       sigaction(SIGALRM, &sa, &osa);
00409 
00410       // Start timer
00411       struct ::itimerval tv, otv;
00412       tv.it_value.tv_sec = 0;
00413       tv.it_value.tv_usec = 10 * 1000; // 10 ms
00414       tv.it_interval = tv.it_value;
00415       setitimer(ITIMER_REAL, &tv, &otv);
00416    
00417       while ((*iterations)-- > 0)
00418       {
00419          // g_dataIdx++ in sigALRM
00420          for (g_dataIdx = 0; g_dataIdx < g_dataSize;)
00421          {
00422             ++g_counter;
00423          }
00424          // rotate the bits to accomodate for a possible lack of
00425          // low-bit entropy
00426          for (int j = 0; j < g_dataSize; j++)
00427          {
00428             g_data[j] = (g_data[j]>>3) | (g_data[j]<<5);
00429          }
00430       }
00431       setitimer(ITIMER_REAL, &otv, 0);
00432 
00433       // reset signal handler
00434       sigaction(SIGALRM, &osa, 0);
00435 
00436    }
00437 
00438    void generateRandomDataFromFile(const char* name, int len)
00439    {
00440       int fd = ::open(name, O_RDONLY);
00441       if (fd == -1)
00442       {
00443          return;
00444       }
00445 
00446       std::vector<char> buf(len);
00447       int bytesRead = ::read(fd, &buf[0], len);
00448       if (bytesRead == -1)
00449       {
00450          return;
00451       }
00452       buf.resize(bytesRead);
00453       ::RAND_add(&buf[0], buf.size(), 0.0);
00454       // 0 entropy, since this could all be observable by someone else.
00455    }
00456 
00457    void generateRandomDataFromTime(double entropy)
00458    {
00459       struct timeval tv;
00460       ::gettimeofday(&tv, 0);
00461       ::RAND_add(&tv, sizeof(tv), entropy);
00462 
00463       clock_t c(::clock());
00464       ::RAND_add(&c, sizeof(c), entropy);
00465 
00466       struct rusage ru;
00467       ::getrusage(RUSAGE_SELF, &ru);
00468       ::RAND_add(&ru, sizeof(ru), entropy);
00469 
00470       ::getrusage(RUSAGE_CHILDREN, &ru);
00471       ::RAND_add(&ru, sizeof(ru), entropy);
00472    }
00473 
00474    struct cmd
00475    {
00476       const char* command;
00477 
00478       // estimated number of bytes of entropy per 1K of output
00479       double usefulness;
00480    };
00481 
00482    // This list of sources comes from gnupg, prngd and egd.
00483    const cmd randomSourceCommands[] =
00484    {
00485       { "advfsstat -b usr_domain", 0.01 },
00486       { "advfsstat -l 2 usr_domain", 0.5 },
00487       { "advfsstat -p usr_domain", 0.01 },
00488       { "arp -a -n", 0.5 },
00489       { "df", 0.5 },
00490       { "df -i", 0.5 },
00491       { "df -a", 0.5 },
00492       { "df -in", 0.5 },
00493       { "dmesg", 0.5 },
00494       { "errpt -a", 0.5 },
00495       { "ifconfig -a", 0.5 },
00496       { "iostat", 0.5 },
00497       { "ipcs -a", 0.5 },
00498       { "last", 0.5 },
00499       { "lastlog", 0.5 },
00500       { "lpstat -t", 0.1 },
00501       { "ls -alniR /var/log", 1.0 },
00502       { "ls -alniR /var/adm", 1.0 },
00503       { "ls -alni /var/spool/mail", 1.0 },
00504       { "ls -alni /proc", 1.0 },
00505       { "ls -alniR /tmp", 1.0 },
00506       { "ls -alniR /var/tmp", 1.0 },
00507       { "ls -alni /var/mail", 1.0 },
00508       { "ls -alniR /var/db", 1.0 },
00509       { "ls -alniR /etc", 1.0 },
00510       { "ls -alniR /private/var/log", 1.0 },
00511       { "ls -alniR /private/var/db", 1.0 },
00512       { "ls -alniR /private/etc", 1.0 },
00513       { "ls -alniR /private/tmp", 1.0 },
00514       { "ls -alniR /private/var/tmp", 1.0 },
00515       { "mpstat", 1.5 },
00516       { "netstat -s", 1.5 },
00517       { "netstat -n", 1.5 },
00518       { "netstat -a -n", 1.5 },
00519       { "netstat -anv", 1.5 },
00520       { "netstat -i -n", 0.5 },
00521       { "netstat -r -n", 0.1 },
00522       { "netstat -m", 0.5 },
00523       { "netstat -ms", 0.5 },
00524       { "nfsstat", 0.5 },
00525       { "ps laxww", 1.5 },
00526       { "ps -laxww", 1.5 },
00527       { "ps -al", 1.5 },
00528       { "ps -el", 1.5 },
00529       { "ps -efl", 1.5 },
00530       { "ps -efly", 1.5 },
00531       { "ps aux", 1.5 },
00532       { "ps -A", 1.5 },
00533       { "pfstat", 0.5 },
00534       { "portstat", 0.5 },
00535       { "pstat -p", 0.5 },
00536       { "pstat -S", 0.5 },
00537       { "pstat -A", 0.5 },
00538       { "pstat -t", 0.5 },
00539       { "pstat -v", 0.5 },
00540       { "pstat -x", 0.5 },
00541       { "pstat -t", 0.5 },
00542       { "ripquery -nw 1 127.0.0.1", 0.5 },
00543       { "sar -A 1 1", 0.5 },
00544       { "snmp_request localhost public get 1.3.6.1.2.1.7.1.0", 0.5 },
00545       { "snmp_request localhost public get 1.3.6.1.2.1.7.4.0", 0.5 },
00546       { "snmp_request localhost public get 1.3.6.1.2.1.4.3.0", 0.5 },
00547       { "snmp_request localhost public get 1.3.6.1.2.1.6.10.0", 0.5 },
00548       { "snmp_request localhost public get 1.3.6.1.2.1.6.11.0", 0.5 },
00549       { "snmp_request localhost public get 1.3.6.1.2.1.6.13.0", 0.5 },
00550       { "snmp_request localhost public get 1.3.6.1.2.1.5.1.0", 0.5 },
00551       { "snmp_request localhost public get 1.3.6.1.2.1.5.3.0", 0.5 },
00552       { "tail -c 1024 /var/log/messages", 1.0 },
00553       { "tail -c 1024 /var/log/syslog", 1.0 },
00554       { "tail -c 1024 /var/log/system.log", 1.0 },
00555       { "tail -c 1024 /var/log/debug", 1.0 },
00556       { "tail -c 1024 /var/adm/messages", 1.0 },
00557       { "tail -c 1024 /var/adm/syslog", 1.0 },
00558       { "tail -c 1024 /var/adm/syslog/mail.log", 1.0 },
00559       { "tail -c 1024 /var/adm/syslog/syslog.log", 1.0 },
00560       { "tail -c 1024 /var/log/maillog", 1.0 },
00561       { "tail -c 1024 /var/adm/maillog", 1.0 },
00562       { "tail -c 1024 /var/adm/SPlogs/SPdaemon.log", 1.0 },
00563       { "tail -c 1024 /usr/es/adm/cluster.log", 1.0 },
00564       { "tail -c 1024 /usr/adm/cluster.log", 1.0 },
00565       { "tail -c 1024 /var/adm/cluster.log", 1.0 },
00566       { "tail -c 1024 /var/adm/ras/conslog", 1.0 },
00567       { "tcpdump -c 100 -efvvx", 1 },
00568       { "uptime", 0.5 },
00569       { "vmstat", 2.0 },
00570       { "vmstat -c", 2.0 },
00571       { "vmstat -s", 2.0 },
00572       { "vmstat -i", 2.0 },
00573       { "vmstat -f", 2.0 },
00574       { "w", 2.5 },
00575       { "who -u", 0.5 },
00576       { "who -i", 0.5 },
00577       { "who -a", 0.5 },
00578 
00579       { 0, 0 }
00580    };
00581 
00582    class RandomOutputGatherer : public Exec::OutputCallback
00583    {
00584    private:
00585       virtual void doHandleData(
00586          const char* data, size_t dataLen, Exec::EOutputSource outputSource,
00587          const ProcessRef& theProc, size_t streamIndex,
00588          Array<char>& inputBuffer
00589       )
00590       {
00591          if (outputSource == Exec::E_STDERR)
00592          {
00593             // for all the commands we run, anything output to stderr
00594             // doesn't have any entropy.
00595             ::RAND_add(data, dataLen, 0.0);
00596          }
00597          else
00598          {
00599             // streamIndex is the index into the PopenStreams array which
00600             // correlates to randomSourceCommands
00601             ::RAND_add(
00602                data, dataLen,
00603                randomSourceCommands[streamIndex].usefulness * 
00604                static_cast<double>(dataLen) / 1024.0
00605             );
00606          }
00607          // the actual length of stuff we got could be random, but we can't
00608          // say for sure, so it gets 0.0 entropy.
00609          ::RAND_add(&dataLen, sizeof(dataLen), 0.0);
00610          ::RAND_add(&outputSource, sizeof(outputSource), 0.0);
00611          // The timing is random too.
00612          generateRandomDataFromTime(0.1);
00613       }
00614 
00615    };
00616 
00617    class RandomInputCallback : public Exec::InputCallback
00618    {
00619    private:
00620       virtual void doGetData(
00621          Array<char>& inputBuffer, const ProcessRef& theProcess,
00622          size_t streamIndex
00623       )
00624       {
00625          // none of the processes we run need data from stdin
00626          if (theProcess->in()->isOpen())
00627          {
00628             theProcess->in()->close();
00629          }
00630       }
00631    };
00632 
00633    String locateInPath(const String& cmd, const String& path)
00634    {
00635       StringArray pathElements(path.tokenize(":"));
00636       for (size_t i = 0; i < pathElements.size(); ++i)
00637       {
00638          String testCmd(pathElements[i] + '/' + cmd);
00639          if (FileSystem::exists(testCmd))
00640          {
00641             return testCmd;
00642          }
00643       }
00644       return cmd;
00645    }
00646 
00647    class RandomTimerThread : public Thread
00648    {
00649       virtual Int32 run()
00650       {
00651          // don't initialize to anything, as we may pick up some good
00652          // random junk off the stack.
00653          unsigned char buf[256];
00654          int iterations = 8;
00655          generateRandomTimerData(buf, sizeof(buf), &iterations);
00656          ::RAND_add(buf, sizeof(buf), 32);
00657          // 32 is if we assume 1 bit per byte, and most systems should have
00658          // something better than that.
00659 
00660          generateRandomDataFromTime(0.1);
00661       
00662          return 0;
00663       }
00664    };
00665 #endif
00666 
00667    void rand_init_impl()
00668    {
00669 #ifdef BLOCXX_WIN32
00670       // There are issues on win32 with calling RAND_status() w/out sufficient
00671       // entropy in a threaded environment, so we'll just add some before
00672       // calling RAND_status()
00673       HCRYPTPROV hProvider = 0;
00674       BYTE buf[64];
00675 
00676       if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL,
00677             CRYPT_VERIFYCONTEXT))
00678       {
00679          if (CryptGenRandom(hProvider, sizeof(buf), buf))
00680          {
00681             RAND_add(buf, sizeof(buf), sizeof(buf));
00682          }
00683          CryptReleaseContext(hProvider, 0);
00684       }
00685       // provided by OpenSSL. Try doing something in addition to
00686       // CryptGenRandom(), since we can't trust closed source.
00687       ::RAND_screen();
00688 #endif
00689 
00690       // try a egd socket that OpenSSL wouldn't by default.
00691       RAND_egd(BLOCXX_DEFAULT_STATE_DIR"/egd-pool");
00692 
00693       // with OpenSSL 0.9.7 calling RAND_status() will try to load
00694       // sufficient randomness, so hopefully we won't have to do anything.
00695       if (::RAND_status() == 1)
00696       {
00697          return;
00698       }
00699 
00700 #ifndef BLOCXX_WIN32
00701       // OpenSSL 0.9.7 does this automatically, so only try if we've got an
00702       // older version of OpenSSL.
00703       if (::SSLeay() < 0x00907000L)
00704       {
00705          // now try adding in /dev/random
00706          int loadedBytes = RAND_load_file("/dev/random", 1024);
00707          if (loadedBytes == 0)
00708          {
00709             // okay, no /dev/random... try adding in /dev/urandom
00710             RAND_load_file("/dev/urandom", 1024);
00711          }
00712 
00713          if (RAND_status() == 1)
00714          {
00715             return;
00716          }
00717 
00718          // now try adding in data from an entropy gathering daemon (egd)
00719          const char *names[] =
00720          {
00721             "/var/run/egd-pool", 
00722             "/dev/egd-pool", 
00723             "/etc/egd-pool",
00724             "/etc/entropy", 
00725             NULL
00726          };
00727 
00728          for (int i = 0; names[i]; i++)
00729          {
00730             if (RAND_egd(names[i]) != -1)
00731             {
00732                break;
00733             }
00734          }
00735 
00736          if (RAND_status() == 1)
00737          {
00738             return;
00739          }
00740       }
00741 
00742       // try loading up randomness from a previous run.
00743       char randFile[MAXPATHLEN];
00744       const char* rval = ::RAND_file_name(randFile, MAXPATHLEN);
00745       if (rval)
00746       {
00747          using namespace FileSystem::Path;
00748          try
00749          {
00750             if (security(randFile).first == E_SECURE_FILE)
00751             {
00752                ::RAND_load_file(randFile, -1);
00753             }
00754          }
00755          catch (FileSystemException& e)
00756          {
00757             // ignore
00758          }
00759       }
00760 
00761       // don't check RAND_status() again, since we don't really trust the
00762       // random file to be very secure--there are too many ways an attacker
00763       // could get or change it, so we'll do this other stuff as well.
00764 
00765       // we're on a really broken system.  We'll try to get some random data
00766       // by:
00767       // - running commands that reflect random system activity.
00768       //   This is the same approach a egd daemon would do, but we do it only
00769       //   once to seed the randomness.
00770       //   The list of sources comes from gnupg, prngd and egd.
00771       // - use a timing based approach which gives decent randomness.
00772       // - use other variable things, such as pid, execution times, etc.
00773       //   most of these values have an entropy of 0, since they are
00774       //   observable to any other user on the system, so even though they
00775       //   are random, they're observable, and we can't count them as entropy.
00776 
00777       // do the time based ones before we start, after the timing tests,
00778       // and then again after running commands.
00779       generateRandomDataFromTime(0.0);
00780 
00781       RandomTimerThread randomTimerThread;
00782       randomTimerThread.start();
00783 
00784       // - read some portions of files and dirs (e.g. /dev/mem) if possible
00785       const char* files[] = { "/dev/mem", 0 };
00786       for (const char** p = files; *p; ++p)
00787       {
00788          generateRandomDataFromFile(*p, 1024*1024*2);
00789       }
00790 
00791       generateRandomDataFromTime(0.1);
00792 
00793       pid_t myPid(::getpid());
00794       ::RAND_add(&myPid, sizeof(myPid), 0.0);
00795 
00796       pid_t parentPid(::getppid());
00797       ::RAND_add(&parentPid, sizeof(parentPid), 0.0);
00798    
00799       uid_t myUid(::getuid());
00800       ::RAND_add(&myUid, sizeof(myUid), 0.0);
00801 
00802       gid_t myGid(::getgid());
00803       ::RAND_add(&myGid, sizeof(myGid), 0.0);
00804 
00805       // now run commands
00806       Array<ProcessRef> procs;
00807       for (size_t i = 0; randomSourceCommands[i].command != 0; ++i)
00808       {
00809          StringArray cmd = String(randomSourceCommands[i].command).tokenize();
00810          if( cmd.empty() )
00811          {
00812             // This shouldn't happen unless someone messes up the command table above.
00813             continue;
00814          }
00815          // If it isn't an absolute path, search for it.
00816          if (cmd[0][0] != '/')
00817          {
00818             const char * RANDOM_COMMAND_PATH =
00819                "/bin:/sbin:/usr/bin:/usr/sbin:/usr/ucb:/usr/etc:/usr/bsd:"
00820                "/etc:/usr/local/bin:/usr/local/sbin";
00821 
00822             // Attempt to locate the command in our chosen "secure" path
00823             String locatedCmd = locateInPath(cmd[0], RANDOM_COMMAND_PATH);
00824             if( locatedCmd == cmd[0] )
00825             {
00826                // This command must not exist.  Skip it to avoid long delays of attempted execution.
00827                continue;
00828             }
00829 
00830             try
00831             {
00832                using namespace FileSystem::Path;
00833                if (security(locatedCmd).first != E_SECURE_FILE)
00834                {
00835                   // This command is not in a secure location, we are going to skip it.
00836                   continue;
00837                }
00838             }
00839             catch (FileSystemException& e)
00840             {
00841                // ignore the error and continue
00842                continue;
00843             }
00844 
00845             cmd[0] = locatedCmd;
00846          }
00847          try
00848          {
00849             // This may throw an exception before the command is executed.  We
00850             // can't let it cause the random init to fail.
00851             procs.push_back(Exec::spawn(cmd));
00852          }
00853          catch(const ExecErrorException& e)
00854          {
00855             // Ignore it.  We'll get random data from the other commands.
00856          }
00857       }
00858 
00859       RandomOutputGatherer randomOutputGatherer;
00860       RandomInputCallback randomInputCallback;
00861       const Timeout RANDOM_COMMAND_TIMEOUT = Timeout::relative(10.0);
00862       try
00863       {
00864          Exec::processInputOutput(
00865             randomOutputGatherer, procs,
00866             randomInputCallback, RANDOM_COMMAND_TIMEOUT
00867          );
00868       }
00869       catch (ExecTimeoutException&)
00870       {
00871          // ignore it.
00872       }
00873 
00874       // terminate all the processes and add their return code to the pool.
00875       for (size_t i = 0; i < procs.size(); ++i)
00876       {
00877          procs[i]->waitCloseTerm(Timeout::relative(0.001), Timeout::relative(0.002), Timeout::relative(0.003));
00878          Process::Status status = procs[i]->processStatus();
00879          if (!status.terminatedSuccessfully())
00880          {
00881             int rv = status.exitStatus();
00882             ::RAND_add(&rv, sizeof(rv), 0.0);
00883          }
00884       }
00885 
00886       randomTimerThread.join();
00887 
00888       generateRandomDataFromTime(0.1);
00889 #endif
00890    } // rand_init_impl
00891 
00892 } // anonymous namespace
00893 #endif // #ifdef BLOCXX_HAVE_OPENSSL