Nim icon indicating copy to clipboard operation
Nim copied to clipboard

SSL issue CONNECT_CR_SRVR_HELLO on macOS

Open anta40 opened this issue 1 year ago • 7 comments

Consider this code:

import httpclient

var client = newHttpClient()
echo client.getContent "http://rosettacode.org/wiki/Rosetta_Code"

On macOS 15 M2, compiled with Nim 2.2.0 (installed via Homebrew):

$ nim c -d:release -d:ssl test01.nim
Hint: used config file '/opt/homebrew/Cellar/nim/2.2.2/nim/config/nim.cfg' [Conf]
Hint: used config file '/opt/homebrew/Cellar/nim/2.2.2/nim/config/config.nims' [Conf]
.........................................................................................................................................................
Hint:  [Link]
Hint: mm: orc; threads: on; opt: speed; options: -d:release
76365 lines; 0.814s; 140.973MiB peakmem; proj: /Users/andretampubolon/Codes/Nim/test01.nim; out: /Users/andretampubolon/Codes/Nim/test01 [SuccessX]
$./test01
net.nim(578)             raiseSSLError
httpclient.nim(986)      newConnection
Error: unhandled exception: error:1400442E:SSL routines:CONNECT_CR_SRVR_HELLO:tlsv1 alert protocol version [SslError]

On Debian 64, compiled with Nim 2.2.0 (installed via choosenim):

$ nim c -d:release -d:ssl test01.nim
Hint: used config file '/home/anta40/.choosenim/toolchains/nim-2.2.0/config/nim.cfg' [Conf]
Hint: used config file '/home/anta40/.choosenim/toolchains/nim-2.2.0/config/config.nims' [Conf]
........................................................................................................................................................
Hint:  [Link]
Hint: mm: orc; threads: on; opt: speed; options: -d:release
75605 lines; 1.555s; 141.062MiB peakmem; proj: /home/anta40/Codes/test01.nim; out: /home/anta40/Codes/test01 [SuccessX]
$./test01
<!DOCTYPE html>
<html class="client-nojs vector-feature-language-in-header-enabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-0 vector-feature-appearance-pinned-clientpref-1 vector-feature-night-mode-disabled skin-theme-clientpref-day vector-toc-available" lang="en" dir="ltr">
<head>
...
...
...

anta40 avatar Feb 07 '25 16:02 anta40

We still have protSSLv23 as the default, it may have been removed in a newer OpenSSL 3 version installed on macos 15. Try this:

import httpclient, net

var client = newHttpClient(sslContext = newContext(protTLSv1))
echo client.getContent "http://rosettacode.org/wiki/Rosetta_Code"

metagn avatar Feb 07 '25 17:02 metagn

Perhaps this is specific macOS' OpenSSL issue

import httpclient, net

#var client = newHttpClient()
var client = newHttpClient(sslContext = newContext(protTLSv1))

# error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
#echo client.getContent("http://rosettacode.org/wiki/Rosetta_Code")

# OK
#echo client.getContent("https://gcc.gnu.org")

# OK
#echo client.getContent("https://kernel.org")

# error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
#echo client.getContent("http://amazon.com")

# OK
#echo client.getContent("http://samsung.com")

# OK
#echo client.getContent("http://quora.com")

# error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
#echo client.getContent("https://ebay.com")

# OK
#echo client.getContent("https://edition.cnn.com")

# OK
echo client.getContent("https://www.apple.com")

All works fine on Debian, though.

anta40 avatar Feb 08 '25 04:02 anta40

On my machine (MacOS 15.4, Intel), run with -d:nimDebugDlOpen, it fails to locate a modern OpenSSL library:

dlopen(libcrypto.3.dylib, 0x0002): tried: 'libcrypto.3.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcrypto.3.dylib' (no such file), '/usr/lib/libcrypto.3.dylib' (no such file, not in dyld cache), 'libcrypto.3.dylib' (no such file)
dlopen(libcrypto.1.1.dylib, 0x0002): tried: 'libcrypto.1.1.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcrypto.1.1.dylib' (no such file), '/usr/lib/libcrypto.1.1.dylib' (no such file, not in dyld cache), 'libcrypto.1.1.dylib' (no such file)
dlopen(libcrypto.38.dylib, 0x0002): tried: 'libcrypto.38.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcrypto.38.dylib' (no such file), '/usr/lib/libcrypto.38.dylib' (no such file, not in dyld cache), 'libcrypto.38.dylib' (no such file)
dlopen(libcrypto.39.dylib, 0x0002): tried: 'libcrypto.39.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcrypto.39.dylib' (no such file), '/usr/lib/libcrypto.39.dylib' (no such file, not in dyld cache), 'libcrypto.39.dylib' (no such file)
dlopen(libssl.3.dylib, 0x0002): tried: 'libssl.3.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibssl.3.dylib' (no such file), '/usr/lib/libssl.3.dylib' (no such file, not in dyld cache), 'libssl.3.dylib' (no such file)
dlopen(libssl.1.1.dylib, 0x0002): tried: 'libssl.1.1.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibssl.1.1.dylib' (no such file), '/usr/lib/libssl.1.1.dylib' (no such file, not in dyld cache), 'libssl.1.1.dylib' (no such file)
dlopen(libssl.38.dylib, 0x0002): tried: 'libssl.38.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibssl.38.dylib' (no such file), '/usr/lib/libssl.38.dylib' (no such file, not in dyld cache), 'libssl.38.dylib' (no such file)
dlopen(libssl.39.dylib, 0x0002): tried: 'libssl.39.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibssl.39.dylib' (no such file), '/usr/lib/libssl.39.dylib' (no such file, not in dyld cache), 'libssl.39.dylib' (no such file)
dlopen(libssl.41.dylib, 0x0002): tried: 'libssl.41.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibssl.41.dylib' (no such file), '/usr/lib/libssl.41.dylib' (no such file, not in dyld cache), 'libssl.41.dylib' (no such file)
net.nim(578)             raiseSSLError
httpclient.nim(986)      newConnection
Error: unhandled exception: error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure [SslError]

