Bug #38895 MySQL Connector/Net Should Not Use Hashed Password for Mono
Submitted: 19 Aug 2008 16:20 Modified: 8 Sep 2008 10:26
Reporter: Joshua Martin Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S2 (Serious)
Version:5.2.3.0 OS:Any (Running Mono)
Assigned to: CPU Architecture:Any
Tags: get_hashed, Mono

[19 Aug 2008 16:20] Joshua Martin
Description:
On any box with Mono and the MySQL Connector/Net (Mono 2.0 Preview 2 and Connector/Net 5.2.3) the membership provider is incapable of use.

The resulting error is displayed whenever membership provider use is need (i.e. logging in or creating a new user).

Method not found: 'System.Web.Security.Membership.get_HashAlgorithmType'

The flaw lies in the fact that Mono does not have the necessary methods in place to allow for hashed passwords. However, even when specifying to use Clear or Encrypted passwords with the Connector/Net, the Connector/Net still has code to call the absent method.

This one flaw is all that holds users back from using Mono and the full capabilities of Connector/Net.

How to repeat:
Simply install Mono and the Connector/Net. Make the MySQL membership provider the default one.

Then, try any membership tasks like logging in or creating a new user.

Suggested fix:
The problem can be resolved by changing a few lines of code in the MySQL.Web Membership provider located at mysql-connector-net-5.2.3-src\MySql.Web\Providers\Source\MembershipProvider.cs

One must comment out 2 lines of code and add a line to produce an supported password format exception.

This is before:

        private string EncodePassword(string password, string passwordKey,
            MembershipPasswordFormat format)
        {
            if (password == null)
                return null;
            if (format == MembershipPasswordFormat.Clear)
                return password;

            byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
            byte[] keyBytes = Convert.FromBase64String(passwordKey);
            byte[] keyedBytes = new byte[passwordBytes.Length + keyBytes.Length];
            Array.Copy(keyBytes, keyedBytes, keyBytes.Length);
            Array.Copy(passwordBytes, 0, keyedBytes, keyBytes.Length, passwordBytes.Length);

            if (format == MembershipPasswordFormat.Encrypted)
            {
                byte[] encryptedBytes = EncryptPassword(passwordBytes);
                return Convert.ToBase64String(encryptedBytes);
            }
            else if (format == MembershipPasswordFormat.Hashed)
            {
                HashAlgorithm hash = HashAlgorithm.Create(Membership.HashAlgorithmType);
                return Convert.ToBase64String(hash.ComputeHash(keyedBytes));
            }
            else
                throw new ProviderException(Resources.UnsupportedPasswordFormat);
        }

This is after:

        private string EncodePassword(string password, string passwordKey,
            MembershipPasswordFormat format)
        {
            if (password == null)
                return null;
            if (format == MembershipPasswordFormat.Clear)
                return password;

            byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
            byte[] keyBytes = Convert.FromBase64String(passwordKey);
            byte[] keyedBytes = new byte[passwordBytes.Length + keyBytes.Length];
            Array.Copy(keyBytes, keyedBytes, keyBytes.Length);
            Array.Copy(passwordBytes, 0, keyedBytes, keyBytes.Length, passwordBytes.Length);

            if (format == MembershipPasswordFormat.Encrypted)
            {
                byte[] encryptedBytes = EncryptPassword(passwordBytes);
                return Convert.ToBase64String(encryptedBytes);
            }
            else if (format == MembershipPasswordFormat.Hashed)
            {
                throw new ProviderException(Resources.UnsupportedPasswordFormat);
                //HashAlgorithm hash = HashAlgorithm.Create(Membership.HashAlgorithmType);
                //return Convert.ToBase64String(hash.ComputeHash(keyedBytes));
            }
            else
                throw new ProviderException(Resources.UnsupportedPasswordFormat);
        }

What I suggest is that two versions of the Connector/Net be compiled when a new release is made (just until the necessary methods are implemented for Mono). One version for .NET and the other for Mono.
[19 Aug 2008 23:05] Joshua Martin
After reporting the missing method as a bug to the Mono community, I have received confirmation that the method has been added to the SVN and the Mono 2.0 builds.

The first release candidate for Mono 2.0 will be released on September 8, 2008. After verifying that the patch is included and there is no issue between Mono and Connector/Net in regards to this flaw, then I will close this bug report.
[19 Aug 2008 23:25] Reggie Burnett
Great.  Thanks for letting me know.  I'm marking this as verified for now so our support crew won't grab it and waste time on it.
[5 Sep 2008 20:09] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/53396
[5 Sep 2008 20:10] Reggie Burnett
fixed in 5.2.4
[8 Sep 2008 10:26] Tony Bedford
An entry was added to the 5.2.4 changelog:

Connector/NET called hashed password methods not supported in Mono 2.0 Preview 2.
[12 Sep 2008 12:18] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/53972
[12 Sep 2008 21:56] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/54011