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:
parent
5380f7b636
commit
e73ccc06ef
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user