aboutsummaryrefslogtreecommitdiffstats
path: root/ext
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2016-06-27 21:41:05 +0900
committerKazuki Yamaguchi <k@rhe.jp>2016-07-23 23:28:40 +0900
commit028e495734e9e6aa5dba1a2e130b08f66cf31a21 (patch)
tree263cfa336fc4efb66dd74e1b14594ba8ebbf91d7 /ext
parent6c387d4cf1e9cc1a304cb71260079ba9a8db022d (diff)
downloadruby-openssl-028e495734e9e6aa5dba1a2e130b08f66cf31a21.tar.gz
ssl: add verify_hostname option to SSLContexttopic/ssl-verify-hostname
If a client sets this to true and enables SNI with SSLSocket#hostname=, the hostname verification on the server certificate is performed automatically during the handshake using OpenSSL::SSL.verify_certificate_identity(). Currently an user who wants to do the hostname verification needs to call SSLSocket#post_connection_check explicitly after the TLS connection is established. This commit also enables the option in SSLContext::DEFAULT_PARAMS. Applications using SSLContext#set_params may be affected by this. [GH ruby/openssl#8]
Diffstat (limited to 'ext')
-rw-r--r--ext/openssl/ossl_ssl.c48
1 files changed, 46 insertions, 2 deletions
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index fd753254..0e46a442 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -64,6 +64,7 @@ static VALUE eSSLErrorWaitWritable;
#define ossl_sslctx_get_client_cert_cb(o) rb_iv_get((o),"@client_cert_cb")
#define ossl_sslctx_get_tmp_ecdh_cb(o) rb_iv_get((o),"@tmp_ecdh_callback")
#define ossl_sslctx_get_sess_id_ctx(o) rb_iv_get((o),"@session_id_context")
+#define ossl_sslctx_get_verify_hostname(o) rb_iv_get((o),"@verify_hostname")
#define ossl_ssl_get_io(o) rb_iv_get((o),"@io")
#define ossl_ssl_get_ctx(o) rb_iv_get((o),"@context")
@@ -319,14 +320,48 @@ ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength)
}
#endif
+static VALUE
+call_verify_certificate_identity(VALUE ctx_v)
+{
+ X509_STORE_CTX *ctx = (X509_STORE_CTX *)ctx_v;
+ SSL *ssl;
+ VALUE ssl_obj, hostname, cert_obj;
+
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ hostname = rb_attr_get(ssl_obj, rb_intern("@hostname"));
+
+ if (!RTEST(hostname)) {
+ rb_warning("verify_hostname requires hostname to be set");
+ return Qtrue;
+ }
+
+ cert_obj = ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx));
+ return rb_funcall(mSSL, rb_intern("verify_certificate_identity"), 2,
+ cert_obj, hostname);
+}
+
static int
ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
- VALUE cb;
+ VALUE cb, ssl_obj, verify_hostname, ret;
SSL *ssl;
+ int status;
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx);
+ ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ verify_hostname = ossl_sslctx_get_verify_hostname(ossl_ssl_get_ctx(ssl_obj));
+
+ if (preverify_ok && RTEST(verify_hostname) && !SSL_is_server(ssl) &&
+ !X509_STORE_CTX_get_error_depth(ctx)) {
+ ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status);
+ if (status) {
+ rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status));
+ return 0;
+ }
+ preverify_ok = ret == Qtrue;
+ }
return ossl_verify_cb_call(cb, preverify_ok, ctx);
}
@@ -2278,11 +2313,20 @@ Init_ossl_ssl(void)
* +store_context+ is an OpenSSL::X509::StoreContext containing the
* context used for certificate verification.
*
- * If the callback returns false verification is stopped.
+ * If the callback returns false, the chain verification is immediately
+ * stopped and a bad_certificate alert is then sent.
*/
rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse);
/*
+ * Whether to check the server certificate is valid for the hostname.
+ *
+ * In order to make this work, verify_mode must be set to VERIFY_PEER and
+ * the server hostname must be given by OpenSSL::SSL::SSLSocket#hostname=.
+ */
+ rb_attr(cSSLContext, rb_intern("verify_hostname"), 1, 1, Qfalse);
+
+ /*
* An OpenSSL::X509::Store used for certificate verification
*/
rb_attr(cSSLContext, rb_intern("cert_store"), 1, 1, Qfalse);