From: Nikos Mavrogiannopoulos Date: Sun, 11 Jul 2010 09:52:13 +0000 (+0200) Subject: Added support for EGD daemon in nettle's RNG. It is used if /dev/urandom X-Git-Tag: gnutls_2_11_3~99 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dd87d15e88d3705eddf69cf6380123a12007bb9a;p=thirdparty%2Fgnutls.git Added support for EGD daemon in nettle's RNG. It is used if /dev/urandom is not present. --- diff --git a/NEWS b/NEWS index 2fe8a5f770..d1c81a6f4e 100644 --- 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. diff --git a/lib/nettle/Makefile.am b/lib/nettle/Makefile.am index 5a78b92f25..a580fc258e 100644 --- a/lib/nettle/Makefile.am +++ b/lib/nettle/Makefile.am @@ -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 index 0000000000..60467c6813 --- /dev/null +++ b/lib/nettle/egd.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "egd.h" + +#include + +#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 index 0000000000..74f3ed968b --- /dev/null +++ b/lib/nettle/egd.h @@ -0,0 +1,2 @@ +int _rndegd_read (int * fd, void* output, size_t length); +int _rndegd_connect_socket (void); diff --git a/lib/nettle/rnd.c b/lib/nettle/rnd.c index c4a0057d17..d130edabb9 100644 --- a/lib/nettle/rnd.c +++ b/lib/nettle/rnd.c @@ -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 +# 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 # include # include +# 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;