Skip to content

Commit ba10a93

Browse files
committed
Add support for wide characters in passwords.
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.
1 parent df4b848 commit ba10a93

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed

lib/WeBWorK/Authen.pm

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,15 @@ sub checkPassword {
633633
my $Password = $db->getPassword($userID);
634634
if (defined $Password) {
635635
# Check against the password in the database.
636-
my $possibleCryptPassword = crypt $possibleClearPassword, $Password->password;
637-
my $dbPassword = $Password->password;
636+
my $possibleCryptPassword = '';
637+
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
638+
# When such an error occurs - try to encode to UTF-8 and they run crypt.
639+
eval { $possibleCryptPassword = crypt $possibleClearPassword, $Password->password; };
640+
if ($@ && $@ =~ /Wide char/) {
641+
$possibleCryptPassword = crypt Encode::encode_utf8($possibleClearPassword), $Password->password;
642+
}
643+
644+
my $dbPassword = $Password->password;
638645
# This next line explicitly insures that blank or null passwords from the database can never succeed in matching
639646
# an entered password. This also rejects cases when the database has a crypted password which matches a
640647
# submitted all white-space or null password by requiring that the $possibleClearPassword contain some non-space

lib/WeBWorK/ContentGenerator/Options.pm

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,16 @@ sub initialize ($c) {
5656
$userID ne $effectiveUserID ? eval { $db->getPassword($c->{effectiveUser}->user_id) } : $password;
5757

5858
# Check that either password is not defined or if it is defined then we have the right one.
59-
if (!defined $password || crypt($currP // '', $password->password) eq $password->password) {
59+
my $cryptedCurrP;
60+
if (defined $password) {
61+
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
62+
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
63+
eval { $cryptedCurrP = crypt($currP // '', $password->password); };
64+
if ($@ && $@ =~ /Wide char/) {
65+
$cryptedCurrP = crypt(Encode::encode_utf8($currP), $password->password);
66+
}
67+
}
68+
if (!defined $password || $cryptedCurrP eq $password->password) {
6069
my $e_user_name = $c->{effectiveUser}->first_name . ' ' . $c->{effectiveUser}->last_name;
6170
if ($newP eq $confirmP) {
6271
if (!defined $effectiveUserPassword) {

lib/WeBWorK/Utils.pm

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,18 @@ sub cryptPassword ($clearPassword) {
151151
$salt .= ('.', '/', '0' .. '9', 'A' .. 'Z', 'a' .. 'z')[ rand 64 ];
152152
}
153153

154-
return crypt(trim_spaces($clearPassword), $salt);
154+
# crypt does not support much beyond ASCII and fails on most wide
155+
# characters. Work around this without changing behavior whenever
156+
# crypt works as-is, so as not to break existing passwords which
157+
# worked before the change.
158+
my $cryptedPassword = '';
159+
eval { $cryptedPassword = crypt(trim_spaces($clearPassword), $salt); };
160+
if ($@ && $@ =~ /Wide char/) {
161+
# Try to encode to UTF-8 and they run crypt
162+
$cryptedPassword = crypt(Encode::encode_utf8(trim_spaces($clearPassword)), $salt);
163+
}
164+
165+
return $cryptedPassword;
155166
}
156167

157168
sub undefstr ($default, @values) {

0 commit comments

Comments
 (0)