OW_CryptographicRandomNumber.cpp

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

Generated on Thu Feb 9 08:47:58 2006 for openwbem by  doxygen 1.4.6