Bug 7386 - Cannot mount CIFS share if password contains non-ASCII character
Summary: Cannot mount CIFS share if password contains non-ASCII character
Status: RESOLVED FIXED
Alias: None
Product: CifsVFS
Classification: Unclassified
Component: kernel fs (show other bugs)
Version: 2.6
Hardware: x86 Linux
: P3 normal
Target Milestone: ---
Assignee: shirishpargaonkar@gmail.com
QA Contact:
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-04-22 07:59 UTC by Alex Zeffertt
Modified: 2018-05-12 20:38 UTC (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alex Zeffertt 2010-04-22 07:59:07 UTC
The command "mount.cifs //servername/share /mnt -ocredentials=path/to/file" fails if the password contains a non-ASCII character.  

I tested this with a password containing the £ (British pound) sign and I got 
the following error:

   mount error 13 = Permission denied
   Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)

The credentials file appears to be UTF-8, with all the characters one byte wide 
except for the "£" sign which is encoded as "0xc2 0xa3".

I am using samba-client-3.0.33-3.15.el5_4.1 with linux-2.6.27 and Windows 2003 
Server to host the share.
Comment 1 shirishpargaonkar@gmail.com 2010-04-22 09:53:39 UTC
It is a problem. I put a $ in the password and authentication fails.
Comment 2 Rahul 2011-04-08 04:41:30 UTC
I am also facing this issue.  We are using french domain. Username and password contains ù character.  I am able to map the share from windows with username and password which contains ù character. 

CalcNTLMv2_partial_mac_key() function is correctly printing password in logs(if /proc/fs/cifs/Debug and /proc/fs/cifs/cifsFYI are set to 1). But I don't know why mount is failing. 

Is it a problem because CIFS converts username to uppercase letters and then send it?
Comment 3 Rahul 2011-04-08 12:12:15 UTC
Or there could be issue with MD5 encryption of username and password where non-ascii characters are part of password.
Comment 4 shirishpargaonkar@gmail.com 2011-04-08 12:53:45 UTC
There is no CalcNTLMv2_partial_mac_key in current cifs code.
Did yo mean CalcNTLMv2_response() instead?
What is the version of cifs module that you are using?

NTLMv1, NTLMv2, and NTLMv2 within NTLMSSP make use of NTLM hash
generated using MD4 algorithm on the Unicode password (the password
you provide is converted to Unicode password).

Have you tried mounts using -o sec=ntlm or -o sec=ntlmv2 or -o sec=ntlmssp
options?
Comment 5 Jeff Layton 2011-04-08 13:00:01 UTC
Reassigning this to Shirish since he is far more well versed in NTLM auth nuances than I am...
Comment 6 Jeff Layton 2011-04-08 13:05:29 UTC
Now, that said, there's a patch that was sent to the mailing list a while back by 
Oskar Liljeblad.

Subject: [PATCH] CIFS: Assume passwords are encoded according to iocharset

...I wonder if that might fix this. That _my_mbstowcs routine has always seemed quite suspect.
Comment 7 shirishpargaonkar@gmail.com 2011-04-08 13:17:00 UTC
Yes. I think that patch needs to be tried/tested here.
The only suspect is conversion of text password (pertaining to a locale)
to unicode password and Oscar's fix is worth trying here.
Comment 8 Rahul 2011-04-11 14:14:55 UTC
Thanks for proving pointer. 

I have taken Oskar's changes and tried mount.
<1> For ascii(normal) username, mount worked where password contains ù character
<2> For username and password which contains ù character, mount failed.


At least something is working now.  Thanks.
Comment 9 Alex Zeffertt 2011-04-11 14:42:25 UTC
Hi Rahul,

We found that non-ascii usernames worked provided the credentials file (passed to "mount" with -ocredentials=<file>) was in UTF-8.

In our python code we got non-ascii usernames working like this:

-- snip --
f.write("username=%s\npassword=%s\n" % \
         (username.encode("utf-8"),password.encode("utf-8")))
--/snip --

It sounds like the patch you tested fixes the other half of the problem, namely non-ascii passwords!

Could you attach the patch to this ticket for reference?

Thanks
Comment 10 Rahul 2011-04-12 04:40:42 UTC
Patch for reference: https://patchwork.kernel.org/patch/666551/
Patch
==============================================================================
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 5bb4b09..3c1306d 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -226,7 +226,7 @@  int cifs_verify_signature(struct smb_hdr *cifs_pdu,
 }
 
 /* first calculate 24 bytes ntlm response and then 16 byte session key */
