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 2 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, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22 23#include "includes.h" 24 25static unsigned char hash[258]; 26static uint32 counter; 27 28static BOOL done_reseed = False; 29static void (*reseed_callback)(int *newseed); 30 31/**************************************************************** 32 Copy any user given reseed data. 33*****************************************************************/ 34 35void set_rand_reseed_callback(void (*fn)(int *)) 36{ 37 reseed_callback = fn; 38 set_need_random_reseed(); 39} 40 41void set_need_random_reseed(void) 42{ 43 done_reseed = False; 44} 45 46static void get_rand_reseed_data(int *reseed_data) 47{ 48 if (reseed_callback) { 49 reseed_callback(reseed_data); 50 } else { 51 *reseed_data = 0; 52 } 53} 54 55/**************************************************************** 56 Setup the seed. 57*****************************************************************/ 58 59static void seed_random_stream(unsigned char *seedval, size_t seedlen) 60{ 61 unsigned char j = 0; 62 size_t ind; 63 64 for (ind = 0; ind < 256; ind++) 65 hash[ind] = (unsigned char)ind; 66 67 for( ind = 0; ind < 256; ind++) { 68 unsigned char tc; 69 70 j += (hash[ind] + seedval[ind%seedlen]); 71 72 tc = hash[ind]; 73 hash[ind] = hash[j]; 74 hash[j] = tc; 75 } 76 77 hash[256] = 0; 78 hash[257] = 0; 79} 80 81/**************************************************************** 82 Get datasize bytes worth of random data. 83*****************************************************************/ 84 85static void get_random_stream(unsigned char *data, size_t datasize) 86{ 87 unsigned char index_i = hash[256]; 88 unsigned char index_j = hash[257]; 89 size_t ind; 90 91 for( ind = 0; ind < datasize; ind++) { 92 unsigned char tc; 93 unsigned char t; 94 95 index_i++; 96 index_j += hash[index_i]; 97 98 tc = hash[index_i]; 99 hash[index_i] = hash[index_j]; 100 hash[index_j] = tc; 101 102 t = hash[index_i] + hash[index_j]; 103 data[ind] = hash[t]; 104 } 105 106 hash[256] = index_i; 107 hash[257] = index_j; 108} 109 110/**************************************************************** 111 Get a 16 byte hash from the contents of a file. 112 Note that the hash is not initialised. 113*****************************************************************/ 114 115static void do_filehash(const char *fname, unsigned char *the_hash) 116{ 117 unsigned char buf[1011]; /* deliberate weird size */ 118 unsigned char tmp_md4[16]; 119 int fd, n; 120 121 fd = sys_open(fname,O_RDONLY,0); 122 if (fd == -1) 123 return; 124 125 while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) { 126 mdfour(tmp_md4, buf, n); 127 for (n=0;n<16;n++) 128 the_hash[n] ^= tmp_md4[n]; 129 } 130 close(fd); 131} 132 133/************************************************************** 134 Try and get a good random number seed. Try a number of 135 different factors. Firstly, try /dev/urandom - use if exists. 136 137 We use /dev/urandom as a read of /dev/random can block if 138 the entropy pool dries up. This leads clients to timeout 139 or be very slow on connect. 140 141 If we can't use /dev/urandom then seed the stream random generator 142 above... 143**************************************************************/ 144 145static int do_reseed(BOOL use_fd, int fd) 146{ 147 unsigned char seed_inbuf[40]; 148 uint32 v1, v2; struct timeval tval; pid_t mypid; 149 struct passwd *pw; 150 int reseed_data = 0; 151 152 if (use_fd) { 153 if (fd != -1) 154 return fd; 155 156 fd = sys_open( "/dev/urandom", O_RDONLY,0); 157 if(fd >= 0) 158 return fd; 159 } 160 161 /* Add in some secret file contents */ 162 163 do_filehash("/etc/shadow", &seed_inbuf[0]); 164 do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]); 165 166 /* 167 * Add in the root encrypted password. 168 * On any system where security is taken 169 * seriously this will be secret. 170 */ 171 172 pw = getpwnam_alloc("root"); 173 if (pw && pw->pw_passwd) { 174 size_t i; 175 unsigned char md4_tmp[16]; 176 mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd)); 177 for (i=0;i<16;i++) 178 seed_inbuf[8+i] ^= md4_tmp[i]; 179 passwd_free(&pw); 180 } 181 182 /* 183 * Add the counter, time of day, and pid. 184 */ 185 186 GetTimeOfDay(&tval); 187 mypid = sys_getpid(); 188 v1 = (counter++) + mypid + tval.tv_sec; 189 v2 = (counter++) * mypid + tval.tv_usec; 190 191 SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32)); 192 SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36)); 193 194 /* 195 * Add any user-given reseed data. 196 */ 197 198 get_rand_reseed_data(&reseed_data); 199 if (reseed_data) { 200 size_t i; 201 for (i = 0; i < sizeof(seed_inbuf); i++) 202 seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)]; 203 } 204 205 seed_random_stream(seed_inbuf, sizeof(seed_inbuf)); 206 207 return -1; 208} 209 210/******************************************************************* 211 Interface to the (hopefully) good crypto random number generator. 212********************************************************************/ 213 214void generate_random_buffer( unsigned char *out, int len) 215{ 216 static int urand_fd = -1; 217 unsigned char md4_buf[64]; 218 unsigned char tmp_buf[16]; 219 unsigned char *p; 220 221 if(!done_reseed) { 222 urand_fd = do_reseed(True, urand_fd); 223 done_reseed = True; 224 } 225 226 if (urand_fd != -1 && len > 0) { 227 228 if (read(urand_fd, out, len) == len) 229 return; /* len bytes of random data read from urandom. */ 230 231 /* Read of urand error, drop back to non urand method. */ 232 close(urand_fd); 233 urand_fd = -1; 234 do_reseed(False, -1); 235 done_reseed = True; 236 } 237 238 /* 239 * Generate random numbers in chunks of 64 bytes, 240 * then md4 them & copy to the output buffer. 241 * This way the raw state of the stream is never externally 242 * seen. 243 */ 244 245 p = out; 246 while(len > 0) { 247 int copy_len = len > 16 ? 16 : len; 248 249 get_random_stream(md4_buf, sizeof(md4_buf)); 250 mdfour(tmp_buf, md4_buf, sizeof(md4_buf)); 251 memcpy(p, tmp_buf, copy_len); 252 p += copy_len; 253 len -= copy_len; 254 } 255} 256 257/******************************************************************* 258 Use the random number generator to generate a random string. 259********************************************************************/ 260 261static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,"; 262 263char *generate_random_str(size_t len) 264{ 265 static unsigned char retstr[256]; 266 size_t i; 267 268 memset(retstr, '\0', sizeof(retstr)); 269 270 if (len > sizeof(retstr)-1) 271 len = sizeof(retstr) -1; 272 generate_random_buffer( retstr, len); 273 for (i = 0; i < len; i++) 274 retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ]; 275 276 retstr[i] = '\0'; 277 278 return (char *)retstr; 279} 280