Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libreSSL support #4676

Closed
cschlack opened this issue Jul 5, 2017 · 34 comments · Fixed by #5676
Closed

libreSSL support #4676

cschlack opened this issue Jul 5, 2017 · 34 comments · Fixed by #5676

Comments

@cschlack
Copy link

cschlack commented Jul 5, 2017

crystal version >= 0.22 fails using libressl:

_main.o: In function `add_options':
/usr/lib/crystal/src/openssl/ssl/context.cr:274: undefined reference to `SSL_CTX_set_options'
_main.o: In function `initialize':
/usr/lib/crystal/src/openssl/ssl/socket.cr:15: undefined reference to `SSL_get0_param'
/usr/lib/crystal/src/openssl/ssl/socket.cr:15: undefined reference to `X509_VERIFY_PARAM_set1_ip_asc'
/usr/lib/crystal/src/openssl/ssl/socket.cr:15: undefined reference to `X509_VERIFY_PARAM_set1_host'
collect2: error: ld returned 1 exit status

The problem seem to be here:

OPENSSL_110 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.1.0 libssl || printf %s false`.stringify != "false" }}

pkg-config --atleast-version=1.1.0 libssl

This is a check for libssl version >= 1.1.0
libreSSL reports (in my case) version 2.5.4

The libreSSL API follows (asfaik) openssl version 1.0.1

@ysbaddaden
Copy link
Contributor

We use the OpenSSL version numbers to determine which OpenSSL API we should follow. If LibreSSL uses a custom scheme, well... that's complicated :(

@cschlack
Copy link
Author

cschlack commented Jul 5, 2017

libgit2 (C) uses the following for example:

#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
// this is NOT openssl version 1.1.0
#endif

But there's no way we can access these constants from crystal, right?

Could get the library version using pkg-config --modversion libssl and the make sure the version is between 1.1.0 and 2.0.0 for openssl version 1.1.0 API support?

@ysbaddaden
Copy link
Contributor

Yes, Crystal can't access C headers at compile time. Is libressl installing another .pc file that could be checked for?

@ysbaddaden
Copy link
Contributor

Or what does lib/pkgconfig/openssl.pc says?

@cschlack
Copy link
Author

cschlack commented Jul 5, 2017

The package installs

/usr/lib/pkgconfig/libtls.pc
/usr/lib/pkgconfig/libcrypto.pc
/usr/lib/pkgconfig/libssl.pc
/usr/lib/pkgconfig/openssl.pc

Contents of /usr/lib/pkgconfig/openssl.pc:

#openssl pkg-config source file

prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: LibreSSL
Description: Secure Sockets Layer and cryptography libraries and tools
Version: 2.5.4
Requires: libssl libcrypto

@ysbaddaden
Copy link
Contributor

Sigh, that won't help.

@sdogruyol
Copy link
Member

Shall we close this or add a label?

@RX14
Copy link
Contributor

RX14 commented Sep 10, 2017

perhaps we could run something through the C preprocessor quickly to determine the status of LIBRESSL_VERSION_NUMBER. Or perhaps we should bat this over to the libressl guys, or the distro packagers, to come up with a solution.

@ysbaddaden
Copy link
Contributor

@RX14 the C preprocessor may be the only viable solution.

@ysbaddaden
Copy link
Contributor

ysbaddaden commented Sep 12, 2017

Apparently we can use SHLIB_VERSION_NUMBER to detect the API version, so this could help:

OPENSSL_VERSION = {{ `echo "#include <openssl/opensslv.h>\nSHLIB_VERSION_NUMBER" | cc -E -`.chomp.split('\n').last.id }}
puts OPENSSL_VERSION #=> 1.0.0 or 1.1

That doesn't help to detect features introduced in 1.0.2 thought (SHLIB reports 1.0.0), and we can't rely on OPENSSL_VERSION_NUMBER with LibreSSL. We could detect that LIBRESSL_VERSION_NUMBER is defined, then check OPENSSL_VERSION_NUMBER when it's not defined, but it's starting to be very complicated...

@valpackett
Copy link
Contributor

So why not assume that anything above 2.0.0 in pkg-config is LibreSSL?

@LVMBDV
Copy link

LVMBDV commented Dec 12, 2017

(Originally suggested by @Vaelatern on voidlinux/void-packages#9831)

LibreSSL has a new TLS library called libtls, different from the OpenSSL-compatible libssl. It could be used to detect if LibreSSL is present on the system. LibreSSL currently doesn't support OpenSSL's libssl API beyond version 1.0.1 so both version checks could be modified to simply check if libtls exists. And that --exists check would be replaced by a --atleast-version check when a new LibreSSL supporting newer OpenSSL APIs is released.

@valpackett
Copy link
Contributor

LIBRESSL_VERSION_NUMBER is better than searching for libtls

@LVMBDV
Copy link

LVMBDV commented Dec 12, 2017

Although LibreSSL/OpenSSL version conflict requires a hack to resolve, calling cc to get a version number is too hacky, in my opinion.

@Vaelatern
Copy link

cc to get a version number also breaks some cross building setups. A lot of code can be cross compiled, but then some configure script protests because of a trivial test.

@RX14
Copy link
Contributor

RX14 commented Dec 13, 2017

@LVMBDV just because libtls is present doesn't mean libssl refers to libressl. You could have a libtls from libressl and a libssl from openssl on the same system.

Yes, calling cc is a hack. If we don't want a hack the libressl guys should come up with a better way of detecting their ABI.

@RX14
Copy link
Contributor

RX14 commented Dec 13, 2017

Seems rust-openssl does a similar thing to attempt to detect libressl: parse the headers (we just get gcc to do that for us)

@ysbaddaden
Copy link
Contributor

Calling cc to parse actual C headers is no more a hack than guessing based on the pkg-config version. It's just the same level of issue for cross compilation: we parse a .h file instead of a .pc file (both expected by developers).

Guessing based on the presence of a library file (that we don't even use) is... well... just pure guess.

IMHO: using a C preprocessor is a good solution we have at hand. Just as hacky as guessing from pkg-config.

@vtambourine
Copy link

I'm having same problem on Crystal 0.24.1 (2017-12-22). Build an application fails with following error:

O-penS-S-L-5858S-S-L-5858C-ontext5858C-lient.o: In function `add_options':
/usr/lib/crystal/src/openssl/ssl/context.cr:274: undefined reference to `SSL_CTX_set_options'
O-penS-S-L-5858S-S-L-5858S-ocket5858C-lient.o: In function `initialize':
/usr/lib/crystal/src/openssl/ssl/socket.cr:15: undefined reference to `SSL_get0_param'
/usr/lib/crystal/src/openssl/ssl/socket.cr:15: undefined reference to `X509_VERIFY_PARAM_set1_ip_asc'
/usr/lib/crystal/src/openssl/ssl/socket.cr:15: undefined reference to `X509_VERIFY_PARAM_set1_host'
collect2: error: ld returned 1 exit status

openssl v1.0.2

@aisrael
Copy link

aisrael commented Feb 2, 2018

Am having the exact same issue as @vtambourine trying to build Amber using ysbaddaden/crystal-alpine as Docker base image.

@ysbaddaden
Copy link
Contributor

May someone that reproduces the issue prepare a pull request to replace pkg-config to use the cc preprocessor command instead, as proposed above? That would be awesome. Thanks!

@LVMBDV
Copy link

LVMBDV commented Feb 2, 2018

On it :)

jirutka referenced this issue in alpinelinux/aports Mar 24, 2018
@asterite asterite reopened this Apr 20, 2018
@asterite
Copy link
Member

Did anyone even try this on a Mac? I can't compile anything right now:

<stdin>:1:10: fatal error: 'openssl/opensslv.h' file not found
#include <openssl/opensslv.h>
         ^~~~~~~~~~~~~~~~~~~~
1 error generated.
Error in line 1: while requiring "./spec/compiler/codegen/macro_spec.cr"

in spec/compiler/codegen/macro_spec.cr:1: while requiring "../../spec_helper"

require "../../spec_helper"
^

in spec/spec_helper.cr:6: while requiring "../src/compiler/crystal/**"

require "../src/compiler/crystal/**"
^

in src/compiler/crystal/tools/playground/agent.cr:1: while requiring "http"

require "http"
^

in src/http.cr:2: while requiring "./http/**"

require "./http/**"
^

in src/http/client.cr:749: expanding macro

{% if !flag?(:without_openssl) %}
^

in macro 'macro_4626924080' /Users/asterite/Projects/crystal/src/http/client.cr:749, line 2:

   1. 
>  2.   require "openssl"

while requiring "openssl"
in src/openssl.cr:1: while requiring "./openssl/lib_ssl"

require "./openssl/lib_ssl"
^

in src/openssl/lib_ssl.cr:1: while requiring "./lib_crypto"

require "./lib_crypto"
^

in src/openssl/lib_crypto.cr:1: expanding macro

{% begin %}
^

in src/openssl/lib_crypto.cr:7: error executing command: echo "#include <openssl/opensslv.h>
OPENSSL_VERSION_NUMBER" | cc  -E -, got exit status 1:

# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 341 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2

OPENSSL_VERSION_NUMBER



    OPENSSL_VERSION = {{ system("echo \"#include <openssl/opensslv.h>\nOPENSSL_VERSION_NUMBER\" | " +
                         ^~~~~~

Mac doesn't use OpenSSL, so those headers will never exit there.

How can we fix this?

@ysbaddaden
Copy link
Contributor

Sigh. I'm bored with C bindings. I wish we could just do require "openssl.h" and access whatever on LibC —adding a pass to have libclang parse all headers to resolve all LibC nodes in the AST.

Maybe the current hack can be fixed with a || echo "" so it doesn't fail anymore?

@LVMBDV
Copy link

LVMBDV commented Apr 20, 2018

Maybe the current hack can be fixed with a || echo "" so it doesn't fail anymore?

Looks like there was a fallback to true already but I munged it while turning a backtick string to a system() call.

EDIT: Nevermind that || true was for the pkg-config command. You are absolutely right. I am on it.

Mac doesn't use OpenSSL, so those headers will never exit there.

AFAIK (from my research for that PR) Mac used OpenSSL AND included its headers until Sierra or something. Then they clumsily removed the headers between some subversion of Sierra until they switched to LibreSSL in High Sierra. From the blog posts I found, most developers just used homebrew openssl the during that shitshow.

@asterite
Copy link
Member

@LVMBDV Good catch, thank you!

@RX14
Copy link
Contributor

RX14 commented May 5, 2018

This has all been reverted - we need to come up with a better way of detecting libressl.

@j8r
Copy link
Contributor

j8r commented Apr 16, 2019

Fixed with #6917

@vinaysolanki
Copy link

I first encountered this issue while install Crystal on my Mac and second time while installing icr (Crystal Interactive Shell)

Easiest way I was able to make it work was by doing:

ln -s /usr/local/Cellar/openssl/1.0.2q/lib/libssl.dylib /usr/local/lib/

(Make sure your openssl version is correct)

and then

ln -s /usr/local/opt/openssl/lib/libcrypto.dylib /usr/local/lib

Then continue with installation...

@ndbroadbent
Copy link

I also ran into a similar problem while compiling Crystal and running the specs on my Mac (Catalina 10.15.1):

$ make all
Using /usr/local/opt/llvm/bin/llvm-config [version=9.0.0]
c++ -c  -o src/llvm/ext/llvm_ext.o src/llvm/ext/llvm_ext.cc -I/usr/local/Cellar/llvm/9.0.0_1/include -std=c++11 -stdlib=libc++   -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
cc -fPIC    -c -o src/ext/sigfault.o src/ext/sigfault.c
ar -rcs src/ext/libcrystal.a src/ext/sigfault.o
CRYSTAL_CONFIG_PATH="/Users/ndbroadbent/code/crystal/src" CRYSTAL_CONFIG_LIBRARY_PATH="/usr/local/Cellar/crystal/0.31.1/embedded/lib" CRYSTAL_CONFIG_BUILD_COMMIT="54e68f0b3" ./bin/crystal build  -o .build/crystal src/compiler/crystal.cr -D without_openssl -D without_zlib
$ make spec
Using /usr/local/opt/llvm/bin/llvm-config [version=9.0.0]
./bin/crystal build  --exclude-warnings spec/std --exclude-warnings spec/compiler -o .build/all_spec spec/all_spec.cr
Using compiled compiler at .build/crystal
n
ld: library not found for -lssl (this usually means you need to install the development package for libssl)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Error: execution of command failed with code: 1: `cc "${@}" -o '/Users/ndbroadbent/code/crystal/.build/all_spec'  -rdynamic  -lgmp -lxml2 -lyaml -lz `command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libssl || printf %s '-lssl -lcrypto'` `command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libcrypto || printf %s '-lcrypto'` /Users/ndbroadbent/code/crystal/src/llvm/ext/llvm_ext.o `/usr/local/opt/llvm/bin/llvm-config --libs --system-libs --ldflags 2> /dev/null` -lstdc++ -lpcre /usr/local/Cellar/crystal/0.31.1/embedded/lib/libgc.a -lpthread /Users/ndbroadbent/code/crystal/src/ext/libcrystal.a -levent -liconv -ldl -L/usr/local/Cellar/crystal/0.31.1/embedded/lib -L/usr/lib -L/usr/local/lib`
make: *** [.build/all_spec] Error 1

Then I ran these ln commands:

$ ln -s /usr/local/Cellar/[email protected]/1.1.1d/lib/libssl.dylib /usr/local/lib/
$ ln -s /usr/local/opt/openssl/lib/libcrypto.dylib /usr/local/lib/

make spec crashed with a different error:

$ make spec
Using /usr/local/opt/llvm/bin/llvm-config [version=9.0.0]
./bin/crystal build  --exclude-warnings spec/std --exclude-warnings spec/compiler -o .build/all_spec spec/all_spec.cr
Using compiled compiler at .build/crystal
Undefined symbols for architecture x86_64:
  "_ERR_load_crypto_strings", referenced from:
      ___crystal_main in _main.o
  "_EVP_MD_CTX_create", referenced from:
      _*OpenSSL::Digest::new_evp_mt_ctx<String>:Pointer(LibCrypto::EVP_MD_CTX_Struct) in O-penS-S-L-5858D-igest.o
      _*OpenSSL::Digest#clone:OpenSSL::Digest in O-penS-S-L-5858D-igest.o
  "_EVP_MD_CTX_destroy", referenced from:
      _*OpenSSL::Digest#finalize:Nil in O-penS-S-L-5858D-igest.o
      _*OpenSSL::Digest#clone:OpenSSL::Digest in O-penS-S-L-5858D-igest.o
  "_OPENSSL_add_all_algorithms_noconf", referenced from:
      ___crystal_main in _main.o
  "_SSL_library_init", referenced from:
      ___crystal_main in _main.o
  "_SSL_load_error_strings", referenced from:
      ___crystal_main in _main.o
  "_SSLv23_method", referenced from:
      _*OpenSSL::SSL::Context::default_method:Pointer(Void) in O-penS-S-L-5858S-S-L-5858C-ontext.o
  "_sk_free", referenced from:
      _~procProc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      _~proc2Proc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      _~proc3Proc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      _~proc4Proc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      _~proc5Proc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      _~proc6Proc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      _~proc7Proc(Pointer(Void), Nil)@src/openssl/ssl/hostname_validation.cr:66 in _main.o
      ...
  "_sk_num", referenced from:
      _*OpenSSL::SSL::HostnameValidation::matches_subject_alternative_name<String, Pointer(Void)>:OpenSSL::SSL::HostnameValidation::Result in O-penS-S-L-5858S-S-L-5858H-ostnameV-alidation.o
  "_sk_pop_free", referenced from:
      _*OpenSSL::SSL::HostnameValidation::matches_subject_alternative_name<String, Pointer(Void)>:OpenSSL::SSL::HostnameValidation::Result in O-penS-S-L-5858S-S-L-5858H-ostnameV-alidation.o
  "_sk_value", referenced from:
      _*OpenSSL::SSL::HostnameValidation::matches_subject_alternative_name<String, Pointer(Void)>:OpenSSL::SSL::HostnameValidation::Result in O-penS-S-L-5858S-S-L-5858H-ostnameV-alidation.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Error: execution of command failed with code: 1: `cc "${@}" -o '/Users/ndbroadbent/code/crystal/.build/all_spec'  -rdynamic  -lgmp -lxml2 -lyaml -lz `command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libssl || printf %s '-lssl -lcrypto'` `command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libcrypto || printf %s '-lcrypto'` /Users/ndbroadbent/code/crystal/src/llvm/ext/llvm_ext.o `/usr/local/opt/llvm/bin/llvm-config --libs --system-libs --ldflags 2> /dev/null` -lstdc++ -lpcre /usr/local/Cellar/crystal/0.31.1/embedded/lib/libgc.a -lpthread /Users/ndbroadbent/code/crystal/src/ext/libcrystal.a -levent -liconv -ldl -L/usr/local/Cellar/crystal/0.31.1/embedded/lib -L/usr/lib -L/usr/local/lib`
make: *** [.build/all_spec] Error 1

I've installed all these required libraries using brew.

@asterite
Copy link
Member

asterite commented Dec 2, 2019

@ndbroadbent In https://crystal-lang.org/install/on_mac_os/ there's an alternative fix that might work. Could you try it out?

brew install openssl
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/opt/openssl/lib/pkgconfig

@ndbroadbent
Copy link

ndbroadbent commented Dec 2, 2019

I saw that I needed LLVM 8.0, so I downgraded:

$ brew install llvm@8
$ brew unlink llvm
$ brew link --force --overwrite llvm@8
Linking /usr/local/Cellar/llvm@8/8.0.1_1... 474 symlinks created

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/llvm@8/bin:$PATH"' >> ~/.bash_profile

Then ran that command to add it to my PATH. (But LLVM 8.0 didn't fix the ld: library not found for -lssl error.)

I also removed the symlinks I added earlier:

$ rm /usr/local/lib/libssl.dylib
$ rm /usr/local/lib/libcrypto.dylib

And saw this article about issues with Crystal and openssl. They recommended setting the PKG_CONFIG_PATH variable:

export PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig"

After that, I got this error:

$ make spec
Using /usr/local/opt/llvm@8/bin/llvm-config [version=8.0.1]
./bin/crystal build  --exclude-warnings spec/std --exclude-warnings spec/compiler -o .build/all_spec spec/all_spec.cr
Using compiled compiler at .build/crystal
Undefined symbols for architecture x86_64:
  "llvm::MetadataTracking::track(void*, llvm::Metadata&, llvm::PointerUnion<llvm::MetadataAsValue*, llvm::Metadata*>)", referenced from:
      llvm::MetadataTracking::track(llvm::Metadata*&) in llvm_ext.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Error: execution of command failed with code: 1: `cc "${@}" -o '/Users/ndbroadbent/code/crystal/.build/all_spec'  -rdynamic  -lgmp -lxml2 -lyaml -lz `command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libssl || printf %s '-lssl -lcrypto'` `command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libcrypto || printf %s '-lcrypto'` /Users/ndbroadbent/code/crystal/src/llvm/ext/llvm_ext.o `/usr/local/opt/llvm@8/bin/llvm-config --libs --system-libs --ldflags 2> /dev/null` -lstdc++ -lpcre /usr/local/Cellar/crystal/0.31.1/embedded/lib/libgc.a -lpthread /Users/ndbroadbent/code/crystal/src/ext/libcrystal.a -levent -liconv -ldl -L/usr/local/Cellar/crystal/0.31.1/embedded/lib -L/usr/lib -L/usr/local/lib`
make: *** [.build/all_spec] Error 1

I realized that's probably because I changed the LLVM version and/or setting PKG_CONFIG_PATH, so I ran make clean and then make all to rebuild the crystal compiler. Then I was able to get the specs running!


I saw that the Circle CI config already has a test_darwin job that tests on MacOS. So I was thinking that it would be great to add a CI job that installs homebrew and the required packages, and makes sure that developers don't run into issues like this. But then I realized that it does already do this! And the only problem is that the docs in the wiki got out of sync with the CI commands.

It would be really nice if there was some way to automate this and use a shared file somewhere, so that the docs + CI are always in sync.

@ndbroadbent
Copy link

ndbroadbent commented Dec 2, 2019

Thanks @asterite, I also found that PKG_CONFIG_PATH fix in this article!

I installed a fresh copy of macOS last week, and I already had openssl installed, so it would be great if Crystal could compile on a Mac without any errors or troubleshooting steps like this. Would it be possible to detect and fix this issue automatically?

@appcypher
Copy link

In addition to the PKG_CONFIG_PATH env variable, I had to brew install pkg-config to get this working. It was not mentioned here but it is in the CI job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.