summaryrefslogtreecommitdiffstats
path: root/lib/util/genrand_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/util/genrand_util.c')
-rw-r--r--lib/util/genrand_util.c531
1 files changed, 531 insertions, 0 deletions
diff --git a/lib/util/genrand_util.c b/lib/util/genrand_util.c
new file mode 100644
index 0000000..43005c5
--- /dev/null
+++ b/lib/util/genrand_util.c
@@ -0,0 +1,531 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Functions to create reasonable random numbers for crypto use.
+
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/locale.h"
+#include <tevent.h>
+#include "lib/util/samba_util.h"
+#include "lib/util/debug.h"
+
+/**
+ * @file
+ * @brief Random number generation
+ */
+
+/**
+ generate a single random uint32_t
+**/
+_PUBLIC_ uint32_t generate_random(void)
+{
+ uint8_t v[4];
+ generate_random_buffer(v, 4);
+ return IVAL(v, 0);
+}
+
+/**
+ @brief generate a random uint64
+**/
+_PUBLIC_ uint64_t generate_random_u64(void)
+{
+ uint8_t v[8];
+ generate_random_buffer(v, 8);
+ return BVAL(v, 0);
+}
+
+/**
+ * @brief Generate a random number in the given range.
+ *
+ * @param lower The lower value of the range
+
+ * @param upper The upper value of the range
+ *
+ * @return A random number bigger than than lower and smaller than upper.
+ */
+_PUBLIC_ uint64_t generate_random_u64_range(uint64_t lower, uint64_t upper)
+{
+ return generate_random_u64() % (upper - lower) + lower;
+}
+
+_PUBLIC_ uint64_t generate_unique_u64(uint64_t veto_value)
+{
+ static struct generate_unique_u64_state {
+ uint64_t next_value;
+ int pid;
+ } generate_unique_u64_state;
+
+ int pid = tevent_cached_getpid();
+
+ if (unlikely(pid != generate_unique_u64_state.pid)) {
+ generate_unique_u64_state = (struct generate_unique_u64_state) {
+ .pid = pid,
+ .next_value = veto_value,
+ };
+ }
+
+ while (unlikely(generate_unique_u64_state.next_value == veto_value)) {
+ generate_nonce_buffer(
+ (void *)&generate_unique_u64_state.next_value,
+ sizeof(generate_unique_u64_state.next_value));
+ }
+
+ return generate_unique_u64_state.next_value++;
+}
+
+/**
+ Microsoft composed the following rules (among others) for quality
+ checks. This is an abridgment from
+ http://msdn.microsoft.com/en-us/subscriptions/cc786468%28v=ws.10%29.aspx:
+
+ Passwords must contain characters from three of the following five
+ categories:
+
+ - Uppercase characters of European languages (A through Z, with
+ diacritic marks, Greek and Cyrillic characters)
+ - Lowercase characters of European languages (a through z, sharp-s,
+ with diacritic marks, Greek and Cyrillic characters)
+ - Base 10 digits (0 through 9)
+ - Nonalphanumeric characters: ~!@#$%^&*_-+=`|\(){}[]:;"'<>,.?/
+ - Any Unicode character that is categorized as an alphabetic character
+ but is not uppercase or lowercase. This includes Unicode characters
+ from Asian languages.
+
+ Note: for now do not check if the unicode category is
+ alphabetic character
+**/
+_PUBLIC_ bool check_password_quality(const char *pwd)
+{
+ size_t ofs = 0;
+ size_t num_digits = 0;
+ size_t num_upper = 0;
+ size_t num_lower = 0;
+ size_t num_nonalpha = 0;
+ size_t num_unicode = 0;
+ size_t num_categories = 0;
+
+ if (pwd == NULL) {
+ return false;
+ }
+
+ while (true) {
+ const char *s = &pwd[ofs];
+ size_t len = 0;
+ codepoint_t c;
+
+ c = next_codepoint(s, &len);
+ if (c == INVALID_CODEPOINT) {
+ return false;
+ } else if (c == 0) {
+ break;
+ }
+ ofs += len;
+
+ if (len == 1) {
+ const char *na = "~!@#$%^&*_-+=`|\\(){}[]:;\"'<>,.?/";
+
+ if (isdigit(c)) {
+ num_digits += 1;
+ continue;
+ }
+
+ if (isupper(c)) {
+ num_upper += 1;
+ continue;
+ }
+
+ if (islower(c)) {
+ num_lower += 1;
+ continue;
+ }
+
+ if (strchr(na, c)) {
+ num_nonalpha += 1;
+ continue;
+ }
+
+ /*
+ * the rest does not belong to
+ * a category.
+ */
+ continue;
+ }
+
+ if (isupper_m(c)) {
+ num_upper += 1;
+ continue;
+ }
+
+ if (islower_m(c)) {
+ num_lower += 1;
+ continue;
+ }
+
+ /*
+ * Note: for now do not check if the unicode category is
+ * alphabetic character
+ *
+ * We would have to import the details from
+ * ftp://ftp.unicode.org/Public/6.3.0/ucd/UnicodeData-6.3.0d1.txt
+ */
+ num_unicode += 1;
+ continue;
+ }
+
+ if (num_digits > 0) {
+ num_categories += 1;
+ }
+ if (num_upper > 0) {
+ num_categories += 1;
+ }
+ if (num_lower > 0) {
+ num_categories += 1;
+ }
+ if (num_nonalpha > 0) {
+ num_categories += 1;
+ }
+ if (num_unicode > 0) {
+ num_categories += 1;
+ }
+
+ if (num_categories >= 3) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ Use the random number generator to generate a random string.
+**/
+
+_PUBLIC_ char *generate_random_str_list(TALLOC_CTX *mem_ctx, size_t len, const char *list)
+{
+ size_t i;
+ size_t list_len = strlen(list);
+
+ char *retstr = talloc_array(mem_ctx, char, len + 1);
+ if (!retstr) return NULL;
+
+ generate_secret_buffer((uint8_t *)retstr, len);
+ for (i = 0; i < len; i++) {
+ retstr[i] = list[retstr[i] % list_len];
+ }
+ retstr[i] = '\0';
+
+ return retstr;
+}
+
+/**
+ * Generate a random text string consisting of the specified length.
+ * The returned string will be allocated.
+ *
+ * Characters used are: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,
+ */
+
+_PUBLIC_ char *generate_random_str(TALLOC_CTX *mem_ctx, size_t len)
+{
+ char *retstr;
+ const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
+
+again:
+ retstr = generate_random_str_list(mem_ctx, len, c_list);
+ if (!retstr) return NULL;
+
+ /* we need to make sure the random string passes basic quality tests
+ or it might be rejected by windows as a password */
+ if (len >= 7 && !check_password_quality(retstr)) {
+ talloc_free(retstr);
+ goto again;
+ }
+
+ return retstr;
+}
+
+/**
+ * Generate a random text password (based on printable ascii characters).
+ */
+
+_PUBLIC_ char *generate_random_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
+{
+ char *retstr;
+ /* This list does not include { or } because they cause
+ * problems for our provision (it can create a substring
+ * ${...}, and for Fedora DS (which treats {...} at the start
+ * of a stored password as special
+ * -- Andrew Bartlett 2010-03-11
+ */
+ const char *c_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,@$%&!?:;<=>()[]~";
+ size_t len = max;
+ size_t diff;
+
+ if (min > max) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ diff = max - min;
+
+ if (diff > 0 ) {
+ size_t tmp;
+
+ generate_secret_buffer((uint8_t *)&tmp, sizeof(tmp));
+
+ tmp %= diff;
+
+ len = min + tmp;
+ }
+
+again:
+ retstr = generate_random_str_list(mem_ctx, len, c_list);
+ if (!retstr) return NULL;
+
+ /* we need to make sure the random string passes basic quality tests
+ or it might be rejected by windows as a password */
+ if (len >= 7 && !check_password_quality(retstr)) {
+ talloc_free(retstr);
+ goto again;
+ }
+
+ return retstr;
+}
+
+/**
+ * Generate a random machine password (based on random utf16 characters,
+ * converted to utf8). min must be at least 14, max must be at most 255.
+ *
+ * If 'unix charset' is not utf8, the password consist of random ascii
+ * values!
+ *
+ * The return value is a talloc string with destructor talloc_keep_secret() set.
+ * The content will be overwritten by zeros when the mem_ctx is destroyed.
+ */
+
+_PUBLIC_ char *generate_random_machine_password(TALLOC_CTX *mem_ctx, size_t min, size_t max)
+{
+ TALLOC_CTX *frame = NULL;
+ struct generate_random_machine_password_state {
+ uint8_t password_buffer[256 * 2];
+ uint8_t tmp;
+ } *state;
+ char *new_pw = NULL;
+ size_t len = max;
+ char *utf8_pw = NULL;
+ size_t utf8_len = 0;
+ char *unix_pw = NULL;
+ size_t unix_len = 0;
+ size_t diff;
+ size_t i;
+ bool ok;
+ int cmp;
+
+ if (max > 255) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (min < 14) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (min > max) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ frame = talloc_stackframe_pool(2048);
+ state = talloc_zero(frame, struct generate_random_machine_password_state);
+ talloc_keep_secret(state);
+
+ diff = max - min;
+
+ if (diff > 0) {
+ size_t tmp;
+
+ generate_secret_buffer((uint8_t *)&tmp, sizeof(tmp));
+
+ tmp %= diff;
+
+ len = min + tmp;
+ }
+
+ /*
+ * Create a random machine account password
+ * We create a random buffer and convert that to utf8.
+ * This is similar to what windows is doing.
+ *
+ * In future we may store the raw random buffer,
+ * but for now we need to pass the password as
+ * char pointer through some layers.
+ *
+ * As most kerberos keys are derived from the
+ * utf8 password we need to fallback to
+ * ASCII passwords if "unix charset" is not utf8.
+ */
+ generate_secret_buffer(state->password_buffer, len * 2);
+ for (i = 0; i < len; i++) {
+ size_t idx = i*2;
+ uint16_t c;
+
+ /*
+ * both MIT krb5 and HEIMDAL only
+ * handle codepoints up to 0xffff.
+ *
+ * It means we need to avoid
+ * 0xD800 - 0xDBFF (high surrogate)
+ * and
+ * 0xDC00 - 0xDFFF (low surrogate)
+ * in the random utf16 data.
+ *
+ * 55296 0xD800 0154000 0b1101100000000000
+ * 57343 0xDFFF 0157777 0b1101111111111111
+ * 8192 0x2000 020000 0b10000000000000
+ *
+ * The above values show that we can check
+ * for 0xD800 and just add 0x2000 to avoid
+ * the surrogate ranges.
+ *
+ * The rest will be handled by CH_UTF16MUNGED
+ * see utf16_munged_pull().
+ */
+ c = SVAL(state->password_buffer, idx);
+ if (c & 0xD800) {
+ c |= 0x2000;
+ }
+ SSVAL(state->password_buffer, idx, c);
+ }
+ ok = convert_string_talloc(frame,
+ CH_UTF16MUNGED, CH_UTF8,
+ state->password_buffer, len * 2,
+ (void *)&utf8_pw, &utf8_len);
+ if (!ok) {
+ DEBUG(0, ("%s: convert_string_talloc() failed\n",
+ __func__));
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ talloc_keep_secret(utf8_pw);
+
+ ok = convert_string_talloc(frame,
+ CH_UTF16MUNGED, CH_UNIX,
+ state->password_buffer, len * 2,
+ (void *)&unix_pw, &unix_len);
+ if (!ok) {
+ goto ascii_fallback;
+ }
+ talloc_keep_secret(unix_pw);
+
+ if (utf8_len != unix_len) {
+ goto ascii_fallback;
+ }
+
+ cmp = memcmp((const uint8_t *)utf8_pw,
+ (const uint8_t *)unix_pw,
+ utf8_len);
+ if (cmp != 0) {
+ goto ascii_fallback;
+ }
+
+ new_pw = talloc_strdup(mem_ctx, utf8_pw);
+ if (new_pw == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ talloc_keep_secret(new_pw);
+ talloc_set_name_const(new_pw, __func__);
+ TALLOC_FREE(frame);
+ return new_pw;
+
+ascii_fallback:
+ for (i = 0; i < len; i++) {
+ /*
+ * truncate to ascii
+ */
+ state->tmp = state->password_buffer[i] & 0x7f;
+ if (state->tmp == 0) {
+ state->tmp = state->password_buffer[i] >> 1;
+ }
+ if (state->tmp == 0) {
+ state->tmp = 0x01;
+ }
+ state->password_buffer[i] = state->tmp;
+ }
+ state->password_buffer[i] = '\0';
+
+ new_pw = talloc_strdup(mem_ctx, (const char *)state->password_buffer);
+ if (new_pw == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ talloc_keep_secret(new_pw);
+ talloc_set_name_const(new_pw, __func__);
+ TALLOC_FREE(frame);
+ return new_pw;
+}
+
+/**
+ * Generate an array of unique text strings all of the same length.
+ * The returned string will be allocated.
+ * Returns NULL if the number of unique combinations cannot be created.
+ *
+ * Characters used are: abcdefghijklmnopqrstuvwxyz0123456789+_-#.,
+ */
+_PUBLIC_ char** generate_unique_strs(TALLOC_CTX *mem_ctx, size_t len,
+ uint32_t num)
+{
+ const char *c_list = "abcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
+ const unsigned c_size = 42;
+ size_t i, j;
+ unsigned rem;
+ char ** strs = NULL;
+
+ if (num == 0 || len == 0)
+ return NULL;
+
+ strs = talloc_array(mem_ctx, char *, num);
+ if (strs == NULL) return NULL;
+
+ for (i = 0; i < num; i++) {
+ char *retstr = (char *)talloc_size(strs, len + 1);
+ if (retstr == NULL) {
+ talloc_free(strs);
+ return NULL;
+ }
+ rem = i;
+ for (j = 0; j < len; j++) {
+ retstr[j] = c_list[rem % c_size];
+ rem = rem / c_size;
+ }
+ retstr[j] = 0;
+ strs[i] = retstr;
+ if (rem != 0) {
+ /* we were not able to fit the number of
+ * combinations asked for in the length
+ * specified */
+ DEBUG(0,(__location__ ": Too many combinations %u for length %u\n",
+ num, (unsigned)len));
+
+ talloc_free(strs);
+ return NULL;
+ }
+ }
+
+ return strs;
+}