diff options
-rw-r--r-- | source3/lib/genrand.c | 354 |
1 files changed, 186 insertions, 168 deletions
diff --git a/source3/lib/genrand.c b/source3/lib/genrand.c index 4a7de802e8..86b3b56696 100644 --- a/source3/lib/genrand.c +++ b/source3/lib/genrand.c @@ -1,10 +1,10 @@ /* Unix SMB/Netbios implementation. - Version 1.9. + Version 2.2 Functions to create reasonable random numbers for crypto use. - Copyright (C) Jeremy Allison 1998 + Copyright (C) Jeremy Allison 2001 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,12 +24,88 @@ #include "includes.h" extern int DEBUGLEVEL; -static uint32 counter = 0; + + +static unsigned char hash[258]; +static uint32 counter; +unsigned char *reseed_data; +size_t reseed_data_size; + +/**************************************************************** + Copy any user given reseed data. +*****************************************************************/ + +void set_rand_reseed_data(unsigned char *data, size_t len) +{ + if (reseed_data) + free(reseed_data); + reseed_data_size = 0; + + reseed_data = (unsigned char *)memdup(data, len); + if (reseed_data) + reseed_data_size = len; +} + +/**************************************************************** + 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 not initialised. + Get a 16 byte hash from the contents of a file. + Note that the hash is not initialised. *****************************************************************/ + static void do_filehash(char *fname, unsigned char *hash) { unsigned char buf[1011]; /* deliberate weird size */ @@ -37,7 +113,8 @@ static void do_filehash(char *fname, unsigned char *hash) int fd, n; fd = sys_open(fname,O_RDONLY,0); - if (fd == -1) return; + if (fd == -1) + return; while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) { mdfour(tmp_md4, buf, n); @@ -47,185 +124,126 @@ static void do_filehash(char *fname, unsigned char *hash) close(fd); } - - -/**************************************************************** - Try and get a seed by looking at the atimes of files in a given - directory. XOR them into the buf array. -*****************************************************************/ - -static void do_dirrand(char *name, unsigned char *buf, int buf_len) -{ - DIR *dp = opendir(name); - pstring fullname; - int len_left; - int fullname_len; - char *pos; - - pstrcpy(fullname, name); - fullname_len = strlen(fullname); - - if(fullname_len + 2 > sizeof(pstring)) - return; - - if(fullname[fullname_len] != '/') { - fullname[fullname_len] = '/'; - fullname[fullname_len+1] = '\0'; - fullname_len = strlen(fullname); - } - - len_left = sizeof(pstring) - fullname_len - 1; - pos = &fullname[fullname_len]; - - if(dp != NULL) { - char *p; - - while ((p = readdirname(dp))) { - SMB_STRUCT_STAT st; - - if(strlen(p) <= len_left) - pstrcpy(pos, p); - - if(sys_stat(fullname,&st) == 0) { - SIVAL(buf, ((counter * 4)%(buf_len-4)), - IVAL(buf,((counter * 4)%(buf_len-4))) ^ st.st_atime); - counter++; - DEBUG(10,("do_dirrand: value from file %s.\n", fullname)); - } - } - closedir(dp); - } -} - /************************************************************** Try and get a good random number seed. Try a number of - different factors. Firstly, try /dev/urandom and try and - read from this. If this fails iterate through /tmp and - /dev and XOR all the file timestamps. Next add in - a hash of the contents of /etc/shadow and the smb passwd - file and a combination of pid and time of day (yes I know this - sucks :-). Finally md4 the result. + 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. - The result goes in a 16 byte buffer passed from the caller + If we can't use /dev/urandom then seed the stream random generator + above... **************************************************************/ -static uint32 do_reseed(unsigned char *md4_outbuf) +static int do_reseed(BOOL use_fd, int fd) { - unsigned char md4_inbuf[40]; - BOOL got_random = False; - uint32 v1, v2, ret; - int fd; - struct timeval tval; - pid_t mypid; - struct passwd *pw; - - memset(md4_inbuf, '\0', sizeof(md4_inbuf)); - - fd = sys_open( "/dev/urandom", O_RDONLY,0); - if(fd >= 0) { - /* - * We can use /dev/urandom ! - */ - if(read(fd, md4_inbuf, 40) == 40) { - got_random = True; - DEBUG(10,("do_reseed: got 40 bytes from /dev/urandom.\n")); - } - close(fd); - } - - if(!got_random) { - /* - * /dev/urandom failed - try /dev for timestamps. - */ - do_dirrand("/dev", md4_inbuf, sizeof(md4_inbuf)); - } - - /* possibly add in some secret file contents */ - do_filehash("/etc/shadow", &md4_inbuf[0]); - do_filehash(lp_smb_passwd_file(), &md4_inbuf[16]); - - /* add in the root encrypted password. On any system where security is taken - seriously this will be secret */ - pw = sys_getpwnam("root"); - if (pw && pw->pw_passwd) { - int i; - unsigned char md4_tmp[16]; - mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd)); - for (i=0;i<16;i++) - md4_inbuf[8+i] ^= md4_tmp[i]; - } - - /* - * Finally add the counter, time of day, and pid. - */ - GetTimeOfDay(&tval); - mypid = sys_getpid(); - v1 = (counter++) + mypid + tval.tv_sec; - v2 = (counter++) * mypid + tval.tv_usec; - - SIVAL(md4_inbuf, 32, v1 ^ IVAL(md4_inbuf, 32)); - SIVAL(md4_inbuf, 36, v2 ^ IVAL(md4_inbuf, 36)); - - mdfour(md4_outbuf, md4_inbuf, sizeof(md4_inbuf)); - - /* - * Return a 32 bit int created from XORing the - * 16 bit return buffer. - */ - - ret = IVAL(md4_outbuf, 0); - ret ^= IVAL(md4_outbuf, 4); - ret ^= IVAL(md4_outbuf, 8); - return (ret ^ IVAL(md4_outbuf, 12)); + unsigned char seed_inbuf[40]; + uint32 v1, v2; struct timeval tval; pid_t mypid; + struct passwd *pw; + + if (use_fd) { + if (fd != -1) + return fd; + + fd = sys_open( "/dev/urandom", O_RDONLY,0); + if(fd >= 0) + return fd; + } + + /* Add in some secret file contents */ + + do_filehash("/etc/shadow", &seed_inbuf[0]); + do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]); + + /* + * Add in the root encrypted password. + * On any system where security is taken + * seriously this will be secret. + */ + + pw = sys_getpwnam("root"); + if (pw && pw->pw_passwd) { + size_t i; + unsigned char md4_tmp[16]; + mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd)); + for (i=0;i<16;i++) + seed_inbuf[8+i] ^= md4_tmp[i]; + } + + /* + * Add the counter, time of day, and pid. + */ + + GetTimeOfDay(&tval); + mypid = sys_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. + */ + + if (reseed_data) { + size_t i; + for (i = 0; i < sizeof(seed_inbuf); i++) + seed_inbuf[i] ^= reseed_data[i % reseed_data_size]; + } + + seed_random_stream(seed_inbuf, sizeof(seed_inbuf)); + + return -1; } /******************************************************************* Interface to the (hopefully) good crypto random number generator. ********************************************************************/ -void generate_random_buffer( unsigned char *out, int len, BOOL re_seed) +void generate_random_buffer( unsigned char *out, int len, BOOL do_reseed_now) { - static BOOL done_reseed = False; - static unsigned char md4_buf[16]; - unsigned char tmp_buf[16]; - unsigned char *p; - - if(!done_reseed || re_seed) { - sys_srandom(do_reseed(md4_buf)); - done_reseed = True; - } - - /* - * Generate random numbers in chunks of 64 bytes, - * then md4 them & copy to the output buffer. - * Added XOR with output from random, seeded - * by the original md4_buf. This is to stop the - * output from this function being the previous - * md4_buf md4'ed. The output from this function - * is often output onto the wire, and so it should - * not be possible to guess the next output from - * this function based on the previous output. - * XORing in the output from random(), seeded by - * the original md4 hash should stop this. JRA. - */ - - p = out; - while(len > 0) { - int i; - int copy_len = len > 16 ? 16 : len; - mdfour(tmp_buf, md4_buf, sizeof(md4_buf)); - memcpy(md4_buf, tmp_buf, sizeof(md4_buf)); - /* XOR in output from random(). */ - for(i = 0; i < 4; i++) - SIVAL(tmp_buf, i*4, (IVAL(tmp_buf, i*4) ^ (uint32)sys_random())); - memcpy(p, tmp_buf, copy_len); - p += copy_len; - len -= copy_len; - } + static BOOL done_reseed = False; + static int urand_fd = -1; + unsigned char md4_buf[64]; + unsigned char tmp_buf[16]; + unsigned char *p; + + if(!done_reseed || do_reseed_now) { + urand_fd = do_reseed(True, urand_fd); + done_reseed = True; + } + + if (urand_fd != -1 && len > 0) { + + if (read(urand_fd, out, len) == len) + return; /* len bytes of random data read from urandom. */ + + /* Read of urand error, drop back to non urand method. */ + close(urand_fd); + urand_fd = -1; + do_reseed(False, -1); + 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; + } } /******************************************************************* |