Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/util/genrand_util.c
Line
Count
Source
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 "replace.h"
23
#include "system/locale.h"
24
#include <tevent.h>
25
#include "lib/util/samba_util.h"
26
#include "lib/util/debug.h"
27
28
/**
29
 * @file
30
 * @brief Random number generation
31
 */
32
33
/**
34
  generate a single random uint32_t
35
**/
36
_PUBLIC_ uint32_t generate_random(void)
37
0
{
38
0
  uint8_t v[4];
39
0
  generate_random_buffer(v, 4);
40
0
  return IVAL(v, 0);
41
0
}
42
43
/**
44
  @brief generate a random uint64
45
**/
46
_PUBLIC_ uint64_t generate_random_u64(void)
47
0
{
48
0
  uint8_t v[8];
49
0
  generate_random_buffer(v, 8);
50
0
  return BVAL(v, 0);
51
0
}
52
53
/**
54
 * @brief Generate a random number in the given range.
55
 *
56
 * @param lower    The lower value of the range
57
58
 * @param upper    The upper value of the range
59
 *
60
 * @return A random number bigger than than lower and smaller than upper.
61
 */
62
_PUBLIC_ uint64_t generate_random_u64_range(uint64_t lower, uint64_t upper)
63
0
{
64
0
  return generate_random_u64() % (upper - lower) + lower;
65
0
}
66
67
_PUBLIC_ uint64_t generate_unique_u64(uint64_t veto_value)
68
0
{
69
0
  static struct generate_unique_u64_state {
70
0
    uint64_t next_value;
71
0
    int pid;
72
0
  } generate_unique_u64_state;
73
74
0
  int pid = tevent_cached_getpid();
75
76
0
  if (unlikely(pid != generate_unique_u64_state.pid)) {
77
0
    generate_unique_u64_state = (struct generate_unique_u64_state) {
78
0
      .pid = pid,
79
0
      .next_value = veto_value,
80
0
    };
81
0
  }
82
83
0
  generate_unique_u64_state.next_value++;
84
85
0
  while (unlikely(generate_unique_u64_state.next_value == veto_value)) {
86
0
    generate_nonce_buffer(
87
0
        (void *)&generate_unique_u64_state.next_value,
88
0
        sizeof(generate_unique_u64_state.next_value));
89
0
  }
90
91
0
  return generate_unique_u64_state.next_value;
92
0
}
93
94
/**
95
  Microsoft composed the following rules (among others) for quality
96
  checks. This is an abridgment from
97
  http://msdn.microsoft.com/en-us/subscriptions/cc786468%28v=ws.10%29.aspx:
98
99
  Passwords must contain characters from three of the following five
100
  categories:
101
102
   - Uppercase characters of European languages (A through Z, with
103
     diacritic marks, Greek and Cyrillic characters)
104
   - Lowercase characters of European languages (a through z, sharp-s,
105
     with diacritic marks, Greek and Cyrillic characters)
106
   - Base 10 digits (0 through 9)
107
   - Nonalphanumeric characters: ~!@#$%^&*_-+=`|\(){}[]:;"'<>,.?/
108
   - Any Unicode character that is categorized as an alphabetic character
109
     but is not uppercase or lowercase. This includes Unicode characters
110
     from Asian languages.
111
112
 Note: for now do not check if the unicode category is
113
       alphabetic character
114
**/
115
_PUBLIC_ bool check_password_quality(const char *pwd)
116
0
{
117
0
  size_t ofs = 0;
118
0
  size_t num_digits = 0;
119
0
  size_t num_upper = 0;
120
0
  size_t num_lower = 0;
121
0
  size_t num_nonalpha = 0;
122
0
  size_t num_unicode = 0;
123
0
  size_t num_categories = 0;
124
125
0
  if (pwd == NULL) {
126
0
    return false;
127
0
  }
128
129
0
  while (true) {
130
0
    const char *s = &pwd[ofs];
131
0
    size_t len = 0;
132
0
    codepoint_t c;
133
134
0
    c = next_codepoint(s, &len);
135
0
    if (c == INVALID_CODEPOINT) {
136
0
      return false;
137
0
    } else if (c == 0) {
138
0
      break;
139
0
    }
140
0
    ofs += len;
141
142
0
    if (len == 1) {
143
0
      const char *na = "~!@#$%^&*_-+=`|\\(){}[]:;\"'<>,.?/";
144
145
0
      if (isdigit(c)) {
146
0
        num_digits += 1;
147
0
        continue;
148
0
      }
149
150
0
      if (isupper(c)) {
151
0
        num_upper += 1;
152
0
        continue;
153
0
      }
154
155
0
      if (islower(c)) {
156
0
        num_lower += 1;
157
0
        continue;
158
0
      }
159
160
0
      if (strchr(na, c)) {
161
0
        num_nonalpha += 1;
162
0
        continue;
163
0
      }
164
165
      /*
166
       * the rest does not belong to
167
       * a category.
168
       */
169
0
      continue;
170
0
    }
171
172
0
    if (isupper_m(c)) {
173
0
      num_upper += 1;
174
0
      continue;
175
0
    }
176
177
0
    if (islower_m(c)) {
178
0
      num_lower += 1;
179
0
      continue;
180
0
    }
181
182
    /*
183
     * Note: for now do not check if the unicode category is
184
     *       alphabetic character
185
     *
186
     * We would have to import the details from
187
     * ftp://ftp.unicode.org/Public/6.3.0/ucd/UnicodeData-6.3.0d1.txt
188
     */
189
0
    num_unicode += 1;
190
0
    continue;
191
0
  }
192
193
0
  if (num_digits > 0) {
194
0
    num_categories += 1;
195
0
  }
196
0
  if (num_upper > 0) {
197
0
    num_categories += 1;
198
0
  }
199
0
  if (num_lower > 0) {
200
0
    num_categories += 1;
201
0
  }
202
0
  if (num_nonalpha > 0) {
203
0
    num_categories += 1;
204
0
  }
205
0
  if (num_unicode > 0) {
206
0
    num_categories += 1;
207
0
  }
208
209
0
  if (num_categories >= 3) {
210
0
    return true;
211
0
  }
212
213
0
  return false;
214
0
}
215
216
_PUBLIC_ char *generate_random_str_list_buf(char *buf,
217
              size_t buflen,
218
              const char *list)
219
0
{
220
0
  const size_t list_len = strlen(list);
221
0
  size_t i, len;
222
223
0
  if (buflen == 0) {
224
0
    return buf;
225
0
  }
226
0
  buf[buflen-1] = '\0';
227
228
0
  if (buflen == 1) {
229
0
    return buf;
230
0
  }
231
232
0
  len = buflen-1;
233
0
  generate_secret_buffer((uint8_t *)buf, len);
234
235
0
  for (i=0; i<len; i++) {
236
0
    buf[i] = list[(uint8_t)buf[i] % list_len];
237
0
  }
238
239
0
  return buf;
240
0
}
241
242
/**
243
 Use the random number generator to generate a random string.
244
**/
245
246
_PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
247
0
{
248
0
  char *retstr = talloc_array(mem_ctx, char, len + 1);
249
0
  if (!retstr) return NULL;
250
251
0
  return generate_random_str_list_buf(retstr, len+1, list);
252
0
}
253
254
/**
255
 * Generate a random text string consisting of the specified length.
256
 * The returned string will be allocated.
257
 *
258
 * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
259
 */
260
261
_PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
262
0
{
263
0
  char *retstr;
264
0
  const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
265
266
0
again:
267
0
  retstr = generate_random_str_list(mem_ctx, len, c_list);
268
0
  if (!retstr) return NULL;
269
270
  /* we need to make sure the random string passes basic quality tests
271
     or it might be rejected by windows as a password */
272
0
  if (len >= 7 && !check_password_quality(retstr)) {
273
0
    talloc_free(retstr);
274
0
    goto again;
275
0
  }
276
277
0
  return retstr;
278
0
}
279
280
/**
281
 * Generate a random text password (based on printable ascii characters).
282
 */
