Skip to content

Commit d5621d5

Browse files
committed
Expose SSPI context provider as public
1 parent 42146a3 commit d5621d5

File tree

19 files changed

+275
-33
lines changed

19 files changed

+275
-33
lines changed

doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2133,6 +2133,19 @@ The following sample tries to open a connection to an invalid database to simula
21332133
Returns 0 if the connection is inactive on the client side.
21342134
</remarks>
21352135
</ServerProcessId>
2136+
<SspiContextProvider>
2137+
<summary>
2138+
Gets or sets the <see cref="SspiContextProvider"/> instance for customizing the SSPI context. If not set, the default for the platform will be used.
2139+
</summary>
2140+
<value>
2141+
An <see cref="T:Micorosft.Data.SqlClient.SspiContextProvider" /> instance.
2142+
</value>
2143+
<remarks>
2144+
<para>
2145+
The SspiContextProvider is a part of the connection pool key. Care should be taken when using this property to ensure the implementation returns a stable identity per resource.
2146+
</para>
2147+
</remarks>
2148+
</SspiContextProvider>
21362149
<State>
21372150
<summary>
21382151
Indicates the state of the <see cref="T:Microsoft.Data.SqlClient.SqlConnection" /> during the most recent network operation performed on the connection.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0"?>
2+
<docs>
3+
<members name="SspiAuthenticationParameters">
4+
<SspiAuthenticationParameters>
5+
<summary>Provides parameters used during SSPI authentication.</summary>
6+
</SspiAuthenticationParameters>
7+
<ctor>
8+
<summary>Creates an instance of the SspiAuthenticationParameters.</summary>
9+
<param name="serverName">The name of the server.</param>
10+
<param name="resource">The resource (often the server service principal name).</param>
11+
</ctor>
12+
<Resource>
13+
<summary>Gets the resource (often the server service principal name).</summary>
14+
</Resource>
15+
<ServerName>
16+
<summary>Gets the server name.</summary>
17+
</ServerName>
18+
<UserId>
19+
<summary>Gets or sets the user id if available.</summary>
20+
</UserId>
21+
<DatabaseName>
22+
<summary>Gets or sets the database name if available.</summary>
23+
</DatabaseName>
24+
<Password>
25+
<summary>Gets or sets the password if available.</summary>
26+
</Password>
27+
</members>
28+
</docs>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0"?>
2+
<docs>
3+
<members name="SspiContextProvider">
4+
<SspiContextProvider>
5+
<summary>Provides the ability to customize SSPI context generation.</summary>
6+
</SspiContextProvider>
7+
<ctor>
8+
<summary>Creates an instance of the SSPIContextProvider.</summary>
9+
</ctor>
10+
<GenerateContext>
11+
<summary>Generates an SSPI outgoing blob given the incoming blob.</summary>
12+
<param name="incomingBlob">Incoming blob</param>
13+
<param name="outgoingBlobWriter">Outgoing blob</param>
14+
<param name="authParams">Gets the authentication parameters associated with this connection.</param>
15+
<returns>
16+
<c>true</c> if the context was generated, otherwise <c>false</c>.
17+
</returns>
18+
</GenerateContext>
19+
</members>
20+
</docs>

