Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ext/openssl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def find_openssl_library
have_func("EVP_PKEY_check(NULL)", evp_h)

# added in 3.0.0
have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
have_func("SSL_CTX_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
have_func("ERR_get_error_all(NULL, NULL, NULL, NULL, NULL)", "openssl/err.h")
have_func("SSL_CTX_load_verify_file(NULL, \"\")", ssl_h)
have_func("BN_check_prime(NULL, NULL, NULL)", "openssl/bn.h")
Expand Down
48 changes: 10 additions & 38 deletions ext/openssl/lib/openssl/ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,6 @@ class SSLContext
}.call
}

if defined?(OpenSSL::PKey::DH)
DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
_end_of_pem_
private_constant :DH_ffdhe2048

DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
warn "using default DH parameters." if $VERBOSE
DH_ffdhe2048
}
end

if !OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL")
DEFAULT_PARAMS.merge!(
min_version: OpenSSL::SSL::TLS1_VERSION,
Expand Down Expand Up @@ -85,26 +66,14 @@ class SSLContext
AES256-SHA256
AES128-SHA
AES256-SHA
}.join(":"),
}.join(":").freeze,
)
end
DEFAULT_PARAMS.freeze

DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
DEFAULT_CERT_STORE.set_default_paths

# A callback invoked when DH parameters are required for ephemeral DH key
# exchange.
#
# The callback is invoked with the SSLSocket, a
# flag indicating the use of an export cipher and the keylength
# required.
#
# The callback must return an OpenSSL::PKey::DH instance of the correct
# key length.
#
# <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead.
attr_accessor :tmp_dh_callback

# A callback invoked at connect time to distinguish between multiple
# server names.
#
Expand Down Expand Up @@ -146,7 +115,14 @@ def set_params(params={})
params.each{|name, value| self.__send__("#{name}=", value) }
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
unless self.ca_file or self.ca_path or self.cert_store
self.cert_store = DEFAULT_CERT_STORE
if not defined?(Ractor) or Ractor.current == Ractor.main
self.cert_store = DEFAULT_CERT_STORE
else
self.cert_store = Ractor.current[:__openssl_default_store__] ||=
OpenSSL::X509::Store.new.tap { |store|
store.set_default_paths
}
end
end
end
return params
Expand Down Expand Up @@ -456,10 +432,6 @@ def client_cert_cb
@context.client_cert_cb
end

def tmp_dh_callback
@context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
end

def session_new_cb
@context.session_new_cb
end
Expand Down
85 changes: 47 additions & 38 deletions ext/openssl/ossl_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ VALUE cSSLSocket;
static VALUE eSSLErrorWaitReadable;
static VALUE eSSLErrorWaitWritable;

static ID id_call, ID_callback_state, id_tmp_dh_callback,
id_npn_protocols_encoded, id_each;
static ID id_call, ID_callback_state, 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,
Expand All @@ -47,7 +46,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
id_i_verify_hostname, id_i_keylog_cb;
id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback;
static ID id_i_io, id_i_context, id_i_hostname;

static int ossl_ssl_ex_ptr_idx;
Expand Down Expand Up @@ -90,6 +89,7 @@ ossl_sslctx_s_alloc(VALUE klass)
ossl_raise(eSSLError, "SSL_CTX_new");
}
SSL_CTX_set_mode(ctx, mode);
SSL_CTX_set_dh_auto(ctx, 1);
RTYPEDDATA_DATA(obj) = ctx;
SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_ptr_idx, (void *)obj);

Expand Down Expand Up @@ -133,8 +133,6 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
#if !defined(OPENSSL_NO_DH)
struct tmp_dh_callback_args {
VALUE ssl_obj;
ID id;
int type;
int is_export;
int keylength;
};
Expand All @@ -143,48 +141,36 @@ static VALUE
ossl_call_tmp_dh_callback(VALUE arg)
{
struct tmp_dh_callback_args *args = (struct tmp_dh_callback_args *)arg;
VALUE cb, dh;
EVP_PKEY *pkey;
VALUE ctx_obj, cb, obj;
const DH *dh;

cb = rb_funcall(args->ssl_obj, args->id, 0);
ctx_obj = rb_attr_get(args->ssl_obj, id_i_context);
cb = rb_attr_get(ctx_obj, id_i_tmp_dh_callback);
if (NIL_P(cb))
return (VALUE)NULL;
dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
INT2NUM(args->keylength));
pkey = GetPKeyPtr(dh);
if (EVP_PKEY_base_id(pkey) != args->type)
return (VALUE)NULL;
return (VALUE)NULL;

