更改 Windows 本地网络安全策略后出现 Npgsql 错误

Npgsql error after changing Windows Local Network Security policy

我们公司最近开始与合作伙伴开展业务,该合作伙伴试图为我们提供基于 Web 的 RDP 连接到他们的系统。作为他们的设置文档的一部分,他们要求我们更改我们工作站的本地网络安全策略:具体来说,更改 LAN Manager 身份验证级别 设置从以前的 发送 LM 和 NTLM 响应仅发送 NTLMv2 响应。一旦我进行了此更改并且 gpupdate /force 对工作站进行了更改(然后显示策略 "correctly" 集),我就可以通过 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

如果我将 LAN Manager 身份验证级别 设置为 发送 LM 和 NTLM 响应[=56=,则所有这些代码都能完美运行],但如果我将其更改为 Send NTLMv2 response only 作为我们新合作伙伴的要求,它会抛出异常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 Manager 身份验证级别 设置时如何让 Npgsql 连接到 PostgreSQL 数据库仅发送 NTLMv2 响应?由于其他一切似乎都正常工作(据我所知),这似乎是失败点。

编辑: 因为我在 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) 有解决方法,否则我将不得不更新我所有的内部应用程序以使用更新的库。我 真的 现在没有时间做所有这些,所以如果有人有办法完成这项工作,我很想听听。

@G_Hosa_Phat。 我记得我们对 ntlm 支持进行了重大修改,因为您可以通过测试最近的 3.0.x 版本来检查并使其正常工作...

没有配置选项可以更改您可以使用的 ntlm 身份验证的行为。 我认为您确实需要更新您的图书馆。

但我建议您更新到最新的 2.x 版本,而不是 3.0.x,因为 3.0.x 的更改超出了 ntlm 支持范围,并且它可能会破坏您现有的系统。

很抱歉告诉你这个坏消息。 :(

希望对您有所帮助。