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

Miscellaneous changes for OpenSSL 3.0 support (part 2) #481

Merged
merged 10 commits into from
Dec 20, 2021
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ jobs:
- openssl-1.0.2u # EOL
- openssl-1.1.0l # EOL
- openssl-1.1.1l
- openssl-3.0.1
- libressl-3.1.5 # EOL
- libressl-3.2.6
- libressl-3.3.4
Expand All @@ -89,7 +90,7 @@ jobs:
curl -OL https://ftp.openssl.org/source/${{ matrix.openssl }}.tar.gz
tar xf ${{ matrix.openssl }}.tar.gz && cd ${{ matrix.openssl }}
# shared is required for 1.0.x.
./Configure --prefix=$HOME/.openssl/${{ matrix.openssl }} \
./Configure --prefix=$HOME/.openssl/${{ matrix.openssl }} --libdir=lib \
shared linux-x86_64
make depend
;;
Expand Down
3 changes: 0 additions & 3 deletions ext/openssl/openssl_missing.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
#include RUBY_EXTCONF_H

#include <string.h> /* memcpy() */
#if !defined(OPENSSL_NO_ENGINE)
# include <openssl/engine.h>
#endif
#include <openssl/x509_vfy.h>

#include "openssl_missing.h"
Expand Down
8 changes: 5 additions & 3 deletions ext/openssl/ossl.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <ruby/io.h>
#include <ruby/thread.h>
#include <openssl/opensslv.h>

#include <openssl/err.h>
#include <openssl/asn1.h>
#include <openssl/x509v3.h>
Expand All @@ -30,9 +31,6 @@
#include <openssl/ts.h>
#endif
#include <openssl/crypto.h>
#if !defined(OPENSSL_NO_ENGINE)
# include <openssl/engine.h>
#endif
#if !defined(OPENSSL_NO_OCSP)
# include <openssl/ocsp.h>
#endif
Expand All @@ -54,6 +52,10 @@
(LIBRESSL_VERSION_NUMBER >= (maj << 28) | (min << 20) | (pat << 12))
#endif

#if !defined(OPENSSL_NO_ENGINE) && !OSSL_OPENSSL_PREREQ(3, 0, 0)
# define OSSL_USE_ENGINE
#endif

/*
* Common Module
*/
Expand Down
3 changes: 2 additions & 1 deletion ext/openssl/ossl_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
*/
#include "ossl.h"

#if !defined(OPENSSL_NO_ENGINE)
#ifdef OSSL_USE_ENGINE
# include <openssl/engine.h>

#define NewEngine(klass) \
TypedData_Wrap_Struct((klass), &ossl_engine_type, 0)
Expand Down
4 changes: 2 additions & 2 deletions ext/openssl/ossl_hmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ static VALUE
ossl_hmac_digest(VALUE self)
{
EVP_MD_CTX *ctx;
size_t buf_len;
size_t buf_len = EVP_MAX_MD_SIZE;
VALUE ret;

GetHMAC(self, ctx);
Expand All @@ -200,7 +200,7 @@ ossl_hmac_hexdigest(VALUE self)
{
EVP_MD_CTX *ctx;
unsigned char buf[EVP_MAX_MD_SIZE];
size_t buf_len;
size_t buf_len = EVP_MAX_MD_SIZE;
VALUE ret;

GetHMAC(self, ctx);
Expand Down
21 changes: 21 additions & 0 deletions ext/openssl/ossl_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
*/
#include "ossl.h"

#ifdef OSSL_USE_ENGINE
# include <openssl/engine.h>
#endif

/*
* Classes
*/
Expand Down Expand Up @@ -312,6 +316,11 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam)
ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
}
else {
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
ctx = EVP_PKEY_CTX_new_from_name(NULL, StringValueCStr(alg), NULL);
if (!ctx)
ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_from_name");
#else
const EVP_PKEY_ASN1_METHOD *ameth;
ENGINE *tmpeng;
int pkey_id;
Expand All @@ -330,6 +339,7 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam)
ctx = EVP_PKEY_CTX_new_id(pkey_id, NULL/* engine */);
if (!ctx)
ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_id");
#endif
}

if (genparam && EVP_PKEY_paramgen_init(ctx) <= 0) {
Expand Down Expand Up @@ -425,9 +435,19 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
return pkey_generate(argc, argv, self, 0);
}

