A lot of cleanups as well as minor API changes.
[libgcrypt.git] / cipher / random.c
index 1b60ade..c9c95cc 100644 (file)
@@ -1,6 +1,6 @@
 /* random.c  - random number generator
  * Copyright (C) 1998, 2000, 2001, 2002, 2003,
- *               2004  Free Software Foundation, Inc.
+ *               2004, 2005, 2006  Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -43,7 +43,7 @@
 #include <sys/times.h>
 #endif
 #ifdef HAVE_GETTIMEOFDAY
-#include <sys/times.h>
+#include <sys/time.h>
 #endif
 #ifdef HAVE_GETRUSAGE
 #include <sys/resource.h>
 #define RAND_MAX 32767
 #endif
 
+/* Check whether we can lock the seed file read write. */
+#if defined(HAVE_FCNTL) && defined(HAVE_FTRUNCATE) && !defined(HAVE_W32_SYSTEM)
+#define LOCK_SEED_FILE 1
+#else
+#define LOCK_SEED_FILE 0
+#endif
+
 
 #if SIZEOF_UNSIGNED_LONG == 8
 #define ADD_VALUE 0xa5a5a5a5a5a5a5a5
 
 
 static int is_initialized;
+static int allow_daemon; /* If true, try to use the daemon first. */
 #define MASK_LEVEL(a) do { (a) &= 3; } while(0)
-static char *rndpool;  /* allocated size is POOLSIZE+BLOCKLEN */
-static char *keypool;  /* allocated size is POOLSIZE+BLOCKLEN */
+static unsigned char *rndpool; /* Allocated size is POOLSIZE+BLOCKLEN.  */
+static unsigned char *keypool; /* Allocated size is POOLSIZE+BLOCKLEN.  */
 static size_t pool_readpos;
 static size_t pool_writepos;
 static int pool_filled;
@@ -97,6 +105,7 @@ static int pool_balance;
 static int just_mixed;
 static int did_initial_extra_seeding;
 static char *seed_file_name;
+static char *daemon_socket_name;
 static int allow_seed_file_update;
 
 static int secure_alloc;
@@ -152,6 +161,7 @@ initialize_basics(void)
       if (err)
         log_fatal ("failed to create the nonce buffer lock: %s\n",
                    strerror (err) );
+      _gcry_daemon_initialize_basics ();
     }
 }
 
