Skip to content

Add hook for custom SSPI context for SqlConnection instances #2253

Open
@twsouthwick

Description

@twsouthwick

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.

Some follow up items:

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

No one assigned

    Labels

    Enhancement 💡Issues that are feature requests for the drivers we maintain.Public API 🆕Issues/PRs that introduce new APIs to the driver.Up-for-Grabs 🙌Issues that are ready to be picked up for anyone interested. Please self-assign and remove the label

    Type

    No type

    Projects

    Status

    Needs Investigation

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions