LCOV - code coverage report
Current view: top level - lib/util - genrand_util.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 156 199 78.4 %
Date: 2021-09-23 10:06:22 Functions: 9 9 100.0 %

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

Generated by: LCOV version 1.13