Skip to content

Commit

Permalink
Support OpenSSL 1.1.1 (#6738)
Browse files Browse the repository at this point in the history
* OpenSSL: use public API to create BIO_METHOD when available

OpenSSL 1.1.0 introduced a public API to create and populate
BIO_METHOD. We now use it when available.

This fixes an internal ABI breaking change introduced in
OpenSSL 1.1.1 that causes a compiled program to segfault.

* OpenSSL: use public API to init BIO when available

OpenSSL 1.1.0 introduced a public API to get/set BIO struct values.
We now use it them when available.

Fixes compatibility issues with OpenSSL 1.1.1

* Fix: broken OpenSSL::SSL::Context specs with OpenSSL 1.1.1

* Implement BIO read/write methods for OpenSSL 1.1.1
  • Loading branch information
ysbaddaden authored and RX14 committed Sep 17, 2018
1 parent 94f76a1 commit e752415
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 34 deletions.
8 changes: 8 additions & 0 deletions spec/std/openssl/ssl/context_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ 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 LibSSL::OPENSSL_111 %}
context.modes.should eq(OpenSSL::SSL::Modes::AUTO_RETRY)
{% else %}
context.modes.should eq(OpenSSL::SSL::Modes::None)
{% end %}

OpenSSL::SSL::Context::Client.insecure(LibSSL.tlsv1_method)
end
Expand All @@ -63,7 +67,11 @@ 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 LibSSL::OPENSSL_111 %}
context.modes.should eq(OpenSSL::SSL::Modes::AUTO_RETRY)
{% else %}
context.modes.should eq(OpenSSL::SSL::Modes::None)
{% end %}

OpenSSL::SSL::Context::Server.insecure(LibSSL.tlsv1_method)
end
Expand Down
98 changes: 80 additions & 18 deletions src/openssl/bio.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,54 @@ require "./lib_crypto"

# :nodoc:
struct OpenSSL::BIO
CRYSTAL_BIO = begin
crystal_bio = LibCrypto::BioMethod.new
crystal_bio.name = "Crystal BIO".to_unsafe
def self.get_data(bio) : Void*
{% if LibCrypto::OPENSSL_110 %}
LibCrypto.BIO_get_data(bio)
{% else %}
bio.value.ptr
{% end %}
end

def self.set_data(bio, data : Void*)
{% if LibCrypto::OPENSSL_110 %}
LibCrypto.BIO_set_data(bio, data)
{% else %}
bio.value.ptr = data
{% end %}
end

crystal_bio.bwrite = LibCrypto::BioMethodWrite.new do |bio, data, len|
io = Box(IO).unbox(bio.value.ptr)
CRYSTAL_BIO = begin
bwrite = LibCrypto::BioMethodWriteOld.new do |bio, data, len|
io = Box(IO).unbox(BIO.get_data(bio))
io.write Slice.new(data, len)
len
end

crystal_bio.bread = LibCrypto::BioMethodRead.new do |bio, buffer, len|
io = Box(IO).unbox(bio.value.ptr)
bwrite_ex = LibCrypto::BioMethodWrite.new do |bio, data, len, writep|
count = len > Int32::MAX ? Int32::MAX : len.to_i
io = Box(IO).unbox(BIO.get_data(bio))
io.write Slice.new(data, count)
writep.value = LibC::SizeT.new(count)
1
end

bread = LibCrypto::BioMethodReadOld.new do |bio, buffer, len|
io = Box(IO).unbox(BIO.get_data(bio))
io.flush
io.read(Slice.new(buffer, len)).to_i
end

crystal_bio.ctrl = LibCrypto::BioMethodCtrl.new do |bio, cmd, num, ptr|
io = Box(IO).unbox(bio.value.ptr)
bread_ex = LibCrypto::BioMethodWrite.new do |bio, buffer, len, readp|
count = len > Int32::MAX ? Int32::MAX : len.to_i
io = Box(IO).unbox(BIO.get_data(bio))
io.flush
ret = io.read Slice.new(buffer, count)
readp.value = LibC::SizeT.new(ret)
1
end

ctrl = LibCrypto::BioMethodCtrl.new do |bio, cmd, num, ptr|
io = Box(IO).unbox(BIO.get_data(bio))

val = case cmd
when LibCrypto::CTRL_FLUSH
Expand All @@ -34,31 +64,63 @@ struct OpenSSL::BIO
LibCrypto::Long.new(val)
end

crystal_bio.create = LibCrypto::BioMethodCreate.new do |bio|
bio.value.shutdown = 1
bio.value.init = 1
bio.value.num = -1
create = LibCrypto::BioMethodCreate.new do |bio|
{% if LibCrypto::OPENSSL_110 %}
LibCrypto.BIO_set_shutdown(bio, 1)
LibCrypto.BIO_set_init(bio, 1)
# bio.value.num = -1
{% else %}
bio.value.shutdown = 1
bio.value.init = 1
bio.value.num = -1
{% end %}
1
end

crystal_bio.destroy = LibCrypto::BioMethodDestroy.new do |bio|
bio.value.ptr = Pointer(Void).null
destroy = LibCrypto::BioMethodDestroy.new do |bio|
BIO.set_data(bio, Pointer(Void).null)
1
end

crystal_bio
{% if LibCrypto::OPENSSL_110 %}
biom = LibCrypto.BIO_meth_new(Int32::MAX, "Crystal BIO")

{% if LibCrypto::OPENSSL_111 %}
LibCrypto.BIO_meth_set_write_ex(biom, bwrite_ex)
LibCrypto.BIO_meth_set_read_ex(biom, bread_ex)
{% else %}
LibCrypto.BIO_meth_set_write(biom, bwrite)
LibCrypto.BIO_meth_set_read(biom, bread)
{% end %}

LibCrypto.BIO_meth_set_ctrl(biom, ctrl)
LibCrypto.BIO_meth_set_create(biom, create)
LibCrypto.BIO_meth_set_destroy(biom, destroy)
biom
{% else %}
biom = Pointer(LibCrypto::BioMethod).malloc(1)
biom.value.type_id = Int32::MAX
biom.value.name = "Crystal BIO"
biom.value.bwrite = bwrite
biom.value.bread = bread
biom.value.ctrl = ctrl
biom.value.create = create
biom.value.destroy = destroy
biom
{% end %}
end

@boxed_io : Void*

def initialize(@io : IO)
@bio = LibCrypto.bio_new(pointerof(CRYSTAL_BIO))
@bio = LibCrypto.BIO_new(CRYSTAL_BIO)

# We need to store a reference to the box because it's
# stored in `@bio.value.ptr`, but that lives in C-land,
# not in Crystal-land.
@boxed_io = Box(IO).box(io)

@bio.value.ptr = @boxed_io
BIO.set_data(@bio, @boxed_io)
end

getter io
Expand Down
59 changes: 43 additions & 16 deletions src/openssl/lib_crypto.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% begin %}
lib LibCrypto
OPENSSL_111 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.1.1 libcrypto || printf %s false`.stringify != "false" }}
OPENSSL_110 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.1.0 libcrypto || printf %s false`.stringify != "false" }}
OPENSSL_102 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.0.2 libcrypto || printf %s false`.stringify != "false" }}
end
Expand Down Expand Up @@ -45,30 +46,56 @@ lib LibCrypto
CTRL_POP = 7
CTRL_FLUSH = 11

alias BioMethodWrite = (Bio*, Char*, Int) -> Int
alias BioMethodRead = (Bio*, Char*, Int) -> Int
alias BioMethodWrite = (Bio*, Char*, SizeT, SizeT*) -> Int
alias BioMethodWriteOld = (Bio*, Char*, Int) -> Int
alias BioMethodRead = (Bio*, Char*, SizeT, SizeT*) -> Int
alias BioMethodReadOld = (Bio*, Char*, Int) -> Int
alias BioMethodPuts = (Bio*, Char*) -> Int
alias BioMethodGets = (Bio*, Char*, Int) -> Int
alias BioMethodCtrl = (Bio*, Int, Long, Void*) -> Long
alias BioMethodCreate = Bio* -> Int
alias BioMethodDestroy = Bio* -> Int
alias BioMethodCallbackCtrl = (Bio*, Int, Void*) -> Long

struct BioMethod
type_id : Int
name : Char*
bwrite : BioMethodWrite
bread : BioMethodRead
bputs : BioMethodPuts
bgets : BioMethodGets
ctrl : BioMethodCtrl
create : BioMethodCreate
destroy : BioMethodDestroy
callback_ctrl : BioMethodCallbackCtrl
end
{% if LibCrypto::OPENSSL_110 %}
type BioMethod = Void
{% else %}
struct BioMethod
type_id : Int
name : Char*
bwrite : BioMethodWriteOld
bread : BioMethodReadOld
bputs : BioMethodPuts
bgets : BioMethodGets
ctrl : BioMethodCtrl
create : BioMethodCreate
destroy : BioMethodDestroy
callback_ctrl : BioMethodCallbackCtrl
end
{% end %}

fun bio_new = BIO_new(method : BioMethod*) : Bio*
fun bio_free = BIO_free(bio : Bio*) : Int
fun BIO_new(BioMethod*) : Bio*
fun BIO_free(Bio*) : Int
fun BIO_set_data(Bio*, Void*)
fun BIO_get_data(Bio*) : Void*
fun BIO_set_init(Bio*, Int)
fun BIO_set_shutdown(Bio*, Int)

{% if LibCrypto::OPENSSL_110 %}
fun BIO_meth_new(Int, Char*) : BioMethod*
fun BIO_meth_set_read(BioMethod*, BioMethodReadOld)
fun BIO_meth_set_write(BioMethod*, BioMethodWriteOld)
fun BIO_meth_set_puts(BioMethod*, BioMethodPuts)
fun BIO_meth_set_gets(BioMethod*, BioMethodGets)
fun BIO_meth_set_ctrl(BioMethod*, BioMethodCtrl)
fun BIO_meth_set_create(BioMethod*, BioMethodCreate)
fun BIO_meth_set_destroy(BioMethod*, BioMethodDestroy)
fun BIO_meth_set_callback_ctrl(BioMethod*, BioMethodCallbackCtrl)
{% end %}
{% if LibCrypto::OPENSSL_111 %}
fun BIO_meth_set_read_ex(BioMethod*, BioMethodRead)
fun BIO_meth_set_write_ex(BioMethod*, BioMethodWrite)
{% end %}

fun sha1 = SHA1(data : Char*, length : SizeT, md : Char*) : Char*

Expand Down
1 change: 1 addition & 0 deletions src/openssl/lib_ssl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ require "./lib_crypto"

{% begin %}
lib LibSSL
OPENSSL_111 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.1.1 libssl || printf %s false`.stringify != "false" }}
OPENSSL_110 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.1.0 libssl || printf %s false`.stringify != "false" }}
OPENSSL_102 = {{ `command -v pkg-config > /dev/null && pkg-config --atleast-version=1.0.2 libssl || printf %s false`.stringify != "false" }}
end
Expand Down

0 comments on commit e752415

Please sign in to comment.