Skip to content

Commit

Permalink
Add support for wide characters in passwords.
Browse files Browse the repository at this point in the history
The crypt function cannot handle most wide (Unicode) characters,
but does handle some by attempting to downgrade to an 8-bit string.

In order to support wide characters in passwords while not breaking
any passwords which worked before this change, a password will be
encoded into UTF-8 before being sent to crypt only when crypt dies
on the original string.
  • Loading branch information
taniwallach committed Nov 21, 2024
1 parent df4b848 commit 4058ed8
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 4 deletions.
11 changes: 9 additions & 2 deletions lib/WeBWorK/Authen.pm
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,15 @@ sub checkPassword {
my $Password = $db->getPassword($userID);
if (defined $Password) {
# Check against the password in the database.
my $possibleCryptPassword = crypt $possibleClearPassword, $Password->password;
my $dbPassword = $Password->password;
my $possibleCryptPassword = '';
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
eval { $possibleCryptPassword = crypt $possibleClearPassword, $Password->password; };
if ($@ && $@ =~ /Wide char/) {
$possibleCryptPassword = crypt Encode::encode_utf8($possibleClearPassword), $Password->password;
}

my $dbPassword = $Password->password;
# This next line explicitly insures that blank or null passwords from the database can never succeed in matching
# an entered password. This also rejects cases when the database has a crypted password which matches a
# submitted all white-space or null password by requiring that the $possibleClearPassword contain some non-space
Expand Down
11 changes: 10 additions & 1 deletion lib/WeBWorK/ContentGenerator/Options.pm
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,16 @@ sub initialize ($c) {
$userID ne $effectiveUserID ? eval { $db->getPassword($c->{effectiveUser}->user_id) } : $password;

# Check that either password is not defined or if it is defined then we have the right one.
if (!defined $password || crypt($currP // '', $password->password) eq $password->password) {
my $cryptedCurrP;
if (defined $password) {
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
eval { $cryptedCurrP = crypt($currP // '', $password->password); };
if ($@ && $@ =~ /Wide char/) {
$cryptedCurrP = crypt(Encode::encode_utf8($currP), $password->password);
}
}
if (!defined $password || $cryptedCurrP eq $password->password) {
my $e_user_name = $c->{effectiveUser}->first_name . ' ' . $c->{effectiveUser}->last_name;
if ($newP eq $confirmP) {
if (!defined $effectiveUserPassword) {
Expand Down
10 changes: 9 additions & 1 deletion lib/WeBWorK/Utils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,15 @@ sub cryptPassword ($clearPassword) {
$salt .= ('.', '/', '0' .. '9', 'A' .. 'Z', 'a' .. 'z')[ rand 64 ];
}

return crypt(trim_spaces($clearPassword), $salt);
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
my $cryptedPassword = '';
eval { $cryptedPassword = crypt(trim_spaces($clearPassword), $salt); };
if ($@ && $@ =~ /Wide char/) {
$cryptedPassword = crypt(Encode::encode_utf8(trim_spaces($clearPassword)), $salt);
}

return $cryptedPassword;
}

sub undefstr ($default, @values) {
Expand Down

0 comments on commit 4058ed8

Please sign in to comment.