/* * Copyright (C) 2007 The Android Open Source Project * * Licensed 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. */ package org.conscrypt; import org.conscrypt.util.ArrayUtils; import org.conscrypt.ct.CTVerifier; import org.conscrypt.ct.CTVerificationResult; import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.security.InvalidKeyException; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.interfaces.ECKey; import java.security.spec.ECParameterSpec; import java.util.ArrayList; import javax.crypto.SecretKey; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLProtocolException; import javax.net.ssl.SSLSession; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; /** * Implementation of the class OpenSSLSocketImpl based on OpenSSL. *
* Extensions to SSLSocket include: *
This method needs to be invoked before the handshake starts. * * @throws IllegalStateException if this is a client socket or if the handshake has already * started. */ public void setChannelIdEnabled(boolean enabled) { if (getUseClientMode()) { throw new IllegalStateException("Client mode"); } synchronized (stateLock) { if (state != STATE_NEW) { throw new IllegalStateException( "Could not enable/disable Channel ID after the initial handshake has" + " begun."); } } sslParameters.channelIdEnabled = enabled; } /** * Gets the TLS Channel ID for this server socket. Channel ID is only available once the * handshake completes. * * @return channel ID or {@code null} if not available. * * @throws IllegalStateException if this is a client socket or if the handshake has not yet * completed. * @throws SSLException if channel ID is available but could not be obtained. */ public byte[] getChannelId() throws SSLException { if (getUseClientMode()) { throw new IllegalStateException("Client mode"); } synchronized (stateLock) { if (state != STATE_READY) { throw new IllegalStateException( "Channel ID is only available after handshake completes"); } } return NativeCrypto.SSL_get_tls_channel_id(sslNativePointer); } /** * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket. * *
This method needs to be invoked before the handshake starts. * * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables * TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST * P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1). * * @throws IllegalStateException if this is a server socket or if the handshake has already * started. */ public void setChannelIdPrivateKey(PrivateKey privateKey) { if (!getUseClientMode()) { throw new IllegalStateException("Server mode"); } synchronized (stateLock) { if (state != STATE_NEW) { throw new IllegalStateException( "Could not change Channel ID private key after the initial handshake has" + " begun."); } } if (privateKey == null) { sslParameters.channelIdEnabled = false; channelIdPrivateKey = null; } else { sslParameters.channelIdEnabled = true; try { ECParameterSpec ecParams = null; if (privateKey instanceof ECKey) { ecParams = ((ECKey) privateKey).getParams(); } if (ecParams == null) { // Assume this is a P-256 key, as specified in the contract of this method. ecParams = OpenSSLECGroupContext.getCurveByName("prime256v1").getECParameterSpec(); } channelIdPrivateKey = OpenSSLKey.fromECPrivateKeyForTLSStackOnly(privateKey, ecParams); } catch (InvalidKeyException e) { // Will have error in startHandshake } } } @Override public boolean getUseClientMode() { return sslParameters.getUseClientMode(); } @Override public void setUseClientMode(boolean mode) { synchronized (stateLock) { if (state != STATE_NEW) { throw new IllegalArgumentException( "Could not change the mode after the initial handshake has begun."); } } sslParameters.setUseClientMode(mode); } @Override public boolean getWantClientAuth() { return sslParameters.getWantClientAuth(); } @Override public boolean getNeedClientAuth() { return sslParameters.getNeedClientAuth(); } @Override public void setNeedClientAuth(boolean need) { sslParameters.setNeedClientAuth(need); } @Override public void setWantClientAuth(boolean want) { sslParameters.setWantClientAuth(want); } @Override public void sendUrgentData(int data) throws IOException { throw new SocketException("Method sendUrgentData() is not supported."); } @Override public void setOOBInline(boolean on) throws SocketException { throw new SocketException("Methods sendUrgentData, setOOBInline are not supported."); } @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException { if (socket != this) { socket.setSoTimeout(readTimeoutMilliseconds); } else { super.setSoTimeout(readTimeoutMilliseconds); } this.readTimeoutMilliseconds = readTimeoutMilliseconds; } @Override public int getSoTimeout() throws SocketException { return readTimeoutMilliseconds; } /** * Note write timeouts are not part of the javax.net.ssl.SSLSocket API */ public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException { this.writeTimeoutMilliseconds = writeTimeoutMilliseconds; Platform.setSocketWriteTimeout(this, writeTimeoutMilliseconds); } /** * Note write timeouts are not part of the javax.net.ssl.SSLSocket API */ public int getSoWriteTimeout() throws SocketException { return writeTimeoutMilliseconds; } /** * Set the handshake timeout on this socket. This timeout is specified in * milliseconds and will be used only during the handshake process. */ public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException { this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds; } @Override public void close() throws IOException { // TODO: Close SSL sockets using a background thread so they close gracefully. SSLInputStream sslInputStream = null; SSLOutputStream sslOutputStream = null; synchronized (stateLock) { if (state == STATE_CLOSED) { // close() has already been called, so do nothing and return. return; } int oldState = state; state = STATE_CLOSED; if (oldState == STATE_NEW) { // The handshake hasn't been started yet, so there's no OpenSSL related // state to clean up. We still need to close the underlying socket if // we're wrapping it and were asked to autoClose. closeUnderlyingSocket(); stateLock.notifyAll(); return; } if (oldState != STATE_READY && oldState != STATE_READY_HANDSHAKE_CUT_THROUGH) { // If we're in these states, we still haven't returned from startHandshake. // We call SSL_interrupt so that we can interrupt SSL_do_handshake and then // set the state to STATE_CLOSED. startHandshake will handle all cleanup // after SSL_do_handshake returns, so we don't have anything to do here. NativeCrypto.SSL_interrupt(sslNativePointer); stateLock.notifyAll(); return; } stateLock.notifyAll(); // We've already returned from startHandshake, so we potentially have // input and output streams to clean up. sslInputStream = is; sslOutputStream = os; } // Don't bother interrupting unless we have something to interrupt. if (sslInputStream != null || sslOutputStream != null) { NativeCrypto.SSL_interrupt(sslNativePointer); } // Wait for the input and output streams to finish any reads they have in // progress. If there are no reads in progress at this point, future reads will // throw because state == STATE_CLOSED if (sslInputStream != null) { sslInputStream.awaitPendingOps(); } if (sslOutputStream != null) { sslOutputStream.awaitPendingOps(); } shutdownAndFreeSslNative(); } private void shutdownAndFreeSslNative() throws IOException { try { Platform.blockGuardOnNetwork(); NativeCrypto.SSL_shutdown(sslNativePointer, Platform.getFileDescriptor(socket), this); } catch (IOException ignored) { /* * Note that although close() can throw * IOException, the RI does not throw if there * is problem sending a "close notify" which * can happen if the underlying socket is closed. */ } finally { free(); closeUnderlyingSocket(); } } private void closeUnderlyingSocket() throws IOException { if (socket != this) { if (autoClose && !socket.isClosed()) { socket.close(); } } else { if (!super.isClosed()) { super.close(); } } } private void free() { if (sslNativePointer == 0) { return; } NativeCrypto.SSL_free(sslNativePointer); sslNativePointer = 0; Platform.closeGuardClose(guard); } @Override protected void finalize() throws Throwable { try { /* * Just worry about our own state. Notably we do not try and * close anything. The SocketImpl, either our own * PlainSocketImpl, or the Socket we are wrapping, will do * that. This might mean we do not properly SSL_shutdown, but * if you want to do that, properly close the socket yourself. * * The reason why we don't try to SSL_shutdown, is that there * can be a race between finalizers where the PlainSocketImpl * finalizer runs first and closes the socket. However, in the * meanwhile, the underlying file descriptor could be reused * for another purpose. If we call SSL_shutdown, the * underlying socket BIOs still have the old file descriptor * and will write the close notify to some unsuspecting * reader. */ if (guard != null) { Platform.closeGuardWarnIfOpen(guard); } free(); } finally { super.finalize(); } } /* @Override */ public FileDescriptor getFileDescriptor$() { if (socket == this) { return Platform.getFileDescriptorFromSSLSocket(this); } else { return Platform.getFileDescriptor(socket); } } /** * Returns the protocol agreed upon by client and server, or null if no * protocol was agreed upon. */ public byte[] getNpnSelectedProtocol() { return NativeCrypto.SSL_get_npn_negotiated_protocol(sslNativePointer); } /** * Returns the protocol agreed upon by client and server, or {@code null} if * no protocol was agreed upon. */ public byte[] getAlpnSelectedProtocol() { return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer); } /** * Sets the list of protocols this peer is interested in. If null no * protocols will be used. * * @param npnProtocols a non-empty array of protocol names. From * SSL_select_next_proto, "vector of 8-bit, length prefixed byte * strings. The length byte itself is not included in the length. A byte * string of length 0 is invalid. No byte string may be truncated.". */ public void setNpnProtocols(byte[] npnProtocols) { if (npnProtocols != null && npnProtocols.length == 0) { throw new IllegalArgumentException("npnProtocols.length == 0"); } sslParameters.npnProtocols = npnProtocols; } /** * Sets the list of protocols this peer is interested in. If the list is * {@code null}, no protocols will be used. * * @param alpnProtocols a non-empty array of protocol names. From * SSL_select_next_proto, "vector of 8-bit, length prefixed byte * strings. The length byte itself is not included in the length. * A byte string of length 0 is invalid. No byte string may be * truncated.". */ public void setAlpnProtocols(byte[] alpnProtocols) { if (alpnProtocols != null && alpnProtocols.length == 0) { throw new IllegalArgumentException("alpnProtocols.length == 0"); } sslParameters.alpnProtocols = alpnProtocols; } @Override public SSLParameters getSSLParameters() { SSLParameters params = super.getSSLParameters(); Platform.getSSLParameters(params, sslParameters, this); return params; } @Override public void setSSLParameters(SSLParameters p) { super.setSSLParameters(p); Platform.setSSLParameters(p, sslParameters, this); } @Override public String chooseServerAlias(X509KeyManager keyManager, String keyType) { return keyManager.chooseServerAlias(keyType, null, this); } @Override public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers, String[] keyTypes) { return keyManager.chooseClientAlias(keyTypes, null, this); } @Override public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) { return keyManager.chooseServerKeyIdentityHint(this); } @Override public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) { return keyManager.chooseClientKeyIdentity(identityHint, this); } @Override public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) { return keyManager.getKey(identityHint, identity, this); } }