src/Microsoft.Data.SqlClient.sln

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient",
149149
..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml
150150
..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml
151151
..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml
152+
..\doc\snippets\Microsoft.Data.SqlClient\SspiAuthenticationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SspiAuthenticationParameters.xml
153+
..\doc\snippets\Microsoft.Data.SqlClient\SspiContextProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SspiContextProvider.xml
152154
EndProjectSection
153155
EndProject
154156
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.DataClassification", "Microsoft.Data.SqlClient.DataClassification", "{5D1F0032-7B0D-4FB6-A969-FCFB25C9EA1D}"

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,8 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collect
929929
[System.ComponentModel.BrowsableAttribute(false)]
930930
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
931931
public Microsoft.Data.SqlClient.SqlCredential Credential { get { throw null; } set { } }
932+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/SspiContextProvider/*' />
933+
public SspiContextProvider SspiContextProvider { get { throw null; } set { } }
932934
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Database/*'/>
933935
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
934936
public override string Database { get { throw null; } }
@@ -1976,6 +1978,37 @@ public sealed class SqlConfigurableRetryFactory
19761978
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml' path='docs/members[@name="SqlConfigurableRetryFactory"]/CreateNoneRetryProvider/*' />
19771979
public static SqlRetryLogicBaseProvider CreateNoneRetryProvider() { throw null; }
19781980
}
1981+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiContextProvider.xml' path='docs/members[@name="SspiContextProvider"]/SspiContextProvider/*'/>
1982+
public abstract class SspiContextProvider
1983+
{
1984+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiContextProvider.xml' path='docs/members[@name="SspiContextProvider"]/GenerateContext/*'/>
1985+
protected abstract bool GenerateContext(System.ReadOnlySpan<byte> incomingBlob, System.Buffers.IBufferWriter<byte> outgoingBlobWriter, SspiAuthenticationParameters authParams);
1986+
}
1987+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/*'/>
1988+
public sealed class SspiAuthenticationParameters
1989+
{
1990+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/ctor'/>
1991+
public SspiAuthenticationParameters(string serverName, string resource)
1992+
{
1993+
ServerName = serverName;
1994+
Resource = resource;
1995+
}
1996+
1997+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/Resource'/>
1998+
public string Resource { get; }
1999+
2000+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/ServerName'/>
2001+
public string ServerName { get; }
2002+
2003+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/UserId'/>
2004+
public string UserId { get; set; }
2005+
2006+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/DatabaseName'/>
2007+
public string DatabaseName { get; set; }
2008+
2009+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/Password'/>
2010+
public string Password { get; set; }
2011+
}
19792012
}
19802013
namespace Microsoft.Data.SqlClient.Diagnostics
19812014
{

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ private static readonly Dictionary<string, SqlColumnEncryptionKeyStoreProvider>
9191
private IReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _customColumnEncryptionKeyStoreProviders;
9292

9393
private Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken>> _accessTokenCallback;
94+
private SspiContextProvider _sspiContextProvider;
9495

9596
internal bool HasColumnEncryptionKeyStoreProvidersRegistered =>
9697
_customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0;
@@ -650,7 +651,7 @@ public override string ConnectionString
650651
CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback(connectionOptions);
651652
}
652653
}
653-
ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken, _accessTokenCallback));
654+
ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken, _accessTokenCallback, _sspiContextProvider));
654655
_connectionString = value; // Change _connectionString value only after value is validated
655656
CacheConnectionStringProperties();
656657
}
@@ -710,7 +711,7 @@ public string AccessToken
710711
}
711712

712713
// Need to call ConnectionString_Set to do proper pool group check
713-
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value, accessTokenCallback: null));
714+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value, accessTokenCallback: null, sspiContextProvider: null));
714715
_accessToken = value;
715716
}
716717
}
@@ -733,11 +734,22 @@ public Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticati
733734
CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback((SqlConnectionString)ConnectionOptions);
734735
}
735736

736-
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: null, accessTokenCallback: value));
737+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: null, accessTokenCallback: value, sspiContextProvider: null));
737738
_accessTokenCallback = value;
738739
}
739740
}
740741

742+
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/SspiContextProvider/*' />
743+
public SspiContextProvider SspiContextProvider
744+
{
745+
get { return _sspiContextProvider; }
746+
set
747+
{
748+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: null, accessTokenCallback: null, sspiContextProvider: value));
749+
_sspiContextProvider = value;
750+
}
751+
}
752+
741753
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Database/*' />
742754
[ResDescription(StringsHelper.ResourceNames.SqlConnection_Database)]
743755
[ResCategory(StringsHelper.ResourceNames.SqlConnection_DataSource)]
@@ -1035,7 +1047,7 @@ public SqlCredential Credential
10351047
_credential = value;
10361048

10371049
// Need to call ConnectionString_Set to do proper pool group check
1038-
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken, accessTokenCallback: _accessTokenCallback));
1050+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken, accessTokenCallback: _accessTokenCallback, sspiContextProvider: null));
10391051
}
10401052
}
10411053

@@ -2265,7 +2277,7 @@ public static void ChangePassword(string connectionString, string newPassword)
22652277
throw ADP.InvalidArgumentLength(nameof(newPassword), TdsEnums.MAXLEN_NEWPASSWORD);
22662278
}
22672279

2268-
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null, accessTokenCallback: null);
2280+
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null, accessTokenCallback: null, sspiContextProvider: null);
22692281

22702282
SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
22712283
if (connectionOptions.IntegratedSecurity)
@@ -2314,7 +2326,7 @@ public static void ChangePassword(string connectionString, SqlCredential credent
23142326
throw ADP.InvalidArgumentLength(nameof(newSecurePassword), TdsEnums.MAXLEN_NEWPASSWORD);
23152327
}
23162328

2317-
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null);
2329+
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null, sspiContextProvider: null);
23182330

23192331
SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
23202332

@@ -2352,7 +2364,7 @@ private static void ChangePassword(string connectionString, SqlConnectionString
23522364
{
23532365
con?.Dispose();
23542366
}
2355-
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null);
2367+
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null, sspiContextProvider: null);
23562368

23572369
SqlConnectionFactory.SingletonInstance.ClearPool(key);
23582370
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposa
135135
SqlFedAuthToken _fedAuthToken = null;
136136
internal byte[] _accessTokenInBytes;
137137
internal readonly Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken>> _accessTokenCallback;
138+
internal readonly SspiContextProvider _sspiContextProvider;
138139

139140
private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper;
140141

@@ -460,8 +461,8 @@ internal SqlInternalConnectionTds(
460461
bool applyTransientFaultHandling = false,
461462
string accessToken = null,
462463
IDbConnectionPool pool = null,
463-
Func<SqlAuthenticationParameters, CancellationToken,
464-
Task<SqlAuthenticationToken>> accessTokenCallback = null) : base(connectionOptions)
464+
Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken>> accessTokenCallback = null,
465+
SspiContextProvider sspiContextProvider = null) : base(connectionOptions)
465466
{
466467
#if DEBUG
467468
if (reconnectSessionData != null)
@@ -514,6 +515,7 @@ internal SqlInternalConnectionTds(
514515
}
515516

516517
_accessTokenCallback = accessTokenCallback;
518+
_sspiContextProvider = sspiContextProvider;
517519

518520
_activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper();
519521

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ internal void Connect(ServerInfo serverInfo,
409409
// AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server
410410
if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
411411
{
412-
_authenticationProvider = _physicalStateObj.CreateSspiContextProvider();
412+
_authenticationProvider = Connection._sspiContextProvider ?? _physicalStateObj.CreateSspiContextProvider();
413413
SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | SSPI or Active Directory Authentication Library loaded for SQL Server based integrated authentication");
414414
}
415415

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,8 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden
810810
[System.ComponentModel.BrowsableAttribute(false)]
811811
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
812812
public Microsoft.Data.SqlClient.SqlCredential Credential { get { throw null; } set { } }
813+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/SspiContextProvider/*' />
814+
public SspiContextProvider SspiContextProvider { get { throw null; } set { } }
813815
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Database/*'/>
814816
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
815817
public override string Database { get { throw null; } }
@@ -1959,6 +1961,37 @@ public sealed class SqlConfigurableRetryFactory
19591961
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml' path='docs/members[@name="SqlConfigurableRetryFactory"]/CreateNoneRetryProvider/*' />
19601962
public static SqlRetryLogicBaseProvider CreateNoneRetryProvider() { throw null; }
19611963
}
1964+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiContextProvider.xml' path='docs/members[@name="SspiContextProvider"]/SspiContextProvider/*'/>
1965+
public abstract class SspiContextProvider
1966+
{
1967+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiContextProvider.xml' path='docs/members[@name="SspiContextProvider"]/GenerateContext/*'/>
1968+
protected abstract bool GenerateContext(System.ReadOnlySpan<byte> incomingBlob, System.Buffers.IBufferWriter<byte> outgoingBlobWriter, SspiAuthenticationParameters authParams);
1969+
}
1970+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/*'/>
1971+
public sealed class SspiAuthenticationParameters
1972+
{
1973+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/ctor'/>
1974+
public SspiAuthenticationParameters(string serverName, string resource)
1975+
{
1976+
ServerName = serverName;
1977+
Resource = resource;
1978+
}
1979+
1980+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/Resource'/>
1981+
public string Resource { get; }
1982+
1983+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/ServerName'/>
1984+
public string ServerName { get; }
1985+
1986+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/UserId'/>
1987+
public string UserId { get; set; }
1988+
1989+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/DatabaseName'/>
1990+
public string DatabaseName { get; set; }
1991+
1992+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/Password'/>
1993+
public string Password { get; set; }
1994+
}
19621995
}
19631996
namespace Microsoft.Data.SqlClient.Server
19641997
{

0 commit comments

Comments
 (0)