Rely on /dev/urandom
authorVolker Lendecke <vl@samba.org>
Thu, 1 Oct 2015 22:27:22 +0000 (00:27 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 13 Oct 2015 02:25:38 +0000 (04:25 +0200)
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

lib/util/genrand.c
lib/util/genrand.h
lib/util/tests/genrand.c
lib/util/wscript_build
source3/lib/util.c
source3/passdb/secrets.c
source4/param/secrets.c
source4/param/secrets.h
source4/smbd/process_standard.c
source4/smbd/server.c

index 4473433b5f78e363137a90ea2c4556ebb7b44623..a775535c49e781140bbf5a26a8577f01193f0c52 100644 (file)
 
 #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)
-{
-       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)
+static void open_urandom(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);
-}
-
-/**************************************************************
- 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)];
+       urand_fd = open( "/dev/urandom", O_RDONLY,0);
+       if (urand_fd == -1) {
+               abort();
        }
-
-       seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
-
-       return -1;
+       smb_set_close_on_exec(urand_fd);
 }
 
-/**
- 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;
-
-       if(!done_reseed) {
-               bytes_since_reseed += len;
-
-               /* 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.
-        */
+       ssize_t rw_ret;
 
-       p = out;
-       while(len > 0) {
-               int copy_len = len > 16 ? 16 : len;
+       open_urandom();
 
-               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);
 }
index 73ca601df2fb2f075a86b7f5165533842a40ffd4..ef6bbc641570b5fcaa8dbc6b2949ebbc8894cafe 100644 (file)
    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
index 3d48be0c22c52cf598b46898ce9208641efbb80c..81c20bc386f4e1a86ae543df64c1625d4b41342f 100644 (file)
 #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;
index 2c4d093faea967ac3843e308e4fdb88ef47afe01..81578a954f1510aa82cfcabfc7fd50fe4b29fe1d 100755 (executable)
@@ -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)
 
index f6335756a4fff3b7116042fb35e321715c75443e..a0da08723c0d154ce71a3d73f8ac2f7e5b44b003 100644 (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"));
index 7071fd9b9b06ea08a2fc7c5d2dfa600769ddb52f..4372c635f1cf943aa85da91c6602b4212e9a0c69 100644 (file)
 
 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;
 }
index 92e338a6baba2b8872f255a763899effc0d418af..987408829ea5bad4caa689cb48d78c3a3c1a3021 100644 (file)
 #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
 */
index 1e7849f978893f2a14f4a39f9db3e1ed6929c91a..015ea1200411b76b9c90e9cfb5bce94ce399ca33 100644 (file)
 #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;
index b55a1a7f309fc875ea16a68b29690ed276478357..d223776d0e9f1473e922553b458fdb6173037173 100644 (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 */
index b0f67c97f074947b71bace4bf4ad5d3def88cd97..cb1a20b4eccd2552913f45a127ee07b7527f9e28 100644 (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);