]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added support for EGD daemon in nettle's RNG. It is used if /dev/urandom
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 11 Jul 2010 09:52:13 +0000 (11:52 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 11 Jul 2010 09:55:46 +0000 (11:55 +0200)
is not present.

NEWS
lib/nettle/Makefile.am
lib/nettle/egd.c [new file with mode: 0644]
lib/nettle/egd.h [new file with mode: 0644]
lib/nettle/rnd.c

diff --git a/NEWS b/NEWS
index 2fe8a5f77083a08116fb314ce7367f38f363800b..d1c81a6f4e41c924eb0758b91b90b13c340f463e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -45,7 +45,9 @@ imported from tokens, and operations can be performed on private keys.
 
 ** libgnutls: Added abstract gnutls_privkey_t and gnutls_pubkey_t
 
-** libgnutls: Added initial support for the nettle library (unsupported)
+** libgnutls: Added initial support for the nettle library. It uses
+the system's random generator for seeding. That is /dev/urandom in Linux, 
+system calls in Win32 and EGD on other systems.
 
 ** libgnutls: Corrected issue on the %SSL3_RECORD_VERSION priority string. It now
     works even when resuming a session.
index 5a78b92f25d23c8a9e2227d903be21cf4bc33c50..a580fc258ef4756fe5c35ac3faea828151747f04 100644 (file)
@@ -35,4 +35,4 @@ endif
 
 noinst_LTLIBRARIES = libcrypto.la
 
-libcrypto_la_SOURCES = pk.c mpi.c mac.c cipher.c rnd.c init.c
+libcrypto_la_SOURCES = pk.c mpi.c mac.c cipher.c rnd.c init.c egd.c egd.h
diff --git a/lib/nettle/egd.c b/lib/nettle/egd.c
new file mode 100644 (file)
index 0000000..60467c6
--- /dev/null
@@ -0,0 +1,256 @@
+/* rndegd.c  - interface to the EGD
+ *     Copyright (C) 1999, 2000, 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "egd.h"
+
+#include <gnutls_errors.h>
+
+#ifndef offsetof
+#define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
+
+static int egd_socket = -1;
+
+static int
+do_write( int fd, void *buf, size_t nbytes )
+{
+  size_t nleft = nbytes;
+  int nwritten;
+  
+  while( nleft > 0 ) 
+    {
+      nwritten = write( fd, buf, nleft);
+      if( nwritten < 0 )
+        {
+          if( errno == EINTR )
+            continue;
+          return -1;
+       }
+      nleft -= nwritten;
+      buf = (char*)buf + nwritten;
+    }
+  return 0;
+}
+
+static int
+do_read( int fd, void *buf, size_t nbytes )
+{
+  int n, nread = 0;
+
+  do
+    {
+      do
+        {
+          n = read(fd, (char*)buf + nread, nbytes );
+        } 
+      while( n == -1 && errno == EINTR );
+      if( n == -1)
+        return nread? nread:-1;
+      if( n == 0)
+        return -1;
+      nread += n;
+      nbytes -= n;
+    } 
+  while( nread < nbytes );
+  return nread;
+}
+
+static const char* egd_names[] = { 
+  "/var/run/egd-pool", 
+  "/dev/egd-pool", 
+  "/etc/egd-pool", 
+  "/etc/entropy", 
+  "/var/run/entropy", 
+  "/dev/entropy",
+  NULL 
+};
+
+static const char* find_egd_name(void)
+{
+int i = 0;
+struct stat st;
+
+  do {
+    if (stat(egd_names[i], &st) != 0) continue;
+    
+    if (st.st_mode & S_IFSOCK) { /* found */
+      return egd_names[i];
+    }
+  
+  } while(egd_names[++i] != NULL);
+  
+  return NULL;
+}
+
+/* Connect to the EGD and return the file descriptor.  Return -1 on
+   error.  With NOFAIL set to true, silently fail and return the
+   error, otherwise print an error message and die. */
+int _rndegd_connect_socket (void)
+{
+  int fd;
+  const char *name;
+  struct sockaddr_un addr;
+  int addr_len;
+
+  if (egd_socket != -1)
+    {
+      close (egd_socket);
+      egd_socket = -1;
+    }
+
+  name = find_egd_name();
+
+  if (strlen(name)+1 >= sizeof addr.sun_path) 
+    {
+      _gnutls_debug_log ("EGD socketname is too long\n");
+      return -1;
+    }
+  
+  memset( &addr, 0, sizeof addr );
+  addr.sun_family = AF_LOCAL;
+  strcpy( addr.sun_path, name );         
+  addr_len = (offsetof( struct sockaddr_un, sun_path )
+              + strlen( addr.sun_path ));
+  
+  fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+  if (fd == -1)
+    {
+      _gnutls_debug_log("can't create unix domain socket: %s\n", strerror(errno) );
+      return -1;
+    }
+  else if (connect (fd, (struct sockaddr*)&addr, addr_len) == -1)
+    {
+      _gnutls_debug_log("can't connect to EGD socket `%s': %s\n",
+                 name, strerror(errno) );
+      close (fd);
+      fd = -1;
+    }
+
+  if (fd != -1)
+    egd_socket = fd;
+  return fd;
+}
+
+/****************
+ * Note: We always use the highest level.
+ * To boost the performance we may want to add some
+ * additional code for level 1
+ *
+ * Using a level of 0 should never block and better add nothing
+ * to the pool.  So this is just a dummy for EGD.
+ */
+int _rndegd_read (int * fd, void* _output, size_t _length)
+{
+  int n;
+  uint8_t buffer[256+2];
+  int nbytes;
+  int do_restart = 0;
+  unsigned char* output = _output;
+  size_t length = _length;
+
+  if( !length )
+    return 0;
+
+
+ restart:
+  if (*fd == -1 || do_restart)
+    *fd = _rndegd_connect_socket ();
+
+  do_restart = 0;
+
+  nbytes = length < 255 ? length : 255;
+  /* First time we do it with a non blocking request */
+  buffer[0] = 1; /* non blocking */
+  buffer[1] = nbytes;
+
+  if( do_write( *fd, buffer, 2 ) == -1 )
+    _gnutls_debug_log("can't write to the EGD: %s\n", strerror(errno) );
+
+  n = do_read( *fd, buffer, 1 );
+  if( n == -1 )
+    {
+      _gnutls_debug_log("read error on EGD: %s\n", strerror(errno));
+      do_restart = 1;
+      goto restart;
+    }
+
+  n = buffer[0];
+  if( n )
+    {
+      n = do_read( *fd, buffer, n );
+      if( n == -1 )
+        {
+          _gnutls_debug_log("read error on EGD: %s\n", strerror(errno));
+          do_restart = 1;
+          goto restart;
+       }
+
+      if (n > length)
+        {
+          _gnutls_debug_log("read error on EGD: returned more bytes!\n");
+          n = length;
+        }
+
+      memcpy(output, buffer, n);
+      output += n;
+      length -= n;
+    }
+
+  while( length )
+    {
+      nbytes = length < 255? length : 255;
+
+      buffer[0] = 2; /* blocking */
+      buffer[1] = nbytes;
+      if( do_write( *fd, buffer, 2 ) == -1 )
+        _gnutls_debug_log("can't write to the EGD: %s\n", strerror(errno) );
+      n = do_read( *fd, buffer, nbytes );
+      if( n == -1 )
+        {
+          _gnutls_debug_log("read error on EGD: %s\n", strerror(errno));
+          do_restart = 1;
+          goto restart;
+       }
+
+      if (n > length)
+        {
+          _gnutls_debug_log("read error on EGD: returned more bytes!\n");
+          n = length;
+        }
+      
+      memcpy(output, buffer, n);
+      output += n;
+      length -= n;
+    }
+
+  return _length; /* success */
+}
+
diff --git a/lib/nettle/egd.h b/lib/nettle/egd.h
new file mode 100644 (file)
index 0000000..74f3ed9
--- /dev/null
@@ -0,0 +1,2 @@
+int _rndegd_read (int * fd, void* output, size_t length);
+int _rndegd_connect_socket (void);
index c4a0057d17bd7308d4164fce028a44071caceed3..d130edabb987430378d5e7b20df2c0ac154633d0 100644 (file)
@@ -47,14 +47,15 @@ static time_t trivia_time_count = 0;
 
 static void* rnd_mutex;
 
