diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2021-09-28 18:03:24 +0900 |
---|---|---|
committer | nagachika <nagachika@ruby-lang.org> | 2021-10-30 20:22:06 +0900 |
commit | 00e89fe36b57e2d7c4ea269bc827d9806edef5ed (patch) | |
tree | a076873763b50437c2ffaf258ca14e57a519067f | |
parent | 5427b08381fb0d644ec69d5aa94234f90a4fbed1 (diff) |
openssl: import v2.2.1
Bring the local copy of ruby/openssl in sync with the upstream gem
release v2.2.1. The commits happened in the upstream repository can be
found at:
https://github.com/ruby/openssl/compare/v2.2.0...v2.2.1
Note that many of these have already been applied to ruby.git and don't
appear in the file changes of this commit.
-rw-r--r-- | ext/openssl/History.md | 53 | ||||
-rw-r--r-- | ext/openssl/extconf.rb | 43 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/version.rb | 2 | ||||
-rw-r--r-- | ext/openssl/openssl.gemspec | 3 | ||||
-rw-r--r-- | ext/openssl/ossl_bn.c | 34 | ||||
-rw-r--r-- | ext/openssl/ossl_cipher.c | 26 | ||||
-rw-r--r-- | ext/openssl/ossl_digest.c | 8 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_ec.c | 16 | ||||
-rw-r--r-- | ext/openssl/ossl_ssl.c | 80 | ||||
-rw-r--r-- | ext/openssl/ossl_ts.c | 16 | ||||
-rw-r--r-- | ext/openssl/ossl_x509store.c | 59 | ||||
-rw-r--r-- | test/openssl/test_cipher.rb | 42 | ||||
-rw-r--r-- | test/openssl/test_config.rb | 16 | ||||
-rw-r--r-- | test/openssl/test_ssl.rb | 48 | ||||
-rw-r--r-- | test/openssl/test_ssl_session.rb | 1 | ||||
-rw-r--r-- | test/openssl/test_ts.rb | 18 | ||||
-rw-r--r-- | test/openssl/test_x509store.rb | 4 | ||||
-rw-r--r-- | test/openssl/utils.rb | 8 |
18 files changed, 375 insertions, 102 deletions
diff --git a/ext/openssl/History.md b/ext/openssl/History.md index a4a82a146c..46435f9be6 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,20 @@ +Version 2.2.1 +============= + +Merged changes in 2.1.3. Additionally, the following issues are fixed by this +release. + +Bug fixes +--------- + +* Fix crash in `OpenSSL::Timestamp::{Request,Response,TokenInfo}.new` when + invalid arguments are given. + [[GitHub #407]](https://github.com/ruby/openssl/pull/407) +* Fix `OpenSSL::Timestamp::Factory#create_timestamp` with LibreSSL on platforms + where `time_t` has a different size from `long`. + [[GitHub #454]](https://github.com/ruby/openssl/pull/454) + + Version 2.2.0 ============= @@ -75,6 +92,42 @@ Notable changes [[GitHub #297]](https://github.com/ruby/openssl/pull/297) +Version 2.1.3 +============= + +Bug fixes +--------- + +* Fix deprecation warnings on Ruby 3.0. +* Add ".include" directive support in `OpenSSL::Config`. + [[GitHub #216]](https://github.com/ruby/openssl/pull/216) +* Fix handling of IPv6 address SANs. + [[GitHub #185]](https://github.com/ruby/openssl/pull/185) +* Hostname verification failure with `OpenSSL::SSL::SSLContext#verify_hostname=` + sets a proper error code. + [[GitHub #350]](https://github.com/ruby/openssl/pull/350) +* Fix crash with `OpenSSL::BN.new(nil, 2)`. + [[Bug #15760]](https://bugs.ruby-lang.org/issues/15760) +* `OpenSSL::SSL::SSLSocket#sys{read,write}` prevent internal string buffers from + being modified by another thread. + [[GitHub #453]](https://github.com/ruby/openssl/pull/453) +* Fix misuse of input record separator in `OpenSSL::Buffering` where it was + for output. +* Fix wrong interger casting in `OpenSSL::PKey::EC#dsa_verify_asn1`. + [[GitHub #460]](https://github.com/ruby/openssl/pull/460) +* `extconf.rb` explicitly checks that OpenSSL's version number is 1.0.1 or + newer but also less than 3.0. Ruby/OpenSSL v2.1.x and v2.2.x will not support + OpenSSL 3.0 API. + [[GitHub #458]](https://github.com/ruby/openssl/pull/458) +* Activate `digest` gem correctly. `digest` library could go into an + inconsistent state if there are multiple versions of `digest` is installed + and `openssl` is `require`d before `digest`. + [[GitHub #463]](https://github.com/ruby/openssl/pull/463) +* Fix GC.compact compatibility. + [[GitHub #464]](https://github.com/ruby/openssl/issues/464) + [[GitHub #465]](https://github.com/ruby/openssl/pull/465) + + Version 2.1.2 ============= diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 693e55cd97..e13595c7c7 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -33,9 +33,6 @@ if $mswin || $mingw have_library("ws2_32") end -Logging::message "=== Checking for required stuff... ===\n" -result = pkg_config("openssl") && have_header("openssl/ssl.h") - if $mingw append_cflags '-D_FORTIFY_SOURCE=2' append_ldflags '-fstack-protector' @@ -92,19 +89,33 @@ def find_openssl_library return false end -unless result - unless find_openssl_library - Logging::message "=== Checking for required stuff failed. ===\n" - Logging::message "Makefile wasn't created. Fix the errors above.\n" - raise "OpenSSL library could not be found. You might want to use " \ - "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \ - "is installed." - end +Logging::message "=== Checking for required stuff... ===\n" +pkg_config_found = pkg_config("openssl") && have_header("openssl/ssl.h") + +if !pkg_config_found && !find_openssl_library + Logging::message "=== Checking for required stuff failed. ===\n" + Logging::message "Makefile wasn't created. Fix the errors above.\n" + raise "OpenSSL library could not be found. You might want to use " \ + "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \ + "is installed." end -unless checking_for("OpenSSL version is 1.0.1 or later") { - try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") } - raise "OpenSSL >= 1.0.1 or LibreSSL is required" +version_ok = if have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h") + is_libressl = true + checking_for("LibreSSL version >= 2.5.0") { + try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x20500000L", "openssl/opensslv.h") } +else + checking_for("OpenSSL version >= 1.0.1 and < 3.0.0") { + try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") && + !try_static_assert("OPENSSL_VERSION_MAJOR >= 3", "openssl/opensslv.h") } +end +unless version_ok + raise "OpenSSL >= 1.0.1, < 3.0.0 or LibreSSL >= 2.5.0 is required" +end + +# Prevent wincrypt.h from being included, which defines conflicting macro with openssl/x509.h +if is_libressl && ($mswin || $mingw) + $defs.push("-DNOCRYPT") end Logging::message "=== Checking for OpenSSL features... ===\n" @@ -116,10 +127,6 @@ engines.each { |name| have_func("ENGINE_load_#{name}()", "openssl/engine.h") } -if ($mswin || $mingw) && have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h") - $defs.push("-DNOCRYPT") -end - # added in 1.0.2 have_func("EC_curve_nist2nid") have_func("X509_REVOKED_dup") diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 9c7515ba0f..d541cbf162 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module OpenSSL - VERSION = "2.2.0" + VERSION = "2.2.1" end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index 471a3c4265..ebbd0923dd 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "2.2.0" + spec.version = "2.2.1" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} @@ -17,6 +17,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = ">= 2.3.0" + spec.add_runtime_dependency "ipaddr" spec.add_development_dependency "rake" spec.add_development_dependency "rake-compiler" spec.add_development_dependency "test-unit", "~> 3.0" diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index d94b8e375c..bec37299f7 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -453,7 +453,7 @@ ossl_bn_is_negative(VALUE self) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func(result, bn, ossl_bn_ctx)) { \ + if (BN_##func(result, bn, ossl_bn_ctx) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ @@ -479,7 +479,7 @@ BIGNUM_1c(sqr) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func(result, bn1, bn2)) { \ + if (BN_##func(result, bn1, bn2) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ @@ -512,7 +512,7 @@ BIGNUM_2(sub) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) { \ + if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ @@ -556,11 +556,21 @@ BIGNUM_2c(gcd) BIGNUM_2c(mod_sqr) /* - * Document-method: OpenSSL::BN#mod_inverse * call-seq: - * bn.mod_inverse(bn2) => aBN + * bn.mod_inverse(bn2) => aBN */ -BIGNUM_2c(mod_inverse) +static VALUE +ossl_bn_mod_inverse(VALUE self, VALUE other) +{ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; + VALUE obj; + GetBN(self, bn1); + obj = NewBN(rb_obj_class(self)); + if (!(result = BN_mod_inverse(NULL, bn1, bn2, ossl_bn_ctx))) + ossl_raise(eBNError, "BN_mod_inverse"); + SetBN(obj, result); + return obj; +} /* * call-seq: @@ -609,7 +619,7 @@ ossl_bn_div(VALUE self, VALUE other) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) { \ + if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ @@ -651,7 +661,7 @@ BIGNUM_3c(mod_exp) { \ BIGNUM *bn; \ GetBN(self, bn); \ - if (!BN_##func(bn, NUM2INT(bit))) { \ + if (BN_##func(bn, NUM2INT(bit)) <= 0) { \ ossl_raise(eBNError, NULL); \ } \ return self; \ @@ -711,7 +721,7 @@ ossl_bn_is_bit_set(VALUE self, VALUE bit) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func(result, bn, b)) { \ + if (BN_##func(result, bn, b) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ @@ -741,7 +751,7 @@ BIGNUM_SHIFT(rshift) int b; \ b = NUM2INT(bits); \ GetBN(self, bn); \ - if (!BN_##func(bn, bn, b)) \ + if (BN_##func(bn, bn, b) <= 0) \ ossl_raise(eBNError, NULL); \ return self; \ } @@ -780,7 +790,7 @@ BIGNUM_SELF_SHIFT(rshift) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func(result, b, top, bottom)) { \ + if (BN_##func(result, b, top, bottom) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ @@ -809,7 +819,7 @@ BIGNUM_RAND(pseudo_rand) if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ - if (!BN_##func##_range(result, bn)) { \ + if (BN_##func##_range(result, bn) <= 0) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 0b78f40b72..5b92fc39f0 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -814,6 +814,31 @@ ossl_cipher_block_size(VALUE self) } /* + * call-seq: + * cipher.ccm_data_len = integer -> integer + * + * Sets the length of the plaintext / ciphertext message that will be + * processed in CCM mode. Make sure to call this method after #key= and + * #iv= have been set, and before #auth_data=. + * + * Only call this method after calling Cipher#encrypt or Cipher#decrypt. + */ +static VALUE +ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len) +{ + int in_len, out_len; + EVP_CIPHER_CTX *ctx; + + in_len = NUM2INT(data_len); + + GetCipher(self, ctx); + if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1) + ossl_raise(eCipherError, NULL); + + return data_len; +} + +/* * INIT */ void @@ -1043,6 +1068,7 @@ Init_ossl_cipher(void) rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0); rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0); rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1); + rb_define_method(cCipher, "ccm_data_len=", ossl_cipher_set_ccm_data_len, 1); id_auth_tag_len = rb_intern_const("auth_tag_len"); id_key_set = rb_intern_const("key_set"); diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index e2157cb02f..6294fa2acf 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -313,8 +313,6 @@ ossl_digest_block_length(VALUE self) void Init_ossl_digest(void) { - rb_require("digest"); - #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); @@ -398,6 +396,12 @@ Init_ossl_digest(void) * digest2 = sha256.digest(data2) * */ + + /* + * Digest::Class is defined by the digest library. rb_require() cannot be + * used here because it bypasses RubyGems. + */ + rb_funcall(Qnil, rb_intern_const("require"), 1, rb_str_new_cstr("digest")); cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class")); /* Document-class: OpenSSL::Digest::DigestError * diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index fc2bc6c815..1d105abd35 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -653,15 +653,15 @@ static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig) StringValue(data); StringValue(sig); - switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(sig), (int)RSTRING_LEN(sig), ec)) { - case 1: return Qtrue; - case 0: return Qfalse; - default: break; + switch (ECDSA_verify(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data), + (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), ec)) { + case 1: + return Qtrue; + case 0: + return Qfalse; + default: + ossl_raise(eECError, "ECDSA_verify"); } - - ossl_raise(eECError, "ECDSA_verify"); - - UNREACHABLE; } /* diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 4b7efa39f5..7654be12ee 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -13,6 +13,12 @@ #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) +#if !defined(TLS1_3_VERSION) && \ + defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3020000fL +# define TLS1_3_VERSION 0x0304 +#endif + #ifdef _WIN32 # define TO_SOCKET(s) _get_osfhandle(s) #else @@ -33,7 +39,7 @@ static VALUE eSSLErrorWaitReadable; static VALUE eSSLErrorWaitWritable; static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback, - id_npn_protocols_encoded; + id_npn_protocols_encoded, id_each; static VALUE sym_exception, sym_wait_readable, sym_wait_writable; static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, @@ -54,6 +60,13 @@ static int ossl_sslctx_ex_store_p; #endif static void +ossl_sslctx_mark(void *ptr) +{ + SSL_CTX *ctx = ptr; + rb_gc_mark((VALUE)SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_ptr_idx)); +} + +static void ossl_sslctx_free(void *ptr) { SSL_CTX *ctx = ptr; @@ -67,7 +80,7 @@ ossl_sslctx_free(void *ptr) static const rb_data_type_t ossl_sslctx_type = { "OpenSSL/SSL/CTX", { - 0, ossl_sslctx_free, + ossl_sslctx_mark, ossl_sslctx_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; @@ -616,7 +629,7 @@ static VALUE ssl_encode_npn_protocols(VALUE protocols) { VALUE encoded = rb_str_new(NULL, 0); - rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded); + rb_block_call(protocols, id_each, 0, 0, ssl_npn_encode_protocol_i, encoded); return encoded; } @@ -686,7 +699,7 @@ static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) { - VALUE protocols = (VALUE)arg; + VALUE protocols = rb_attr_get((VALUE)arg, id_npn_protocols_encoded); *out = (const unsigned char *) RSTRING_PTR(protocols); *outlen = RSTRING_LENINT(protocols); @@ -908,7 +921,7 @@ ossl_sslctx_setup(VALUE self) if (!NIL_P(val)) { VALUE encoded = ssl_encode_npn_protocols(val); rb_ivar_set(self, id_npn_protocols_encoded, encoded); - SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded); + SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); OSSL_Debug("SSL NPN advertise callback added"); } if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) { @@ -1527,6 +1540,14 @@ ssl_started(SSL *ssl) } static void +ossl_ssl_mark(void *ptr) +{ + SSL *ssl = ptr; + rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)); + rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx)); +} + +static void ossl_ssl_free(void *ssl) { SSL_free(ssl); @@ -1535,7 +1556,7 @@ ossl_ssl_free(void *ssl) const rb_data_type_t ossl_ssl_type = { "OpenSSL/SSL", { - 0, ossl_ssl_free, + ossl_ssl_mark, ossl_ssl_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; @@ -1691,6 +1712,11 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) rb_io_wait_readable(fptr->fd); continue; case SSL_ERROR_SYSCALL: +#ifdef __APPLE__ + /* See ossl_ssl_write_internal() */ + if (errno == EPROTOTYPE) + continue; +#endif if (errno) rb_sys_fail(funcname); ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); #if defined(SSL_R_CERTIFICATE_VERIFY_FAILED) @@ -1847,26 +1873,36 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); if (ssl_started(ssl)) { - for (;;){ + rb_str_locktmp(str); + for (;;) { nread = SSL_read(ssl, RSTRING_PTR(str), ilen); switch(ssl_get_error(ssl, nread)){ case SSL_ERROR_NONE: + rb_str_unlocktmp(str); goto end; case SSL_ERROR_ZERO_RETURN: + rb_str_unlocktmp(str); if (no_exception_p(opts)) { return Qnil; } rb_eof_error(); case SSL_ERROR_WANT_WRITE: - if (no_exception_p(opts)) { return sym_wait_writable; } - write_would_block(nonblock); + if (nonblock) { + rb_str_unlocktmp(str); + if (no_exception_p(opts)) { return sym_wait_writable; } + write_would_block(nonblock); + } rb_io_wait_writable(fptr->fd); continue; case SSL_ERROR_WANT_READ: - if (no_exception_p(opts)) { return sym_wait_readable; } - read_would_block(nonblock); + if (nonblock) { + rb_str_unlocktmp(str); + if (no_exception_p(opts)) { return sym_wait_readable; } + read_would_block(nonblock); + } rb_io_wait_readable(fptr->fd); continue; case SSL_ERROR_SYSCALL: if (!ERR_peek_error()) { + rb_str_unlocktmp(str); if (errno) rb_sys_fail(0); else { @@ -1883,6 +1919,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) } /* fall through */ default: + rb_str_unlocktmp(str); ossl_raise(eSSLError, "SSL_read"); } } @@ -1953,21 +1990,21 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) int nwrite = 0; rb_io_t *fptr; int nonblock = opts != Qfalse; - VALUE io; + VALUE tmp, io; - StringValue(str); + tmp = rb_str_new_frozen(StringValue(str)); GetSSL(self, ssl); io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); if (ssl_started(ssl)) { - for (;;){ - int num = RSTRING_LENINT(str); + for (;;) { + int num = RSTRING_LENINT(tmp); /* SSL_write(3ssl) manpage states num == 0 is undefined */ if (num == 0) goto end; - nwrite = SSL_write(ssl, RSTRING_PTR(str), num); + nwrite = SSL_write(ssl, RSTRING_PTR(tmp), num); switch(ssl_get_error(ssl, nwrite)){ case SSL_ERROR_NONE: goto end; @@ -1982,6 +2019,16 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) rb_io_wait_readable(fptr->fd); continue; case SSL_ERROR_SYSCALL: +#ifdef __APPLE__ + /* + * It appears that send syscall can return EPROTOTYPE if the + * socket is being torn down. Retry to get a proper errno to + * make the error handling in line with the socket library. + * [Bug #14713] https://bugs.ruby-lang.org/issues/14713 + */ + if (errno == EPROTOTYPE) + continue; +#endif if (errno) rb_sys_fail(0); default: ossl_raise(eSSLError, "SSL_write"); @@ -2997,6 +3044,7 @@ Init_ossl_ssl(void) id_tmp_dh_callback = rb_intern_const("tmp_dh_callback"); id_tmp_ecdh_callback = rb_intern_const("tmp_ecdh_callback"); id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded"); + id_each = rb_intern_const("each"); #define DefIVarID(name) do \ id_i_##name = rb_intern_const("@"#name); while (0) diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index 692c0d620f..cff9b7bfff 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -205,8 +205,10 @@ ossl_ts_req_initialize(int argc, VALUE *argv, VALUE self) in = ossl_obj2bio(&arg); ts_req = d2i_TS_REQ_bio(in, &ts_req); BIO_free(in); - if (!ts_req) + if (!ts_req) { + DATA_PTR(self) = NULL; ossl_raise(eTimestampError, "Error when decoding the timestamp request"); + } DATA_PTR(self) = ts_req; return self; @@ -529,8 +531,10 @@ ossl_ts_resp_initialize(VALUE self, VALUE der) in = ossl_obj2bio(&der); ts_resp = d2i_TS_RESP_bio(in, &ts_resp); BIO_free(in); - if (!ts_resp) + if (!ts_resp) { + DATA_PTR(self) = NULL; ossl_raise(eTimestampError, "Error when decoding the timestamp response"); + } DATA_PTR(self) = ts_resp; return self; @@ -871,8 +875,10 @@ ossl_ts_token_info_initialize(VALUE self, VALUE der) in = ossl_obj2bio(&der); info = d2i_TS_TST_INFO_bio(in, &info); BIO_free(in); - if (!info) + if (!info) { + DATA_PTR(self) = NULL; ossl_raise(eTimestampError, "Error when decoding the timestamp token info"); + } DATA_PTR(self) = info; return self; @@ -1074,7 +1080,11 @@ ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data) } static int +#if !defined(LIBRESSL_VERSION_NUMBER) ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec) +#else +ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, time_t *sec, long *usec) +#endif { *sec = *((long *)data); *usec = 0; diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index 61543d44f6..9035a70aa9 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -106,6 +106,13 @@ VALUE cX509StoreContext; VALUE eX509StoreError; static void +ossl_x509store_mark(void *ptr) +{ + X509_STORE *store = ptr; + rb_gc_mark((VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx)); +} + +static void ossl_x509store_free(void *ptr) { X509_STORE_free(ptr); @@ -114,7 +121,7 @@ ossl_x509store_free(void *ptr) static const rb_data_type_t ossl_x509store_type = { "OpenSSL/X509/STORE", { - 0, ossl_x509store_free, + ossl_x509store_mark, ossl_x509store_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; @@ -457,23 +464,16 @@ ossl_x509store_verify(int argc, VALUE *argv, VALUE self) } /* - * Public Functions - */ -static void ossl_x509stctx_free(void*); - - -static const rb_data_type_t ossl_x509stctx_type = { - "OpenSSL/X509/STORE_CTX", - { - 0, ossl_x509stctx_free, - }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, -}; - -/* * Private functions */ static void +ossl_x509stctx_mark(void *ptr) +{ + X509_STORE_CTX *ctx = ptr; + rb_gc_mark((VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx)); +} + +static void ossl_x509stctx_free(void *ptr) { X509_STORE_CTX *ctx = ptr; @@ -484,6 +484,14 @@ ossl_x509stctx_free(void *ptr) X509_STORE_CTX_free(ctx); } +static const rb_data_type_t ossl_x509stctx_type = { + "OpenSSL/X509/STORE_CTX", + { + ossl_x509stctx_mark, ossl_x509stctx_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + static VALUE ossl_x509stctx_alloc(VALUE klass) { @@ -517,7 +525,9 @@ static VALUE ossl_x509stctx_set_time(VALUE, VALUE); /* * call-seq: - * StoreContext.new(store, cert = nil, chain = nil) + * StoreContext.new(store, cert = nil, untrusted = nil) + * + * Sets up a StoreContext for a verification of the X.509 certificate _cert_. */ static VALUE ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) @@ -527,15 +537,24 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) X509_STORE *x509st; X509 *x509 = NULL; STACK_OF(X509) *x509s = NULL; + int state; rb_scan_args(argc, argv, "12", &store, &cert, &chain); GetX509StCtx(self, ctx); GetX509Store(store, x509st); - if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */ - if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain); - if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){ + if (!NIL_P(cert)) + x509 = DupX509CertPtr(cert); /* NEED TO DUP */ + if (!NIL_P(chain)) { + x509s = ossl_protect_x509_ary2sk(chain, &state); + if (state) { + X509_free(x509); + rb_jump_tag(state); + } + } + if (X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){ + X509_free(x509); sk_X509_pop_free(x509s, X509_free); - ossl_raise(eX509StoreError, NULL); + ossl_raise(eX509StoreError, "X509_STORE_CTX_init"); } if (!NIL_P(t = rb_iv_get(store, "@time"))) ossl_x509stctx_set_time(self, t); diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index 178f5aba0e..65b36dd180 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -174,6 +174,48 @@ class OpenSSL::TestCipher < OpenSSL::TestCase assert_not_predicate(cipher, :authenticated?) end + def test_aes_ccm + # RFC 3610 Section 8, Test Case 1 + key = ["c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"].pack("H*") + iv = ["00000003020100a0a1a2a3a4a5"].pack("H*") + aad = ["0001020304050607"].pack("H*") + pt = ["08090a0b0c0d0e0f101112131415161718191a1b1c1d1e"].pack("H*") + ct = ["588c979a61c663d2f066d0c2c0f989806d5f6b61dac384"].pack("H*") + tag = ["17e8d12cfdf926e0"].pack("H*") + + kwargs = {auth_tag_len: 8, iv_len: 13, key: key, iv: iv} + cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag, cipher.auth_tag + cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad) + assert_equal pt, cipher.update(ct) << cipher.final + + # truncated tag is accepted + cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad) + assert_equal ct, cipher.update(pt) << cipher.final + assert_equal tag[0, 8], cipher.auth_tag(8) + cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag[0, 8], auth_data: aad) + assert_equal pt, cipher.update(ct) << cipher.final + + # wrong tag is rejected + tag2 = tag.dup + tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff) + cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag2, auth_data: aad) + assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) } + + # wrong aad is rejected + aad2 = aad[0..-2] << aad[-1].succ + cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad2) + assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) } + + # wrong ciphertext is rejected + ct2 = ct[0..-2] << ct[-1].succ + cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct2.length, auth_tag: tag, auth_data: aad) + assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) } + end if has_cipher?("aes-128-ccm") && + OpenSSL::Cipher.new("aes-128-ccm").authenticated? && + OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10101000 # version >= v1.1.1 + def test_aes_gcm # GCM spec Appendix B Test Case 4 key = ["feffe9928665731c6d6a8f9467308308"].pack("H*") diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb index f65392c18d..e041c1a3ba 100644 --- a/test/openssl/test_config.rb +++ b/test/openssl/test_config.rb @@ -61,14 +61,14 @@ foo\\bar::foo\\bar = baz [default1 default2]\t\t # space is allowed in section name fo =b ar # space allowed in value [emptysection] - [doller ] + [dollar ] foo=bar bar = $(foo) baz = 123$(default::bar)456${foo}798 qux = ${baz} quxx = $qux.$qux __EOC__ - assert_equal(['default', 'default1 default2', 'doller', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort) + assert_equal(['default', 'default1 default2', 'dollar', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort) assert_equal(['', 'a', 'bar', 'baz', 'd', 'dq', 'dq2', 'esc', 'foo\\bar', 'sq'], c['default'].keys.sort) assert_equal('c', c['default']['']) assert_equal('', c['default']['a']) @@ -84,12 +84,12 @@ __EOC__ assert_equal('baz', c['foo\\bar']['foo\\bar']) assert_equal('b ar', c['default1 default2']['fo']) - # dolloer - assert_equal('bar', c['doller']['foo']) - assert_equal('bar', c['doller']['bar']) - assert_equal('123baz456bar798', c['doller']['baz']) - assert_equal('123baz456bar798', c['doller']['qux']) - assert_equal('123baz456bar798.123baz456bar798', c['doller']['quxx']) + # dollar + assert_equal('bar', c['dollar']['foo']) + assert_equal('bar', c['dollar']['bar']) + assert_equal('123baz456bar798', c['dollar']['baz']) + assert_equal('123baz456bar798', c['dollar']['qux']) + assert_equal('123baz456bar798.123baz456bar798', c['dollar']['quxx']) excn = assert_raise(OpenSSL::ConfigError) do OpenSSL::Config.parse("foo = $bar") diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 6095d545b5..2d9ee7bc70 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -257,7 +257,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase def test_client_auth_success vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - start_server(verify_mode: vflag) { |port| + start_server(verify_mode: vflag, + ctx_proc: proc { |ctx| + ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0) + }) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.key = @cli_key ctx.cert = @cli_cert @@ -303,6 +306,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase end def test_client_ca + pend "LibreSSL 3.2 has broken client CA support" if libressl?(3, 2, 0) + ctx_proc = Proc.new do |ctx| ctx.client_ca = [@ca_cert] end @@ -481,6 +486,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase }) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE + ctx.max_version = :TLS1_2 if libressl?(3, 2, 0) && !libressl?(3, 3, 0) server_connect(port, ctx) { |ssl| ssl.puts "abc"; ssl.gets @@ -868,11 +874,13 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase def test_verify_hostname_on_connect ctx_proc = proc { |ctx| + san = "DNS:a.example.com,DNS:*.b.example.com" + san += ",DNS:c*.example.com,DNS:d.*.example.com" unless libressl?(3, 2, 2) exts = [ ["keyUsage", "keyEncipherment,digitalSignature", true], - ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \ - "DNS:c*.example.com,DNS:d.*.example.com"], + ["subjectAltName", san], ] + ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) ctx.key = @svr_key } @@ -893,6 +901,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase ["cx.example.com", true], ["d.x.example.com", false], ].each do |name, expected_ok| + next if name.start_with?('cx') if libressl?(3, 2, 2) begin sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) @@ -1581,12 +1590,13 @@ end end end - def test_ecdh_curves + def test_ecdh_curves_tls12 pend "EC is disabled" unless defined?(OpenSSL::PKey::EC) ctx_proc = -> ctx { # Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3 - ctx.ciphers = "DEFAULT:!kRSA:!kEDH" + ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + ctx.ciphers = "kEECDH" ctx.ecdh_curves = "P-384:P-521" } start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| @@ -1595,13 +1605,9 @@ end server_connect(port, ctx) { |ssl| cs = ssl.cipher[0] - if /\ATLS/ =~ cs # Is TLS 1.3 is used? + assert_match (/\AECDH/), cs + if ssl.respond_to?(:tmp_key) assert_equal "secp384r1", ssl.tmp_key.group.curve_name - else - assert_match (/\AECDH/), cs - if ssl.respond_to?(:tmp_key) - assert_equal "secp384r1", ssl.tmp_key.group.curve_name - end end ssl.puts "abc"; assert_equal "abc\n", ssl.gets } @@ -1625,6 +1631,26 @@ end end end + def test_ecdh_curves_tls13 + pend "EC is disabled" unless defined?(OpenSSL::PKey::EC) + pend "TLS 1.3 not supported" unless tls13_supported? + + ctx_proc = -> ctx { + # Assume TLS 1.3 is enabled and chosen by default + ctx.ecdh_curves = "P-384:P-521" + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.ecdh_curves = "P-256:P-384" # disable P-521 + + server_connect(port, ctx) { |ssl| + assert_equal "TLSv1.3", ssl.ssl_version + assert_equal "secp384r1", ssl.tmp_key.group.curve_name + ssl.puts "abc"; assert_equal "abc\n", ssl.gets + } + end + end + def test_security_level ctx = OpenSSL::SSL::SSLContext.new begin diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb index 89726d4463..a98efdae2a 100644 --- a/test/openssl/test_ssl_session.rb +++ b/test/openssl/test_ssl_session.rb @@ -122,6 +122,7 @@ __EOS__ ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET # Disable server-side session cache which is enabled by default ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF + ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0) } start_server(ctx_proc: ctx_proc) do |port| sess1 = server_connect_with_session(port, nil, nil) { |ssl| diff --git a/test/openssl/test_ts.rb b/test/openssl/test_ts.rb index 8e31a7d28d..7cb1a1fe8e 100644 --- a/test/openssl/test_ts.rb +++ b/test/openssl/test_ts.rb @@ -181,6 +181,12 @@ _end_of_pem_ assert_equal(42, qer2.nonce) end + def test_request_invalid_asn1 + assert_raise(OpenSSL::Timestamp::TimestampError) do + OpenSSL::Timestamp::Request.new("*" * 44) + end + end + def test_response_constants assert_equal(0, OpenSSL::Timestamp::Response::GRANTED) assert_equal(1, OpenSSL::Timestamp::Response::GRANTED_WITH_MODS) @@ -338,6 +344,12 @@ _end_of_pem_ end end + def test_response_invalid_asn1 + assert_raise(OpenSSL::Timestamp::TimestampError) do + OpenSSL::Timestamp::Response.new("*" * 44) + end + end + def test_no_cert_requested req = OpenSSL::Timestamp::Request.new req.algorithm = "SHA1" @@ -590,6 +602,12 @@ _end_of_pem_ assert_equal(123, info.nonce) end + def test_token_info_invalid_asn1 + assert_raise(OpenSSL::Timestamp::TimestampError) do + OpenSSL::Timestamp::TokenInfo.new("*" * 44) + end + end + private def assert_cert expected, actual diff --git a/test/openssl/test_x509store.rb b/test/openssl/test_x509store.rb index 1cbc73d539..e9602e3434 100644 --- a/test/openssl/test_x509store.rb +++ b/test/openssl/test_x509store.rb @@ -66,7 +66,7 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase ee1_cert = issue_cert(@ee1, @dsa256, 10, ee_exts, ca2_cert, @rsa1024) ee2_cert = issue_cert(@ee2, @dsa512, 20, ee_exts, ca2_cert, @rsa1024) ee3_cert = issue_cert(@ee2, @dsa512, 30, ee_exts, ca2_cert, @rsa1024, - not_before: now-100, not_after: now-50) + not_before: now-100, not_after: now-1) ee4_cert = issue_cert(@ee2, @dsa512, 40, ee_exts, ca2_cert, @rsa1024, not_before: now+1000, not_after: now+2000,) @@ -128,7 +128,7 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase assert_equal(@ee2.to_der, chain[0].subject.to_der) assert_equal(@ca2.to_der, chain[1].subject.to_der) assert_equal(@ca1.to_der, chain[2].subject.to_der) - assert_equal(false, store.verify(ee3_cert), "now=#{now.inspect} Time.now=#{Time.now.inspect} store=#{store.inspect} ee3_cert=#{ee3_cert.inspect}") + assert_equal(false, store.verify(ee3_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) assert_match(/expire/i, store.error_string) assert_equal(false, store.verify(ee4_cert)) diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb index 3776fbac4e..ee734d98a4 100644 --- a/test/openssl/utils.rb +++ b/test/openssl/utils.rb @@ -199,6 +199,14 @@ class OpenSSL::SSLTestCase < OpenSSL::TestCase rescue end + def tls13_supported? + return false unless defined?(OpenSSL::SSL::TLS1_3_VERSION) + ctx = OpenSSL::SSL::SSLContext.new + ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION + true + rescue + end + def readwrite_loop(ctx, ssl) while line = ssl.gets ssl.write(line) |