Skip to content

Commit

Permalink
lib/util: improve check_password_quality() to handle utf8
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Metzmacher <[email protected]>
Reviewed-by: Michael Adam <[email protected]>
  • Loading branch information
metze-samba authored and obnoxxx committed Feb 4, 2013
1 parent e5ca813 commit 65f2bba
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 19 deletions.
134 changes: 116 additions & 18 deletions lib/util/genrand.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/util/tests/genrand.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 65f2bba

Please sign in to comment.