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/>.
23 #include "system/filesys.h"
24 #include "../lib/crypto/crypto.h"
25 #include "system/locale.h"
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
;
44 Copy any user given reseed data.
47 _PUBLIC_
void set_rand_reseed_callback(void (*fn
)(void *, int *), void *userdata
)
50 reseed_callback_userdata
= userdata
;
51 set_need_random_reseed();
55 * Tell the random number generator it needs to reseed.
57 _PUBLIC_
void set_need_random_reseed(void)
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
);
72 /****************************************************************
74 *****************************************************************/
76 static void seed_random_stream(unsigned char *seedval
, size_t seedlen
)
81 for (ind
= 0; ind
< 256; ind
++)
82 hash
[ind
] = (unsigned char)ind
;
84 for( ind
= 0; ind
< 256; ind
++) {
87 j
+= (hash
[ind
] + seedval
[ind
%seedlen
]);
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];
108 for( ind
= 0; ind
< datasize
; ind
++) {
113 index_j
+= hash
[index_i
];
116 hash
[index_i
] = hash
[index_j
];
119 t
= hash
[index_i
] + hash
[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];
140 ZERO_STRUCT(tmp_md4
);
142 fd
= open(fname
,O_RDONLY
,0);
146 while ((n
= read(fd
, (char *)buf
, sizeof(buf
))) > 0) {
147 mdfour(tmp_md4
, buf
, n
);
149 the_hash
[n
] ^= tmp_md4
[n
];
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
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
;
174 fd
= open( "/dev/urandom", O_RDONLY
,0);
176 smb_set_close_on_exec(fd
);
180 && (read(fd
, seed_inbuf
, sizeof(seed_inbuf
)) == sizeof(seed_inbuf
))) {
181 seed_random_stream(seed_inbuf
, sizeof(seed_inbuf
));
186 /* Add in some secret file contents */
188 do_filehash("/etc/shadow", &seed_inbuf
[0]);
191 * Add the counter, time of day, and pid.
196 v1
= (counter
++) + mypid
+ tval
.tv_sec
;
197 v2
= (counter
++) * mypid
+ tval
.tv_usec
;
199 SIVAL(seed_inbuf
, 32, v1
^ IVAL(seed_inbuf
, 32));
200 SIVAL(seed_inbuf
, 36, v2
^ IVAL(seed_inbuf
, 36));
203 * Add any user-given reseed data.
206 get_rand_reseed_data(&reseed_data
);
209 for (i
= 0; i
< sizeof(seed_inbuf
); i
++)
210 seed_inbuf
[i
] ^= ((char *)(&reseed_data
))[i
% sizeof(reseed_data
)];
213 seed_random_stream(seed_inbuf
, sizeof(seed_inbuf
));
219 Interface to the (hopefully) good crypto random number generator.
220 Will use our internal PRNG if more than 40 bytes of random generation
221 has been requested, otherwise tries to read from /dev/random
223 _PUBLIC_
void generate_random_buffer(uint8_t *out
, int len
)
225 unsigned char md4_buf
[64];
226 unsigned char tmp_buf
[16];
230 bytes_since_reseed
+= len
;
232 /* Magic constant to try and avoid reading 40 bytes
233 * and setting up the PRNG if the app only ever wants
235 if (bytes_since_reseed
< 40) {
236 if (urand_fd
== -1) {
237 urand_fd
= open( "/dev/urandom", O_RDONLY
,0);
238 if (urand_fd
!= -1) {
239 smb_set_close_on_exec(urand_fd
);
242 if(urand_fd
!= -1 && (read(urand_fd
, out
, len
) == len
)) {
247 urand_fd
= do_reseed(true, urand_fd
);
252 * Generate random numbers in chunks of 64 bytes,
253 * then md4 them & copy to the output buffer.
254 * This way the raw state of the stream is never externally
260 int copy_len
= len
> 16 ? 16 : len
;
262 get_random_stream(md4_buf
, sizeof(md4_buf
));
263 mdfour(tmp_buf
, md4_buf
, sizeof(md4_buf
));
264 memcpy(p
, tmp_buf
, copy_len
);
271 Interface to the (hopefully) good crypto random number generator.
272 Will always use /dev/urandom if available.
274 _PUBLIC_
void generate_secret_buffer(uint8_t *out
, int len
)
276 if (urand_fd
== -1) {
277 urand_fd
= open( "/dev/urandom", O_RDONLY
,0);
278 if (urand_fd
!= -1) {
279 smb_set_close_on_exec(urand_fd
);
282 if(urand_fd
!= -1 && (read(urand_fd
, out
, len
) == len
)) {
286 generate_random_buffer(out
, len
);
290 generate a single random uint32_t
292 _PUBLIC_
uint32_t generate_random(void)
295 generate_random_buffer(v
, 4);
301 very basic password quality checker
303 _PUBLIC_
bool check_password_quality(const char *s
)
305 int has_digit
=0, has_capital
=0, has_lower
=0, has_special
=0, has_high
=0;
306 const char* reals
= s
;
308 if (isdigit((unsigned char)*s
)) {
310 } else if (isupper((unsigned char)*s
)) {
312 } else if (islower((unsigned char)*s
)) {
314 } else if (isascii((unsigned char)*s
)) {
322 return ((has_digit
+ has_lower
+ has_capital
+ has_special
) >= 3
323 || (has_high
> strlen(reals
)/2));
327 Use the random number generator to generate a random string.
330 _PUBLIC_
char *generate_random_str_list(TALLOC_CTX
*mem_ctx
, size_t len
, const char *list
)
333 size_t list_len
= strlen(list
);
335 char *retstr
= talloc_array(mem_ctx
, char, len
+ 1);
336 if (!retstr
) return NULL
;
338 generate_random_buffer((uint8_t *)retstr
, len
);
339 for (i
= 0; i
< len
; i
++) {
340 retstr
[i
] = list
[retstr
[i
] % list_len
];
348 * Generate a random text string consisting of the specified length.
349 * The returned string will be allocated.
351 * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
354 _PUBLIC_
char *generate_random_str(TALLOC_CTX
*mem_ctx
, size_t len
)
357 const char *c_list
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
360 retstr
= generate_random_str_list(mem_ctx
, len
, c_list
);
361 if (!retstr
) return NULL
;
363 /* we need to make sure the random string passes basic quality tests
364 or it might be rejected by windows as a password */
365 if (len
>= 7 && !check_password_quality(retstr
)) {
374 * Generate a random text password.
377 _PUBLIC_
char *generate_random_password(TALLOC_CTX
*mem_ctx
, size_t min
, size_t max
)
380 /* This list does not include { or } because they cause
381 * problems for our provision (it can create a substring
382 * ${...}, and for Fedora DS (which treats {...} at the start
383 * of a stored password as special
384 * -- Andrew Bartlett 2010-03-11
386 const char *c_list
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,@$%&!?:;<=>()[]~";
400 generate_random_buffer((uint8_t *)&tmp
, sizeof(tmp
));
408 retstr
= generate_random_str_list(mem_ctx
, len
, c_list
);
409 if (!retstr
) return NULL
;
411 /* we need to make sure the random string passes basic quality tests
412 or it might be rejected by windows as a password */
413 if (len
>= 7 && !check_password_quality(retstr
)) {
422 * Generate an array of unique text strings all of the same length.
423 * The returned string will be allocated.
424 * Returns NULL if the number of unique combinations cannot be created.
426 * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
428 _PUBLIC_
char** generate_unique_strs(TALLOC_CTX
*mem_ctx
, size_t len
,
431 const char *c_list
= "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
432 const unsigned c_size
= 42;
437 if (num
== 0 || len
== 0)
440 strs
= talloc_array(mem_ctx
, char *, num
);
441 if (strs
== NULL
) return NULL
;
443 for (i
= 0; i
< num
; i
++) {
444 char *retstr
= (char *)talloc_size(strs
, len
+ 1);
445 if (retstr
== NULL
) {
450 for (j
= 0; j
< len
; j
++) {
451 retstr
[j
] = c_list
[rem
% c_size
];
457 /* we were not able to fit the number of
458 * combinations asked for in the length
460 DEBUG(0,(__location__
": Too many combinations %u for length %u\n",
461 num
, (unsigned)len
));