1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

Rely on /dev/urandom

This removes quite a bit of code. All reasonable systems have /dev/urandom
these days. Linux, Solaris and the BSDs do.  In case we find a system
without /dev/urandom, we will have to go hunting in other libraries.

The main reason for this is speed: On Ubuntu 14.04 doing direct reads from
/dev/urandom is 2-3 times faster than our md4 based code. On virtualized
FreeBSD 10 the difference is even larger.

My first approach was to use fopen/fread. It was even faster, but less
than twice as fast. So I thought we could save the additional complexity
when having to deal with throwing away buffers when forking and the
additional memory footprint per process.

With this simple generate_random_buffer it will be easier to adapt new
syscalls to get randomness.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>

Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Tue Oct 13 04:25:39 CEST 2015 on sn-devel-104
This commit is contained in:
Volker Lendecke 2015-10-02 00:27:22 +02:00 committed by Jeremy Allison
parent 5380f7b636
commit e73ccc06ef
10 changed files with 18 additions and 376 deletions

View File

@ -21,268 +21,41 @@
#include "replace.h"
#include "system/filesys.h"
#include "../lib/crypto/crypto.h"
#include "lib/util/genrand.h"
#include "sys_rw_data.h"
#include "lib/util/blocking.h"
#include "lib/util/time_basic.h"
#include "lib/util/byteorder.h"
/**
* @file
* @brief Random number generation
*/
static unsigned char hash[258];
static uint32_t counter;
static bool done_reseed = false;
static unsigned int bytes_since_reseed = 0;
static int urand_fd = -1;
static void (*reseed_callback)(void *userdata, int *newseed);
static void *reseed_callback_userdata = NULL;
/**
Copy any user given reseed data.
**/
_PUBLIC_ void set_rand_reseed_callback(void (*fn)(void *, int *), void *userdata)
static void open_urandom(void)
{
reseed_callback = fn;
reseed_callback_userdata = userdata;
set_need_random_reseed();
}
/**
* Tell the random number generator it needs to reseed.
*/
_PUBLIC_ void set_need_random_reseed(void)
{
done_reseed = false;
bytes_since_reseed = 0;
}
static void get_rand_reseed_data(int *reseed_data)
{
if (reseed_callback) {
reseed_callback(reseed_callback_userdata, reseed_data);
} else {
*reseed_data = 0;
}
}
/****************************************************************
Setup the seed.
*****************************************************************/
static void seed_random_stream(unsigned char *seedval, size_t seedlen)
{
unsigned char j = 0;
size_t ind;
for (ind = 0; ind < 256; ind++)
hash[ind] = (unsigned char)ind;
for( ind = 0; ind < 256; ind++) {
unsigned char tc;
j += (hash[ind] + seedval[ind%seedlen]);
tc = hash[ind];
hash[ind] = hash[j];
hash[j] = tc;
}
hash[256] = 0;
hash[257] = 0;
}
/****************************************************************
Get datasize bytes worth of random data.
*****************************************************************/
static void get_random_stream(unsigned char *data, size_t datasize)
{
unsigned char index_i = hash[256];
unsigned char index_j = hash[257];
size_t ind;
for( ind = 0; ind < datasize; ind++) {
unsigned char tc;
unsigned char t;
index_i++;
index_j += hash[index_i];
tc = hash[index_i];
hash[index_i] = hash[index_j];
hash[index_j] = tc;
t = hash[index_i] + hash[index_j];
data[ind] = hash[t];
}
hash[256] = index_i;
hash[257] = index_j;
}
/****************************************************************
Get a 16 byte hash from the contents of a file.
Note that the hash is initialised, because the extra entropy is not
worth the valgrind pain.
*****************************************************************/
static void do_filehash(const char *fname, unsigned char *the_hash)
{
unsigned char buf[1011]; /* deliberate weird size */
unsigned char tmp_md4[16];
int fd, n;
ZERO_STRUCT(tmp_md4);
fd = open(fname,O_RDONLY,0);
if (fd == -1)
if (urand_fd != -1) {
return;
while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
mdfour(tmp_md4, buf, n);
for (n=0;n<16;n++)
the_hash[n] ^= tmp_md4[n];
}
close(fd);
urand_fd = open( "/dev/urandom", O_RDONLY,0);
if (urand_fd == -1) {
abort();
}
smb_set_close_on_exec(urand_fd);
}
/**************************************************************
Try and get a good random number seed. Try a number of
different factors. Firstly, try /dev/urandom - use if exists.
We use /dev/urandom as a read of /dev/random can block if
the entropy pool dries up. This leads clients to timeout
or be very slow on connect.
If we can't use /dev/urandom then seed the stream random generator
above...
**************************************************************/
static int do_reseed(int fd)
{
unsigned char seed_inbuf[40];
uint32_t v1, v2; struct timeval tval; pid_t mypid;
int reseed_data = 0;
if (fd == -1) {
fd = open( "/dev/urandom", O_RDONLY,0);
if (fd != -1) {
smb_set_close_on_exec(fd);
}
}
if (fd != -1
&& (read(fd, seed_inbuf, sizeof(seed_inbuf)) == sizeof(seed_inbuf))) {
seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
return fd;
}
/* Add in some secret file contents */
do_filehash("/etc/shadow", &seed_inbuf[0]);
/*
* Add the counter, time of day, and pid.
*/
GetTimeOfDay(&tval);
mypid = getpid();
v1 = (counter++) + mypid + tval.tv_sec;
v2 = (counter++) * mypid + tval.tv_usec;
SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
/*
* Add any user-given reseed data.
*/
get_rand_reseed_data(&reseed_data);
if (reseed_data) {
size_t i;
for (i = 0; i < sizeof(seed_inbuf); i++)
seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)];
}
seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
return -1;
}
/**
Interface to the (hopefully) good crypto random number generator.
Will use our internal PRNG if more than 40 bytes of random generation
has been requested, otherwise tries to read from /dev/random
**/
_PUBLIC_ void generate_random_buffer(uint8_t *out, int len)
{
unsigned char md4_buf[64];
unsigned char tmp_buf[16];
unsigned char *p;
ssize_t rw_ret;
if(!done_reseed) {
bytes_since_reseed += len;
open_urandom();
/* Magic constant to try and avoid reading 40 bytes
* and setting up the PRNG if the app only ever wants
* a few bytes */
if (bytes_since_reseed < 40) {
if (urand_fd == -1) {
urand_fd = open( "/dev/urandom", O_RDONLY,0);
if (urand_fd != -1) {
smb_set_close_on_exec(urand_fd);
}
}
if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
return;
}
}
urand_fd = do_reseed(urand_fd);
done_reseed = true;
}
/*
* Generate random numbers in chunks of 64 bytes,
* then md4 them & copy to the output buffer.
* This way the raw state of the stream is never externally
* seen.
*/
p = out;
while(len > 0) {
int copy_len = len > 16 ? 16 : len;
get_random_stream(md4_buf, sizeof(md4_buf));
mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
memcpy(p, tmp_buf, copy_len);
p += copy_len;
len -= copy_len;
rw_ret = read_data(urand_fd, out, len);
if (rw_ret != len) {
abort();
}
}
/**
Interface to the (hopefully) good crypto random number generator.
Will always use /dev/urandom if available.
**/
/*
* Keep generate_secret_buffer in case we ever want to do something
* different
*/
_PUBLIC_ void generate_secret_buffer(uint8_t *out, int len)
{
if (urand_fd == -1) {
urand_fd = open( "/dev/urandom", O_RDONLY,0);
if (urand_fd != -1) {
smb_set_close_on_exec(urand_fd);
}
}
if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
return;
}
generate_random_buffer(out, len);
}

