aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2017-01-20 23:12:10 +0900
committerKazuki Yamaguchi <k@rhe.jp>2017-01-24 13:28:48 +0900
commit654e024beec5a4f6deb05c8c7994544aabd1e825 (patch)
treedc5986694fa444c8eba205f71e6a798bdabac13d
parent54db4a4abd22ee2ee204f473143c4ad23d894bc4 (diff)
downloadruby-openssl-654e024beec5a4f6deb05c8c7994544aabd1e825.tar.gz
ssl: show reason of 'certificate verify error' in exception messagetopic/ssl-certificate-verify-error-desc
The 'certificate verify error' is one of the most common errors that can be raised by OpenSSL::SSL::SSLSocket#connect. The certificate verification may fail due to many different issues such as misconfigured trusted certificate store or inaccurate system clock. Unfortunately, since the detail is not put to the queue and is only accessible through OpenSSL::SSL::SSLSocket#verify_result, it is sometimes hard to figure out the real reason. Let's include a human readable reason message in the exception message. Like this: require "socket" require "openssl" ctx = OpenSSL::SSL::SSLContext.new ctx.set_params(cert_store: OpenSSL::X509::Store.new) ssl = OpenSSL::SSL::SSLSocket.new(Socket.tcp("www.ruby-lang.org", 443), ctx) ssl.connect #=> -:7:in `connect': SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate) (OpenSSL::SSL::SSLError) from -:7:in `<main>'
-rw-r--r--ext/openssl/ossl_ssl.c20
-rw-r--r--test/test_ssl.rb24
2 files changed, 44 insertions, 0 deletions
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index eef7dbec..fae4c66a 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -1525,6 +1525,9 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
int ret, ret2;
VALUE cb_state;
int nonblock = opts != Qfalse;
+#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
+ unsigned long err;
+#endif
rb_ivar_set(self, ID_callback_state, Qnil);
@@ -1558,6 +1561,23 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
case SSL_ERROR_SYSCALL:
if (errno) rb_sys_fail(funcname);
ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
+#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
+ case SSL_ERROR_SSL:
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
+ ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
+ const char *err_msg = ERR_reason_error_string(err),
+ *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl));
+ if (!err_msg)
+ err_msg = "(null)";
+ if (!verify_msg)
+ verify_msg = "(null)";
+ ossl_clear_error(); /* let ossl_raise() not append message */
+ ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s: %s (%s)",
+ funcname, ret2, errno, SSL_state_string_long(ssl),
+ err_msg, verify_msg);
+ }
+#endif
default:
ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
}
diff --git a/test/test_ssl.rb b/test/test_ssl.rb
index 8d74f25f..2b6d9291 100644
--- a/test/test_ssl.rb
+++ b/test/test_ssl.rb
@@ -749,6 +749,30 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
+ def test_connect_certificate_verify_failed_exception_message
+ start_server(ignore_listener_error: true) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /self signed/) {
+ server_connect(port, ctx)
+ }
+ }
+
+ ctx_proc = proc { |ctx|
+ ctx.cert = issue_cert(@svr, @svr_key, 30, [], @ca_cert, @ca_key,
+ not_before: Time.now-100, not_after: Time.now-10)
+ }
+ start_server(ignore_listener_error: true, ctx_proc: ctx_proc) { |server, port|
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params(cert_store: store)
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /expired/) {
+ server_connect(port, ctx)
+ }
+ }
+ end
+
def test_multibyte_read_write
#German a umlaut
auml = [%w{ C3 A4 }.join('')].pack('H*')