Skip to content

Commit b375f91

Browse files
committed
Reset negotiateAuth if SNI doesn't work
This change also adds some book keeping to ensure we're only using the spn that has previously generated a context once one has been created.
1 parent feec5a7 commit b375f91

File tree

5 files changed

+53
-23
lines changed

5 files changed

+53
-23
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ internal void Connect(ServerInfo serverInfo,
459459
hostNameInCertificate,
460460
serverCertificateFilename);
461461

462-
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this);
462+
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, _serverSpn);
463463

464464
if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
465465
{
@@ -559,7 +559,7 @@ internal void Connect(ServerInfo serverInfo,
559559
hostNameInCertificate,
560560
serverCertificateFilename);
561561

562-
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this);
562+
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, _serverSpn);
563563

564564
if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
565565
{

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ internal void Connect(ServerInfo serverInfo,
518518
FQDNforDNSCache,
519519
hostNameInCertificate);
520520

521-
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this);
521+
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, _serverSpn);
522522

523523
if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
524524
{
@@ -612,7 +612,7 @@ internal void Connect(ServerInfo serverInfo,
612612
serverInfo.ResolvedServerName,
613613
hostNameInCertificate);
614614

615-
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this);
615+
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, _serverSpn);
616616

617617
if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
618618
{

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SSPI/NegotiateSspiContextProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System;
44
using System.Buffers;
5+
using System.Diagnostics;
56
using System.Net.Security;
67

78
#nullable enable
@@ -17,6 +18,9 @@ protected override bool GenerateSspiClientContext(ReadOnlySpan<byte> incomingBlo
1718
NegotiateAuthenticationStatusCode statusCode = NegotiateAuthenticationStatusCode.UnknownCredentials;
1819

1920
_negotiateAuth ??= new(new NegotiateAuthenticationClientOptions { Package = "Negotiate", TargetName = authParams.Resource });
21+
22+
Debug.Assert(_negotiateAuth.TargetName == authParams.Resource, "SSPI resource does not match TargetName");
23+
2024
var sendBuff = _negotiateAuth.GetOutgoingBlob(incomingBlob, out statusCode)!;
2125

2226
// Log session id, status code and the actual SPN used in the negotiation
@@ -29,6 +33,8 @@ protected override bool GenerateSspiClientContext(ReadOnlySpan<byte> incomingBlo
2933
return true;
3034
}
3135

36+
// Reset _negotiateAuth to be generated again for next SPN.
37+
_negotiateAuth = null;
3238
return false;
3339
}
3440
}

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SSPI/SspiContextProvider.cs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Buffers;
33
using System.Diagnostics;
4+
using System.Linq;
45

56
#nullable enable
67

@@ -10,14 +11,34 @@ internal abstract class SspiContextProvider
1011
{
1112
private TdsParser _parser = null!;
1213
private ServerInfo _serverInfo = null!;
14+
15+
// This is used to store either a single or multiple SspiAuthenticationParameters. Since we initially have potential
16+
// multiple SPNs, we'll start with that. However, once we've succeeded creating an SSPI context, we'll consider that
17+
// to be the correct SPN going forward
18+
private object? _authParams;
19+
1320
private protected TdsParserStateObject _physicalStateObj = null!;
1421

15-
internal void Initialize(ServerInfo serverInfo, TdsParserStateObject physicalStateObj, TdsParser parser)
22+
internal void Initialize(
23+
ServerInfo serverInfo,
24+
TdsParserStateObject physicalStateObj,
25+
TdsParser parser,
26+
#if NETFRAMEWORK
27+
string serverSpn
28+
#else
29+
string[] serverSpns
30+
#endif
31+
)
1632
{
1733
_parser = parser;
1834
_physicalStateObj = physicalStateObj;
1935
_serverInfo = serverInfo;
2036

37+
#if NETFRAMEWORK
38+
_authParams = CreateAuthParams(serverSpn);
39+
#else
40+
_authParams = serverSpns.Select(CreateAuthParams).ToArray();
41+
#endif
2142
Initialize();
2243
}
2344

@@ -27,46 +48,49 @@ private protected virtual void Initialize()
2748

2849
protected abstract bool GenerateSspiClientContext(ReadOnlySpan<byte> incomingBlob, IBufferWriter<byte> outgoingBlobWriter, SspiAuthenticationParameters authParams);
2950

30-
internal void SSPIData(ReadOnlySpan<byte> receivedBuff, IBufferWriter<byte> outgoingBlobWriter, string serverSpn)
51+
internal void SSPIData(ReadOnlySpan<byte> receivedBuff, IBufferWriter<byte> outgoingBlobWriter)
3152
{
3253
using var _ = TrySNIEventScope.Create(nameof(SspiContextProvider));
3354

34-
if (!RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, serverSpn))
55+
if (_authParams is SspiAuthenticationParameters authParam)
3556
{
36-
// If we've hit here, the SSPI context provider implementation failed to generate the SSPI context.
37-
SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT);
57+
RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, authParam);
58+
return;
3859
}
39-
}
40-
41-
internal void SSPIData(ReadOnlySpan<byte> receivedBuff, IBufferWriter<byte> outgoingBlobWriter, ReadOnlySpan<string> serverSpns)
42-
{
43-
using var _ = TrySNIEventScope.Create(nameof(SspiContextProvider));
44-
45-
foreach (var serverSpn in serverSpns)
60+
else if (_authParams is SspiAuthenticationParameters[] authParams)
4661
{
47-
if (RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, serverSpn))
62+
foreach (var p in authParams)
4863
{
49-
return;
64+
if (RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, p))
65+
{
66+
// Reset the _authParams to only have a single one going forward to always call the context with that one
67+
_authParams = p;
68+
return;
69+
}
5070
}
5171
}
5272

5373
// If we've hit here, the SSPI context provider implementation failed to generate the SSPI context.
5474
SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT);
5575
}
5676

57-
private bool RunGenerateSspiClientContext(ReadOnlySpan<byte> incomingBlob, IBufferWriter<byte> outgoingBlobWriter, string serverSpn)
77+
private SspiAuthenticationParameters CreateAuthParams(string serverSpn)
5878
{
5979
var options = _parser.Connection.ConnectionOptions;
60-
var authParams = new SspiAuthenticationParameters(options.DataSource, serverSpn)
80+
81+
return new SspiAuthenticationParameters(options.DataSource, serverSpn)
6182
{
6283
DatabaseName = options.InitialCatalog,
6384
UserId = options.UserID,
6485
Password = options.Password,
6586
};
87+
}
6688

89+
private bool RunGenerateSspiClientContext(ReadOnlySpan<byte> incomingBlob, IBufferWriter<byte> outgoingBlobWriter, SspiAuthenticationParameters authParams)
90+
{
6791
try
6892
{
69-
SqlClientEventSource.Log.TryTraceEvent("{0}.{1} | Info | SPN={1}", GetType().FullName, nameof(GenerateSspiClientContext), serverSpn);
93+
SqlClientEventSource.Log.TryTraceEvent("{0}.{1} | Info | SPN={1}", GetType().FullName, nameof(GenerateSspiClientContext), authParams.Resource);
7094

7195
return GenerateSspiClientContext(incomingBlob, outgoingBlobWriter, authParams);
7296
}

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal void ProcessSSPI(int receivedLength)
3535
try
3636
{
3737
// make call for SSPI data
38-
_authenticationProvider!.SSPIData(receivedBuff.AsSpan(0, receivedLength), writer, _serverSpn);
38+
_authenticationProvider!.SSPIData(receivedBuff.AsSpan(0, receivedLength), writer);
3939

4040
// DO NOT SEND LENGTH - TDS DOC INCORRECT! JUST SEND SSPI DATA!
4141
_physicalStateObj.WriteByteSpan(writer.WrittenSpan);
@@ -175,7 +175,7 @@ internal void TdsLogin(
175175
// byte[] buffer and 0 for the int length.
176176
Debug.Assert(SniContext.Snix_Login == _physicalStateObj.SniContext, $"Unexpected SniContext. Expecting Snix_Login, actual value is '{_physicalStateObj.SniContext}'");
177177
_physicalStateObj.SniContext = SniContext.Snix_LoginSspi;
178-
_authenticationProvider.SSPIData(ReadOnlySpan<byte>.Empty, sspiWriter, _serverSpn);
178+
_authenticationProvider.SSPIData(ReadOnlySpan<byte>.Empty, sspiWriter);
179179

180180
_physicalStateObj.SniContext = SniContext.Snix_Login;
181181

0 commit comments

Comments
 (0)