Skip to content

Commit a6ef565

Browse files
mrotteveelcincuranet
authored andcommitted
Add support for case sensitive user names (DNET-851)
1 parent ca896e7 commit a6ef565

File tree

4 files changed

+126
-4
lines changed

4 files changed

+126
-4
lines changed

Provider/src/FirebirdSql.Data.FirebirdClient.Tests/FbConnectionTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,47 @@ public void DoNotGoBackToPoolAfterBroken()
401401
}
402402
}
403403

404+
[Test]
405+
public void CaseSensitiveLogin()
406+
{
407+
if (!EnsureVersion(new Version("3.0.0.0")))
408+
return;
409+
410+
var connectionString = BuildConnectionString(FbServerType, Compression);
411+
using (var conn = new FbConnection(connectionString))
412+
{
413+
conn.Open();
414+
using (var cmd = conn.CreateCommand())
415+
{
416+
cmd.CommandText = "create or alter user \"CaseSensitive\" password 'password' using plugin Srp";
417+
cmd.ExecuteNonQuery();
418+
}
419+
420+
var csBuilder = new FbConnectionStringBuilder(connectionString)
421+
{
422+
Pooling = false,
423+
UserID = "\"CaseSensitive\"",
424+
Password = "password"
425+
};
426+
try
427+
{
428+
using (var conn2 = new FbConnection(csBuilder.ToString()))
429+
//using (var conn2 = new FbConnection("user id='\"CaseSensitive\"';..."))
430+
{
431+
conn2.Open();
432+
}
433+
}
434+
finally
435+
{
436+
using (var cmd = conn.CreateCommand())
437+
{
438+
cmd.CommandText = "drop user \"CaseSensitive\" using plugin Srp";
439+
cmd.ExecuteNonQuery();
440+
}
441+
}
442+
}
443+
}
444+
404445
#endregion
405446

406447
#region Methods
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using NUnit.Framework;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace FirebirdSql.Data.FirebirdClient.Tests
9+
{
10+
[TestFixture]
11+
class GdsConnectionTests
12+
{
13+
[Test, TestCaseSource(typeof(NormalizeLoginTestData), "TestCases")]
14+
public string NormalizeLoginTest(string login)
15+
{
16+
return Client.Managed.GdsConnection.NormalizeLogin(login);
17+
}
18+
}
19+
20+
class NormalizeLoginTestData
21+
{
22+
public static IEnumerable<TestCaseData> TestCases
23+
{
24+
get
25+
{
26+
yield return new TestCaseData("sysdba").Returns("SYSDBA");
27+
yield return new TestCaseData("s").Returns("S");
28+
yield return new TestCaseData("\"CaseSensitive\"").Returns("CaseSensitive");
29+
yield return new TestCaseData("\"s\"").Returns("s");
30+
yield return new TestCaseData("\"With\"\"EscapedQuote\"").Returns("With\"EscapedQuote");
31+
yield return new TestCaseData("\"Invalid\"Escape\"").Returns("Invalid");
32+
yield return new TestCaseData("\"DanglingInvalidEscape\"\"").Returns("DanglingInvalidEscape");
33+
yield return new TestCaseData("\"EscapedQuoteAtEnd\"\"\"").Returns("EscapedQuoteAtEnd\"");
34+
yield return new TestCaseData("\"StartNoEndQuote").Returns("\"STARTNOENDQUOTE");
35+
yield return new TestCaseData("\"\"").Returns("\"\"");
36+
yield return new TestCaseData("").Returns("");
37+
yield return new TestCaseData(null).Returns(null);
38+
}
39+
}
40+
}
41+
}

Provider/src/FirebirdSql.Data.FirebirdClient/Client/Managed/GdsConnection.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public void Identify(string database)
187187
switch (acceptPluginName)
188188
{
189189
case SrpClient.PluginName:
190-
_authData = Encoding.ASCII.GetBytes(_srp.ClientProof(_userID, _password, data).ToHexString());
190+
_authData = Encoding.ASCII.GetBytes(_srp.ClientProof(NormalizeLogin(_userID), _password, data).ToHexString());
191191
break;
192192
case SspiHelper.PluginName:
193193
_authData = _sspi.GetClientSecurity(data);
@@ -385,6 +385,46 @@ private static void WriteMultiPartHelper(Stream stream, byte code, byte[] data)
385385
}
386386
}
387387

388+
public static string NormalizeLogin(string login)
389+
{
390+
if (string.IsNullOrEmpty(login))
391+
{
392+
return login;
393+
}
394+
if (login.Length > 2 && login[0] == '"' && login[login.Length - 1] == '"')
395+
{
396+
return NormalizeQuotedLogin(login);
397+
}
398+
return login.ToUpperInvariant();
399+
}
400+
401+
private static string NormalizeQuotedLogin(string login)
402+
{
403+
var sb = new StringBuilder(login, 1, login.Length - 2, login.Length - 2);
404+
for (int idx = 0; idx < sb.Length; idx++)
405+
{
406+
// Double double quotes ("") escape a double quote in a quoted string
407+
if (sb[idx] == '"')
408+
{
409+
// Strip double quote escape
410+
sb.Remove(idx, 1);
411+
if (idx < sb.Length && sb[idx] == '"')
412+
{
413+
// Retain escaped double quote
414+
idx += 1;
415+
}
416+
else
417+
{
418+
// The character after escape is not a double quote, we terminate the conversion and truncate.
419+
// Firebird does this as well (see common/utils.cpp#dpbItemUpper)
420+
sb.Length = idx;
421+
return sb.ToString();
422+
}
423+
}
424+
}
425+
return sb.ToString();
426+
}
427+
388428
#endregion
389429
}
390430
}

Provider/src/FirebirdSql.Data.FirebirdClient/Client/Managed/SrpClient.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public byte[] ClientProof(string user, string password, byte[] salt, BigInteger
5959
var n2 = BigIntegerFromByteArray(ComputeHash(BigIntegerToByteArray(g)));
6060

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

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

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

134134
private static BigInteger GetUserHash(string user, string password, byte[] salt)
135135
{
136-
var userBytes = Encoding.UTF8.GetBytes(user.ToUpper());
136+
var userBytes = Encoding.UTF8.GetBytes(user);
137137
var passwordBytes = Encoding.UTF8.GetBytes(password);
138138
var hash1 = ComputeHash(userBytes, SEPARATOR_BYTES, passwordBytes);
139139
var hash2 = ComputeHash(salt, hash1);

0 commit comments

Comments
 (0)