s3: Add an async smbsock_connect
[Samba.git] / lib / util / genrand.c
blobcd1823a9a07bcacc2063713b22c3d772398ddca6
1 /*
2 Unix SMB/CIFS implementation.
4 Functions to create reasonable random numbers for crypto use.
6 Copyright (C) Jeremy Allison 2001
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.
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.
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/>.
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "../lib/crypto/crypto.h"
25 #include "system/locale.h"
27 /**
28 * @file
29 * @brief Random number generation
32 static unsigned char hash[258];
33 static uint32_t counter;
35 static bool done_reseed = false;
36 static unsigned int bytes_since_reseed = 0;
38 static int urand_fd = -1;
40 static void (*reseed_callback)(void *userdata, int *newseed);
41 static void *reseed_callback_userdata = NULL;
43 /**
44 Copy any user given reseed data.
45 **/
47 _PUBLIC_ void set_rand_reseed_callback(void (*fn)(void *, int *), void *userdata)
49 reseed_callback = fn;
50 reseed_callback_userdata = userdata;
51 set_need_random_reseed();
54 /**
55 * Tell the random number generator it needs to reseed.
57 _PUBLIC_ void set_need_random_reseed(void)
59 done_reseed = false;
60 bytes_since_reseed = 0;
63 static void get_rand_reseed_data(int *reseed_data)
65 if (reseed_callback) {
66 reseed_callback(reseed_callback_userdata, reseed_data);
67 } else {
68 *reseed_data = 0;
72 /****************************************************************
73 Setup the seed.
74 *****************************************************************/
76 static void seed_random_stream(unsigned char *seedval, size_t seedlen)
78 unsigned char j = 0;
79 size_t ind;
81 for (ind = 0; ind < 256; ind++)
82 hash[ind] = (unsigned char)ind;
84 for( ind = 0; ind < 256; ind++) {
85 unsigned char tc;
87 j += (hash[ind] + seedval[ind%seedlen]);
89 tc = hash[ind];
90 hash[ind] = hash[j];
91 hash[j] = tc;
94 hash[256] = 0;
95 hash[257] = 0;
98 /****************************************************************
99 Get datasize bytes worth of random data.
100 *****************************************************************/
102 static void get_random_stream(unsigned char *data, size_t datasize)
104 unsigned char index_i = hash[256];
105 unsigned char index_j = hash[257];
106 size_t ind;
108 for( ind = 0; ind < datasize; ind++) {
109 unsigned char tc;
110 unsigned char t;
112 index_i++;
113 index_j += hash[index_i];
115 tc = hash[index_i];
116 hash[index_i] = hash[index_j];
117 hash[index_j] = tc;
119 t = hash[index_i] + hash[index_j];
120 data[ind] = hash[t];
123 hash[256] = index_i;
124 hash[257] = index_j;
127 /****************************************************************
128 Get a 16 byte hash from the contents of a file.
130 Note that the hash is initialised, because the extra entropy is not
131 worth the valgrind pain.
132 *****************************************************************/
134 static void do_filehash(const char *fname, unsigned char *the_hash)
136 unsigned char buf[1011]; /* deliberate weird size */
137 unsigned char tmp_md4[16];
138 int fd, n;
140 ZERO_STRUCT(tmp_md4);
142 fd = open(fname,O_RDONLY,0);
143 if (fd == -1)
144 return;
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];
151 close(fd);
154 /**************************************************************
155 Try and get a good random number seed. Try a number of
156 different factors. Firstly, try /dev/urandom - use if exists.
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.
162 If we can't use /dev/urandom then seed the stream random generator
163 above...
164 **************************************************************/
166 static int do_reseed(bool use_fd, int fd)
168 unsigned char seed_inbuf[40];
169 uint32_t v1, v2; struct timeval tval; pid_t mypid;
170 int reseed_data = 0;
172 if (use_fd) {
173 if (fd == -1) {
174 fd = open( "/dev/urandom", O_RDONLY,0);
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;
183 /* Add in some secret file contents */
185 do_filehash("/etc/shadow", &seed_inbuf[0]);
188 * Add the counter, time of day, and pid.
191 GetTimeOfDay(&tval);
192 mypid = getpid();
193 v1 = (counter++) + mypid + tval.tv_sec;
194 v2 = (counter++) * mypid + tval.tv_usec;
196 SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
197 SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
200 * Add any user-given reseed data.
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)];
210 seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
212 return -1;
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
220 _PUBLIC_ void generate_random_buffer(uint8_t *out, int len)
222 unsigned char md4_buf[64];
223 unsigned char tmp_buf[16];
224 unsigned char *p;
226 if(!done_reseed) {
227 bytes_since_reseed += len;
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);
236 if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
237 return;
241 urand_fd = do_reseed(true, urand_fd);
242 done_reseed = true;
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.
252 p = out;
253 while(len > 0) {
254 int copy_len = len > 16 ? 16 : len;
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;
265 Interface to the (hopefully) good crypto random number generator.
266 Will always use /dev/urandom if available.
268 _PUBLIC_ void generate_secret_buffer(uint8_t *out, int len)
270 if (urand_fd == -1) {
271 urand_fd = open( "/dev/urandom", O_RDONLY,0);
273 if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
274 return;
277 generate_random_buffer(out, len);
281 generate a single random uint32_t
283 _PUBLIC_ uint32_t generate_random(void)
285 uint8_t v[4];
286 generate_random_buffer(v, 4);
287 return IVAL(v, 0);
292 very basic password quality checker
294 _PUBLIC_ bool check_password_quality(const char *s)
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++;
309 s++;
312 return ((has_digit + has_lower + has_capital + has_special) >= 3
313 || (has_high > strlen(s)/2));
317 Use the random number generator to generate a random string.
320 _PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
322 size_t i;
323 size_t list_len = strlen(list);
325 char *retstr = talloc_array(mem_ctx, char, len + 1);
326 if (!retstr) return NULL;
328 generate_random_buffer((uint8_t *)retstr, len);
329 for (i = 0; i < len; i++) {
330 retstr[i] = list[retstr[i] % list_len];
332 retstr[i] = '\0';
334 return retstr;
338 * Generate a random text string consisting of the specified length.
339 * The returned string will be allocated.
341 * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
344 _PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
346 char *retstr;
347 const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
349 again:
350 retstr = generate_random_str_list(mem_ctx, len, c_list);
351 if (!retstr) return NULL;
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;
360 return retstr;