00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
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 } 
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       
00134       randNum = randNum < 0 ? -randNum : randNum;
00135       
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    
00154    return false;
00155 #else
00156    
00157    
00158    
00159    
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    
00172    do
00173    {
00174       struct stat dirStats;
00175       if (::lstat(dir.c_str(), &dirStats) == -1)
00176       {
00177          return false;
00178       }
00179       else
00180       {
00181          
00182          if ((dirStats.st_mode & S_IWGRP == S_IWGRP) ||
00183             (dirStats.st_mode & S_IWOTH == S_IWOTH) )
00184          {
00185             return false;
00186          }
00187          
00188          if (dirStats.st_nlink > 1)
00189          {
00190             return false;
00191          }
00192          
00193          if (dirStats.st_uid != ::getuid() && dirStats.st_uid != 0)
00194          {
00195             return false;
00196          }
00197          
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    
00223    return false;
00224 #else
00225 
00226    
00227    
00228    
00229    
00230    struct stat randFileStats;
00231    if (::lstat(randFile, &randFileStats) == -1)
00232    {
00233       return false;
00234    }
00235    else
00236    {
00237       
00238       if ((randFileStats.st_mode & S_IWGRP == S_IWGRP) ||
00239          (randFileStats.st_mode & S_IWOTH == S_IWOTH) )
00240       {
00241          return false;
00242       }
00243       
00244       if (randFileStats.st_nlink > 1)
00245       {
00246          return false;
00247       }
00248       
00249       if (randFileStats.st_uid != ::getuid())
00250       {
00251          return false;
00252       }
00253       
00254       if (!S_ISREG(randFileStats.st_mode))
00255       {
00256          return false;
00257       }
00258    }
00259 
00260    return true;
00261 #endif
00262 }
00263 
00264 
00265 
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 
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 
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    
00294    MutexLock l(g_randomTimerGuard);
00295 
00296    
00297    g_data = data;
00298    g_dataSize = size;
00299    g_dataIdx = 0;
00300 
00301    
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    
00309    struct ::itimerval tv, otv;
00310    tv.it_value.tv_sec = 0;
00311    tv.it_value.tv_usec = 10 * 1000; 
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;) 
00318       {
00319          ++g_counter;
00320       }
00321       for (int j = 0; j < g_dataSize; j++) 
00322       {
00323          g_data[j] = (g_data[j]>>3) | (g_data[j]<<5);
00324       }
00325    }
00326    setitimer(ITIMER_REAL, &otv, 0);
00327 
00328    
00329    sigaction(SIGALRM, &osa, 0);
00330 
00331 }
00332 
00333 
00334 
00335 
00336 
00337 
00338 
00339 
00340 
00341 
00342 
00343 
00344 
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); 
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; 
00387 };
00388 
00389 
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          
00498          ::RAND_add(data, dataLen, 0.0);
00499       }
00500       else
00501       {
00502          
00503          ::RAND_add(data, dataLen, randomSourceCommands[streamIndex].usefulness * static_cast<double>(dataLen) / 1024.0);
00504       }
00505       
00506       ::RAND_add(&dataLen, sizeof(dataLen), 0.0);
00507       ::RAND_add(&outputSource, sizeof(outputSource), 0.0);
00508       
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       
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]; 
00550       int iterations = 8;
00551       generateRandomTimerData(buf, sizeof(buf), &iterations);
00552       ::RAND_add(buf, sizeof(buf), 32); 
00553 
00554       generateRandomDataFromTime(0.1);
00555       
00556       return 0;
00557    }
00558 };
00559 #endif
00560 } 
00561 
00563 void
00564 CryptographicRandomNumber::initRandomness()
00565 {
00566 #ifdef OW_WIN32
00567    
00568    
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(); 
00581 #endif
00582 
00583    
00584    if (::RAND_status() == 1)
00585    {
00586       return;
00587    }
00588 
00589 #ifndef OW_WIN32
00590    
00591    if (::SSLeay() < 0x00907000L)
00592    {
00593       
00594       int loadedBytes = RAND_load_file("/dev/random", 1024);
00595       if (loadedBytes == 0)
00596       {
00597          
00598          RAND_load_file("/dev/urandom", 1024);
00599       }
00600 
00601       if (RAND_status() == 1)
00602       {
00603          return;
00604       }
00605 
00606       
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    
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    
00635    
00636 
00637    
00638    
00639    
00640    
00641    
00642    
00643    
00644    
00645 
00646    
00647    generateRandomDataFromTime(0.0);
00648 
00649    RandomTimerThread randomTimerThread;
00650    randomTimerThread.start();
00651 
00652 
00653    
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    
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          
00691          streams.push_back(Exec::safePopen(cmd));
00692       }
00693       catch(const ExecErrorException&)
00694       {
00695          
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       
00710    }
00711 
00712    
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       
00734       if (randFilePathIsSecure(FileSystem::Path::dirname(randFile)))
00735       {
00736          if (RAND_write_file(randFile) <= 0)
00737          {
00738             
00739             FileSystem::removeFile(randFile);
00740          }
00741       }
00742    }
00743 }
00744 
00745 } 
00746 
00747 #endif // #if defined(OW_HAVE_OPENSSL)
00748