obj = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
INT2NUM(args->keylength));
// TODO: We should riase if obj is not DH
dh = EVP_PKEY_get0_DH(GetPKeyPtr(obj));
if (!dh)
ossl_clear_error();

return (VALUE)pkey;
return (VALUE)dh;
}
#endif

#if !defined(OPENSSL_NO_DH)
static DH *
ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength)
{
VALUE rb_ssl;
EVP_PKEY *pkey;
struct tmp_dh_callback_args args;
int state;

rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
args.ssl_obj = rb_ssl;
args.id = id_tmp_dh_callback;
args.is_export = is_export;
args.keylength = keylength;
args.type = EVP_PKEY_DH;

pkey = (EVP_PKEY *)rb_protect(ossl_call_tmp_dh_callback,
(VALUE)&args, &state);
VALUE rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
struct tmp_dh_callback_args args = {rb_ssl, is_export, keylength};
VALUE ret = rb_protect(ossl_call_tmp_dh_callback, (VALUE)&args, &state);
if (state) {
rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
return NULL;
}
if (!pkey)
return NULL;

return (DH *)EVP_PKEY_get0_DH(pkey);
return (DH *)ret;
}
#endif /* OPENSSL_NO_DH */

Expand Down Expand Up @@ -703,7 +689,10 @@ ossl_sslctx_setup(VALUE self)
GetSSLCTX(self, ctx);

#if !defined(OPENSSL_NO_DH)
SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
if (!NIL_P(rb_attr_get(self, id_i_tmp_dh_callback))) {
SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
SSL_CTX_set_dh_auto(ctx, 0);
}
#endif

#if !defined(OPENSSL_IS_AWSLC) /* AWS-LC has no support for TLS 1.3 PHA. */
Expand Down Expand Up @@ -1148,7 +1137,7 @@ ossl_sslctx_set_client_sigalgs(VALUE self, VALUE v)
* contained in the key object, if any, are ignored. The server will always
* generate a new key pair for each handshake.
*
* Added in version 3.0. See also the man page SSL_set0_tmp_dh_pkey(3).
* Added in version 3.0. See also the man page SSL_CTX_set0_tmp_dh_pkey(3).
*
* Example:
* ctx = OpenSSL::SSL::SSLContext.new
Expand All @@ -1169,7 +1158,7 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH)
rb_raise(eSSLError, "invalid pkey type %s (expected DH)",
OBJ_nid2sn(EVP_PKEY_base_id(pkey)));
#ifdef HAVE_SSL_SET0_TMP_DH_PKEY
#ifdef HAVE_SSL_CTX_SET0_TMP_DH_PKEY
if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey))
ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey");
EVP_PKEY_up_ref(pkey);
Expand All @@ -1178,6 +1167,9 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh");
#endif

// Turn off the "auto" DH parameters set by ossl_sslctx_s_alloc()
SSL_CTX_set_dh_auto(ctx, 0);

return arg;
}
#endif
Expand Down Expand Up @@ -2865,6 +2857,23 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern_const("client_cert_cb"), 1, 1, Qfalse);

#ifndef OPENSSL_NO_DH
/*
* A callback invoked when DH parameters are required for ephemeral DH key
* exchange.
*
* The callback is invoked with the SSLSocket, a
* flag indicating the use of an export cipher and the keylength
* required.
*
* The callback must return an OpenSSL::PKey::DH instance of the correct
* key length.
*
* <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead.
*/
rb_attr(cSSLContext, rb_intern_const("tmp_dh_callback"), 1, 1, Qfalse);
#endif

