microsoft / msphpsql

Microsoft Drivers for PHP for SQL Server
MIT License
1.79k stars 371 forks source link

Can't Connect to Server, TCP Provider: Error code 0x2746. #1453

Closed savecol closed 1 day ago

savecol commented 1 year ago

PHP version
PHP 8.1

PHP SQLSRV or PDO_SQLSRV version
PHP sqlsrv 5.111

Microsoft ODBC Driver versions tested
[ODBC Driver 18 for SQL Server] libmsodbcsql-18.2.so.1.1

[ODBC Driver 17 for SQL Server] libmsodbcsql-17.10.so.2.1

SQL Server version
SQL Server 2014 SP3 - Windows Server 2012

Client operating system
Fedora 38

Problem description
I'm trying to connect to a Windows Server machine that has a SQL Server 2014 SP3 instance, but neither php driver or sqlcdm cli connect to the server because they use TLS v1 in the ssl negotiation where TLS v1.2 should be used as shown in the packet trace. The connection gets terminated by the server image

Expected behavior and actual behavior
The connection to the server should be made using TLS v1.2, as shown in this packet trace generated from a connection using DBeaver with the Microsoft JDBC SQL Server driver.

image

Tried changing the openssl.cnf file to have

[system_default_sect] MinProtocol = TLSv1.2 CipherString = DEFAULT@SECLEVEL=0 ## tried with 1 too
# CipherString = DEFAULT@SECLEVEL=1

but none of these worked, the connection is still forced to use TLS v1

v-makouz commented 1 year ago

Which version of OpenSSL are you using? Also, do you know which ODBC driver PHP calls 17 or 18?

savecol commented 1 year ago

My system uses OpenSSL 3.0.8 and PHP is calling the 18 version of the ODBC driver image

v-makouz commented 1 year ago

Looks like all of those are fine. Can you look at the server event log right after a failed attempt to see what it says? Usually the log would be in something like: C:\Program Files\Microsoft SQL Server\MSSQL<Your Version>.MSSQLSERVER\MSSQL\Log\ERRORLOG

savecol commented 1 year ago

Sadly I haven't been granted access to the server yet, I'm working on that. What feels odd is even the cli trying to make a connection through TLS v1 where my system is configured to allow a minimum of v1.2 (the default) like the JDBC dirver does. Should I attach my openssl conf?.

Also, is there a way to force the TLS version through ODBC config? I feel that could work.

savecol commented 1 year ago

Tried opening an ODBC connection using Devart ODBC driver and connected immediately, w/o having to deal with any configuration, but using Microsoft Drivers fails every time.

This is the packet register from the Devart driver connection

image

v-makouz commented 1 year ago

Can you provide contents of the Client Hello, Server Hello, Client Key Exchange, and Change Cipher Spec from the Wireshark log, for both working and non-working case? We need to see what the client and server are negotiating.

Microsoft ODBC Driver should use the system setting for TLS, maybe Devart ODBC doesn't? I'm not familiar with it, so it'll be interesting to see what they negotiate differently.

Lathanderjk commented 1 year ago

I have similar problem on RHEL 9, it's probably caused by unexpected eof. I tried to block access to OpenSSL 3 library forcing libmsodbcsql use OpenSSL 1.1 (for compat-openssl11 package) and everything works as expected.(blocked by mounting custom /usr/lib64 in cgroup) It`s same with msodbcsql17 or 18, no configuration change(legacy, TLS version,SECLEVEL) helps, i also whent throught similiar path with tcpdump and wireshark and notice TLS 1.0...

Building OpenSSL with forced SSL_OP_IGNORE_UNEXPECTED_EOF helps to get working openssl s_client :1433 without needing -ignore_unexpected_eof but not for msodbcsql... maybe it`s not unexpected_eof after all.

utkalRP commented 1 year ago

We have same problem with OpenSSL 3.0.8, MS ODBC Driver 17, unixODBC2.3.11, SQL Server 2014. Client keeps waiting for 'Server Hello' but server doesn't reply. Even openssl is not able to connect to sql server, not even sslcan shows the server ciphers. Everything works with OpenSSL 1.1.1. Also the client uses TLSv1 with OpenSLL 3.0.8 but it's TLSv1.2 with OpenSLL 1.1.1 !!!

@Lathanderjk Did you find a solution to it? Could you please share the steps?

taozuhong commented 1 year ago

SQLServer could provide a library that could use by C/C++/PHP/ODBC/Ruby/Nodejs/JDBC?

ODBC is not friendly for App, can't package it directly, and it's huge.

ngochangngo52 commented 11 months ago

Just run this in your shell: update-crypto-policies --set DEFAULT:SHA1

thdaguin commented 7 months ago

Works for me on Docker Ubuntu 22.04 ODBC Driver 18 for SQL Server

In /etc/ssl/openssl.cnf file :

1/ change openssl_conf = openssl_init to openssl_conf = default_conf

2/ add those lines at the end of file

[ default_conf ]
ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT@SECLEVEL=0

Then test it with

isql -v -k "Driver={ODBC Driver 18 for SQL Server};Server=xx;Database=xx;UID=xx;PWD=xx;Encrypt=no"

junionestor commented 5 months ago

Works for me on Docker Ubuntu 22.04 ODBC Driver 18 for SQL Server

In /etc/ssl/openssl.cnf file :

1/ change openssl_conf = openssl_init to openssl_conf = default_conf

2/ add those lines at the end of file

[ default_conf ]
ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT@SECLEVEL=0

Then test it with

isql -v -k "Driver={ODBC Driver 18 for SQL Server};Server=xx;Database=xx;UID=xx;PWD=xx;Encrypt=no"

worked for me on same environment, thank you!

juresaht2 commented 1 month ago

A cleaner solution for RHEL9: https://stackoverflow.com/a/75944492/2897386

update-crypto-policies --set LEGACY

You might have to reboot afterwards.

v-makouz commented 1 day ago

This is not a PHP driver issue, but rather compatibility between OpenSSL and SQL Server. Starting with OpenSSL 3.0 TLSv1.0 and TLSv1.1 were deprecated and are not allowed by default, requiring at least TLSv1.2. Many older servers don't have TLSv1.2, thus the connection fails. The ideal solution is to patch or upgrade the server. If that's not possible, one can configure OpenSSL to still allow use of older protocols, but it's best to consult OpenSSL documentation or support for the most up to date instructions for how to do so.