However, I do have OpenSSL installed:

$ brew info openssl
==> openssl@3: stable 3.4.1 (bottled)
Cryptography and SSL/TLS Toolkit
https://openssl-library.org
Installed
/usr/local/Cellar/openssl@3/3.4.0 (7,236 files, 33.8MB) *

~~Setting DYLD_LIBRARY_PATH seems not working, but~~ soft linking libssl.3.dylib and libcrypto.3.dylib to the working directory does work.

Edit: Setting DYLD_LIBRARY_PATH does work, just don't run with the -r flag. Running the compiled binary with DYLD_LIBRARY_PATH set is OK.

qszhu avatar Apr 06 '25 02:04 qszhu

Maybe possibly related (I only mention it as Homebrew was mentioned in the first post), brew install [email protected] gives CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure.

Recently (the past week or so), brew install libzip gives a similar error:

==> Fetching libzip
==> Downloading https://raw.githubusercontent.com/Homebrew/homebrew-core/8919b08
######################################################################### 100.0%
==> Downloading https://libzip.org/download/libzip-1.11.3.tar.xz
#=#=#                                                                          
curl: (35) error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
Error: libzip: Failed to download resource "libzip"
Download failed: https://libzip.org/download/libzip-1.11.3.tar.xz

So, it probably is a Mac SSL issue. I am seeing it on Catalina, which is a deprecated OS version, w.r.t. Homebrew, which made me wonder if it was a deprecated OS version issue. However, if you are also seeing it on macOS 15.4, then it may be a generic Mac issue.

greenonline avatar Apr 06 '25 14:04 greenonline

I noticed the following code, which was introduced in this commit:

  when defined(osx):
    const versions = "(.3|.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.48|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"

On my machine, it finds libcrypto.41.dylib and libssl.43.dylib. (Interestingly, despite the mismatched versions, these files are reported to be under /usr/lib, but actually there is none:

$ ls /usr/lib/libcrypto*
zsh: no matches found: /usr/lib/libcrypto*
$ ls /usr/lib/libssl*
zsh: no matches found: /usr/lib/libssl*

After some trial and error, the first version that works on my machine is .46, i.e, libcrypto.46.dylib and libssl.46.dylib. Would it be safe to change the matching orders? @metagn

P.S. Quote from here

... Use of the Apple-provided OpenSSL libraries by apps is strongly discouraged.

To ensure compatibility, if your app depends on OpenSSL, you should compile it yourself and statically link a known version of OpenSSL into your app. ...

qszhu avatar Apr 07 '25 09:04 qszhu

The actual original commit adding that line is https://github.com/nim-lang/Nim/commit/98ef545bedd27959a568a8e99f889a32c3ae1f72, it was changed in https://github.com/nim-lang/Nim/commit/2dcfd732609a2cfa805e5a94cc105399a2f18632 but this commit was reverted. 98ef545bedd27959a568a8e99f889a32c3ae1f72 itself is also a conditional revert of https://github.com/nim-lang/Nim/commit/3ed833198b95e74de29d2b0905247ead52478aa3 for osx. You could just remove the when defined(osx): branch and see if it breaks anything now, but maybe a special order is still needed to deal with #10281, possibly just changing the order of the .xx versions as you said to give:

(.3|.1.1|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)

such that the 1.0.2 line is at the end after the .xx versions as it seems to be the version that caused problems in #10281. But it is not exactly clear what the problem actually was in #10281.

metagn avatar Apr 07 '25 09:04 metagn

On my machine, removing when defined(osx) and using const versions = "(.3|.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|)" matches version .0.9.8, which gives an error could not import: X509_check_host. This makes sense since X509_check_host is only available from 1.0.2. This is confusing to me since net seems to be expecting specific openssl versions, while the openssl wrapper is still matching older versions.

Version .46 works because these .xxs are libressl versions and X509_check_host was added from libressl 2.5.0, which seems to correspond to a much earlier version.

(.3|.1.1|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|) does work on my machine. It matches libcrypto.46.dylib and libssl.48.dylib.

qszhu avatar Apr 08 '25 06:04 qszhu