diff options
Diffstat (limited to 'x-net/src')
24 files changed, 1341 insertions, 1813 deletions
diff --git a/x-net/src/main/java/javax/net/package.html b/x-net/src/main/java/javax/net/package.html index 27ba790..5674d06 100644 --- a/x-net/src/main/java/javax/net/package.html +++ b/x-net/src/main/java/javax/net/package.html @@ -3,14 +3,5 @@ <p> This package provides factory classes to create sockets and server-sockets. This classes can be subclassed to create factories for other kinds of socket for example the SSL-capable sockets from the package javax.net.ssl. </p> - @since Android 1.0 - </body> -</html> -<html> - <body> - <p> - This package provides factory classes to create sockets and server-sockets. This classes can be subclassed to create factories for other kinds of socket for example the SSL-capable sockets from the package javax.net.ssl. - </p> - @since Android 1.0 </body> </html> diff --git a/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java b/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java index 6620841..3e58897 100644 --- a/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java +++ b/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java @@ -24,8 +24,6 @@ import java.net.SocketException; /** * Default inoperative implementation of javax.net.ssl.SSLServerSocketFactory - * - * @since Android 1.0 */ class DefaultSSLServerSocketFactory extends SSLServerSocketFactory { diff --git a/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java b/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java index d9db099..b75c218 100644 --- a/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java +++ b/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java @@ -23,6 +23,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.Security; // BEGIN android-added +import java.util.logging.Level; import java.util.logging.Logger; // END android-added @@ -48,7 +49,7 @@ public abstract class SSLSocketFactory extends SocketFactory { public static synchronized SocketFactory getDefault() { if (defaultSocketFactory != null) { // BEGIN android-added - log("SSLSocketFactory", "Using factory " + defaultSocketFactory); + // log("SSLSocketFactory", "Using factory " + defaultSocketFactory, null); // END android-added return defaultSocketFactory; } @@ -65,6 +66,9 @@ public abstract class SSLSocketFactory extends SocketFactory { final Class<?> sfc = Class.forName(defaultName, true, cl); defaultSocketFactory = (SocketFactory) sfc.newInstance(); } catch (Exception e) { + // BEGIN android-added + log("SSLSocketFactory", "Problem creating " + defaultName, e); + // END android-added } } return null; @@ -83,16 +87,16 @@ public abstract class SSLSocketFactory extends SocketFactory { // Use internal implementation defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed"); } - // BEGIN android-added - log("SSLSocketFactory", "Using factory " + defaultSocketFactory); - // END android-added + // BEGIN android-added + // log("SSLSocketFactory", "Using factory " + defaultSocketFactory, null); + // END android-added return defaultSocketFactory; } // BEGIN android-added @SuppressWarnings("unchecked") - private static void log(String tag, String msg) { - Logger.getLogger(tag).info(msg); + private static void log(String tag, String msg, Throwable throwable) { + Logger.getLogger(tag).log(Level.INFO, msg, throwable); } // END android-added diff --git a/x-net/src/main/java/javax/net/ssl/package.html b/x-net/src/main/java/javax/net/ssl/package.html index 3e6448e..14753c8 100644 --- a/x-net/src/main/java/javax/net/ssl/package.html +++ b/x-net/src/main/java/javax/net/ssl/package.html @@ -5,7 +5,7 @@ <html> <body> <p> -This package provides all the classes and interfaces needed to implemenet and program the Secure Socket +This package provides all the classes and interfaces needed to implement and program the Secure Socket abstraction based on the SSL protocol SSSLv3.0 or TLSv1.2. All the details of the SSL handshake protocol are accounted for, and a client or a server can specify the cipher set to use. @@ -13,14 +13,8 @@ set to use. X.509 certificates are verified, and, if desired, the client and the server each have the option of verifying the entire certificate chain until the root Certificate Authority is reached. -Notice that the Android javax.net.ssl package uses the OpenSSL Library to implement the low level -SSL functionality. All the relevant OpenSSl write(...) and read(...) functions are hidden within two -JNI files. The signatures of all the Java SSL methods are compliant with the Java 5.0 -specification. - -The provider for all SSL cryptological tools is The Legion of Bouncy Castle (http://www.bouncycastle.org). +Android uses code from The Legion of the Bouncy Castle (http://www.bouncycastle.org) and OpenSSL (http://openssl.org). </p> -@since Android 1.0 </body> -</html>
\ No newline at end of file +</html> diff --git a/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java b/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java deleted file mode 100644 index e9f2b0e..0000000 --- a/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL. - * All changes made to this file manually will be overwritten - * if this tool runs again. Better make changes in the template file. - */ - -// BEGIN android-note -// Redundant code has been removed and is now called from MsgHelp. -// END android-note - -package org.apache.harmony.xnet.internal.nls; - - -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -// BEGIN android-changed -import org.apache.harmony.luni.util.MsgHelp; -// END android-changed - -/** - * This class retrieves strings from a resource bundle and returns them, - * formatting them with MessageFormat when required. - * <p> - * It is used by the system classes to provide national language support, by - * looking up messages in the <code> - * org.apache.harmony.xnet.internal.nls.messages - * </code> - * resource bundle. Note that if this file is not available, or an invalid key - * is looked up, or resource bundle support is not available, the key itself - * will be returned as the associated message. This means that the <em>KEY</em> - * should a reasonable human-readable (english) string. - * - */ -public class Messages { - - // BEGIN android-changed - private static final String sResource = - "org.apache.harmony.xnet.internal.nls.messages"; //$NON-NLS-1$ - // END android-changed - - /** - * Retrieves a message which has no arguments. - * - * @param msg - * String the key to look up. - * @return String the message for that key in the system message bundle. - */ - static public String getString(String msg) { - // BEGIN android-changed - return MsgHelp.getString(sResource, msg); - // END android-changed - } - - /** - * Retrieves a message which takes 1 argument. - * - * @param msg - * String the key to look up. - * @param arg - * Object the object to insert in the formatted output. - * @return String the message for that key in the system message bundle. - */ - static public String getString(String msg, Object arg) { - return getString(msg, new Object[] { arg }); - } - - /** - * Retrieves a message which takes 1 integer argument. - * - * @param msg - * String the key to look up. - * @param arg - * int the integer to insert in the formatted output. - * @return String the message for that key in the system message bundle. - */ - static public String getString(String msg, int arg) { - return getString(msg, new Object[] { Integer.toString(arg) }); - } - - /** - * Retrieves a message which takes 1 character argument. - * - * @param msg - * String the key to look up. - * @param arg - * char the character to insert in the formatted output. - * @return String the message for that key in the system message bundle. - */ - static public String getString(String msg, char arg) { - return getString(msg, new Object[] { String.valueOf(arg) }); - } - - /** - * Retrieves a message which takes 2 arguments. - * - * @param msg - * String the key to look up. - * @param arg1 - * Object an object to insert in the formatted output. - * @param arg2 - * Object another object to insert in the formatted output. - * @return String the message for that key in the system message bundle. - */ - static public String getString(String msg, Object arg1, Object arg2) { - return getString(msg, new Object[] { arg1, arg2 }); - } - - /** - * Retrieves a message which takes several arguments. - * - * @param msg - * String the key to look up. - * @param args - * Object[] the objects to insert in the formatted output. - * @return String the message for that key in the system message bundle. - */ - static public String getString(String msg, Object[] args) { - // BEGIN android-changed - return MsgHelp.getString(sResource, msg, args); - // END android-changed - } - - // BEGIN android-note - // Duplicate code was dropped in favor of using MsgHelp. - // END android-note -} diff --git a/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties b/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties deleted file mode 100644 index 229ca61..0000000 --- a/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# messages for EN locale
\ No newline at end of file diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java index a95d38f..b780943 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java @@ -35,6 +35,7 @@ abstract class AbstractSessionContext implements SSLSessionContext { volatile int timeout; final SSLParameters parameters; + final int sslCtxNativePointer; /** Identifies OpenSSL sessions. */ static final int OPEN_SSL = 1; @@ -46,9 +47,10 @@ abstract class AbstractSessionContext implements SSLSessionContext { * @param maximumSize of cache * @param timeout for cache entries */ - AbstractSessionContext(SSLParameters parameters, int maximumSize, - int timeout) { + AbstractSessionContext(SSLParameters parameters, int sslCtxNativePointer, + int maximumSize, int timeout) { this.parameters = parameters; + this.sslCtxNativePointer = sslCtxNativePointer; this.maximumSize = maximumSize; this.timeout = timeout; } @@ -181,11 +183,20 @@ abstract class AbstractSessionContext implements SSLSessionContext { } } + /** + * Puts an SSLSession in the AbstractSessionContext cache + */ + abstract void putSession(SSLSession session); + static void log(Throwable t) { java.util.logging.Logger.global.log(Level.WARNING, "Error converting session.", t); } + protected void finalize() throws IOException { + NativeCrypto.SSL_CTX_free(sslCtxNativePointer); + } + /** * Byte array wrapper. Implements equals() and hashCode(). */ @@ -209,4 +220,4 @@ abstract class AbstractSessionContext implements SSLSessionContext { return Arrays.equals(bytes, other.bytes); } } -}
\ No newline at end of file +} diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java index 7b27787..7246c4d 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java @@ -55,7 +55,7 @@ public class CertificateRequest extends Message { */ X500Principal[] certificate_authorities; - //Requested certificate types as Strings + // Requested certificate types as Strings // ("RSA", "DSA", "DH_RSA" or "DH_DSA") private String[] types; diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java index 2c8738f..338fc39 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java @@ -64,8 +64,9 @@ public class ClientSessionContext extends AbstractSessionContext { final SSLClientSessionCache persistentCache; public ClientSessionContext(SSLParameters parameters, + int sslCtxNativePointer, SSLClientSessionCache persistentCache) { - super(parameters, 10, 0); + super(parameters, sslCtxNativePointer, 10, 0); this.persistentCache = persistentCache; } @@ -144,9 +145,9 @@ public class ClientSessionContext extends AbstractSessionContext { * Adds the given session to the ID-based index if the index has already * been initialized. */ - private void indexById(SSLSession session) { + private void indexById(byte[] id, SSLSession session) { if (sessionsById != null) { - sessionsById.put(new ByteArray(session.getId()), session); + sessionsById.put(new ByteArray(id), session); } } @@ -173,7 +174,7 @@ public class ClientSessionContext extends AbstractSessionContext { if (session != null) { synchronized (sessions) { sessions.put(new HostAndPort(host, port), session); - indexById(session); + indexById(session.getId(), session); } return session; } @@ -183,12 +184,17 @@ public class ClientSessionContext extends AbstractSessionContext { return null; } + @Override void putSession(SSLSession session) { + byte[] id = session.getId(); + if (id.length == 0) { + return; + } HostAndPort key = new HostAndPort(session.getPeerHost(), session.getPeerPort()); synchronized (sessions) { sessions.put(key, session); - indexById(session); + indexById(id, session); } // TODO: This in a background thread. diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java index 33b0a45..083a342 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java @@ -115,6 +115,8 @@ public final class JSSEProvider extends Provider { put("KeyManagerFactory.X509", KeyManagerFactoryImpl.class.getName()); put("TrustManagerFactory.X509", TrustManagerFactoryImpl.class.getName()); // BEGIN android-added + put("SSLContext.SSL", SSLContextImpl.class.getName()); + put("Alg.Alias.SSLContext.SSLv3", "SSL"); put("MessageDigest.SHA-1", "org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK$SHA1"); put("Alg.Alias.MessageDigest.SHA1", "SHA-1"); put("Alg.Alias.MessageDigest.SHA", "SHA-1"); diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java index 7698035..ad6ae15 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java @@ -16,6 +16,15 @@ package org.apache.harmony.xnet.provider.jsse; +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import org.bouncycastle.openssl.PEMWriter; + /** * Provides the Java side of our JNI glue for OpenSSL. Currently only hashing * and verifying are covered. Is expected to grow over time. Also needs to move @@ -23,31 +32,31 @@ package org.apache.harmony.xnet.provider.jsse; */ public class NativeCrypto { + // --- OpenSSL library initialization -------------------------------------- static { - // Need to ensure that OpenSSL initialization is done exactly once. - // This can be cleaned up later, when all OpenSSL glue moves into its - // own libcore module. Make it run, make it nice. - OpenSSLSocketImpl.class.getClass(); + clinit(); } + private native static void clinit(); + // --- DSA/RSA public/private key handling functions ----------------------- - + public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key); public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q); - + public static native void EVP_PKEY_free(int pkey); - + // --- General context handling functions (despite the names) -------------- - + public static native int EVP_new(); - + public static native void EVP_free(int ctx); - + // --- Digest handling functions ------------------------------------------- - + public static native void EVP_DigestInit(int ctx, String algorithm); - + public static native void EVP_DigestUpdate(int ctx, byte[] buffer, int offset, int length); public static native int EVP_DigestFinal(int ctx, byte[] hash, int offset); @@ -55,13 +64,186 @@ public class NativeCrypto { public static native int EVP_DigestSize(int ctx); public static native int EVP_DigestBlockSize(int ctx); - + // --- Signature handling functions ---------------------------------------- - + public static native void EVP_VerifyInit(int ctx, String algorithm); - + public static native void EVP_VerifyUpdate(int ctx, byte[] buffer, int offset, int length); - + public static native int EVP_VerifyFinal(int ctx, byte[] signature, int offset, int length, int key); - + + // --- SSL handling -------------------------------------------------------- + + private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3"; + private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1"; + + public static long SSL_OP_NO_SSLv3 = 0x02000000L; + public static long SSL_OP_NO_TLSv1 = 0x04000000L; + + public static native int SSL_CTX_new(); + + public static native String[] SSL_CTX_get_ciphers(int ssl_ctx); + + public static String[] getDefaultCipherSuites() { + int ssl_ctx = SSL_CTX_new(); + String[] supportedCiphers = SSL_CTX_get_ciphers(ssl_ctx); + SSL_CTX_free(ssl_ctx); + return supportedCiphers; + } + + public static String[] getSupportedCipherSuites() { + // TODO really return full cipher list + return getDefaultCipherSuites(); + } + + public static native void SSL_CTX_free(int ssl_ctx); + + public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException; + + /** + * Initialize the SSL socket and set the certificates for the + * future handshaking. + */ + public static int SSL_new(SSLParameters sslParameters) throws IOException { + boolean client = sslParameters.getUseClientMode(); + + final int ssl_ctx = (client) ? + sslParameters.getClientSessionContext().sslCtxNativePointer : + sslParameters.getServerSessionContext().sslCtxNativePointer; + + // TODO support more than RSA certificates? non-openssl + // SSLEngine implementation did these callbacks during + // handshake after selecting cipher suite, not before + // handshake. + final String alias = (client) ? + sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null) : + sslParameters.getKeyManager().chooseServerAlias("RSA", null, null); + + final String privateKeyString; + final String certificateString; + if (alias == null) { + privateKeyString = null; + certificateString = null; + } else { + PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); + X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); + + ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream(); + PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS)); + privateKeyPEMWriter.writeObject(privateKey); + privateKeyPEMWriter.close(); + privateKeyString = privateKeyOS.toString(); + + ByteArrayOutputStream certificateOS = new ByteArrayOutputStream(); + PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS)); + + for (X509Certificate certificate : certificates) { + certificateWriter.writeObject(certificate); + } + certificateWriter.close(); + certificateString = certificateOS.toString(); + } + + final byte[] seed = (sslParameters.getSecureRandomMember() != null) ? + sslParameters.getSecureRandomMember().generateSeed(1024) : + null; + + return SSL_new(ssl_ctx, + privateKeyString, + certificateString, + seed); + } + + + public static native long SSL_get_options(int ssl); + + public static native long SSL_set_options(int ssl, long options); + + public static String[] getSupportedProtocols() { + return new String[] { SUPPORTED_PROTOCOL_SSLV3, SUPPORTED_PROTOCOL_TLSV1 }; + } + + public static String[] getEnabledProtocols(int ssl) { + long options = SSL_get_options(ssl); + ArrayList<String> array = new ArrayList<String>(); + if ((options & NativeCrypto.SSL_OP_NO_SSLv3) == 0) { + array.add(SUPPORTED_PROTOCOL_SSLV3); + } + if ((options & NativeCrypto.SSL_OP_NO_TLSv1) == 0) { + array.add(SUPPORTED_PROTOCOL_TLSV1); + } + return array.toArray(new String[array.size()]); + } + + public static void setEnabledProtocols(int ssl, String[] protocols) { + if (protocols == null) { + throw new IllegalArgumentException("Provided parameter is null"); + } + // openssl uses negative logic letting you disable protocols. + // so first, lets turn them all off, and in the loop selectively enable + long options = SSL_get_options(ssl); + options |= (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + for (int i = 0; i < protocols.length; i++) { + if (protocols[i].equals(SUPPORTED_PROTOCOL_SSLV3)) { + options ^= SSL_OP_NO_SSLv3; + } else if (protocols[i].equals(SUPPORTED_PROTOCOL_TLSV1)) { + options ^= SSL_OP_NO_TLSv1; + } else { + throw new IllegalArgumentException("Protocol " + protocols[i] + + " is not supported"); + } + } + SSL_set_options(ssl, options); + } + + public static native String[] SSL_get_ciphers(int ssl); + + public static native void SSL_set_cipher_list(int ssl, String ciphers); + + public static void setEnabledCipherSuites(int ssl, String[] suites) { + if (suites == null) { + throw new IllegalArgumentException("Provided parameter is null"); + } + + // makes sure all suites are valid, throwing on error + String[] supportedCipherSuites = getSupportedCipherSuites(); + for (String suite : suites) { + findSuite(supportedCipherSuites, suite); + } + + String controlString = ""; + for (int i = 0; i < suites.length; i++) { + if (i == 0) { + controlString = suites[i]; + } else { + controlString += ":" + suites[i]; + } + } + SSL_set_cipher_list(ssl, controlString); + } + + private static void findSuite(String[] supportedCipherSuites, String suite) { + for(int i = 0; i < supportedCipherSuites.length; i++) { + if (supportedCipherSuites[i].equals(suite)) { + return; + } + } + throw new IllegalArgumentException("Protocol " + suite + " is not supported."); + } + + public static native void SSL_free(int ssl); + + public interface CertificateChainVerifier { + /** + * Verify that we trust the certificate chain is trusted. + * + * @param bytes An array of certficates in byte form + * + * @throws AlertException if the certificate is untrusted + * @return false if there are other problems verifying the certificate chain + */ + // TODO throw on error in all cases instead of returning false + public boolean verifyCertificateChain(byte[][] bytes); + } } diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java index 970f5dc..f342457 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java @@ -39,16 +39,16 @@ public class OpenSSLServerSocketFactoryImpl extends javax.net.ssl.SSLServerSocke } public OpenSSLServerSocketFactoryImpl(SSLParameters sslParameters) { - this.sslParameters = sslParameters; + this.sslParameters = (SSLParameters) sslParameters.clone(); + this.sslParameters.setUseClientMode(false); } public String[] getDefaultCipherSuites() { - // TODO There might be a better way to implement this... - return OpenSSLServerSocketImpl.nativegetsupportedciphersuites(); + return NativeCrypto.getDefaultCipherSuites(); } public String[] getSupportedCipherSuites() { - return OpenSSLServerSocketImpl.nativegetsupportedciphersuites(); + return NativeCrypto.getSupportedCipherSuites(); } public ServerSocket createServerSocket() throws IOException { diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java index d52f08f..c79dccf 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java @@ -16,98 +16,46 @@ package org.apache.harmony.xnet.provider.jsse; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.Socket; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; - -import org.bouncycastle.openssl.PEMWriter; /** * OpenSSL-based implementation of server sockets. - * + * * This class only supports SSLv3 and TLSv1. This should be documented elsewhere * later, for example in the package.html or a separate reference document. - */ + */ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { - private int ssl_ctx; - private boolean client_mode = true; - private long ssl_op_no = 0x00000000L; - private SSLParameters sslParameters; - private static final String[] supportedProtocols = new String[] { - "SSLv3", - "TLSv1" - }; - - private native static void nativeinitstatic(); - - static { - nativeinitstatic(); - } - - private native void nativeinit(String privatekey, String certificate, byte[] seed); - - /** - * Initialize the SSL server socket and set the certificates for the - * future handshaking. - */ - private void init() throws IOException { - String alias = sslParameters.getKeyManager().chooseServerAlias("RSA", null, null); - if (alias == null) { - throw new IOException("No suitable certificates found"); - } - - PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); - X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); - - ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream(); - PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS)); - privateKeyPEMWriter.writeObject(privateKey); - privateKeyPEMWriter.close(); - - ByteArrayOutputStream certificateOS = new ByteArrayOutputStream(); - PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS)); - - for (int i = 0; i < certificates.length; i++) { - certificateWriter.writeObject(certificates[i]); - } - certificateWriter.close(); - - nativeinit(privateKeyOS.toString(), certificateOS.toString(), - sslParameters.getSecureRandomMember() != null ? - sslParameters.getSecureRandomMember().generateSeed(1024) : null); - } + private final SSLParameters sslParameters; + private int sslNativePointer; protected OpenSSLServerSocketImpl(SSLParameters sslParameters) throws IOException { super(); this.sslParameters = sslParameters; - init(); + this.sslNativePointer = NativeCrypto.SSL_new(sslParameters); } protected OpenSSLServerSocketImpl(int port, SSLParameters sslParameters) throws IOException { super(port); this.sslParameters = sslParameters; - init(); + this.sslNativePointer = NativeCrypto.SSL_new(sslParameters); } protected OpenSSLServerSocketImpl(int port, int backlog, SSLParameters sslParameters) throws IOException { super(port, backlog); this.sslParameters = sslParameters; - init(); + this.sslNativePointer = NativeCrypto.SSL_new(sslParameters); } protected OpenSSLServerSocketImpl(int port, int backlog, InetAddress iAddress, SSLParameters sslParameters) throws IOException { super(port, backlog, iAddress); this.sslParameters = sslParameters; - init(); + this.sslNativePointer = NativeCrypto.SSL_new(sslParameters); } @Override @@ -127,78 +75,41 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { */ @Override public String[] getSupportedProtocols() { - return supportedProtocols.clone(); + return NativeCrypto.getSupportedProtocols(); } /** - * See the OpenSSL ssl.h header file for more information. - */ - static private long SSL_OP_NO_SSLv3 = 0x02000000L; - static private long SSL_OP_NO_TLSv1 = 0x04000000L; - - /** * The names of the protocols' versions that in use on this SSL connection. - * + * * @return an array of protocols names */ @Override public String[] getEnabledProtocols() { - ArrayList<String> array = new ArrayList<String>(); - - if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) { - array.add(supportedProtocols[0]); - } - if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) { - array.add(supportedProtocols[1]); - } - return array.toArray(new String[array.size()]); + return NativeCrypto.getEnabledProtocols(sslNativePointer); } - private native void nativesetenabledprotocols(long l); - /** * This method enables the protocols' versions listed by * getSupportedProtocols(). - * + * * @param protocols names of all the protocols to enable. - * + * * @throws IllegalArgumentException when one or more of the names in the * array are not supported, or when the array is null. */ @Override public void setEnabledProtocols(String[] protocols) { - if (protocols == null) { - throw new IllegalArgumentException("Provided parameter is null"); - } - - ssl_op_no = SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; - - for (int i = 0; i < protocols.length; i++) { - if (protocols[i].equals("SSLv3")) - ssl_op_no ^= SSL_OP_NO_SSLv3; - else if (protocols[i].equals("TLSv1")) - ssl_op_no ^= SSL_OP_NO_TLSv1; - else throw new IllegalArgumentException("Protocol " + protocols[i] + - " is not supported."); - } - - nativesetenabledprotocols(ssl_op_no); + NativeCrypto.setEnabledProtocols(sslNativePointer, protocols); } - /** - * Gets all available ciphers from the current OpenSSL library. - * Needed by OpenSSLServerSocketFactory too. - */ - static native String[] nativegetsupportedciphersuites(); - @Override public String[] getSupportedCipherSuites() { - return nativegetsupportedciphersuites(); + return NativeCrypto.getSupportedCipherSuites(); } @Override public String[] getEnabledCipherSuites() { - return OpenSSLSocketImpl.nativeGetEnabledCipherSuites(ssl_ctx); + return NativeCrypto.SSL_get_ciphers(sslNativePointer); } /** @@ -211,7 +122,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { */ @Override public void setEnabledCipherSuites(String[] suites) { - OpenSSLSocketImpl.setEnabledCipherSuites(ssl_ctx, suites); + NativeCrypto.setEnabledCipherSuites(sslNativePointer, suites); } /** @@ -223,10 +134,10 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { static private int SSL_VERIFY_CLIENT_ONCE = 0x04; /** - * Calls the SSL_CTX_set_verify(...) OpenSSL function with the passed int + * Calls the SSL_set_verify(...) OpenSSL function with the passed int * value. */ - private native void nativesetclientauth(int value); + private static native void nativesetclientauth(int sslNativePointer, int value); private void setClientAuth() { int value = SSL_VERIFY_NONE; @@ -237,7 +148,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { value |= SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE; } - nativesetclientauth(value); + nativesetclientauth(sslNativePointer, value); } @Override @@ -274,20 +185,13 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { @Override public Socket accept() throws IOException { - OpenSSLSocketImpl socket - = new OpenSSLSocketImpl(sslParameters, ssl_op_no); + OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, null); implAccept(socket); - socket.accept(ssl_ctx, client_mode); - + socket.accept(sslNativePointer); return socket; } /** - * Removes OpenSSL objects from memory. - */ - private native void nativefree(); - - /** * Unbinds the port if the socket is open. */ @Override @@ -297,7 +201,10 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { @Override public synchronized void close() throws IOException { - nativefree(); + if (sslNativePointer != 0) { + NativeCrypto.SSL_free(sslNativePointer); + sslNativePointer = 0; + } super.close(); } } diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java index 17cd088..2a3908c 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java @@ -41,7 +41,7 @@ import org.apache.harmony.security.provider.cert.X509CertImpl; * Implementation of the class OpenSSLSessionImpl * based on OpenSSL. The JNI native interface for some methods * of this this class are defined in the file: - * org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp + * org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp */ public class OpenSSLSessionImpl implements SSLSession { @@ -52,11 +52,11 @@ public class OpenSSLSessionImpl implements SSLSession { private boolean isValid = true; private TwoKeyHashMap values = new TwoKeyHashMap(); private javax.security.cert.X509Certificate[] peerCertificateChain; - protected int session; + protected int sslSessionNativePointer; private SSLParameters sslParameters; private String peerHost; private int peerPort; - private final SSLSessionContext sessionContext; + private final AbstractSessionContext sessionContext; /** * Class constructor creates an SSL session context given the appropriate @@ -65,9 +65,9 @@ public class OpenSSLSessionImpl implements SSLSession { * @param session the Identifier for SSL session * @param sslParameters the SSL parameters like ciphers' suites etc. */ - protected OpenSSLSessionImpl(int session, SSLParameters sslParameters, - String peerHost, int peerPort, SSLSessionContext sessionContext) { - this.session = session; + protected OpenSSLSessionImpl(int sslSessionNativePointer, SSLParameters sslParameters, + String peerHost, int peerPort, AbstractSessionContext sessionContext) { + this.sslSessionNativePointer = sslSessionNativePointer; this.sslParameters = sslParameters; this.peerHost = peerHost; this.peerPort = peerPort; @@ -75,51 +75,58 @@ public class OpenSSLSessionImpl implements SSLSession { } /** - * Constructs a session from a byte[]. + * Constructs a session from a byte[] containing DER data. This + * allows loading the saved session. + * @throws IOException */ OpenSSLSessionImpl(byte[] derData, SSLParameters sslParameters, String peerHost, int peerPort, javax.security.cert.X509Certificate[] peerCertificateChain, - SSLSessionContext sessionContext) + AbstractSessionContext sessionContext) throws IOException { - this.sslParameters = sslParameters; - this.peerHost = peerHost; - this.peerPort = peerPort; + this(initializeNativeImpl(derData, derData.length), + sslParameters, + peerHost, + peerPort, + sessionContext); this.peerCertificateChain = peerCertificateChain; - this.sessionContext = sessionContext; - initializeNative(derData); + // TODO move this check into native code so we can throw an error with more information + if (this.sslSessionNativePointer == 0) { + throw new IOException("Invalid session data"); + } } + private static native int initializeNativeImpl(byte[] data, int size); + /** * Gets the identifier of the actual SSL session * @return array of sessions' identifiers. */ - public native byte[] getId(); + public byte[] getId() { + return getId(sslSessionNativePointer); + } + + private static native byte[] getId(int sslSessionNativePointer); /** * Get the session object in DER format. This allows saving the session * data or sharing it with other processes. */ - native byte[] getEncoded(); - - /** - * Init the underlying native object from DER data. This - * allows loading the saved session. - * @throws IOException - */ - private void initializeNative(byte[] derData) throws IOException { - this.session = initializeNativeImpl(derData, derData.length); - if (this.session == 0) { - throw new IOException("Invalid session data"); - } + byte[] getEncoded() { + return getEncoded(sslSessionNativePointer); } - private native int initializeNativeImpl(byte[] data, int size); - + + private native static byte[] getEncoded(int sslSessionNativePointer); + /** * Gets the creation time of the SSL session. * @return the session's creation time in milliseconds since the epoch */ - public native long getCreationTime(); + public long getCreationTime() { + return getCreationTime(sslSessionNativePointer); + } + + private static native long getCreationTime(int sslSessionNativePointer); /** * Gives the last time this concrete SSL session was accessed. Accessing @@ -184,7 +191,8 @@ public class OpenSSLSessionImpl implements SSLSession { /** * Returns the X509 certificates of the peer in the PEM format. */ - private native byte[][] getPeerCertificatesImpl(); + private static native byte[][] getPeerCertificatesImpl(int sslCtxNativePointer, + int sslSessionNativePointer); /** * Gives the certificate(s) of the peer in this SSL session @@ -201,7 +209,7 @@ public class OpenSSLSessionImpl implements SSLSession { public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { if (peerCertificateChain == null) { try { - byte[][] bytes = getPeerCertificatesImpl(); + byte[][] bytes = getPeerCertificatesImpl(sessionContext.sslCtxNativePointer, sslSessionNativePointer); if (bytes == null) throw new SSLPeerUnverifiedException("No certificate available"); peerCertificateChain = new javax.security.cert.X509Certificate[bytes.length]; @@ -303,7 +311,11 @@ public class OpenSSLSessionImpl implements SSLSession { * @return an identifier for all the cryptographic algorithms used in the * actual SSL session. */ - public native String getCipherSuite(); + public String getCipherSuite() { + return getCipherSuite(sslSessionNativePointer); + } + + private static native String getCipherSuite(int sslSessionNativePointer); /** * Gives back the standard version name of the SSL protocol used in all @@ -313,7 +325,11 @@ public class OpenSSLSessionImpl implements SSLSession { * connections pertaining to this SSL session. * */ - public native String getProtocol(); + public String getProtocol() { + return getProtocol(sslSessionNativePointer); + } + + private static native String getProtocol(int sslSessionNativePointer); /** * Gives back the context to which the actual SSL session is bound. A SSL @@ -457,10 +473,8 @@ public class OpenSSLSessionImpl implements SSLSession { } protected void finalize() { - synchronized (OpenSSLSocketImpl.class) { - freeImpl(session); - } + freeImpl(sslSessionNativePointer); } - private native void freeImpl(int session); + private static native void freeImpl(int session); } diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java index aeb23b6..7b6d7c8 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java @@ -46,12 +46,11 @@ public class OpenSSLSocketFactoryImpl extends javax.net.ssl.SSLSocketFactory { } public String[] getDefaultCipherSuites() { - // TODO There might be a better implementation for this... - return OpenSSLSocketImpl.nativegetsupportedciphersuites(); + return NativeCrypto.getDefaultCipherSuites(); } public String[] getSupportedCipherSuites() { - return OpenSSLSocketImpl.nativegetsupportedciphersuites(); + return NativeCrypto.getSupportedCipherSuites(); } public Socket createSocket() throws IOException { diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java index 8006757..58d2110 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java @@ -16,7 +16,6 @@ package org.apache.harmony.xnet.provider.jsse; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,7 +24,6 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; -import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; @@ -41,43 +39,37 @@ import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import org.apache.harmony.security.provider.cert.X509CertImpl; -import org.bouncycastle.openssl.PEMWriter; /** * Implementation of the class OpenSSLSocketImpl * based on OpenSSL. The JNI native interface for some methods * of this this class are defined in the file: - * org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp - * + * org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp + * * This class only supports SSLv3 and TLSv1. This should be documented elsewhere - * later, for example in the package.html or a separate reference document. + * later, for example in the package.html or a separate reference document. */ -public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { - private int ssl_ctx; - private int ssl; +public class OpenSSLSocketImpl + extends javax.net.ssl.SSLSocket + implements NativeCrypto.CertificateChainVerifier { + private int sslNativePointer; private InputStream is; private OutputStream os; private final Object handshakeLock = new Object(); - private Object readLock = new Object(); - private Object writeLock = new Object(); + private final Object readLock = new Object(); + private final Object writeLock = new Object(); private SSLParameters sslParameters; private OpenSSLSessionImpl sslSession; private Socket socket; private boolean autoClose; private boolean handshakeStarted = false; private ArrayList<HandshakeCompletedListener> listeners; - private long ssl_op_no = 0x00000000L; private int timeout = 0; // BEGIN android-added private int handshakeTimeout = -1; // -1 = same as timeout; 0 = infinite // END android-added private InetSocketAddress address; - private static final String[] supportedProtocols = new String[] { - "SSLv3", - "TLSv1" - }; - private static final AtomicInteger instanceCount = new AtomicInteger(0); public static int getInstanceCount() { @@ -89,66 +81,6 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } /** - * Initialize OpenSSL library. - */ - private native static void nativeinitstatic(); - - static { - nativeinitstatic(); - } - - private native void nativeinit(String privatekey, String certificate, byte[] seed); - - /** - * Initialize the SSL socket and set the certificates for the - * future handshaking. - */ - private void init() throws IOException { - String alias = sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null); - if (alias != null) { - PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); - X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); - - ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream(); - PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS)); - privateKeyPEMWriter.writeObject(privateKey); - privateKeyPEMWriter.close(); - - ByteArrayOutputStream certificateOS = new ByteArrayOutputStream(); - PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS)); - - for (int i = 0; i < certificates.length; i++) { - certificateWriter.writeObject(certificates[i]); - } - certificateWriter.close(); - - nativeinit(privateKeyOS.toString(), certificateOS.toString(), - sslParameters.getSecureRandomMember() != null ? - sslParameters.getSecureRandomMember().generateSeed(1024) : null); - } else { - nativeinit(null, null, - sslParameters.getSecureRandomMember() != null ? - sslParameters.getSecureRandomMember().generateSeed(1024) : null); - } - } - - /** - * Class constructor with 2 parameters - * - * @param sslParameters Parameters for the SSL - * context - * @param ssl_op_no Parameter to set the enabled - * protocols - * @throws IOException if network fails - */ - protected OpenSSLSocketImpl(SSLParameters sslParameters, long ssl_op_no) throws IOException { - super(); - this.sslParameters = sslParameters; - this.ssl_op_no = ssl_op_no; - updateInstanceCount(1); - } - - /** * Class constructor with 1 parameter * * @param sslParameters Parameters for the SSL @@ -157,9 +89,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { */ protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException { super(); - this.sslParameters = sslParameters; - init(); - updateInstanceCount(1); + init(sslParameters); } /** @@ -172,12 +102,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { SSLParameters sslParameters) throws IOException { super(host, port); - this.sslParameters = sslParameters; - init(); - updateInstanceCount(1); + init(sslParameters); } - /** * Class constructor with 3 parameters: 1st is InetAddress * @@ -188,9 +115,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { SSLParameters sslParameters) throws IOException { super(address, port); - this.sslParameters = sslParameters; - init(); - updateInstanceCount(1); + init(sslParameters); } @@ -204,9 +129,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { int clientPort, SSLParameters sslParameters) throws IOException { super(host, port, clientAddress, clientPort); - this.sslParameters = sslParameters; - init(); - updateInstanceCount(1); + init(sslParameters); } /** @@ -219,9 +142,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { InetAddress clientAddress, int clientPort, SSLParameters sslParameters) throws IOException { super(address, port, clientAddress, clientPort); - this.sslParameters = sslParameters; - init(); - updateInstanceCount(1); + init(sslParameters); } /** @@ -237,8 +158,28 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { this.timeout = socket.getSoTimeout(); this.address = new InetSocketAddress(host, port); this.autoClose = autoClose; + init(sslParameters); + } + + /** + * Initialize the SSL socket and set the certificates for the + * future handshaking. + */ + private void init(SSLParameters sslParameters) throws IOException { this.sslParameters = sslParameters; - init(); + this.sslNativePointer = NativeCrypto.SSL_new(sslParameters); + updateInstanceCount(1); + } + + /** + * Construct a OpenSSLSocketImpl from an SSLParameters and an + * existing SSL native pointer. Used for transitioning accepting + * the OpenSSLSocketImpl within OpenSSLServerSocketImpl. + */ + protected OpenSSLSocketImpl(SSLParameters sslParameters, + OpenSSLServerSocketImpl dummy) { + super(); + this.sslParameters = (SSLParameters) sslParameters.clone(); updateInstanceCount(1); } @@ -246,9 +187,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * Adds OpenSSL functionality to the existing socket and starts the SSL * handshaking. */ - private native boolean nativeconnect(int ctx, Socket sock, boolean client_mode, int sslsession) throws IOException; - private native int nativegetsslsession(int ssl); - private native String nativecipherauthenticationmethod(); + private native boolean nativeconnect(int sslNativePointer, Socket sock, int timeout, boolean client_mode, int sslSessionNativePointer) throws IOException; + private native int nativegetsslsession(int sslNativePointer); + private native String nativecipherauthenticationmethod(int sslNativePointer); /** * Gets the suitable session reference from the session cache container. @@ -256,6 +197,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * @return OpenSSLSessionImpl */ private OpenSSLSessionImpl getCachedClientSession() { + if (!sslParameters.getUseClientMode()) { + return null; + } if (super.getInetAddress() == null || super.getInetAddress().getHostAddress() == null || super.getInetAddress().getHostName() == null) { @@ -273,8 +217,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * before logging is ready. */ static class LoggerHolder { - static final Logger logger = Logger.getLogger( - OpenSSLSocketImpl.class.getName()); + static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName()); } /** @@ -282,7 +225,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * from the OpenSSL library. It can negotiate new encryption keys, change * cipher suites, or initiate a new session. The certificate chain is * verified if the correspondent property in java.Security is set. All - * listensers are notified at the end of the TLS/SSL handshake. + * listeners are notified at the end of the TLS/SSL handshake. * * @throws <code>IOException</code> if network fails */ @@ -300,86 +243,77 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { // Check if it's allowed to create a new session (default is true) if (session == null && !sslParameters.getEnableSessionCreation()) { throw new SSLHandshakeException("SSL Session may not be created"); + } + + // BEGIN android-added + // Temporarily use a different timeout for the handshake process + int savedTimeout = timeout; + if (handshakeTimeout >= 0) { + setSoTimeout(handshakeTimeout); + } + // END android-added + + Socket socket = this.socket != null ? this.socket : this; + int sslSessionNativePointer = session != null ? session.sslSessionNativePointer : 0; + boolean reusedSession = nativeconnect(sslNativePointer, socket, timeout, + sslParameters.getUseClientMode(), sslSessionNativePointer); + if (reusedSession) { + // nativeconnect shouldn't return true if the session is not + // done + session.lastAccessedTime = System.currentTimeMillis(); + sslSession = session; + + LoggerHolder.logger.fine("Reused cached session for " + + getInetAddress().getHostName() + "."); } else { - // BEGIN android-added - // Temporarily use a different timeout for the handshake process - int savedTimeout = timeout; - if (handshakeTimeout >= 0) { - setSoTimeout(handshakeTimeout); - } - // END android-added - - Socket socket = this.socket != null ? this.socket : this; - int sessionId = session != null ? session.session : 0; - boolean reusedSession; - synchronized (OpenSSLSocketImpl.class) { - reusedSession = nativeconnect(ssl_ctx, socket, - sslParameters.getUseClientMode(), sessionId); - } - if (reusedSession) { - // nativeconnect shouldn't return true if the session is not - // done - session.lastAccessedTime = System.currentTimeMillis(); - sslSession = session; - - LoggerHolder.logger.fine("Reused cached session for " - + getInetAddress().getHostName() + "."); + if (session != null) { + LoggerHolder.logger.fine("Reuse of cached session for " + + getInetAddress().getHostName() + " failed."); } else { - if (session != null) { - LoggerHolder.logger.fine("Reuse of cached session for " - + getInetAddress().getHostName() + " failed."); - } else { - LoggerHolder.logger.fine("Created new session for " - + getInetAddress().getHostName() + "."); - } - - ClientSessionContext sessionContext - = sslParameters.getClientSessionContext(); - synchronized (OpenSSLSocketImpl.class) { - sessionId = nativegetsslsession(ssl); - } - if (address == null) { - sslSession = new OpenSSLSessionImpl( - sessionId, sslParameters, - super.getInetAddress().getHostName(), - super.getPort(), sessionContext); - } else { - sslSession = new OpenSSLSessionImpl( - sessionId, sslParameters, - address.getHostName(), address.getPort(), - sessionContext); - } + LoggerHolder.logger.fine("Created new session for " + + getInetAddress().getHostName() + "."); + } - try { - X509Certificate[] peerCertificates = (X509Certificate[]) - sslSession.getPeerCertificates(); + AbstractSessionContext sessionContext = + (sslParameters.getUseClientMode()) ? + sslParameters.getClientSessionContext() : + sslParameters.getServerSessionContext(); + sslSessionNativePointer = nativegetsslsession(sslNativePointer); + if (address == null) { + sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, sslParameters, + super.getInetAddress().getHostName(), + super.getPort(), sessionContext); + } else { + sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, sslParameters, + address.getHostName(), address.getPort(), + sessionContext); + } - if (peerCertificates == null - || peerCertificates.length == 0) { - throw new SSLException("Server sends no certificate"); - } + try { + X509Certificate[] peerCertificates = (X509Certificate[]) + sslSession.getPeerCertificates(); - String authMethod; - synchronized (OpenSSLSocketImpl.class) { - authMethod = nativecipherauthenticationmethod(); - } - sslParameters.getTrustManager().checkServerTrusted( - peerCertificates, - authMethod); - sessionContext.putSession(sslSession); - } catch (CertificateException e) { - throw new SSLException("Not trusted server certificate", e); + if (peerCertificates == null + || peerCertificates.length == 0) { + throw new SSLException("Server sends no certificate"); } - } - // BEGIN android-added - // Restore the original timeout now that the handshake is complete - if (handshakeTimeout >= 0) { - setSoTimeout(savedTimeout); + String authMethod = nativecipherauthenticationmethod(sslNativePointer); + sslParameters.getTrustManager().checkServerTrusted(peerCertificates, + authMethod); + sessionContext.putSession(sslSession); + } catch (CertificateException e) { + throw new SSLException("Not trusted server certificate", e); } - // END android-added } + // BEGIN android-added + // Restore the original timeout now that the handshake is complete + if (handshakeTimeout >= 0) { + setSoTimeout(savedTimeout); + } + // END android-added + if (listeners != null) { // notify the listeners HandshakeCompletedEvent event = @@ -392,22 +326,22 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } // To be synchronized because of the verify_callback - native synchronized void nativeaccept(Socket socketObject, int m_ctx, boolean client_mode); + native synchronized int nativeaccept(int sslNativePointer, Socket socketObject); /** * Performs the first part of a SSL/TLS handshaking process with a given * 'host' connection and initializes the SSLSession. */ - protected void accept(int m_ctx, boolean client_mode) throws IOException { + protected void accept(int serverSslNativePointer) throws IOException { // Must be set because no handshaking is necessary // in this situation handshakeStarted = true; - nativeaccept(this, m_ctx, client_mode); + sslNativePointer = nativeaccept(serverSslNativePointer, this); ServerSessionContext sessionContext = sslParameters.getServerSessionContext(); - sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl), + sslSession = new OpenSSLSessionImpl(nativegetsslsession(sslNativePointer), sslParameters, super.getInetAddress().getHostName(), super.getPort(), sessionContext); sslSession.lastAccessedTime = System.currentTimeMillis(); @@ -415,14 +349,16 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } /** - * Callback methode for the OpenSSL native certificate verification process. + * Implementation of NativeCrypto.CertificateChainVerifier. + * + * Callback method for the OpenSSL native certificate verification process. * * @param bytes Byte array containing the cert's * information. - * @return 0 if the certificate verification fails or 1 if OK + * @return false if the certificate verification fails or true if OK */ @SuppressWarnings("unused") - private int verify_callback(byte[][] bytes) { + public boolean verifyCertificateChain(byte[][] bytes) { try { X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length]; @@ -439,11 +375,13 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { new SSLException("Not trusted server certificate", e)); } } catch (javax.security.cert.CertificateException e) { - return 0; + // TODO throw in all cases for consistency + return false; } catch (IOException e) { - return 0; + // TODO throw in all cases for consistency + return false; } - return 1; + return true; } /** @@ -483,28 +421,28 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } } - /** - * This method is not supported for this SSLSocket implementation. - */ public void shutdownInput() throws IOException { - throw new UnsupportedOperationException( - "Method shutdownInput() is not supported."); + if (socket == null) { + super.shutdownInput(); + return; + } + socket.shutdownInput(); } - /** - * This method is not supported for this SSLSocket implementation. - */ public void shutdownOutput() throws IOException { - throw new UnsupportedOperationException( - "Method shutdownOutput() is not supported."); + if (socket == null) { + super.shutdownOutput(); + return; + } + socket.shutdownOutput(); } /** * Reads with the native SSL_read function from the encrypted data stream * @return -1 if error or the end of the stream is reached. */ - private native int nativeread(int timeout) throws IOException; - private native int nativeread(byte[] b, int off, int len, int timeout) throws IOException; + private native int nativeread(int sslNativePointer, int timeout) throws IOException; + private native int nativeread(int sslNativePointer, byte[] b, int off, int len, int timeout) throws IOException; /** * This inner class provides input data stream functionality @@ -529,7 +467,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { */ public int read() throws IOException { synchronized(readLock) { - return OpenSSLSocketImpl.this.nativeread(timeout); + return OpenSSLSocketImpl.this.nativeread(sslNativePointer, timeout); } } @@ -539,7 +477,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { */ public int read(byte[] b, int off, int len) throws IOException { synchronized(readLock) { - return OpenSSLSocketImpl.this.nativeread(b, off, len, timeout); + return OpenSSLSocketImpl.this.nativeread(sslNativePointer, b, off, len, timeout); } } } @@ -547,8 +485,8 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Writes with the native SSL_write function to the encrypted data stream. */ - private native void nativewrite(int b) throws IOException; - private native void nativewrite(byte[] b, int off, int len) throws IOException; + private native void nativewrite(int sslNativePointer, int b) throws IOException; + private native void nativewrite(int sslNativePointer, byte[] b, int off, int len) throws IOException; /** * This inner class provides output data stream functionality @@ -559,7 +497,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { SSLOutputStream() throws IOException { /** /* Note: When startHandshake() throws an exception, no - * SSLInputStream object will be created. + * SSLOutputStream object will be created. */ OpenSSLSocketImpl.this.startHandshake(); } @@ -570,7 +508,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { */ public void write(int b) throws IOException { synchronized(writeLock) { - OpenSSLSocketImpl.this.nativewrite(b); + OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b); } } @@ -580,7 +518,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { */ public void write(byte[] b, int start, int len) throws IOException { synchronized(writeLock) { - OpenSSLSocketImpl.this.nativewrite(b, start, len); + OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b, start, len); } } } @@ -598,9 +536,6 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { try { startHandshake(); } catch (IOException e) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, - "Error negotiating SSL connection.", e); - // return an invalid session with // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" return SSLSessionImpl.NULL_SESSION; @@ -666,22 +601,14 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } /** - * Gets all available ciphers from the current OpenSSL library. - * Needed by OpenSSLSocketFactory too. - */ - static native String[] nativegetsupportedciphersuites(); - - /** * The names of the cipher suites which could be used by the SSL connection * are returned. * @return an array of cipher suite names */ public String[] getSupportedCipherSuites() { - return nativegetsupportedciphersuites(); + return NativeCrypto.getSupportedCipherSuites(); } - static native String[] nativeGetEnabledCipherSuites(int ssl_ctx); - /** * The names of the cipher suites that are in use in the actual the SSL * connection are returned. @@ -689,23 +616,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * @return an array of cipher suite names */ public String[] getEnabledCipherSuites() { - return nativeGetEnabledCipherSuites(ssl_ctx); - } - - /** - * Calls the SSL_CTX_set_cipher_list(...) OpenSSL function with the passed - * char array. - */ - static native void nativeSetEnabledCipherSuites(int ssl_ctx, String controlString); - - private static boolean findSuite(String suite) { - String[] supportedCipherSuites = nativegetsupportedciphersuites(); - for(int i = 0; i < supportedCipherSuites.length; i++) { - if (supportedCipherSuites[i].equals(suite)) { - return true; - } - } - throw new IllegalArgumentException("Protocol " + suite + " is not supported."); + return NativeCrypto.SSL_get_ciphers(sslNativePointer); } /** @@ -719,20 +630,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * is null. */ public void setEnabledCipherSuites(String[] suites) { - setEnabledCipherSuites(ssl_ctx, suites); - } - - static void setEnabledCipherSuites(int ssl_ctx, String[] suites) { - if (suites == null) { - throw new IllegalArgumentException("Provided parameter is null"); - } - String controlString = ""; - for (int i = 0; i < suites.length; i++) { - findSuite(suites[i]); - if (i == 0) controlString = suites[i]; - else controlString += ":" + suites[i]; - } - nativeSetEnabledCipherSuites(ssl_ctx, controlString); + NativeCrypto.setEnabledCipherSuites(sslNativePointer, suites); } /** @@ -741,65 +639,32 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * @return an array of protocols names */ public String[] getSupportedProtocols() { - return supportedProtocols.clone(); + return NativeCrypto.getSupportedProtocols(); } /** - * SSL mode of operation with or without back compatibility. See the OpenSSL - * ssl.h header file for more information. - */ - static private long SSL_OP_NO_SSLv3 = 0x02000000L; - static private long SSL_OP_NO_TLSv1 = 0x04000000L; - - /** * The names of the protocols' versions that are in use on this SSL * connection. - * + * * @return an array of protocols names */ @Override public String[] getEnabledProtocols() { - ArrayList<String> array = new ArrayList<String>(); - - if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) { - array.add(supportedProtocols[1]); - } - if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) { - array.add(supportedProtocols[2]); - } - return array.toArray(new String[array.size()]); + return NativeCrypto.getEnabledProtocols(sslNativePointer); } - private native void nativesetenabledprotocols(long l); - /** * This method enables the protocols' versions listed by * getSupportedProtocols(). - * + * * @param protocols The names of all the protocols to put on use - * + * * @throws IllegalArgumentException when one or more of the names in the * array are not supported, or when the array is null. */ @Override public synchronized void setEnabledProtocols(String[] protocols) { - - if (protocols == null) { - throw new IllegalArgumentException("Provided parameter is null"); - } - - ssl_op_no = SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; - - for(int i = 0; i < protocols.length; i++) { - if (protocols[i].equals("SSLv3")) - ssl_op_no ^= SSL_OP_NO_SSLv3; - else if (protocols[i].equals("TLSv1")) - ssl_op_no ^= SSL_OP_NO_TLSv1; - else throw new IllegalArgumentException("Protocol " + protocols[i] + - " is not supported."); - } - - nativesetenabledprotocols(ssl_op_no); + NativeCrypto.setEnabledProtocols(sslNativePointer, protocols); } /** @@ -912,8 +777,8 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } // END android-added - private native void nativeinterrupt() throws IOException; - private native void nativeclose() throws IOException; + private native void nativeinterrupt(int sslNativePointer) throws IOException; + private native void nativeclose(int sslNativePointer) throws IOException; /** * Closes the SSL socket. Once closed, a socket is not available for further @@ -929,9 +794,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { synchronized (handshakeLock) { if (!handshakeStarted) { handshakeStarted = true; - + synchronized (this) { - nativefree(); + free(); if (socket != null) { if (autoClose && !socket.isClosed()) socket.close(); @@ -939,12 +804,12 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { if (!super.isClosed()) super.close(); } } - + return; } } - nativeinterrupt(); + nativeinterrupt(sslNativePointer); synchronized (this) { synchronized (writeLock) { @@ -955,7 +820,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { // Shut down the SSL connection, per se. try { if (handshakeStarted) { - nativeclose(); + nativeclose(sslNativePointer); } } catch (IOException ex) { /* @@ -970,7 +835,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * the native structs, and we need to do so lest we leak * memory. */ - nativefree(); + free(); if (socket != null) { if (autoClose && !socket.isClosed()) @@ -988,12 +853,18 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } } - private native void nativefree(); + private void free() { + if (sslNativePointer == 0) { + return; + } + NativeCrypto.SSL_free(sslNativePointer); + sslNativePointer = 0; + } protected void finalize() throws IOException { updateInstanceCount(-1); - if (ssl == 0) { + if (sslNativePointer == 0) { /* * It's already been closed, so there's no need to do anything * more at this point. diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java index c39e3ff..34942e1 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java @@ -73,8 +73,6 @@ public class SSLContextImpl extends SSLContextSpi { * @param clientCache persistent client session cache or {@code null} * @param serverCache persistent server session cache or {@code null} * @throws KeyManagementException if initializing this instance fails - * - * @since Android 1.1 */ public void engineInit(KeyManager[] kms, TrustManager[] tms, SecureRandom sr, SSLClientSessionCache clientCache, @@ -126,4 +124,4 @@ public class SSLContextImpl extends SSLContextSpi { public ClientSessionContext engineGetClientSessionContext() { return clientSessionContext; } -}
\ No newline at end of file +} diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java index 89916de..525756d 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java @@ -93,29 +93,11 @@ public class SSLParameters implements Cloneable { if (enabledCipherSuites == null) this.enabledCipherSuites = CipherSuite.defaultCipherSuites; return enabledCipherSuites; } - - /** - * Holds a pointer to our native SSL context. - */ - private int ssl_ctx = 0; - - /** - * Initializes our native SSL context. - */ - private native int nativeinitsslctx(); - - /** - * Returns the native SSL context, creating it on-the-fly, if necessary. - */ - protected synchronized int getSSLCTX() { - if (ssl_ctx == 0) ssl_ctx = nativeinitsslctx(); - return ssl_ctx; - } // END android-changed /** * Initializes the parameters. Naturally this constructor is used - * in SSLContextImpl.engineInit method which dirrectly passes its + * in SSLContextImpl.engineInit method which dirrectly passes its * parameters. In other words this constructor holds all * the functionality provided by SSLContext.init method. * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[], @@ -127,21 +109,21 @@ public class SSLParameters implements Cloneable { SSLServerSessionCache serverCache) throws KeyManagementException { this.serverSessionContext - = new ServerSessionContext(this, serverCache); + = new ServerSessionContext(this, NativeCrypto.SSL_CTX_new(), serverCache); this.clientSessionContext - = new ClientSessionContext(this, clientCache); + = new ClientSessionContext(this, NativeCrypto.SSL_CTX_new(), clientCache); // END android-changed try { // initialize key manager boolean initialize_default = false; - // It's not described by the spec of SSLContext what should happen + // It's not described by the spec of SSLContext what should happen // if the arrays of length 0 are specified. This implementation // behave as for null arrays (i.e. use installed security providers) if ((kms == null) || (kms.length == 0)) { if (defaultKeyManager == null) { KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(null, null); + kmf.init(null, null); kms = kmf.getKeyManagers(); // tell that we are trying to initialize defaultKeyManager initialize_default = true; diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java index a3c2c6d..c379fee 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java @@ -46,8 +46,9 @@ public class ServerSessionContext extends AbstractSessionContext { private final SSLServerSessionCache persistentCache; public ServerSessionContext(SSLParameters parameters, + int sslCtxNativePointer, SSLServerSessionCache persistentCache) { - super(parameters, 100, 0); + super(parameters, sslCtxNativePointer, 100, 0); this.persistentCache = persistentCache; } @@ -107,8 +108,13 @@ public class ServerSessionContext extends AbstractSessionContext { return null; } + @Override void putSession(SSLSession session) { - ByteArray key = new ByteArray(session.getId()); + byte[] id = session.getId(); + if (id.length == 0) { + return; + } + ByteArray key = new ByteArray(id); synchronized (sessions) { sessions.put(key, session); } diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp index d0682a4..8af33aa 100644 --- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp +++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp @@ -46,7 +46,7 @@ struct jsse_ssl_app_data_t { /** * Frees the SSL error state. - * + * * OpenSSL keeps an "error stack" per thread, and given that this code * can be called from arbitrary threads that we don't keep track of, * we err on the side of freeing the error state promptly (instead of, @@ -60,7 +60,7 @@ static void freeSslErrorState(void) { /* * Checks this thread's OpenSSL error queue and throws a RuntimeException if * necessary. - * + * * @return 1 if an exception was thrown, 0 if not. */ static int throwExceptionIfNecessary(JNIEnv* env) { @@ -79,13 +79,166 @@ static int throwExceptionIfNecessary(JNIEnv* env) { return result; } + +/** + * Throws an SocketTimeoutException with the given string as a message. + */ +static void throwSocketTimeoutException(JNIEnv* env, const char* message) { + if (jniThrowException(env, "java/net/SocketTimeoutException", message)) { + LOGE("Unable to throw"); + } +} + +/** + * Throws a java.io.IOException with the given string as a message. + */ +static void throwIOExceptionStr(JNIEnv* env, const char* message) { + if (jniThrowException(env, "java/io/IOException", message)) { + LOGE("Unable to throw"); + } +} + +/** + * Throws an IOException with a message constructed from the current + * SSL errors. This will also log the errors. + * + * @param env the JNI environment + * @param sslReturnCode return code from failing SSL function + * @param sslErrorCode error code returned from SSL_get_error() + * @param message null-ok; general error message + */ +static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode, + int sslErrorCode, const char* message) { + const char* messageStr = NULL; + char* str; + int ret; + + // First consult the SSL error code for the general message. + switch (sslErrorCode) { + case SSL_ERROR_NONE: + messageStr = "Ok"; + break; + case SSL_ERROR_SSL: + messageStr = "Failure in SSL library, usually a protocol error"; + break; + case SSL_ERROR_WANT_READ: + messageStr = "SSL_ERROR_WANT_READ occured. You should never see this."; + break; + case SSL_ERROR_WANT_WRITE: + messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this."; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this."; + break; + case SSL_ERROR_SYSCALL: + messageStr = "I/O error during system call"; + break; + case SSL_ERROR_ZERO_RETURN: + messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this."; + break; + case SSL_ERROR_WANT_CONNECT: + messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this."; + break; + case SSL_ERROR_WANT_ACCEPT: + messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this."; + break; + default: + messageStr = "Unknown SSL error"; + } + + // Prepend either our explicit message or a default one. + if (asprintf(&str, "%s: %s", + (message != NULL) ? message : "SSL error", messageStr) == 0) { + throwIOExceptionStr(env, messageStr); + LOGV("%s", messageStr); + freeSslErrorState(); + return; + } + + char* allocStr = str; + + // For SSL protocol errors, SSL might have more information. + if (sslErrorCode == SSL_ERROR_SSL) { + // Append each error as an additional line to the message. + for (;;) { + char errStr[256]; + const char* file; + int line; + const char* data; + int flags; + unsigned long err = + ERR_get_error_line_data(&file, &line, &data, &flags); + if (err == 0) { + break; + } + + ERR_error_string_n(err, errStr, sizeof(errStr)); + + ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)", + (allocStr == NULL) ? "" : allocStr, + errStr, + file, + line, + data, + flags); + + if (ret < 0) { + break; + } + + free(allocStr); + allocStr = str; + } + // For errors during system calls, errno might be our friend. + } else if (sslErrorCode == SSL_ERROR_SYSCALL) { + if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) { + free(allocStr); + allocStr = str; + } + // If the error code is invalid, print it. + } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) { + if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) { + free(allocStr); + allocStr = str; + } + } + + throwIOExceptionStr(env, allocStr); + + LOGV("%s", allocStr); + free(allocStr); + freeSslErrorState(); +} + +/** + * Helper function that grabs the casts an ssl pointer and then checks for nullness. + * If this function returns NULL and <code>throwIfNull</code> is + * passed as <code>true</code>, then this function will call + * <code>throwIOExceptionStr</code> before returning, so in this case of + * NULL, a caller of this function should simply return and allow JNI + * to do its thing. + * + * @param env the JNI environment + * @param ssl_address; the ssl_address pointer as an integer + * @param throwIfNull whether to throw if the SSL pointer is NULL + * @returns the pointer, which may be NULL + */ +static SSL* getSslPointer(JNIEnv* env, int ssl_address, bool throwIfNull) { + SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address)); + if ((ssl == NULL) && throwIfNull) { + throwIOExceptionStr(env, "null SSL pointer"); + } + + return ssl; +} + /** * Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the * fly. */ static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) { // LOGD("Entering arrayToBignum()"); - + jbyte* sourceBytes = env->GetByteArrayElements(source, NULL); int sourceLength = env->GetArrayLength(source); BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes, sourceLength, NULL); @@ -94,18 +247,98 @@ static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) { } /** - * private static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key); + * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I + * suppose there are not many other ways to do this on a Linux system (modulo + * isomorphism). + */ +#define MUTEX_TYPE pthread_mutex_t +#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) +#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) +#define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) +#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) +#define THREAD_ID pthread_self() +#define THROW_EXCEPTION (-2) +#define THROW_SOCKETTIMEOUTEXCEPTION (-3) + +static MUTEX_TYPE *mutex_buf = NULL; + +static void locking_function(int mode, int n, const char * file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(mutex_buf[n]); + } else { + MUTEX_UNLOCK(mutex_buf[n]); + } +} + +static unsigned long id_function(void) { + return ((unsigned long)THREAD_ID); +} + +int THREAD_setup(void) { + int i; + + mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE)); + + if(!mutex_buf) { + return 0; + } + + for (i = 0; i < CRYPTO_num_locks( ); i++) { + MUTEX_SETUP(mutex_buf[i]); + } + + CRYPTO_set_id_callback(id_function); + CRYPTO_set_locking_callback(locking_function); + + return 1; +} + +int THREAD_cleanup(void) { + int i; + + if (!mutex_buf) { + return 0; + } + + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < CRYPTO_num_locks( ); i++) { + MUTEX_CLEANUP(mutex_buf[i]); + } + + free(mutex_buf); + mutex_buf = NULL; + + return 1; +} + +/** + * Initialization phase for every OpenSSL job: Loads the Error strings, the + * crypto algorithms and reset the OpenSSL library + */ +static void NativeCrypto_clinit(JNIEnv* env, jclass) +{ + SSL_load_error_strings(); + ERR_load_crypto_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + THREAD_setup(); +} + +/** + * public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key); */ static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteArray p, jbyteArray q, jbyteArray g, jbyteArray pub_key, jbyteArray priv_key) { // LOGD("Entering EVP_PKEY_new_DSA()"); - + DSA* dsa = DSA_new(); - + dsa->p = arrayToBignum(env, p); dsa->q = arrayToBignum(env, q); dsa->g = arrayToBignum(env, g); dsa->pub_key = arrayToBignum(env, pub_key); - + if (priv_key != NULL) { dsa->priv_key = arrayToBignum(env, priv_key); } @@ -115,10 +348,10 @@ static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteA jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM"); return NULL; } - + EVP_PKEY* pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); - + return pkey; } @@ -127,36 +360,36 @@ static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteA */ static EVP_PKEY* NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) { // LOGD("Entering EVP_PKEY_new_RSA()"); - + RSA* rsa = RSA_new(); - + rsa->n = arrayToBignum(env, n); rsa->e = arrayToBignum(env, e); - + if (d != NULL) { rsa->d = arrayToBignum(env, d); } - + if (p != NULL) { rsa->p = arrayToBignum(env, p); } - + if (q != NULL) { rsa->q = arrayToBignum(env, q); } // int check = RSA_check_key(rsa); // LOGI("RSA_check_key returns %d", check); - + if (rsa->n == NULL || rsa->e == NULL) { RSA_free(rsa); jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM"); return NULL; } - + EVP_PKEY* pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); - + return pkey; } @@ -176,7 +409,7 @@ static void NativeCrypto_EVP_PKEY_free(JNIEnv* env, jclass clazz, EVP_PKEY* pkey */ static jint NativeCrypto_EVP_new(JNIEnv* env, jclass clazz) { // LOGI("NativeCrypto_EVP_DigestNew"); - + return (jint)EVP_MD_CTX_create(); } @@ -185,7 +418,7 @@ static jint NativeCrypto_EVP_new(JNIEnv* env, jclass clazz) { */ static void NativeCrypto_EVP_free(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) { // LOGI("NativeCrypto_EVP_DigestFree"); - + if (ctx != NULL) { EVP_MD_CTX_destroy(ctx); } @@ -196,20 +429,20 @@ static void NativeCrypto_EVP_free(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) { */ static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray hash, jint offset) { // LOGI("NativeCrypto_EVP_DigestFinal%x, %x, %d, %d", ctx, hash, offset); - + if (ctx == NULL || hash == NULL) { jniThrowNullPointerException(env, NULL); return -1; } int result = -1; - + jbyte* hashBytes = env->GetByteArrayElements(hash, NULL); EVP_DigestFinal(ctx, (unsigned char*) (hashBytes + offset), (unsigned int*)&result); env->ReleaseByteArrayElements(hash, hashBytes, 0); throwExceptionIfNecessary(env); - + return result; } @@ -218,24 +451,24 @@ static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* */ static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) { // LOGI("NativeCrypto_EVP_DigestInit"); - + if (ctx == NULL || algorithm == NULL) { jniThrowNullPointerException(env, NULL); return; } - + const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL); - + const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars)); env->ReleaseStringUTFChars(algorithm, algorithmChars); - + if (digest == NULL) { jniThrowRuntimeException(env, "Hash algorithm not found"); return; } - + EVP_DigestInit(ctx, digest); - + throwExceptionIfNecessary(env); } @@ -244,16 +477,16 @@ static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* c */ static jint NativeCrypto_EVP_DigestSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) { // LOGI("NativeCrypto_EVP_DigestSize"); - + if (ctx == NULL) { jniThrowNullPointerException(env, NULL); return -1; } - + int result = EVP_MD_CTX_size(ctx); - + throwExceptionIfNecessary(env); - + return result; } @@ -262,16 +495,16 @@ static jint NativeCrypto_EVP_DigestSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* c */ static jint NativeCrypto_EVP_DigestBlockSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) { // LOGI("NativeCrypto_EVP_DigestBlockSize"); - + if (ctx == NULL) { jniThrowNullPointerException(env, NULL); return -1; } - + int result = EVP_MD_CTX_block_size(ctx); - + throwExceptionIfNecessary(env); - + return result; } @@ -280,16 +513,16 @@ static jint NativeCrypto_EVP_DigestBlockSize(JNIEnv* env, jclass clazz, EVP_MD_C */ static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) { // LOGI("NativeCrypto_EVP_DigestUpdate %x, %x, %d, %d", ctx, buffer, offset, length); - + if (ctx == NULL || buffer == NULL) { jniThrowNullPointerException(env, NULL); return; } - + jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL); EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes + offset), length); env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT); - + throwExceptionIfNecessary(env); } @@ -298,24 +531,24 @@ static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* */ static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) { // LOGI("NativeCrypto_EVP_VerifyInit"); - + if (ctx == NULL || algorithm == NULL) { jniThrowNullPointerException(env, NULL); return; } - + const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL); - + const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars)); env->ReleaseStringUTFChars(algorithm, algorithmChars); - + if (digest == NULL) { jniThrowRuntimeException(env, "Hash algorithm not found"); return; } - + EVP_VerifyInit(ctx, digest); - + throwExceptionIfNecessary(env); } @@ -324,16 +557,16 @@ static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* c */ static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) { // LOGI("NativeCrypto_EVP_VerifyUpdate %x, %x, %d, %d", ctx, buffer, offset, length); - + if (ctx == NULL || buffer == NULL) { jniThrowNullPointerException(env, NULL); return; } - + jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL); EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes + offset), length); env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT); - + throwExceptionIfNecessary(env); } @@ -342,343 +575,349 @@ static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* */ static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length, EVP_PKEY* pkey) { // LOGI("NativeCrypto_EVP_VerifyFinal %x, %x, %d, %d %x", ctx, buffer, offset, length, pkey); - + if (ctx == NULL || buffer == NULL || pkey == NULL) { jniThrowNullPointerException(env, NULL); return -1; } - + jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL); int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes + offset), length, pkey); env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT); - - throwExceptionIfNecessary(env); - - return result; -} - -/* - * Defines the mapping from Java methods and their signatures - * to native functions. Order is (1) Java name, (2) signature, - * (3) pointer to C function. - */ -static JNINativeMethod sNativeCryptoMethods[] = { - { "EVP_PKEY_new_DSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA }, - { "EVP_PKEY_new_RSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA }, - { "EVP_PKEY_free", "(I)V", (void*)NativeCrypto_EVP_PKEY_free }, - { "EVP_new", "()I", (void*)NativeCrypto_EVP_new }, - { "EVP_free", "(I)V", (void*)NativeCrypto_EVP_free }, - { "EVP_DigestFinal", "(I[BI)I", (void*)NativeCrypto_EVP_DigestFinal }, - { "EVP_DigestInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit }, - { "EVP_DigestBlockSize", "(I)I", (void*)NativeCrypto_EVP_DigestBlockSize }, - { "EVP_DigestSize", "(I)I", (void*)NativeCrypto_EVP_DigestSize }, - { "EVP_DigestUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_DigestUpdate }, - { "EVP_VerifyInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit }, - { "EVP_VerifyUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_VerifyUpdate }, - { "EVP_VerifyFinal", "(I[BIII)I", (void*)NativeCrypto_EVP_VerifyFinal } -}; - -/** - * Module scope variables initialized during JNI registration. - */ -static jfieldID field_Socket_ssl_ctx; -static jfieldID field_Socket_ssl; -static jfieldID field_FileDescriptor_descriptor; -static jfieldID field_Socket_mImpl; -static jfieldID field_Socket_mFD; -static jfieldID field_Socket_timeout; -/** - * Gets the chars of a String object as a '\0'-terminated UTF-8 string, - * stored in a freshly-allocated BIO memory buffer. - */ -static BIO *stringToMemBuf(JNIEnv* env, jstring string) { - jsize byteCount = env->GetStringUTFLength(string); - LocalArray<1024> buf(byteCount + 1); - env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]); + throwExceptionIfNecessary(env); - BIO* result = BIO_new(BIO_s_mem()); - BIO_puts(result, &buf[0]); return result; } /** - * Throws an SocketTimeoutException with the given string as a message. + * Convert ssl version constant to string. Based on SSL_get_version */ -static void throwSocketTimeoutException(JNIEnv* env, const char* message) { - if (jniThrowException(env, "java/net/SocketTimeoutException", message)) { - LOGE("Unable to throw"); +static const char* get_ssl_version(int ssl_version) { + switch (ssl_version) { + // newest to oldest + case TLS1_VERSION: { + return SSL_TXT_TLSV1; + } + case SSL3_VERSION: { + return SSL_TXT_SSLV3; + } + case SSL2_VERSION: { + return SSL_TXT_SSLV2; + } + default: { + return "unknown"; + } } } /** - * Throws a java.io.IOException with the given string as a message. + * Convert content type constant to string. */ -static void throwIOExceptionStr(JNIEnv* env, const char* message) { - if (jniThrowException(env, "java/io/IOException", message)) { - LOGE("Unable to throw"); +static const char* get_content_type(int content_type) { + switch (content_type) { + case SSL3_RT_CHANGE_CIPHER_SPEC: { + return "SSL3_RT_CHANGE_CIPHER_SPEC"; + } + case SSL3_RT_ALERT: { + return "SSL3_RT_ALERT"; + } + case SSL3_RT_HANDSHAKE: { + return "SSL3_RT_HANDSHAKE"; + } + case SSL3_RT_APPLICATION_DATA: { + return "SSL3_RT_APPLICATION_DATA"; + } + default: { + LOGD("Unknown TLS/SSL content type %d", content_type); + return "<unknown>"; + } } } /** - * Throws an IOException with a message constructed from the current - * SSL errors. This will also log the errors. - * - * @param env the JNI environment - * @param sslReturnCode return code from failing SSL function - * @param sslErrorCode error code returned from SSL_get_error() - * @param message null-ok; general error message + * Simple logging call back to show hand shake messages */ -static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode, - int sslErrorCode, const char* message) { - const char* messageStr = NULL; - char* str; - int ret; - - // First consult the SSL error code for the general message. - switch (sslErrorCode) { - case SSL_ERROR_NONE: - messageStr = "Ok"; - break; - case SSL_ERROR_SSL: - messageStr = "Failure in SSL library, usually a protocol error"; - break; - case SSL_ERROR_WANT_READ: - messageStr = "SSL_ERROR_WANT_READ occured. You should never see this."; - break; - case SSL_ERROR_WANT_WRITE: - messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this."; - break; - case SSL_ERROR_WANT_X509_LOOKUP: - messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this."; - break; - case SSL_ERROR_SYSCALL: - messageStr = "I/O error during system call"; - break; - case SSL_ERROR_ZERO_RETURN: - messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this."; - break; - case SSL_ERROR_WANT_CONNECT: - messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this."; - break; - case SSL_ERROR_WANT_ACCEPT: - messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this."; - break; - default: - messageStr = "Unknown SSL error"; - } - - // Prepend either our explicit message or a default one. - if (asprintf(&str, "%s: %s", - (message != NULL) ? message : "SSL error", messageStr) == 0) { - throwIOExceptionStr(env, messageStr); - LOGV("%s", messageStr); - freeSslErrorState(); - return; - } - - char* allocStr = str; - - // For SSL protocol errors, SSL might have more information. - if (sslErrorCode == SSL_ERROR_SSL) { - // Append each error as an additional line to the message. - for (;;) { - char errStr[256]; - const char* file; - int line; - const char* data; - int flags; - unsigned long err = - ERR_get_error_line_data(&file, &line, &data, &flags); - if (err == 0) { - break; - } +static void ssl_msg_callback_LOG(int write_p, int ssl_version, int content_type, + const void *buf, size_t len, SSL* ssl, void* arg) { + LOGD("SSL %p %s %s %s %p %d %p", + ssl, + (write_p) ? "send" : "recv", + get_ssl_version(ssl_version), + get_content_type(content_type), + buf, + len, + arg); +} - ERR_error_string_n(err, errStr, sizeof(errStr)); +/* + * public static native int SSL_CTX_new(); + */ +static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass clazz) { + SSL_CTX* sslCtx = SSL_CTX_new(SSLv23_method()); + // Note: We explicitly do not allow SSLv2 to be used. + SSL_CTX_set_options(sslCtx, SSL_OP_ALL | SSL_OP_NO_SSLv2); - ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)", - (allocStr == NULL) ? "" : allocStr, - errStr, - file, - line, - data, - flags); + int mode = SSL_CTX_get_mode(sslCtx); + /* + * Turn on "partial write" mode. This means that SSL_write() will + * behave like Posix write() and possibly return after only + * writing a partial buffer. Note: The alternative, perhaps + * surprisingly, is not that SSL_write() always does full writes + * but that it will force you to retry write calls having + * preserved the full state of the original call. (This is icky + * and undesirable.) + */ + mode |= SSL_MODE_ENABLE_PARTIAL_WRITE; +#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */ + mode |= SSL_MODE_SMALL_BUFFERS; /* lazily allocate record buffers; usually saves + * 44k over the default */ +#endif +#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */ + mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH; /* enable sending of client data as soon as + * ClientCCS and ClientFinished are sent */ +#endif + SSL_CTX_set_mode(sslCtx, mode); - if (ret < 0) { - break; - } + // SSL_CTX_set_msg_callback(sslCtx, ssl_msg_callback_LOG); /* enable for handshake debug */ + return (jint) sslCtx; +} - free(allocStr); - allocStr = str; - } - // For errors during system calls, errno might be our friend. - } else if (sslErrorCode == SSL_ERROR_SYSCALL) { - if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) { - free(allocStr); - allocStr = str; - } - // If the error code is invalid, print it. - } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) { - if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) { - free(allocStr); - allocStr = str; - } +static jobjectArray makeCipherList(JNIEnv* env, STACK_OF(SSL_CIPHER)* cipher_list) { + // Create a String[]. + jclass stringClass = env->FindClass("java/lang/String"); + if (stringClass == NULL) { + return NULL; + } + int cipherCount = sk_SSL_CIPHER_num(cipher_list); + jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL); + if (array == NULL) { + return NULL; } - throwIOExceptionStr(env, allocStr); - - LOGV("%s", allocStr); - free(allocStr); - freeSslErrorState(); + // Fill in the cipher names. + for (int i = 0; i < cipherCount; ++i) { + const char* c = sk_SSL_CIPHER_value(cipher_list, i)->name; + env->SetObjectArrayElement(array, i, env->NewStringUTF(c)); + } + return array; } /** - * Helper function that grabs the ssl pointer out of the given object. - * If this function returns NULL and <code>throwIfNull</code> is - * passed as <code>true</code>, then this function will call - * <code>throwIOExceptionStr</code> before returning, so in this case of - * NULL, a caller of this function should simply return and allow JNI - * to do its thing. - * - * @param env non-null; the JNI environment - * @param obj non-null; socket object - * @param throwIfNull whether to throw if the SSL pointer is NULL - * @returns the pointer, which may be NULL + * Loads the ciphers suites that are supported by an SSL_CTX + * and returns them in a string array. */ -static SSL *getSslPointer(JNIEnv* env, jobject obj, bool throwIfNull) { - SSL *ssl = (SSL *)env->GetIntField(obj, field_Socket_ssl); - - if ((ssl == NULL) && throwIfNull) { - throwIOExceptionStr(env, "null SSL pointer"); +static jobjectArray NativeCrypto_SSL_CTX_get_ciphers(JNIEnv* env, + jclass, jint ssl_ctx_address) +{ + SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); + if (ssl_ctx == NULL) { + jniThrowNullPointerException(env, "SSL_CTX is null"); + return NULL; } - - return ssl; + return makeCipherList(env, ssl_ctx->cipher_list); } -// ============================================================================ -// === OpenSSL-related helper stuff begins here. ============================== -// ============================================================================ - /** - * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I - * suppose there are not many other ways to do this on a Linux system (modulo - * isomorphism). + * public static native void SSL_CTX_free(int ssl_ctx) */ -#define MUTEX_TYPE pthread_mutex_t -#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) -#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) -#define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) -#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) -#define THREAD_ID pthread_self() -#define THROW_EXCEPTION (-2) -#define THROW_SOCKETTIMEOUTEXCEPTION (-3) - -static MUTEX_TYPE *mutex_buf = NULL; - -static void locking_function(int mode, int n, const char * file, int line) { - if (mode & CRYPTO_LOCK) { - MUTEX_LOCK(mutex_buf[n]); - } else { - MUTEX_UNLOCK(mutex_buf[n]); +static void NativeCrypto_SSL_CTX_free(JNIEnv* env, + jclass, jint ssl_ctx_address) +{ + SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); + if (ssl_ctx == NULL) { + jniThrowNullPointerException(env, "SSL_CTX is null"); + return; } + SSL_CTX_free(ssl_ctx); } -static unsigned long id_function(void) { - return ((unsigned long)THREAD_ID); -} - -int THREAD_setup(void) { - int i; +/** + * Gets the chars of a String object as a '\0'-terminated UTF-8 string, + * stored in a freshly-allocated BIO memory buffer. + */ +static BIO *stringToMemBuf(JNIEnv* env, jstring string) { + jsize byteCount = env->GetStringUTFLength(string); + LocalArray<1024> buf(byteCount + 1); + env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]); - mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE)); + BIO* result = BIO_new(BIO_s_mem()); + BIO_puts(result, &buf[0]); + return result; +} - if(!mutex_buf) { +/** + * public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException; + */ +static jint NativeCrypto_SSL_new(JNIEnv* env, jclass, + jint ssl_ctx_address, jstring privatekey, jstring certificates, jbyteArray seed) +{ + SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); + if (ssl_ctx == NULL) { + jniThrowNullPointerException(env, "SSL_CTX is null"); return 0; } - for (i = 0; i < CRYPTO_num_locks( ); i++) { - MUTEX_SETUP(mutex_buf[i]); + // 'seed == null' when no SecureRandom Object is set + // in the SSLContext. + if (seed != NULL) { + jbyte* randseed = env->GetByteArrayElements(seed, NULL); + RAND_seed((unsigned char*) randseed, 1024); + env->ReleaseByteArrayElements(seed, randseed, 0); + } else { + RAND_load_file("/dev/urandom", 1024); } - CRYPTO_set_id_callback(id_function); - CRYPTO_set_locking_callback(locking_function); + SSL* ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + throwIOExceptionWithSslErrors(env, 0, 0, + "Unable to create SSL structure"); + return NULL; + } - return 1; -} + /* Java code in class OpenSSLSocketImpl does the verification. Meaning of + * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher + * (by default disabled), the server will send a certificate which will + * be checked. The result of the certificate verification process can be + * checked after the TLS/SSL handshake using the SSL_get_verify_result(3) + * function. The handshake will be continued regardless of the + * verification result. + */ + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); -int THREAD_cleanup(void) { - int i; + if (privatekey != NULL) { + BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey); + EVP_PKEY* privatekeyevp = + PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL); + BIO_free(privatekeybio); - if (!mutex_buf) { - return 0; - } + if (privatekeyevp == NULL) { + LOGE(ERR_error_string(ERR_get_error(), NULL)); + throwIOExceptionWithSslErrors(env, 0, 0, + "Error parsing the private key"); + SSL_free(ssl); + return NULL; + } - CRYPTO_set_id_callback(NULL); - CRYPTO_set_locking_callback(NULL); + BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates); + X509* certificatesx509 = + PEM_read_bio_X509(certificatesbio, NULL, 0, NULL); + BIO_free(certificatesbio); - for (i = 0; i < CRYPTO_num_locks( ); i++) { - MUTEX_CLEANUP(mutex_buf[i]); - } + if (certificatesx509 == NULL) { + LOGE(ERR_error_string(ERR_get_error(), NULL)); + throwIOExceptionWithSslErrors(env, 0, 0, + "Error parsing the certificates"); + EVP_PKEY_free(privatekeyevp); + SSL_free(ssl); + return NULL; + } - free(mutex_buf); - mutex_buf = NULL; + int ret = SSL_use_certificate(ssl, certificatesx509); + if (ret != 1) { + LOGE(ERR_error_string(ERR_get_error(), NULL)); + throwIOExceptionWithSslErrors(env, ret, 0, + "Error setting the certificates"); + X509_free(certificatesx509); + EVP_PKEY_free(privatekeyevp); + SSL_free(ssl); + return NULL; + } - return 1; -} + ret = SSL_use_PrivateKey(ssl, privatekeyevp); + if (ret != 1) { + LOGE(ERR_error_string(ERR_get_error(), NULL)); + throwIOExceptionWithSslErrors(env, ret, 0, + "Error setting the private key"); + X509_free(certificatesx509); + EVP_PKEY_free(privatekeyevp); + SSL_free(ssl); + return NULL; + } -int get_socket_timeout(int type, int sd) { - struct timeval tv; - socklen_t len = sizeof(tv); - if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) { - LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)", - sd, - strerror(errno), - errno); - return 0; + ret = SSL_check_private_key(ssl); + if (ret != 1) { + throwIOExceptionWithSslErrors(env, ret, 0, + "Error checking the private key"); + X509_free(certificatesx509); + EVP_PKEY_free(privatekeyevp); + SSL_free(ssl); + return NULL; + } } - // LOGI("Current socket timeout (%d(s), %d(us))!", - // (int)tv.tv_sec, (int)tv.tv_usec); - int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; - return timeout; + return (jint)ssl; } -#ifdef TIMEOUT_DEBUG_SSL +/** + * public static native long SSL_get_options(int ssl); + */ +static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass, + jint ssl_address) { + SSL* ssl = getSslPointer(env, ssl_address, true); + if (ssl == NULL) { + return 0; + } + return SSL_get_options(ssl); +} -void print_socket_timeout(const char* name, int type, int sd) { - struct timeval tv; - int len = sizeof(tv); - if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) { - LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)", - sd, - name, - strerror(errno), - errno); +/** + * public static native long SSL_set_options(int ssl, long options); + */ +static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass, + jint ssl_address, jlong options) { + SSL* ssl = getSslPointer(env, ssl_address, true); + if (ssl == NULL) { + return 0 ; } - LOGI("Current socket %s is (%d(s), %d(us))!", - name, (int)tv.tv_sec, (int)tv.tv_usec); + return SSL_set_options(ssl, options); } -void print_timeout(const char* method, SSL* ssl) { - LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method); - int fd = SSL_get_fd(ssl); - print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd); - print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd); +/** + * Loads the ciphers suites that are enabled in the SSL + * and returns them in a string array. + */ +static jobjectArray NativeCrypto_SSL_get_ciphers(JNIEnv* env, + jclass, jint ssl_address) +{ + SSL* ssl = getSslPointer(env, ssl_address, true); + if (ssl == NULL) { + return NULL; + } + return makeCipherList(env, SSL_get_ciphers(ssl)); } -#endif +/** + * Sets the ciphers suites that are enabled in the SSL + */ +static void NativeCrypto_SSL_set_cipher_list(JNIEnv* env, jclass, + jint ssl_address, jstring controlString) +{ + SSL* ssl = getSslPointer(env, ssl_address, true); + if (ssl == NULL) { + return; + } + const char* str = env->GetStringUTFChars(controlString, NULL); + int rc = SSL_set_cipher_list(ssl, str); + env->ReleaseStringUTFChars(controlString, str); + if (rc == 0) { + freeSslErrorState(); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Illegal cipher suite strings."); + } +} /** * Our additional application data needed for getting synchronization right. * This maybe warrants a bit of lengthy prose: - * + * * (1) We use a flag to reflect whether we consider the SSL connection alive. * Any read or write attempt loops will be cancelled once this flag becomes 0. - * + * * (2) We use an int to count the number of threads that are blocked by the * underlying socket. This may be at most two (one reader and one writer), since * the Java layer ensures that no more threads will enter the native code at the * same time. - * + * * (3) The pipe is used primarily as a means of cancelling a blocking select() * when we want to close the connection (aka "emergency button"). It is also * necessary for dealing with a possible race condition situation: There might @@ -687,23 +926,23 @@ void print_timeout(const char* method, SSL* ssl) { * If one leaves the select() successfully before the other enters it, the * "success" event is already consumed and the second thread will be blocked, * possibly forever (depending on network conditions). - * + * * The idea for solving the problem looks like this: Whenever a thread is * successful in moving around data on the network, and it knows there is * another thread stuck in a select(), it will write a byte to the pipe, waking * up the other thread. A thread that returned from select(), on the other hand, * knows whether it's been woken up by the pipe. If so, it will consume the * byte, and the original state of affairs has been restored. - * + * * The pipe may seem like a bit of overhead, but it fits in nicely with the * other file descriptors of the select(), so there's only one condition to wait * for. - * + * * (4) Finally, a mutex is needed to make sure that at most one thread is in * either SSL_read() or SSL_write() at any given time. This is an OpenSSL * requirement. We use the same mutex to guard the field for counting the * waiting threads. - * + * * Note: The current implementation assumes that we don't have to deal with * problems induced by multiple cores or processors and their respective * memory caches. One possible problem is that of inconsistent views on the @@ -712,7 +951,7 @@ void print_timeout(const char* method, SSL* ssl) { * currently this seems a bit like overkill. */ typedef struct app_data { - int aliveAndKicking; + volatile int aliveAndKicking; int waitingThreads; int fdsEmergency[2]; MUTEX_TYPE mutex; @@ -720,7 +959,7 @@ typedef struct app_data { /** * Creates our application data and attaches it to a given SSL connection. - * + * * @param ssl The SSL connection to attach the data to. * @return 0 on success, -1 on failure. */ @@ -751,16 +990,16 @@ static int sslCreateAppData(SSL* ssl) { /** * Destroys our application data, cleaning up everything in the process. - * + * * @param ssl The SSL connection to take the data from. - */ + */ static void sslDestroyAppData(SSL* ssl) { APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl); if (data != NULL) { SSL_set_app_data(ssl, NULL); - data -> aliveAndKicking = 0; + data->aliveAndKicking = 0; if (data->fdsEmergency[0] != -1) { close(data->fdsEmergency[0]); @@ -776,72 +1015,101 @@ static void sslDestroyAppData(SSL* ssl) { } } - /** - * Frees the SSL_CTX struct for the given instance. + * public static native void SSL_free(int ssl); */ -static void free_ssl_ctx(JNIEnv* env, jobject object) { - /* - * Preserve and restore the exception state around this call, so - * that GetIntField and SetIntField will operate without complaint. - */ - jthrowable exception = env->ExceptionOccurred(); - - if (exception != NULL) { - env->ExceptionClear(); - } - - SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_Socket_ssl_ctx); - - if (ctx != NULL) { - SSL_CTX_free(ctx); - env->SetIntField(object, field_Socket_ssl_ctx, (int) NULL); - } - - if (exception != NULL) { - env->Throw(exception); +static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jint ssl_address) +{ + SSL* ssl = getSslPointer(env, ssl_address, true); + if (ssl == NULL) { + return; } + sslDestroyAppData(ssl); + SSL_free(ssl); } -/** - * Frees the SSL struct for the given instance. +/* + * Defines the mapping from Java methods and their signatures + * to native functions. Order is (1) Java name, (2) signature, + * (3) pointer to C function. */ -static void free_ssl(JNIEnv* env, jobject object) { - /* - * Preserve and restore the exception state around this call, so - * that GetIntField and SetIntField will operate without complaint. - */ - jthrowable exception = env->ExceptionOccurred(); +static JNINativeMethod sNativeCryptoMethods[] = { + { "clinit", "()V", (void*)NativeCrypto_clinit}, + { "EVP_PKEY_new_DSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA }, + { "EVP_PKEY_new_RSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA }, + { "EVP_PKEY_free", "(I)V", (void*)NativeCrypto_EVP_PKEY_free }, + { "EVP_new", "()I", (void*)NativeCrypto_EVP_new }, + { "EVP_free", "(I)V", (void*)NativeCrypto_EVP_free }, + { "EVP_DigestFinal", "(I[BI)I", (void*)NativeCrypto_EVP_DigestFinal }, + { "EVP_DigestInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit }, + { "EVP_DigestBlockSize", "(I)I", (void*)NativeCrypto_EVP_DigestBlockSize }, + { "EVP_DigestSize", "(I)I", (void*)NativeCrypto_EVP_DigestSize }, + { "EVP_DigestUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_DigestUpdate }, + { "EVP_VerifyInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit }, + { "EVP_VerifyUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_VerifyUpdate }, + { "EVP_VerifyFinal", "(I[BIII)I", (void*)NativeCrypto_EVP_VerifyFinal }, + { "SSL_CTX_new", "()I", (void*)NativeCrypto_SSL_CTX_new }, + { "SSL_CTX_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_CTX_get_ciphers}, + { "SSL_CTX_free", "(I)V", (void*)NativeCrypto_SSL_CTX_free }, + { "SSL_new", "(ILjava/lang/String;Ljava/lang/String;[B)I", (void*)NativeCrypto_SSL_new}, + { "SSL_get_options", "(I)J", (void*)NativeCrypto_SSL_get_options }, + { "SSL_set_options", "(IJ)J", (void*)NativeCrypto_SSL_set_options }, + { "SSL_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_get_ciphers}, + { "SSL_set_cipher_list", "(ILjava/lang/String;)V", (void*)NativeCrypto_SSL_set_cipher_list}, + { "SSL_free", "(I)V", (void*)NativeCrypto_SSL_free}, +}; - if (exception != NULL) { - env->ExceptionClear(); - } +/** + * Module scope variables initialized during JNI registration. + */ +static jfieldID field_Socket_mImpl; +static jfieldID field_Socket_mFD; - SSL *ssl = (SSL *)env->GetIntField(object, field_Socket_ssl); +// ============================================================================ +// === OpenSSL-related helper stuff begins here. ============================== +// ============================================================================ - if (ssl != NULL) { - sslDestroyAppData(ssl); - SSL_free(ssl); - env->SetIntField(object, field_Socket_ssl, (int) NULL); +int get_socket_timeout(int type, int sd) { + struct timeval tv; + socklen_t len = sizeof(tv); + if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) { + LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)", + sd, + strerror(errno), + errno); + return 0; } + // LOGI("Current socket timeout (%d(s), %d(us))!", + // (int)tv.tv_sec, (int)tv.tv_usec); + int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; + return timeout; +} + +#ifdef TIMEOUT_DEBUG_SSL - if (exception != NULL) { - env->Throw(exception); +void print_socket_timeout(const char* name, int type, int sd) { + struct timeval tv; + int len = sizeof(tv); + if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) { + LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)", + sd, + name, + strerror(errno), + errno); } + LOGI("Current socket %s is (%d(s), %d(us))!", + name, (int)tv.tv_sec, (int)tv.tv_usec); } -/** - * Constructs the SSL struct for the given instance, replacing one - * that was already made, if any. - */ -static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX* ssl_ctx) { - free_ssl(env, object); - - SSL *ssl = SSL_new(ssl_ctx); - env->SetIntField(object, field_Socket_ssl, (int) ssl); - return ssl; +void print_timeout(const char* method, SSL* ssl) { + LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method); + int fd = SSL_get_fd(ssl); + print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd); + print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd); } +#endif + /** * Dark magic helper function that checks, for a given SSL session, whether it * can SSL_read() or SSL_write() without blocking. Takes into account any @@ -851,15 +1119,15 @@ static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX* ssl_ctx) { * specifies whether we are waiting for readability or writability. It expects * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we * only need to wait in case one of these problems occurs. - * + * * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE * @param fd The file descriptor to wait for (the underlying socket) - * @param data The application data structure with mutex info etc. + * @param data The application data structure with mutex info etc. * @param timeout The timeout value for select call, with the special value * 0 meaning no timeout at all (wait indefinitely). Note: This is * the Java semantics of the timeout value, not the usual * select() semantics. - * @return The result of the inner select() call, -1 on additional errors + * @return The result of the inner select() call, -1 on additional errors */ static int sslSelect(int type, int fd, APP_DATA *data, int timeout) { fd_set rfds; @@ -888,16 +1156,16 @@ static int sslSelect(int type, int fd, APP_DATA *data, int timeout) { } else { ptv = NULL; } - + // LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE"); int result = select(max + 1, &rfds, &wfds, NULL, ptv); // LOGD("Returned from select(), result is %d", result); - + // Lock if (MUTEX_LOCK(data->mutex) == -1) { return -1; } - + // If we have been woken up by the emergency pipe, there must be a token in // it. Thus we can safely read it (even in a blocking way). if (FD_ISSET(data->fdsEmergency[0], &rfds)) { @@ -910,7 +1178,7 @@ static int sslSelect(int type, int fd, APP_DATA *data, int timeout) { // Tell the world that there is now one thread less waiting for the // underlying network. data->waitingThreads--; - + // Unlock MUTEX_UNLOCK(data->mutex); // LOGD("leave sslSelect"); @@ -921,8 +1189,8 @@ static int sslSelect(int type, int fd, APP_DATA *data, int timeout) { * Helper function that wakes up a thread blocked in select(), in case there is * one. Is being called by sslRead() and sslWrite() as well as by JNI glue * before closing the connection. - * - * @param data The application data structure with mutex info etc. + * + * @param data The application data structure with mutex info etc. */ static void sslNotify(APP_DATA *data) { // Write a byte to the emergency pipe, so a concurrent select() can return. @@ -940,7 +1208,7 @@ static void sslNotify(APP_DATA *data) { /** * Helper function which does the actual reading. The Java layer guarantees that * at most one thread will enter this function at any given time. - * + * * @param ssl non-null; the SSL context * @param buf non-null; buffer to read into * @param len length of the buffer, in bytes @@ -953,7 +1221,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, int* sslErrorCode, int timeout) { // LOGD("Entering sslRead, caller requests to read %d bytes...", len); - + if (len == 0) { // Don't bother doing anything in this case. return 0; @@ -961,7 +1229,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, int fd = SSL_get_fd(ssl); BIO *bio = SSL_get_rbio(ssl); - + APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl); while (data->aliveAndKicking) { @@ -973,7 +1241,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, } unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio); - + // LOGD("Doing SSL_Read()"); int result = SSL_read(ssl, buf, len); int error = SSL_ERROR_NONE; @@ -989,13 +1257,13 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) { sslNotify(data); } - + // If we are blocked by the underlying socket, tell the world that // there will be one more waiting thread now. if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { data->waitingThreads++; } - + // Unlock MUTEX_UNLOCK(data->mutex); @@ -1010,7 +1278,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, return -1; } - // Need to wait for availability of underlying layer, then retry. + // Need to wait for availability of underlying layer, then retry. case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { int selectResult = sslSelect(error, fd, data, timeout); @@ -1021,7 +1289,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, } else if (selectResult == 0) { return THROW_SOCKETTIMEOUTEXCEPTION; } - + break; } @@ -1033,16 +1301,16 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, if (result == 0) { return -1; } - + // System call has been interrupted. Simply retry. if (errno == EINTR) { break; } - + // Note that for all other system call errors we fall through - // to the default case, which results in an Exception. + // to the default case, which results in an Exception. } - + // Everything else is basically an error. default: { *sslReturnCode = result; @@ -1051,14 +1319,14 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, } } } - + return -1; } /** * Helper function which does the actual writing. The Java layer guarantees that * at most one thread will enter this function at any given time. - * + * * @param ssl non-null; the SSL context * @param buf non-null; buffer to write * @param len length of the buffer, in bytes @@ -1069,29 +1337,29 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, */ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, int* sslErrorCode) { - + // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len); if (len == 0) { // Don't bother doing anything in this case. return 0; } - + int fd = SSL_get_fd(ssl); BIO *bio = SSL_get_wbio(ssl); - + APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl); - + int count = len; - - while(data->aliveAndKicking && len > 0) { + + while (data->aliveAndKicking && len > 0) { errno = 0; if (MUTEX_LOCK(data->mutex) == -1) { return -1; } - + unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio); - + // LOGD("Doing SSL_write() with %d bytes to go", len); int result = SSL_write(ssl, buf, len); int error = SSL_ERROR_NONE; @@ -1107,15 +1375,15 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) { sslNotify(data); } - + // If we are blocked by the underlying socket, tell the world that // there will be one more waiting thread now. if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { data->waitingThreads++; } - + MUTEX_UNLOCK(data->mutex); - + switch (error) { // Sucessfully write at least one byte. case SSL_ERROR_NONE: { @@ -1128,7 +1396,7 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, case SSL_ERROR_ZERO_RETURN: { return -1; } - + // Need to wait for availability of underlying layer, then retry. // The concept of a write timeout doesn't really make sense, and // it's also not standard Java behavior, so we wait forever here. @@ -1142,7 +1410,7 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, } else if (selectResult == 0) { return THROW_SOCKETTIMEOUTEXCEPTION; } - + break; } @@ -1154,16 +1422,16 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, if (result == 0) { return -1; } - + // System call has been interrupted. Simply retry. if (errno == EINTR) { break; } - + // Note that for all other system call errors we fall through - // to the default case, which results in an Exception. + // to the default case, which results in an Exception. } - + // Everything else is basically an error. default: { *sslReturnCode = result; @@ -1173,20 +1441,20 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, } } // LOGD("Successfully wrote %d bytes", count); - + return count; } /** * Helper function that creates an RSA public key from two buffers containing * the big-endian bit representation of the modulus and the public exponent. - * + * * @param mod The data of the modulus * @param modLen The length of the modulus data * @param exp The data of the exponent * @param expLen The length of the exponent data - * - * @return A pointer to the new RSA structure, or NULL on error + * + * @return A pointer to the new RSA structure, or NULL on error */ static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) { // LOGD("Entering rsaCreateKey()"); @@ -1207,7 +1475,7 @@ static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int /** * Helper function that frees an RSA key. Just calls the corresponding OpenSSL * function. - * + * * @param rsa The pointer to the new RSA structure to free. */ static void rsaFreeKey(RSA* rsa) { @@ -1220,16 +1488,16 @@ static void rsaFreeKey(RSA* rsa) { /** * Helper function that verifies a given RSA signature for a given message. - * + * * @param msg The message to verify * @param msgLen The length of the message * @param sig The signature to verify * @param sigLen The length of the signature * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1" * @param rsa The RSA public key to use - * + * * @return 1 on success, 0 on failure, -1 on error (check SSL errors then) - * + * */ static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig, unsigned int sigLen, char* algorithm, RSA* rsa) { @@ -1248,7 +1516,7 @@ static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); + EVP_MD_CTX_init(&ctx); if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) { goto cleanup; } @@ -1271,163 +1539,21 @@ static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig // ============================================================================ /** - * Initialization phase for every OpenSSL job: Loads the Error strings, the - * crypto algorithms and reset the OpenSSL library - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic(JNIEnv* env, jobject obj) -{ - SSL_load_error_strings(); - ERR_load_crypto_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - THREAD_setup(); -} - -/** - * Initialization phase for a socket with OpenSSL. The server's private key - * and X509 certificate are read and the Linux /dev/urandom file is loaded - * as RNG for the session keys. - * - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* env, jobject object, - jstring privatekey, jstring certificates, jbyteArray seed) -{ - SSL_CTX* ssl_ctx; - - // 'seed == null' when no SecureRandom Object is set - // in the SSLContext. - if (seed != NULL) { - jbyte* randseed = env->GetByteArrayElements(seed, NULL); - RAND_seed((unsigned char*) randseed, 1024); - env->ReleaseByteArrayElements(seed, randseed, 0); - } else { - RAND_load_file("/dev/urandom", 1024); - } - - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - - // Note: We explicitly do not allow SSLv2 to be used. It - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); - - /* Java code in class OpenSSLSocketImpl does the verification. Meaning of - * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher - * (by default disabled), the server will send a certificate which will - * be checked. The result of the certificate verification process can be - * checked after the TLS/SSL handshake using the SSL_get_verify_result(3) - * function. The handshake will be continued regardless of the - * verification result. - */ - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); - - int mode = SSL_CTX_get_mode(ssl_ctx); - /* - * Turn on "partial write" mode. This means that SSL_write() will - * behave like Posix write() and possibly return after only - * writing a partial buffer. Note: The alternative, perhaps - * surprisingly, is not that SSL_write() always does full writes - * but that it will force you to retry write calls having - * preserved the full state of the original call. (This is icky - * and undesirable.) - */ - mode |= SSL_MODE_ENABLE_PARTIAL_WRITE; -#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */ - mode |= SSL_MODE_SMALL_BUFFERS; /* lazily allocate record buffers; usually saves - * 44k over the default */ -#endif -#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */ - mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH; /* enable sending of client data as soon as - * ClientCCS and ClientFinished are sent */ -#endif - - SSL_CTX_set_mode(ssl_ctx, mode); - - if (privatekey != NULL) { - BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey); - EVP_PKEY* privatekeyevp = - PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL); - BIO_free(privatekeybio); - - if (privatekeyevp == NULL) { - throwIOExceptionWithSslErrors(env, 0, 0, - "Error parsing the private key"); - SSL_CTX_free(ssl_ctx); - return; - } - - BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates); - X509* certificatesx509 = - PEM_read_bio_X509(certificatesbio, NULL, 0, NULL); - BIO_free(certificatesbio); - - if (certificatesx509 == NULL) { - throwIOExceptionWithSslErrors(env, 0, 0, - "Error parsing the certificates"); - EVP_PKEY_free(privatekeyevp); - SSL_CTX_free(ssl_ctx); - return; - } - - int ret = SSL_CTX_use_certificate(ssl_ctx, certificatesx509); - if (ret != 1) { - throwIOExceptionWithSslErrors(env, ret, 0, - "Error setting the certificates"); - X509_free(certificatesx509); - EVP_PKEY_free(privatekeyevp); - SSL_CTX_free(ssl_ctx); - return; - } - - ret = SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp); - if (ret != 1) { - throwIOExceptionWithSslErrors(env, ret, 0, - "Error setting the private key"); - X509_free(certificatesx509); - EVP_PKEY_free(privatekeyevp); - SSL_CTX_free(ssl_ctx); - return; - } - - ret = SSL_CTX_check_private_key(ssl_ctx); - if (ret != 1) { - throwIOExceptionWithSslErrors(env, ret, 0, - "Error checking the private key"); - X509_free(certificatesx509); - EVP_PKEY_free(privatekeyevp); - SSL_CTX_free(ssl_ctx); - return; - } - } - - env->SetIntField(object, field_Socket_ssl_ctx, (int)ssl_ctx); -} - -/** * A connection within an OpenSSL context is established. (1) A new socket is - * constructed, (2) the TLS/SSL handshake with a server is initiated. + * constructed, (2) the TLS/SSL handshake with a server is initiated. */ -static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jobject object, - jint ctx, jobject socketObject, jboolean client_mode, jint session) +static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jclass, + jint ssl_address, jobject socketObject, jint timeout, jboolean client_mode, jint ssl_session_address) { // LOGD("ENTER connect"); - int ret, fd; - SSL_CTX* ssl_ctx; - SSL* ssl; - SSL_SESSION* ssl_session; - - ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx); - - ssl = create_ssl(env, object, ssl_ctx); + SSL* ssl = getSslPointer(env, ssl_address, true); if (ssl == NULL) { - throwIOExceptionWithSslErrors(env, 0, 0, - "Unable to create SSL structure"); - free_ssl_ctx(env, object); - return (jboolean) false; + return (jboolean) false; } + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl); if (socketImplObject == NULL) { - free_ssl(env, object); - free_ssl_ctx(env, object); throwIOExceptionStr(env, "couldn't get the socket impl from the socket"); return (jboolean) false; @@ -1435,30 +1561,25 @@ static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect( jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD); if (fdObject == NULL) { - free_ssl(env, object); - free_ssl_ctx(env, object); throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl"); return (jboolean) false; } - fd = jniGetFDFromFileDescriptor(env, fdObject); - - ssl_session = (SSL_SESSION *) session; + int fd = jniGetFDFromFileDescriptor(env, fdObject); - ret = SSL_set_fd(ssl, fd); + int ret = SSL_set_fd(ssl, fd); if (ret != 1) { throwIOExceptionWithSslErrors(env, ret, 0, "Error setting the file descriptor"); - free_ssl(env, object); - free_ssl_ctx(env, object); + SSL_clear(ssl); return (jboolean) false; } if (ssl_session != NULL) { + // LOGD("Trying to reuse session %p", ssl_session); ret = SSL_set_session(ssl, ssl_session); - if (ret != 1) { /* * Translate the error, and throw if it turns out to be a real @@ -1468,8 +1589,7 @@ static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect( if (sslErrorCode != SSL_ERROR_ZERO_RETURN) { throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "SSL session set"); - free_ssl(env, object); - free_ssl_ctx(env, object); + SSL_clear(ssl); return (jboolean) false; } } @@ -1482,8 +1602,7 @@ static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect( int mode = fcntl(fd, F_GETFL); if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) { throwIOExceptionStr(env, "Unable to make socket non blocking"); - free_ssl(env, object); - free_ssl_ctx(env, object); + SSL_clear(ssl); return (jboolean) false; } @@ -1492,19 +1611,15 @@ static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect( */ if (sslCreateAppData(ssl) == -1) { throwIOExceptionStr(env, "Unable to create application data"); - free_ssl(env, object); - free_ssl_ctx(env, object); + SSL_clear(ssl); // TODO return (jboolean) false; } - + APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl); - env->SetIntField(object, field_Socket_ssl, (int)ssl); - - int timeout = (int)env->GetIntField(object, field_Socket_timeout); - + while (data->aliveAndKicking) { - errno = 0; + errno = 0; ret = SSL_connect(ssl); if (ret == 1) { break; @@ -1525,26 +1640,24 @@ static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect( if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) { data->waitingThreads++; int selectResult = sslSelect(error, fd, data, timeout); - + if (selectResult == -1) { throwIOExceptionWithSslErrors(env, -1, error, "Connect error"); - free_ssl(env, object); - free_ssl_ctx(env, object); + SSL_clear(ssl); return (jboolean) false; } else if (selectResult == 0) { throwSocketTimeoutException(env, "SSL handshake timed out"); + SSL_clear(ssl); freeSslErrorState(); - free_ssl(env, object); - free_ssl_ctx(env, object); return (jboolean) false; } } else { LOGE("Unknown error %d during connect", error); break; } - } - } + } + } if (ret != 1) { /* @@ -1555,80 +1668,72 @@ static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect( if (sslErrorCode != SSL_ERROR_ZERO_RETURN) { throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "SSL handshake failure"); - free_ssl(env, object); - free_ssl_ctx(env, object); + SSL_clear(ssl); return (jboolean) false; } } if (ssl_session != NULL) { ret = SSL_session_reused(ssl); - // if (ret == 1) LOGD("A session was reused"); - // else LOGD("A new session was negotiated"); + // if (ret == 1) LOGD("Session %p was reused", ssl_session); + // else LOGD("Session %p was not reused, using new session %p", ssl_session, SSL_get_session(ssl)); return (jboolean) ret; } else { - // LOGD("A new session was negotiated"); + // LOGD("New session %p was negotiated", SSL_get_session(ssl)); return (jboolean) 0; } // LOGD("LEAVE connect"); } -static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jobject object, +static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jclass, jint jssl) { - return (jint) SSL_get1_session((SSL *) jssl); + return (jint) SSL_get1_session((SSL*) jssl); } -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jobject object, - jobject socketObject, jint jssl_ctx, jboolean client_mode) +static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jclass, + jint ssl_address, jobject socketObject) { - int sd, ret; - BIO *bio; - SSL *ssl; - SSL_CTX *ssl_ctx; - jsse_ssl_app_data_t appdata; - - ssl_ctx = (SSL_CTX *)jssl_ctx; + SSL* serverSocketSsl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address)); + if (serverSocketSsl == NULL) { + throwIOExceptionWithSslErrors(env, 0, 0, + "Unusable SSL structure"); + return NULL; + } - ssl = create_ssl(env, object, ssl_ctx); + SSL* ssl = SSL_dup(serverSocketSsl); if (ssl == NULL) { throwIOExceptionWithSslErrors(env, 0, 0, "Unable to create SSL structure"); - return; + return NULL; } jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl); if (socketImplObject == NULL) { - free_ssl(env, object); throwIOExceptionStr(env, "couldn't get the socket impl from the socket"); - return; + return NULL; } jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD); if (fdObject == NULL) { - free_ssl(env, object); throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl"); - return; + return NULL; } - sd = jniGetFDFromFileDescriptor(env, fdObject); - - bio = BIO_new_socket(sd, BIO_NOCLOSE); - - /* The parameter client_mode must be 1 */ - if (client_mode != 0) - client_mode = 1; - BIO_set_ssl_mode(bio, client_mode); + int sd = jniGetFDFromFileDescriptor(env, fdObject); + BIO* bio = BIO_new_socket(sd, BIO_NOCLOSE); SSL_set_bio(ssl, bio, bio); /* - * Fill in the appdata structure needed for the certificate callback and - * store this in the SSL application data slot. + * Fill in the stack allocated appdata structure needed for the + * certificate callback and store this in the SSL application data + * slot. */ + jsse_ssl_app_data_t appdata; appdata.env = env; - appdata.object = object; + appdata.object = socketObject; SSL_set_app_data(ssl, &appdata); /* @@ -1636,7 +1741,7 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEn * Maybe we need to deal with all the special SSL error cases (WANT_*), * just like we do for SSL_connect(). But currently it is looking ok. */ - ret = SSL_accept(ssl); + int ret = SSL_accept(ssl); /* * Clear the SSL application data slot again, so we can safely use it for @@ -1660,8 +1765,8 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEn throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "Trouble accepting connection"); } - free_ssl(env, object); - return; + SSL_clear(ssl); + return NULL; } else if (ret < 0) { /* * Translate the error and throw exception. We are sure it is an error @@ -1670,8 +1775,8 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEn int sslErrorCode = SSL_get_error(ssl, ret); throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "Trouble accepting connection"); - free_ssl(env, object); - return; + SSL_clear(ssl); + return NULL; } /* @@ -1682,8 +1787,8 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEn int mode = fcntl(fd, F_GETFL); if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) { throwIOExceptionStr(env, "Unable to make socket non blocking"); - free_ssl(env, object); - return; + SSL_clear(ssl); + return NULL; } /* @@ -1691,126 +1796,11 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEn */ if (sslCreateAppData(ssl) == -1) { throwIOExceptionStr(env, "Unable to create application data"); - free_ssl(env, object); - return; - } -} - -/** - * Loads the desired protocol for the OpenSSL client and enables it. - * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1. - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols(JNIEnv* env, jobject object, - jlong protocol) -{ - if (protocol != 0x00000000L) { - if (protocol & SSL_OP_NO_SSLv3) - LOGD("SSL_OP_NO_SSLv3 is set"); - if (protocol & SSL_OP_NO_TLSv1) - LOGD("SSL_OP_NO_TLSv1 is set"); - - SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx); - int options = SSL_CTX_get_options(ctx); - options |= protocol; // Note: SSLv2 disabled earlier. - SSL_CTX_set_options(ctx, options); - } -} - -static jobjectArray makeCipherList(JNIEnv* env, SSL* ssl) { - STACK_OF(SSL_CIPHER)* cipher_list = SSL_get_ciphers(ssl); - // Count the ciphers. - int num = sk_SSL_CIPHER_num(cipher_list); - int cipherCount = 0; - for (int i = 0; i < num; ++i) { - SSL_CIPHER* cipher = sk_SSL_CIPHER_value(cipher_list, i); - if (strcmp(SSL_CIPHER_get_version(cipher), SSL_TXT_SSLV2) == 0) { - // openssl-1.0.0 includes duplicate names for SSLv2 and SSLv3 ciphers - continue; - } - ++cipherCount; - } - - // Create a String[]. - jclass stringClass = env->FindClass("java/lang/String"); - if (stringClass == NULL) { - return NULL; - } - jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL); - if (array == NULL) { + SSL_clear(ssl); return NULL; } - // Fill in the cipher names. - int cipherIndex = 0; - for (int i = 0; i < num; ++i) { - SSL_CIPHER* cipher = sk_SSL_CIPHER_value(cipher_list, i); - if (strcmp(SSL_CIPHER_get_version(cipher), SSL_TXT_SSLV2) == 0) { - continue; - } - env->SetObjectArrayElement(array, cipherIndex, env->NewStringUTF(cipher->name)); - ++cipherIndex; - } - return array; -} - -jobjectArray makeCipherList(JNIEnv* env, SSL_CTX* ssl_ctx) { - SSL* ssl = SSL_new(ssl_ctx); - if (ssl == NULL) { - return NULL; - } - jobjectArray result = makeCipherList(env, ssl); - SSL_free(ssl); - return result; -} - -/** - * Loads the ciphers suites that are supported by the OpenSSL client - * and returns them in a string array. - */ -static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites(JNIEnv* env, - jobject object) -{ - SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (ssl_ctx == NULL) { - return NULL; - } - jobjectArray result = makeCipherList(env, ssl_ctx); - SSL_CTX_free(ssl_ctx); - return result; -} - -/** - * Loads the ciphers suites that are enabled in the OpenSSL client - * and returns them in a string array. - */ -static jobjectArray OpenSSLSocketImpl_nativeGetEnabledCipherSuites(JNIEnv* env, - jclass, jint ssl_ctx_address) -{ - SSL_CTX* ssl_ctx = - reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); - return makeCipherList(env, ssl_ctx); -} - -void setEnabledCipherSuites(JNIEnv* env, jstring controlString, SSL_CTX* ssl_ctx) { - const char* str = env->GetStringUTFChars(controlString, NULL); - int rc = SSL_CTX_set_cipher_list(ssl_ctx, str); - env->ReleaseStringUTFChars(controlString, str); - if (rc == 0) { - freeSslErrorState(); - jniThrowException(env, "java/lang/IllegalArgumentException", - "Illegal cipher suite strings."); - } -} - -/** - * Sets the ciphers suites that are enabled in the OpenSSL client. - */ -static void OpenSSLSocketImpl_nativeSetEnabledCipherSuites(JNIEnv* env, jclass, - jint ssl_ctx_address, jstring controlString) -{ - SSL_CTX* ssl_ctx = - reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); - setEnabledCipherSuites(env, controlString, ssl_ctx); + return (jint) ssl; } #define SSL_aRSA 0x00000001L @@ -1826,24 +1816,18 @@ static void OpenSSLSocketImpl_nativeSetEnabledCipherSuites(JNIEnv* env, jclass, * Sets the client's crypto algorithms and authentication methods. */ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env, - jobject object) + jclass, jint ssl_address) { - SSL* ssl; - const SSL_CIPHER *cipher; - jstring ret; - char buf[512]; - unsigned long alg_auth; - const char *au; - - ssl = getSslPointer(env, object, true); + SSL* ssl = getSslPointer(env, ssl_address, true); if (ssl == NULL) { return NULL; } - cipher = SSL_get_current_cipher(ssl); + const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl); - alg_auth = cipher->algorithm_auth; + unsigned long alg_auth = cipher->algorithm_auth; + const char *au; switch (alg_auth) { case SSL_aRSA: au="RSA"; @@ -1874,7 +1858,7 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipheraut break; } - ret = env->NewStringUTF(au); + jstring ret = env->NewStringUTF(au); return ret; } @@ -1882,9 +1866,9 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipheraut /** * OpenSSL read function (1): only one chunk is read (returned as jint). */ -static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jobject object, jint timeout) +static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jclass, jint ssl_address, jint timeout) { - SSL *ssl = getSslPointer(env, object, true); + SSL* ssl = getSslPointer(env, ssl_address, true); if (ssl == NULL) { return 0; } @@ -1914,12 +1898,12 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* } /** - * OpenSSL read function (2): read into buffer at offset n chunks. + * OpenSSL read function (2): read into buffer at offset n chunks. * Returns 1 (success) or value <= 0 (failure). */ -static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jobject obj, jbyteArray dest, jint offset, jint len, jint timeout) +static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jclass, jint ssl_address, jbyteArray dest, jint offset, jint len, jint timeout) { - SSL *ssl = getSslPointer(env, obj, true); + SSL* ssl = getSslPointer(env, ssl_address, true); if (ssl == NULL) { return 0; } @@ -1949,9 +1933,9 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEn /** * OpenSSL write function (1): only one chunk is written. */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jobject object, jint b) +static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jclass, jint ssl_address, jint b) { - SSL *ssl = getSslPointer(env, object, true); + SSL* ssl = getSslPointer(env, ssl_address, true); if (ssl == NULL) { return; } @@ -1971,12 +1955,12 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv } /** - * OpenSSL write function (2): write into buffer at offset n chunks. + * OpenSSL write function (2): write into buffer at offset n chunks. */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jobject obj, - jbyteArray dest, jint offset, jint len) +static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jclass, + jint ssl_address, jbyteArray dest, jint offset, jint len) { - SSL *ssl = getSslPointer(env, obj, true); + SSL* ssl = getSslPointer(env, ssl_address, true); if (ssl == NULL) { return; } @@ -1984,8 +1968,7 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIE jbyte* bytes = env->GetByteArrayElements(dest, NULL); int returnCode = 0; int errorCode = 0; - int timeout = (int)env->GetIntField(obj, field_Socket_timeout); - int ret = sslWrite(ssl, (const char *) (bytes + offset), len, + int ret = sslWrite(ssl, (const char *) (bytes + offset), len, &returnCode, &errorCode); env->ReleaseByteArrayElements(dest, bytes, 0); @@ -2000,11 +1983,11 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIE } /** - * Interrupt any pending IO before closing the socket. + * Interrupt any pending IO before closing the socket. */ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt( - JNIEnv* env, jobject object) { - SSL *ssl = getSslPointer(env, object, false); + JNIEnv* env, jclass, jint ssl_address) { + SSL* ssl = getSslPointer(env, ssl_address, false); if (ssl == NULL) { return; } @@ -2024,11 +2007,11 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt( } /** - * OpenSSL close SSL socket function. + * OpenSSL close SSL socket function. */ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close( - JNIEnv* env, jobject object) { - SSL *ssl = getSslPointer(env, object, false); + JNIEnv* env, jclass, jint ssl_address) { + SSL* ssl = getSslPointer(env, ssl_address, false); if (ssl == NULL) { return; } @@ -2068,24 +2051,14 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close( * Everything else is a real error condition. We should * let the Java layer know about this by throwing an * exception. - */ + */ int sslErrorCode = SSL_get_error(ssl, ret); throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "SSL shutdown failed"); break; } + SSL_clear(ssl); freeSslErrorState(); - free_ssl(env, object); - free_ssl_ctx(env, object); -} - -/** - * OpenSSL free SSL socket function. - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free(JNIEnv* env, jobject object) -{ - free_ssl(env, object); - free_ssl_ctx(env, object); } /** @@ -2148,151 +2121,20 @@ static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignatu static JNINativeMethod sSocketImplMethods[] = { - {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic}, - {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init}, - {"nativeconnect", "(ILjava/net/Socket;ZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect}, + {"nativeconnect", "(ILjava/net/Socket;IZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect}, {"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession}, - {"nativeread", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read}, - {"nativeread", "([BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba}, - {"nativewrite", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write}, - {"nativewrite", "([BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba}, - {"nativeaccept", "(Ljava/net/Socket;IZ)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept}, - {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols}, - {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites}, - {"nativeGetEnabledCipherSuites", "(I)[Ljava/lang/String;", (void*) OpenSSLSocketImpl_nativeGetEnabledCipherSuites}, - {"nativeSetEnabledCipherSuites", "(ILjava/lang/String;)V", (void*) OpenSSLSocketImpl_nativeSetEnabledCipherSuites}, - {"nativecipherauthenticationmethod", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod}, - {"nativeinterrupt", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt}, - {"nativeclose", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close}, - {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free}, + {"nativeread", "(II)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read}, + {"nativeread", "(I[BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba}, + {"nativewrite", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write}, + {"nativewrite", "(I[BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba}, + {"nativeaccept", "(ILjava/net/Socket;)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept}, + {"nativecipherauthenticationmethod", "(I)Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod}, + {"nativeinterrupt", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt}, + {"nativeclose", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close}, {"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature}, }; /** - * Module scope variables initialized during JNI registration. - */ -static jfieldID field_ServerSocket_ssl_ctx; - -/** - * Initialization phase of OpenSSL: Loads the Error strings, the crypto algorithms and reset the OpenSSL library - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic(JNIEnv* env, jobject obj) -{ - SSL_load_error_strings(); - ERR_load_crypto_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); -} - -/** - * Initialization phase for a server socket with OpenSSL. The server's private key and X509 certificate are read and - * the Linux /dev/random file is loaded as RNG for the session keys. - * - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(JNIEnv* env, jobject object, - jstring privatekey, jstring certificates, jbyteArray seed) -{ - SSL_CTX *ssl_ctx; - const char *privatekeychar; - const char *certificateschar; - EVP_PKEY * privatekeyevp; - - BIO *privatekeybio; - BIO *certificatesbio; - - // 'seed == null' when no SecureRandom Object is set - // in the SSLContext. - if (seed != NULL) { - jbyte* randseed = env->GetByteArrayElements(seed, NULL); - RAND_seed((unsigned char*) randseed, 1024); - env->ReleaseByteArrayElements(seed, randseed, 0); - } else { - RAND_load_file("/dev/urandom", 1024); - } - - ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); - - privatekeychar = env->GetStringUTFChars((jstring)privatekey, NULL); - privatekeybio = BIO_new_mem_buf((void*)privatekeychar, -1); - - privatekeyevp = PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL); - env->ReleaseStringUTFChars(privatekey, privatekeychar); - - if (privatekeyevp == NULL) { - LOGE(ERR_error_string(ERR_get_error(), NULL)); - throwIOExceptionStr(env, "Error parsing the private key"); - return; - } - - certificateschar = env->GetStringUTFChars((jstring)certificates, NULL); - certificatesbio = BIO_new_mem_buf((void*)certificateschar, -1); - - X509 * certificatesx509 = PEM_read_bio_X509(certificatesbio, NULL, 0, NULL); - env->ReleaseStringUTFChars(certificates, certificateschar); - - if (certificatesx509 == NULL) { - LOGE(ERR_error_string(ERR_get_error(), NULL)); - throwIOExceptionStr(env, "Error parsing the certificates"); - return; - } - - if (!SSL_CTX_use_certificate(ssl_ctx, certificatesx509)) { - LOGE(ERR_error_string(ERR_get_error(), NULL)); - throwIOExceptionStr(env, "Error setting the certificates"); - return; - } - - if (!SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp)) { - LOGE(ERR_error_string(ERR_get_error(), NULL)); - throwIOExceptionStr(env, "Error setting the private key"); - return; - } - - if (!SSL_CTX_check_private_key(ssl_ctx)) { - LOGE(ERR_error_string(ERR_get_error(), NULL)); - throwIOExceptionStr(env, "Error checking private key"); - return; - } - - env->SetIntField(object, field_ServerSocket_ssl_ctx, (int)ssl_ctx); -} - -/** - * Loads the desired protocol for the OpenSSL server and enables it. - * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1. - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols(JNIEnv* env, - jobject object, jlong protocol) -{ - if (protocol != 0x00000000L) { - if (protocol & SSL_OP_NO_SSLv3) - LOGD("SSL_OP_NO_SSLv3 is set"); - if (protocol & SSL_OP_NO_TLSv1) - LOGD("SSL_OP_NO_TLSv1 is set"); - - SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ServerSocket_ssl_ctx); - SSL_CTX_set_options((SSL_CTX*)ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|(long)protocol); - } -} - -/** - * Loads the ciphers suites that are supported by the OpenSSL server - * and returns them in a string array. - */ -static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites(JNIEnv* env, - jobject object) -{ - SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if (ssl_ctx == NULL) { - return NULL; - } - jobjectArray result = makeCipherList(env, ssl_ctx); - SSL_CTX_free(ssl_ctx); - return result; -} - -/** * Gives an array back containing all the X509 certificate's bytes. */ static jobjectArray getcertificatebytes(JNIEnv* env, @@ -2356,79 +2198,64 @@ static jobjectArray getcertificatebytes(JNIEnv* env, */ static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx) { - SSL *ssl; - jsse_ssl_app_data_t *appdata; - jclass cls; - - jobjectArray objectArray; - /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */ - ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl); + jsse_ssl_app_data_t* appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl); - cls = appdata->env->GetObjectClass(appdata->object); + jclass cls = appdata->env->GetObjectClass(appdata->object); - jmethodID methodID = appdata->env->GetMethodID(cls, "verify_callback", "([[B)I"); + jmethodID methodID = appdata->env->GetMethodID(cls, "verifyCertificateChain", "([[B)Z"); - objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted); + jobjectArray objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted); - appdata->env->CallIntMethod(appdata->object, methodID, objectArray); + jboolean verified = appdata->env->CallBooleanMethod(appdata->object, methodID, objectArray); - return 1; + return (verified) ? 1 : 0; } /** * Sets the client's credentials and the depth of theirs verification. */ static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env, - jobject object, jint value) -{ - SSL_CTX *ssl_ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx); - SSL_CTX_set_verify(ssl_ctx, (int)value, verify_callback); -} - -/** - * The actual SSL context is reset. - */ -static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree(JNIEnv* env, jobject object) + jclass, jint ssl_address, jint value) { - SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx); - SSL_CTX_free(ctx); - env->SetIntField(object, field_ServerSocket_ssl_ctx, 0); + SSL* ssl = getSslPointer(env, ssl_address, true); + if (ssl == NULL) { + return; + } + SSL_set_verify(ssl, (int)value, verify_callback); } static JNINativeMethod sServerSocketImplMethods[] = { - {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic}, - {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init}, - {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols}, - {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites}, - {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth}, - {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree} + {"nativesetclientauth", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth}, }; -static jfieldID field_Session_session; - -static SSL_SESSION* getSslSessionPointer(JNIEnv* env, jobject object) { - return reinterpret_cast<SSL_SESSION*>(env->GetIntField(object, field_Session_session)); +/** + * Our implementation of what might be considered + * SSL_SESSION_get_peer_cert_chain + */ +static STACK_OF(X509)* SSL_SESSION_get_peer_cert_chain(SSL_CTX* ssl_ctx, SSL_SESSION* ssl_session) { + SSL* ssl = SSL_new(ssl_ctx); + SSL_set_session(ssl, ssl_session); + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); + SSL_free(ssl); + return chain; } // Fills a byte[][] with the peer certificates in the chain. static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env, - jobject object, jint jssl) + jclass, jint ssl_ctx_address, jint ssl_session_address) { - SSL_SESSION* ssl_session = getSslSessionPointer(env, object); - SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - SSL* ssl = SSL_new(ssl_ctx); - - SSL_set_session(ssl, ssl_session); - - STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); + SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address)); + if (ssl_ctx == NULL) { + jniThrowNullPointerException(env, "SSL_CTX is null"); + return NULL; + } + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); + STACK_OF(X509)* chain = SSL_SESSION_get_peer_cert_chain(ssl_ctx, ssl_session); jobjectArray objectArray = getcertificatebytes(env, chain); - - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); return objectArray; } @@ -2437,8 +2264,8 @@ static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env, * not certificates). Returns a byte[] containing the DER-encoded state. * See apache mod_ssl. */ -static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jobject object) { - SSL_SESSION* ssl_session = getSslSessionPointer(env, object); +static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jclass, jint ssl_session_address) { + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); if (ssl_session == NULL) { return NULL; } @@ -2463,7 +2290,7 @@ static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jobject object) { /** * Deserialize the session. */ -static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jobject object, jbyteArray bytes, jint size) { +static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jclass, jbyteArray bytes, jint size) { if (bytes == NULL) { return 0; } @@ -2479,8 +2306,8 @@ static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jobject object, /** * Gets and returns in a byte array the ID of the actual SSL session. */ -static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jobject object) { - SSL_SESSION* ssl_session = getSslSessionPointer(env, object); +static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jclass, jint ssl_session_address) { + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); jbyteArray result = env->NewByteArray(ssl_session->session_id_length); if (result != NULL) { @@ -2495,68 +2322,61 @@ static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jobject object) { * Gets and returns in a long integer the creation's time of the * actual SSL session. */ -static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jobject object) { - SSL_SESSION* ssl_session = getSslSessionPointer(env, object); +static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jclass, jint ssl_session_address) { + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); jlong result = SSL_SESSION_get_time(ssl_session); result *= 1000; // OpenSSL uses seconds, Java uses milliseconds. return result; } /** + * Our implementation of what might be considered + * SSL_SESSION_get_version, based on SSL_get_version. + * See get_ssl_version above. + */ +static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) { + return get_ssl_version(ssl_session->ssl_version); +} + +/** * Gets and returns in a string the version of the SSL protocol. If it * returns the string "unknown" it means that no connection is established. */ -static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jobject object) { - SSL_SESSION* ssl_session = getSslSessionPointer(env, object); - SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - SSL* ssl = SSL_new(ssl_ctx); - - SSL_set_session(ssl, ssl_session); - - const char* protocol = SSL_get_version(ssl); +static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jclass, jint ssl_session_address) { + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); + const char* protocol = SSL_SESSION_get_version(ssl_session); jstring result = env->NewStringUTF(protocol); - - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); return result; } /** * Gets and returns in a string the set of ciphers the actual SSL session uses. */ -static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jobject object) { - SSL_SESSION* ssl_session = getSslSessionPointer(env, object); - SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - SSL* ssl = SSL_new(ssl_ctx); - - SSL_set_session(ssl, ssl_session); - - const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl); +static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jclass, jint ssl_session_address) { + SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address)); + const SSL_CIPHER* cipher = ssl_session->cipher; jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher)); - - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); return result; } /** * Frees the SSL session. */ -static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jobject object, jint session) { - LOGD("Freeing OpenSSL session"); +static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jclass, jint session) { SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session); + // LOGD("Freeing OpenSSL session %p", session); SSL_SESSION_free(ssl_session); } static JNINativeMethod sSessionImplMethods[] = { { "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl }, - { "getCipherSuite", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite }, - { "getCreationTime", "()J", (void*) OpenSSLSessionImpl_getCreationTime }, - { "getEncoded", "()[B", (void*) OpenSSLSessionImpl_getEncoded }, - { "getId", "()[B", (void*) OpenSSLSessionImpl_getId }, - { "getPeerCertificatesImpl", "()[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl }, - { "getProtocol", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol }, - { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl } + { "getCipherSuite", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite }, + { "getCreationTime", "(I)J", (void*) OpenSSLSessionImpl_getCreationTime }, + { "getEncoded", "(I)[B", (void*) OpenSSLSessionImpl_getEncoded }, + { "getId", "(I)[B", (void*) OpenSSLSessionImpl_getId }, + { "getPeerCertificatesImpl", "(II)[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl }, + { "getProtocol", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol }, + { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl }, }; typedef struct { @@ -2571,15 +2391,7 @@ static JNINativeClass sClasses[] = { { "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl", sServerSocketImplMethods, NELEM(sServerSocketImplMethods) }, { "org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl", sSessionImplMethods, NELEM(sSessionImplMethods) }, }; - -/* - * Peforms the actual registration of the native methods. - * Also looks up the fields that belong to the class (if - * any) and stores the field IDs. Simply remove what you - * don't need. - */ -extern "C" int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) { - +int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) { // Register org.apache.harmony.xnet.provider.jsse.* methods for (int i = 0; i < NELEM(sClasses); i++) { int result = jniRegisterNativeMethods(env, @@ -2591,18 +2403,6 @@ extern "C" int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEn } } - // java.io.FileDescriptor - jclass fileDescriptor = env->FindClass("java/io/FileDescriptor"); - if (fileDescriptor == NULL) { - LOGE("Can't find java/io/FileDescriptor"); - return -1; - } - field_FileDescriptor_descriptor = env->GetFieldID(fileDescriptor, "descriptor", "I"); - if (field_FileDescriptor_descriptor == NULL) { - LOGE("Can't find FileDescriptor.descriptor"); - return -1; - } - // java.net.Socket jclass socket = env->FindClass("java/net/Socket"); if (socket == NULL) { @@ -2627,58 +2427,5 @@ extern "C" int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEn return -1; } - // org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl - jclass socketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl"); - if (socketImpl == NULL) { - LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl"); - return -1; - } - // Note: do these after the registration of native methods, because - // there is a static method "initstatic" that's called when the - // OpenSSLSocketImpl class is first loaded, and that required - // a native method to be associated with it. - field_Socket_ssl_ctx = env->GetFieldID(socketImpl, "ssl_ctx", "I"); - if (field_Socket_ssl_ctx == NULL) { - LOGE("Can't find OpenSSLSocketImpl.ssl_ctx"); - return -1; - } - field_Socket_ssl = env->GetFieldID(socketImpl, "ssl", "I"); - if (field_Socket_ssl == NULL) { - LOGE("Can't find OpenSSLSocketImpl.ssl"); - return -1; - } - field_Socket_timeout = env->GetFieldID(socketImpl, "timeout", "I"); - if (field_Socket_timeout == NULL) { - LOGE("Can't find OpenSSLSocketImpl.timeout"); - return -1; - } - - // org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl - jclass serverSocketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl"); - if (serverSocketImpl == NULL) { - LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl"); - return -1; - } - // Note: do these after the registration of native methods, because - // there is a static method "initstatic" that's called when the - // OpenSSLServerSocketImpl class is first loaded, and that required - // a native method to be associated with it. - field_ServerSocket_ssl_ctx = env->GetFieldID(serverSocketImpl, "ssl_ctx", "I"); - if (field_ServerSocket_ssl_ctx == NULL) { - LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx"); - return -1; - } - - // org.apache.harmony.xnet.provider.jsse.OpenSSLSessionImpl - jclass sessionImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl"); - if (sessionImpl == NULL) { - return -1; - } - field_Session_session = env->GetFieldID(sessionImpl, "session", "I"); - if (field_Session_session == NULL) { - LOGE("Can't find OpenSSLSessionImpl.session"); - return -1; - } - return 0; } diff --git a/x-net/src/test/java/tests/api/javax/net/AllTests.java b/x-net/src/test/java/tests/api/javax/net/AllTests.java index 90c2a6a..35cd6f5 100644 --- a/x-net/src/test/java/tests/api/javax/net/AllTests.java +++ b/x-net/src/test/java/tests/api/javax/net/AllTests.java @@ -24,13 +24,8 @@ import junit.framework.TestSuite; */ public class AllTests { - - public static void main(String[] args) { - junit.textui.TestRunner.run(AllTests.suite()); - } - public static Test suite() { - TestSuite suite = tests.TestSuiteFactory.createTestSuite("All tests for package tests.api.javax.net;"); + TestSuite suite = new TestSuite("All tests for package tests.api.javax.net;"); // $JUnit-BEGIN$ suite.addTestSuite(ServerSocketFactoryTest.class); diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/AllTests.java b/x-net/src/test/java/tests/api/javax/net/ssl/AllTests.java index ecfe83f..5f9c32d 100644 --- a/x-net/src/test/java/tests/api/javax/net/ssl/AllTests.java +++ b/x-net/src/test/java/tests/api/javax/net/ssl/AllTests.java @@ -24,13 +24,8 @@ import junit.framework.TestSuite; */ public class AllTests { - - public static void main(String[] args) { - junit.textui.TestRunner.run(AllTests.suite()); - } - public static Test suite() { - TestSuite suite = tests.TestSuiteFactory.createTestSuite("All tests for package tests.api.javax.net.ssl;"); + TestSuite suite = new TestSuite("All tests for package tests.api.javax.net.ssl;"); // $JUnit-BEGIN$ suite.addTestSuite(CertPathTrustManagerParametersTest.class); diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/SSLEngineTest.java b/x-net/src/test/java/tests/api/javax/net/ssl/SSLEngineTest.java index f659919..8205059 100644 --- a/x-net/src/test/java/tests/api/javax/net/ssl/SSLEngineTest.java +++ b/x-net/src/test/java/tests/api/javax/net/ssl/SSLEngineTest.java @@ -62,10 +62,6 @@ public class SSLEngineTest extends TestCase { private HandshakeHandler clientEngine; private HandshakeHandler serverEngine; - public static void main(String[] args) { - junit.textui.TestRunner.run(SSLEngineTest.class); - } - @Override protected void setUp() throws Exception { super.setUp(); TestEnvironment.reset(); diff --git a/x-net/src/test/java/tests/xnet/AllTests.java b/x-net/src/test/java/tests/xnet/AllTests.java index 53b11dc..04a2ed6 100644 --- a/x-net/src/test/java/tests/xnet/AllTests.java +++ b/x-net/src/test/java/tests/xnet/AllTests.java @@ -23,14 +23,8 @@ import junit.framework.TestSuite; * Test suite that includes all tests for the Math project. */ public class AllTests { - - public static void main(String[] args) { - junit.textui.TestRunner.run(AllTests.suite()); - } - public static Test suite() { - TestSuite suite = tests.TestSuiteFactory.createTestSuite( - "All javax.net and javax.net.ssl test suites"); + TestSuite suite = new TestSuite("All javax.net and javax.net.ssl test suites"); // $JUnit-BEGIN$ suite.addTest(tests.api.javax.net.AllTests.suite()); suite.addTest(tests.api.javax.net.ssl.AllTests.suite()); |