View File

@ -19,17 +19,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
Copy any user given reseed data.
**/
void set_rand_reseed_callback(void (*fn)(void *, int *), void *userdata);
/**
* Tell the random number generator it needs to reseed.
*/
void set_need_random_reseed(void);
/**
Interface to the (hopefully) good crypto random number generator.
Will use our internal PRNG if more than 40 bytes of random generation

View File

@ -23,17 +23,6 @@
#include "torture/torture.h"
#include "torture/local/proto.h"
static void dummy_reseed(void *userdata, int *d)
{
*d = 42;
}
static bool test_reseed_callback(struct torture_context *tctx)
{
set_rand_reseed_callback(dummy_reseed, NULL);
return true;
}
static bool test_check_password_quality(struct torture_context *tctx)
{
torture_assert(tctx, !check_password_quality(""), "empty password");
@ -64,7 +53,6 @@ static bool test_generate_random_str(struct torture_context *tctx)
struct torture_suite *torture_local_genrand(TALLOC_CTX *mem_ctx)
{
struct torture_suite *suite = torture_suite_create(mem_ctx, "genrand");
torture_suite_add_simple_test(suite, "reseed_callback", test_reseed_callback);
torture_suite_add_simple_test(suite, "check_password_quality", test_check_password_quality);
torture_suite_add_simple_test(suite, "generate_random_str", test_generate_random_str);
return suite;

View File

@ -84,7 +84,7 @@ if not bld.env.SAMBA_UTIL_CORE_ONLY:
bld.SAMBA_LIBRARY('genrand',
source='genrand.c',
deps='time-basic socket-blocking LIBCRYPTO',
deps='replace socket-blocking sys_rw',
local_include=False,
private_library=True)

View File

@ -441,12 +441,6 @@ NTSTATUS reinit_after_fork(struct messaging_context *msg_ctx,
reinit_after_fork_pipe[1] = -1;
}
/* Reset the state of the random
* number generation system, so
* children do not get the same random
* numbers as each other */
set_need_random_reseed();
/* tdb needs special fork handling */
if (tdb_reopen_all(parent_longlived ? 1 : 0) != 0) {
DEBUG(0,("tdb_reopen_all failed.\n"));

View File

@ -37,28 +37,10 @@
static struct db_context *db_ctx;
/**
* Use a TDB to store an incrementing random seed.
*
* Initialised to the current pid, the very first time Samba starts,
* and incremented by one each time it is needed.
*
* @note Not called by systems with a working /dev/urandom.
*/
static void get_rand_seed(void *userdata, int *new_seed)
{
*new_seed = getpid();
if (db_ctx) {
dbwrap_trans_change_int32_atomic_bystring(
db_ctx, "INFO/random_seed", new_seed, 1);
}
}
/* open up the secrets database with specified private_dir path */
bool secrets_init_path(const char *private_dir)
{
char *fname = NULL;
unsigned char dummy;
TALLOC_CTX *frame;
if (db_ctx != NULL) {
@ -86,17 +68,6 @@ bool secrets_init_path(const char *private_dir)
return False;
}
/**
* Set a reseed function for the crypto random generator
*
* This avoids a problem where systems without /dev/urandom
* could send the same challenge to multiple clients
*/
set_rand_reseed_callback(get_rand_seed, NULL);
/* Ensure that the reseed is done now, while we are root, etc */
generate_random_buffer(&dummy, sizeof(dummy));
TALLOC_FREE(frame);
return True;
}

View File

@ -32,59 +32,6 @@
#include "librpc/gen_ndr/ndr_security.h"
#include "dsdb/samdb/samdb.h"
/**
* Use a TDB to store an incrementing random seed.
*
* Initialised to the current pid, the very first time Samba starts,
* and incremented by one each time it is needed.
*
* @note Not called by systems with a working /dev/urandom.
*/
static void get_rand_seed(struct tdb_wrap *secretsdb, int *new_seed)
{
*new_seed = getpid();
if (secretsdb != NULL) {
tdb_change_int32_atomic(secretsdb->tdb, "INFO/random_seed", new_seed, 1);
}
}
/**
* open up the randseed database and set the random number generator callback
*/
bool randseed_init(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
{
char *fname;
uint8_t dummy;
struct tdb_wrap *tdb;
fname = lpcfg_private_path(mem_ctx, lp_ctx, "randseed.tdb");
tdb = tdb_wrap_open(mem_ctx, fname,
lpcfg_tdb_hash_size(lp_ctx, fname),
lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT),
O_RDWR|O_CREAT, 0600);
if (!tdb) {
DEBUG(0,("Failed to open %s\n", fname));
talloc_free(fname);
return false;
}
talloc_free(fname);
/**
* Set a reseed function for the crypto random generator
*
* This avoids a problem where systems without /dev/urandom
* could send the same challenge to multiple clients
*/
set_rand_reseed_callback((void (*) (void *, int *))get_rand_seed, tdb);
/* Ensure that the reseed is done now, while we are root, etc */
generate_random_buffer(&dummy, sizeof(dummy));
return true;
}
/**
connect to the secrets ldb
*/

View File

@ -28,14 +28,6 @@
#define SECRETS_PRINCIPAL_SEARCH "(&(|(realm=%s)(flatname=%s))(servicePrincipalName=%s))"
#define SECRETS_LDAP_FILTER "(&(objectclass=ldapSecret)(cn=SAMDB Credentials))"
/**
* Use a TDB to store an incrementing random seed.
*
* Initialised to the current pid, the very first time Samba starts,
* and incremented by one each time it is needed.
*
* @note Not called by systems with a working /dev/urandom.
*/
struct loadparm_context;
struct tevent_context;
struct ldb_message;

View File

@ -277,9 +277,6 @@ static void standard_accept_connection(struct tevent_context *ev,
child_pipe[1] = -1;
}
/* Ensure that the forked children do not expose identical random streams */
set_need_random_reseed();
/* setup the process title */
c = socket_get_peer_addr(sock2, ev);
s = socket_get_my_addr(sock2, ev);
@ -356,9 +353,6 @@ static void standard_new_task(struct tevent_context *ev,
child_pipe[1] = -1;
}
/* Ensure that the forked children do not expose identical random streams */
set_need_random_reseed();
setproctitle("task %s server_id[%d]", service_name, (int)pid);
/* setup this new task. Cluster ID is PID based for this process model */

View File

@ -392,12 +392,6 @@ static int binary_smbd_main(const char *binary_name, int argc, const char *argv[
pidfile_create(lpcfg_pid_directory(cmdline_lp_ctx), binary_name);
/* Set up a database to hold a random seed, in case we don't
* have /dev/urandom */
if (!randseed_init(talloc_autofree_context(), cmdline_lp_ctx)) {
return 1;
}
if (lpcfg_server_role(cmdline_lp_ctx) == ROLE_ACTIVE_DIRECTORY_DC) {
if (!open_schannel_session_store(talloc_autofree_context(), cmdline_lp_ctx)) {
exit_daemon("Samba cannot open schannel store for secured NETLOGON operations.", EACCES);