summaryrefslogtreecommitdiff
path: root/ext/openssl
diff options
context:
space:
mode:
authorrhe <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-05-30 09:30:38 +0000
committerrhe <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-05-30 09:30:38 +0000
commitf26f3589302e7e26ecb69180d2b2a2f7f7eaf416 (patch)
tree4a798f0604e2c2a87bbd93955cd87ed754c2133f /ext/openssl
parent94a91b1d60d048dcf75039d6d64ad9ee7e5929f4 (diff)
openssl: add SSLContext#ecdh_curves=
* ext/openssl/ossl_ssl.c (ossl_sslctx_s_alloc): Enable the automatic curve selection for ECDH by calling SSL_CTX_set_ecdh_auto(). With this a TLS server automatically selects a curve which both the client and the server support to use in ECDH. This changes the default behavior but users can still disable ECDH by excluding 'ECDH' cipher suites from the cipher list (with SSLContext#ciphers=). This commit also deprecate #tmp_ecdh_callback=. It was added in Ruby 2.3.0. It wraps SSL_CTX_set_tmp_ecdh_callback() which will be removed in OpenSSL 1.1.0. Its callback receives two values 'is_export' and 'keylength' but both are completely useless for determining a curve to use in ECDH. The automatic curve selection was introduced to replace this. (ossl_sslctx_setup): Deprecate SSLContext#tmp_ecdh_callback=. Emit a warning if this is in use. (ossl_sslctx_set_ecdh_curves): Add SSLContext#ecdh_curves=. Wrap SSL_CTX_set1_curves_list(). If it is not available, this falls back to SSL_CTX_set_tmp_ecdh(). (Init_ossl_ssl): Define SSLContext#ecdh_curves=. * ext/openssl/extconf.rb: Check the existence of EC_curve_nist2nid(), SSL_CTX_set1_curves_list(), SSL_CTX_set_ecdh_auto() and SSL_CTX_set_tmp_ecdh_callback(). * ext/openssl/openssl_missing.[ch]: Implement EC_curve_nist2nid() if missing. * test/openssl/test_pair.rb (test_ecdh_callback): Use EnvUtil.suppress_warning to suppress deprecated warning. (test_ecdh_curves): Test that SSLContext#ecdh_curves= works. * test/openssl/utils.rb (start_server): Use SSLContext#ecdh_curves=. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/openssl')
-rw-r--r--ext/openssl/extconf.rb4
-rw-r--r--ext/openssl/openssl_missing.c37
-rw-r--r--ext/openssl/openssl_missing.h6
-rw-r--r--ext/openssl/ossl_ssl.c118
4 files changed, 161 insertions, 4 deletions
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 14daae9786..eeeae448c0 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -98,13 +98,17 @@ have_func("SSL_CTX_set_next_proto_select_cb")
have_macro("EVP_CTRL_GCM_GET_TAG", ['openssl/evp.h']) && $defs.push("-DHAVE_AUTHENTICATED_ENCRYPTION")
# added in 1.0.2
+have_func("EC_curve_nist2nid")
have_func("X509_REVOKED_dup")
have_func("SSL_CTX_set_alpn_select_cb")
+OpenSSL.check_func_or_macro("SSL_CTX_set1_curves_list", "openssl/ssl.h")
+OpenSSL.check_func_or_macro("SSL_CTX_set_ecdh_auto", "openssl/ssl.h")
OpenSSL.check_func_or_macro("SSL_get_server_tmp_key", "openssl/ssl.h")
# added in 1.1.0
have_func("X509_STORE_get_ex_data")
have_func("X509_STORE_set_ex_data")
+OpenSSL.check_func_or_macro("SSL_CTX_set_tmp_ecdh_callback", "openssl/ssl.h") # removed
Logging::message "=== Checking done. ===\n"
diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c
index 7b00ae4d8c..796d8aa082 100644
--- a/ext/openssl/openssl_missing.c
+++ b/ext/openssl/openssl_missing.c
@@ -58,3 +58,40 @@ HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in)
}
#endif /* HAVE_HMAC_CTX_COPY */
#endif /* NO_HMAC */
+
+/* added in 1.0.2 */
+#if !defined(OPENSSL_NO_EC)
+#if !defined(HAVE_EC_CURVE_NIST2NID)
+static struct {
+ const char *name;
+ int nid;
+} nist_curves[] = {
+ {"B-163", NID_sect163r2},
+ {"B-233", NID_sect233r1},
+ {"B-283", NID_sect283r1},
+ {"B-409", NID_sect409r1},
+ {"B-571", NID_sect571r1},
+ {"K-163", NID_sect163k1},
+ {"K-233", NID_sect233k1},
+ {"K-283", NID_sect283k1},
+ {"K-409", NID_sect409k1},
+ {"K-571", NID_sect571k1},
+ {"P-192", NID_X9_62_prime192v1},
+ {"P-224", NID_secp224r1},
+ {"P-256", NID_X9_62_prime256v1},
+ {"P-384", NID_secp384r1},
+ {"P-521", NID_secp521r1}
+};
+
+int
+EC_curve_nist2nid(const char *name)
+{
+ size_t i;
+ for (i = 0; i < (sizeof(nist_curves) / sizeof(nist_curves[0])); i++) {
+ if (!strcmp(nist_curves[i].name, name))
+ return nist_curves[i].nid;
+ }
+ return NID_undef;
+}
+#endif
+#endif
diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h
index 0f82a180ef..b8f9d3d183 100644
--- a/ext/openssl/openssl_missing.h
+++ b/ext/openssl/openssl_missing.h
@@ -20,6 +20,12 @@ void HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in);
#endif
/* added in 1.0.2 */
+#if !defined(OPENSSL_NO_EC)
+#if !defined(HAVE_EC_CURVE_NIST2NID)
+int EC_curve_nist2nid(const char *);
+#endif
+#endif
+
#if !defined(HAVE_X509_REVOKED_DUP)
# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((i2d_of_void *)i2d_X509_REVOKED, \
(d2i_of_void *)d2i_X509_REVOKED, (char *)(rev))
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);