ext/puma_http11/org/jruby/puma/MiniSSL.java in piesync-puma-3.12.6.1 vs ext/puma_http11/org/jruby/puma/MiniSSL.java in piesync-puma-5.4.0.1

- old
+ new

@@ -20,19 +20,23 @@ import javax.net.ssl.SSLEngineResult; 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; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; 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; public class MiniSSL extends RubyObject { @@ -63,11 +67,11 @@ private MiniSSLBuffer(int capacity) { buffer = ByteBuffer.allocate(capacity); } private MiniSSLBuffer(byte[] initialContents) { buffer = ByteBuffer.wrap(initialContents); } public void clear() { buffer.clear(); } public void compact() { buffer.compact(); } - public void flip() { buffer.flip(); } + public void flip() { ((Buffer) buffer).flip(); } public boolean hasRemaining() { return buffer.hasRemaining(); } public int position() { return buffer.position(); } public ByteBuffer getRawBuffer() { return buffer; @@ -87,11 +91,11 @@ * Ensures that newCapacity bytes can be written to this buffer, only re-allocating if necessary */ public void resize(int newCapacity) { if (newCapacity > buffer.capacity()) { ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity); - buffer.flip(); + flip(); dstTmp.put(buffer); buffer = dstTmp; } else { buffer.limit(newCapacity); } @@ -99,11 +103,11 @@ /** * Drains the buffer to a ByteList, or returns null for an empty buffer */ public ByteList asByteList() { - buffer.flip(); + flip(); if (!buffer.hasRemaining()) { buffer.clear(); return null; } @@ -117,54 +121,93 @@ @Override public String toString() { return buffer.toString(); } } private SSLEngine engine; + private boolean closed; + private boolean handshake; private MiniSSLBuffer inboundNetData; private MiniSSLBuffer outboundAppData; private MiniSSLBuffer outboundNetData; public MiniSSL(Ruby runtime, RubyClass klass) { super(runtime, klass); } + private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>(); + private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>(); + @JRubyMethod(meta = true) - public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) { - RubyClass klass = (RubyClass) recv; + 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); } @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 = keyManagerFactoryMap.get(keystoreFile); + TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile); + if(kmf == null || tmf == null) { + throw new KeyStoreException("Could not find KeyManagerFactory/TrustManagerFactory for keystore: " + keystoreFile); + } - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, password); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); - SSLContext sslCtx = SSLContext.getInstance("TLS"); sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + closed = false; + handshake = false; engine = sslCtx.createSSLEngine(); - String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" }; + String[] protocols; + if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) { + protocols = new String[] { "TLSv1.1", "TLSv1.2" }; + } else { + protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" }; + } + + if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) { + protocols = new String[] { "TLSv1.2" }; + } + engine.setEnabledProtocols(protocols); engine.setUseClientMode(false); - long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue(); + long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger("to_i").getLongValue(); if ((verify_mode & 0x1) != 0) { // 'peer' engine.setWantClientAuth(true); } if ((verify_mode & 0x2) != 0) { // 'force_peer' engine.setNeedClientAuth(true); @@ -227,18 +270,25 @@ break; case BUFFER_UNDERFLOW: // need to wait for more data to come in before we retry retryOp = false; break; + case CLOSED: + closed = true; + retryOp = false; + break; default: - // other cases are OK and CLOSED. We're done here. + // other case is OK. We're done here. retryOp = false; } + if (res.getHandshakeStatus() == HandshakeStatus.FINISHED) { + handshake = true; + } } // after each op, run any delegated tasks if needed - if(engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + if(res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { Runnable runnable; while ((runnable = engine.getDelegatedTask()) != null) { runnable.run(); } } @@ -258,26 +308,29 @@ MiniSSLBuffer inboundAppData = new MiniSSLBuffer(engine.getSession().getApplicationBufferSize()); doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData); HandshakeStatus handshakeStatus = engine.getHandshakeStatus(); boolean done = false; + SSLEngineResult res = null; while (!done) { switch (handshakeStatus) { case NEED_WRAP: - doOp(SSLOperation.WRAP, inboundAppData, outboundNetData); + res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData); break; case NEED_UNWRAP: - SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData); + res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData); if (res.getStatus() == Status.BUFFER_UNDERFLOW) { // need more data before we can shake more hands done = true; } break; default: done = true; } - handshakeStatus = engine.getHandshakeStatus(); + if (!done) { + handshakeStatus = res.getHandshakeStatus(); + } } if (inboundNetData.hasRemaining()) { inboundNetData.compact(); } else { @@ -345,8 +398,25 @@ public IRubyObject peercert() throws CertificateEncodingException { try { return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded()); } catch (SSLPeerUnverifiedException ex) { return getRuntime().getNil(); + } + } + + @JRubyMethod(name = "init?") + public IRubyObject isInit(ThreadContext context) { + return handshake ? getRuntime().getFalse() : getRuntime().getTrue(); + } + + @JRubyMethod + public IRubyObject shutdown() { + if (closed || engine.isInboundDone() && engine.isOutboundDone()) { + if (engine.isOutboundDone()) { + engine.closeOutbound(); + } + return getRuntime().getTrue(); + } else { + return getRuntime().getFalse(); } } }