Skip to content

Commit

Permalink
Add support for case sensitive user names (DNET-851)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrotteveel authored and cincuranet committed Oct 8, 2018
1 parent ca896e7 commit a6ef565
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 4 deletions.
Expand Up @@ -401,6 +401,47 @@ public void DoNotGoBackToPoolAfterBroken()
}
}

[Test]
public void CaseSensitiveLogin()
{
if (!EnsureVersion(new Version("3.0.0.0")))
return;

var connectionString = BuildConnectionString(FbServerType, Compression);
using (var conn = new FbConnection(connectionString))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "create or alter user \"CaseSensitive\" password 'password' using plugin Srp";
cmd.ExecuteNonQuery();
}

var csBuilder = new FbConnectionStringBuilder(connectionString)
{
Pooling = false,
UserID = "\"CaseSensitive\"",
Password = "password"
};
try
{
using (var conn2 = new FbConnection(csBuilder.ToString()))
//using (var conn2 = new FbConnection("user id='\"CaseSensitive\"';..."))
{
conn2.Open();
}
}
finally
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "drop user \"CaseSensitive\" using plugin Srp";
cmd.ExecuteNonQuery();
}
}
}
}

#endregion

#region Methods
Expand Down
@@ -0,0 +1,41 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FirebirdSql.Data.FirebirdClient.Tests
{
[TestFixture]
class GdsConnectionTests
{
[Test, TestCaseSource(typeof(NormalizeLoginTestData), "TestCases")]
public string NormalizeLoginTest(string login)
{
return Client.Managed.GdsConnection.NormalizeLogin(login);
}
}

class NormalizeLoginTestData
{
public static IEnumerable<TestCaseData> TestCases
{
get
{
yield return new TestCaseData("sysdba").Returns("SYSDBA");
yield return new TestCaseData("s").Returns("S");
yield return new TestCaseData("\"CaseSensitive\"").Returns("CaseSensitive");
yield return new TestCaseData("\"s\"").Returns("s");
yield return new TestCaseData("\"With\"\"EscapedQuote\"").Returns("With\"EscapedQuote");
yield return new TestCaseData("\"Invalid\"Escape\"").Returns("Invalid");
yield return new TestCaseData("\"DanglingInvalidEscape\"\"").Returns("DanglingInvalidEscape");
yield return new TestCaseData("\"EscapedQuoteAtEnd\"\"\"").Returns("EscapedQuoteAtEnd\"");
yield return new TestCaseData("\"StartNoEndQuote").Returns("\"STARTNOENDQUOTE");
yield return new TestCaseData("\"\"").Returns("\"\"");
yield return new TestCaseData("").Returns("");
yield return new TestCaseData(null).Returns(null);
}
}
}
}
Expand Up @@ -187,7 +187,7 @@ public void Identify(string database)
switch (acceptPluginName)
{
case SrpClient.PluginName:
_authData = Encoding.ASCII.GetBytes(_srp.ClientProof(_userID, _password, data).ToHexString());
_authData = Encoding.ASCII.GetBytes(_srp.ClientProof(NormalizeLogin(_userID), _password, data).ToHexString());
break;
case SspiHelper.PluginName:
_authData = _sspi.GetClientSecurity(data);
Expand Down Expand Up @@ -385,6 +385,46 @@ private static void WriteMultiPartHelper(Stream stream, byte code, byte[] data)
}
}

public static string NormalizeLogin(string login)
{
if (string.IsNullOrEmpty(login))
{
return login;
}
if (login.Length > 2 && login[0] == '"' && login[login.Length - 1] == '"')
{
return NormalizeQuotedLogin(login);
}
return login.ToUpperInvariant();
}

private static string NormalizeQuotedLogin(string login)
{
var sb = new StringBuilder(login, 1, login.Length - 2, login.Length - 2);
for (int idx = 0; idx < sb.Length; idx++)
{
// Double double quotes ("") escape a double quote in a quoted string
if (sb[idx] == '"')
{
// Strip double quote escape
sb.Remove(idx, 1);
if (idx < sb.Length && sb[idx] == '"')
{
// Retain escaped double quote
idx += 1;
}
else
{
// The character after escape is not a double quote, we terminate the conversion and truncate.
// Firebird does this as well (see common/utils.cpp#dpbItemUpper)
sb.Length = idx;
return sb.ToString();
}
}
}
return sb.ToString();
}

#endregion
}
}
Expand Up @@ -59,7 +59,7 @@ public byte[] ClientProof(string user, string password, byte[] salt, BigInteger
var n2 = BigIntegerFromByteArray(ComputeHash(BigIntegerToByteArray(g)));

n1 = BigInteger.ModPow(n1, n2, N);
n2 = BigIntegerFromByteArray(ComputeHash(Encoding.UTF8.GetBytes(user.ToUpper())));
n2 = BigIntegerFromByteArray(ComputeHash(Encoding.UTF8.GetBytes(user)));
var M = ComputeHash(BigIntegerToByteArray(n1), BigIntegerToByteArray(n2), salt, BigIntegerToByteArray(PublicKey), BigIntegerToByteArray(serverPublicKey), K);

SessionKey = K;
Expand All @@ -80,7 +80,7 @@ public byte[] ClientProof(string user, string password, byte[] authData)
Array.Copy(authData, serverKeyStart, hexServerPublicKey, 0, serverKeyLength);
var hexServerPublicKeyString = Encoding.UTF8.GetString(hexServerPublicKey);
var serverPublicKey = BigInteger.Parse($"00{hexServerPublicKeyString}", NumberStyles.HexNumber);
return ClientProof(user.ToUpper(), password, salt, serverPublicKey);
return ClientProof(user, password, salt, serverPublicKey);
}

public Tuple<BigInteger, BigInteger> ServerSeed(string user, string password, byte[] salt)
Expand Down Expand Up @@ -133,7 +133,7 @@ private byte[] GetClientSessionKey(string user, string password, byte[] salt, Bi

private static BigInteger GetUserHash(string user, string password, byte[] salt)
{
var userBytes = Encoding.UTF8.GetBytes(user.ToUpper());
var userBytes = Encoding.UTF8.GetBytes(user);
var passwordBytes = Encoding.UTF8.GetBytes(password);
var hash1 = ComputeHash(userBytes, SEPARATOR_BYTES, passwordBytes);
var hash2 = ComputeHash(salt, hash1);
Expand Down

0 comments on commit a6ef565

Please sign in to comment.