我们公司最近开始与一个合作伙伴开展业务,该合作伙伴正试图为我们提供一个基于 Web 的 RDP 连接到他们的系统。作为他们设置文档的一部分,他们要求我们更改工作站的本地网络安全策略:具体而言,将LAN Manager 身份验证级别设置从之前的发送 LM 和 NTLM 响应更改为仅发送 NTLMv2 响应。一旦我进行此更改并对gpupdate /force
工作站进行更改(然后显示“正确”设置的策略),我可以 RDP 进入我们新合作伙伴的服务器就好了,但我所有连接到 PostgreSQL 数据库文件的应用程序都出现以下错误:
FATAL: XX000: could not accept SSPI security context
我构建了一个临时应用程序来测试 Visual Studio 中的连接,代码如下(网络详细信息已编辑):
Imports Npgsql
Imports System.DirectoryServices
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim PGUserName As String
Dim PGDB As NpgsqlConnection = Nothing
Dim PGConnection As NpgsqlConnectionStringBuilder
Dim PGCommand As NpgsqlCommand = Nothing
Dim PGAdapter As NpgsqlDataAdapter = Nothing
Dim TestSQL As String = String.Empty
Dim TestData As New DataTable
PGUserName = GetActiveDirectoryUsername()
TestSQL = "SELECT * FROM testdb"
PGConnection = New NpgsqlConnectionStringBuilder
With PGConnection
NpgsqlEventLog.Level = LogLevel.Debug
NpgsqlEventLog.LogName = "C:\TEST\NPGSQLEVENTLOG.TXT"
.Host = "<PGSQLSERVER>"
.Port = <PORT>
.UserName = PGUserName
.IntegratedSecurity = True
.Database = "testing"
End With
PGDB = New NpgsqlConnection(PGConnection.ConnectionString)
Try
PGDB.Open()
PGCommand = New NpgsqlCommand(TestSQL, PGDB)
PGAdapter = New NpgsqlDataAdapter(PGCommand)
PGAdapter.Fill(TestData)
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
If Not PGAdapter Is Nothing Then
PGAdapter.Dispose()
End If
If Not PGCommand Is Nothing Then
PGCommand.Dispose()
End If
If PGDB.State <> ConnectionState.Closed Then
PGDB.Close()
End If
If Not PGDB Is Nothing Then
PGDB.Dispose()
End If
If Not TestData Is Nothing Then
TestData.Dispose()
End If
End Try
End Sub
Friend Function GetDirectoryEntry() As DirectoryEntry
Dim dirEntry As DirectoryEntry = New DirectoryEntry()
dirEntry.Path = "LDAP://<ADSERVERIP>/DC=<DOMAIN>"
Return dirEntry
End Function
Friend Function GetActiveDirectoryUsername() As String
Try
Dim MyDirectory As DirectoryEntry = GetDirectoryEntry()
Dim search As New DirectorySearcher(MyDirectory)
search.Filter = String.Format("(&(SAMAccountName={0}))", Environment.UserName)
'Use the .FindOne() Method to stop as soon as a match is found
Dim result As SearchResult = search.FindOne()
If result Is Nothing Then
Return ""
Else
Return result.Properties("samaccountname").Item(0).ToString
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "Active Directory Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Return ""
End Try
End Function
End Class
如果我在发送 LM 和 NTLM 响应上设置LAN Manager 身份验证级别,所有这些代码都可以完美运行,但如果我将其更改为仅根据我们的新合作伙伴的要求发送 NTLMv2 响应,它会在语句中引发异常。从调试日志:PGDB.Open()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlConnection.NpgsqlConnection(NpgsqlConnection())
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: HOST = <PGSQLSERVER>
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: PORT = <PORT>
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: PROTOCOL = 3
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: DATABASE = testing
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: USER ID = <PGSQLUSERNAME>
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: SSL = False
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: SSLMODE = Disable
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: TIMEOUT = 15
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: POOLING = True
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: CONNECTIONLIFETIME = 15
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: MINPOOLSIZE = 1
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: MAXPOOLSIZE = 20
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: SYNCNOTIFICATION = False
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: COMMANDTIMEOUT = 20
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: ENLIST = False
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: PRELOADREADER = False
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: USEEXTENDEDTYPES = False
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: INTEGRATED SECURITY = True
2/22/16 12:32:58 PM 11452 Debug ConnectionString Option: COMPATIBLE = 2.0.11.92
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlConnection.Open()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlClosedState.Instance
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlClosedState.Instance
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlClosedState.Open()
2/22/16 12:32:58 PM 11452 Debug Attempt to connect to '<PGSQLSERVERIP>'.
2/22/16 12:32:58 PM 11452 Normal Connected to: <PGSQLSERVER>:<PORT>.
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlStartupPacket.NpgsqlStartupPacket()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlStartupPacket.WriteToStream()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlStartupPacket.WriteToStream_Ver_3()
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteString()
2/22/16 12:32:58 PM 11452 Debug String written: user.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteString()
2/22/16 12:32:58 PM 11452 Debug String written: <PGSQLUSERNAME>.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteString()
2/22/16 12:32:58 PM 11452 Debug String written: database.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteString()
2/22/16 12:32:58 PM 11452 Debug String written: testing.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteString()
2/22/16 12:32:58 PM 11452 Debug String written: DateStyle.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteString()
2/22/16 12:32:58 PM 11452 Debug String written: ISO.
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlState.ProcessBackendResponses()
2/22/16 12:32:58 PM 11452 Debug AuthenticationRequest message received from server.
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlStartupState.Authenticate()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlPasswordPacket.NpgsqlPasswordPacket()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlPasswordPacket.WriteToStream()
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteBytes()
2/22/16 12:32:58 PM 11452 Debug Unable to find resource string Log_BytesWritten for class PGUtil
2/22/16 12:32:58 PM 11452 Debug AuthenticationRequest message received from server.
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlStartupState.Authenticate()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlPasswordPacket.NpgsqlPasswordPacket()
2/22/16 12:32:58 PM 11452 Debug Entering NpgsqlPasswordPacket.WriteToStream()
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.WriteBytes()
2/22/16 12:32:58 PM 11452 Debug Unable to find resource string Log_BytesWritten for class PGUtil
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: FATAL.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: XX000.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: could not accept SSPI security context.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: The token supplied to the function is invalid
(80090308).
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: src\backend\libpq\auth.c.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: 1024.
2/22/16 12:32:58 PM 11452 Debug Entering PGUtil.ReadString()
2/22/16 12:32:58 PM 11452 Debug Get NpgsqlEventLog.LogLevel
2/22/16 12:32:58 PM 11452 Debug String read: pg_SSPI_error.
2/22/16 12:32:58 PM 11452 Debug ErrorResponse message from Server: could not accept SSPI security context.
2/22/16 12:32:58 PM 11452 Normal An NpgsqlException occured: FATAL: XX000: could not accept SSPI security context.
2/22/16 12:33:02 PM 11452 Debug Entering NpgsqlConnection.Dispose()
2/22/16 12:33:03 PM 11452 Debug Entering NpgsqlConnection.Close()
奇怪的是,如果我尝试使用 SSPI 使用 PGAdmin III 进行连接,则没有错误。我重新启动了服务器上的 PostgreSQL 服务(以防万一),但仍然出现同样的错误。我什至重新启动了整个服务器,但错误仍然存在。
现在,如果我将本地网络安全策略设置改回发送 LM & NTLM 响应,应用程序可以正常工作,但我们无法建立与合作伙伴系统的 RDP 连接。我什至尝试将策略设置更改为发送 LM 和 NTLM - 如果协商使用 NTLMv2 会话安全。同样,我的应用程序将无错误地连接到数据库,但 RDP 连接失败。
我想问题归结为:当本地网络安全策略的LAN 管理器身份验证级别设置配置为仅发送 NTLMv2 响应时,有谁知道如何让 Npgsql 连接到 PostgreSQL 数据库?由于其他一切似乎都正常工作(据我所知),这似乎是失败的地方。
编辑:由于我在 Visual Studio 2015 中工作,因此我尝试通过 NuGet 控制台更新我正在使用的 Npgsql 版本。这将库更新到版本 3.0.5.0,我在Send NTLMv2 response only设置下再次测试了连接。这次应用程序的连接似乎工作正常。可以肯定的是,我删除了 3.0.5.0 引用并添加了我的旧版本(显然我在 2.0.11.92 -是的,是的......我知道)并再次测试,这给了我和以前一样的错误。
看起来导致问题的任何原因都已通过对 Npgsql 库的后续更新得到解决。当然,这意味着,除非我正在使用的版本 (2.0.11.92) 有解决方法,否则我将不得不更新我所有的内部应用程序以使用更新的库。我现在真的没有时间做所有这些,所以如果有人有办法完成这项工作,我很想听听。