/*
* TODO: There is no convenient way to check the presence of public key
* components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
* these should only be created by OpenSSL::PKey.generate_parameters or by
* parsing DER-/PEM-encoded string. We would need another flag for that.
*/
void
ossl_pkey_check_public_key(const EVP_PKEY *pkey)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
if (EVP_PKEY_missing_parameters(pkey))
ossl_raise(ePKeyError, "parameters missing");
#else
void *ptr;
const BIGNUM *n, *e, *pubkey;

Expand Down Expand Up @@ -463,6 +483,7 @@ ossl_pkey_check_public_key(const EVP_PKEY *pkey)
return;
}
ossl_raise(ePKeyError, "public key missing");
#endif
}

EVP_PKEY *
Expand Down
35 changes: 29 additions & 6 deletions ext/openssl/ossl_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2825,9 +2825,24 @@ Init_ossl_ssl(void)
rb_define_const(mSSL, "VERIFY_CLIENT_ONCE", INT2NUM(SSL_VERIFY_CLIENT_ONCE));

rb_define_const(mSSL, "OP_ALL", ULONG2NUM(SSL_OP_ALL));
#ifdef SSL_OP_CLEANSE_PLAINTEXT /* OpenSSL 3.0 */
rb_define_const(mSSL, "OP_CLEANSE_PLAINTEXT", ULONG2NUM(SSL_OP_CLEANSE_PLAINTEXT));
#endif
rb_define_const(mSSL, "OP_LEGACY_SERVER_CONNECT", ULONG2NUM(SSL_OP_LEGACY_SERVER_CONNECT));
#ifdef SSL_OP_ENABLE_KTLS /* OpenSSL 3.0 */
rb_define_const(mSSL, "OP_ENABLE_KTLS", ULONG2NUM(SSL_OP_ENABLE_KTLS));
#endif
rb_define_const(mSSL, "OP_TLSEXT_PADDING", ULONG2NUM(SSL_OP_TLSEXT_PADDING));
rb_define_const(mSSL, "OP_SAFARI_ECDHE_ECDSA_BUG", ULONG2NUM(SSL_OP_SAFARI_ECDHE_ECDSA_BUG));
#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF /* OpenSSL 3.0 */
rb_define_const(mSSL, "OP_IGNORE_UNEXPECTED_EOF", ULONG2NUM(SSL_OP_IGNORE_UNEXPECTED_EOF));
#endif
#ifdef SSL_OP_ALLOW_CLIENT_RENEGOTIATION /* OpenSSL 3.0 */
rb_define_const(mSSL, "OP_ALLOW_CLIENT_RENEGOTIATION", ULONG2NUM(SSL_OP_ALLOW_CLIENT_RENEGOTIATION));
#endif
#ifdef SSL_OP_DISABLE_TLSEXT_CA_NAMES /* OpenSSL 3.0 */
rb_define_const(mSSL, "OP_DISABLE_TLSEXT_CA_NAMES", ULONG2NUM(SSL_OP_DISABLE_TLSEXT_CA_NAMES));
#endif
#ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_ALLOW_NO_DHE_KEX", ULONG2NUM(SSL_OP_ALLOW_NO_DHE_KEX));
#endif
Expand All @@ -2839,20 +2854,28 @@ Init_ossl_ssl(void)
#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_NO_ENCRYPT_THEN_MAC", ULONG2NUM(SSL_OP_NO_ENCRYPT_THEN_MAC));
#endif
rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE));
rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG));
#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION));
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_ENABLE_MIDDLEBOX_COMPAT", ULONG2NUM(SSL_OP_ENABLE_MIDDLEBOX_COMPAT));
#endif
#ifdef SSL_OP_PRIORITIZE_CHACHA /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_PRIORITIZE_CHACHA", ULONG2NUM(SSL_OP_PRIORITIZE_CHACHA));
#endif
#ifdef SSL_OP_NO_ANTI_REPLAY /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_NO_ANTI_REPLAY", ULONG2NUM(SSL_OP_NO_ANTI_REPLAY));
#endif
rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG));

rb_define_const(mSSL, "OP_NO_SSLv3", ULONG2NUM(SSL_OP_NO_SSLv3));
rb_define_const(mSSL, "OP_NO_TLSv1", ULONG2NUM(SSL_OP_NO_TLSv1));
rb_define_const(mSSL, "OP_NO_TLSv1_1", ULONG2NUM(SSL_OP_NO_TLSv1_1));
rb_define_const(mSSL, "OP_NO_TLSv1_2", ULONG2NUM(SSL_OP_NO_TLSv1_2));
#ifdef SSL_OP_NO_TLSv1_3 /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_NO_TLSv1_3", ULONG2NUM(SSL_OP_NO_TLSv1_3));
#endif
rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE));
rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG));
#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */
rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION));
#endif
rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG));

