CryptographicRandomNumber.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2004 Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2005 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 *
00011 *  - Redistributions in binary form must reproduce the above copyright notice,
00012 *    this list of conditions and the following disclaimer in the documentation
00013 *    and/or other materials provided with the distribution.
00014 *
00015 *  - Neither the name of Vintela, Inc., Novell, Inc., nor the names of its
00016 *    contributors may be used to endorse or promote products derived from this
00017 *    software without specific prior written permission.
00018 *
00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc., Novell, Inc., OR THE 
00023 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00024 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00025 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
00026 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00027 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
00028 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
00029 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 *******************************************************************************/
00031 
00036 #include "blocxx/BLOCXX_config.h"
00037 #include "blocxx/CryptographicRandomNumber.hpp"
00038 #include "blocxx/ExceptionIds.hpp"
00039 
00040 namespace BLOCXX_NAMESPACE
00041 {
00042 BLOCXX_DEFINE_EXCEPTION_WITH_ID(CryptographicRandomNumber);
00043 }
00044 
00045 
00046 #if defined(BLOCXX_HAVE_OPENSSL)
00047 #include "blocxx/Assertion.hpp"
00048 #include "blocxx/Exec.hpp"
00049 #include "blocxx/Thread.hpp"
00050 #include "blocxx/FileSystem.hpp"
00051 #include "blocxx/ThreadOnce.hpp"
00052 #include "blocxx/Mutex.hpp"
00053 #include "blocxx/MutexLock.hpp"
00054 #include "blocxx/String.hpp"
00055 #include "blocxx/Array.hpp"
00056 #include "blocxx/SSLCtxMgr.hpp"
00057 #include "blocxx/UnnamedPipe.hpp"
00058 
00059 #include <vector>
00060 #include <climits>
00061 #include <csignal>
00062 
00063 #include <openssl/rand.h>
00064 #include <openssl/crypto.h>
00065 #include <openssl/err.h>
00066 
00067 #ifndef BLOCXX_WIN32
00068 #include <sys/time.h>
00069 #include <sys/resource.h>
00070 #endif
00071 #include <fcntl.h>
00072 
00073 #ifdef BLOCXX_HAVE_SYS_TYPES_H
00074 #include <sys/types.h>
00075 #endif
00076 
00077 #ifdef BLOCXX_HAVE_SYS_STAT_H
00078 #include <sys/stat.h>
00079 #endif
00080 
00081 #ifdef BLOCXX_HAVE_UNISTD_H
00082 #include <unistd.h>
00083 #endif
00084 
00085 namespace BLOCXX_NAMESPACE
00086 {
00087 
00088 namespace
00089 {
00090 
00092 int getNumBits(Int32 num)
00093 {
00094    for (size_t i = 0; i < sizeof(num) * CHAR_BIT; ++i)
00095    {
00096       if (num < (1 << i))
00097       {
00098          return i;
00099       }
00100    }
00101    return sizeof(num) * CHAR_BIT;
00102 }
00103 
00105 OnceFlag guard;
00106 unsigned int seed = 0;
00107 
00108 } // end unnamed namespace
00109 
00111 CryptographicRandomNumber::CryptographicRandomNumber(Int32 lowVal, Int32 highVal)
00112    : m_lowVal(lowVal)
00113    , m_highVal(highVal)
00114    , m_range(m_highVal - m_lowVal)
00115    , m_numBits(getNumBits(m_range))
00116 {
00117    BLOCXX_ASSERT(lowVal < highVal);
00118    callOnce(guard, &initRandomness);
00119 }
00120 
00122 Int32
00123 CryptographicRandomNumber::getNextNumber()
00124 {
00125    Int32 randNum = 0;
00126    do
00127    {
00128       ERR_clear_error();
00129       int rv = RAND_bytes(reinterpret_cast<unsigned char*>(&randNum), sizeof(randNum));
00130       if (rv != 1)
00131       {
00132          BLOCXX_THROW(CryptographicRandomNumberException, SSLCtxMgr::getOpenSSLErrorDescription().c_str());
00133       }
00134       // make it positive
00135       randNum = randNum < 0 ? -randNum : randNum;
00136       // filter out all the unnecessary high bits
00137       randNum &= ~(~0 << m_numBits);
00138    } while (randNum > m_range);
00139 
00140    return randNum + m_lowVal;
00141 }
00142 
00143 namespace
00144 {
00145 
00149 bool randFilePathIsSecure(const String& randFilePath)
00150 {
00151    BLOCXX_ASSERT(!randFilePath.empty());
00152 
00153 #ifdef BLOCXX_WIN32
00154    // TODO: write this
00155    return false;
00156 #else
00157    // only load or write the file if it's "the directory in which the file resides and all parent directories should have only write access
00158    // enabled for the directory owner" (Network Security with OpenSSL p. 101).  This is so that we don't load up a rogue random
00159    // file. If we load one someone created which we didn't write, or someone else gets it, our security is blown!
00160    // Also, check that the owner of each directory is either the current user or root, just to be completely paranoid!
00161    String dir;
00162    try
00163    {
00164       dir = FileSystem::Path::realPath(randFilePath);
00165    }
00166    catch (FileSystemException&)
00167    {
00168       return false;
00169    }
00170    BLOCXX_ASSERT(!dir.empty() && dir[0] == '/');
00171 
00172    // now check all dirs
00173    do
00174    {
00175       struct stat dirStats;
00176       if (::lstat(dir.c_str(), &dirStats) == -1)
00177       {
00178          return false;
00179       }
00180       else
00181       {
00182          // if either group or other write access is enabled, then fail.
00183          if ((dirStats.st_mode & S_IWGRP == S_IWGRP) ||
00184             (dirStats.st_mode & S_IWOTH == S_IWOTH) )
00185          {
00186             return false;
00187          }
00188          // no hard links allowed
00189          if (dirStats.st_nlink > 1)
00190          {
00191             return false;
00192          }
00193          // must own it or else root
00194          if (dirStats.st_uid != ::getuid() && dirStats.st_uid != 0)
00195          {
00196             return false;
00197          }
00198          // directory
00199          if (!S_ISDIR(dirStats.st_mode))
00200          {
00201             return false;
00202          }
00203       }
00204 
00205 
00206       size_t lastSlash = dir.lastIndexOf('/');
00207       dir = dir.substring(0, lastSlash);
00208    } while (!dir.empty());
00209 
00210    return true;
00211 #endif
00212 }
00213 
00215 bool randFileIsSecure(const char* randFile)
00216 {
00217    if (!randFilePathIsSecure(FileSystem::Path::dirname(randFile)))
00218    {
00219       return false;
00220    }
00221 
00222 #ifdef BLOCXX_WIN32
00223    // TODO: write this
00224    return false;
00225 #else
00226 
00227    // only load or write the file if it's "owned by the user ID of the application, and all access to group members and other users should be
00228    // disallowed. Additionally, the directory in which the file resides and all parent directories should have only write access
00229    // enabled for the directory owner" (Network Security with OpenSSL p. 101).  This is so that we don't load up a rogue random
00230    // file. If we load one someone created which we didn't write, or someone else gets it, our security is blown!
00231    struct stat randFileStats;
00232    if (::lstat(randFile, &randFileStats) == -1)
00233    {
00234       return false;
00235    }
00236    else
00237    {
00238       // if either group or other write access is enabled, then fail.
00239       if ((randFileStats.st_mode & S_IWGRP == S_IWGRP) ||
00240          (randFileStats.st_mode & S_IWOTH == S_IWOTH) )
00241       {
00242          return false;
00243       }
00244       // no hard links allowed
00245       if (randFileStats.st_nlink > 1)
00246       {
00247          return false;
00248       }
00249       // must own it
00250       if (randFileStats.st_uid != ::getuid())
00251       {
00252          return false;
00253       }
00254       // regular file
00255       if (!S_ISREG(randFileStats.st_mode))
00256       {
00257          return false;
00258       }
00259    }
00260 
00261    return true;
00262 #endif
00263 }
00264 
00265 // These are used to generate random data via signal delivery timing differences.
00266 // We have to use global data since it's modified from a signal handler.
00267 volatile sig_atomic_t g_counter;
00268 volatile unsigned char* g_data;
00269 volatile sig_atomic_t g_dataIdx;
00270 int g_dataSize;
00271 
00272 extern "C"
00273 {
00274 // this needs to still be static, since it gets exported because of extern "C"
00275 static void randomALRMHandler(int sig)
00276 {
00277    if (g_dataIdx < g_dataSize)
00278    {
00279       g_data[g_dataIdx++] ^= g_counter & 0xFF;
00280    }
00281 }
00282 }
00283 
00284 Mutex g_randomTimerGuard;
00285 
00286 #ifndef BLOCXX_WIN32
00287 // This function will continue to iterate until *iterations <= 0. *iterations may be set by another thread. *iterations should not be < 8.
00288 void generateRandomTimerData(unsigned char* data, int size, int* iterations)
00289 {
00290    BLOCXX_ASSERT(data != 0);
00291    BLOCXX_ASSERT(size > 0);
00292    BLOCXX_ASSERT(iterations != 0);
00293 
00294    // make sure we only have one thread running this at a time.
00295    MutexLock l(g_randomTimerGuard);
00296 
00297    // set up the global data for the signal handler
00298    g_data = data;
00299    g_dataSize = size;
00300    g_dataIdx = 0;
00301 
00302    // install our ALRM handler
00303    struct sigaction sa, osa;
00304    sa.sa_handler = randomALRMHandler;
00305    sa.sa_flags = 0;
00306    sigemptyset(&sa.sa_mask);
00307    sigaction(SIGALRM, &sa, &osa);
00308 
00309    // Start timer
00310    struct ::itimerval tv, otv;
00311    tv.it_value.tv_sec = 0;
00312    tv.it_value.tv_usec = 10 * 1000; // 10 ms
00313    tv.it_interval = tv.it_value;
00314    setitimer(ITIMER_REAL, &tv, &otv);
00315    
00316    while ((*iterations)-- > 0)
00317    {
00318       for (g_dataIdx = 0; g_dataIdx < g_dataSize;) // g_dataIdx++ in sigALRM
00319       {
00320          ++g_counter;
00321       }
00322       for (int j = 0; j < g_dataSize; j++) // rotate the bits to accomodate for a possible lack of low-bit entropy
00323       {
00324          g_data[j] = (g_data[j]>>3) | (g_data[j]<<5);
00325       }
00326    }
00327    setitimer(ITIMER_REAL, &otv, 0);
00328 
00329    // reset signal handler
00330    sigaction(SIGALRM, &osa, 0);
00331 
00332 }
00333 
00334 // void printBuffer(unsigned char* buf, int size)
00335 // {
00336 //     for (int i = 0; i < size; ++i)
00337 //     {
00338 //         if (i % 10 == 0)
00339 //         {
00340 //             printf("\n");
00341 //         }
00342 //         printf("%2.2X ", buf[i]);
00343 //     }
00344 //     printf("\n");
00345 //     fflush(stdout);
00346 // }
00347 
00348 void generateRandomDataFromFile(const char* name, int len)
00349 {
00350    int fd = ::open(name, O_RDONLY);
00351    if (fd == -1)
00352    {
00353       return;
00354    }
00355 
00356    std::vector<char> buf(len);
00357    int bytesRead = ::read(fd, &buf[0], len);
00358    if (bytesRead == -1)
00359    {
00360       return;
00361    }
00362    buf.resize(bytesRead);
00363    ::RAND_add(&buf[0], buf.size(), 0.0); // 0 entropy, since this could all be observable by someone else.
00364 }
00365 
00367 void generateRandomDataFromTime(double entropy)
00368 {
00369    struct timeval tv;
00370    ::gettimeofday(&tv, 0);
00371    ::RAND_add(&tv, sizeof(tv), entropy);
00372 
00373    clock_t c(::clock());
00374    ::RAND_add(&c, sizeof(c), entropy);
00375 
00376    struct rusage ru;
00377    ::getrusage(RUSAGE_SELF, &ru);
00378    ::RAND_add(&ru, sizeof(ru), entropy);
00379 
00380    ::getrusage(RUSAGE_CHILDREN, &ru);
00381    ::RAND_add(&ru, sizeof(ru), entropy);
00382 }
00383 
00384 struct cmd
00385 {
00386    const char* command;
00387    double usefulness; // estimated number of bytes of entropy per 1K of output
00388 };
00389 
00390 // This list of sources comes from gnupg, prngd and egd.
00391 const cmd randomSourceCommands[] =
00392 {
00393    { "advfsstat -b usr_domain", 0.01 },
00394    { "advfsstat -l 2 usr_domain", 0.5 },
00395    { "advfsstat -p usr_domain", 0.01 },
00396    { "arp -a -n", 0.5 },
00397    { "df", 0.5 },
00398    { "df -i", 0.5 },
00399    { "df -a", 0.5 },
00400    { "df -in", 0.5 },
00401    { "dmesg", 0.5 },
00402    { "errpt -a", 0.5 },
00403    { "ifconfig -a", 0.5 },
00404    { "iostat", 0.5 },
00405    { "ipcs -a", 0.5 },
00406    { "last", 0.5 },
00407    { "lastlog", 0.5 },
00408    { "lpstat -t", 0.1 },
00409    { "ls -alniR /var/log", 1.0 },
00410    { "ls -alniR /var/adm", 1.0 },
00411    { "ls -alni /var/spool/mail", 1.0 },
00412    { "ls -alni /proc", 1.0 },
00413    { "ls -alniR /tmp", 1.0 },
00414    { "ls -alniR /var/tmp", 1.0 },
00415    { "ls -alni /var/mail", 1.0 },
00416    { "ls -alniR /var/db", 1.0 },
00417    { "ls -alniR /etc", 1.0 },
00418    { "ls -alniR /private/var/log", 1.0 },
00419    { "ls -alniR /private/var/db", 1.0 },
00420    { "ls -alniR /private/etc", 1.0 },
00421    { "ls -alniR /private/tmp", 1.0 },
00422    { "ls -alniR /private/var/tmp", 1.0 },
00423    { "mpstat", 1.5 },
00424    { "netstat -s", 1.5 },
00425    { "netstat -n", 1.5 },
00426    { "netstat -a -n", 1.5 },
00427    { "netstat -anv", 1.5 },
00428    { "netstat -i -n", 0.5 },
00429    { "netstat -r -n", 0.1 },
00430    { "netstat -m", 0.5 },
00431    { "netstat -ms", 0.5 },
00432    { "nfsstat", 0.5 },
00433    { "ps laxww", 1.5 },
00434    { "ps -laxww", 1.5 },
00435    { "ps -al", 1.5 },
00436    { "ps -el", 1.5 },
00437    { "ps -efl", 1.5 },
00438    { "ps -efly", 1.5 },
00439    { "ps aux", 1.5 },
00440    { "ps -A", 1.5 },
00441    { "pfstat", 0.5 },
00442    { "portstat", 0.5 },
00443    { "pstat -p", 0.5 },
00444    { "pstat -S", 0.5 },
00445    { "pstat -A", 0.5 },
00446    { "pstat -t", 0.5 },
00447    { "pstat -v", 0.5 },
00448    { "pstat -x", 0.5 },
00449    { "pstat -t", 0.5 },
00450    { "ripquery -nw 1 127.0.0.1", 0.5 },
00451    { "sar -A 1 1", 0.5 },
00452    { "snmp_request localhost public get 1.3.6.1.2.1.7.1.0", 0.5 },
00453    { "snmp_request localhost public get 1.3.6.1.2.1.7.4.0", 0.5 },
00454    { "snmp_request localhost public get 1.3.6.1.2.1.4.3.0", 0.5 },
00455    { "snmp_request localhost public get 1.3.6.1.2.1.6.10.0", 0.5 },
00456    { "snmp_request localhost public get 1.3.6.1.2.1.6.11.0", 0.5 },
00457    { "snmp_request localhost public get 1.3.6.1.2.1.6.13.0", 0.5 },
00458    { "snmp_request localhost public get 1.3.6.1.2.1.5.1.0", 0.5 },
00459    { "snmp_request localhost public get 1.3.6.1.2.1.5.3.0", 0.5 },
00460    { "tail -c 1024 /var/log/messages", 1.0 },
00461    { "tail -c 1024 /var/log/syslog", 1.0 },
00462    { "tail -c 1024 /var/log/system.log", 1.0 },
00463    { "tail -c 1024 /var/log/debug", 1.0 },
00464    { "tail -c 1024 /var/adm/messages", 1.0 },
00465    { "tail -c 1024 /var/adm/syslog", 1.0 },
00466    { "tail -c 1024 /var/adm/syslog/mail.log", 1.0 },
00467    { "tail -c 1024 /var/adm/syslog/syslog.log", 1.0 },
00468    { "tail -c 1024 /var/log/maillog", 1.0 },
00469    { "tail -c 1024 /var/adm/maillog", 1.0 },
00470    { "tail -c 1024 /var/adm/SPlogs/SPdaemon.log", 1.0 },
00471    { "tail -c 1024 /usr/es/adm/cluster.log", 1.0 },
00472    { "tail -c 1024 /usr/adm/cluster.log", 1.0 },
00473    { "tail -c 1024 /var/adm/cluster.log", 1.0 },
00474    { "tail -c 1024 /var/adm/ras/conslog", 1.0 },
00475    { "tcpdump -c 100 -efvvx", 1 },
00476    { "uptime", 0.5 },
00477    { "vmstat", 2.0 },
00478    { "vmstat -c", 2.0 },
00479    { "vmstat -s", 2.0 },
00480    { "vmstat -i", 2.0 },
00481    { "vmstat -f", 2.0 },
00482    { "w", 2.5 },
00483    { "who -u", 0.5 },
00484    { "who -i", 0.5 },
00485    { "who -a", 0.5 },
00486 
00487    { 0, 0 }
00488 };
00489 
00491 class RandomOutputGatherer : public Exec::OutputCallback
00492 {
00493 private:
00494    virtual void doHandleData(const char* data, size_t dataLen, Exec::EOutputSource outputSource, PopenStreams& theStream, size_t streamIndex, Array<char>& inputBuffer)
00495    {
00496       if (outputSource == Exec::E_STDERR)
00497       {
00498          // for all the commands we run, anything output to stderr doesn't have any entropy.
00499          ::RAND_add(data, dataLen, 0.0);
00500       }
00501       else
00502       {
00503          // streamIndex is the index into the PopenStreams array which correlates to randomSourceCommands
00504          ::RAND_add(data, dataLen, randomSourceCommands[streamIndex].usefulness * static_cast<double>(dataLen) / 1024.0);
00505       }
00506       // the actual length of stuff we got could be random, but we can't say for sure, so it gets 0.0 entropy.
00507       ::RAND_add(&dataLen, sizeof(dataLen), 0.0);
00508       ::RAND_add(&outputSource, sizeof(outputSource), 0.0);
00509       // The timing is random too.
00510       generateRandomDataFromTime(0.1);
00511    }
00512 
00513 };
00514 
00516 class RandomInputCallback : public Exec::InputCallback
00517 {
00518 private:
00519    virtual void doGetData(Array<char>& inputBuffer, PopenStreams& theStream, size_t streamIndex)
00520    {
00521       // none of the processes we run need data from stdin, so just close it.
00522       if (theStream.in()->isOpen())
00523       {
00524          theStream.in()->close();
00525       }
00526    }
00527 };
00528 
00530 String
00531 locateInPath(const String& cmd, const String& path)
00532 {
00533    StringArray pathElements(path.tokenize(":"));
00534    for (size_t i = 0; i < pathElements.size(); ++i)
00535    {
00536       String testCmd(pathElements[i] + '/' + cmd);
00537       if (FileSystem::exists(testCmd))
00538       {
00539          return testCmd;
00540       }
00541    }
00542    return cmd;
00543 }
00544 
00546 class RandomTimerThread : public Thread
00547 {
00548    virtual Int32 run()
00549    {
00550       unsigned char buf[256]; // don't initialize to anything, as we may pick up some good random junk off the stack.
00551       int iterations = 8;
00552       generateRandomTimerData(buf, sizeof(buf), &iterations);
00553       ::RAND_add(buf, sizeof(buf), 32); // 32 is if we assume 1 bit per byte, and most systems should have something better than that.
00554 
00555       generateRandomDataFromTime(0.1);
00556       
00557       return 0;
00558    }
00559 };
00560 #endif
00561 } // end unnamed namespace
00562 
00564 void
00565 CryptographicRandomNumber::initRandomness()
00566 {
00567 #ifdef BLOCXX_WIN32
00568    // There are issues on win32 with calling RAND_status() w/out sufficient entropy 
00569    // in a threaded environment, so we'll just add some before calling RAND_status()
00570    HCRYPTPROV hProvider = 0;
00571    BYTE buf[64];
00572 
00573    if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
00574    {
00575       if (CryptGenRandom(hProvider, sizeof(buf), buf))
00576       {
00577          RAND_add(buf, sizeof(buf), sizeof(buf));
00578       }
00579       CryptReleaseContext(hProvider, 0);
00580    }
00581    ::RAND_screen(); // provided by OpenSSL. Try doing something in addition to CryptGenRandom(), since we can't trust closed source.
00582 #endif
00583 
00584    // with OpenSSL 0.9.7 calling RAND_status() will try to load sufficient randomness, so hopefully we won't have to do anything.
00585    if (::RAND_status() == 1)
00586    {
00587       return;
00588    }
00589 
00590 #ifndef BLOCXX_WIN32
00591    // OpenSSL 0.9.7 does this automatically, so only try if we've got an older version of OpenSSL.
00592    if (::SSLeay() < 0x00907000L)
00593    {
00594       // now try adding in /dev/random
00595       int loadedBytes = RAND_load_file("/dev/random", 1024);
00596       if (loadedBytes == 0)
00597       {
00598          // okay, no /dev/random... try adding in /dev/urandom
00599          RAND_load_file("/dev/urandom", 1024);
00600       }
00601 
00602       if (RAND_status() == 1)
00603       {
00604          return;
00605       }
00606 
00607       // now try adding in data from an entropy gathering daemon (egd)
00608       const char *names[] = { "/var/run/egd-pool","/dev/egd-pool","/etc/egd-pool","/etc/entropy", NULL };
00609 
00610       for (int i = 0; names[i]; i++)
00611       {
00612          if (RAND_egd(names[i]) != -1)
00613          {
00614             break;
00615          }
00616       }
00617 
00618       if (RAND_status() == 1)
00619       {
00620          return;
00621       }
00622    }
00623 
00624    // try loading up randomness from a previous run.
00625    char randFile[MAXPATHLEN];
00626    const char* rval = ::RAND_file_name(randFile, MAXPATHLEN);
00627    if (rval)
00628    {
00629       if (randFileIsSecure(randFile))
00630       {
00631          ::RAND_load_file(randFile, -1);
00632       }
00633    }
00634 
00635    // don't check RAND_status() again, since we don't really trust the random file to be very secure--there are too many ways an attacker
00636    // could get or change it, so we'll do this other stuff as well.
00637 
00638    // we're on a really broken system.  We'll try to get some random data by:
00639    // - running commands that reflect random system activity.
00640    //   This is the same approach a egd daemon would do, but we do it only once to seed the randomness.
00641    //   The list of sources comes from gnupg, prngd and egd.
00642    // - use a timing based approach which gives decent randomness.
00643    // - use other variable things, such as pid, execution times, etc.
00644    //   most of these values have an entropy of 0, since they are observable to any other user on the system, so even though they are random, they're
00645    //   observable, and we can't count them as entropy.
00646 
00647    // do the time based ones before we start, after the timing tests, and then again after running commands.
00648    generateRandomDataFromTime(0.0);
00649 
00650    RandomTimerThread randomTimerThread;
00651    randomTimerThread.start();
00652 
00653 
00654    // - read some portions of files and dirs (e.g. /dev/mem) if possible
00655    const char* files[] = {
00656       "/dev/mem",
00657       0
00658    };
00659    for (const char** p = files; *p; ++p)
00660    {
00661       generateRandomDataFromFile(*p, 1024*1024*2);
00662    }
00663 
00664    generateRandomDataFromTime(0.1);
00665 
00666    pid_t myPid(::getpid());
00667    ::RAND_add(&myPid, sizeof(myPid), 0.0);
00668 
00669    pid_t parentPid(::getppid());
00670    ::RAND_add(&parentPid, sizeof(parentPid), 0.0);
00671    
00672    uid_t myUid(::getuid());
00673    ::RAND_add(&myUid, sizeof(myUid), 0.0);
00674 
00675    gid_t myGid(::getgid());
00676    ::RAND_add(&myGid, sizeof(myGid), 0.0);
00677 
00678    // now run commands
00679    Array<PopenStreams> streams;
00680    for (size_t i = 0; randomSourceCommands[i].command != 0; ++i)
00681    {
00682       StringArray cmd = StringArray(String(randomSourceCommands[i].command).tokenize());
00683       if (cmd[0] != "/")
00684       {
00685          const char* RANDOM_COMMAND_PATH = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/ucb:/usr/etc:/usr/bsd:/etc:/usr/local/bin:/usr/local/sbin";
00686          cmd[0] = locateInPath(cmd[0], RANDOM_COMMAND_PATH);
00687       }
00688 
00689       try
00690       {
00691          // safePopen can now throw an exception.
00692          streams.push_back(Exec::safePopen(cmd));
00693       }
00694       catch(const ExecErrorException&)
00695       {
00696          // ignore it
00697       }
00698    }
00699 
00700    RandomOutputGatherer randomOutputGatherer;
00701    RandomInputCallback randomInputCallback;
00702    Array<Exec::ProcessStatus> processStatuses;
00703    const int RANDOM_COMMAND_TIMEOUT = 10;
00704    try
00705    {
00706       Exec::processInputOutput(randomOutputGatherer, streams, processStatuses, randomInputCallback, RANDOM_COMMAND_TIMEOUT);
00707    }
00708    catch (ExecTimeoutException&)
00709    {
00710       // ignore it.
00711    }
00712 
00713    // terminate all the processes and add their return code to the pool.
00714    for (size_t i = 0; i < streams.size(); ++i)
00715    {
00716       int rv = streams[i].getExitStatus();
00717       ::RAND_add(&rv, sizeof(rv), 0.0);
00718    }
00719 
00720    randomTimerThread.join();
00721 
00722    generateRandomDataFromTime(0.1);
00723 #endif
00724 }
00725 
00727 void
00728 CryptographicRandomNumber::saveRandomState()
00729 {
00730    char randFile[MAXPATHLEN];
00731    const char* rval = RAND_file_name(randFile, MAXPATHLEN);
00732    if (rval)
00733    {
00734       // we only create this file is there's no chance an attacker could read or write it. see Network Security with OpenSSL p. 101
00735       if (randFilePathIsSecure(FileSystem::Path::dirname(randFile)))
00736       {
00737          if (RAND_write_file(randFile) <= 0)
00738          {
00739             // in case "the bytes written were generated without appropriate seed.", we don't want to load it up next time.
00740             FileSystem::removeFile(randFile);
00741          }
00742       }
00743    }
00744 }
00745 
00746 } // end namespace BLOCXX_NAMESPACE
00747 
00748 #endif // #if defined(BLOCXX_HAVE_OPENSSL)
00749 

Generated on Fri Jun 16 15:39:08 2006 for blocxx by  doxygen 1.4.6