source: branches/samba-3.5.x/lib/util/genrand.c@ 1002

Last change on this file since 1002 was 456, checked in by Silvan Scherrer, 15 years ago

Samba Server 3.5: changes from 3.3

File size: 9.8 KB
Line 
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
32static unsigned char hash[258];
33static uint32_t counter;
34
35static bool done_reseed = false;
36static unsigned int bytes_since_reseed = 0;
37
38static int urand_fd = -1;
39
40static void (*reseed_callback)(void *userdata, int *newseed);
41static 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)
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)
58{
59 done_reseed = false;
60 bytes_since_reseed = 0;
61}
62
63static void get_rand_reseed_data(int *reseed_data)
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
76static void seed_random_stream(unsigned char *seedval, size_t seedlen)
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
102static void get_random_stream(unsigned char *data, size_t datasize)
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
134static void do_filehash(const char *fname, unsigned char *the_hash)
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
166static int do_reseed(bool use_fd, int fd)
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)
221{
222 unsigned char md4_buf[64];
223 unsigned char tmp_buf[16];
224 unsigned char *p;
225
226#ifndef __OS2__
227 if(!done_reseed) {
228 bytes_since_reseed += len;
229
230 /* Magic constant to try and avoid reading 40 bytes
231 * and setting up the PRNG if the app only ever wants
232 * a few bytes */
233 if (bytes_since_reseed < 40) {
234 if (urand_fd == -1) {
235 urand_fd = open( "/dev/urandom", O_RDONLY,0);
236 }
237 if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
238 return;
239 }
240 }
241
242 urand_fd = do_reseed(true, urand_fd);
243 done_reseed = true;
244 }
245#endif
246
247 /*
248 * Generate random numbers in chunks of 64 bytes,
249 * then md4 them & copy to the output buffer.
250 * This way the raw state of the stream is never externally
251 * seen.
252 */
253
254 p = out;
255 while(len > 0) {
256 int copy_len = len > 16 ? 16 : len;
257
258#ifdef __OS2__
259 os2_randget(md4_buf, sizeof(md4_buf));
260#else
261 get_random_stream(md4_buf, sizeof(md4_buf));
262#endif
263 mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
264 memcpy(p, tmp_buf, copy_len);
265 p += copy_len;
266 len -= copy_len;
267 }
268}
269
270/**
271 Interface to the (hopefully) good crypto random number generator.
272 Will always use /dev/urandom if available.
273**/
274_PUBLIC_ void generate_secret_buffer(uint8_t *out, int len)
275{
276 if (urand_fd == -1) {
277 urand_fd = open( "/dev/urandom", O_RDONLY,0);
278 }
279 if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
280 return;
281 }
282
283 generate_random_buffer(out, len);
284}
285
286/**
287 generate a single random uint32_t
288**/
289_PUBLIC_ uint32_t generate_random(void)
290{
291 uint8_t v[4];
292 generate_random_buffer(v, 4);
293 return IVAL(v, 0);
294}
295
296
297/**
298 very basic password quality checker
299**/
300_PUBLIC_ bool check_password_quality(const char *s)
301{
302 int has_digit=0, has_capital=0, has_lower=0, has_special=0, has_high=0;
303 const char* reals = s;
304 while (*s) {
305 if (isdigit((unsigned char)*s)) {
306 has_digit |= 1;
307 } else if (isupper((unsigned char)*s)) {
308 has_capital |= 1;
309 } else if (islower((unsigned char)*s)) {
310 has_lower |= 1;
311 } else if (isascii((unsigned char)*s)) {
312 has_special |= 1;
313 } else {
314 has_high++;
315 }
316 s++;
317 }
318
319 return ((has_digit + has_lower + has_capital + has_special) >= 3
320 || (has_high > strlen(reals)/2));
321}
322
323/**
324 Use the random number generator to generate a random string.
325**/
326
327_PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
328{
329 size_t i;
330 size_t list_len = strlen(list);
331
332 char *retstr = talloc_array(mem_ctx, char, len + 1);
333 if (!retstr) return NULL;
334
335 generate_random_buffer((uint8_t *)retstr, len);
336 for (i = 0; i < len; i++) {
337 retstr[i] = list[retstr[i] % list_len];
338 }
339 retstr[i] = '\0';
340
341 return retstr;
342}
343
344/**
345 * Generate a random text string consisting of the specified length.
346 * The returned string will be allocated.
347 *
348 * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
349 */
350
351_PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
352{
353 char *retstr;
354 const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
355
356again:
357 retstr = generate_random_str_list(mem_ctx, len, c_list);
358 if (!retstr) return NULL;
359
360 /* we need to make sure the random string passes basic quality tests
361 or it might be rejected by windows as a password */
362 if (len >= 7 && !check_password_quality(retstr)) {
363 talloc_free(retstr);
364 goto again;
365 }
366
367 return retstr;
368}
369
370/**
371 * Generate an array of unique text strings all of the same length.
372 * The returned string will be allocated.
373 * Returns NULL if the number of unique combinations cannot be created.
374 *
375 * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
376 */
377_PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len,
378 uint32_t num)
379{
380 const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
381 const unsigned c_size = 42;
382 int i, j;
383 unsigned rem;
384 char ** strs = NULL;
385
386 if (num == 0 || len == 0)
387 return NULL;
388
389 strs = talloc_array(mem_ctx, char *, num);
390 if (strs == NULL) return NULL;
391
392 for (i = 0; i < num; i++) {
393 char *retstr = (char *)talloc_size(strs, len + 1);
394 if (retstr == NULL) {
395 talloc_free(strs);
396 return NULL;
397 }
398 rem = i;
399 for (j = 0; j < len; j++) {
400 retstr[j] = c_list[rem % c_size];
401 rem = rem / c_size;
402 }
403 retstr[j] = 0;
404 strs[i] = retstr;
405 if (rem != 0) {
406 /* we were not able to fit the number of
407 * combinations asked for in the length
408 * specified */
409 DEBUG(0,(__location__ ": Too many combinations %u for length %u\n",
410 num, (unsigned)len));
411
412 talloc_free(strs);
413 return NULL;
414 }
415 }
416
417 return strs;
418}
Note: See TracBrowser for help on using the repository browser.