summaryrefslogtreecommitdiff
path: root/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'lib/util')
-rw-r--r--lib/util/genrand.c134
-rw-r--r--lib/util/tests/genrand.c5
2 files changed, 120 insertions, 19 deletions
diff --git a/lib/util/genrand.c b/lib/util/genrand.c
index 57884ef791..3dfaf089d1 100644
--- a/lib/util/genrand.c
+++ b/lib/util/genrand.c
@@ -298,29 +298,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;
}
- s++;
+
+ 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 ((has_digit + has_lower + has_capital + has_special) >= 3
- || (has_high > strlen(reals)/2));
+ return false;
}
/**
diff --git a/lib/util/tests/genrand.c b/lib/util/tests/genrand.c
index 50d77bb03a..ff608cd925 100644
--- a/lib/util/tests/genrand.c
+++ b/lib/util/tests/genrand.c
@@ -41,7 +41,10 @@ static bool test_check_password_quality(struct torture_context *tctx)
torture_assert(tctx, !check_password_quality("BLA"), "multiple upcases password");
torture_assert(tctx, !check_password_quality("123"), "digits only");
torture_assert(tctx, !check_password_quality("matthiéu"), "not enough high symbols");
- torture_assert(tctx, check_password_quality("abcdééàçè"), "valid");
+ torture_assert(tctx, !check_password_quality("abcdééàçè"), "only lower case");
+ torture_assert(tctx, !check_password_quality("abcdééàçè+"), "only lower and symbols");
+ torture_assert(tctx, check_password_quality("abcdééàçè+ढ"), "valid");
+ torture_assert(tctx, check_password_quality("ç+ढ"), "valid");
torture_assert(tctx, check_password_quality("A2e"), "valid");
torture_assert(tctx, check_password_quality("BA2eLi443"), "valid");
return true;