Skip to content

Commit 9a5fd7c

Browse files
committed
Expose SSPI context provider as public
Adds a property to SqlConnection to allow setting a provider Plumbs that property into the TdsParser so that it can be used if set Fixes #2253
1 parent f7c90b4 commit 9a5fd7c

File tree

17 files changed

+215
-37
lines changed

17 files changed

+215
-37
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,6 +2086,11 @@ The following sample tries to open a connection to an invalid database to simula
20862086
Returns 0 if the connection is inactive on the client side.
20872087
</remarks>
20882088
</ServerProcessId>
2089+
<SspiContextProvider>
2090+
<summary>
2091+
Gets or sets the <see cref="SspiContextProvider"/> instance for customizing the SSPI context. If not set, the default for the platform will be used.
2092+
</summary>
2093+
</SspiContextProvider>
20892094
<State>
20902095
<summary>
20912096
Indicates the state of the <see cref="T:Microsoft.Data.SqlClient.SqlConnection" /> during the most recent network operation performed on the connection.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
</ctor>
10+
<Resource>
11+
<summary>Gets the resource (often the server service principal name).</summary>
12+
</Resource>
13+
<ServerName>
14+
<summary>Gets the server name.</summary>
15+
</ServerName>
16+
<UserId>
17+
<summary>Gets or sets the user id if available.</summary>
18+
</UserId>
19+
<DatabaseName>
20+
<summary>Gets or sets the database name if available.</summary>
21+
</DatabaseName>
22+
<Password>
23+
<summary>Gets or sets the password if available.</summary>
24+
</Password>
25+
</members>
26+
</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+
<GenerateSspiClientContext>
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+
</GenerateSspiClientContext>
19+
</members>
20+
</docs>

