2014-12-10 21:56:49 +00:00
/*
* Dropbear - a SSH2 server
*
* Copyright ( c ) 2002 , 2003 Matt Johnston
* All rights reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE . */
# include "includes.h"
# include "buffer.h"
# include "dbutil.h"
# include "bignum.h"
# include "dbrandom.h"
2019-06-09 20:44:26 +00:00
# include "runopts.h"
2014-12-10 21:56:49 +00:00
/* this is used to generate unique output from the same hashpool */
static uint32_t counter = 0 ;
/* the max value for the counter, so it won't integer overflow */
2019-06-09 20:44:26 +00:00
# define MAX_COUNTER (1<<30)
2014-12-10 21:56:49 +00:00
static unsigned char hashpool [ SHA1_HASH_SIZE ] = { 0 } ;
static int donerandinit = 0 ;
# define INIT_SEED_SIZE 32 /* 256 bits */
/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
* into hashpool . To read data , we hash together current hashpool contents ,
* and a counter . We feed more data in by hashing the current pool and new
* data into the pool .
*
* It is important to ensure that counter doesn ' t wrap around before we
* feed in new entropy .
*
*/
2020-12-28 21:40:37 +00:00
/* Pass wantlen=0 to hash an entire file */
2014-12-10 21:56:49 +00:00
static int
process_file ( hash_state * hs , const char * filename ,
2020-12-28 21:40:37 +00:00
unsigned int wantlen , int prngd ) {
int readfd = - 1 ;
2014-12-10 21:56:49 +00:00
unsigned int readcount ;
int ret = DROPBEAR_FAILURE ;
2020-12-29 01:23:27 +00:00
int already_blocked = 0 ;
2014-12-10 21:56:49 +00:00
2020-12-28 21:40:37 +00:00
if ( prngd ) {
2019-06-09 20:44:26 +00:00
# if DROPBEAR_USE_PRNGD
2014-12-10 21:56:49 +00:00
readfd = connect_unix ( filename ) ;
# endif
2020-12-28 21:40:37 +00:00
} else {
2014-12-10 21:56:49 +00:00
readfd = open ( filename , O_RDONLY ) ;
}
if ( readfd < 0 ) {
goto out ;
}
readcount = 0 ;
2020-12-28 21:40:37 +00:00
while ( wantlen = = 0 | | readcount < wantlen ) {
2014-12-10 21:56:49 +00:00
int readlen , wantread ;
unsigned char readbuf [ 4096 ] ;
2020-12-29 01:23:27 +00:00
/* <<<<<<< HEAD
* dropbear removed this code between 2019.78 and 2020.81 , I guess they didn ' t
* really care because all it does is print a warning ( I added the break that
* makes already_blocked non - optional ) . I think somebody went through here with
* a mind towards guaranteeing there is always sufficient entropy to prevent
* obvious attacks , but I don ' t care . My change ( commit 60f caa6 ) solved a real
* problem so I ' m preserving this hack . - Greg 2020 / 12 / 28 */
2014-12-10 21:56:49 +00:00
if ( ! already_blocked & & ! prngd )
{
int res ;
struct timeval timeout ;
fd_set read_fds ;
2016-08-08 18:08:49 +00:00
timeout . tv_sec = 0 ;
timeout . tv_usec = 1000 ;
2014-12-10 21:56:49 +00:00
2019-06-09 20:44:26 +00:00
DROPBEAR_FD_ZERO ( & read_fds ) ;
2014-12-10 21:56:49 +00:00
FD_SET ( readfd , & read_fds ) ;
res = select ( readfd + 1 , & read_fds , NULL , NULL , & timeout ) ;
if ( res = = 0 )
{
dropbear_log ( LOG_WARNING , " Warning: Reading the randomness source '%s' seems to have blocked. \n You may need to find a better entropy source. " , filename ) ;
already_blocked = 1 ;
}
}
2016-08-08 18:08:49 +00:00
if ( already_blocked ) break ;
2020-12-29 01:23:27 +00:00
/* =======
* > > > > > > > dropbear */
2016-08-08 18:08:49 +00:00
2020-12-28 21:40:37 +00:00
if ( wantlen = = 0 ) {
2014-12-10 21:56:49 +00:00
wantread = sizeof ( readbuf ) ;
2020-12-28 21:40:37 +00:00
} else {
wantread = MIN ( sizeof ( readbuf ) , wantlen - readcount ) ;
2014-12-10 21:56:49 +00:00
}
2019-06-09 20:44:26 +00:00
# if DROPBEAR_USE_PRNGD
2020-12-28 21:40:37 +00:00
if ( prngd ) {
2014-12-10 21:56:49 +00:00
char egdcmd [ 2 ] ;
egdcmd [ 0 ] = 0x02 ; /* blocking read */
egdcmd [ 1 ] = ( unsigned char ) wantread ;
2020-12-28 21:40:37 +00:00
if ( write ( readfd , egdcmd , 2 ) < 0 ) {
2014-12-10 21:56:49 +00:00
dropbear_exit ( " Can't send command to egd " ) ;
}
}
# endif
readlen = read ( readfd , readbuf , wantread ) ;
if ( readlen < = 0 ) {
if ( readlen < 0 & & errno = = EINTR ) {
continue ;
}
2020-12-28 21:40:37 +00:00
if ( readlen = = 0 & & wantlen = = 0 ) {
2014-12-10 21:56:49 +00:00
/* whole file was read as requested */
break ;
}
goto out ;
}
sha1_process ( hs , readbuf , readlen ) ;
readcount + = readlen ;
}
ret = DROPBEAR_SUCCESS ;
out :
close ( readfd ) ;
return ret ;
}
2019-06-09 20:44:26 +00:00
void addrandom ( const unsigned char * buf , unsigned int len )
2014-12-10 21:56:49 +00:00
{
hash_state hs ;
2019-06-09 20:44:26 +00:00
# if DROPBEAR_FUZZ
if ( fuzz . fuzzing ) {
return ;
}
# endif
2014-12-10 21:56:49 +00:00
/* hash in the new seed data */
sha1_init ( & hs ) ;
/* existing state (zeroes on startup) */
sha1_process ( & hs , ( void * ) hashpool , sizeof ( hashpool ) ) ;
/* new */
sha1_process ( & hs , buf , len ) ;
sha1_done ( & hs , hashpool ) ;
}
static void write_urandom ( )
{
2019-06-09 20:44:26 +00:00
# if DROPBEAR_FUZZ
if ( fuzz . fuzzing ) {
return ;
}
# endif
# if !DROPBEAR_USE_PRNGD
2014-12-10 21:56:49 +00:00
/* This is opportunistic, don't worry about failure */
unsigned char buf [ INIT_SEED_SIZE ] ;
FILE * f = fopen ( DROPBEAR_URANDOM_DEV , " w " ) ;
if ( ! f ) {
return ;
}
genrandom ( buf , sizeof ( buf ) ) ;
fwrite ( buf , sizeof ( buf ) , 1 , f ) ;
fclose ( f ) ;
# endif
}
2019-06-09 20:44:26 +00:00
# if DROPBEAR_FUZZ
void fuzz_seed ( void ) {
hash_state hs ;
sha1_init ( & hs ) ;
sha1_process ( & hs , " fuzzfuzzfuzz " , strlen ( " fuzzfuzzfuzz " ) ) ;
sha1_done ( & hs , hashpool ) ;
counter = 0 ;
donerandinit = 1 ;
}
# endif
2020-12-28 21:40:37 +00:00
# ifdef HAVE_GETRANDOM
/* Reads entropy seed with getrandom().
* May block if the kernel isn ' t ready .
* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
static int process_getrandom ( hash_state * hs ) {
char buf [ INIT_SEED_SIZE ] ;
ssize_t ret ;
/* First try non-blocking so that we can warn about waiting */
ret = getrandom ( buf , sizeof ( buf ) , GRND_NONBLOCK ) ;
if ( ret = = - 1 ) {
if ( errno = = ENOSYS ) {
/* Old kernel */
return DROPBEAR_FAILURE ;
}
/* Other errors fall through to blocking getrandom() */
TRACE ( ( " first getrandom() failed: %d %s " , errno , strerror ( errno ) ) )
if ( errno = = EAGAIN ) {
dropbear_log ( LOG_WARNING , " Waiting for kernel randomness to be initialised... " ) ;
}
}
/* Wait blocking if needed. Loop in case we get EINTR */
while ( ret ! = sizeof ( buf ) ) {
ret = getrandom ( buf , sizeof ( buf ) , 0 ) ;
if ( ret = = sizeof ( buf ) ) {
/* Success */
break ;
}
if ( ret = = - 1 & & errno = = EINTR ) {
/* Try again. */
continue ;
}
if ( ret > = 0 ) {
TRACE ( ( " Short read %zd from getrandom() shouldn't happen " , ret ) )
/* Try again? */
continue ;
}
/* Unexpected problem, fall back to /dev/urandom */
TRACE ( ( " 2nd getrandom() failed: %d %s " , errno , strerror ( errno ) ) )
break ;
}
if ( ret = = sizeof ( buf ) ) {
/* Success, stir in the entropy */
sha1_process ( hs , ( void * ) buf , sizeof ( buf ) ) ;
return DROPBEAR_SUCCESS ;
}
return DROPBEAR_FAILURE ;
}
# endif /* HAVE_GETRANDOM */
2014-12-10 21:56:49 +00:00
/* Initialise the prng from /dev/urandom or prngd. This function can
* be called multiple times */
void seedrandom ( ) {
hash_state hs ;
pid_t pid ;
struct timeval tv ;
clock_t clockval ;
2020-12-28 21:40:37 +00:00
int urandom_seeded = 0 ;
2014-12-10 21:56:49 +00:00
2019-06-09 20:44:26 +00:00
# if DROPBEAR_FUZZ
if ( fuzz . fuzzing ) {
return ;
}
# endif
2014-12-10 21:56:49 +00:00
/* hash in the new seed data */
sha1_init ( & hs ) ;
2019-06-09 20:44:26 +00:00
2014-12-10 21:56:49 +00:00
/* existing state */
sha1_process ( & hs , ( void * ) hashpool , sizeof ( hashpool ) ) ;
2020-12-28 21:40:37 +00:00
# ifdef HAVE_GETRANDOM
if ( process_getrandom ( & hs ) = = DROPBEAR_SUCCESS ) {
urandom_seeded = 1 ;
2014-12-10 21:56:49 +00:00
}
2020-12-28 21:40:37 +00:00
# endif
if ( ! urandom_seeded ) {
# if DROPBEAR_USE_PRNGD
if ( process_file ( & hs , DROPBEAR_PRNGD_SOCKET , INIT_SEED_SIZE , 1 )
! = DROPBEAR_SUCCESS ) {
dropbear_exit ( " Failure reading random device %s " ,
DROPBEAR_PRNGD_SOCKET ) ;
urandom_seeded = 1 ;
}
2014-12-10 21:56:49 +00:00
# else
2020-12-28 21:40:37 +00:00
/* non-blocking random source (probably /dev/urandom) */
if ( process_file ( & hs , DROPBEAR_URANDOM_DEV , INIT_SEED_SIZE , 0 )
! = DROPBEAR_SUCCESS ) {
dropbear_exit ( " Failure reading random device %s " ,
DROPBEAR_URANDOM_DEV ) ;
urandom_seeded = 1 ;
}
2014-12-10 21:56:49 +00:00
# endif
2020-12-28 21:40:37 +00:00
} /* urandom_seeded */
2014-12-10 21:56:49 +00:00
/* A few other sources to fall back on.
* Add more here for other platforms */
# ifdef __linux__
/* Seems to be a reasonable source of entropy from timers. Possibly hard
* for even local attackers to reproduce */
2016-08-08 18:08:49 +00:00
process_file ( & hs , " /proc/timer_list " , 4096 , 0 ) ;
2014-12-10 21:56:49 +00:00
/* Might help on systems with wireless */
2016-08-08 18:08:49 +00:00
process_file ( & hs , " /proc/interrupts " , 4096 , 0 ) ;
2014-12-10 21:56:49 +00:00
2016-08-08 18:08:49 +00:00
process_file ( & hs , " /proc/loadavg " , 4096 , 0 ) ;
process_file ( & hs , " /proc/sys/kernel/random/entropy_avail " , 4096 , 0 ) ;
2014-12-10 21:56:49 +00:00
/* Mostly network visible but useful in some situations.
* Limit size to avoid slowdowns on systems with lots of routes */
process_file ( & hs , " /proc/net/netstat " , 4096 , 0 ) ;
process_file ( & hs , " /proc/net/dev " , 4096 , 0 ) ;
process_file ( & hs , " /proc/net/tcp " , 4096 , 0 ) ;
/* Also includes interface lo */
process_file ( & hs , " /proc/net/rt_cache " , 4096 , 0 ) ;
2016-08-08 18:08:49 +00:00
process_file ( & hs , " /proc/vmstat " , 4096 , 0 ) ;
2014-12-10 21:56:49 +00:00
# endif
pid = getpid ( ) ;
sha1_process ( & hs , ( void * ) & pid , sizeof ( pid ) ) ;
/* gettimeofday() doesn't completely fill out struct timeval on
OS X ( 10.8 .3 ) , avoid valgrind warnings by clearing it first */
memset ( & tv , 0x0 , sizeof ( tv ) ) ;
gettimeofday ( & tv , NULL ) ;
sha1_process ( & hs , ( void * ) & tv , sizeof ( tv ) ) ;
clockval = clock ( ) ;
sha1_process ( & hs , ( void * ) & clockval , sizeof ( clockval ) ) ;
/* When a private key is read by the client or server it will
* be added to the hashpool - see runopts . c */
sha1_done ( & hs , hashpool ) ;
counter = 0 ;
donerandinit = 1 ;
/* Feed it all back into /dev/urandom - this might help if Dropbear
* is running from inetd and gets new state each time */
write_urandom ( ) ;
}
/* return len bytes of pseudo-random data */
void genrandom ( unsigned char * buf , unsigned int len ) {
hash_state hs ;
unsigned char hash [ SHA1_HASH_SIZE ] ;
unsigned int copylen ;
if ( ! donerandinit ) {
dropbear_exit ( " seedrandom not done " ) ;
}
while ( len > 0 ) {
sha1_init ( & hs ) ;
sha1_process ( & hs , ( void * ) hashpool , sizeof ( hashpool ) ) ;
sha1_process ( & hs , ( void * ) & counter , sizeof ( counter ) ) ;
sha1_done ( & hs , hash ) ;
counter + + ;
if ( counter > MAX_COUNTER ) {
seedrandom ( ) ;
}
copylen = MIN ( len , SHA1_HASH_SIZE ) ;
memcpy ( buf , hash , copylen ) ;
len - = copylen ;
buf + = copylen ;
}
m_burn ( hash , sizeof ( hash ) ) ;
}
/* Generates a random mp_int.
* max is a * mp_int specifying an upper bound .
* rand must be an initialised * mp_int for the result .
* the result rand satisfies : 0 < rand < max
* */
void gen_random_mpint ( mp_int * max , mp_int * rand ) {
unsigned char * randbuf = NULL ;
unsigned int len = 0 ;
const unsigned char masks [ ] = { 0xff , 0x01 , 0x03 , 0x07 , 0x0f , 0x1f , 0x3f , 0x7f } ;
const int size_bits = mp_count_bits ( max ) ;
len = size_bits / 8 ;
if ( ( size_bits % 8 ) ! = 0 ) {
len + = 1 ;
}
randbuf = ( unsigned char * ) m_malloc ( len ) ;
do {
genrandom ( randbuf , len ) ;
/* Mask out the unrequired bits - mp_read_unsigned_bin expects
* MSB first . */
randbuf [ 0 ] & = masks [ size_bits % 8 ] ;
bytes_to_mp ( rand , randbuf , len ) ;
/* keep regenerating until we get one satisfying
* 0 < rand < max */
2019-06-09 20:44:26 +00:00
} while ( ! ( mp_cmp ( rand , max ) = = MP_LT & & mp_cmp_d ( rand , 0 ) = = MP_GT ) ) ;
2014-12-10 21:56:49 +00:00
m_burn ( randbuf , len ) ;
m_free ( randbuf ) ;
}