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