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

Fix: detect OpenSSL/LibreSSL from official C headers #7245

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/ci
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ format() {
prepare_build() {
on_linux verify_linux_environment

on_osx brew install crystal
on_osx brew install crystal openssl

# Make sure binaries from llvm are available in PATH
on_osx brew install jq
Expand Down
12 changes: 6 additions & 6 deletions spec/std/openssl/ssl/context_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe OpenSSL::SSL::Context do
context.should be_a(OpenSSL::SSL::Context::Client)
context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::NONE)
context.options.no_ssl_v3?.should_not be_true
{% if compare_versions(LibSSL::OPENSSL_VERSION, "1.1.1") >= 0 %}
{% if LibSSL::OPENSSL_VERSION_NUMBER >= 0x1_01_01_00_0 %}
context.modes.should eq(OpenSSL::SSL::Modes::AUTO_RETRY)
{% else %}
context.modes.should eq(OpenSSL::SSL::Modes::None)
Expand All @@ -67,7 +67,7 @@ describe OpenSSL::SSL::Context do
context.should be_a(OpenSSL::SSL::Context::Server)
context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::NONE)
context.options.no_ssl_v3?.should_not be_true
{% if compare_versions(LibSSL::OPENSSL_VERSION, "1.1.1") >= 0 %}
{% if LibSSL::OPENSSL_VERSION_NUMBER >= 0x1_01_01_00_0 %}
context.modes.should eq(OpenSSL::SSL::Modes::AUTO_RETRY)
{% else %}
context.modes.should eq(OpenSSL::SSL::Modes::None)
Expand Down Expand Up @@ -162,7 +162,7 @@ describe OpenSSL::SSL::Context do
context.verify_mode.should eq(OpenSSL::SSL::VerifyMode::PEER)
end

{% if compare_versions(LibSSL::OPENSSL_VERSION, "1.0.2") >= 0 %}
{% if LibSSL::OPENSSL_VERSION_NUMBER >= 0x1_00_02_00_0 || LibSSL::LIBRESSL_VERSION_NUMBER >= 0x2_05_00_00__0 %}
it "alpn_protocol=" do
context = OpenSSL::SSL::Context::Client.insecure
context.alpn_protocol = "h2"
Expand Down Expand Up @@ -203,13 +203,13 @@ describe OpenSSL::SSL::Context do
expect_raises(ArgumentError, "missing private key") do
OpenSSL::SSL::Context::Client.from_hash({} of String => String)
end
expect_raises(OpenSSL::Error, "SSL_CTX_use_PrivateKey_file: error:02001002:system library:fopen:No such file or directory") do
expect_raises(OpenSSL::Error, /SSL_CTX_use_PrivateKey_file: .+No such file or directory/) do
OpenSSL::SSL::Context::Client.from_hash({"key" => nonexistent})
end
expect_raises(ArgumentError, "missing certificate") do
OpenSSL::SSL::Context::Client.from_hash({"key" => private_key})
end
expect_raises(OpenSSL::Error, "SSL_CTX_use_certificate_chain_file: error:02001002:system library:fopen:No such file or directory") do
expect_raises(OpenSSL::Error, /SSL_CTX_use_certificate_chain_file: .+No such file or directory/) do
OpenSSL::SSL::Context::Client.from_hash({"key" => private_key, "cert" => nonexistent})
end
expect_raises(ArgumentError, "Invalid SSL context: missing CA certificate") do
Expand All @@ -221,7 +221,7 @@ describe OpenSSL::SSL::Context do
expect_raises(ArgumentError, "Invalid SSL context: missing CA certificate") do
OpenSSL::SSL::Context::Client.from_hash({"key" => private_key, "cert" => certificate, "verify_mode" => "peer"})
end
expect_raises(OpenSSL::Error, "SSL_CTX_load_verify_locations: error:02001002:system library:fopen:No such file or directory") do
expect_raises(OpenSSL::Error, /SSL_CTX_load_verify_locations: .+No such file or directory/) do
OpenSSL::SSL::Context::Client.from_hash({"key" => private_key, "cert" => certificate, "ca" => nonexistent})
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/openssl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ module OpenSSL
alias Options = LibSSL::Options
alias VerifyMode = LibSSL::VerifyMode
alias ErrorType = LibSSL::SSLError
{% if compare_versions(LibSSL::OPENSSL_VERSION, "1.0.2") >= 0 %}
{% if LibSSL::OPENSSL_VERSION_NUMBER >= 0x1_00_02_00_0 %}
alias X509VerifyFlags = LibCrypto::X509VerifyFlags
{% end %}

