nwrap: Fix resolving hostnames with a trailing dot.
[sfrench/samba-autobuild/.git] / lib / util / genrand.c
index f0544023f14d29c7c8104d825a246e2cd9a9d48b..0e5902fea9ab73b1842248bcea9e8d59b1ae5012 100644 (file)
@@ -163,22 +163,23 @@ static void do_filehash(const char *fname, unsigned char *the_hash)
  above...
 **************************************************************/
 
-static int do_reseed(bool use_fd, int fd)
+static int do_reseed(int fd)
 {
        unsigned char seed_inbuf[40];
        uint32_t v1, v2; struct timeval tval; pid_t mypid;
        int reseed_data = 0;
 
-       if (use_fd) {
-               if (fd == -1) {
-                       fd = open( "/dev/urandom", O_RDONLY,0);
-               }
-               if (fd != -1
-                   && (read(fd, seed_inbuf, sizeof(seed_inbuf)) == sizeof(seed_inbuf))) {
-                       seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
-                       return fd;
+       if (fd == -1) {
+               fd = open( "/dev/urandom", O_RDONLY,0);
+               if (fd != -1) {
+                       smb_set_close_on_exec(fd);
                }
        }
+       if (fd != -1
+           && (read(fd, seed_inbuf, sizeof(seed_inbuf)) == sizeof(seed_inbuf))) {
+               seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
+               return fd;
+       }
 
        /* Add in some secret file contents */
 
@@ -232,13 +233,16 @@ _PUBLIC_ void generate_random_buffer(uint8_t *out, int len)
                if (bytes_since_reseed < 40) {
                        if (urand_fd == -1) {
                                urand_fd = open( "/dev/urandom", O_RDONLY,0);
+                               if (urand_fd != -1) {
+                                       smb_set_close_on_exec(urand_fd);
+                               }
                        }
                        if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
                                return;
                        }
                }
 
-               urand_fd = do_reseed(true, urand_fd);
+               urand_fd = do_reseed(urand_fd);
                done_reseed = true;
        }
 
@@ -269,6 +273,9 @@ _PUBLIC_ void generate_secret_buffer(uint8_t *out, int len)
 {
        if (urand_fd == -1) {
                urand_fd = open( "/dev/urandom", O_RDONLY,0);
+               if (urand_fd != -1) {
+                       smb_set_close_on_exec(urand_fd);
+               }
        }
        if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
                return;
@@ -289,29 +296,127 @@ _PUBLIC_ uint32_t generate_random(void)
 
 
 /**
-  very basic password quality checker
+  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 *s)
+_PUBLIC_ bool check_password_quality(const char *pwd)
 {
-       int has_digit=0, has_capital=0, has_lower=0, has_special=0, has_high=0;
-       const char* reals = s;
-       while (*s) {
-               if (isdigit((unsigned char)*s)) {
-                       has_digit |= 1;
-               } else if (isupper((unsigned char)*s)) {
-                       has_capital |= 1;
-               } else if (islower((unsigned char)*s)) {
-                       has_lower |= 1;
-               } else if (isascii((unsigned char)*s)) {
-                       has_special |= 1;
-               } else {
-                       has_high++;
+       size_t ofs = 0;
+       size_t num_chars = 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;
+               num_chars += 1;
+
+               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;
                }
-               s++;
+
+               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 ((has_digit + has_lower + has_capital + has_special) >= 3
-               || (has_high > strlen(reals)/2));
+       return false;
 }
 
 /**
@@ -361,6 +466,54 @@ again:
        return retstr;
 }
 
+/**
+ * Generate a random text password.
+ */
+
+_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_random_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 an array of unique text strings all of the same length.
  * The returned string will be allocated.