283
284
_PUBLIC_ char *generate_random_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
285
0
{
286
0
  char *retstr;
287
  /* This list does not include { or } because they cause
288
   * problems for our provision (it can create a substring
289
   * ${...}, and for Fedora DS (which treats {...} at the start
290
   * of a stored password as special
291
   *  -- Andrew Bartlett 2010-03-11
292
   */
293
0
  const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,@$%&!?:;<=>()[]~";
294
0
  size_t len = max;
295
0
  size_t diff;
296
297
0
  if (min > max) {
298
0
    errno = EINVAL;
299
0
    return NULL;
300
0
  }
301
302
0
  diff = max - min;
303
304
0
  if (diff > 0 ) {
305
0
    size_t tmp;
306
307
0
    generate_secret_buffer((uint8_t *)&tmp, sizeof(tmp));
308
309
0
    tmp %= diff;
310
311
0
    len = min + tmp;
312
0
  }
313
314
0
again:
315
0
  retstr = generate_random_str_list(mem_ctx, len, c_list);
316
0
  if (!retstr) return NULL;
317
318
  /* we need to make sure the random string passes basic quality tests
319
     or it might be rejected by windows as a password */
320
0
  if (len >= 7 && !check_password_quality(retstr)) {
321
0
    talloc_free(retstr);
322
0
    goto again;
323
0
  }
324
325
0
  return retstr;
326
0
}
327
328
/**
329
 * Generate a random machine password (based on random utf16 characters,
330
 * converted to utf8). min must be at least 14, max must be at most 255.
331
 *
332
 * If 'unix charset' is not utf8, the password consist of random ascii
333
 * values!
334
 *
335
 * The return value is a talloc string with destructor talloc_keep_secret() set.
336
 * The content will be overwritten by zeros when the mem_ctx is destroyed.
337
 */
338
339
_PUBLIC_ char *generate_random_machine_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
340
0
{
341
0
  TALLOC_CTX *frame = NULL;
342
0
  struct generate_random_machine_password_state {
343
0
    uint8_t password_buffer[256 * 2];
344
0
    uint8_t tmp;
345
0
  } *state;
346
0
  char *new_pw = NULL;
347
0
  size_t len = max;
348
0
  char *utf8_pw = NULL;
349
0
  size_t utf8_len = 0;
350
0
  char *unix_pw = NULL;
351
0
  size_t unix_len = 0;
352
0
  size_t diff;
353
0
  size_t i;
354
0
  bool ok;
355
0
  int cmp;
356
357
0
  if (max > 255) {
358
0
    errno = EINVAL;
359
0
    return NULL;
360
0
  }
361
362
0
  if (min < 14) {
363
0
    errno = EINVAL;
364
0
    return NULL;
365
0
  }
366
367
0
  if (min > max) {
368
0
    errno = EINVAL;
369
0
    return NULL;
370
0
  }
371
372
0
  frame = talloc_stackframe_pool(2048);
373
0
  state = talloc_zero(frame, struct generate_random_machine_password_state);
374
0
  talloc_keep_secret(state);
375
376
0
  diff = max - min;
377
378
0
  if (diff > 0) {
379
0
    size_t tmp;
380
381
0
    generate_secret_buffer((uint8_t *)&tmp, sizeof(tmp));
382
383
0
    tmp %= diff;
384
385
0
    len = min + tmp;
386
0
  }
387
388
  /*
389
   * Create a random machine account password
390
   * We create a random buffer and convert that to utf8.
391
   * This is similar to what windows is doing.
392
   *
393
   * In future we may store the raw random buffer,
394
   * but for now we need to pass the password as
395
   * char pointer through some layers.
396
   *
397
   * As most kerberos keys are derived from the
398
   * utf8 password we need to fallback to
399
   * ASCII passwords if "unix charset" is not utf8.
400
   */
401
0
  generate_secret_buffer(state->password_buffer, len * 2);
402
0
  for (i = 0; i < len; i++) {
403
0
    size_t idx = i*2;
404
0
    uint16_t c;
405
406
    /*
407
     * both MIT krb5 and HEIMDAL only
408
     * handle codepoints up to 0xffff.
409
     *
410
     * It means we need to avoid
411
     * 0xD800 - 0xDBFF (high surrogate)
412
     * and
413
     * 0xDC00 - 0xDFFF (low surrogate)
414
     * in the random utf16 data.
415
     *
416
     * 55296 0xD800 0154000 0b1101100000000000
417
     * 57343 0xDFFF 0157777 0b1101111111111111
418
     * 8192  0x2000  020000   0b10000000000000
419
     *
420
     * The above values show that we can check
421
     * for 0xD800 and just add 0x2000 to avoid
422
     * the surrogate ranges.
423
     *
424
     * The rest will be handled by CH_UTF16MUNGED
425
     * see utf16_munged_pull().
426
     */
427
0
    c = SVAL(state->password_buffer, idx);
428
0
    if (c & 0xD800) {
429
0
      c |= 0x2000;
430
0
    }
431
0
    SSVAL(state->password_buffer, idx, c);
432
0
  }
433
0
  ok = convert_string_talloc(frame,
434
0
           CH_UTF16MUNGED, CH_UTF8,
435
0
           state->password_buffer, len * 2,
436
0
           (void *)&utf8_pw, &utf8_len);
437
0
  if (!ok) {
438
0
    DEBUG(0, ("%s: convert_string_talloc() failed\n",
439
0
        __func__));
440
0
    TALLOC_FREE(frame);
441
0
    return NULL;
442
0
  }
443
0
  talloc_keep_secret(utf8_pw);
444
445
0
  ok = convert_string_talloc(frame,
446
0
           CH_UTF16MUNGED, CH_UNIX,
447
0
           state->password_buffer, len * 2,
448
0
           (void *)&unix_pw, &unix_len);
449
0
  if (!ok) {
450
0
    goto ascii_fallback;
451
0
  }
452
0
  talloc_keep_secret(unix_pw);
453
454
0
  if (utf8_len != unix_len) {
455
0
    goto ascii_fallback;
456
0
  }
457
458
0
  cmp = memcmp((const uint8_t *)utf8_pw,
459
0
         (const uint8_t *)unix_pw,
460
0
         utf8_len);
461
0
  if (cmp != 0) {
462
0
    goto ascii_fallback;
463
0
  }
464
465
0
  new_pw = talloc_strdup(mem_ctx, utf8_pw);
466
0
  if (new_pw == NULL) {
467
0
    TALLOC_FREE(frame);
468
0
    return NULL;
469
0
  }
470
0
  talloc_keep_secret(new_pw);
471
0
  TALLOC_FREE(frame);
472
0
  return new_pw;
473
474
0
ascii_fallback:
475
0
  for (i = 0; i < len; i++) {
476
    /*
477
     * truncate to ascii
478
     */
479
0
    state->tmp = state->password_buffer[i] & 0x7f;
480
0
    if (state->tmp == 0) {
481
0
      state->tmp = state->password_buffer[i] >> 1;
482
0
    }
483
0
    if (state->tmp == 0) {
484
0
      state->tmp = 0x01;
485
0
    }
486
0
    state->password_buffer[i] = state->tmp;
487
0
  }
488
0
  state->password_buffer[i] = '\0';
489
490
0
  new_pw = talloc_strdup(mem_ctx, (const char *)state->password_buffer);
491
0
  if (new_pw == NULL) {
492
0
    TALLOC_FREE(frame);
493
0
    return NULL;
494
0
  }
495
0
  talloc_keep_secret(new_pw);
496
0
  talloc_set_name_const(new_pw, __func__);
497
0
  TALLOC_FREE(frame);
498
0
  return new_pw;
499
0
}
500
501
/**
502
 * Generate an array of unique text strings all of the same length.
503
 * The returned string will be allocated.
504
 * Returns NULL if the number of unique combinations cannot be created.
505
 *
506
 * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
507
 */