@@ -208,6 +218,8 @@ _gcry_random_initialize (int full)
 void
 _gcry_random_dump_stats()
 {
+  /* FIXME: don't we need proper locking here? -mo */
+
     log_info (
            "random usage: poolsize=%d mixed=%lu polls=%lu/%lu added=%lu/%lu\n"
            "              outmix=%lu getlvl1=%lu/%lu getlvl2=%lu/%lu\n",
@@ -239,6 +251,33 @@ _gcry_quick_random_gen( int onoff )
   return faked_rng? 1 : last;
 }
 
+
+void
+_gcry_set_random_daemon_socket (const char *socketname)
+{
+  if (daemon_socket_name)
+    BUG ();
+
+  daemon_socket_name = gcry_xstrdup (socketname);
+}
+
+/* With ONOFF set to 1, enable the use of the daemon.  With ONOFF set
+   to 0, disable the use of the daemon.  With ONOF set to -1, return
+   whether the daemon has been enabled. */
+int
+_gcry_use_random_daemon (int onoff)
+{
+  int last;
+  
+  /* FIXME: This is not really thread safe. */
+  last = allow_daemon;
+  if (onoff != -1)
+    allow_daemon = onoff;
+
+  return last;
+}
+
+
 int
 _gcry_random_is_faked()
 {
@@ -257,13 +296,20 @@ get_random_bytes ( size_t nbytes, int level, int secure)
   byte *buf, *p;
   int err;
 
-  /* First a hack toavoid the strong random using our regression test suite. */
+  /* First a hack to avoid the strong random using our regression test
+     suite. */
   if (quick_test && level > 1)
     level = 1;
 
   /* Make sure the requested level is in range. */
   MASK_LEVEL(level);
 
+  if (allow_daemon &&
+      (p=_gcry_daemon_get_random_bytes (daemon_socket_name,
+                                        nbytes, level,secure)))
+    return p; /* The daemon succeeded. */
+  allow_daemon = 0; /* Daemon failed - switch off. */
+
   /* Lock the pool. */
   err = ath_mutex_lock (&pool_lock);
   if (err)
@@ -342,7 +388,7 @@ gcry_random_bytes( size_t nbytes, enum gcry_random_level level )
 }
 
 /* The public function to return random data of the quality LEVEL;
-   this version of the function retrun the random a buffer allocated
+   this version of the function return the random a buffer allocated
    in secure memory. */
 void *
 gcry_random_bytes_secure( size_t nbytes, enum gcry_random_level level )
@@ -358,9 +404,9 @@ gcry_random_bytes_secure( size_t nbytes, enum gcry_random_level level )
    1 is strong enough for most usage, 2 is good for key generation
    stuff but may be very slow.  */
 void
-gcry_randomize (byte *buffer, size_t length, enum gcry_random_level level)
+gcry_randomize (void *buffer, size_t length, enum gcry_random_level level)
 {
-  byte *p;
+  unsigned char *p;
   int err;
 
   /* Make sure we are initialized. */
@@ -374,6 +420,11 @@ gcry_randomize (byte *buffer, size_t length, enum gcry_random_level level)
   /* Make sure the level is okay. */
   MASK_LEVEL(level);
 
+  if (allow_daemon
+      && !_gcry_daemon_randomize (daemon_socket_name, buffer, length, level))
+    return; /* The daemon succeeded. */
+  allow_daemon = 0; /* Daemon failed - switch off. */
+
   /* Acquire the pool lock. */
   err = ath_mutex_lock (&pool_lock);
   if (err)
@@ -449,16 +500,16 @@ gcry_randomize (byte *buffer, size_t length, enum gcry_random_level level)
    To better protect against implementation errors in this code, we
    xor a digest of the entire pool into the pool before mixing.
 
-   Note, that this function muts only be called with a locked pool.
+   Note: this function must only be called with a locked pool.
  */
 static void
-mix_pool(byte *pool)
+mix_pool(unsigned char *pool)
 {
   static unsigned char failsafe_digest[DIGESTLEN];
   static int failsafe_digest_valid;
 
-  char *hashbuf = pool + POOLSIZE;
-  char *p, *pend;
+  unsigned char *hashbuf = pool + POOLSIZE;
+  unsigned char *p, *pend;
   int i, n;
   RMD160_CONTEXT md;
 
@@ -469,14 +520,14 @@ mix_pool(byte *pool)
   assert (pool_is_locked);
   _gcry_rmd160_init( &md );
 
-  /* loop over the pool */
+  /* Loop over the pool.  */
   pend = pool + POOLSIZE;
   memcpy(hashbuf, pend - DIGESTLEN, DIGESTLEN );
   memcpy(hashbuf+DIGESTLEN, pool, BLOCKLEN-DIGESTLEN);
   _gcry_rmd160_mixblock( &md, hashbuf);
   memcpy(pool, hashbuf, 20 );
 
-  if (failsafe_digest_valid && (char *)pool == rndpool)
+  if (failsafe_digest_valid && pool == rndpool)
     {
       for (i=0; i < 20; i++)
         pool[i] ^= failsafe_digest[i];
@@ -492,7 +543,7 @@ mix_pool(byte *pool)
         memcpy (hashbuf+DIGESTLEN, p+DIGESTLEN, BLOCKLEN-DIGESTLEN);
       else 
         {
-          char *pp = p + DIGESTLEN;
+          unsigned char *pp = p + DIGESTLEN;
           
           for (i=DIGESTLEN; i < BLOCKLEN; i++ )
             {
@@ -502,7 +553,7 @@ mix_pool(byte *pool)
            }
        }
       
-      _gcry_rmd160_mixblock( &md, hashbuf);
+      _gcry_rmd160_mixblock ( &md, hashbuf);
       memcpy(p, hashbuf, 20 );
     }
 
@@ -510,7 +561,7 @@ mix_pool(byte *pool)
        of the pool on the stack, so it is okay not to require secure
        memory here.  Before we use this pool, it will be copied to the
        help buffer anyway. */
-    if ( (char*)pool == rndpool)
+    if ( pool == rndpool)
       {
         _gcry_rmd160_hash_buffer (failsafe_digest, pool, POOLSIZE);
         failsafe_digest_valid = 1;
@@ -529,6 +580,45 @@ _gcry_set_random_seed_file( const char *name )
 }
 
 
+/* Lock an open file identified by file descriptor FD and wait a
+   reasonable time to succeed.  With FOR_WRITE set to true a write
+   lock will be taken.  FNAME is used only for diagnostics. Returns 0
+   on success or -1 on error. */
+static int
+lock_seed_file (int fd, const char *fname, int for_write)
+{
+#if LOCK_SEED_FILE
+  struct flock lck;
+  struct timeval tv;
+  int backoff=0;
+
+  /* We take a lock on the entire file. */
+  memset (&lck, 0, sizeof lck);
+  lck.l_type = for_write? F_WRLCK : F_RDLCK;
+  lck.l_whence = SEEK_SET;
+
+  while (fcntl (fd, F_SETLK, &lck) == -1)
+    {
+      if (errno != EAGAIN && errno != EACCES)
+        {
+          log_info (_("can't lock `%s': %s\n"), fname, strerror (errno));
+          return -1;
+        }
+
+      if (backoff > 2) /* Show the first message after ~2.25 seconds. */
+        log_info( _("waiting for lock on `%s'...\n"), fname);
+      
+      tv.tv_sec = backoff;
+      tv.tv_usec = 250000;
+      select (0, NULL, NULL, NULL, &tv);
+      if (backoff < 10)
+        backoff++ ;
+    }
+#endif /*LOCK_SEED_FILE*/
+  return 0;
+}
+
+
 /*
   Read in a seed form the random_seed file
   and return true if this was successful.
@@ -562,6 +652,11 @@ read_seed_file (void)
       log_info(_("can't open `%s': %s\n"), seed_file_name, strerror(errno) );
       return 0;
     }
+  if (lock_seed_file (fd, seed_file_name, 0))
+    {
+      close (fd);
+      return 0;
+    }
   if (fstat( fd, &sb ) )
     {
       log_info(_("can't stat `%s': %s\n"), seed_file_name, strerror(errno) );
@@ -650,7 +745,7 @@ _gcry_update_random_seed_file()
     log_fatal ("failed to acquire the pool lock: %s\n", strerror (err));
   pool_is_locked = 1;
 
-  /* copy the entropy pool to a scratch pool and mix both of them */
+  /* Copy the entropy pool to a scratch pool and mix both of them. */
   for (i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool;
        i < POOLWORDS; i++, dp++, sp++ ) 
     {
@@ -659,28 +754,41 @@ _gcry_update_random_seed_file()
   mix_pool(rndpool); rndstats.mixrnd++;
   mix_pool(keypool); rndstats.mixkey++;
 
-#ifdef HAVE_DOSISH_SYSTEM
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
   fd = open (seed_file_name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
              S_IRUSR|S_IWUSR );
 #else
-  fd = open (seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR );
+# if LOCK_SEED_FILE
+    fd = open (seed_file_name, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR );
+# else
+    fd = open (seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR );
+# endif
 #endif
 
   if (fd == -1 )
     log_info (_("can't create `%s': %s\n"), seed_file_name, strerror(errno) );
+  else if (lock_seed_file (fd, seed_file_name, 1))
+    {
+      close (fd);
+    }
+#if LOCK_SEED_FILE
+  else if (ftruncate (fd, 0))
+    {
+      log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno));
+      close (fd);
+    }
+#endif /*LOCK_SEED_FILE*/
   else 
     {
       do
         {
           i = write (fd, keypool, POOLSIZE );
         } 
-      while( i == -1 && errno == EINTR );
+      while (i == -1 && errno == EINTR);
       if (i != POOLSIZE) 
-        log_info (_("can't write `%s': %s\n"),
-                  seed_file_name, strerror(errno) );
+        log_info (_("can't write `%s': %s\n"),seed_file_name, strerror(errno));
       if (close(fd))
-        log_info(_("can't close `%s': %s\n"),
-                 seed_file_name, strerror(errno) );
+        log_info (_("can't close `%s': %s\n"),seed_file_name, strerror(errno));
     }
   
   pool_is_locked = 0;
@@ -692,21 +800,39 @@ _gcry_update_random_seed_file()
 
 
 /* Read random out of the pool. This function is the core of the
-   public random fucntions.  Note that Level 0 is not anymore handeld
-   special and in fact an alias for level 1. */
+   public random functions.  Note that Level 0 is not anymore handeld
+   special and in fact an alias for level 1.  Must be called with the
+   pool already locked.  */
 static void
 read_pool (byte *buffer, size_t length, int level)
 {
   int i;
   unsigned long *sp, *dp;
-  volatile pid_t my_pid; /* The volatile is there to make sure the
-                            compiler does not optimize the code away
-                            in case the getpid function is badly
-                            attributed. */
+  /* The volatile is there to make sure the compiler does not optimize
+     the code away in case the getpid function is badly attributed.
+     Note that we keep a pid in a static variable as well as in a
+     stack based one; the latter is to detect ill behaving thread
+     libraries, ignoring the pool mutexes. */
+  static volatile pid_t my_pid = (pid_t)(-1); 
+  volatile pid_t my_pid2;
+
 
  retry:
   /* Get our own pid, so that we can detect a fork. */
-  my_pid = getpid ();
+  my_pid2 = getpid ();
+  if (my_pid == (pid_t)(-1))                                
+    my_pid = my_pid2;
+  if ( my_pid != my_pid2 )
+    {
+      /* We detected a plain fork; i.e. we are now the child.  Update
+         the static pid and add some randomness. */
+      pid_t x;
+
+      my_pid = my_pid2;
+      x = my_pid;
+      add_randomness (&x, sizeof(x), 0);
+      just_mixed = 0; /* Make sure it will get mixed. */
+    }
 
   assert (pool_is_locked);
 
@@ -754,7 +880,7 @@ read_pool (byte *buffer, size_t length, int level)
       pool_balance += needed;
     }
 
-  /* make sure the pool is filled */
+  /* Make sure the pool is filled. */
   while (!pool_filled)
     random_poll();
 
@@ -763,7 +889,10 @@ read_pool (byte *buffer, size_t length, int level)
   
   /* Mix the pid in so that we for sure won't deliver the same random
      after a fork. */
-  add_randomness (&my_pid, sizeof (my_pid), 0);
+  {
+    pid_t apid = my_pid;
+    add_randomness (&apid, sizeof (apid), 0);
+  }
 
   /* Mix the pool (if add_randomness() didn't it). */
   if (!just_mixed)
@@ -781,8 +910,8 @@ read_pool (byte *buffer, size_t length, int level)
   mix_pool(rndpool); rndstats.mixrnd++;
   mix_pool(keypool); rndstats.mixkey++;
 
-  /* Read the required data.  We use a readpointer to read from a
-     different position each time */
+  /* Read the requested data.  We use a read pointer to read from a
+     different position each time */
   while (length--)
     {
       *buffer++ = keypool[pool_readpos++];
@@ -799,17 +928,14 @@ read_pool (byte *buffer, size_t length, int level)
 
   /* We need to detect whether a fork has happened.  A fork might have
      an identical pool and thus the child and the parent could emit
-     the very same random number.  Obviously this can only happen when
-     running multi-threaded and the pool lock should even catch this.
-     However things do get wrong and thus we better check and retry it
-     here.  We assume that the thread library has no other fatal
-     faults, though.
-   */
-  if ( getpid () != my_pid )
+     the very same random number.  This test here is to detect forks
+     in a multi-threaded process. */
+  if ( getpid () != my_pid2 )
     {
       pid_t x = getpid();
       add_randomness (&x, sizeof(x), 0);
       just_mixed = 0; /* Make sure it will get mixed. */
+      my_pid = x;     /* Also update the static pid. */
       goto retry;
     }
 }
@@ -1060,6 +1186,9 @@ gather_faked( void (*add)(const void*, size_t, int), int requester,
     size_t n;
     char *buffer, *p;
 
+    (void)add;
+    (void)level;
+
     if( !initialized ) {
        log_info(_("WARNING: using insecure random number generator!!\n"));
        /* we can't use tty_printf here - do we need this function at
@@ -1095,10 +1224,15 @@ gather_faked( void (*add)(const void*, size_t, int), int requester,
 
 /* Create an unpredicable nonce of LENGTH bytes in BUFFER. */
 void
-gcry_create_nonce (unsigned char *buffer, size_t length)
+gcry_create_nonce (void *buffer, size_t length)
 {
   static unsigned char nonce_buffer[20+8];
   static int nonce_buffer_initialized = 0;
+  static volatile pid_t my_pid; /* The volatile is there to make sure the
+                                   compiler does not optimize the code away
+                                   in case the getpid function is badly
+                                   attributed. */
+  volatile pid_t apid;
   unsigned char *p;
   size_t n;
   int err;
@@ -1107,17 +1241,25 @@ gcry_create_nonce (unsigned char *buffer, size_t length)
   if (!is_initialized)
     initialize ();
 
+  if (allow_daemon
+      && !_gcry_daemon_create_nonce (daemon_socket_name, buffer, length))
+    return; /* The daemon succeeded. */
+  allow_daemon = 0; /* Daemon failed - switch off. */
+
   /* Acquire the nonce buffer lock. */
   err = ath_mutex_lock (&nonce_buffer_lock);
   if (err)
     log_fatal ("failed to acquire the nonce buffer lock: %s\n",
                strerror (err));
 
+  apid = getpid ();
   /* The first time intialize our buffer. */
   if (!nonce_buffer_initialized)
     {
-      pid_t apid = getpid ();
       time_t atime = time (NULL);
+      pid_t xpid = apid;
+
+      my_pid = apid;
 
       if ((sizeof apid + sizeof atime) > sizeof nonce_buffer)
         BUG ();
@@ -1126,8 +1268,8 @@ gcry_create_nonce (unsigned char *buffer, size_t length)
          a failure of gcry_randomize won't affect us too much.  Don't
          care about the uninitialized remaining bytes. */
       p = nonce_buffer;
-      memcpy (p, &apid, sizeof apid);
-      p += sizeof apid;
+      memcpy (p, &xpid, sizeof xpid);
+      p += sizeof xpid;
       memcpy (p, &atime, sizeof atime); 
 
       /* Initialize the never changing private part of 64 bits. */
@@ -1135,6 +1277,15 @@ gcry_create_nonce (unsigned char *buffer, size_t length)
 
       nonce_buffer_initialized = 1;
     }
+  else if ( my_pid != apid )
+    {
+      /* We forked. Need to reseed the buffer - doing this for the
+         private part should be sufficient. */
+      gcry_randomize (nonce_buffer+20, 8, GCRY_WEAK_RANDOM);
+      /* Update the pid so that we won't run into here again and
+         again. */
+      my_pid = apid;
+    }
 
   /* Create the nonce by hashing the entire buffer, returning the hash
      and updating the first 20 bytes of the buffer with this hash. */