-#define DEVICE_READ_SIZE 16
-#define DEVICE_READ_SIZE_MAX 32
 #define DEVICE_READ_INTERVAL 1200
 
 #ifdef _WIN32
 
 # include <windows.h>
 
+# define DEVICE_READ_SIZE 16
+# define DEVICE_READ_SIZE_MAX 32
+
 static HCRYPTPROV device_fd = NULL;
 
 static int do_trivia_source(int init)
@@ -133,6 +134,10 @@ static void wrap_nettle_rnd_deinit(void* ctx)
 # include <sys/time.h>
 # include <fcntl.h>
 # include <locks.h>
+# include "egd.h"
+
+# define DEVICE_READ_SIZE 16
+# define DEVICE_READ_SIZE_MAX 32
 
 static int device_fd;
 static time_t trivia_previous_time = 0;
@@ -185,10 +190,10 @@ static int do_trivia_source(int init)
                            sizeof(event), (const uint8_t *) &event);
 }
 
-static int do_device_source(int init)
+static int do_device_source_urandom(int init)
 {
     time_t now = time(NULL);
-       int read_size = DEVICE_READ_SIZE;
+    int read_size = DEVICE_READ_SIZE;
 
     if (init) {
                int old;
@@ -242,6 +247,80 @@ static int do_device_source(int init)
     return 0;
 }
 
+static int do_device_source_egd(int init)
+{
+    time_t now = time(NULL);
+    int read_size = DEVICE_READ_SIZE;
+
+    if (init) {
+               device_fd = _rndegd_connect_socket();
+               if (device_fd < 0) {
+                       _gnutls_debug_log("Cannot open egd socket!\n");
+                       return GNUTLS_E_FILE_ERROR;
+               }
+
+               device_last_read = now;
+               
+               read_size = DEVICE_READ_SIZE_MAX; /* initially read more data */
+    }
+
+    if ((device_fd > 0)
+               && (init || ((now - device_last_read) > DEVICE_READ_INTERVAL))) {
+
+               /* More than a minute since we last read the device */
+               uint8_t buf[DEVICE_READ_SIZE_MAX];
+               uint32_t done;
+
+               for (done = 0; done < read_size;) {
+                       int res;
+                       res = _rndegd_read(&device_fd, buf + done, sizeof(buf) - done);
+                       if (res <= 0) {
+                               if (res < 0) {
+                                       _gnutls_debug_log("Failed to read egd.\n");
+                               } else {
+                                       _gnutls_debug_log("Failed to read egd: end of file\n");
+                               }
+
+                               return GNUTLS_E_INTERNAL_ERROR;
+                       }
+                       done += res;
+               }
+
+               device_last_read = now;
+               return yarrow256_update(&yctx, RANDOM_SOURCE_DEVICE, read_size*8/2, 
+                                       read_size, buf);
+    }
+    return 0;
+}
+
+static int do_device_source(int init)
+{
+int ret;
+static int (*do_source)(int init) = NULL;
+/* using static var here is ok since we are
+ * always called with mutexes down 
+ */
+
+    if (init == 1) {
+        do_source = do_device_source_urandom;
+        ret = do_source(init);
+        if (ret < 0) {
+            do_source = do_device_source_egd;
+            ret = do_source(init);
+        }
+        
+        if (ret < 0) {
+            gnutls_assert();
+            return ret;
+        }
+        
+        return ret;
+    } else {
+        return do_source(init);
+    }
+}
+
+
 static void wrap_nettle_rnd_deinit(void* ctx)
 {
        RND_LOCK;