src/Microsoft.Data.SqlClient.sln

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient",
152152
..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml
153153
..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml
154154
..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml
155+
..\doc\snippets\Microsoft.Data.SqlClient\SspiAuthenticationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SspiAuthenticationParameters.xml
156+
..\doc\snippets\Microsoft.Data.SqlClient\SspiContextProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SspiContextProvider.xml
155157
EndProjectSection
156158
EndProject
157159
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.DataClassification", "Microsoft.Data.SqlClient.DataClassification", "{5D1F0032-7B0D-4FB6-A969-FCFB25C9EA1D}"
@@ -221,11 +223,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{4600328C-C13
221223
EndProject
222224
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{4CAE9195-4F1A-4D48-854C-1C9FBC512C66}"
223225
ProjectSection(SolutionItems) = preProject
226+
..\eng\pipelines\akv-official-pipeline.yml = ..\eng\pipelines\akv-official-pipeline.yml
224227
..\eng\pipelines\dotnet-sqlclient-ci-core.yml = ..\eng\pipelines\dotnet-sqlclient-ci-core.yml
225228
..\eng\pipelines\dotnet-sqlclient-ci-package-reference-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-ci-package-reference-pipeline.yml
226229
..\eng\pipelines\dotnet-sqlclient-ci-project-reference-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-ci-project-reference-pipeline.yml
227230
..\eng\pipelines\dotnet-sqlclient-signing-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-signing-pipeline.yml
228-
..\eng\pipelines\akv-official-pipeline.yml = ..\eng\pipelines\akv-official-pipeline.yml
229231
EndProjectSection
230232
EndProject
231233
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{FD4D7A96-79B1-4F89-B64D-29FACCC9232F}"
@@ -257,9 +259,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{EABE3A3E
257259
..\eng\pipelines\common\templates\steps\ci-project-build-step.yml = ..\eng\pipelines\common\templates\steps\ci-project-build-step.yml
258260
..\eng\pipelines\common\templates\steps\code-analyze-step.yml = ..\eng\pipelines\common\templates\steps\code-analyze-step.yml
259261
..\eng\pipelines\common\templates\steps\configure-sql-server-linux-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-linux-step.yml
262+
..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml
260263
..\eng\pipelines\common\templates\steps\configure-sql-server-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-step.yml
261264
..\eng\pipelines\common\templates\steps\configure-sql-server-win-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-win-step.yml
262265
..\eng\pipelines\common\templates\steps\copy-dlls-for-test-step.yml = ..\eng\pipelines\common\templates\steps\copy-dlls-for-test-step.yml
266+
..\eng\pipelines\common\templates\steps\ensure-dotnet-version.yml = ..\eng\pipelines\common\templates\steps\ensure-dotnet-version.yml
263267
..\eng\pipelines\common\templates\steps\esrp-code-signing-step.yml = ..\eng\pipelines\common\templates\steps\esrp-code-signing-step.yml
264268
..\eng\pipelines\common\templates\steps\generate-nuget-package-step.yml = ..\eng\pipelines\common\templates\steps\generate-nuget-package-step.yml
265269
..\eng\pipelines\common\templates\steps\override-sni-version.yml = ..\eng\pipelines\common\templates\steps\override-sni-version.yml
@@ -270,8 +274,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{EABE3A3E
270274
..\eng\pipelines\common\templates\steps\run-all-tests-step.yml = ..\eng\pipelines\common\templates\steps\run-all-tests-step.yml
271275
..\eng\pipelines\common\templates\steps\update-config-file-step.yml = ..\eng\pipelines\common\templates\steps\update-config-file-step.yml
272276
..\eng\pipelines\common\templates\steps\update-nuget-config-local-feed-step.yml = ..\eng\pipelines\common\templates\steps\update-nuget-config-local-feed-step.yml
273-
..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml
274-
..\eng\pipelines\common\templates\steps\ensure-dotnet-version.yml = ..\eng\pipelines\common\templates\steps\ensure-dotnet-version.yml
275277
EndProjectSection
276278
EndProject
277279
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{75BAE755-3A1F-41F2-9176-9F8FF9FEE2DD}"
@@ -288,24 +290,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "variables", "variables", "{
288290
ProjectSection(SolutionItems) = preProject
289291
..\eng\pipelines\variables\akv-official-variables.yml = ..\eng\pipelines\variables\akv-official-variables.yml
290292
..\eng\pipelines\variables\common-variables.yml = ..\eng\pipelines\variables\common-variables.yml
291-
..\eng\pipelines\variables\onebranch-variables.yml = ..\eng\pipelines\variables\onebranch-variables.yml
292293
..\eng\pipelines\variables\esrp-signing-variables.yml = ..\eng\pipelines\variables\esrp-signing-variables.yml
294+
..\eng\pipelines\variables\onebranch-variables.yml = ..\eng\pipelines\variables\onebranch-variables.yml
293295
EndProjectSection
294296
EndProject
295297
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{09352F1D-878F-4F55-8AA2-6E47F1AD37D5}"
296-
ProjectSection(SolutionItems) = preProject
297-
..\eng\pipelines\jobs\ob-build-akv-official-job.yml = ..\eng\pipelines\jobs\build-akv-official-job.yml
298-
EndProjectSection
299298
EndProject
300299
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{AD738BD4-6A02-4B88-8F93-FBBBA49A74C8}"
301300
ProjectSection(SolutionItems) = preProject
302-
..\eng\pipelines\steps\script-output-environment-variables-step.yml = ..\eng\pipelines\steps\script-output-environment-variables-step.yml
301+
..\eng\pipelines\steps\compound-build-akv-step.yml = ..\eng\pipelines\steps\compound-build-akv-step.yml
303302
..\eng\pipelines\steps\compound-esrp-code-signing-step.yml = ..\eng\pipelines\steps\compound-esrp-code-signing-step.yml
303+
..\eng\pipelines\steps\compound-extract-akv-apiscan-files-step.yml = ..\eng\pipelines\steps\compound-extract-akv-apiscan-files-step.yml
304304
..\eng\pipelines\steps\compound-nuget-pack-step.yml = ..\eng\pipelines\steps\compound-nuget-pack-step.yml
305305
..\eng\pipelines\steps\compound-publish-symbols-step.yml = ..\eng\pipelines\steps\compound-publish-symbols-step.yml
306306
..\eng\pipelines\steps\roslyn-analyzers-akv-step.yml = ..\eng\pipelines\steps\roslyn-analyzers-akv-step.yml
307-
..\eng\pipelines\steps\compound-build-akv-step.yml = ..\eng\pipelines\steps\compound-build-akv-step.yml
308-
..\eng\pipelines\steps\compound-extract-akv-apiscan-files-step.yml = ..\eng\pipelines\steps\compound-extract-akv-apiscan-files-step.yml
307+
..\eng\pipelines\steps\script-output-environment-variables-step.yml = ..\eng\pipelines\steps\script-output-environment-variables-step.yml
309308
EndProjectSection
310309
EndProject
311310
Global

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,8 @@ public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collect
905905
[System.ComponentModel.BrowsableAttribute(false)]
906906
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
907907
public Microsoft.Data.SqlClient.SqlCredential Credential { get { throw null; } set { } }
908+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/SspiContextProviderFactory/*' />
909+
public System.Func<SspiContextProvider> SspiContextProviderFactory { get { throw null; } set { } }
908910
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Database/*'/>
909911
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
910912
public override string Database { get { throw null; } }
@@ -1940,6 +1942,37 @@ public sealed class SqlConfigurableRetryFactory
19401942
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml' path='docs/members[@name="SqlConfigurableRetryFactory"]/CreateNoneRetryProvider/*' />
19411943
public static SqlRetryLogicBaseProvider CreateNoneRetryProvider() { throw null; }
19421944
}
1945+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiContextProvider.xml' path='docs/members[@name="SspiContextProvider"]/SspiContextProvider/*'/>
1946+
public abstract class SspiContextProvider
1947+
{
1948+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiContextProvider.xml' path='docs/members[@name="SspiContextProvider"]/GenerateSspiClientContext/*'/>
1949+
protected abstract bool GenerateSspiClientContext(System.ReadOnlySpan<byte> incomingBlob, System.Buffers.IBufferWriter<byte> outgoingBlobWriter, SspiAuthenticationParameters authParams);
1950+
}
1951+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/*'/>
1952+
public sealed class SspiAuthenticationParameters
1953+
{
1954+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/ctor'/>
1955+
public SspiAuthenticationParameters(string serverName, string resource)
1956+
{
1957+
ServerName = serverName;
1958+
Resource = resource;
1959+
}
1960+
1961+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/Resource'/>
1962+
public string Resource { get; }
1963+
1964+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/ServerName'/>
1965+
public string ServerName { get; }
1966+
1967+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/UserId'/>
1968+
public string UserId { get; set; }
1969+
1970+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/DatabaseName'/>
1971+
public string DatabaseName { get; set; }
1972+
1973+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SspiAuthenticationParameters.xml' path='docs/members[@name="SspiAuthenticationParameters"]/SspiAuthenticationParameters/Password'/>
1974+
public string Password { get; set; }
1975+
}
19431976
}
19441977
namespace Microsoft.Data.SqlClient.Diagnostics
19451978
{

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
@@ -90,6 +90,7 @@ private static readonly Dictionary<string, SqlColumnEncryptionKeyStoreProvider>
9090
private IReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _customColumnEncryptionKeyStoreProviders;
9191

9292
private Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken>> _accessTokenCallback;
93+
private SspiContextProvider _sspiContextProvider;
9394

9495
internal bool HasColumnEncryptionKeyStoreProvidersRegistered =>
9596
_customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0;
@@ -647,7 +648,7 @@ public override string ConnectionString
647648
CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback(connectionOptions);
648649
}
649650
}
650-
ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken, _accessTokenCallback));
651+
ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken, _accessTokenCallback, _sspiContextProvider));
651652
_connectionString = value; // Change _connectionString value only after value is validated
652653
CacheConnectionStringProperties();
653654
}
@@ -707,7 +708,7 @@ public string AccessToken
707708
}
708709

709710
// Need to call ConnectionString_Set to do proper pool group check
710-
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value, accessTokenCallback: null));
711+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: value, accessTokenCallback: null, sspiContextProvider: _sspiContextProvider));
711712
_accessToken = value;
712713
}
713714
}
@@ -730,11 +731,22 @@ public Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticati
730731
CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessTokenCallback((SqlConnectionString)ConnectionOptions);
731732
}
732733

733-
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: null, accessTokenCallback: value));
734+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: null, accessTokenCallback: value, sspiContextProvider: _sspiContextProvider));
734735
_accessTokenCallback = value;
735736
}
736737
}
737738

739+
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/SspiContextProvider/*' />
740+
public SspiContextProvider SspiContextProvider
741+
{
742+
get { return _sspiContextProvider; }
743+
set
744+
{
745+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: null, accessTokenCallback: _accessTokenCallback, sspiContextProvider: value));
746+
_sspiContextProvider = value;
747+
}
748+
}
749+
738750
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/Database/*' />
739751
[ResDescription(StringsHelper.ResourceNames.SqlConnection_Database)]
740752
[ResCategory(StringsHelper.ResourceNames.SqlConnection_DataSource)]
@@ -1032,7 +1044,7 @@ public SqlCredential Credential
10321044
_credential = value;
10331045

10341046
// Need to call ConnectionString_Set to do proper pool group check
1035-
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken, accessTokenCallback: _accessTokenCallback));
1047+
ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken, accessTokenCallback: _accessTokenCallback, _sspiContextProvider));
10361048
}
10371049
}
10381050

@@ -2262,7 +2274,7 @@ public static void ChangePassword(string connectionString, string newPassword)
22622274
throw ADP.InvalidArgumentLength(nameof(newPassword), TdsEnums.MAXLEN_NEWPASSWORD);
22632275
}
22642276

2265-
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null, accessTokenCallback: null);
2277+
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null, accessTokenCallback: null, sspiContextProvider: null);
22662278

22672279
SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
22682280
if (connectionOptions.IntegratedSecurity)
@@ -2311,7 +2323,7 @@ public static void ChangePassword(string connectionString, SqlCredential credent
23112323
throw ADP.InvalidArgumentLength(nameof(newSecurePassword), TdsEnums.MAXLEN_NEWPASSWORD);
23122324
}
23132325

2314-
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null);
2326+
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null, sspiContextProvider: null);
23152327

23162328
SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
23172329

@@ -2349,7 +2361,7 @@ private static void ChangePassword(string connectionString, SqlConnectionString
23492361
{
23502362
con?.Dispose();
23512363
}
2352-
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null);
2364+
SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null, accessTokenCallback: null, sspiContextProvider: null);
23532365

23542366
SqlConnectionFactory.SingletonInstance.ClearPool(key);
23552367
}

0 commit comments

Comments
 (0)