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