aboutsummaryrefslogtreecommitdiffstats
path: root/ext/openssl/ossl_ssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/openssl/ossl_ssl.c')
-rw-r--r--ext/openssl/ossl_ssl.c118
1 files changed, 114 insertions, 4 deletions
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 282ed2915c..0f5313bd75 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -162,6 +162,18 @@ ossl_sslctx_s_alloc(VALUE klass)
RTYPEDDATA_DATA(obj) = ctx;
SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj);
+#if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
+ /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It
+ * allows to specify multiple curve names and OpenSSL will select
+ * automatically from them. In OpenSSL 1.0.2, the automatic selection has to
+ * be enabled explicitly. But OpenSSL 1.1.0 removed the knob and it is
+ * always enabled. To uniform the behavior, we enable the automatic
+ * selection also in 1.0.2. Users can still disable ECDH by removing ECDH
+ * cipher suites by SSLContext#ciphers=. */
+ if (!SSL_CTX_set_ecdh_auto(ctx, 1))
+ ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
+#endif
+
return obj;
}
@@ -711,10 +723,24 @@ ossl_sslctx_setup(VALUE self)
#endif
#if !defined(OPENSSL_NO_EC)
- if (RTEST(ossl_sslctx_get_tmp_ecdh_cb(self))){
+ /* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0,
+ * but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */
+ if (RTEST(ossl_sslctx_get_tmp_ecdh_cb(self))) {
+# if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
+ rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead");
SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback);
+# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
+ /* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores
+ * tmp_ecdh_callback. So disable ecdh_auto. */
+ if (!SSL_CTX_set_ecdh_auto(ctx, 0))
+ ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
+# endif
+# else
+ ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; "
+ "use #ecdh_curves= instead");
+# endif
}
-#endif
+#endif /* OPENSSL_NO_EC */
val = ossl_sslctx_get_cert_store(self);
if(!NIL_P(val)){
@@ -953,6 +979,87 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
return v;
}
+#if !defined(OPENSSL_NO_EC)
+/*
+ * call-seq:
+ * ctx.ecdh_curves = curve_list -> curve_list
+ *
+ * Sets the list of "supported elliptic curves" for this context.
+ *
+ * For a TLS client, the list is directly used in the Supported Elliptic Curves
+ * Extension. For a server, the list is used by OpenSSL to determine the set of
+ * shared curves. OpenSSL will pick the most appropriate one from it.
+ *
+ * Note that this works differently with old OpenSSL (<= 1.0.1). Only one curve
+ * can be set, and this has no effect for TLS clients.
+ *
+ * === Example
+ * ctx1 = OpenSSL::SSL::SSLContext.new
+ * ctx1.ecdh_curves = "X25519:P-256:P-224"
+ * svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1)
+ * Thread.new { svr.accept }
+ *
+ * ctx2 = OpenSSL::SSL::SSLContext.new
+ * ctx2.ecdh_curves = "P-256"
+ * cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2)
+ * cli.connect
+ *
+ * p cli.tmp_key.group.curve_name
+ * # => "prime256v1" (is an alias for NIST P-256)
+ */
+static VALUE
+ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg)
+{
+ SSL_CTX *ctx;
+
+ rb_check_frozen(self);
+ GetSSLCTX(self, ctx);
+ StringValueCStr(arg);
+
+#if defined(HAVE_SSL_CTX_SET1_CURVES_LIST)
+ if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg)))
+ ossl_raise(eSSLError, NULL);
+#else
+ /* OpenSSL does not have SSL_CTX_set1_curves_list()... Fallback to
+ * SSL_CTX_set_tmp_ecdh(). So only the first curve is used. */
+ {
+ VALUE curve, splitted;
+ EC_KEY *ec;
+ int nid;
+
+ splitted = rb_str_split(arg, ":");
+ if (!RARRAY_LEN(splitted))
+ ossl_raise(eSSLError, "invalid input format");
+ curve = RARRAY_AREF(splitted, 0);
+ StringValueCStr(curve);
+
+ /* SSL_CTX_set1_curves_list() accepts NIST names */
+ nid = EC_curve_nist2nid(RSTRING_PTR(curve));
+ if (nid == NID_undef)
+ nid = OBJ_txt2nid(RSTRING_PTR(curve));
+ if (nid == NID_undef)
+ ossl_raise(eSSLError, "unknown curve name");
+
+ ec = EC_KEY_new_by_curve_name(nid);
+ if (!ec)
+ ossl_raise(eSSLError, NULL);
+ EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
+ SSL_CTX_set_tmp_ecdh(ctx, ec);
+# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
+ /* tmp_ecdh and ecdh_auto conflict. tmp_ecdh is ignored when ecdh_auto
+ * is enabled. So disable ecdh_auto. */
+ if (!SSL_CTX_set_ecdh_auto(ctx, 0))
+ ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
+# endif
+ }
+#endif
+
+ return arg;
+}
+#else
+#define ossl_sslctx_set_ecdh_curves rb_f_notimplement
+#endif
+
/*
* call-seq:
* ctx.session_add(session) -> true | false
@@ -2119,6 +2226,7 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse);
+#if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
/*
* A callback invoked when ECDH parameters are required.
*
@@ -2126,10 +2234,11 @@ Init_ossl_ssl(void)
* flag indicating the use of an export cipher and the keylength
* required.
*
- * The callback must return an OpenSSL::PKey::EC instance of the correct
- * key length.
+ * The callback is deprecated. This does not work with recent versions of
+ * OpenSSL. Use OpenSSL::SSL::SSLContext#ecdh_curves= instead.
*/
rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse);
+#endif
/*
* Sets the context in which a session can be reused. This allows
@@ -2265,6 +2374,7 @@ Init_ossl_ssl(void)
rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1);
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
+ rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);