Bug #38939 MembershipUser.GetPassword(string answer) fails when incorrect answer is passed.
Submitted: 21 Aug 2008 14:19 Modified: 5 Sep 2008 16:30
Reporter: Craig Ritchie Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S2 (Serious)
Version:5.2.3.0 OS:Windows
Assigned to: CPU Architecture:Any
Tags: MembershipUser GetPassword

[21 Aug 2008 14:19] Craig Ritchie
Description:
Event Type: Information
Event Source: MySQLMembershipProvider
Event Category: None
Event ID: 0
Date: 8/20/2008
Time: 9:02:39 PM
User: N/A
Description:
An exception occurred communicating with the data source.

Action: UpdateFailureCount

Exception: MySql.Data.MySqlClient.MySqlException: There is already an open DataReader associated with this Connection which must be closed first.
at MySql.Data.MySqlClient.MySqlCommand.CheckState()
at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
at MySql.Web.Security.MySQLMembershipProvider.UpdateFailureCount(Int32 userId, String failureType, MySqlConnection connection)

How to repeat:
config settings:
  requiresQuestionAndAnswer="true"
  enablePasswordRetrieval="true"
  passwordFormat="Encrypted"

Create a user with a question and answer for password retrieval.
Attempt to call MembershipUser.GetPassword() with wrong answer.

The above exception should be the results.

Suggested fix:
I believe the offending code is the call to UpdateFailureCount within this 'if' statement:
if (this.RequiresQuestionAndAnswer && !this.CheckPassword(answer, dbpassword, passwordKey, format))
{
this.UpdateFailureCount(userId, "PasswordAnswer", connection);
throw new MembershipPasswordException(Resources.IncorrectPasswordAnswer);
}

Trying to reuse the connection in this call could be the problem. Everything seems to work if the correct password answer is passed to GetPassword().
[25 Aug 2008 9:34] Tonci Grgin
Hi Craig and thanks for your report. What puzzles me is what did you expect from c/NET in case wrong answer is provided?
[25 Aug 2008 13:44] Craig Ritchie
From: http://msdn.microsoft.com/en-us/library/kx96zecz.aspx

"GetPassword calls the MembershipProvider.GetPassword method of the membership provider referenced by the ProviderName property to retrieve the password for the membership user from the membership data store. If a password answer is required and an incorrect password answer is supplied, a MembershipPasswordException is thrown by the membership provider."

The exception that is thrown is not a MembershipPasswordException but a MySqlException regarding the reuse of a connection... which seems totally irrelevant to providing a wrong answer.

Further Information: Disassembled from SqlMembershipProvider:
public override string GetPassword(string username, string passwordAnswer)
{
    if (!this.EnablePasswordRetrieval)
    {
        throw new NotSupportedException(SR.GetString("Membership_PasswordRetrieval_not_supported"));
    }
    SecUtility.CheckParameter(ref username, true, true, true, 0x100, "username");
    string encodedPasswordAnswer = this.GetEncodedPasswordAnswer(username, passwordAnswer);
    SecUtility.CheckParameter(ref encodedPasswordAnswer, this.RequiresQuestionAndAnswer, this.RequiresQuestionAndAnswer, false, 0x80, "passwordAnswer");
    int passwordFormat = 0;
    int status = 0;
    string pass = this.GetPasswordFromDB(username, encodedPasswordAnswer, this.RequiresQuestionAndAnswer, out passwordFormat, out status);
    if (pass != null)
    {
        return base.UnEncodePassword(pass, passwordFormat);
    }
    string exceptionText = this.GetExceptionText(status);
    if (this.IsStatusDueToBadPassword(status))
    {
        throw new MembershipPasswordException(exceptionText);
    }
    throw new ProviderException(exceptionText);
}

I guess the final answer is that I would expect some exception (preferably a MembershipPasswordException) indicating that the answer is incorrect as the MSDN document states.
[25 Aug 2008 13:47] Craig Ritchie
Sorry... looking back over the code I see where the proper exception is going to be thrown... but...

this line:
  this.UpdateFailureCount(userId, "PasswordAnswer", connection);
is failing so that this line:
  throw new MembershipPasswordException(Resources.IncorrectPasswordAnswer);
is never reached.
[28 Aug 2008 20:21] 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/52874
[28 Aug 2008 20:22] Reggie Burnett
fixed in 5.3.0
[5 Sep 2008 16:30] Tony Bedford
An entry has been added to the 5.3.0 changelog:

When called with an incorrect password the MembershipProvider.GetPassword() method threw a MySQLException instead of a MembershipPasswordException.