[pkg-wpa-devel] Bug#649202: wpasupplicant does not correctly handle non-ASCII passwords for MS-CHAP, MS-CHAPv2

Evan Broder evan at ebroder.net
Fri Nov 18 19:29:00 UTC 2011


Package: wpasupplicant
Version: 0.7.3-5
Severity: wishlist
Tags: patch

Debian's current version of wpasupplicant treats the password provided
for MS-CHAP and MS-CHAPv2 as ASCII, converting it to the RFC-required
UCS-2 by inserting NULL between each byte.

Obviously, this means that the password can't contain non-ASCII
characters, in spite of that being allowed by the spec.

I submitted a patch for this to wpasupplicant upstream, which was
accepted as c48183fca2d32cd45763fdbf45144aac856bea93 (attached). I
would appreciate it if tihs patch could be included in Debian's
wpasupplicant package.

Thanks

-- System Information:
Debian Release: wheezy/sid
  APT prefers oneiric-updates
  APT policy: (500, 'oneiric-updates'), (500, 'oneiric-security'), (500, 'oneiric'), (100, 'oneiric-backports')
Architecture: amd64 (x86_64)

Kernel: Linux 3.0.0-12-generic (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages wpasupplicant depends on:
ii  adduser                3.112+nmu1ubuntu5 add and remove users and groups
ii  libc6                  2.13-20ubuntu5    Embedded GNU C Library: Shared lib
ii  libdbus-1-3            1.4.14-1ubuntu1   simple interprocess messaging syst
ii  libnl3                 3.0-1.1ubuntu1    library for dealing with netlink s
ii  libpcsclite1           1.7.2-2ubuntu2    Middleware to access a smart card 
ii  libreadline6           6.2-2ubuntu1      GNU readline and history libraries
ii  libssl1.0.0            1.0.0e-2ubuntu4   SSL shared libraries
ii  lsb-base               4.0-0ubuntu16     Linux Standard Base 4.0 init scrip

wpasupplicant recommends no packages.

Versions of packages wpasupplicant suggests:
pn  libengine-pkcs11-openssl      <none>     (no description available)
pn  wpagui                        <none>     (no description available)

-- no debconf information
-------------- next part --------------
>From c48183fca2d32cd45763fdbf45144aac856bea93 Mon Sep 17 00:00:00 2001
From: Evan Broder <ebroder at mokafive.com>
Date: Sat, 29 Oct 2011 00:23:54 +0300
Subject: [PATCH] For MS-CHAP, convert the password from UTF-8 to UCS-2

The MS-CHAPv1 and MS-CHAPv2 RFCs specify that the password is a string
of "Unicode characters", which for Windows means UCS-2; thus the
password could be any even-length string of up to 512 bytes.

Instead of making the incompatible change of requiring the incoming
password to be UCS-2 encoded, assume the password is UTF-8 encoded and
convert it before using it in NtPasswordHash and
EncryptPwBlockWithPasswordHash.

Signed-off-by: Evan Broder <ebroder at mokafive.com>
---
 src/crypto/ms_funcs.c |  106 +++++++++++++++++++++++++++++++++++++------------
 1 files changed, 80 insertions(+), 26 deletions(-)

diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index dae15ab..c439ae9 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -19,6 +19,60 @@
 #include "ms_funcs.h"
 #include "crypto.h"
 
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+                        u8 *ucs2_buffer, size_t ucs2_buffer_size,
+                        size_t *ucs2_string_size)
+{
+	size_t i, j;
+
+	for (i = 0, j = 0; i < utf8_string_len; i++) {
+		u8 c = utf8_string[i];
+		if (j >= ucs2_buffer_size) {
+			/* input too long */
+			return -1;
+		}
+		if (c <= 0x7F) {
+			WPA_PUT_LE16(ucs2_buffer + j, c);
+			j += 2;
+		} else if (i == utf8_string_len - 1 ||
+			   j >= ucs2_buffer_size - 1) {
+			/* incomplete surrogate */
+			return -1;
+		} else {
+			u8 c2 = utf8_string[++i];
+			if ((c & 0xE0) == 0xC0) {
+				/* two-byte encoding */
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0x1F) << 6) | (c2 & 0x3F));
+				j += 2;
+			} else if (i == utf8_string_len ||
+				   j >= ucs2_buffer_size - 1) {
+				/* incomplete surrogate */
+				return -1;
+			} else {
+				/* three-byte encoding */
+				u8 c3 = utf8_string[++i];
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0xF) << 12) |
+					     ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+			}
+		}
+	}
+
+	if (ucs2_string_size)
+		*ucs2_string_size = j / 2;
+	return 0;
+}
+
 
 /**
  * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
@@ -53,7 +107,7 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
 
 /**
  * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @password_hash: 16-octet PasswordHash (OUT)
  * Returns: 0 on success, -1 on failure
@@ -62,18 +116,13 @@ int nt_password_hash(const u8 *password, size_t password_len,
 		      u8 *password_hash)
 {
 	u8 buf[512], *pos;
-	size_t i, len;
+	size_t len, max_len;
 
-	if (password_len > 256)
-		password_len = 256;
-
-	/* Convert password into unicode */
-	for (i = 0; i < password_len; i++) {
-		buf[2 * i] = password[i];
-		buf[2 * i + 1] = 0;
-	}
+	max_len = sizeof(buf);
+	if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+		return -1;
 
