From f639b96597baec1b7c286c135fa5f05f6778f44c Mon Sep 17 00:00:00 2001 From: "John W. Phillips" Date: Wed, 1 Jul 2020 15:05:17 +0000 Subject: [PATCH 1/2] Move initialization of KeyManagerFactory, TrustManagerFactory to server initialization. This avoids reading the keystore file twice on every ssl request, and also fixes a filehandle leak from reading the keystore file without closing it properly. --- ext/puma_http11/org/jruby/puma/MiniSSL.java | 54 ++++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/ext/puma_http11/org/jruby/puma/MiniSSL.java b/ext/puma_http11/org/jruby/puma/MiniSSL.java index 33b6d91a..e32e602c 100644 --- a/ext/puma_http11/org/jruby/puma/MiniSSL.java +++ b/ext/puma_http11/org/jruby/puma/MiniSSL.java @@ -22,6 +22,7 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import java.io.FileInputStream; +import java.io.InputStream; import java.io.IOException; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -32,6 +33,8 @@ import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; import static javax.net.ssl.SSLEngineResult.Status; import static javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -128,10 +131,39 @@ public class MiniSSL extends RubyObject { super(runtime, klass); } - @JRubyMethod(meta = true) - public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) { - RubyClass klass = (RubyClass) recv; + private static Map keyManagerFactoryMap = new ConcurrentHashMap(); + private static Map trustManagerFactoryMap = new ConcurrentHashMap(); + @JRubyMethod(meta = true) + public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) + throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { + // Create the KeyManagerFactory and TrustManagerFactory for this server + String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString(); + char[] password = miniSSLContext.callMethod(context, "keystore_pass").convertToString().asJavaString().toCharArray(); + + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + InputStream is = new FileInputStream(keystoreFile); + try { + ks.load(is, password); + } finally { + is.close(); + } + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, password); + keyManagerFactoryMap.put(keystoreFile, kmf); + + KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType()); + is = new FileInputStream(keystoreFile); + try { + ts.load(is, password); + } finally { + is.close(); + } + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ts); + trustManagerFactoryMap.put(keystoreFile, tmf); + + RubyClass klass = (RubyClass) recv; return klass.newInstance(context, new IRubyObject[] { miniSSLContext }, Block.NULL_BLOCK); @@ -139,20 +171,16 @@ public class MiniSSL extends RubyObject { @JRubyMethod public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext) - throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException { + throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType()); - char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray(); String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString(); - ks.load(new FileInputStream(keystoreFile), password); - ts.load(new FileInputStream(keystoreFile), password); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, password); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); + KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile); + TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile); + if(kmf == null || tmf == null) { + throw new KeyStoreException("Could not find KeyManagerFactory/TrustManagerFactory for keystore: " + keystoreFile); + } SSLContext sslCtx = SSLContext.getInstance("TLS"); From 830433813be59a8683985f8d4c648fb58667676c Mon Sep 17 00:00:00 2001 From: "John W. Phillips" Date: Wed, 1 Jul 2020 16:41:37 +0000 Subject: [PATCH 2/2] Add changelog for bug fix. --- History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/History.md b/History.md index 1238bd8e..97e25250 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ ### Master * Bugfixes * Resolve issue with threadpool waiting counter decrement when thread is killed + * Fix filehandle leak in MiniSSL (#2299) ## 5.0.0