/*
* Sets the context in which a session can be reused. This allows
* sessions for multiple applications to be distinguished, for example, by
Expand Down Expand Up @@ -3258,7 +3267,6 @@ Init_ossl_ssl(void)
sym_wait_readable = ID2SYM(rb_intern_const("wait_readable"));
sym_wait_writable = ID2SYM(rb_intern_const("wait_writable"));

id_tmp_dh_callback = rb_intern_const("tmp_dh_callback");
id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded");
id_each = rb_intern_const("each");

Expand Down Expand Up @@ -3289,6 +3297,7 @@ Init_ossl_ssl(void)
DefIVarID(servername_cb);
DefIVarID(verify_hostname);
DefIVarID(keylog_cb);
DefIVarID(tmp_dh_callback);

DefIVarID(io);
DefIVarID(context);
Expand Down
2 changes: 1 addition & 1 deletion gems/bundled_gems
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ ostruct 0.6.3 https://github.com/ruby/ostruct
pstore 0.2.0 https://github.com/ruby/pstore
benchmark 0.4.1 https://github.com/ruby/benchmark
logger 1.7.0 https://github.com/ruby/logger
rdoc 6.14.2 https://github.com/ruby/rdoc
rdoc 6.15.0 https://github.com/ruby/rdoc ac2a6fbf62b584a8325a665a9e7b368388bc7df6
win32ole 1.9.2 https://github.com/ruby/win32ole
irb 1.15.2 https://github.com/ruby/irb d43c3d764ae439706aa1b26a3ec299cc45eaed5b
reline 0.6.2 https://github.com/ruby/reline
Expand Down
1 change: 1 addition & 0 deletions prism/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ errors:
- CONDITIONAL_WHILE_PREDICATE
- CONSTANT_PATH_COLON_COLON_CONSTANT
- DEF_ENDLESS
- DEF_ENDLESS_PARAMETERS
- DEF_ENDLESS_SETTER
- DEF_NAME
- DEF_PARAMS_TERM
Expand Down
32 changes: 32 additions & 0 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -14612,6 +14612,18 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord
return true;
}

/**
* Ensures that after parsing a parameter, the next token is not `=`.
* Some parameters like `def(* = 1)` cannot become optional. When no parens
* are present like in `def * = 1`, this creates ambiguity with endless method definitions.
*/
static inline void
refute_optional_parameter(pm_parser_t *parser) {
if (match1(parser, PM_TOKEN_EQUAL)) {
pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
}
}

/**
* Parse a list of parameters on a method definition.
*/
Expand Down Expand Up @@ -14664,6 +14676,10 @@ parse_parameters(
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
}

if (!uses_parentheses) {
refute_optional_parameter(parser);
}

pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
if (repeated) {
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
Expand All @@ -14685,6 +14701,10 @@ parse_parameters(
bool succeeded = update_parameter_state(parser, &parser->current, &order);
parser_lex(parser);

if (!uses_parentheses) {
refute_optional_parameter(parser);
}

parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);

Expand Down Expand Up @@ -14866,6 +14886,10 @@ parse_parameters(
context_pop(parser);
pm_parameters_node_keywords_append(params, param);

if (!uses_parentheses) {
refute_optional_parameter(parser);
}

// If parsing the value of the parameter resulted in error recovery,
// then we can put a missing node in its place and stop parsing the
// parameters entirely now.
Expand Down Expand Up @@ -14897,6 +14921,10 @@ parse_parameters(
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
}

if (!uses_parentheses) {
refute_optional_parameter(parser);
}

pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name);
if (repeated) {
pm_node_flag_set_repeated_parameter(param);
Expand Down Expand Up @@ -14945,6 +14973,10 @@ parse_parameters(
}
}

if (!uses_parentheses) {
refute_optional_parameter(parser);
}

if (params->keyword_rest == NULL) {
pm_parameters_node_keyword_rest_set(params, param);
} else {
Expand Down
1 change: 1 addition & 0 deletions prism/templates/src/diagnostic.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_CONDITIONAL_WHILE_PREDICATE] = { "expected a predicate expression for the `while` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_ENDLESS_PARAMETERS] = { "could not parse the endless method parameters", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX },
Expand Down
1 change: 1 addition & 0 deletions spec/mspec/lib/mspec/commands/mspec-ci.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def options(argv = ARGV)
options.chdir
options.prefix
options.configure { |f| load f }
options.repeat
options.pretend
options.interrupt
options.timeout
Expand Down
5 changes: 5 additions & 0 deletions spec/mspec/spec/commands/mspec_ci_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@
@script.options []
end

it "enables the repeat option" do
expect(@options).to receive(:repeat)
@script.options @argv
end

it "calls #custom_options" do
expect(@script).to receive(:custom_options).with(@options)
@script.options []
Expand Down
Loading