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 smb_arc4_state[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 Get a 16 byte hash from the contents of a file. 57 Note that the hash is not initialised. 58*****************************************************************/ 59 60static void do_filehash(const char *fname, unsigned char *the_hash) 61{ 62 unsigned char buf[1011]; /* deliberate weird size */ 63 unsigned char tmp_md4[16]; 64 int fd, n; 65 66 fd = sys_open(fname,O_RDONLY,0); 67 if (fd == -1) 68 return; 69 70 while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) { 71 mdfour(tmp_md4, buf, n); 72 for (n=0;n<16;n++) 73 the_hash[n] ^= tmp_md4[n]; 74 } 75 close(fd); 76} 77 78/************************************************************** 79 Try and get a good random number seed. Try a number of 80 different factors. Firstly, try /dev/urandom - use if exists. 81 82 We use /dev/urandom as a read of /dev/random can block if 83 the entropy pool dries up. This leads clients to timeout 84 or be very slow on connect. 85 86 If we can't use /dev/urandom then seed the stream random generator 87 above... 88**************************************************************/ 89 90static int do_reseed(BOOL use_fd, int fd) 91{ 92 unsigned char seed_inbuf[40]; 93 uint32 v1, v2; struct timeval tval; pid_t mypid; 94 struct passwd *pw; 95 int reseed_data = 0; 96 97 if (use_fd) { 98 if (fd != -1) 99 return fd; 100 101 fd = sys_open( "/dev/urandom", O_RDONLY,0); 102 if(fd >= 0) 103 return fd; 104 } 105 106 /* Add in some secret file contents */ 107 108 do_filehash("/etc/shadow", &seed_inbuf[0]); 109 do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]); 110 111 /* 112 * Add in the root encrypted password. 113 * On any system where security is taken 114 * seriously this will be secret. 115 */ 116 117 pw = getpwnam_alloc(NULL, "root"); 118 if (pw && pw->pw_passwd) { 119 size_t i; 120 unsigned char md4_tmp[16]; 121 mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd)); 122 for (i=0;i<16;i++) 123 seed_inbuf[8+i] ^= md4_tmp[i]; 124 TALLOC_FREE(pw); 125 } 126 127 /* 128 * Add the counter, time of day, and pid. 129 */ 130 131 GetTimeOfDay(&tval); 132 mypid = sys_getpid(); 133 v1 = (counter++) + mypid + tval.tv_sec; 134 v2 = (counter++) * mypid + tval.tv_usec; 135 136 SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32)); 137 SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36)); 138 139 /* 140 * Add any user-given reseed data. 141 */ 142 143 get_rand_reseed_data(&reseed_data); 144 if (reseed_data) { 145 size_t i; 146 for (i = 0; i < sizeof(seed_inbuf); i++) 147 seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)]; 148 } 149 150 smb_arc4_init(smb_arc4_state, seed_inbuf, sizeof(seed_inbuf)); 151 152 return -1; 153} 154 155/******************************************************************* 156 Interface to the (hopefully) good crypto random number generator. 157********************************************************************/ 158 159void generate_random_buffer( unsigned char *out, int len) 160{ 161 static int urand_fd = -1; 162 unsigned char md4_buf[64]; 163 unsigned char tmp_buf[16]; 164 unsigned char *p; 165 166 if(!done_reseed) { 167 urand_fd = do_reseed(True, urand_fd); 168 done_reseed = True; 169 } 170 171 if (urand_fd != -1 && len > 0) { 172 173 if (read(urand_fd, out, len) == len) 174 return; /* len bytes of random data read from urandom. */ 175 176 /* Read of urand error, drop back to non urand method. */ 177 close(urand_fd); 178 urand_fd = -1; 179 do_reseed(False, -1); 180 done_reseed = True; 181 } 182 183 /* 184 * Generate random numbers in chunks of 64 bytes, 185 * then md4 them & copy to the output buffer. 186 * This way the raw state of the stream is never externally 187 * seen. 188 */ 189 190 p = out; 191 while(len > 0) { 192 int copy_len = len > 16 ? 16 : len; 193 194 smb_arc4_crypt(smb_arc4_state, md4_buf, sizeof(md4_buf)); 195 mdfour(tmp_buf, md4_buf, sizeof(md4_buf)); 196 memcpy(p, tmp_buf, copy_len); 197 p += copy_len; 198 len -= copy_len; 199 } 200} 201 202/******************************************************************* 203 Use the random number generator to generate a random string. 204********************************************************************/ 205 206static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,"; 207 208char *generate_random_str(size_t len) 209{ 210 static unsigned char retstr[256]; 211 size_t i; 212 213 memset(retstr, '\0', sizeof(retstr)); 214 215 if (len > sizeof(retstr)-1) 216 len = sizeof(retstr) -1; 217 generate_random_buffer( retstr, len); 218 for (i = 0; i < len; i++) 219 retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ]; 220 221 retstr[i] = '\0'; 222 223 return (char *)retstr; 224} 225