fbd99989ac2af9baf2bd065f97d84d23c655747c
[nivanova/samba-autobuild/.git] / lib / util / genrand_util.c
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/locale.h"
24
25 /**
26  * @file
27  * @brief Random number generation
28  */
29
30 /**
31   generate a single random uint32_t
32 **/
33 _PUBLIC_ uint32_t generate_random(void)
34 {
35         uint8_t v[4];
36         generate_random_buffer(v, 4);
37         return IVAL(v, 0);
38 }
39
40
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:
45
46   Passwords must contain characters from three of the following five
47   categories:
48
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.
58
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)
63 {
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;
72
73         if (pwd == NULL) {
74                 return false;
75         }
76
77         while (true) {
78                 const char *s = &pwd[ofs];
79                 size_t len = 0;
80                 codepoint_t c;
81
82                 c = next_codepoint(s, &len);
83                 if (c == INVALID_CODEPOINT) {
84                         return false;
85                 } else if (c == 0) {
86                         break;
87                 }
88                 ofs += len;
89                 num_chars += 1;
90
91                 if (len == 1) {
92                         const char *na = "~!@#$%^&*_-+=`|\\(){}[]:;\"'<>,.?/";
93
94                         if (isdigit(c)) {
95                                 num_digits += 1;
96                                 continue;
97                         }
98
99                         if (isupper(c)) {
100                                 num_upper += 1;
101                                 continue;
102                         }
103
104                         if (islower(c)) {
105                                 num_lower += 1;
106                                 continue;
107                         }
108
109                         if (strchr(na, c)) {
110                                 num_nonalpha += 1;
111                                 continue;
112                         }
113
114                         /*
115                          * the rest does not belong to
116                          * a category.
117                          */
118                         continue;
119                 }
120
121                 if (isupper_m(c)) {
122                         num_upper += 1;
123                         continue;
124                 }
125
126                 if (islower_m(c)) {
127                         num_lower += 1;
128                         continue;
129                 }
130
131                 /*
132                  * Note: for now do not check if the unicode category is
133                  *       alphabetic character
134                  *
135                  * We would have to import the details from
136                  * ftp://ftp.unicode.org/Public/6.3.0/ucd/UnicodeData-6.3.0d1.txt
137                  */
138                 num_unicode += 1;
139                 continue;
140         }
141
142         if (num_digits > 0) {
143                 num_categories += 1;
144         }
145         if (num_upper > 0) {
146                 num_categories += 1;
147         }
148         if (num_lower > 0) {
149                 num_categories += 1;
150         }
151         if (num_nonalpha > 0) {
152                 num_categories += 1;
153         }
154         if (num_unicode > 0) {
155                 num_categories += 1;
156         }
157
158         if (num_categories >= 3) {
159                 return true;
160         }
161
162         return false;
163 }
164
165 /**
166  Use the random number generator to generate a random string.
167 **/
168
169 _PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
170 {
171         size_t i;
172         size_t list_len = strlen(list);
173
174         char *retstr = talloc_array(mem_ctx, char, len + 1);
175         if (!retstr) return NULL;
176
177         generate_random_buffer((uint8_t *)retstr, len);
178         for (i = 0; i < len; i++) {
179                 retstr[i] = list[retstr[i] % list_len];
180         }
181         retstr[i] = '\0';
182
183         return retstr;
184 }
185
186 /**
187  * Generate a random text string consisting of the specified length.
188  * The returned string will be allocated.
189  *
190  * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
191  */
192
193 _PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
194 {
195         char *retstr;
196         const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
197
198 again:
199         retstr = generate_random_str_list(mem_ctx, len, c_list);
200         if (!retstr) return NULL;
201
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;
207         }
208
209         return retstr;
210 }
211
212 /**
213  * Generate a random text password.
214  */
215
216 _PUBLIC_ char *generate_random_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
217 {
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
224          */
225         const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,@$%&!?:;<=>()[]~";
226         size_t len = max;
227         size_t diff;
228
229         if (min > max) {
230                 errno = EINVAL;
231                 return NULL;
232         }
233
234         diff = max - min;
235
236         if (diff > 0 ) {
237                 size_t tmp;
238
239                 generate_random_buffer((uint8_t *)&tmp, sizeof(tmp));
240
241                 tmp %= diff;
242
243                 len = min + tmp;
244         }
245
246 again:
247         retstr = generate_random_str_list(mem_ctx, len, c_list);
248         if (!retstr) return NULL;
249
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;
255         }
256
257         return retstr;
258 }
259
260 /**
261  * Generate an array of unique text strings all of the same length.
262  * The returned string will be allocated.
263  * Returns NULL if the number of unique combinations cannot be created.
264  *
265  * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
266  */
267 _PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len,
268                                      uint32_t num)
269 {
270         const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
271         const unsigned c_size = 42;
272         size_t i, j;
273         unsigned rem;
274         char ** strs = NULL;
275
276         if (num == 0 || len == 0)
277                 return NULL;
278
279         strs = talloc_array(mem_ctx, char *, num);
280         if (strs == NULL) return NULL;
281
282         for (i = 0; i < num; i++) {
283                 char *retstr = (char *)talloc_size(strs, len + 1);
284                 if (retstr == NULL) {
285                         talloc_free(strs);
286                         return NULL;
287                 }
288                 rem = i;
289                 for (j = 0; j < len; j++) {
290                         retstr[j] = c_list[rem % c_size];
291                         rem = rem / c_size;
292                 }
293                 retstr[j] = 0;
294                 strs[i] = retstr;
295                 if (rem != 0) {
296                         /* we were not able to fit the number of
297                          * combinations asked for in the length
298                          * specified */
299                         DEBUG(0,(__location__ ": Too many combinations %u for length %u\n",
300                                  num, (unsigned)len));
301
302                         talloc_free(strs);
303                         return NULL;
304                 }
305         }
306
307         return strs;
308 }