Add patch for BoringSSL

This commit is contained in:
Omar Roth 2019-11-15 21:22:20 -05:00
parent 6e4b028f45
commit fcf16e715a
No known key found for this signature in database
GPG Key ID: B8254FB7EC3D37F2
3 changed files with 190 additions and 2 deletions

View File

@ -1,5 +1,4 @@
require "./lsquic/*"
# require "http/headers"
require "http"
require "socket"
@ -17,6 +16,8 @@ HEADERS = HTTP::Headers{
# "content-length" => "0",
}
pp HTTP::Client.get("https://www.youtube.com#{PATH}").status_code
# logger_if = LibLsquic::LoggerIf.new
# logger_if.log_buf = ->(logger_ctx : Void*, msg_buf : LibC::Char*, msg_size : LibC::SizeT) { puts String.new(msg_buf); 0 }
# LibLsquic.logger_init(pointerof(logger_if), nil, LibLsquic::LoggerTimestampStyle::LltsHhmmssms)

View File

@ -1,4 +1,4 @@
@[Link(ldflags: "#{__DIR__}/ext/liblsquic.a #{__DIR__}/ext/libssl.a #{__DIR__}/ext/libcrypto.a -lz")]
@[Link(ldflags: "#{__DIR__}/ext/liblsquic.a")]
lib LibLsquic
MAX_CID_LEN = 20
QQUIC_CID_LEN = 8
@ -375,3 +375,5 @@ lib LibLsquic
end
$ver2str : LibC::Char*[6]
end
require "./patch"

185
src/lsquic/patch.cr Normal file
View File

@ -0,0 +1,185 @@
require "openssl/lib_ssl"
require "openssl/lib_crypto"
require "openssl/bio"
@[Link(ldflags: "#{__DIR__}/ext/liblsquic.a #{__DIR__}/ext/libcrypto.a")]
lib LibCrypto
fun evp_ripemd160 = EVP_sha1 : EVP_MD
fun sk_free = sk_free(st : Void*)
fun sk_num = sk_num(x0 : Void*) : Int
fun sk_pop_free = sk_pop_free(st : Void*, callback : (Void*) ->)
fun sk_value = sk_value(x0 : Void*, x1 : Int) : Void*
end
@[Link(ldflags: "#{__DIR__}/ext/libssl.a")]
lib LibSSL
fun ssl_set_tlsext_host_name = SSL_set_tlsext_host_name(handle : SSL, name : Char*) : Long
fun ssl_ctx_set_tmp_ecdh = SSL_CTX_set_tmp_ecdh(ctx : SSLContext, parg : Void*) : ULong
fun ssl_ctx_get_mode = SSL_CTX_get_mode(ctx : SSLContext) : ULong
fun ssl_ctx_set_mode = SSL_CTX_set_mode(ctx : SSLContext, mode : ULong) : ULong
fun ssl_ctx_clear_mode = SSL_CTX_clear_mode(ctx : SSLContext, mode : ULong) : ULong
end
require "openssl/ssl/context"
abstract class OpenSSL::SSL::Context
def set_tmp_ecdh_key(curve = LibCrypto::NID_X9_62_prime256v1)
key = LibCrypto.ec_key_new_by_curve_name(curve)
raise OpenSSL::Error.new("ec_key_new_by_curve_name") if key.null?
LibSSL.ssl_ctx_set_tmp_ecdh(@handle, key)
LibCrypto.ec_key_free(key)
end
# Returns the current modes set on the TLS context.
def modes
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_get_mode(@handle)
end
# Adds modes to the TLS context.
def add_modes(mode : OpenSSL::SSL::Modes)
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_set_mode(@handle, mode)
end
# Removes modes from the TLS context.
def remove_modes(mode : OpenSSL::SSL::Modes)
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_clear_mode(@handle, mode)
end
end
struct OpenSSL::BIO
CRYSTAL_BIO_BORING = 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
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
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
io.flush
1
when LibCrypto::CTRL_PUSH, LibCrypto::CTRL_POP
0
else
STDERR.puts "WARNING: Unsupported BIO ctrl call (#{cmd})"
0
end
LibCrypto::Long.new(val)
end
create = LibCrypto::BioMethodCreate.new do |bio|
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
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
destroy = LibCrypto::BioMethodDestroy.new do |bio|
BIO.set_data(bio, Pointer(Void).null)
1
end
{% if compare_versions(LibCrypto::OPENSSL_VERSION, "1.1.0") >= 0 %}
biom = LibCrypto.BIO_meth_new(Int32::MAX, "Crystal BIO")
LibCrypto.BIO_meth_set_write(biom, bwrite)
LibCrypto.BIO_meth_set_read(biom, bread)
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
def initialize(@io : IO)
@bio = LibCrypto.BIO_new(CRYSTAL_BIO_BORING)
# 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.set_data(@bio, @boxed_io)
end
end
require "openssl/ssl/socket"
abstract class OpenSSL::SSL::Socket < IO
class Client < Socket
def initialize(io, context : Context::Client = Context::Client.new, sync_close : Bool = false, hostname : String? = nil)
super(io, context, sync_close)
begin
if hostname
LibSSL.ssl_set_tlsext_host_name(@ssl, hostname)
{% if compare_versions(LibSSL::OPENSSL_VERSION, "1.0.2") >= 0 %}
param = LibSSL.ssl_get0_param(@ssl)
if ::Socket.ip?(hostname)
unless LibCrypto.x509_verify_param_set1_ip_asc(param, hostname) == 1
raise OpenSSL::Error.new("X509_VERIFY_PARAM_set1_ip_asc")
end
else
unless LibCrypto.x509_verify_param_set1_host(param, hostname, hostname.bytesize) == 1
raise OpenSSL::Error.new("X509_VERIFY_PARAM_set1_host")
end
end
{% else %}
context.set_cert_verify_callback(hostname)
{% end %}
end
ret = LibSSL.ssl_connect(@ssl)
unless ret == 1
raise OpenSSL::SSL::Error.new(@ssl, ret, "SSL_connect")
end
rescue ex
LibSSL.ssl_free(@ssl) # GC never calls finalize, avoid mem leak
raise ex
end
end
end
end