ldb: version 1.5.2
[Samba.git] / lib / util / genrand_util.c
blob76b7cd918908bab93ee54298f029f55d49f4069a
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/locale.h"
25 /**
26 * @file
27 * @brief Random number generation
30 /**
31 generate a single random uint32_t
32 **/
33 _PUBLIC_ uint32_t generate_random(void)
35 uint8_t v[4];
36 generate_random_buffer(v, 4);
37 return IVAL(v, 0);
41 /**
42 Microsoft composed the following rules (among others) for quality
43 checks. This is an abridgment from
44 http://msdn.microsoft.com/en-us/subscriptions/cc786468%28v=ws.10%29.aspx:
46 Passwords must contain characters from three of the following five
47 categories:
49 - Uppercase characters of European languages (A through Z, with
50 diacritic marks, Greek and Cyrillic characters)
51 - Lowercase characters of European languages (a through z, sharp-s,
52 with diacritic marks, Greek and Cyrillic characters)
53 - Base 10 digits (0 through 9)
54 - Nonalphanumeric characters: ~!@#$%^&*_-+=`|\(){}[]:;"'<>,.?/
55 - Any Unicode character that is categorized as an alphabetic character
56 but is not uppercase or lowercase. This includes Unicode characters
57 from Asian languages.
59 Note: for now do not check if the unicode category is
60 alphabetic character
61 **/
62 _PUBLIC_ bool check_password_quality(const char *pwd)
64 size_t ofs = 0;
65 size_t num_chars = 0;
66 size_t num_digits = 0;
67 size_t num_upper = 0;
68 size_t num_lower = 0;
69 size_t num_nonalpha = 0;
70 size_t num_unicode = 0;
71 size_t num_categories = 0;
73 if (pwd == NULL) {
74 return false;
77 while (true) {
78 const char *s = &pwd[ofs];
79 size_t len = 0;
80 codepoint_t c;
82 c = next_codepoint(s, &len);
83 if (c == INVALID_CODEPOINT) {
84 return false;
85 } else if (c == 0) {
86 break;
88 ofs += len;
89 num_chars += 1;
91 if (len == 1) {
92 const char *na = "~!@#$%^&*_-+=`|\\(){}[]:;\"'<>,.?/";
94 if (isdigit(c)) {
95 num_digits += 1;
96 continue;
99 if (isupper(c)) {
100 num_upper += 1;
101 continue;
104 if (islower(c)) {
105 num_lower += 1;
106 continue;
109 if (strchr(na, c)) {
110 num_nonalpha += 1;
111 continue;
115 * the rest does not belong to
116 * a category.
118 continue;
121 if (isupper_m(c)) {
122 num_upper += 1;
123 continue;
126 if (islower_m(c)) {
127 num_lower += 1;
128 continue;
132 * Note: for now do not check if the unicode category is
133 * alphabetic character
135 * We would have to import the details from
136 * ftp://ftp.unicode.org/Public/6.3.0/ucd/UnicodeData-6.3.0d1.txt
138 num_unicode += 1;
139 continue;
142 if (num_digits > 0) {
143 num_categories += 1;
145 if (num_upper > 0) {
146 num_categories += 1;
148 if (num_lower > 0) {
149 num_categories += 1;
151 if (num_nonalpha > 0) {
152 num_categories += 1;
154 if (num_unicode > 0) {
155 num_categories += 1;
158 if (num_categories >= 3) {
159 return true;
162 return false;
166 Use the random number generator to generate a random string.
169 _PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
171 size_t i;
172 size_t list_len = strlen(list);
174 char *retstr = talloc_array(mem_ctx, char, len + 1);
175 if (!retstr) return NULL;
177 generate_random_buffer((uint8_t *)retstr, len);
178 for (i = 0; i < len; i++) {
179 retstr[i] = list[retstr[i] % list_len];
181 retstr[i] = '\0';
183 return retstr;
187 * Generate a random text string consisting of the specified length.
188 * The returned string will be allocated.
190 * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
193 _PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
195 char *retstr;
196 const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
198 again:
199 retstr = generate_random_str_list(mem_ctx, len, c_list);
200 if (!retstr) return NULL;
202 /* we need to make sure the random string passes basic quality tests
203 or it might be rejected by windows as a password */
204 if (len >= 7 && !check_password_quality(retstr)) {
205 talloc_free(retstr);
206 goto again;
209 return retstr;
213 * Generate a random text password (based on printable ascii characters).
216 _PUBLIC_ char *generate_random_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
218 char *retstr;
219 /* This list does not include { or } because they cause
220 * problems for our provision (it can create a substring
221 * ${...}, and for Fedora DS (which treats {...} at the start
222 * of a stored password as special
223 * -- Andrew Bartlett 2010-03-11
225 const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,@$%&!?:;<=>()[]~";
226 size_t len = max;
227 size_t diff;
229 if (min > max) {
230 errno = EINVAL;
231 return NULL;
234 diff = max - min;
236 if (diff > 0 ) {
237 size_t tmp;
239 generate_random_buffer((uint8_t *)&tmp, sizeof(tmp));
241 tmp %= diff;
243 len = min + tmp;
246 again:
247 retstr = generate_random_str_list(mem_ctx, len, c_list);
248 if (!retstr) return NULL;
250 /* we need to make sure the random string passes basic quality tests
251 or it might be rejected by windows as a password */
252 if (len >= 7 && !check_password_quality(retstr)) {
253 talloc_free(retstr);
254 goto again;
257 return retstr;
261 * Generate a random machine password (based on random utf16 characters,
262 * converted to utf8). min must be at least 14, max must be at most 255.
264 * If 'unix charset' is not utf8, the password consist of random ascii
265 * values!
268 _PUBLIC_ char *generate_random_machine_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
270 TALLOC_CTX *frame = NULL;
271 struct generate_random_machine_password_state {
272 uint8_t password_buffer[256 * 2];
273 uint8_t tmp;
274 } *state;
275 char *new_pw = NULL;
276 size_t len = max;
277 char *utf8_pw = NULL;
278 size_t utf8_len = 0;
279 char *unix_pw = NULL;
280 size_t unix_len = 0;
281 size_t diff;
282 size_t i;
283 bool ok;
284 int cmp;
286 if (max > 255) {
287 errno = EINVAL;
288 return NULL;
291 if (min < 14) {
292 errno = EINVAL;
293 return NULL;
296 if (min > max) {
297 errno = EINVAL;
298 return NULL;
301 frame = talloc_stackframe_pool(2048);
302 state = talloc_zero(frame, struct generate_random_machine_password_state);
304 diff = max - min;
306 if (diff > 0) {
307 size_t tmp;
309 generate_random_buffer((uint8_t *)&tmp, sizeof(tmp));
311 tmp %= diff;
313 len = min + tmp;
317 * Create a random machine account password
318 * We create a random buffer and convert that to utf8.
319 * This is similar to what windows is doing.
321 * In future we may store the raw random buffer,
322 * but for now we need to pass the password as
323 * char pointer through some layers.
325 * As most kerberos keys are derived from the
326 * utf8 password we need to fallback to
327 * ASCII passwords if "unix charset" is not utf8.
329 generate_secret_buffer(state->password_buffer, len * 2);
330 for (i = 0; i < len; i++) {
331 size_t idx = i*2;
332 uint16_t c;
335 * both MIT krb5 and HEIMDAL only
336 * handle codepoints up to 0xffff.
338 * It means we need to avoid
339 * 0xD800 - 0xDBFF (high surrogate)
340 * and
341 * 0xDC00 - 0xDFFF (low surrogate)
342 * in the random utf16 data.
344 * 55296 0xD800 0154000 0b1101100000000000
345 * 57343 0xDFFF 0157777 0b1101111111111111
346 * 8192 0x2000 020000 0b10000000000000
348 * The above values show that we can check
349 * for 0xD800 and just add 0x2000 to avoid
350 * the surrogate ranges.
352 * The rest will be handled by CH_UTF16MUNGED
353 * see utf16_munged_pull().
355 c = SVAL(state->password_buffer, idx);
356 if (c & 0xD800) {
357 c |= 0x2000;
359 SSVAL(state->password_buffer, idx, c);
361 ok = convert_string_talloc(frame,
362 CH_UTF16MUNGED, CH_UTF8,
363 state->password_buffer, len * 2,
364 (void *)&utf8_pw, &utf8_len);
365 if (!ok) {
366 DEBUG(0, ("%s: convert_string_talloc() failed\n",
367 __func__));
368 TALLOC_FREE(frame);
369 return NULL;
372 ok = convert_string_talloc(frame,
373 CH_UTF16MUNGED, CH_UNIX,
374 state->password_buffer, len * 2,
375 (void *)&unix_pw, &unix_len);
376 if (!ok) {
377 goto ascii_fallback;
380 if (utf8_len != unix_len) {
381 goto ascii_fallback;
384 cmp = memcmp((const uint8_t *)utf8_pw,
385 (const uint8_t *)unix_pw,
386 utf8_len);
387 if (cmp != 0) {
388 goto ascii_fallback;
391 new_pw = talloc_strdup(mem_ctx, utf8_pw);
392 if (new_pw == NULL) {
393 TALLOC_FREE(frame);
394 return NULL;
396 talloc_set_name_const(new_pw, __func__);
397 TALLOC_FREE(frame);
398 return new_pw;
400 ascii_fallback:
401 for (i = 0; i < len; i++) {
403 * truncate to ascii
405 state->tmp = state->password_buffer[i] & 0x7f;
406 if (state->tmp == 0) {
407 state->tmp = state->password_buffer[i] >> 1;
409 if (state->tmp == 0) {
410 state->tmp = 0x01;
412 state->password_buffer[i] = state->tmp;
414 state->password_buffer[i] = '\0';
416 new_pw = talloc_strdup(mem_ctx, (const char *)state->password_buffer);
417 if (new_pw == NULL) {
418 TALLOC_FREE(frame);
419 return NULL;
421 talloc_set_name_const(new_pw, __func__);
422 TALLOC_FREE(frame);
423 return new_pw;
427 * Generate an array of unique text strings all of the same length.
428 * The returned string will be allocated.
429 * Returns NULL if the number of unique combinations cannot be created.
431 * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
433 _PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len,
434 uint32_t num)
436 const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
437 const unsigned c_size = 42;
438 size_t i, j;
439 unsigned rem;
440 char ** strs = NULL;
442 if (num == 0 || len == 0)
443 return NULL;
445 strs = talloc_array(mem_ctx, char *, num);
446 if (strs == NULL) return NULL;
448 for (i = 0; i < num; i++) {
449 char *retstr = (char *)talloc_size(strs, len + 1);
450 if (retstr == NULL) {
451 talloc_free(strs);
452 return NULL;
454 rem = i;
455 for (j = 0; j < len; j++) {
456 retstr[j] = c_list[rem % c_size];
457 rem = rem / c_size;
459 retstr[j] = 0;
460 strs[i] = retstr;
461 if (rem != 0) {
462 /* we were not able to fit the number of
463 * combinations asked for in the length
464 * specified */
465 DEBUG(0,(__location__ ": Too many combinations %u for length %u\n",
466 num, (unsigned)len));
468 talloc_free(strs);
469 return NULL;
473 return strs;