Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specifying Domain Account in ServerSPN Causes Connection Failure on Linux #3165

Open
AbbasLB opened this issue Feb 19, 2025 · 1 comment
Open
Assignees
Labels
🐛 Bug! Issues that are bugs in the drivers we maintain.

Comments

@AbbasLB
Copy link

AbbasLB commented Feb 19, 2025

When attempting to set the ServerSPN attribute in a connection string to specify the account under which a SQL Server instance is running (Username@Domain), as described in the SQL Server Native Client documentation, the connection succeeds on Windows but fails on Linux with the following exception:

Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Connection failed: The target principal name is incorrect.  Cannot generate SSPI context.
GenericFailure
   at Microsoft.Data.SqlClient.SNI.TdsParserStateObjectManaged.GenerateSspiClientContext(Byte[] receivedBuff, UInt32 receivedLength, Byte[]& sendBuff, UInt32& sendLength, Byte[][] _sniSpnBuffer)
   at Microsoft.Data.SqlClient.TdsParser.SSPIData(Byte[] receivedBuff, UInt32 receivedLength, Byte[]& sendBuff, UInt32& sendLength)
 at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.SSPIError(String error, String procedure)
   at Microsoft.Data.SqlClient.TdsParser.SSPIData(Byte[] receivedBuff, UInt32 receivedLength, Byte[]& sendBuff, UInt32& sendLength)
   at Microsoft.Data.SqlClient.TdsParser.TdsLogin(SqlLogin rec, FeatureExtension requestedFeatures, SessionData recoverySessionData, FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData, SqlConnectionEncryptOption encrypt)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.Login(ServerInfo server, TimeoutTimer timeout, String newPassword, SecureString newSecurePassword, SqlConnectionEncryptOption encrypt)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, TimeoutTimer timeout, Boolean withFailover)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool, Func`3 accessTokenCallback)
   at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
   at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
   at Microsoft.Data.SqlClient.SqlConnection.Open()
   at TestCSharpConnection.Program.Main() in ....

To reproduce

  1. Configure a SQL Server instance to run under a specific domain account.
  2. On a Linux client, setup Kerberos authentication, and set up a connection string that includes the ServerSPN attribute, specifying the account in the format Username@Domain (example: svc-myuser@REALM).
  3. Attempt to establish a connection to the SQL Server instance using this connection string.

The following code snippet works on Windows but doesn't work on Linux without any single change.

using Microsoft.Data.SqlClient;

namespace TestCSharpConnection
{
    internal class Program
    {
        static void Main()
        {
            // Define your connection string
            var connectionString = new SqlConnectionStringBuilder
            {
                DataSource = "myserver.mycompany.com,1033", // Server name and port
                IntegratedSecurity = true, // Enables Windows Authentication (Kerberos)
                TrustServerCertificate = true, // Trust the server certificate
                ServerSPN = "svc-myuser@REALM"
            }.ConnectionString;

            try
            {
                // Establish connection
                using (var connection = new SqlConnection(connectionString))
                {
                    connection.Open();
                    Console.WriteLine("Connected successfully!");

                    // Create a command to execute a query
                    using (var command = new SqlCommand(
                        "SELECT auth_scheme, * FROM sys.dm_exec_connections WHERE session_id = @@SPID", connection))
                    {
                        // Execute the query
                        using (var reader = command.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                Console.WriteLine($"SQL Server Auth Scheme: {reader["auth_scheme"]}");
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Connection failed: {ex.Message}");
            }
        }
    }
}

Note that if we remove ServerSPN = "svc-myuser@REALM" then the connection succeeds on both Windows and Linux using Kerberos. (This indicate that Kerbros is setup correctly on the Linux Client)

Observed Behavior

The connection attempt results in the exception mentioned above, indicating that the target principal name is incorrect and the SSPI context cannot be generated.

  • The issue does not occur when the same connection string is used on a Windows client; the connection is established successfully.

Expected behavior

The connection should succeed in both Windows and Linux.

Further technical details

  • Microsoft.Data.SqlClient version: 5.2.2 and 6.0.1
  • .NET target: .NET 8.0
  • SQL Server version: SQL Server 2022 CU11 Linux and SQL Server 2022 CU11 Windows
  • Client Operating Systems Tested where it is failing:
    • Rocky Linux release 8.10 (Green Obsidian) with kernel version 5.4.249-1.el8.x86_64
    • WSL version 2.4.11.0 with kernel version 5.15.167.4-1 on Windows 10.0.22631.4890 running Ubuntu 22.04
@AbbasLB AbbasLB added 🐛 Bug! Issues that are bugs in the drivers we maintain. 🆕 Triage Needed For new issues, not triaged yet. labels Feb 19, 2025
@AbbasLB
Copy link
Author

AbbasLB commented Feb 21, 2025

Another observation is that on Windows, it's possible to specify the Kerberos realm in the ServerSPN as follows:

ServerSPN = "MSSQLSvc/fqdn:port@REALM"

However, on Linux, specifying the @REALM in the ServerSPN causes the connection to fail with the same exception as mentioned earlier:

Microsoft.Data.SqlClient.SqlException (0x80131904): The target principal name is incorrect. Cannot generate SSPI context.

Tt's important to note that specifying the ServerSPN in the format "MSSQLSvc/fqdn:port@REALM" or "user@REALM" is successful across multiple platforms and drivers, I have successfully tested it with:

  • pyodbc with DRIVER={ODBC Driver 18 for SQL Server} on Linux
  • Java com.microsoft.sqlserver.jdbc.SQLServerDataSource on both Windows and Linux
  • C# Microsoft.Data.SqlClient on Windows
    The discrepancy arises specifically with C# Microsoft.Data.SqlClient on Linux only, where specifying the ServerSPN in this manner results in a connection failure.

@paulmedynski paulmedynski self-assigned this Feb 25, 2025
@paulmedynski paulmedynski removed the 🆕 Triage Needed For new issues, not triaged yet. label Feb 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 Bug! Issues that are bugs in the drivers we maintain.
Projects
None yet
Development

No branches or pull requests

2 participants