Expand Down
10 changes: 5 additions & 5 deletions src/openssl/bio.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ require "./lib_crypto"
# :nodoc:
struct OpenSSL::BIO
def self.get_data(bio) : Void*
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if LibCrypto::OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
LibCrypto.BIO_get_data(bio)
{% else %}
bio.value.ptr
{% end %}
end

def self.set_data(bio, data : Void*)
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if LibCrypto::OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
LibCrypto.BIO_set_data(bio, data)
{% else %}
bio.value.ptr = data
Expand Down Expand Up @@ -65,7 +65,7 @@ struct OpenSSL::BIO
end

create = LibCrypto::BioMethodCreate.new do |bio|
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if LibCrypto::OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
LibCrypto.BIO_set_shutdown(bio, 1)
LibCrypto.BIO_set_init(bio, 1)
# bio.value.num = -1
Expand All @@ -82,10 +82,10 @@ struct OpenSSL::BIO
1
end

{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if LibCrypto::OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
biom = LibCrypto.BIO_meth_new(Int32::MAX, "Crystal BIO")

{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.1") >= 0 %}
{% if LibCrypto::OPENSSL_VERSION_NUMBER >= 0x1_01_01_00_0 %}
LibCrypto.BIO_meth_set_write_ex(biom, bwrite_ex)
LibCrypto.BIO_meth_set_read_ex(biom, bread_ex)
{% else %}
Expand Down
33 changes: 13 additions & 20 deletions src/openssl/lib_crypto.cr
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
{% begin %}
lib LibCrypto
{% from_libressl = (`hash pkg-config 2> /dev/null || printf %s false` != "false") &&
(`test -f $(pkg-config --silence-errors --variable=includedir libcrypto)/openssl/opensslv.h || printf %s false` != "false") &&
(`printf "#include <openssl/opensslv.h>\nLIBRESSL_VERSION_NUMBER" | ${CC:-cc} $(pkg-config --cflags --silence-errors libcrypto || true) -E -`.chomp.split('\n').last != "LIBRESSL_VERSION_NUMBER") %}
{% ssl_version = `hash pkg-config 2> /dev/null && pkg-config --silence-errors --modversion libcrypto || printf %s 0.0.0`.split.last.gsub(/[^0-9.]/, "") %}

{% if from_libressl %}
LIBRESSL_VERSION = {{ ssl_version }}
OPENSSL_VERSION = "0.0.0"
{% else %}
LIBRESSL_VERSION = "0.0.0"
OPENSSL_VERSION = {{ ssl_version }}
{% end %}
{{ `#{__DIR__}/version.sh libcrypto` }}
end
{% end %}

@[Link(ldflags: "`command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libcrypto || printf %s '-lcrypto'`")]
{% if LibCrypto::LDFLAGS %}
@[Link(ldflags: "{{LibCrypto::LDFLAGS.id}} -lcrypto")]
{% else %}
@[Link(ldflags: "`command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libcrypto || printf %s '-lcrypto'`")]
{% end %}
lib LibCrypto
alias Char = LibC::Char
alias Int = LibC::Int
Expand Down Expand Up @@ -66,7 +59,7 @@ lib LibCrypto
alias BioMethodDestroy = Bio* -> Int
alias BioMethodCallbackCtrl = (Bio*, Int, Void*) -> Long

{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
type BioMethod = Void
{% else %}
struct BioMethod
Expand All @@ -90,7 +83,7 @@ lib LibCrypto
fun BIO_set_init(Bio*, Int)
fun BIO_set_shutdown(Bio*, Int)

{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
fun BIO_meth_new(Int, Char*) : BioMethod*
fun BIO_meth_set_read(BioMethod*, BioMethodReadOld)
fun BIO_meth_set_write(BioMethod*, BioMethodWriteOld)
Expand All @@ -101,7 +94,7 @@ lib LibCrypto
fun BIO_meth_set_destroy(BioMethod*, BioMethodDestroy)
fun BIO_meth_set_callback_ctrl(BioMethod*, BioMethodCallbackCtrl)
{% end %}
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.1") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_01_00_0 %}
fun BIO_meth_set_read_ex(BioMethod*, BioMethodRead)
fun BIO_meth_set_write_ex(BioMethod*, BioMethodWrite)
{% end %}
Expand Down Expand Up @@ -174,7 +167,7 @@ lib LibCrypto
fun evp_md_block_size = EVP_MD_block_size(md : EVP_MD) : LibC::Int
fun evp_digestfinal_ex = EVP_DigestFinal_ex(ctx : EVP_MD_CTX, md : UInt8*, size : UInt32*) : Int32

{% if compare_versions(OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
fun evp_md_ctx_new = EVP_MD_CTX_new : EVP_MD_CTX
fun evp_md_ctx_free = EVP_MD_CTX_free(ctx : EVP_MD_CTX)
{% else %}
Expand Down Expand Up @@ -256,7 +249,7 @@ lib LibCrypto
NID_commonName = 13
NID_subject_alt_name = 85

{% if compare_versions(OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
fun sk_free = OPENSSL_sk_free(st : Void*)
fun sk_num = OPENSSL_sk_num(x0 : Void*) : Int
fun sk_pop_free = OPENSSL_sk_pop_free(st : Void*, callback : (Void*) ->)
Expand Down Expand Up @@ -301,12 +294,12 @@ lib LibCrypto
fun x509v3_ext_nconf_nid = X509V3_EXT_nconf_nid(conf : Void*, ctx : Void*, ext_nid : Int, value : Char*) : X509_EXTENSION
fun x509v3_ext_print = X509V3_EXT_print(out : Bio*, ext : X509_EXTENSION, flag : Int, indent : Int) : Int

{% unless compare_versions(OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER < 0x1_01_00_00_0 %}
fun err_load_crypto_strings = ERR_load_crypto_strings
fun openssl_add_all_algorithms = OPENSSL_add_all_algorithms_noconf
{% end %}

{% if compare_versions(OPENSSL_VERSION, "1.0.2") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_00_02_00_0 %}
type X509VerifyParam = Void*

@[Flags]
Expand Down
36 changes: 15 additions & 21 deletions src/openssl/lib_ssl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,15 @@ require "./lib_crypto"

{% begin %}
lib LibSSL
{% from_libressl = (`hash pkg-config 2> /dev/null || printf %s false` != "false") &&
(`test -f $(pkg-config --silence-errors --variable=includedir libssl)/openssl/opensslv.h || printf %s false` != "false") &&
(`printf "#include <openssl/opensslv.h>\nLIBRESSL_VERSION_NUMBER" | ${CC:-cc} $(pkg-config --cflags --silence-errors libssl || true) -E -`.chomp.split('\n').last != "LIBRESSL_VERSION_NUMBER") %}
{% ssl_version = `hash pkg-config 2> /dev/null && pkg-config --silence-errors --modversion libssl || printf %s 0.0.0`.split.last.gsub(/[^0-9.]/, "") %}

{% if from_libressl %}
LIBRESSL_VERSION = {{ ssl_version }}
OPENSSL_VERSION = "0.0.0"
{% else %}
LIBRESSL_VERSION = "0.0.0"
OPENSSL_VERSION = {{ ssl_version }}
{% end %}
{{ `#{__DIR__}/version.sh libssl` }}
end
{% end %}

@[Link(ldflags: "`command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libssl || printf %s '-lssl -lcrypto'`")]
{% if LibSSL::LDFLAGS %}
@[Link(ldflags: "{{LibSSL::LDFLAGS.id}} -lssl -lcrypto")]
{% else %}
@[Link(ldflags: "`command -v pkg-config > /dev/null && pkg-config --libs --silence-errors libssl || printf %s '-lssl -lcrypto'`")]
{% end %}
lib LibSSL
alias Int = LibC::Int
alias Char = LibC::Char
Expand Down Expand Up @@ -102,7 +95,7 @@ lib LibSSL
NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000
CRYPTOPRO_TLSEXT_BUG = 0x80000000

{% if compare_versions(OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
MICROSOFT_SESS_ID_BUG = 0x00000000
NETSCAPE_CHALLENGE_BUG = 0x00000000
NETSCAPE_REUSE_CIPHER_CHANGE_BUG = 0x00000000
Expand Down Expand Up @@ -188,7 +181,7 @@ lib LibSSL
fun ssl_ctx_set_default_verify_paths = SSL_CTX_set_default_verify_paths(ctx : SSLContext) : Int
fun ssl_ctx_ctrl = SSL_CTX_ctrl(ctx : SSLContext, cmd : Int, larg : ULong, parg : Void*) : ULong

{% if compare_versions(OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
fun ssl_ctx_get_options = SSL_CTX_get_options(ctx : SSLContext) : ULong
fun ssl_ctx_set_options = SSL_CTX_set_options(ctx : SSLContext, larg : ULong) : ULong
fun ssl_ctx_clear_options = SSL_CTX_clear_options(ctx : SSLContext, larg : ULong) : ULong
Expand All @@ -197,25 +190,26 @@ lib LibSSL
@[Raises]
fun ssl_ctx_load_verify_locations = SSL_CTX_load_verify_locations(ctx : SSLContext, ca_file : UInt8*, ca_path : UInt8*) : Int

# Hostname validation for OpenSSL <= 1.0.1
fun ssl_ctx_set_cert_verify_callback = SSL_CTX_set_cert_verify_callback(ctx : SSLContext, callback : CertVerifyCallback, arg : Void*)
{% if OPENSSL_VERSION_NUMBER <= 0x1_00_01_00_0 %}
fun ssl_ctx_set_cert_verify_callback = SSL_CTX_set_cert_verify_callback(ctx : SSLContext, callback : CertVerifyCallback, arg : Void*)
{% end %}

{% if compare_versions(OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_01_00_00_0 %}
fun tls_method = TLS_method : SSLMethod
{% else %}
fun ssl_library_init = SSL_library_init
fun ssl_load_error_strings = SSL_load_error_strings
fun sslv23_method = SSLv23_method : SSLMethod
{% end %}

{% if compare_versions(OPENSSL_VERSION, "1.0.2") >= 0 || compare_versions(LIBRESSL_VERSION, "2.5.0") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_00_02_00_0 || LIBRESSL_VERSION_NUMBER >= 0x2_05_00_00_0 %}
alias ALPNCallback = (SSL, Char**, Char*, Char*, Int, Void*) -> Int

fun ssl_get0_alpn_selected = SSL_get0_alpn_selected(handle : SSL, data : Char**, len : LibC::UInt*) : Void
fun ssl_ctx_set_alpn_select_cb = SSL_CTX_set_alpn_select_cb(ctx : SSLContext, cb : ALPNCallback, arg : Void*) : Void
{% end %}

{% if compare_versions(OPENSSL_VERSION, "1.0.2") >= 0 %}
{% if OPENSSL_VERSION_NUMBER >= 0x1_00_02_00_0 %}
alias X509VerifyParam = LibCrypto::X509VerifyParam

fun ssl_get0_param = SSL_get0_param(handle : SSL) : X509VerifyParam
Expand All @@ -224,7 +218,7 @@ lib LibSSL
{% end %}
end

{% unless compare_versions(LibSSL::OPENSSL_VERSION, "1.1.0") >= 0 %}
{% if LibSSL::OPENSSL_VERSION_NUMBER < 0x1_01_00_00_0 %}
LibSSL.ssl_library_init
LibSSL.ssl_load_error_strings
LibCrypto.openssl_add_all_algorithms
Expand Down