root/lib/util/genrand.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_rand_reseed_callback
  2. set_need_random_reseed
  3. get_rand_reseed_data
  4. seed_random_stream
  5. get_random_stream
  6. do_filehash
  7. do_reseed
  8. generate_random_buffer
  9. generate_secret_buffer
  10. generate_random
  11. check_password_quality
  12. generate_random_str_list
  13. generate_random_str

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    Functions to create reasonable random numbers for crypto use.
   5 
   6    Copyright (C) Jeremy Allison 2001
   7    
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 3 of the License, or
  11    (at your option) any later version.
  12    
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17    
  18    You should have received a copy of the GNU General Public License
  19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  20 */
  21 
  22 #include "includes.h"
  23 #include "system/filesys.h"
  24 #include "../lib/crypto/crypto.h"
  25 #include "system/locale.h"
  26 
  27 /**
  28  * @file
  29  * @brief Random number generation
  30  */
  31 
  32 static unsigned char hash[258];
  33 static uint32_t counter;
  34 
  35 static bool done_reseed = false;
  36 static unsigned int bytes_since_reseed = 0;
  37 
  38 static int urand_fd = -1;
  39 
  40 static void (*reseed_callback)(void *userdata, int *newseed);
  41 static void *reseed_callback_userdata = NULL;
  42 
  43 /**
  44  Copy any user given reseed data.
  45 **/
  46 
  47 _PUBLIC_ void set_rand_reseed_callback(void (*fn)(void *, int *), void *userdata)
     /* [<][>][^][v][top][bottom][index][help] */
  48 {
  49         reseed_callback = fn;
  50         reseed_callback_userdata = userdata;
  51         set_need_random_reseed();
  52 }
  53 
  54 /**
  55  * Tell the random number generator it needs to reseed.
  56  */
  57 _PUBLIC_ void set_need_random_reseed(void)
     /* [<][>][^][v][top][bottom][index][help] */
  58 {
  59         done_reseed = false;
  60         bytes_since_reseed = 0;
  61 }
  62 
  63 static void get_rand_reseed_data(int *reseed_data)
     /* [<][>][^][v][top][bottom][index][help] */
  64 {
  65         if (reseed_callback) {
  66                 reseed_callback(reseed_callback_userdata, reseed_data);
  67         } else {
  68                 *reseed_data = 0;
  69         }
  70 }
  71 
  72 /**************************************************************** 
  73  Setup the seed.
  74 *****************************************************************/
  75 
  76 static void seed_random_stream(unsigned char *seedval, size_t seedlen)
     /* [<][>][^][v][top][bottom][index][help] */
  77 {
  78         unsigned char j = 0;
  79         size_t ind;
  80 
  81         for (ind = 0; ind < 256; ind++)
  82                 hash[ind] = (unsigned char)ind;
  83 
  84         for( ind = 0; ind < 256; ind++) {
  85                 unsigned char tc;
  86 
  87                 j += (hash[ind] + seedval[ind%seedlen]);
  88 
  89                 tc = hash[ind];
  90                 hash[ind] = hash[j];
  91                 hash[j] = tc;
  92         }
  93 
  94         hash[256] = 0;
  95         hash[257] = 0;
  96 }
  97 
  98 /**************************************************************** 
  99  Get datasize bytes worth of random data.
 100 *****************************************************************/
 101 
 102 static void get_random_stream(unsigned char *data, size_t datasize)
     /* [<][>][^][v][top][bottom][index][help] */
 103 {
 104         unsigned char index_i = hash[256];
 105         unsigned char index_j = hash[257];
 106         size_t ind;
 107 
 108         for( ind = 0; ind < datasize; ind++) {
 109                 unsigned char tc;
 110                 unsigned char t;
 111 
 112                 index_i++;
 113                 index_j += hash[index_i];
 114 
 115                 tc = hash[index_i];
 116                 hash[index_i] = hash[index_j];
 117                 hash[index_j] = tc;
 118 
 119                 t = hash[index_i] + hash[index_j];
 120                 data[ind] = hash[t];
 121         }
 122 
 123         hash[256] = index_i;
 124         hash[257] = index_j;
 125 }
 126 
 127 /****************************************************************
 128  Get a 16 byte hash from the contents of a file.  
 129 
 130  Note that the hash is initialised, because the extra entropy is not
 131  worth the valgrind pain.
 132 *****************************************************************/
 133 
 134 static void do_filehash(const char *fname, unsigned char *the_hash)
     /* [<][>][^][v][top][bottom][index][help] */
 135 {
 136         unsigned char buf[1011]; /* deliberate weird size */
 137         unsigned char tmp_md4[16];
 138         int fd, n;
 139 
 140         ZERO_STRUCT(tmp_md4);
 141 
 142         fd = open(fname,O_RDONLY,0);
 143         if (fd == -1)
 144                 return;
 145 
 146         while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
 147                 mdfour(tmp_md4, buf, n);
 148                 for (n=0;n<16;n++)
 149                         the_hash[n] ^= tmp_md4[n];
 150         }
 151         close(fd);
 152 }
 153 
 154 /**************************************************************
 155  Try and get a good random number seed. Try a number of
 156  different factors. Firstly, try /dev/urandom - use if exists.
 157 
 158  We use /dev/urandom as a read of /dev/random can block if
 159  the entropy pool dries up. This leads clients to timeout
 160  or be very slow on connect.
 161 
 162  If we can't use /dev/urandom then seed the stream random generator
 163  above...
 164 **************************************************************/
 165 
 166 static int do_reseed(bool use_fd, int fd)
     /* [<][>][^][v][top][bottom][index][help] */
 167 {
 168         unsigned char seed_inbuf[40];
 169         uint32_t v1, v2; struct timeval tval; pid_t mypid;
 170         int reseed_data = 0;
 171 
 172         if (use_fd) {
 173                 if (fd == -1) {
 174                         fd = open( "/dev/urandom", O_RDONLY,0);
 175                 }
 176                 if (fd != -1
 177                     && (read(fd, seed_inbuf, sizeof(seed_inbuf)) == sizeof(seed_inbuf))) {
 178                         seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
 179                         return fd;
 180                 }
 181         }
 182 
 183         /* Add in some secret file contents */
 184 
 185         do_filehash("/etc/shadow", &seed_inbuf[0]);
 186 
 187         /*
 188          * Add the counter, time of day, and pid.
 189          */
 190 
 191         GetTimeOfDay(&tval);
 192         mypid = getpid();
 193         v1 = (counter++) + mypid + tval.tv_sec;
 194         v2 = (counter++) * mypid + tval.tv_usec;
 195 
 196         SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
 197         SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
 198 
 199         /*
 200          * Add any user-given reseed data.
 201          */
 202 
 203         get_rand_reseed_data(&reseed_data);
 204         if (reseed_data) {
 205                 size_t i;
 206                 for (i = 0; i < sizeof(seed_inbuf); i++)
 207                         seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)];
 208         }
 209 
 210         seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
 211 
 212         return -1;
 213 }
 214 
 215 /**
 216  Interface to the (hopefully) good crypto random number generator.
 217  Will use our internal PRNG if more than 40 bytes of random generation
 218  has been requested, otherwise tries to read from /dev/random
 219 **/
 220 _PUBLIC_ void generate_random_buffer(uint8_t *out, int len)
     /* [<][>][^][v][top][bottom][index][help] */
 221 {
 222         unsigned char md4_buf[64];
 223         unsigned char tmp_buf[16];
 224         unsigned char *p;
 225 
 226         if(!done_reseed) {
 227                 bytes_since_reseed += len;
 228                 
 229                 /* Magic constant to try and avoid reading 40 bytes
 230                  * and setting up the PRNG if the app only ever wants
 231                  * a few bytes */
 232                 if (bytes_since_reseed < 40) {
 233                         if (urand_fd == -1) {
 234                                 urand_fd = open( "/dev/urandom", O_RDONLY,0);
 235                         }
 236                         if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
 237                                 return;
 238                         }
 239                 }
 240 
 241                 urand_fd = do_reseed(true, urand_fd);
 242                 done_reseed = true;
 243         }
 244 
 245         /*
 246          * Generate random numbers in chunks of 64 bytes,
 247          * then md4 them & copy to the output buffer.
 248          * This way the raw state of the stream is never externally
 249          * seen.
 250          */
 251 
 252         p = out;
 253         while(len > 0) {
 254                 int copy_len = len > 16 ? 16 : len;
 255 
 256                 get_random_stream(md4_buf, sizeof(md4_buf));
 257                 mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
 258                 memcpy(p, tmp_buf, copy_len);
 259                 p += copy_len;
 260                 len -= copy_len;
 261         }
 262 }
 263 
 264 /**
 265  Interface to the (hopefully) good crypto random number generator.
 266  Will always use /dev/urandom if available.
 267 **/
 268 _PUBLIC_ void generate_secret_buffer(uint8_t *out, int len)
     /* [<][>][^][v][top][bottom][index][help] */
 269 {
 270         if (urand_fd == -1) {
 271                 urand_fd = open( "/dev/urandom", O_RDONLY,0);
 272         }
 273         if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
 274                 return;
 275         }
 276         
 277         generate_random_buffer(out, len);
 278 }
 279 
 280 /**
 281   generate a single random uint32_t
 282 **/
 283 _PUBLIC_ uint32_t generate_random(void)
     /* [<][>][^][v][top][bottom][index][help] */
 284 {
 285         uint8_t v[4];
 286         generate_random_buffer(v, 4);
 287         return IVAL(v, 0);
 288 }
 289 
 290 
 291 /**
 292   very basic password quality checker
 293 **/
 294 _PUBLIC_ bool check_password_quality(const char *s)
     /* [<][>][^][v][top][bottom][index][help] */
 295 {
 296         int has_digit=0, has_capital=0, has_lower=0, has_special=0, has_high=0;
 297         while (*s) {
 298                 if (isdigit((unsigned char)*s)) {
 299                         has_digit |= 1;
 300                 } else if (isupper((unsigned char)*s)) {
 301                         has_capital |= 1;
 302                 } else if (islower((unsigned char)*s)) {
 303                         has_lower |= 1;
 304                 } else if (isascii((unsigned char)*s)) {
 305                         has_special |= 1;
 306                 } else {
 307                         has_high++;
 308                 }
 309                 s++;
 310         }
 311 
 312         return ((has_digit + has_lower + has_capital + has_special) >= 3
 313                 || (has_high > strlen(s)/2));
 314 }
 315 
 316 /**
 317  Use the random number generator to generate a random string.
 318 **/
 319 
 320 _PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
     /* [<][>][^][v][top][bottom][index][help] */
 321 {
 322         size_t i;
 323         size_t list_len = strlen(list);
 324 
 325         char *retstr = talloc_array(mem_ctx, char, len + 1);
 326         if (!retstr) return NULL;
 327 
 328         generate_random_buffer((uint8_t *)retstr, len);
 329         for (i = 0; i < len; i++) {
 330                 retstr[i] = list[retstr[i] % list_len];
 331         }
 332         retstr[i] = '\0';
 333 
 334         return retstr;
 335 }
 336 
 337 /**
 338  * Generate a random text string consisting of the specified length.
 339  * The returned string will be allocated.
 340  *
 341  * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
 342  */
 343 
 344 _PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
     /* [<][>][^][v][top][bottom][index][help] */
 345 {
 346         char *retstr;
 347         const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
 348 
 349 again:
 350         retstr = generate_random_str_list(mem_ctx, len, c_list);
 351         if (!retstr) return NULL;
 352 
 353         /* we need to make sure the random string passes basic quality tests
 354            or it might be rejected by windows as a password */
 355         if (len >= 7 && !check_password_quality(retstr)) {
 356                 talloc_free(retstr);
 357                 goto again;
 358         }
 359 
 360         return retstr;
 361 }

/* [<][>][^][v][top][bottom][index][help] */