在最近 Windows 个补丁和 TLS1 被禁用后,FreeTDS 无法连接到 SQL 服务器

FreeTDS fails to connect to SQL Server after recent Windows patches and TLS1 being disabled

我遇到了一个奇怪的问题,FreeTDS (MacOS 10.11.5) 没有连接到 SQL Server 2014 运行 on Windows Server 2012 r2 我已经能够连接到以前(大约一周前)。上周的服务器一直在进行安全加固(最近的 2012 r2 补丁,加上我认为是罪魁祸首的其他东西*,但我的本地开发机器(实际上我的机器上只有 FreeTDS)似乎是补丁后唯一的问题。

我怀疑导致问题的原因在于 RC4 密码和 SSL 2.0 和 TLS 1.0 被禁用,但我不知道如何补救。

需要说明的是,与其他类似问题不同——我无法通过任何方式将 freetds 连接到 DB01,但我可以将其他驱动程序连接到 DB01(但我正在开发一个 python 应用程序并且在此需要 freetds instance) 和其他机器可以连接到 DB01。

基本错误是:

$ tsql -S DB01 -U db_user
Password: ****************
locale is "en_US.UTF-8"
locale charset is "UTF-8"
using default charset "UTF-8"
Error 20002 (severity 9):
    Adaptive Server connection failed
There was a problem connecting to the server

不是很有帮助 - 我已经完成了正常的故障排除,包括尝试各种 TDS 版本、在命令行上使用不同的开关等。

该日志还指出可能存在 SSL 问题 - 例如tls.c logging "handshake failed",包含字符串"SSL_Self_Signed_Fallback"的数据包(否则数据包不可读):

net.c:216:Connecting to 000.000.000.000 port 1433 (TDS version 7.4)
net.c:242:tds_open_socket: connect(2) returned "Operation now in progress"
net.c:343:tds_open_socket() succeeded
packet.c:741:Sending packet
[blah blah]
login.c:1185:detected flag 0
tls.c:116:in tds_push_func_login
tls.c:86:in tds_pull_func_login
packet.c:741:Sending packet
[blah blah]
packet.c:639:Received packet
[blah blah... what?
xxx |..0.S.S. L._.S.e.|
xxx |l.f._.S. i.g.n.e.|
xxx |d._.F.a. l.l.b.a.|
xxx |c.k0...1 blahblah|
tls.c:116:in tds_push_func_login
packet.c:741:Sending packet
0000 12 01 00 0f 00 00 00 00-15 03 00 00 02 02 28    |........ ......(|
tls.c:923:handshake failed
login.c:530:login packet rejected
query.c:3796:tds_disconnect() 
util.c:165:Changed query state from IDLE to DEAD
util.c:322:tdserror(0x7fef2b403aa0, 0x7fef2b403ba0, 20002, 0)
util.c:352:tdserror: client library returned TDS_INT_CANCEL(2)
util.c:375:tdserror: returning TDS_INT_CANCEL(2)
mem.c:644:tds_free_all_results()

当我连接到其他服务器并查看 freetds.log 时,我可以读取数据包(某种程度上),例如:

xxx |.C.h.a.n .g.e.d. |
xxx |.d.a.t.a .b.a.s.e|
xxx |. .c.o.n .t.e.x.t|
xxx |. .t.o.  .'.m.a.s|
xxx |.t.e.r.' 

不同于 DB01,其中数据包是 }.???G?? .?T???٠

的行和行

这是 freetds 编译时设置——我需要 GnuTLS = yes 吗?:

$ tsql -C
Compile-time settings (established with the "configure" script)
                            Version: freetds v1.00.9
             freetds.conf directory: /usr/local/Cellar/freetds/1.00.9/etc
     MS db-lib source compatibility: no
        Sybase binary compatibility: no
                      Thread safety: yes
                      iconv library: yes
                        TDS version: 7.3
                              iODBC: no
                           unixodbc: yes
              SSPI "trusted" logins: no
                           Kerberos: no
                            OpenSSL: yes
                             GnuTLS: no
                               MARS: no

这是我的 freetds.conf 文件:

[global]
    # TDS protocol version
    tds version = auto
    dump file = /tmp/freetds.log
    debug flags = 4FFF
    text size = 64512
[DB01]
    host = db01.mydomain.tld
    port = 1433
    tds version = 7.4
    database = DB_NAME
    # I added this in case it was a cert issue, see below
    check certificate hostname = no

其他一些快速故障排除数据点:

还有其他人看过吗?有没有办法在连接 freetds 时指定 TLS 1.2 等? (我没能找到这方面的文档)

更新:

我想在 Windows 事件查看器中查找任何错误,这就是其中的内容:

    DB01    17836   Error   MSSQLSERVER Application 7/20/2016 2:52:18 PM
    The login packet used to open the connection is structurally invalid;
 the connection has been closed. Please contact the vendor of the client
 library. [CLIENT: [my ip address]]

    [and also]

    Length specified in network packet payload did not match number of 
bytes read; the connection has been closed. Please contact the vendor 
of the client library. [CLIENT: [my ip address]]

我用这个做了很多测试,我们的解决方案是回滚这个补丁:

https://support.microsoft.com/en-us/kb/3161639

显然,密码套件对 Edge 和 IE 的影响更大。 :) 我向 Microsoft 提出了一个案例,他们知道这导致的问题。来自马口:

Since the issue is no longer occurring after uninstalling that KB update resolved the issue, I did a quick search on that KB update for prior cases. At this point there has been 21 cases on issues with that update since last month. If you are not aware and based off my research, KB 3161639 adds additional cipher keys that utilize the TLS 1.2 protocol that was introduced in KB update 3161608. The following ciphers are most likely the ones that are causing the issue:

TLS_DHE_RSA_WITH_AES_128_CBC_SHA

TLS_DHE_RSA_WITH_AES_256_CBC_SHA

I suspect that either your Unix/Linux server and/or the FreeTDS ODBC does not either support these cipher suites or is not setup for it.
With that said, you have a couple of options:

  1. Use the workaround of not having the KB 3161639 update installed
  2. Reinstall the KB 3161639 update. Configured your cipher ordering to ensure the new cipher suites are not chosen.

我们采用了选项 1,并将其推广到我们的整个网络,并且没有发现任何不良影响。希望对您有所帮助。

TLDR; 我需要重新安装支持 gnutls 而不是 openssl 的 freetds。

经过大量(不,真的很多)反复试验,我终于找到了 mac 未连接上的 freetds 的解决方案。

我仍然需要连接其余部分以便 pyodbc 工作等,但这是基本修复:

brew edit freetds

用这个替换 freetds 公式 https://gist.github.com/hanleybrand/dfb7b9004aae250fabd01cd2466251c4

简而言之,它将选项 --with-gnutls 添加到 brew 安装,并确保如果它存在,它会出现在 --with-openssl 之前。我没有仔细研究它,但我怀疑 openssl/gnutls 是 either/or 而不是 and/or.

brew rm freetds && brew install freetds --with-gnutls --with-unixodbc

在那之后,tsql 工作正常 - 正如我上面提到的,我仍然需要设置其余部分(unixodbc,pyodbc),但我非常有信心如果 tsql 工作,其余部分也会,虽然我不能完全确定。

正如@FlipperPA 指出的那样,这可能与两个包中的密码集有关(openssl agains gnutls)

我们在 Linux 和应用 MS 补丁后间歇性地遇到这个错误。我们仍然可以从 Linux 连接到 MSSQL 服务器,但我们的连接会随机终止并出现 EOF 错误……即使是在查询中间。我打开freetds日志,看到了这样的加密握手失败:

    net.c:1366:handshake failed: A TLS packet with unexpected length was received.          
    login.c:466:login packet rejected
    util.c:331:tdserror(0x1e752b0, 0x2c27f40, 20002, 0)

经过多次故障排除我们在运行我们的 MS SQL 服务器数据库的 MS Windows Server 2008 R2 服务器上回滚了 KB3172605...它解决了问题。 (KB 3172605 取代 KB 3161639。)

在最近的 Microsoft 补丁之后,我在 FreeTDS(版本 0.91)下看到了相同的行为。

简而言之,将 FreeTDS 更新到 http://www.freetds.org/1.00.24 截至 post)可用的夜间快照已为我解决了这个问题。我编译了源代码并更新了 odbcinst.ini 配置文件中的 FreeTDS 驱动程序行以指向新的共享对象文件 (<path>/libtdsodbc.so),例如:

[TDS]
Description = FreeTDS Driver
Driver = /usr/local/lib/libtdsodbc.so

2016 年 9 月初有一些与 OpenSSL 相关的 FreeTDS 提交,包括 this commit,它引用了 Microsoft 补丁不兼容:

Update OpenSSL cipher list

This increase security and also fix a recent incompatibility between OpenSSL and SChannel (SChannel versions patched in a couple of months).

根据此处指向密码套件兼容性问题的其他评论,这可能是解决问题的提交。然而,截至目前 post,还没有包含此提交的 FreeTDS 正式发布版本(最后一次正式发布是 2016 年 5 月的 1.0 版)。

其他评论中提到的微软补丁参考:

  • KB3161639 自 2016 年 6 月补丁汇总。

    This article describes an update in which new TLS cipher suites are added and cipher suite default priorities are changed in Windows RT 8.1, Windows 8.1, Windows Server 2012 R2, Windows 7, or Windows Server 2008 R2. These new cipher suites improve compatibility with servers that support a limited set of cipher suites.

  • KB3172605 from July 2016 patch rollups (and re-released in Sept. 2016). Description of this patch from the Windows Server 2008 R2 SP1 update history文章:

    July 21, 2016 — KB3172605 This update includes quality improvements. No new operating system features are being introduced and no new security updates are included. Key changes include:

    • Improved support in Microsoft Cryptographic Application Programming Interface (CryptoAPI) to help identify websites that use Secure Hash Algorithm 1 (SHA-1).
    • Addressed issue in Microsoft Secure Channel (SChannel) that sometime causes Transport Layer Security (TLS) 1.2 connections to fail depending on whether the root certificate is configured as part of the certificate chain for server authentication.

经过反复试验,我得到了与 Peter Hanley 相反的经历,记录在上述帖子之一中,尽管正如 Peter 指出的那样,错误有点不同。我遇到了 Scott 提到的 EOF 错误,该错误源于 TLS 错误代码 20 (bad_record_MAC)。在 MS 端,它被报告为 SChannel 错误。

因为我要部署到多个服务器并想要一个 RPM,所以我编辑了 freetds.spec 文件以删除 --with-gnutls 并将其替换为 --with-openssl=yes 然后重建 RPM使用 rpmbuild,验证 rpmbuild 实际上使用了我添加的指令并且不包含 --with-gnutls。

为了测试,我编写了一个小型 Perl 实用程序,可以快速连续连接到数据库/断开连接。

我多次验证,当使用 --with-gnutls 时,有 0.5% 到 1% 的时间遇到​​ TLS / SChannel / EOF 错误。

当仅使用 --with-openssl=yes 指令且不存在 --with-gnutls 时,我没有收到任何错误。这些测试相隔 运行 分钟,涉及快速连续的 5000 次连接尝试(每秒大约打开和关闭 10 个连接)。没有修改其他 freetds 配置选项。两个测试之间的唯一区别是上面列出的指令。 RPM、freetds 配置文件等的构建和安装都是自动的,并且在测试之间没有变化。

发生错误时,在客户端报告为:

DBI connect('MYDBNAME','mydomain\myusername',...) failed:
[unixODBC][FreeTDS][SQL Server]Unable to connect to data source (SQL-08001) [state was 08001 now 01000]
[unixODBC][FreeTDS][SQL Server]Adaptive Server connection failed (SQL-01000)
[unixODBC][FreeTDS][SQL Server]Unexpected EOF from the server (SQL-01000) at ./dbconnectiontestlots.pl line 18

本次测试使用了 freetds-1.00.24-1.x86_64,直接从 freetds.org 下载,构建于 RHEL6。每次 运行ning rpmbuild 后,从 /root/rpmbuild/RPMS/ 文件夹安装以下 RPM:

386996 Jan 5 14:28 freetds-1.00.24-1.x86_64.rpm
187560 Jan 5 14:28 freetds-unixodbc-1.00.24-1.x86_64.rpm

unixODBC x86_64 2.2.14-14.el6也是通过yum直接安装的

Update: while this solution works on 32-bit and 64-bit RHEL6 systems it doesn't work on RHEL5 32-bit systems