-int setup_ntlm_response(struct cifs_ses *ses)
+int setup_ntlm_response(struct cifs_ses *ses, const struct nls_table *nls_cp)
 {
 	int rc = 0;
 	unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE;
@@ -243,14 +243,14 @@  int setup_ntlm_response(struct cifs_ses *ses)
 	ses->auth_key.len = temp_len;
 
 	rc = SMBNTencrypt(ses->password, ses->server->cryptkey,
-			ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+			ses->auth_key.response + CIFS_SESS_KEY_SIZE, nls_cp);
 	if (rc) {
 		cFYI(1, "%s Can't generate NTLM response, error: %d",
 			__func__, rc);
 		return rc;
 	}
 
-	rc = E_md4hash(ses->password, temp_key);
+	rc = E_md4hash(ses->password, temp_key, nls_cp);
 	if (rc) {
 		cFYI(1, "%s Can't generate NT hash, error: %d", __func__, rc);
 		return rc;
@@ -458,7 +458,7 @@  static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
 	}
 
 	/* calculate md4 hash of password */
-	E_md4hash(ses->password, nt_hash);
+	E_md4hash(ses->password, nt_hash, nls_cp);
 
 	crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,
 				CIFS_NTHASH_SIZE);
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index e42dc82..fd6d873 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -376,8 +376,9 @@  extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
 extern int cifs_verify_signature(struct smb_hdr *,
 				 struct TCP_Server_Info *server,
 				__u32 expected_sequence_number);
-extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *);
-extern int setup_ntlm_response(struct cifs_ses *);
+extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
+			const struct nls_table *);
+extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
 extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
 extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *);
 extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
@@ -429,7 +430,8 @@  extern int CIFSCheckMFSymlink(struct cifs_fattr *fattr,
 		const unsigned char *path,
 		struct cifs_sb_info *cifs_sb, int xid);
 extern int mdfour(unsigned char *, unsigned char *, int);
-extern int E_md4hash(const unsigned char *passwd, unsigned char *p16);
+extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
+		     const struct nls_table *codepage);
 extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
 			unsigned char *p24);
 #endif			/* _CIFSPROTO_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 96544a4..3c0190f 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -3060,7 +3060,7 @@  CIFSTCon(unsigned int xid, struct cifs_ses *ses,
 		else
 #endif /* CIFS_WEAK_PW_HASH */
 		rc = SMBNTencrypt(tcon->password, ses->server->cryptkey,
-					bcc_ptr);
+					bcc_ptr, nls_codepage);
 
 		bcc_ptr += CIFS_AUTH_RESP_SIZE;
 		if (ses->capabilities & CAP_UNICODE) {
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 6b140e1..17ae0db 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -694,7 +694,7 @@  ssetup_ntlmssp_authenticate:
 			cpu_to_le16(CIFS_AUTH_RESP_SIZE);
 
 		/* calculate ntlm response and session key */
-		rc = setup_ntlm_response(ses);
+		rc = setup_ntlm_response(ses, nls_cp);
 		if (rc) {
 			cERROR(1, "Error %d during NTLM authentication", rc);
 			goto ssetup_exit;
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index 1525d5e..92291c1 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -195,46 +195,13 @@  SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24)
 	return rc;
 }
 
-/* Routines for Windows NT MD4 Hash functions. */
-static int
-_my_wcslen(__u16 *str)
-{
-	int len = 0;
-	while (*str++ != 0)
-		len++;
-	return len;
-}
-
-/*
- * Convert a string into an NT UNICODE string.
- * Note that regardless of processor type
- * this must be in intel (little-endian)
- * format.
- */
-
-static int
-_my_mbstowcs(__u16 *dst, const unsigned char *src, int len)
-{	/* BB not a very good conversion routine - change/fix */
-	int i;
-	__u16 val;
-
-	for (i = 0; i < len; i++) {
-		val = *src;
-		SSVAL(dst, 0, val);
-		dst++;
-		src++;
-		if (val == 0)
-			break;
-	}
-	return i;
-}
-
 /*
  * Creates the MD4 Hash of the users password in NT UNICODE.
  */
 
 int
-E_md4hash(const unsigned char *passwd, unsigned char *p16)
+E_md4hash(const unsigned char *passwd, unsigned char *p16,
+	  const struct nls_table *codepage)
 {
 	int rc;
 	int len;
@@ -242,20 +209,14 @@  E_md4hash(const unsigned char *passwd, unsigned char *p16)
 
 	/* Password cannot be longer than 128 characters */
 	if (passwd) {
-		len = strlen((char *) passwd);
-		if (len > 128)
-			len = 128;
-
 		/* Password must be converted to NT unicode */
-		_my_mbstowcs(wpwd, passwd, len);
-	} else
+		len = cifs_strtoUCS(wpwd, passwd, 128, codepage);
+	} else {
 		len = 0;
+		*wpwd = 0; /* Ensure string is null terminated */
+	}
 