-	len = password_len * 2;
+	len *= 2;
 	pos = buf;
 	return md4_vector(1, (const u8 **) &pos, &len, password_hash);
 }
@@ -117,7 +166,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash,
  * @peer_challenge: 16-octet PeerChallenge (IN)
  * @username: 0-to-256-char UserName (IN)
  * @username_len: Length of username
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @response: 24-octet Response (OUT)
  * Returns: 0 on success, -1 on failure
@@ -225,7 +274,7 @@ int generate_authenticator_response_pwhash(
 
 /**
  * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @nt_response: 24-octet NT-Response (IN)
  * @peer_challenge: 16-octet PeerChallenge (IN)
@@ -254,7 +303,7 @@ int generate_authenticator_response(const u8 *password, size_t password_len,
 /**
  * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
  * @challenge: 8-octet Challenge (IN)
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @response: 24-octet Response (OUT)
  * Returns: 0 on success, -1 on failure
@@ -375,7 +424,7 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
 
 /**
  * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
  * @password_len: Length of password
  * @password_hash: 16-octet PasswordHash (IN)
  * @pw_block: 516-byte PwBlock (OUT)
@@ -385,18 +434,23 @@ int encrypt_pw_block_with_password_hash(
 	const u8 *password, size_t password_len,
 	const u8 *password_hash, u8 *pw_block)
 {
-	size_t i, offset;
+	size_t ucs2_len, offset;
 	u8 *pos;
 
-	if (password_len > 256)
+	os_memset(pw_block, 0, PWBLOCK_LEN);
+
+	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
 		return -1;
 
-	os_memset(pw_block, 0, PWBLOCK_LEN);
-	offset = (256 - password_len) * 2;
-	if (os_get_random(pw_block, offset) < 0)
+	if (ucs2_len > 256)
 		return -1;
-	for (i = 0; i < password_len; i++)
-		pw_block[offset + i * 2] = password[i];
+
+	offset = (256 - ucs2_len) * 2;
+	if (offset != 0) {
+		os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+		if (os_get_random(pw_block, offset) < 0)
+			return -1;
+	}
 	/*
 	 * PasswordLength is 4 octets, but since the maximum password length is
 	 * 256, only first two (in little endian byte order) can be non-zero.
@@ -410,9 +464,9 @@ int encrypt_pw_block_with_password_hash(
 
 /**
  * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
  * @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
  * @old_password_len: Length of old_password
  * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
  * Returns: 0 on success, -1 on failure
@@ -450,9 +504,9 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash,
 
 /**
  * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
  * @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
  * @old_password_len: Length of old_password
  * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
  * Returns: 0 on success, -1 on failure
-- 
1.7.5.4



More information about the Pkg-wpa-devel mailing list