Skip to content

Commit bc2a834

Browse files
authored
Consolidate SSPI context generation to single abstraction (#2255)
* Abstract the SSPI context generation This change introduces SSPIContextProvider that can generate payloads for SSPI. Specifically, this change plumbs the current SSPI context generation into this object, while later changes will continue to update the shape to be a more general purpose, public API.
1 parent 4778b87 commit bc2a834

14 files changed

+519
-616
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

+15
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,18 @@
519519
<Compile Include="$(CommonSourceRoot)System\Diagnostics\CodeAnalysis.cs">
520520
<Link>Common\System\Diagnostics\CodeAnalysis.cs</Link>
521521
</Compile>
522+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\ManagedSSPIContextProvider.cs">
523+
<Link>Microsoft\Data\SqlClient\SSPI\ManagedSSPIContextProvider.cs</Link>
524+
</Compile>
525+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\NegotiateSSPIContextProvider.cs">
526+
<Link>Microsoft\Data\SqlClient\SSPI\NegotiateSSPIContextProvider.cs</Link>
527+
</Compile>
528+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\SSPIContextProvider.cs">
529+
<Link>Microsoft\Data\SqlClient\SSPI\SSPIContextProvider.cs</Link>
530+
</Compile>
531+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\TdsParser.cs">
532+
<Link>Microsoft\Data\SqlClient\TdsParser.cs</Link>
533+
</Compile>
522534
<Compile Include="$(CommonSourceRoot)\Microsoft\Data\ProviderBase\DbReferenceCollection.cs">
523535
<Link>Microsoft\Data\ProviderBase\DbReferenceCollection.cs</Link>
524536
</Compile>
@@ -771,6 +783,9 @@
771783
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs">
772784
<Link>Common\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs</Link>
773785
</Compile>
786+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs">
787+
<Link>Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs</Link>
788+
</Compile>
774789
<Compile Include="Interop\SNINativeMethodWrapper.Windows.cs" />
775790
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.Windows.cs" />
776791
<Compile Include="Microsoft\Data\SqlClient\LocalDBAPI.Windows.cs" />

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

+1-33
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ namespace Microsoft.Data.SqlClient
1010
{
1111
internal sealed partial class TdsParser
1212
{
13-
private static volatile bool s_fSSPILoaded = false; // bool to indicate whether library has been loaded
14-
1513
internal void PostReadAsyncForMars()
1614
{
1715
if (TdsParserStateObjectFactory.UseManagedSNI)
@@ -43,37 +41,7 @@ internal void PostReadAsyncForMars()
4341
_physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
4442
ThrowExceptionAndWarning(_physicalStateObj);
4543
}
46-
}
47-
48-
private void LoadSSPILibrary()
49-
{
50-
if (TdsParserStateObjectFactory.UseManagedSNI)
51-
return;
52-
// Outer check so we don't acquire lock once it's loaded.
53-
if (!s_fSSPILoaded)
54-
{
55-
lock (s_tdsParserLock)
56-
{
57-
// re-check inside lock
58-
if (!s_fSSPILoaded)
59-
{
60-
// use local for ref param to defer setting s_maxSSPILength until we know the call succeeded.
61-
uint maxLength = 0;
62-
63-
if (0 != SNINativeMethodWrapper.SNISecInitPackage(ref maxLength))
64-
SSPIError(SQLMessage.SSPIInitializeError(), TdsEnums.INIT_SSPI_PACKAGE);
65-
66-
s_maxSSPILength = maxLength;
67-
s_fSSPILoaded = true;
68-
}
69-
}
70-
}
71-
72-
if (s_maxSSPILength > int.MaxValue)
73-
{
74-
throw SQL.InvalidSSPIPacketSize(); // SqlBu 332503
75-
}
76-
}
44+
}
7745

7846
private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersion)
7947
{

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

+12-246
Large diffs are not rendered by default.

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ internal TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalCon
6262
AddError(parser.ProcessSNIError(this));
6363
ThrowExceptionAndWarning();
6464
}
65-
65+
6666
// we post a callback that represents the call to dispose; once the
6767
// object is disposed, the next callback will cause the GC Handle to
6868
// be released.
@@ -75,6 +75,7 @@ internal TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalCon
7575
////////////////
7676
internal abstract uint DisableSsl();
7777

78+
internal abstract SSPIContextProvider CreateSSPIContextProvider();
7879

7980
internal abstract uint EnableMars(ref uint info);
8081

@@ -83,6 +84,8 @@ internal abstract uint Status
8384
get;
8485
}
8586

87+
internal abstract Guid? SessionId { get; }
88+
8689
internal abstract SessionHandle SessionHandle
8790
{
8891
get;
@@ -236,8 +239,6 @@ internal abstract void CreatePhysicalSNIHandle(
236239

237240
protected abstract void RemovePacketFromPendingList(PacketHandle pointer);
238241

239-
internal abstract uint GenerateSspiClientContext(byte[] receivedBuff, uint receivedLength, ref byte[] sendBuff, ref uint sendLength, byte[][] _sniSpnBuffer);
240-
241242
internal int DecrementPendingCallbacks(bool release)
242243
{
243244
int remaining = Interlocked.Decrement(ref _pendingCallbacks);

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

+10-29
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ internal sealed class TdsParserStateObjectManaged : TdsParserStateObject
2121
{
2222
private SNIMarsConnection? _marsConnection;
2323
private SNIHandle? _sessionHandle;
24-
#if NET7_0_OR_GREATER
25-
private NegotiateAuthentication? _negotiateAuth = null;
26-
#else
27-
private SspiClientContextStatus? _sspiClientContextStatus;
28-
#endif
24+
2925
public TdsParserStateObjectManaged(TdsParser parser) : base(parser) { }
3026

3127
internal TdsParserStateObjectManaged(TdsParser parser, TdsParserStateObject physicalConnection, bool async) :
@@ -232,6 +228,8 @@ internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint
232228

233229
protected override PacketHandle EmptyReadPacket => PacketHandle.FromManagedPacket(null);
234230

231+
internal override Guid? SessionId => _sessionHandle?.ConnectionId;
232+
235233
internal override bool IsPacketEmpty(PacketHandle packet) => packet.ManagedPacket == null;
236234

237235
internal override void ReleasePacket(PacketHandle syncReadPacket)
@@ -389,30 +387,6 @@ internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize)
389387
return TdsEnums.SNI_SUCCESS;
390388
}
391389

392-
internal override uint GenerateSspiClientContext(byte[] receivedBuff,
393-
uint receivedLength,
394-
ref byte[] sendBuff,
395-
ref uint sendLength,
396-
byte[][] _sniSpnBuffer)
397-
{
398-
#if NET7_0_OR_GREATER
399-
_negotiateAuth ??= new(new NegotiateAuthenticationClientOptions { Package = "Negotiate", TargetName = Encoding.Unicode.GetString(_sniSpnBuffer[0]) });
400-
sendBuff = _negotiateAuth.GetOutgoingBlob(receivedBuff, out NegotiateAuthenticationStatusCode statusCode)!;
401-
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.GenerateSspiClientContext | Info | Session Id {0}, StatusCode={1}", _sessionHandle?.ConnectionId, statusCode);
402-
if (statusCode is not NegotiateAuthenticationStatusCode.Completed and not NegotiateAuthenticationStatusCode.ContinueNeeded)
403-
{
404-
throw new InvalidOperationException(SQLMessage.SSPIGenerateError() + Environment.NewLine + statusCode);
405-
}
406-
#else
407-
_sspiClientContextStatus ??= new SspiClientContextStatus();
408-
409-
SNIProxy.GenSspiClientContext(_sspiClientContextStatus, receivedBuff, ref sendBuff, _sniSpnBuffer);
410-
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.GenerateSspiClientContext | Info | Session Id {0}", _sessionHandle?.ConnectionId);
411-
#endif
412-
sendLength = (uint)(sendBuff != null ? sendBuff.Length : 0);
413-
return 0;
414-
}
415-
416390
internal override uint WaitForSSLHandShakeToComplete(out int protocolVersion)
417391
{
418392
protocolVersion = GetSessionSNIHandleHandleOrThrow().ProtocolVersion;
@@ -432,5 +406,12 @@ private SNIHandle GetSessionSNIHandleHandleOrThrow()
432406
[DoesNotReturn]
433407
[MethodImpl(MethodImplOptions.NoInlining)] // this forces the exception throwing code not to be inlined for performance
434408
private void ThrowClosedConnection() => throw ADP.ClosedConnectionError();
409+
410+
internal override SSPIContextProvider CreateSSPIContextProvider()
411+
#if NET7_0_OR_GREATER
412+
=> new NegotiateSSPIContextProvider();
413+
#else
414+
=> new ManagedSSPIContextProvider();
415+
#endif
435416
}
436417
}

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ internal override void CreatePhysicalSNIHandle(
167167
byte[] srvSPN = Encoding.Unicode.GetBytes(serverSPN);
168168
Trace.Assert(srvSPN.Length <= SNINativeMethodWrapper.SniMaxComposedSpnLength, "Length of the provided SPN exceeded the buffer size.");
169169
spnBuffer[0] = srvSPN;
170-
SqlClientEventSource.Log.TryTraceEvent("<{0}.{1}|SEC> Server SPN `{2}` from the connection string is used.",nameof(TdsParserStateObjectNative), nameof(CreatePhysicalSNIHandle), serverSPN);
170+
SqlClientEventSource.Log.TryTraceEvent("<{0}.{1}|SEC> Server SPN `{2}` from the connection string is used.", nameof(TdsParserStateObjectNative), nameof(CreatePhysicalSNIHandle), serverSPN);
171171
}
172172
else
173173
{
@@ -272,6 +272,8 @@ internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint
272272

273273
protected override PacketHandle EmptyReadPacket => PacketHandle.FromNativePointer(default);
274274

275+
internal override Guid? SessionId => default;
276+
275277
internal override bool IsPacketEmpty(PacketHandle readPacket)
276278
{
277279
Debug.Assert(readPacket.Type == PacketHandle.NativePointerType || readPacket.Type == 0, "unexpected packet type when requiring NativePointer");
@@ -398,9 +400,6 @@ internal override uint EnableSsl(ref uint info, bool tlsFirst, string serverCert
398400
internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize)
399401
=> SNINativeMethodWrapper.SNISetInfo(Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize);
400402

401-
internal override uint GenerateSspiClientContext(byte[] receivedBuff, uint receivedLength, ref byte[] sendBuff, ref uint sendLength, byte[][] _sniSpnBuffer)
402-
=> SNINativeMethodWrapper.SNISecGenClientContext(Handle, receivedBuff, receivedLength, sendBuff, ref sendLength, _sniSpnBuffer[0]);
403-
404403
internal override uint WaitForSSLHandShakeToComplete(out int protocolVersion)
405404
{
406405
uint returnValue = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(Handle, GetTimeoutRemaining(), out uint nativeProtocolVersion);
@@ -452,6 +451,8 @@ internal override void DisposePacketCache()
452451
}
453452
}
454453

454+
internal override SSPIContextProvider CreateSSPIContextProvider() => new NativeSSPIContextProvider();
455+
455456
internal sealed class WritePacketCache : IDisposable
456457
{
457458
private bool _disposed;

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

+16-1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,21 @@
153153
<Compile Include="$(CommonSourceRoot)Microsoft\Data\ProviderBase\TimeoutTimer.cs">
154154
<Link>Microsoft\Data\ProviderBase\TimeoutTimer.cs</Link>
155155
</Compile>
156+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\ManagedSSPIContextProvider.cs">
157+
<Link>Microsoft\Data\SqlClient\SSPI\ManagedSSPIContextProvider.cs</Link>
158+
</Compile>
159+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs">
160+
<Link>Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs</Link>
161+
</Compile>
162+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\NegotiateSSPIContextProvider.cs">
163+
<Link>Microsoft\Data\SqlClient\SSPI\NegotiateSSPIContextProvider.cs</Link>
164+
</Compile>
165+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SSPI\SSPIContextProvider.cs">
166+
<Link>Microsoft\Data\SqlClient\SSPI\SSPIContextProvider.cs</Link>
167+
</Compile>
168+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\TdsParser.cs">
169+
<Link>Microsoft\Data\SqlClient\TdsParser.cs</Link>
170+
</Compile>
156171
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Sql\SqlDataSourceEnumerator.cs">
157172
<Link>Microsoft\Data\Sql\SqlDataSourceEnumerator.cs</Link>
158173
</Compile>
@@ -751,4 +766,4 @@
751766
<Import Project="$(NetFxSource)tools\targets\GenerateThisAssemblyCs.targets" />
752767
<Import Project="$(NetFxSource)tools\targets\GenerateAssemblyRef.targets" />
753768
<Import Project="$(NetFxSource)tools\targets\GenerateAssemblyInfo.targets" />
754-
</Project>
769+
</Project>

0 commit comments

Comments
 (0)