-	wpwd[len] = 0;	/* Ensure string is null terminated */
-	/* Calculate length in bytes */
-	len = _my_wcslen(wpwd) * sizeof(__u16);
-
-	rc = mdfour(p16, (unsigned char *) wpwd, len);
+	rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__u16));
 	memset(wpwd, 0, 129 * 2);
 
 	return rc;
@@ -275,7 +236,7 @@  nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16])
 		memcpy(passwd, pwd, 512);
 	/* Calculate the MD4 hash (NT compatible) of the password */
 	memset(nt_p16, '\0', 16);
-	E_md4hash(passwd, nt_p16);
+	E_md4hash(passwd, nt_p16, /* put nls codepage here */);
 
 	/* Mangle the passwords into Lanman format */
 	passwd[14] = '\0';
@@ -348,7 +309,8 @@  NTLMSSPOWFencrypt(unsigned char passwd[8],
 
 /* Does the NT MD4 hash then des encryption. */
 int
-SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
+SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24,
+	     const struct nls_table *codepage)
 {
 	int rc;
 	unsigned char p16[16], p21[21];
@@ -356,7 +318,7 @@  SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
 	memset(p16, '\0', 16);
 	memset(p21, '\0', 21);
 
-	rc = E_md4hash(passwd, p16);
+	rc = E_md4hash(passwd, p16, codepage);
 	if (rc) {
 		cFYI(1, "%s Can't generate NT hash, error: %d", __func__, rc);
 		return rc;

==============================================================================


In my code, 
<1> I have removed functions _my_wcslen and _my_mbstowcs and replaced call for _my_mbstowcs() by  cifs_strtoUCS().
<2> Added codepage argument to functions E_md4hash,SMBNTencrypt,nt_lm_owf_gen.
Comment 11 Rahul 2011-04-12 10:39:14 UTC
Now mount is working for username and password which contains ù character.

toupper() was a problem for username. Instead used UniStrupr() after converting username to unicode.

It worked.
Comment 12 Alex Zeffertt 2011-04-12 11:36:35 UTC
So just to be clear... this can be fixed entirely in kernel code?  If so, can we push a patch to the kernel maintainers?
Comment 13 shirishpargaonkar@gmail.com 2011-04-12 12:27:54 UTC
(In reply to comment #11)

Which function was changed to call Unistrupr instead of toupper,
calc_lanman_hash()?
Comment 14 shirishpargaonkar@gmail.com 2011-04-12 12:44:27 UTC
user name should not matter, lowercase or upper case.
None of the auth mechs (lm, ntlm, and ntlmv2) use it
so it should not matter.
Only thing uppercase matters is 'uppercase Unicode target user database'
(typically domain name or server name in some cases) which is used during
response blob calculation for ntlmv2 auth mech.
Comment 15 Rahul 2011-04-12 13:22:30 UTC
I am using old version of CIFS 1.47 which comes with cent OS 5.2.  It has function CalcNTLMv2_partial_mac_key() which was converting username by using function toupper(). 

For username ùùùùù, with normal(ascii) password, mount was failing. 
toupper was called before converting to UTF8. 

So after utf8 conversion, I called UniStrupr() [instead of calling toupper before conversion]. 

I need to check how it is with latest code.
Comment 16 shirishpargaonkar@gmail.com 2011-04-12 13:37:37 UTC
(In reply to comment #15)

I think with the latest kernel/cifs_code, Oskar' patch would suffice.
Comment 17 shirishpargaonkar@gmail.com 2011-04-12 13:49:01 UTC
In the later cifs code, in function calc_ntlmv2_hash in file cifsencrypt.c,
Unistrupr() is already used for user name.

And I stated incorrect in comment #14, in ntlmv2 auth, uppercase unicode
user name is used to generate Target String (along with uppercase unicoe
target user database).
Comment 18 shirishpargaonkar@gmail.com 2011-04-18 08:03:52 UTC
(In reply to comment #1)
> It is a problem. I put a $ in the password and authentication fails.

If there is a character like $ in password, password needs a single quotes
around it.
And just to reiterate, all we need i Oskar's patch and bug would be fixed.
Comment 19 nimbus1_03087 2011-09-20 23:52:51 UTC
I was wondering what the status of this bug was? 

Seems like it's been a bit since it was assigned, didn't notice if it had been fixed for a release.

Thanks!
Comment 20 Björn Jacke 2018-05-12 20:38:04 UTC
just checked that this is fixed. Shirish, Steve: please make sure to keep the status of your bugs up to date and close bugs accordingly when they had beed fixed.