Skip to content

Ensure correct SPN when calling SspiContextProvider #3347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jul 15, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -437,14 +437,14 @@ internal void Connect(ServerInfo serverInfo,

_connHandler.pendingSQLDNSObject = null;

string[] serverSpn = null;
string[] serverSpns = null;

// AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server
_physicalStateObj.CreatePhysicalSNIHandle(
serverInfo.ExtendedServerName,
timeout,
out instanceName,
ref serverSpn,
ref serverSpns,
false,
true,
fParallel,
Expand Down Expand Up @@ -542,7 +542,7 @@ internal void Connect(ServerInfo serverInfo,
serverInfo.ExtendedServerName,
timeout,
out instanceName,
ref serverSpn,
ref serverSpns,
true,
true,
fParallel,
Expand Down Expand Up @@ -593,7 +593,7 @@ internal void Connect(ServerInfo serverInfo,
}
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Prelogin handshake successful");

_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, serverSpn);
_authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, serverSpns);

if (_fMARS && marsCapable)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ protected override bool GenerateSspiClientContext(ReadOnlySpan<byte> incomingBlo

_negotiateAuth ??= new(new NegotiateAuthenticationClientOptions { Package = "Negotiate", TargetName = authParams.Resource });

Debug.Assert(_negotiateAuth.TargetName == authParams.Resource, "SSPI resource does not match TargetName");
Debug.Assert(_negotiateAuth.TargetName == authParams.Resource, "SSPI resource does not match TargetName. SspiContextProvider should ensure that once a target is established it will only call with that.");

var sendBuff = _negotiateAuth.GetOutgoingBlob(incomingBlob, out statusCode)!;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

Expand All @@ -12,10 +13,8 @@ internal abstract class SspiContextProvider
private TdsParser _parser = null!;
private ServerInfo _serverInfo = null!;

// This is used to store either a single or multiple SspiAuthenticationParameters. Since we initially have potentially
// multiple SPNs, we'll start with that. However, once we've succeeded creating an SSPI context, we'll consider that
// to be the correct SPN going forward
private object? _authParamValue;
private List<SspiAuthenticationParameters>? _authParams;
private SspiAuthenticationParameters? _authParam;

private protected TdsParserStateObject _physicalStateObj = null!;

Expand All @@ -35,9 +34,9 @@ string[] serverSpns
_serverInfo = serverInfo;

#if NETFRAMEWORK
_authParamValue = CreateAuthParams(serverSpn);
_authParam = CreateAuthParams(serverSpn);
#else
_authParamValue = serverSpns.Select(CreateAuthParams).ToArray();
_authParams = [.. serverSpns.Select(CreateAuthParams)];
#endif
Initialize();
}
Expand All @@ -52,25 +51,41 @@ internal void WriteSSPIContext(ReadOnlySpan<byte> receivedBuff, IBufferWriter<by
{
using var _ = TrySNIEventScope.Create(nameof(SspiContextProvider));

if (_authParamValue is SspiAuthenticationParameters authParam && RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, authParam))
if (TryRunSingle(receivedBuff, outgoingBlobWriter) || TryRunMultiple(receivedBuff, outgoingBlobWriter))
{
return;
// If we've hit here, the SSPI context provider implementation failed to generate the SSPI context.
SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT);
}
else if (_authParamValue is SspiAuthenticationParameters[] authParams)
}

/// <summary>
/// If we only have a single auth param, we know it's the correct one to use.
/// </summary>
private bool TryRunSingle(ReadOnlySpan<byte> receivedBuff, IBufferWriter<byte> outgoingBlobWriter)
{
return _authParam is { } && RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, _authParam);
}

/// <summary>
/// If we have multiple, we need to loop through them, and then identify the correct one for future use.
/// </summary>
private bool TryRunMultiple(ReadOnlySpan<byte> receivedBuff, IBufferWriter<byte> outgoingBlobWriter)
{
if (_authParams is { })
{
foreach (var p in authParams)
foreach (var authParam in _authParams)
{
if (RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, p))
if (RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, authParam))
{
// Reset the _authParams to only have a single one going forward to always call the context with that one
_authParamValue = p;
return;
_authParam = authParam;
_authParams = null;
return true;
}
}
}

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

private SspiAuthenticationParameters CreateAuthParams(string serverSpn)
Expand Down
Loading