blocxx
SecureRand.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Vintela, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Vintela, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
38
39#include "blocxx/BLOCXX_config.h"
40
41#ifdef BLOCXX_HAVE_OPENSSL
42// If you don't have SSL, you don't have cryptographically secure random
43// numbers. Don't try to fall back to a weaker PRNG, as this violates the
44// security principle of "fail safe".
45
46#include "blocxx/Array.hpp"
47#include "blocxx/Assertion.hpp"
48#include "blocxx/Exec.hpp"
49#include "blocxx/FileSystem.hpp"
50#include "blocxx/Mutex.hpp"
51#include "blocxx/MutexLock.hpp"
53#include "blocxx/Secure.hpp"
54#include "blocxx/SecureRand.hpp"
55#include "blocxx/SSLCtxMgr.hpp"
56#include "blocxx/String.hpp"
57#include "blocxx/Thread.hpp"
58#include "blocxx/ThreadOnce.hpp"
60#include "blocxx/UserUtils.hpp"
61#include "blocxx/Process.hpp"
62
63#include <cmath>
64#include <csignal>
65#include <cstring> // for std::memset
66#include <limits>
67#ifdef BLOCXX_HAVE_UNISTD_H
68#include <unistd.h>
69#endif
70
71#include <fcntl.h>
72#include <openssl/rand.h>
73#include <openssl/err.h>
74#ifdef BLOCXX_HAVE_SYS_RESOURCE_H
75#include <sys/resource.h>
76#endif
77#ifndef BLOCXX_WIN32
78#include <sys/time.h>
79#endif
80
81#ifdef BLOCXX_WIN32
82#include <wincrypt.h>
83#endif
84
85using namespace blocxx;
86
87namespace
88{
89 unsigned const RESEED_BYTES = 16; // 128 bits
90 unsigned const SEED_BYTES = 16; // 128 bits
91
92 template <typename T> struct unsigned_equivalent
93 {
94 typedef T type;
95 };
96
97 template <> struct unsigned_equivalent<char>
98 {
99 typedef unsigned char type;
100 };
101
102 template <> struct unsigned_equivalent<signed char>
103 {
104 typedef unsigned char type;
105 };
106
107 template <> struct unsigned_equivalent<short>
108 {
109 typedef unsigned short type;
110 };
111
112 template <> struct unsigned_equivalent<int>
113 {
114 typedef unsigned int type;
115 };
116
117 template <> struct unsigned_equivalent<long>
118 {
119 typedef unsigned long type;
120 };
121
122 template <> struct unsigned_equivalent<long long>
123 {
124 typedef unsigned long long type;
125 };
126
127 blocxx::OnceFlag guard = BLOCXX_ONCE_INIT;
128
129 void rand_init_impl();
130}
131
132namespace BLOCXX_NAMESPACE
133{
134BLOCXX_DEFINE_EXCEPTION(SecureRand);
135
136namespace Secure
137{
138 void rand_init()
139 {
140 callOnce(guard, &rand_init_impl);
141 }
142
143 unsigned char * rand(unsigned char * buf, std::size_t n)
144 {
145 callOnce(guard, &rand_init_impl);
146 ERR_clear_error();
147 if (!RAND_bytes(buf, n))
148 {
149 BLOCXX_THROW(SecureRandException,
150 SSLCtxMgr::getOpenSSLErrorDescription().c_str());
151 }
152 return buf;
153 }
154
155 ::pid_t fork_reseed()
156 {
157#ifdef BLOCXX_WIN32
158#pragma message(Reminder "TODO: implement it for Win!")
159
161#else
162 unsigned char seed[2][RESEED_BYTES];
163 rand(seed[0], sizeof(seed[0]));
164 rand(seed[1], sizeof(seed[1]));
165
166 ::pid_t rv = ::fork();
167 if (rv < 0)
168 {
169 return rv;
170 }
171
172 std::size_t idx = rv > 0; // 0 or 1
173 RAND_seed(seed[idx], sizeof(seed[idx]));
174 // forget other process's seed
175 std::memset(seed[1 - idx], 0, sizeof(seed[1- idx]));
176
177 return rv;
178#endif
179 }
180
181 namespace Impl
182 {
183 template <typename UnsignedInt>
184 UnsignedInt rand_uint_lt(UnsignedInt n)
185 {
186 BLOCXX_ASSERT(n > 0);
187 if ((n & (n - 1)) == 0) // n is a power of two
188 {
189 return rand_uint<UnsignedInt>() % n;
190 }
191 UnsignedInt const uint_max = static_cast<UnsignedInt>(-1);
192 UnsignedInt const bound = uint_max - (uint_max % n);
193 UnsignedInt rn;
194 do
195 {
196 rn = rand_uint<UnsignedInt>();
197 } while (rn >= bound);
198 return rn % n;
199 }
200
201 // Explicit instantiation
202 template unsigned char
203 BLOCXX_COMMON_API rand_uint_lt<unsigned char>(unsigned char);
204 template unsigned short
205 BLOCXX_COMMON_API rand_uint_lt<unsigned short>(unsigned short);
206 template unsigned int
207 BLOCXX_COMMON_API rand_uint_lt<unsigned int>(unsigned int);
208 template unsigned long
209 BLOCXX_COMMON_API rand_uint_lt<unsigned long>(unsigned long);
210 template unsigned long long
211 BLOCXX_COMMON_API rand_uint_lt<unsigned long long>(unsigned long long);
212
213 template <typename Integer>
214 Integer rand_range(Integer min_value, Integer max_value)
215 {
216 BLOCXX_ASSERT(max_value >= min_value);
217
218 // The following code uses these properties of C++:
219 // - Conversions from a signed integer to an unsigned integer
220 // of the same size are always well-defined.
221 // - Arithmetic for unsigned integers is module 2^n, where n
222 // is the number of bits.
223 // - If signed integer k is negative, then converting it to
224 // the equivalent unsigned integer yields 2^n + k.
225
226 typedef typename unsigned_equivalent<Integer>::type UnsignedInt;
227 UnsignedInt const umax = static_cast<UnsignedInt>(max_value);
228 UnsignedInt const umin = static_cast<UnsignedInt>(min_value);
229 UnsignedInt const diff = umax - umin;
230
231 // diff is the mathematical difference between max_value
232 // and min_value, which may not be representable as an Integer,
233 // but is guaranteed to be representable as an UnsignedInt.
234
235 UnsignedInt const range = diff + static_cast<UnsignedInt>(1);
236
237 // range == 0 iff every UnsignedInt value corresponds to
238 // a unique Integer value (e.g., two's complement representation
239 // instead of sign-magnitude), min_value is the smallest possible
240 // Integer value, and max_value is the largest possible Integer
241 // value.
242
243 UnsignedInt rv;
244 if (range == 0)
245 {
246 // All Integer values are allowed return values, and there
247 // is a one-to-one mapping from UnsignedInt values to
248 // Integer values.
249 rv = rand_uint<UnsignedInt>();
250 }
251 else
252 {
253 // Compute the UnsignedInt value corresponding to the desired
254 // Integer value. This works even if min_value < 0 and
255 // max_value >= 0, because the arithmetic is module 2^n.
256 rv = umin + rand_uint_lt(range);
257 }
258 return static_cast<Integer>(rv);
259 }
260
261 // explicit instantiations
262 template char
263 BLOCXX_COMMON_API rand_range(char, char);
264 template signed char
265 BLOCXX_COMMON_API rand_range(signed char, signed char);
266 template unsigned char
267 BLOCXX_COMMON_API rand_range(unsigned char, unsigned char);
268 template short
269 BLOCXX_COMMON_API rand_range(short, short);
270 template unsigned short
271 BLOCXX_COMMON_API rand_range(unsigned short, unsigned short);
272 template int
273 BLOCXX_COMMON_API rand_range(int, int);
274 template unsigned int
275 BLOCXX_COMMON_API rand_range(unsigned int, unsigned int);
276 template long
277 BLOCXX_COMMON_API rand_range(long, long);
278 template unsigned long
279 BLOCXX_COMMON_API rand_range(unsigned long, unsigned long);
280 template long long
281 BLOCXX_COMMON_API rand_range(long long, long long);
282 template unsigned long long
283 BLOCXX_COMMON_API rand_range(unsigned long long, unsigned long long);
284
285 template <unsigned int N>
286 struct log2
287 {
288 enum { value = 1 + log2<N/2>::value };
289 };
290
291 template <>
292 struct log2<1>
293 {
294 enum { value = 0 };
295 };
296
297 // # of mantissa bits if Number is a floating-point type.
298 template <typename Number>
299 struct bits_precision
300 {
301 typedef std::numeric_limits<Number> lim_t;
302 enum { value = lim_t::digits * log2<lim_t::radix>::value };
303 };
304
305 template <typename Real>
306 Real rand_unit_interval()
307 {
308 typedef UInt32 uint_t;
309 int const UINT_BITS = 32;
310 int const NUINT =
311 (bits_precision<Real>::value + UINT_BITS - 1) / UINT_BITS;
312 Real rv = 0.0;
313 for (int i = 1; i <= NUINT; ++i)
314 {
315 Real r = static_cast<Real>(rand_uint<uint_t>());
316 rv += std::ldexp(r, -UINT_BITS * i);
317 }
318 return rv;
319 }
320
321 // explicit instantiations
322 template float rand_unit_interval<float>();
323 template double rand_unit_interval<double>();
324 template long double rand_unit_interval<long double>();
325
326 } // namespace Impl
327
328 void rand_save_state()
329 {
330 char randFile[MAXPATHLEN];
331 char const * rval = RAND_file_name(randFile, MAXPATHLEN);
332 if (rval)
333 {
334 // we only create this file is there's no chance an attacker
335 // could read or write it. see Network Security with OpenSSL p. 101
336 using namespace FileSystem::Path;
337 if (security(dirname(randFile)).first == E_SECURE_DIR)
338 {
339 if (RAND_write_file(randFile) <= 0)
340 {
341 // in case "the bytes written were generated without
342 // appropriate seed.", we don't want to load it up next
343 // time.
344 FileSystem::removeFile(randFile);
345 }
346 }
347 }
348
349 }
350
351} // namespace Secure
352} // namespace BLOCXX_NAMESPACE
353
354namespace
355{
356 // These are used to generate random data via signal delivery timing
357 // differences. We have to use global data since it's modified from a
358 // signal handler.
359 volatile sig_atomic_t g_counter;
360 volatile unsigned char* g_data;
361 volatile sig_atomic_t g_dataIdx;
362 int g_dataSize;
363}
364
365extern "C"
366{
367 // this needs to still be static, since it gets exported because of
368 // extern "C"
369#ifdef BLOCXX_NCR
370 static void randomALRMHandler()
371#else
372 static void randomALRMHandler(int sig)
373#endif
374 {
375 if (g_dataIdx < g_dataSize)
376 {
377 g_data[g_dataIdx++] ^= g_counter & 0xFF;
378 }
379 }
380}
381
382namespace
383{
384 GlobalMutex g_randomTimerGuard = BLOCXX_GLOBAL_MUTEX_INIT();
385
386#ifndef BLOCXX_WIN32
387 // This function will continue to iterate until *iterations <= 0.
388 // *iterations may be set by another thread. *iterations should not be < 8.
389 void generateRandomTimerData(unsigned char* data, int size, int* iterations)
390 {
391 BLOCXX_ASSERT(data != 0);
392 BLOCXX_ASSERT(size > 0);
393 BLOCXX_ASSERT(iterations != 0);
394
395 // make sure we only have one thread running this at a time.
396 MutexLock l(g_randomTimerGuard);
397
398 // set up the global data for the signal handler
399 g_data = data;
400 g_dataSize = size;
401 g_dataIdx = 0;
402
403 // install our ALRM handler
404 struct sigaction sa, osa;
405 sa.sa_handler = randomALRMHandler;
406 sa.sa_flags = 0;
407 sigemptyset(&sa.sa_mask);
408 sigaction(SIGALRM, &sa, &osa);
409
410 // Start timer
411 struct ::itimerval tv, otv;
412 tv.it_value.tv_sec = 0;
413 tv.it_value.tv_usec = 10 * 1000; // 10 ms
414 tv.it_interval = tv.it_value;
415 setitimer(ITIMER_REAL, &tv, &otv);
416
417 while ((*iterations)-- > 0)
418 {
419 // g_dataIdx++ in sigALRM
420 for (g_dataIdx = 0; g_dataIdx < g_dataSize;)
421 {
422 ++g_counter;
423 }
424 // rotate the bits to accomodate for a possible lack of
425 // low-bit entropy
426 for (int j = 0; j < g_dataSize; j++)
427 {
428 g_data[j] = (g_data[j]>>3) | (g_data[j]<<5);
429 }
430 }
431 setitimer(ITIMER_REAL, &otv, 0);
432
433 // reset signal handler
434 sigaction(SIGALRM, &osa, 0);
435
436 }
437
438 void generateRandomDataFromFile(const char* name, int len)
439 {
440 int fd = ::open(name, O_RDONLY);
441 if (fd == -1)
442 {
443 return;
444 }
445
446 std::vector<char> buf(len);
447 int bytesRead = ::read(fd, &buf[0], len);
448 if (bytesRead == -1)
449 {
450 return;
451 }
452 buf.resize(bytesRead);
453 ::RAND_add(&buf[0], buf.size(), 0.0);
454 // 0 entropy, since this could all be observable by someone else.
455 }
456
457 void generateRandomDataFromTime(double entropy)
458 {
459 struct timeval tv;
460 ::gettimeofday(&tv, 0);
461 ::RAND_add(&tv, sizeof(tv), entropy);
462
463 clock_t c(::clock());
464 ::RAND_add(&c, sizeof(c), entropy);
465
466 struct rusage ru;
467 ::getrusage(RUSAGE_SELF, &ru);
468 ::RAND_add(&ru, sizeof(ru), entropy);
469
470 ::getrusage(RUSAGE_CHILDREN, &ru);
471 ::RAND_add(&ru, sizeof(ru), entropy);
472 }
473
474 struct cmd
475 {
476 const char* command;
477
478 // estimated number of bytes of entropy per 1K of output
479 double usefulness;
480 };
481
482 // This list of sources comes from gnupg, prngd and egd.
483 const cmd randomSourceCommands[] =
484 {
485 { "advfsstat -b usr_domain", 0.01 },
486 { "advfsstat -l 2 usr_domain", 0.5 },
487 { "advfsstat -p usr_domain", 0.01 },
488 { "arp -a -n", 0.5 },
489 { "df", 0.5 },
490 { "df -i", 0.5 },
491 { "df -a", 0.5 },
492 { "df -in", 0.5 },
493 { "dmesg", 0.5 },
494 { "errpt -a", 0.5 },
495 { "ifconfig -a", 0.5 },
496 { "iostat", 0.5 },
497 { "ipcs -a", 0.5 },
498 { "last", 0.5 },
499 { "lastlog", 0.5 },
500 { "lpstat -t", 0.1 },
501 { "ls -alniR /var/log", 1.0 },
502 { "ls -alniR /var/adm", 1.0 },
503 { "ls -alni /var/spool/mail", 1.0 },
504 { "ls -alni /proc", 1.0 },
505 { "ls -alniR /tmp", 1.0 },
506 { "ls -alniR /var/tmp", 1.0 },
507 { "ls -alni /var/mail", 1.0 },
508 { "ls -alniR /var/db", 1.0 },
509 { "ls -alniR /etc", 1.0 },
510 { "ls -alniR /private/var/log", 1.0 },
511 { "ls -alniR /private/var/db", 1.0 },
512 { "ls -alniR /private/etc", 1.0 },
513 { "ls -alniR /private/tmp", 1.0 },
514 { "ls -alniR /private/var/tmp", 1.0 },
515 { "mpstat", 1.5 },
516 { "netstat -s", 1.5 },
517 { "netstat -n", 1.5 },
518 { "netstat -a -n", 1.5 },
519 { "netstat -anv", 1.5 },
520 { "netstat -i -n", 0.5 },
521 { "netstat -r -n", 0.1 },
522 { "netstat -m", 0.5 },
523 { "netstat -ms", 0.5 },
524 { "nfsstat", 0.5 },
525 { "ps laxww", 1.5 },
526 { "ps -laxww", 1.5 },
527 { "ps -al", 1.5 },
528 { "ps -el", 1.5 },
529 { "ps -efl", 1.5 },
530 { "ps -efly", 1.5 },
531 { "ps aux", 1.5 },
532 { "ps -A", 1.5 },
533 { "pfstat", 0.5 },
534 { "portstat", 0.5 },
535 { "pstat -p", 0.5 },
536 { "pstat -S", 0.5 },
537 { "pstat -A", 0.5 },
538 { "pstat -t", 0.5 },
539 { "pstat -v", 0.5 },
540 { "pstat -x", 0.5 },
541 { "pstat -t", 0.5 },
542 { "ripquery -nw 1 127.0.0.1", 0.5 },
543 { "sar -A 1 1", 0.5 },
544 { "snmp_request localhost public get 1.3.6.1.2.1.7.1.0", 0.5 },
545 { "snmp_request localhost public get 1.3.6.1.2.1.7.4.0", 0.5 },
546 { "snmp_request localhost public get 1.3.6.1.2.1.4.3.0", 0.5 },
547 { "snmp_request localhost public get 1.3.6.1.2.1.6.10.0", 0.5 },
548 { "snmp_request localhost public get 1.3.6.1.2.1.6.11.0", 0.5 },
549 { "snmp_request localhost public get 1.3.6.1.2.1.6.13.0", 0.5 },
550 { "snmp_request localhost public get 1.3.6.1.2.1.5.1.0", 0.5 },
551 { "snmp_request localhost public get 1.3.6.1.2.1.5.3.0", 0.5 },
552 { "tail -c 1024 /var/log/messages", 1.0 },
553 { "tail -c 1024 /var/log/syslog", 1.0 },
554 { "tail -c 1024 /var/log/system.log", 1.0 },
555 { "tail -c 1024 /var/log/debug", 1.0 },
556 { "tail -c 1024 /var/adm/messages", 1.0 },
557 { "tail -c 1024 /var/adm/syslog", 1.0 },
558 { "tail -c 1024 /var/adm/syslog/mail.log", 1.0 },
559 { "tail -c 1024 /var/adm/syslog/syslog.log", 1.0 },
560 { "tail -c 1024 /var/log/maillog", 1.0 },
561 { "tail -c 1024 /var/adm/maillog", 1.0 },
562 { "tail -c 1024 /var/adm/SPlogs/SPdaemon.log", 1.0 },
563 { "tail -c 1024 /usr/es/adm/cluster.log", 1.0 },
564 { "tail -c 1024 /usr/adm/cluster.log", 1.0 },
565 { "tail -c 1024 /var/adm/cluster.log", 1.0 },
566 { "tail -c 1024 /var/adm/ras/conslog", 1.0 },
567 { "tcpdump -c 100 -efvvx", 1 },
568 { "uptime", 0.5 },
569 { "vmstat", 2.0 },
570 { "vmstat -c", 2.0 },
571 { "vmstat -s", 2.0 },
572 { "vmstat -i", 2.0 },
573 { "vmstat -f", 2.0 },
574 { "w", 2.5 },
575 { "who -u", 0.5 },
576 { "who -i", 0.5 },
577 { "who -a", 0.5 },
578
579 { 0, 0 }
580 };
581
582 class RandomOutputGatherer : public Exec::OutputCallback
583 {
584 private:
585 virtual void doHandleData(
586 const char* data, size_t dataLen, Exec::EOutputSource outputSource,
587 const ProcessRef& theProc, size_t streamIndex,
588 Array<char>& inputBuffer
589 )
590 {
591 if (outputSource == Exec::E_STDERR)
592 {
593 // for all the commands we run, anything output to stderr
594 // doesn't have any entropy.
595 ::RAND_add(data, dataLen, 0.0);
596 }
597 else
598 {
599 // streamIndex is the index into the PopenStreams array which
600 // correlates to randomSourceCommands
601 ::RAND_add(
602 data, dataLen,
603 randomSourceCommands[streamIndex].usefulness *
604 static_cast<double>(dataLen) / 1024.0
605 );
606 }
607 // the actual length of stuff we got could be random, but we can't
608 // say for sure, so it gets 0.0 entropy.
609 ::RAND_add(&dataLen, sizeof(dataLen), 0.0);
610 ::RAND_add(&outputSource, sizeof(outputSource), 0.0);
611 // The timing is random too.
612 generateRandomDataFromTime(0.1);
613 }
614
615 };
616
617 class RandomInputCallback : public Exec::InputCallback
618 {
619 private:
620 virtual void doGetData(
621 Array<char>& inputBuffer, const ProcessRef& theProcess,
622 size_t streamIndex
623 )
624 {
625 // none of the processes we run need data from stdin
626 if (theProcess->in()->isOpen())
627 {
628 theProcess->in()->close();
629 }
630 }
631 };
632
633 String locateInPath(const String& cmd, const String& path)
634 {
635 StringArray pathElements(path.tokenize(":"));
636 for (size_t i = 0; i < pathElements.size(); ++i)
637 {
638 String testCmd(pathElements[i] + '/' + cmd);
639 if (FileSystem::exists(testCmd))
640 {
641 return testCmd;
642 }
643 }
644 return cmd;
645 }
646
647 class RandomTimerThread : public Thread
648 {
649 virtual Int32 run()
650 {
651 // don't initialize to anything, as we may pick up some good
652 // random junk off the stack.
653 unsigned char buf[256];
654 int iterations = 8;
655 generateRandomTimerData(buf, sizeof(buf), &iterations);
656 ::RAND_add(buf, sizeof(buf), 32);
657 // 32 is if we assume 1 bit per byte, and most systems should have
658 // something better than that.
659
660 generateRandomDataFromTime(0.1);
661
662 return 0;
663 }
664 };
665#endif
666
667 void rand_init_impl()
668 {
669#ifdef BLOCXX_WIN32
670 // There are issues on win32 with calling RAND_status() w/out sufficient
671 // entropy in a threaded environment, so we'll just add some before
672 // calling RAND_status()
673 HCRYPTPROV hProvider = 0;
674 BYTE buf[64];
675
676 if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL,
677 CRYPT_VERIFYCONTEXT))
678 {
679 if (CryptGenRandom(hProvider, sizeof(buf), buf))
680 {
681 RAND_add(buf, sizeof(buf), sizeof(buf));
682 }
683 CryptReleaseContext(hProvider, 0);
684 }
685 // provided by OpenSSL. Try doing something in addition to
686 // CryptGenRandom(), since we can't trust closed source.
687 ::RAND_screen();
688#endif
689
690 // try a egd socket that OpenSSL wouldn't by default.
691 RAND_egd(BLOCXX_DEFAULT_STATE_DIR"/egd-pool");
692
693 // with OpenSSL 0.9.7 calling RAND_status() will try to load
694 // sufficient randomness, so hopefully we won't have to do anything.
695 if (::RAND_status() == 1)
696 {
697 return;
698 }
699
700#ifndef BLOCXX_WIN32
701 // OpenSSL 0.9.7 does this automatically, so only try if we've got an
702 // older version of OpenSSL.
703 if (::SSLeay() < 0x00907000L)
704 {
705 // now try adding in /dev/random
706 int loadedBytes = RAND_load_file("/dev/random", 1024);
707 if (loadedBytes == 0)
708 {
709 // okay, no /dev/random... try adding in /dev/urandom
710 RAND_load_file("/dev/urandom", 1024);
711 }
712
713 if (RAND_status() == 1)
714 {
715 return;
716 }
717
718 // now try adding in data from an entropy gathering daemon (egd)
719 const char *names[] =
720 {
721 "/var/run/egd-pool",
722 "/dev/egd-pool",
723 "/etc/egd-pool",
724 "/etc/entropy",
725 NULL
726 };
727
728 for (int i = 0; names[i]; i++)
729 {
730 if (RAND_egd(names[i]) != -1)
731 {
732 break;
733 }
734 }
735
736 if (RAND_status() == 1)
737 {
738 return;
739 }
740 }
741
742 // try loading up randomness from a previous run.
743 char randFile[MAXPATHLEN];
744 const char* rval = ::RAND_file_name(randFile, MAXPATHLEN);
745 if (rval)
746 {
747 using namespace FileSystem::Path;
748 try
749 {
750 if (security(randFile).first == E_SECURE_FILE)
751 {
752 ::RAND_load_file(randFile, -1);
753 }
754 }
755 catch (FileSystemException& e)
756 {
757 // ignore
758 }
759 }
760
761 // don't check RAND_status() again, since we don't really trust the
762 // random file to be very secure--there are too many ways an attacker
763 // could get or change it, so we'll do this other stuff as well.
764
765 // we're on a really broken system. We'll try to get some random data
766 // by:
767 // - running commands that reflect random system activity.
768 // This is the same approach a egd daemon would do, but we do it only
769 // once to seed the randomness.
770 // The list of sources comes from gnupg, prngd and egd.
771 // - use a timing based approach which gives decent randomness.
772 // - use other variable things, such as pid, execution times, etc.
773 // most of these values have an entropy of 0, since they are
774 // observable to any other user on the system, so even though they
775 // are random, they're observable, and we can't count them as entropy.
776
777 // do the time based ones before we start, after the timing tests,
778 // and then again after running commands.
779 generateRandomDataFromTime(0.0);
780
781 RandomTimerThread randomTimerThread;
782 randomTimerThread.start();
783
784 // - read some portions of files and dirs (e.g. /dev/mem) if possible
785 const char* files[] = { "/dev/mem", 0 };
786 for (const char** p = files; *p; ++p)
787 {
788 generateRandomDataFromFile(*p, 1024*1024*2);
789 }
790
791 generateRandomDataFromTime(0.1);
792
793 pid_t myPid(::getpid());
794 ::RAND_add(&myPid, sizeof(myPid), 0.0);
795
796 pid_t parentPid(::getppid());
797 ::RAND_add(&parentPid, sizeof(parentPid), 0.0);
798
799 uid_t myUid(::getuid());
800 ::RAND_add(&myUid, sizeof(myUid), 0.0);
801
802 gid_t myGid(::getgid());
803 ::RAND_add(&myGid, sizeof(myGid), 0.0);
804
805 // now run commands
806 Array<ProcessRef> procs;
807 for (size_t i = 0; randomSourceCommands[i].command != 0; ++i)
808 {
809 StringArray cmd = String(randomSourceCommands[i].command).tokenize();
810 if( cmd.empty() )
811 {
812 // This shouldn't happen unless someone messes up the command table above.
813 continue;
814 }
815 // If it isn't an absolute path, search for it.
816 if (cmd[0][0] != '/')
817 {
818 const char * RANDOM_COMMAND_PATH =
819 "/bin:/sbin:/usr/bin:/usr/sbin:/usr/ucb:/usr/etc:/usr/bsd:"
820 "/etc:/usr/local/bin:/usr/local/sbin";
821
822 // Attempt to locate the command in our chosen "secure" path
823 String locatedCmd = locateInPath(cmd[0], RANDOM_COMMAND_PATH);
824 if( locatedCmd == cmd[0] )
825 {
826 // This command must not exist. Skip it to avoid long delays of attempted execution.
827 continue;
828 }
829
830 try
831 {
832 using namespace FileSystem::Path;
833 if (security(locatedCmd).first != E_SECURE_FILE)
834 {
835 // This command is not in a secure location, we are going to skip it.
836 continue;
837 }
838 }
839 catch (FileSystemException& e)
840 {
841 // ignore the error and continue
842 continue;
843 }
844
845 cmd[0] = locatedCmd;
846 }
847 try
848 {
849 // This may throw an exception before the command is executed. We
850 // can't let it cause the random init to fail.
851 procs.push_back(Exec::spawn(cmd));
852 }
853 catch(const ExecErrorException& e)
854 {
855 // Ignore it. We'll get random data from the other commands.
856 }
857 }
858
859 RandomOutputGatherer randomOutputGatherer;
860 RandomInputCallback randomInputCallback;
861 const Timeout RANDOM_COMMAND_TIMEOUT = Timeout::relative(10.0);
862 try
863 {
864 Exec::processInputOutput(
865 randomOutputGatherer, procs,
866 randomInputCallback, RANDOM_COMMAND_TIMEOUT
867 );
868 }
869 catch (ExecTimeoutException&)
870 {
871 // ignore it.
872 }
873
874 // terminate all the processes and add their return code to the pool.
875 for (size_t i = 0; i < procs.size(); ++i)
876 {
877 procs[i]->waitCloseTerm(Timeout::relative(0.001), Timeout::relative(0.002), Timeout::relative(0.003));
878 Process::Status status = procs[i]->processStatus();
879 if (!status.terminatedSuccessfully())
880 {
881 int rv = status.exitStatus();
882 ::RAND_add(&rv, sizeof(rv), 0.0);
883 }
884 }
885
886 randomTimerThread.join();
887
888 generateRandomDataFromTime(0.1);
889#endif
890 } // rand_init_impl
891
892} // anonymous namespace
893#endif // #ifdef BLOCXX_HAVE_OPENSSL
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_DEFINE_EXCEPTION(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define MAXPATHLEN
#define BLOCXX_GLOBAL_MUTEX_INIT()
#define BLOCXX_INVALID_HANDLE
Definition Types.hpp:136
BLOCXX_COMMON_API std::pair< ESecurity, String > security(String const &path, UserId uid)
BLOCXX_COMMON_API String dirname(const String &filename)
Take a string that contains a pathname, and return a string that is a pathname of the parent director...
BLOCXX_COMMON_API bool removeFile(const String &path)
Remove the given file.
Taken from RFC 1321.
void BLOCXX_COMMON_API callOnce(OnceFlag &flag, FuncT F)
The first time callOnce is called with a given onceFlag argument, it calls func with no argument and ...
Array< String > StringArray
Definition CommonFwd.hpp:73
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex