From b296750db0b83c2a7030d024532cd5a2df40f162 Mon Sep 17 00:00:00 2001 From: aeris Date: Sun, 29 Jan 2017 15:08:09 +0100 Subject: [PATCH] Ruby OpenSSL extension patch to support multiple certificates --- Makefile | 5 +- multiple_certs.patch | 150 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 multiple_certs.patch diff --git a/Makefile b/Makefile index 8a232a8..ad2579b 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ libs: $(LIBS) build/$(RUBY_VERSION)-cryptcheck: $(RBENV_ROOT)/plugins/ruby-build/share/ruby-build/$(RUBY_VERSION) cp $< $@ install-ruby: build/$(RUBY_VERSION)-cryptcheck $(LIBS) | $(OPENSSL_DIR)/ - cat tmp_key.patch set_ecdh_curves.patch fallback_scsv.patch | \ + cat tmp_key.patch set_ecdh_curves.patch fallback_scsv.patch multiple_certs.patch | \ RUBY_BUILD_CACHE_PATH=$(PWD)/build \ RUBY_BUILD_DEFINITIONS=$(PWD)/build \ rbenv install -fp $(RUBY_VERSION)-cryptcheck @@ -88,9 +88,10 @@ $(RUBY_OPENSSL_EXT_DIR)/Makefile: libs | $(RUBY_DIR)/ patch -d $(RUBY_DIR)/ -p1 < tmp_key.patch patch -d $(RUBY_DIR)/ -p1 < set_ecdh_curves.patch patch -d $(RUBY_DIR)/ -p1 < fallback_scsv.patch + patch -d $(RUBY_DIR)/ -p1 < multiple_certs.patch cd $(RUBY_OPENSSL_EXT_DIR) && ruby extconf.rb -$(RUBY_OPENSSL_EXT_DIR)/openssl.so: $(LIBS) $(RUBY_OPENSSL_EXT_DIR)/Makefile +$(RUBY_OPENSSL_EXT_DIR)/openssl.so: $(LIBS) #$(RUBY_OPENSSL_EXT_DIR)/Makefile top_srcdir=../.. $(MAKE) -C $(RUBY_OPENSSL_EXT_DIR) lib/openssl.so: $(RUBY_OPENSSL_EXT_DIR)/openssl.so diff --git a/multiple_certs.patch b/multiple_certs.patch new file mode 100644 index 0000000..ab3494c --- /dev/null +++ b/multiple_certs.patch @@ -0,0 +1,150 @@ +diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb +index bcb167e..5f688db 100644 +--- a/ext/openssl/lib/openssl/ssl.rb ++++ b/ext/openssl/lib/openssl/ssl.rb +@@ -70,7 +70,7 @@ class SSLContext + DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + end + +- INIT_VARS = ["cert", "key", "client_ca", "ca_file", "ca_path", ++ INIT_VARS = ["client_ca", "ca_file", "ca_path", + "timeout", "verify_mode", "verify_depth", "renegotiation_cb", + "verify_callback", "cert_store", "extra_chain_cert", + "client_cert_cb", "session_id_context", "tmp_dh_callback", +@@ -106,6 +106,7 @@ class SSLContext + # + # You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS + def initialize(version = nil, fallback_scsv: false) ++ @certs = @keys = [] + INIT_VARS.each { |v| instance_variable_set v, nil } + self.options = self.options | OpenSSL::SSL::OP_ALL + return unless version +@@ -131,6 +132,22 @@ def set_params(params={}) + end + return params + end ++ ++ # Compatibility with previous version supporting a single certificate ++ def cert=(cert) ++ self.certs = [cert] ++ end ++ def cert ++ ++ self.certs.first ++ end ++ ++ def key=(key) ++ self.keys = [key] ++ end ++ def key ++ self.keys.first ++ end + end + + module SocketForwarder +diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c +index 9f7ee0b..9437793 100644 +--- a/ext/openssl/ossl_ssl.c ++++ b/ext/openssl/ossl_ssl.c +@@ -36,8 +36,8 @@ VALUE cSSLSocket; + static VALUE eSSLErrorWaitReadable; + static VALUE eSSLErrorWaitWritable; + +-#define ossl_sslctx_set_cert(o,v) rb_iv_set((o),"@cert",(v)) +-#define ossl_sslctx_set_key(o,v) rb_iv_set((o),"@key",(v)) ++#define ossl_sslctx_set_certs(o,v) rb_iv_set((o),"@certs",(v)) ++#define ossl_sslctx_set_keys(o,v) rb_iv_set((o),"@keys",(v)) + #define ossl_sslctx_set_client_ca(o,v) rb_iv_set((o),"@client_ca",(v)) + #define ossl_sslctx_set_ca_file(o,v) rb_iv_set((o),"@ca_file",(v)) + #define ossl_sslctx_set_ca_path(o,v) rb_iv_set((o),"@ca_path",(v)) +@@ -50,8 +50,8 @@ static VALUE eSSLErrorWaitWritable; + #define ossl_sslctx_set_client_cert_cb(o,v) rb_iv_set((o),"@client_cert_cb",(v)) + #define ossl_sslctx_set_sess_id_ctx(o, v) rb_iv_set((o),"@session_id_context",(v)) + +-#define ossl_sslctx_get_cert(o) rb_iv_get((o),"@cert") +-#define ossl_sslctx_get_key(o) rb_iv_get((o),"@key") ++#define ossl_sslctx_get_certs(o) rb_iv_get((o),"@certs") ++#define ossl_sslctx_get_keys(o) rb_iv_get((o),"@keys") + #define ossl_sslctx_get_client_ca(o) rb_iv_get((o),"@client_ca") + #define ossl_sslctx_get_ca_file(o) rb_iv_get((o),"@ca_file") + #define ossl_sslctx_get_ca_path(o) rb_iv_get((o),"@ca_path") +@@ -713,7 +713,8 @@ ossl_sslctx_setup(VALUE self) + char *ca_path = NULL, *ca_file = NULL; + int verify_mode; + long i; +- VALUE val; ++ VALUE val, val2; ++ int cert_defined = 0, key_defined = 0; + + if(OBJ_FROZEN(self)) return Qnil; + GetSSLCTX(self, ctx); +@@ -761,19 +762,39 @@ ossl_sslctx_setup(VALUE self) + } + + /* private key may be bundled in certificate file. */ +- val = ossl_sslctx_get_cert(self); +- cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */ +- val = ossl_sslctx_get_key(self); +- key = NIL_P(val) ? NULL : GetPKeyPtr(val); /* NO DUP NEEDED */ +- if (cert && key) { +- if (!SSL_CTX_use_certificate(ctx, cert)) { +- /* Adds a ref => Safe to FREE */ +- ossl_raise(eSSLError, "SSL_CTX_use_certificate"); ++ val = ossl_sslctx_get_certs(self); ++ if (!NIL_P(val)) { ++ Check_Type(val, T_ARRAY); ++ for (i = 0; i < RARRAY_LEN(val); i++) { ++ val2 = rb_ary_entry(val, i); ++ cert = NIL_P(val2) ? NULL : GetX509CertPtr(val2); /* NO DUP NEEDED */ ++ if (cert) { ++ cert_defined = 1; ++ if (!SSL_CTX_use_certificate(ctx, cert)) { ++ /* Adds a ref => Safe to FREE */ ++ ossl_raise(eSSLError, "SSL_CTX_use_certificate"); ++ } ++ } + } +- if (!SSL_CTX_use_PrivateKey(ctx, key)) { +- /* Adds a ref => Safe to FREE */ +- ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); ++ } ++ ++ val = ossl_sslctx_get_keys(self); ++ if (!NIL_P(val)) { ++ Check_Type(val, T_ARRAY); ++ for (i = 0; i < RARRAY_LEN(val); i++) { ++ val2 = rb_ary_entry(val, i); ++ key = NIL_P(val2) ? NULL : GetPKeyPtr(val2); /* NO DUP NEEDED */ ++ if (cert) { ++ key_defined = 1; ++ if (!SSL_CTX_use_PrivateKey(ctx, key)) { ++ /* Adds a ref => Safe to FREE */ ++ ossl_raise(eSSLError, "SSL_CTX_use_certificate"); ++ } ++ } + } ++ } ++ ++ if (cert_defined && key_defined) { + if (!SSL_CTX_check_private_key(ctx)) { + ossl_raise(eSSLError, "SSL_CTX_check_private_key"); + } +@@ -2128,14 +2149,14 @@ Init_ossl_ssl(void) + rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc); + + /* +- * Context certificate ++ * Context certificates + */ +- rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse); ++ rb_attr(cSSLContext, rb_intern("certs"), 1, 1, Qfalse); + + /* +- * Context private key ++ * Context private keys + */ +- rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse); ++ rb_attr(cSSLContext, rb_intern("keys"), 1, 1, Qfalse); + + /* + * A certificate or Array of certificates that will be sent to the client.