Entropy: Fall through to /dev/random if getrandom() syscall unknown

This commit fixes issue #1212 related to platform-specific entropy
polling in an syscall-emulated environment.

Previously, the implementation of the entropy gathering function
`mbedtls_platform_entropy_poll()` for linux machines used the
following logic to determine how to obtain entropy from the kernel:

1. If the getrandom() system call identifier SYS_getrandom is present and
   the kernel version is 3.17 or higher, use syscall( SYS_getrandom, ... )
2. Otherwise, fall back to reading from /dev/random.

There are two issues with this:

1. Portability:
   When cross-compiling the code for a different
   architecture and running it through system call
   emulation in qemu, qemu reports the host kernel
   version through uname but, as of v.2.5.0,
   doesn't support emulating the getrandom() syscall.
   This leads to `mbedtls_platform_entropy_poll()`
   failing even though reading from /dev/random would
   have worked.

2. Style:
   Extracting the linux kernel version from
   the output of `uname` is slightly tedious.

This commit fixes both by implementing the suggestion in #1212:
- It removes the kernel-version detection through uname().
- Instead, it checks whether `syscall( SYS_getrandom, ... )`
  fails with errno set to ENOSYS indicating an unknown system call.
  If so, it falls through to trying to read from /dev/random.

Fixes #1212.
This commit is contained in:
Hanno Becker 2018-10-18 11:49:25 +01:00
parent e2a6f01f3f
commit 6b89197f26

View file

@ -92,6 +92,7 @@ int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len
#include <sys/syscall.h>
#if defined(SYS_getrandom)
#define HAVE_GETRANDOM
#include <errno.h>
static int getrandom_wrapper( void *buf, size_t buflen, unsigned int flags )
{
@ -101,47 +102,8 @@ static int getrandom_wrapper( void *buf, size_t buflen, unsigned int flags )
memset( buf, 0, buflen );
#endif
#endif
return( syscall( SYS_getrandom, buf, buflen, flags ) );
}
#include <sys/utsname.h>
/* Check if version is at least 3.17.0 */
static int check_version_3_17_plus( void )
{
int minor;
struct utsname un;
const char *ver;
/* Get version information */
uname(&un);
ver = un.release;
/* Check major version; assume a single digit */
if( ver[0] < '3' || ver[0] > '9' || ver [1] != '.' )
return( -1 );
if( ver[0] - '0' > 3 )
return( 0 );
/* Ok, so now we know major == 3, check minor.
* Assume 1 or 2 digits. */
if( ver[2] < '0' || ver[2] > '9' )
return( -1 );
minor = ver[2] - '0';
if( ver[3] >= '0' && ver[3] <= '9' )
minor = 10 * minor + ver[3] - '0';
else if( ver [3] != '.' )
return( -1 );
if( minor < 17 )
return( -1 );
return( 0 );
}
static int has_getrandom = -1;
#endif /* SYS_getrandom */
#endif /* __linux__ */
@ -152,22 +114,21 @@ int mbedtls_platform_entropy_poll( void *data,
{
FILE *file;
size_t read_len;
int ret;
((void) data);
#if defined(HAVE_GETRANDOM)
if( has_getrandom == -1 )
has_getrandom = ( check_version_3_17_plus() == 0 );
if( has_getrandom )
ret = getrandom_wrapper( output, len, 0 );
if( ret >= 0 )
{
int ret;
if( ( ret = getrandom_wrapper( output, len, 0 ) ) < 0 )
return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
*olen = ret;
return( 0 );
}
else if( errno != ENOSYS )
return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
/* Fall through if the system call isn't known. */
#else
((void) ret;
#endif /* HAVE_GETRANDOM */
*olen = 0;