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 
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"
52 #include "blocxx/GlobalMutex.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"
59 #include "blocxx/UnnamedPipe.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 
85 using namespace blocxx;
86 
87 namespace
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 
132 namespace BLOCXX_NAMESPACE
133 {
134 BLOCXX_DEFINE_EXCEPTION(SecureRand);
135 
136 namespace 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 
160  return BLOCXX_INVALID_HANDLE;
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 
354 namespace
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 
365 extern "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 
382 namespace
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  {
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