Description
Background
There have been a couple of active issues around NTLM/kerberos authentication and customizing it for various reasons (see discussions in #305 and #31), with an attempt of enabling this in #1379. This issue is to track an API proposal and the steps needed to integrate it into SqlClient.
NOTE:
I'm not sure of naming in this area and very open to better names for things. I haven't spent much time within the SSPI/GSS/NTLM/Kerberos/etc space and this proposal is based on my limited understanding of these concepts. If I'm missing anything important, please let me know
Proposal
The proposal is to add a provider that has the ability to generate a context given an incoming blob. There are currently a few ways that this is handled internally:
- A native implementation on all Windows platforms
- An internal managed implementation on .NET Standard/.NET 6
- System.Net.Security.NegotiateAuthentication on .NET 7+
As part of this change, those different implementations should be moved to follow the same pattern as an external implementer would have to ensure the abstraction is sufficient. After attempting to do so, it appears the best design would be to have a class that is used per connection (retrieved via a factory method) as there is some state that may need to be accessed between invocations:
+ /// <summary>
+ /// Defines a provider to supply and operate on an SSPI context as part of integrated authentication.
+ /// </summary>
+ public abstract class SspiClientContextProvider
+ {
+ /// <summary>
+ /// Generates a context given a (possibly empty) previously buffer.
+ /// </summary>
+ /// <param name="incomingBlob">The incoming blob (may be empty).</param>
+ /// <param name="outgoingBlobWriter">A writer that allows us to write data for the outgoing blob.</param>
+ protected abstract bool GenerateContext(ReadOnlySpan<byte> incomingBlob, IBufferWriter<byte> outgoingBlobWriter, SspiAuthenticationParameters authParams);
+ }
public class SqlConnection
{
+ /// <summary>
+ /// Gets or sets a factory to handle the SSPI context
+ /// </summary>
+ public Func<SspiClientContextProvider> SspiContextProviderFactory { get; set; }
}
Alternate designs
-
We could just return a byte[] - but this would most likely requrie creating new arrays for any authentication
public abstract class SspiClientContextProvider { - protected abstract void GenerateContext(ReadOnlySpan<byte> incomingBlob, IBufferWriter<byte> outgoingBlobWriter); + protected abstract byte[] GenerateContext(ReadOnlySpan<byte> incomingBlob); }
-
Or we could use an IMemoryOwner to track the lifetime of potentially pooled memory:
public abstract class SspiClientContextProvider { - protected abstract void GenerateContext(ReadOnlySpan<byte> incomingBlob, IBufferWriter<byte> outgoingBlobWriter); + protected abstract IMemoryOwner<byte> GenerateContext(ReadOnlySpan<byte> incomingBlob); }
Implementation/Testing
While prototyping this, I found that as-is, it would be a fairly large change. However, I think it should be broken into the following steps to make sure we don't regress anything.
- Consolidate SSPI context generation to single abstraction #2255
- Use memory/span for SSPI generation #2447
- Use an IBufferWriter<byte> to write the outgoing SSPI blob #2452
- Use string for SNI instead of byte[] #2790
- Expose SspiAuthenticationParameters on SspiContextProvider #2454
- Expose SSPI context provider as public #2494
Some follow up items:
- Ensure correct SPN when calling SspiContextProvider #3347
- Handle IDisposable on SspiContextProvider
-
serverSPNs
in the .NET CoreTdsParser
needs to be refactored as it shouldn't be just an array - it's a specific structure and should be a struct (see conversation at Ensure correct SPN when calling SspiContextProvider #3347 (comment))
The first four are mostly refactorings that I'm assuming can use current testing to ensure no regressions (please correct me if that is incorrect). For the final two, the main thing to test will be that a custom implementation is picked up and handled correctly, which can be done with unit tests. I also plan on testing the E2E of this API by enabling NTLM with this enabled hook.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status