blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved. 00003 * Copyright (C) 2006, 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 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Vintela, Inc., 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 00031 *******************************************************************************/ 00032 00033 00039 #include "blocxx/BLOCXX_config.h" 00040 00041 #ifdef BLOCXX_HAVE_OPENSSL 00042 // If you don't have SSL, you don't have cryptographically secure random 00043 // numbers. Don't try to fall back to a weaker PRNG, as this violates the 00044 // security principle of "fail safe". 00045 00046 #include "blocxx/Array.hpp" 00047 #include "blocxx/Assertion.hpp" 00048 #include "blocxx/Exec.hpp" 00049 #include "blocxx/FileSystem.hpp" 00050 #include "blocxx/Mutex.hpp" 00051 #include "blocxx/MutexLock.hpp" 00052 #include "blocxx/GlobalMutex.hpp" 00053 #include "blocxx/Secure.hpp" 00054 #include "blocxx/SecureRand.hpp" 00055 #include "blocxx/SSLCtxMgr.hpp" 00056 #include "blocxx/String.hpp" 00057 #include "blocxx/Thread.hpp" 00058 #include "blocxx/ThreadOnce.hpp" 00059 #include "blocxx/UnnamedPipe.hpp" 00060 #include "blocxx/UserUtils.hpp" 00061 #include "blocxx/Process.hpp" 00062 00063 #include <cmath> 00064 #include <csignal> 00065 #include <cstring> // for std::memset 00066 #include <limits> 00067 #ifdef BLOCXX_HAVE_UNISTD_H 00068 #include <unistd.h> 00069 #endif 00070 00071 #include <fcntl.h> 00072 #include <openssl/rand.h> 00073 #include <openssl/err.h> 00074 #ifdef BLOCXX_HAVE_SYS_RESOURCE_H 00075 #include <sys/resource.h> 00076 #endif 00077 #ifndef BLOCXX_WIN32 00078 #include <sys/time.h> 00079 #endif 00080 00081 #ifdef BLOCXX_WIN32 00082 #include <wincrypt.h> 00083 #endif 00084 00085 using namespace blocxx; 00086 00087 namespace 00088 { 00089 unsigned const RESEED_BYTES = 16; // 128 bits 00090 unsigned const SEED_BYTES = 16; // 128 bits 00091 00092 template <typename T> struct unsigned_equivalent 00093 { 00094 typedef T type; 00095 }; 00096 00097 template <> struct unsigned_equivalent<char> 00098 { 00099 typedef unsigned char type; 00100 }; 00101 00102 template <> struct unsigned_equivalent<signed char> 00103 { 00104 typedef unsigned char type; 00105 }; 00106 00107 template <> struct unsigned_equivalent<short> 00108 { 00109 typedef unsigned short type; 00110 }; 00111 00112 template <> struct unsigned_equivalent<int> 00113 { 00114 typedef unsigned int type; 00115 }; 00116 00117 template <> struct unsigned_equivalent<long> 00118 { 00119 typedef unsigned long type; 00120 }; 00121 00122 template <> struct unsigned_equivalent<long long> 00123 { 00124 typedef unsigned long long type; 00125 }; 00126 00127 blocxx::OnceFlag guard = BLOCXX_ONCE_INIT; 00128 00129 void rand_init_impl(); 00130 } 00131 00132 namespace BLOCXX_NAMESPACE 00133 { 00134 BLOCXX_DEFINE_EXCEPTION(SecureRand); 00135 00136 namespace Secure 00137 { 00138 void rand_init() 00139 { 00140 callOnce(guard, &rand_init_impl); 00141 } 00142 00143 unsigned char * rand(unsigned char * buf, std::size_t n) 00144 { 00145 callOnce(guard, &rand_init_impl); 00146 ERR_clear_error(); 00147 if (!RAND_bytes(buf, n)) 00148 { 00149 BLOCXX_THROW(SecureRandException, 00150 SSLCtxMgr::getOpenSSLErrorDescription().c_str()); 00151 } 00152 return buf; 00153 } 00154 00155 ::pid_t fork_reseed() 00156 { 00157 #ifdef BLOCXX_WIN32 00158 #pragma message(Reminder "TODO: implement it for Win!") 00159 00160 return BLOCXX_INVALID_HANDLE; 00161 #else 00162 unsigned char seed[2][RESEED_BYTES]; 00163 rand(seed[0], sizeof(seed[0])); 00164 rand(seed[1], sizeof(seed[1])); 00165 00166 ::pid_t rv = ::fork(); 00167 if (rv < 0) 00168 { 00169 return rv; 00170 } 00171 00172 std::size_t idx = rv > 0; // 0 or 1 00173 RAND_seed(seed[idx], sizeof(seed[idx])); 00174 // forget other process's seed 00175 std::memset(seed[1 - idx], 0, sizeof(seed[1- idx])); 00176 00177 return rv; 00178 #endif 00179 } 00180 00181 namespace Impl 00182 { 00183 template <typename UnsignedInt> 00184 UnsignedInt rand_uint_lt(UnsignedInt n) 00185 { 00186 BLOCXX_ASSERT(n > 0); 00187 if ((n & (n - 1)) == 0) // n is a power of two 00188 { 00189 return rand_uint<UnsignedInt>() % n; 00190 } 00191 UnsignedInt const uint_max = static_cast<UnsignedInt>(-1); 00192 UnsignedInt const bound = uint_max - (uint_max % n); 00193 UnsignedInt rn; 00194 do 00195 { 00196 rn = rand_uint<UnsignedInt>(); 00197 } while (rn >= bound); 00198 return rn % n; 00199 } 00200 00201 // Explicit instantiation 00202 template unsigned char 00203 BLOCXX_COMMON_API rand_uint_lt<unsigned char>(unsigned char); 00204 template unsigned short 00205 BLOCXX_COMMON_API rand_uint_lt<unsigned short>(unsigned short); 00206 template unsigned int 00207 BLOCXX_COMMON_API rand_uint_lt<unsigned int>(unsigned int); 00208 template unsigned long 00209 BLOCXX_COMMON_API rand_uint_lt<unsigned long>(unsigned long); 00210 template unsigned long long 00211 BLOCXX_COMMON_API rand_uint_lt<unsigned long long>(unsigned long long); 00212 00213 template <typename Integer> 00214 Integer rand_range(Integer min_value, Integer max_value) 00215 { 00216 BLOCXX_ASSERT(max_value >= min_value); 00217 00218 // The following code uses these properties of C++: 00219 // - Conversions from a signed integer to an unsigned integer 00220 // of the same size are always well-defined. 00221 // - Arithmetic for unsigned integers is module 2^n, where n 00222 // is the number of bits. 00223 // - If signed integer k is negative, then converting it to 00224 // the equivalent unsigned integer yields 2^n + k. 00225 00226 typedef typename unsigned_equivalent<Integer>::type UnsignedInt; 00227 UnsignedInt const umax = static_cast<UnsignedInt>(max_value); 00228 UnsignedInt const umin = static_cast<UnsignedInt>(min_value); 00229 UnsignedInt const diff = umax - umin; 00230 00231 // diff is the mathematical difference between max_value 00232 // and min_value, which may not be representable as an Integer, 00233 // but is guaranteed to be representable as an UnsignedInt. 00234 00235 UnsignedInt const range = diff + static_cast<UnsignedInt>(1); 00236 00237 // range == 0 iff every UnsignedInt value corresponds to 00238 // a unique Integer value (e.g., two's complement representation 00239 // instead of sign-magnitude), min_value is the smallest possible 00240 // Integer value, and max_value is the largest possible Integer 00241 // value. 00242 00243 UnsignedInt rv; 00244 if (range == 0) 00245 { 00246 // All Integer values are allowed return values, and there 00247 // is a one-to-one mapping from UnsignedInt values to 00248 // Integer values. 00249 rv = rand_uint<UnsignedInt>(); 00250 } 00251 else 00252 { 00253 // Compute the UnsignedInt value corresponding to the desired 00254 // Integer value. This works even if min_value < 0 and 00255 // max_value >= 0, because the arithmetic is module 2^n. 00256 rv = umin + rand_uint_lt(range); 00257 } 00258 return static_cast<Integer>(rv); 00259 } 00260 00261 // explicit instantiations 00262 template char 00263 BLOCXX_COMMON_API rand_range(char, char); 00264 template signed char 00265 BLOCXX_COMMON_API rand_range(signed char, signed char); 00266 template unsigned char 00267 BLOCXX_COMMON_API rand_range(unsigned char, unsigned char); 00268 template short 00269 BLOCXX_COMMON_API rand_range(short, short); 00270 template unsigned short 00271 BLOCXX_COMMON_API rand_range(unsigned short, unsigned short); 00272 template int 00273 BLOCXX_COMMON_API rand_range(int, int); 00274 template unsigned int 00275 BLOCXX_COMMON_API rand_range(unsigned int, unsigned int); 00276 template long 00277 BLOCXX_COMMON_API rand_range(long, long); 00278 template unsigned long 00279 BLOCXX_COMMON_API rand_range(unsigned long, unsigned long); 00280 template long long 00281 BLOCXX_COMMON_API rand_range(long long, long long); 00282 template unsigned long long 00283 BLOCXX_COMMON_API rand_range(unsigned long long, unsigned long long); 00284 00285 template <unsigned int N> 00286 struct log2 00287 { 00288 enum { value = 1 + log2<N/2>::value }; 00289 }; 00290 00291 template <> 00292 struct log2<1> 00293 { 00294 enum { value = 0 }; 00295 }; 00296 00297 // # of mantissa bits if Number is a floating-point type. 00298 template <typename Number> 00299 struct bits_precision 00300 { 00301 typedef std::numeric_limits<Number> lim_t; 00302 enum { value = lim_t::digits * log2<lim_t::radix>::value }; 00303 }; 00304 00305 template <typename Real> 00306 Real rand_unit_interval() 00307 { 00308 typedef UInt32 uint_t; 00309 int const UINT_BITS = 32; 00310 int const NUINT = 00311 (bits_precision<Real>::value + UINT_BITS - 1) / UINT_BITS; 00312 Real rv = 0.0; 00313 for (int i = 1; i <= NUINT; ++i) 00314 { 00315 Real r = static_cast<Real>(rand_uint<uint_t>()); 00316 rv += std::ldexp(r, -UINT_BITS * i); 00317 } 00318 return rv; 00319 } 00320 00321 // explicit instantiations 00322 template float rand_unit_interval<float>(); 00323 template double rand_unit_interval<double>(); 00324 template long double rand_unit_interval<long double>(); 00325 00326 } // namespace Impl 00327 00328 void rand_save_state() 00329 { 00330 char randFile[MAXPATHLEN]; 00331 char const * rval = RAND_file_name(randFile, MAXPATHLEN); 00332 if (rval) 00333 { 00334 // we only create this file is there's no chance an attacker 00335 // could read or write it. see Network Security with OpenSSL p. 101 00336 using namespace FileSystem::Path; 00337 if (security(dirname(randFile)).first == E_SECURE_DIR) 00338 { 00339 if (RAND_write_file(randFile) <= 0) 00340 { 00341 // in case "the bytes written were generated without 00342 // appropriate seed.", we don't want to load it up next 00343 // time. 00344 FileSystem::removeFile(randFile); 00345 } 00346 } 00347 } 00348 00349 } 00350 00351 } // namespace Secure 00352 } // namespace BLOCXX_NAMESPACE 00353 00354 namespace 00355 { 00356 // These are used to generate random data via signal delivery timing 00357 // differences. We have to use global data since it's modified from a 00358 // signal handler. 00359 volatile sig_atomic_t g_counter; 00360 volatile unsigned char* g_data; 00361 volatile sig_atomic_t g_dataIdx; 00362 int g_dataSize; 00363 } 00364 00365 extern "C" 00366 { 00367 // this needs to still be static, since it gets exported because of 00368 // extern "C" 00369 #ifdef BLOCXX_NCR 00370 static void randomALRMHandler() 00371 #else 00372 static void randomALRMHandler(int sig) 00373 #endif 00374 { 00375 if (g_dataIdx < g_dataSize) 00376 { 00377 g_data[g_dataIdx++] ^= g_counter & 0xFF; 00378 } 00379 } 00380 } 00381 00382 namespace 00383 { 00384 GlobalMutex g_randomTimerGuard = BLOCXX_GLOBAL_MUTEX_INIT(); 00385 00386 #ifndef BLOCXX_WIN32 00387 // This function will continue to iterate until *iterations <= 0. 00388 // *iterations may be set by another thread. *iterations should not be < 8. 00389 void generateRandomTimerData(unsigned char* data, int size, int* iterations) 00390 { 00391 BLOCXX_ASSERT(data != 0); 00392 BLOCXX_ASSERT(size > 0); 00393 BLOCXX_ASSERT(iterations != 0); 00394 00395 // make sure we only have one thread running this at a time. 00396 MutexLock l(g_randomTimerGuard); 00397 00398 // set up the global data for the signal handler 00399 g_data = data; 00400 g_dataSize = size; 00401 g_dataIdx = 0; 00402 00403 // install our ALRM handler 00404 struct sigaction sa, osa; 00405 sa.sa_handler = randomALRMHandler; 00406 sa.sa_flags = 0; 00407 sigemptyset(&sa.sa_mask); 00408 sigaction(SIGALRM, &sa, &osa); 00409 00410 // Start timer 00411 struct ::itimerval tv, otv; 00412 tv.it_value.tv_sec = 0; 00413 tv.it_value.tv_usec = 10 * 1000; // 10 ms 00414 tv.it_interval = tv.it_value; 00415 setitimer(ITIMER_REAL, &tv, &otv); 00416 00417 while ((*iterations)-- > 0) 00418 { 00419 // g_dataIdx++ in sigALRM 00420 for (g_dataIdx = 0; g_dataIdx < g_dataSize;) 00421 { 00422 ++g_counter; 00423 } 00424 // rotate the bits to accomodate for a possible lack of 00425 // low-bit entropy 00426 for (int j = 0; j < g_dataSize; j++) 00427 { 00428 g_data[j] = (g_data[j]>>3) | (g_data[j]<<5); 00429 } 00430 } 00431 setitimer(ITIMER_REAL, &otv, 0); 00432 00433 // reset signal handler 00434 sigaction(SIGALRM, &osa, 0); 00435 00436 } 00437 00438 void generateRandomDataFromFile(const char* name, int len) 00439 { 00440 int fd = ::open(name, O_RDONLY); 00441 if (fd == -1) 00442 { 00443 return; 00444 } 00445 00446 std::vector<char> buf(len); 00447 int bytesRead = ::read(fd, &buf[0], len); 00448 if (bytesRead == -1) 00449 { 00450 return; 00451 } 00452 buf.resize(bytesRead); 00453 ::RAND_add(&buf[0], buf.size(), 0.0); 00454 // 0 entropy, since this could all be observable by someone else. 00455 } 00456 00457 void generateRandomDataFromTime(double entropy) 00458 { 00459 struct timeval tv; 00460 ::gettimeofday(&tv, 0); 00461 ::RAND_add(&tv, sizeof(tv), entropy); 00462 00463 clock_t c(::clock()); 00464 ::RAND_add(&c, sizeof(c), entropy); 00465 00466 struct rusage ru; 00467 ::getrusage(RUSAGE_SELF, &ru); 00468 ::RAND_add(&ru, sizeof(ru), entropy); 00469 00470 ::getrusage(RUSAGE_CHILDREN, &ru); 00471 ::RAND_add(&ru, sizeof(ru), entropy); 00472 } 00473 00474 struct cmd 00475 { 00476 const char* command; 00477 00478 // estimated number of bytes of entropy per 1K of output 00479 double usefulness; 00480 }; 00481 00482 // This list of sources comes from gnupg, prngd and egd. 00483 const cmd randomSourceCommands[] = 00484 { 00485 { "advfsstat -b usr_domain", 0.01 }, 00486 { "advfsstat -l 2 usr_domain", 0.5 }, 00487 { "advfsstat -p usr_domain", 0.01 }, 00488 { "arp -a -n", 0.5 }, 00489 { "df", 0.5 }, 00490 { "df -i", 0.5 }, 00491 { "df -a", 0.5 }, 00492 { "df -in", 0.5 }, 00493 { "dmesg", 0.5 }, 00494 { "errpt -a", 0.5 }, 00495 { "ifconfig -a", 0.5 }, 00496 { "iostat", 0.5 }, 00497 { "ipcs -a", 0.5 }, 00498 { "last", 0.5 }, 00499 { "lastlog", 0.5 }, 00500 { "lpstat -t", 0.1 }, 00501 { "ls -alniR /var/log", 1.0 }, 00502 { "ls -alniR /var/adm", 1.0 }, 00503 { "ls -alni /var/spool/mail", 1.0 }, 00504 { "ls -alni /proc", 1.0 }, 00505 { "ls -alniR /tmp", 1.0 }, 00506 { "ls -alniR /var/tmp", 1.0 }, 00507 { "ls -alni /var/mail", 1.0 }, 00508 { "ls -alniR /var/db", 1.0 }, 00509 { "ls -alniR /etc", 1.0 }, 00510 { "ls -alniR /private/var/log", 1.0 }, 00511 { "ls -alniR /private/var/db", 1.0 }, 00512 { "ls -alniR /private/etc", 1.0 }, 00513 { "ls -alniR /private/tmp", 1.0 }, 00514 { "ls -alniR /private/var/tmp", 1.0 }, 00515 { "mpstat", 1.5 }, 00516 { "netstat -s", 1.5 }, 00517 { "netstat -n", 1.5 }, 00518 { "netstat -a -n", 1.5 }, 00519 { "netstat -anv", 1.5 }, 00520 { "netstat -i -n", 0.5 }, 00521 { "netstat -r -n", 0.1 }, 00522 { "netstat -m", 0.5 }, 00523 { "netstat -ms", 0.5 }, 00524 { "nfsstat", 0.5 }, 00525 { "ps laxww", 1.5 }, 00526 { "ps -laxww", 1.5 }, 00527 { "ps -al", 1.5 }, 00528 { "ps -el", 1.5 }, 00529 { "ps -efl", 1.5 }, 00530 { "ps -efly", 1.5 }, 00531 { "ps aux", 1.5 }, 00532 { "ps -A", 1.5 }, 00533 { "pfstat", 0.5 }, 00534 { "portstat", 0.5 }, 00535 { "pstat -p", 0.5 }, 00536 { "pstat -S", 0.5 }, 00537 { "pstat -A", 0.5 }, 00538 { "pstat -t", 0.5 }, 00539 { "pstat -v", 0.5 }, 00540 { "pstat -x", 0.5 }, 00541 { "pstat -t", 0.5 }, 00542 { "ripquery -nw 1 127.0.0.1", 0.5 }, 00543 { "sar -A 1 1", 0.5 }, 00544 { "snmp_request localhost public get 1.3.6.1.2.1.7.1.0", 0.5 }, 00545 { "snmp_request localhost public get 1.3.6.1.2.1.7.4.0", 0.5 }, 00546 { "snmp_request localhost public get 1.3.6.1.2.1.4.3.0", 0.5 }, 00547 { "snmp_request localhost public get 1.3.6.1.2.1.6.10.0", 0.5 }, 00548 { "snmp_request localhost public get 1.3.6.1.2.1.6.11.0", 0.5 }, 00549 { "snmp_request localhost public get 1.3.6.1.2.1.6.13.0", 0.5 }, 00550 { "snmp_request localhost public get 1.3.6.1.2.1.5.1.0", 0.5 }, 00551 { "snmp_request localhost public get 1.3.6.1.2.1.5.3.0", 0.5 }, 00552 { "tail -c 1024 /var/log/messages", 1.0 }, 00553 { "tail -c 1024 /var/log/syslog", 1.0 }, 00554 { "tail -c 1024 /var/log/system.log", 1.0 }, 00555 { "tail -c 1024 /var/log/debug", 1.0 }, 00556 { "tail -c 1024 /var/adm/messages", 1.0 }, 00557 { "tail -c 1024 /var/adm/syslog", 1.0 }, 00558 { "tail -c 1024 /var/adm/syslog/mail.log", 1.0 }, 00559 { "tail -c 1024 /var/adm/syslog/syslog.log", 1.0 }, 00560 { "tail -c 1024 /var/log/maillog", 1.0 }, 00561 { "tail -c 1024 /var/adm/maillog", 1.0 }, 00562 { "tail -c 1024 /var/adm/SPlogs/SPdaemon.log", 1.0 }, 00563 { "tail -c 1024 /usr/es/adm/cluster.log", 1.0 }, 00564 { "tail -c 1024 /usr/adm/cluster.log", 1.0 }, 00565 { "tail -c 1024 /var/adm/cluster.log", 1.0 }, 00566 { "tail -c 1024 /var/adm/ras/conslog", 1.0 }, 00567 { "tcpdump -c 100 -efvvx", 1 }, 00568 { "uptime", 0.5 }, 00569 { "vmstat", 2.0 }, 00570 { "vmstat -c", 2.0 }, 00571 { "vmstat -s", 2.0 }, 00572 { "vmstat -i", 2.0 }, 00573 { "vmstat -f", 2.0 }, 00574 { "w", 2.5 }, 00575 { "who -u", 0.5 }, 00576 { "who -i", 0.5 }, 00577 { "who -a", 0.5 }, 00578 00579 { 0, 0 } 00580 }; 00581 00582 class RandomOutputGatherer : public Exec::OutputCallback 00583 { 00584 private: 00585 virtual void doHandleData( 00586 const char* data, size_t dataLen, Exec::EOutputSource outputSource, 00587 const ProcessRef& theProc, size_t streamIndex, 00588 Array<char>& inputBuffer 00589 ) 00590 { 00591 if (outputSource == Exec::E_STDERR) 00592 { 00593 // for all the commands we run, anything output to stderr 00594 // doesn't have any entropy. 00595 ::RAND_add(data, dataLen, 0.0); 00596 } 00597 else 00598 { 00599 // streamIndex is the index into the PopenStreams array which 00600 // correlates to randomSourceCommands 00601 ::RAND_add( 00602 data, dataLen, 00603 randomSourceCommands[streamIndex].usefulness * 00604 static_cast<double>(dataLen) / 1024.0 00605 ); 00606 } 00607 // the actual length of stuff we got could be random, but we can't 00608 // say for sure, so it gets 0.0 entropy. 00609 ::RAND_add(&dataLen, sizeof(dataLen), 0.0); 00610 ::RAND_add(&outputSource, sizeof(outputSource), 0.0); 00611 // The timing is random too. 00612 generateRandomDataFromTime(0.1); 00613 } 00614 00615 }; 00616 00617 class RandomInputCallback : public Exec::InputCallback 00618 { 00619 private: 00620 virtual void doGetData( 00621 Array<char>& inputBuffer, const ProcessRef& theProcess, 00622 size_t streamIndex 00623 ) 00624 { 00625 // none of the processes we run need data from stdin 00626 if (theProcess->in()->isOpen()) 00627 { 00628 theProcess->in()->close(); 00629 } 00630 } 00631 }; 00632 00633 String locateInPath(const String& cmd, const String& path) 00634 { 00635 StringArray pathElements(path.tokenize(":")); 00636 for (size_t i = 0; i < pathElements.size(); ++i) 00637 { 00638 String testCmd(pathElements[i] + '/' + cmd); 00639 if (FileSystem::exists(testCmd)) 00640 { 00641 return testCmd; 00642 } 00643 } 00644 return cmd; 00645 } 00646 00647 class RandomTimerThread : public Thread 00648 { 00649 virtual Int32 run() 00650 { 00651 // don't initialize to anything, as we may pick up some good 00652 // random junk off the stack. 00653 unsigned char buf[256]; 00654 int iterations = 8; 00655 generateRandomTimerData(buf, sizeof(buf), &iterations); 00656 ::RAND_add(buf, sizeof(buf), 32); 00657 // 32 is if we assume 1 bit per byte, and most systems should have 00658 // something better than that. 00659 00660 generateRandomDataFromTime(0.1); 00661 00662 return 0; 00663 } 00664 }; 00665 #endif 00666 00667 void rand_init_impl() 00668 { 00669 #ifdef BLOCXX_WIN32 00670 // There are issues on win32 with calling RAND_status() w/out sufficient 00671 // entropy in a threaded environment, so we'll just add some before 00672 // calling RAND_status() 00673 HCRYPTPROV hProvider = 0; 00674 BYTE buf[64]; 00675 00676 if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, 00677 CRYPT_VERIFYCONTEXT)) 00678 { 00679 if (CryptGenRandom(hProvider, sizeof(buf), buf)) 00680 { 00681 RAND_add(buf, sizeof(buf), sizeof(buf)); 00682 } 00683 CryptReleaseContext(hProvider, 0); 00684 } 00685 // provided by OpenSSL. Try doing something in addition to 00686 // CryptGenRandom(), since we can't trust closed source. 00687 ::RAND_screen(); 00688 #endif 00689 00690 // try a egd socket that OpenSSL wouldn't by default. 00691 RAND_egd(BLOCXX_DEFAULT_STATE_DIR"/egd-pool"); 00692 00693 // with OpenSSL 0.9.7 calling RAND_status() will try to load 00694 // sufficient randomness, so hopefully we won't have to do anything. 00695 if (::RAND_status() == 1) 00696 { 00697 return; 00698 } 00699 00700 #ifndef BLOCXX_WIN32 00701 // OpenSSL 0.9.7 does this automatically, so only try if we've got an 00702 // older version of OpenSSL. 00703 if (::SSLeay() < 0x00907000L) 00704 { 00705 // now try adding in /dev/random 00706 int loadedBytes = RAND_load_file("/dev/random", 1024); 00707 if (loadedBytes == 0) 00708 { 00709 // okay, no /dev/random... try adding in /dev/urandom 00710 RAND_load_file("/dev/urandom", 1024); 00711 } 00712 00713 if (RAND_status() == 1) 00714 { 00715 return; 00716 } 00717 00718 // now try adding in data from an entropy gathering daemon (egd) 00719 const char *names[] = 00720 { 00721 "/var/run/egd-pool", 00722 "/dev/egd-pool", 00723 "/etc/egd-pool", 00724 "/etc/entropy", 00725 NULL 00726 }; 00727 00728 for (int i = 0; names[i]; i++) 00729 { 00730 if (RAND_egd(names[i]) != -1) 00731 { 00732 break; 00733 } 00734 } 00735 00736 if (RAND_status() == 1) 00737 { 00738 return; 00739 } 00740 } 00741 00742 // try loading up randomness from a previous run. 00743 char randFile[MAXPATHLEN]; 00744 const char* rval = ::RAND_file_name(randFile, MAXPATHLEN); 00745 if (rval) 00746 { 00747 using namespace FileSystem::Path; 00748 try 00749 { 00750 if (security(randFile).first == E_SECURE_FILE) 00751 { 00752 ::RAND_load_file(randFile, -1); 00753 } 00754 } 00755 catch (FileSystemException& e) 00756 { 00757 // ignore 00758 } 00759 } 00760 00761 // don't check RAND_status() again, since we don't really trust the 00762 // random file to be very secure--there are too many ways an attacker 00763 // could get or change it, so we'll do this other stuff as well. 00764 00765 // we're on a really broken system. We'll try to get some random data 00766 // by: 00767 // - running commands that reflect random system activity. 00768 // This is the same approach a egd daemon would do, but we do it only 00769 // once to seed the randomness. 00770 // The list of sources comes from gnupg, prngd and egd. 00771 // - use a timing based approach which gives decent randomness. 00772 // - use other variable things, such as pid, execution times, etc. 00773 // most of these values have an entropy of 0, since they are 00774 // observable to any other user on the system, so even though they 00775 // are random, they're observable, and we can't count them as entropy. 00776 00777 // do the time based ones before we start, after the timing tests, 00778 // and then again after running commands. 00779 generateRandomDataFromTime(0.0); 00780 00781 RandomTimerThread randomTimerThread; 00782 randomTimerThread.start(); 00783 00784 // - read some portions of files and dirs (e.g. /dev/mem) if possible 00785 const char* files[] = { "/dev/mem", 0 }; 00786 for (const char** p = files; *p; ++p) 00787 { 00788 generateRandomDataFromFile(*p, 1024*1024*2); 00789 } 00790 00791 generateRandomDataFromTime(0.1); 00792 00793 pid_t myPid(::getpid()); 00794 ::RAND_add(&myPid, sizeof(myPid), 0.0); 00795 00796 pid_t parentPid(::getppid()); 00797 ::RAND_add(&parentPid, sizeof(parentPid), 0.0); 00798 00799 uid_t myUid(::getuid()); 00800 ::RAND_add(&myUid, sizeof(myUid), 0.0); 00801 00802 gid_t myGid(::getgid()); 00803 ::RAND_add(&myGid, sizeof(myGid), 0.0); 00804 00805 // now run commands 00806 Array<ProcessRef> procs; 00807 for (size_t i = 0; randomSourceCommands[i].command != 0; ++i) 00808 { 00809 StringArray cmd = String(randomSourceCommands[i].command).tokenize(); 00810 if( cmd.empty() ) 00811 { 00812 // This shouldn't happen unless someone messes up the command table above. 00813 continue; 00814 } 00815 // If it isn't an absolute path, search for it. 00816 if (cmd[0][0] != '/') 00817 { 00818 const char * RANDOM_COMMAND_PATH = 00819 "/bin:/sbin:/usr/bin:/usr/sbin:/usr/ucb:/usr/etc:/usr/bsd:" 00820 "/etc:/usr/local/bin:/usr/local/sbin"; 00821 00822 // Attempt to locate the command in our chosen "secure" path 00823 String locatedCmd = locateInPath(cmd[0], RANDOM_COMMAND_PATH); 00824 if( locatedCmd == cmd[0] ) 00825 { 00826 // This command must not exist. Skip it to avoid long delays of attempted execution. 00827 continue; 00828 } 00829 00830 try 00831 { 00832 using namespace FileSystem::Path; 00833 if (security(locatedCmd).first != E_SECURE_FILE) 00834 { 00835 // This command is not in a secure location, we are going to skip it. 00836 continue; 00837 } 00838 } 00839 catch (FileSystemException& e) 00840 { 00841 // ignore the error and continue 00842 continue; 00843 } 00844 00845 cmd[0] = locatedCmd; 00846 } 00847 try 00848 { 00849 // This may throw an exception before the command is executed. We 00850 // can't let it cause the random init to fail. 00851 procs.push_back(Exec::spawn(cmd)); 00852 } 00853 catch(const ExecErrorException& e) 00854 { 00855 // Ignore it. We'll get random data from the other commands. 00856 } 00857 } 00858 00859 RandomOutputGatherer randomOutputGatherer; 00860 RandomInputCallback randomInputCallback; 00861 const Timeout RANDOM_COMMAND_TIMEOUT = Timeout::relative(10.0); 00862 try 00863 { 00864 Exec::processInputOutput( 00865 randomOutputGatherer, procs, 00866 randomInputCallback, RANDOM_COMMAND_TIMEOUT 00867 ); 00868 } 00869 catch (ExecTimeoutException&) 00870 { 00871 // ignore it. 00872 } 00873 00874 // terminate all the processes and add their return code to the pool. 00875 for (size_t i = 0; i < procs.size(); ++i) 00876 { 00877 procs[i]->waitCloseTerm(Timeout::relative(0.001), Timeout::relative(0.002), Timeout::relative(0.003)); 00878 Process::Status status = procs[i]->processStatus(); 00879 if (!status.terminatedSuccessfully()) 00880 { 00881 int rv = status.exitStatus(); 00882 ::RAND_add(&rv, sizeof(rv), 0.0); 00883 } 00884 } 00885 00886 randomTimerThread.join(); 00887 00888 generateRandomDataFromTime(0.1); 00889 #endif 00890 } // rand_init_impl 00891 00892 } // anonymous namespace 00893 #endif // #ifdef BLOCXX_HAVE_OPENSSL