lib/util: improve check_password_quality() to handle utf8
[Samba/gebeck_regimport.git] / lib / util / genrand.c
blob3dfaf089d16c4afdf25ead27391ce3835ba4b082
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);
175 if (fd != -1) {
176 smb_set_close_on_exec(fd);
179 if (fd != -1
180 && (read(fd, seed_inbuf, sizeof(seed_inbuf)) == sizeof(seed_inbuf))) {
181 seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
182 return fd;
186 /* Add in some secret file contents */
188 do_filehash("/etc/shadow", &seed_inbuf[0]);
191 * Add the counter, time of day, and pid.
194 GetTimeOfDay(&tval);
195 mypid = getpid();
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);
207 if (reseed_data) {
208 size_t i;
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));
215 return -1;
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];
227 unsigned char *p;
229 if(!done_reseed) {
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
234 * a few bytes */
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)) {
243 return;
247 urand_fd = do_reseed(true, urand_fd);
248 done_reseed = true;
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
255 * seen.
258 p = out;
259 while(len > 0) {
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);
265 p += copy_len;
266 len -= 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)) {
283 return;
286 generate_random_buffer(out, len);
290 generate a single random uint32_t
292 _PUBLIC_ uint32_t generate_random(void)
294 uint8_t v[4];
295 generate_random_buffer(v, 4);
296 return IVAL(v, 0);
301 Microsoft composed the following rules (among others) for quality
302 checks. This is an abridgment from
303 http://msdn.microsoft.com/en-us/subscriptions/cc786468%28v=ws.10%29.aspx:
305 Passwords must contain characters from three of the following five
306 categories:
308 - Uppercase characters of European languages (A through Z, with
309 diacritic marks, Greek and Cyrillic characters)
310 - Lowercase characters of European languages (a through z, sharp-s,
311 with diacritic marks, Greek and Cyrillic characters)
312 - Base 10 digits (0 through 9)
313 - Nonalphanumeric characters: ~!@#$%^&*_-+=`|\(){}[]:;"'<>,.?/
314 - Any Unicode character that is categorized as an alphabetic character
315 but is not uppercase or lowercase. This includes Unicode characters
316 from Asian languages.
318 Note: for now do not check if the unicode category is
319 alphabetic character
321 _PUBLIC_ bool check_password_quality(const char *pwd)
323 size_t ofs = 0;
324 size_t num_chars = 0;
325 size_t num_digits = 0;
326 size_t num_upper = 0;
327 size_t num_lower = 0;
328 size_t num_nonalpha = 0;
329 size_t num_unicode = 0;
330 size_t num_categories = 0;
332 if (pwd == NULL) {
333 return false;
336 while (true) {
337 const char *s = &pwd[ofs];
338 size_t len = 0;
339 codepoint_t c;
341 c = next_codepoint(s, &len);
342 if (c == INVALID_CODEPOINT) {
343 return false;
344 } else if (c == 0) {
345 break;
347 ofs += len;
348 num_chars += 1;
350 if (len == 1) {
351 const char *na = "~!@#$%^&*_-+=`|\\(){}[]:;\"'<>,.?/";
353 if (isdigit(c)) {
354 num_digits += 1;
355 continue;
358 if (isupper(c)) {
359 num_upper += 1;
360 continue;
363 if (islower(c)) {
364 num_lower += 1;
365 continue;
368 if (strchr(na, c)) {
369 num_nonalpha += 1;
370 continue;
374 * the rest does not belong to
375 * a category.
377 continue;
380 if (isupper_m(c)) {
381 num_upper += 1;
382 continue;
385 if (islower_m(c)) {
386 num_lower += 1;
387 continue;
391 * Note: for now do not check if the unicode category is
392 * alphabetic character
394 * We would have to import the details from
395 * ftp://ftp.unicode.org/Public/6.3.0/ucd/UnicodeData-6.3.0d1.txt
397 num_unicode += 1;
398 continue;
401 if (num_digits > 0) {
402 num_categories += 1;
404 if (num_upper > 0) {
405 num_categories += 1;
407 if (num_lower > 0) {
408 num_categories += 1;
410 if (num_nonalpha > 0) {
411 num_categories += 1;
413 if (num_unicode > 0) {
414 num_categories += 1;
417 if (num_categories >= 3) {
418 return true;
421 return false;
425 Use the random number generator to generate a random string.
428 _PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
430 size_t i;
431 size_t list_len = strlen(list);
433 char *retstr = talloc_array(mem_ctx, char, len + 1);
434 if (!retstr) return NULL;
436 generate_random_buffer((uint8_t *)retstr, len);
437 for (i = 0; i < len; i++) {
438 retstr[i] = list[retstr[i] % list_len];
440 retstr[i] = '\0';
442 return retstr;
446 * Generate a random text string consisting of the specified length.
447 * The returned string will be allocated.
449 * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
452 _PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
454 char *retstr;
455 const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
457 again:
458 retstr = generate_random_str_list(mem_ctx, len, c_list);
459 if (!retstr) return NULL;
461 /* we need to make sure the random string passes basic quality tests
462 or it might be rejected by windows as a password */
463 if (len >= 7 && !check_password_quality(retstr)) {
464 talloc_free(retstr);
465 goto again;
468 return retstr;
472 * Generate a random text password.
475 _PUBLIC_ char *generate_random_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
477 char *retstr;
478 /* This list does not include { or } because they cause
479 * problems for our provision (it can create a substring
480 * ${...}, and for Fedora DS (which treats {...} at the start
481 * of a stored password as special
482 * -- Andrew Bartlett 2010-03-11
484 const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,@$%&!?:;<=>()[]~";
485 size_t len = max;
486 size_t diff;
488 if (min > max) {
489 errno = EINVAL;
490 return NULL;
493 diff = max - min;
495 if (diff > 0 ) {
496 size_t tmp;
498 generate_random_buffer((uint8_t *)&tmp, sizeof(tmp));
500 tmp %= diff;
502 len = min + tmp;
505 again:
506 retstr = generate_random_str_list(mem_ctx, len, c_list);
507 if (!retstr) return NULL;
509 /* we need to make sure the random string passes basic quality tests
510 or it might be rejected by windows as a password */
511 if (len >= 7 && !check_password_quality(retstr)) {
512 talloc_free(retstr);
513 goto again;
516 return retstr;
520 * Generate an array of unique text strings all of the same length.
521 * The returned string will be allocated.
522 * Returns NULL if the number of unique combinations cannot be created.
524 * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
526 _PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len,
527 uint32_t num)
529 const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
530 const unsigned c_size = 42;
531 int i, j;
532 unsigned rem;
533 char ** strs = NULL;
535 if (num == 0 || len == 0)
536 return NULL;
538 strs = talloc_array(mem_ctx, char *, num);
539 if (strs == NULL) return NULL;
541 for (i = 0; i < num; i++) {
542 char *retstr = (char *)talloc_size(strs, len + 1);
543 if (retstr == NULL) {
544 talloc_free(strs);
545 return NULL;
547 rem = i;
548 for (j = 0; j < len; j++) {
549 retstr[j] = c_list[rem % c_size];
550 rem = rem / c_size;
552 retstr[j] = 0;
553 strs[i] = retstr;
554 if (rem != 0) {
555 /* we were not able to fit the number of
556 * combinations asked for in the length
557 * specified */
558 DEBUG(0,(__location__ ": Too many combinations %u for length %u\n",
559 num, (unsigned)len));
561 talloc_free(strs);
562 return NULL;
566 return strs;