/* SSL_OP_* flags for DTLS */
#if 0
Expand Down
13 changes: 5 additions & 8 deletions test/openssl/test_cipher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,11 @@ def test_ctr_if_exists
end

def test_ciphers
OpenSSL::Cipher.ciphers.each{|name|
next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name
begin
assert_kind_of(OpenSSL::Cipher, OpenSSL::Cipher.new(name))
rescue OpenSSL::Cipher::CipherError => e
raise unless /wrap/ =~ name and /wrap mode not allowed/ =~ e.message
end
}
ciphers = OpenSSL::Cipher.ciphers
assert_kind_of Array, ciphers
assert_include ciphers, "aes-128-cbc"
assert_include ciphers, "aes128" # alias of aes-128-cbc
assert_include ciphers, "aes-128-gcm"
end

def test_AES
Expand Down
1 change: 1 addition & 0 deletions test/openssl/test_hmac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def test_hmac
end

def test_dup
pend "HMAC#initialize_copy is currently broken on OpenSSL 3.0.0" if openssl?(3, 0, 0)
h1 = OpenSSL::HMAC.new("KEY", "MD5")
h1.update("DATA")
h = h1.dup
Expand Down
85 changes: 44 additions & 41 deletions test/openssl/test_ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -893,14 +893,12 @@ def test_accept_errors_include_peeraddr
end
end

begin
sock = TCPSocket.new("127.0.0.1", port)
sock.puts "abc"
ensure
sock&.close
end
sock = TCPSocket.new("127.0.0.1", port)
sock << "\x00" * 1024

assert t.join
ensure
sock&.close
server.close
end

Expand Down Expand Up @@ -1210,46 +1208,51 @@ def test_minmax_version
end

def test_options_disable_versions
# Note: Use of these OP_* flags has been deprecated since OpenSSL 1.1.0.
# It's recommended to use SSLContext#{min,max}_version= instead in real
# applications. The purpose of this test case is to check that SSL options
# are properly propagated to OpenSSL library.
supported = check_supported_protocol_versions
if !defined?(OpenSSL::SSL::TLS1_3_VERSION) ||
!supported.include?(OpenSSL::SSL::TLS1_2_VERSION) ||
!supported.include?(OpenSSL::SSL::TLS1_3_VERSION) ||
!defined?(OpenSSL::SSL::OP_NO_TLSv1_3) # LibreSSL < 3.4
pend "this test case requires both TLS 1.2 and TLS 1.3 to be supported " \
"and enabled by default"
end

if supported.include?(OpenSSL::SSL::TLS1_1_VERSION) &&
supported.include?(OpenSSL::SSL::TLS1_2_VERSION)
# Server disables ~ TLS 1.1
ctx_proc = proc { |ctx|
ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 |
OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
# Client only supports TLS 1.1
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_1_VERSION
assert_handshake_error { server_connect(port, ctx1) { } }
# Server disables TLS 1.2 and earlier
ctx_proc = proc { |ctx|
ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 |
OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 |
OpenSSL::SSL::OP_NO_TLSv1_2
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
# Client only supports TLS 1.2
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION
assert_handshake_error { server_connect(port, ctx1) { } }

# Client only supports TLS 1.2
ctx2 = OpenSSL::SSL::SSLContext.new
ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_2_VERSION
assert_nothing_raised { server_connect(port, ctx2) { } }
}
# Client only supports TLS 1.3
ctx2 = OpenSSL::SSL::SSLContext.new
ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_3_VERSION
assert_nothing_raised { server_connect(port, ctx2) { } }
}

# Server only supports TLS 1.1
ctx_proc = proc { |ctx|
ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
# Client disables TLS 1.1
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_1
assert_handshake_error { server_connect(port, ctx1) { } }
# Server only supports TLS 1.2
ctx_proc = proc { |ctx|
ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
# Client doesn't support TLS 1.2
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_2
assert_handshake_error { server_connect(port, ctx1) { } }

# Client disables TLS 1.2
ctx2 = OpenSSL::SSL::SSLContext.new
ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_2
assert_nothing_raised { server_connect(port, ctx2) { } }
}
else
pend "TLS 1.1 and TLS 1.2 must be supported; skipping"
end
# Client supports TLS 1.2 by default
ctx2 = OpenSSL::SSL::SSLContext.new
ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_3
assert_nothing_raised { server_connect(port, ctx2) { } }
}
end

def test_ssl_methods_constant
Expand Down