508
_PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len,
509
             uint32_t num)
510
0
{
511
0
  const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
512
0
  const unsigned c_size = 42;
513
0
  size_t i, j;
514
0
  unsigned rem;
515
0
  char ** strs = NULL;
516
517
0
  if (num == 0 || len == 0)
518
0
    return NULL;
519
520
0
  strs = talloc_array(mem_ctx, char *, num);
521
0
  if (strs == NULL) return NULL;
522
523
0
  for (i = 0; i < num; i++) {
524
0
    char *retstr = (char *)talloc_size(strs, len + 1);
525
0
    if (retstr == NULL) {
526
0
      talloc_free(strs);
527
0
      return NULL;
528
0
    }
529
0
    rem = i;
530
0
    for (j = 0; j < len; j++) {
531
0
      retstr[j] = c_list[rem % c_size];
532
0
      rem = rem / c_size;
533
0
    }
534
0
    retstr[j] = 0;
535
0
    strs[i] = retstr;
536
0
    if (rem != 0) {
537
      /* we were not able to fit the number of
538
       * combinations asked for in the length
539
       * specified */
540
0
      DEBUG(0,(__location__ ": Too many combinations %u for length %u\n",
541
0
         num, (unsigned)len));
542
543
0
      talloc_free(strs);
544
0
      return NULL;
545
0
    }
546
0
  }
547
548
0
  return strs;
549
0
}