Fix SPN using instance name instead of resolved port for Protocol.None and Protocol.Admin#4180
Fix SPN using instance name instead of resolved port for Protocol.None and Protocol.Admin#4180paulmedynski wants to merge 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes SPN generation for named-instance connections when the connection string omits a protocol prefix (Protocol.None) or uses DAC (Protocol.Admin), ensuring TCP-like protocols use the SSRP-resolved port rather than the instance name in the SPN.
Changes:
- Updated
SniProxy.GetSqlServerSPNs(DataSource, string)to use the instance name only for Named Pipes (NP); all other protocols use the SSRP-resolved port. - Changed
GetSqlServerSPNs(DataSource, string)visibility fromprivatetointernalto enable direct unit testing. - Added unit tests covering Protocol.None/TCP/Admin and custom SPN passthrough behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniProxy.netcore.cs | Adjusts SPN postfix selection logic so TCP-like protocols use SSRP-resolved port; exposes method for unit tests. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/ManagedSni/SniProxyGetSqlServerSPNsTest.cs | Adds regression tests for SPN formatting across protocols and custom SPN passthrough. |
Comments suppressed due to low confidence (1)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniProxy.netcore.cs:144
- The trace event logs
dataSource.Portbut the postfix/SPN decision for named instances now depends ondataSource.ResolvedPort. Consider loggingResolvedPortas well (or instead) so diagnostics reflect the value actually used to build the SPN.
postfix = dataSource.ResolvedProtocol == DataSource.Protocol.NP ? dataSource.InstanceName : dataSource.ResolvedPort.ToString();
}
SqlClientEventSource.Log.TryTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerName {0}, InstanceName {1}, Port {2}, postfix {3}", dataSource?.ServerName, dataSource?.InstanceName, dataSource?.Port, postfix);
return GetSqlServerSPNs(hostName, postfix, dataSource.ResolvedProtocol);
| [Fact] | ||
| public void GetSqlServerSPNs_ProtocolNp_WithInstanceName_UsesInstanceName() | ||
| { | ||
| // Arrange: parse a named pipe data source with instance name | ||
| // Named pipes format: np:server\instance resolves to a pipe path internally, |
There was a problem hiding this comment.
This [Fact] test method is empty (only comments) so it will always pass without validating Named Pipes SPN behavior. Either implement a real assertion (e.g., parse an np:server\\instance data source and assert the generated SPN uses :instance), or remove the test if it can’t be expressed through the current API surface.
da6afed to
f027453
Compare
…ithout tcp: prefix When connecting to a named instance without specifying a protocol prefix (e.g. 'server\instance'), the DataSource.ResolvedProtocol is Protocol.None. The SPN generation in GetSqlServerSPNs only checked for Protocol.TCP when deciding to use the SSRP-resolved port, causing Protocol.None and Protocol.Admin connections to incorrectly use the instance name in the SPN (MSSQLSvc/host:instance) instead of the resolved port (MSSQLSvc/host:port). The fix inverts the condition to check for Protocol.NP (Named Pipes), which is the only protocol that should use instance name in the SPN. All other protocols (TCP, None, Admin) now correctly use the resolved port. Fixes #3566
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## main #4180 +/- ##
==========================================
- Coverage 74.27% 65.04% -9.23%
==========================================
Files 279 274 -5
Lines 42980 65799 +22819
==========================================
+ Hits 31922 42800 +10878
- Misses 11058 22999 +11941
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Fix
Fixes #3566
Problem
When connecting to a named instance without a
tcp:prefix (e.g.Data Source=server\instance), theDataSource.ResolvedProtocolis set toProtocol.Nonerather thanProtocol.TCP. The SPN generation inGetSqlServerSPNsonly checked forProtocol.TCPwhen deciding whether to use the SSRP-resolved port number, soProtocol.None(andProtocol.Admin) connections incorrectly used the instance name in the SPN:instead of:
This is the same class of bug as #2187. The original fix addressed
Protocol.TCPbut missedProtocol.None, which is the most common case (no protocol prefix in the connection string).Fix
Inverted the condition in
GetSqlServerSPNsfrom checking== Protocol.TCP(use port) to checking== Protocol.NP(use instance name). Named Pipes is the only protocol that should use the instance name in the SPN. All TCP-like protocols (TCP,None,Admin) now correctly use the SSRP-resolved port.Testing
SniProxyGetSqlServerSPNsTestcoveringProtocol.None,Protocol.TCP,Protocol.Admin, custom SPN passthrough, and Named PipesProtocol.NoneandProtocol.Admintests fail before the fix and pass afterIntegratedAuthConnectionTest)Changes
SniProxy.netcore.cs: Changed visibility ofGetSqlServerSPNs(DataSource, string)fromprivatetointernalfor testability; inverted protocol check from== TCPto== NPSniProxyGetSqlServerSPNsTest.cs: New unit test class