/* * 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. */ package org.conscrypt; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; /** * Implementation of SSLEngine. * @see javax.net.ssl.SSLEngine class documentation for more information. */ public class SSLEngineImpl extends SSLEngine { // indicates if peer mode was set private boolean peer_mode_was_set = false; // indicates if handshake has been started private boolean handshake_started = false; // indicates if inbound operations finished private boolean isInboundDone = false; // indicates if outbound operations finished private boolean isOutboundDone = false; // indicates if close_notify alert had been sent to another peer private boolean close_notify_was_sent = false; // indicates if close_notify alert had been received from another peer private boolean close_notify_was_received = false; // indicates if engine was closed (it means that // all the works on it are done, except (probably) some finalizing work) private boolean engine_was_closed = false; // indicates if engine was shutted down (it means that // all cleaning work had been done and the engine is not operable) private boolean engine_was_shutteddown = false; // record protocol to be used protected SSLRecordProtocol recordProtocol; // input stream for record protocol private SSLBufferedInput recProtIS; // handshake protocol to be used private HandshakeProtocol handshakeProtocol; // alert protocol to be used private AlertProtocol alertProtocol; // place where application data will be stored private SSLEngineAppData appData; // outcoming application data stream private SSLEngineDataStream dataStream = new SSLEngineDataStream(); // active session object private SSLSessionImpl session; // peer configuration parameters protected SSLParametersImpl sslParameters; // in case of emergency situations when data could not be // placed in destination buffers it will be stored in this // fields private byte[] remaining_wrapped_data = null; private byte[] remaining_hsh_data = null; // logger private Logger.Stream logger = Logger.getStream("engine"); protected SSLEngineImpl(SSLParametersImpl sslParameters) { this.sslParameters = sslParameters; } protected SSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) { super(host, port); this.sslParameters = sslParameters; } /** * Starts the handshake. * @throws SSLException * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation * for more information */ @Override public void beginHandshake() throws SSLException { if (engine_was_closed) { throw new SSLException("Engine has already been closed."); } if (!peer_mode_was_set) { throw new IllegalStateException("Client/Server mode was not set"); } if (!handshake_started) { handshake_started = true; if (getUseClientMode()) { handshakeProtocol = new ClientHandshakeImpl(this); } else { handshakeProtocol = new ServerHandshakeImpl(this); } appData = new SSLEngineAppData(); alertProtocol = new AlertProtocol(); recProtIS = new SSLBufferedInput(); recordProtocol = new SSLRecordProtocol(handshakeProtocol, alertProtocol, recProtIS, appData); } handshakeProtocol.start(); } /** * Closes inbound operations of this engine * @throws SSLException * @see javax.net.ssl.SSLEngine#closeInbound() method documentation * for more information */ @Override public void closeInbound() throws SSLException { if (logger != null) { logger.println("closeInbound() "+isInboundDone); } if (isInboundDone) { return; } isInboundDone = true; engine_was_closed = true; if (handshake_started) { if (!close_notify_was_received) { if (session != null) { session.invalidate(); } alertProtocol.alert(AlertProtocol.FATAL, AlertProtocol.INTERNAL_ERROR); throw new SSLException("Inbound is closed before close_notify " + "alert has been received."); } } else { // engine is closing before initial handshake has been made shutdown(); } } /** * Closes outbound operations of this engine * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation * for more information */ @Override public void closeOutbound() { if (logger != null) { logger.println("closeOutbound() "+isOutboundDone); } if (isOutboundDone) { return; } isOutboundDone = true; if (handshake_started) { // initial handshake had been started alertProtocol.alert(AlertProtocol.WARNING, AlertProtocol.CLOSE_NOTIFY); close_notify_was_sent = true; } else { // engine is closing before initial handshake has been made shutdown(); } engine_was_closed = true; } /** * Returns handshake's delegated tasks to be run * @return the delegated task to be executed. * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation * for more information */ @Override public Runnable getDelegatedTask() { return handshakeProtocol.getTask(); } /** * Returns names of supported cipher suites. * @return array of strings containing the names of supported cipher suites * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method * documentation for more information */ @Override public String[] getSupportedCipherSuites() { return CipherSuite.getSupportedCipherSuiteNames(); } // --------------- SSLParameters based methods --------------------- /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method * documentation for more information */ @Override public String[] getEnabledCipherSuites() { return sslParameters.getEnabledCipherSuites(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[]) method * documentation for more information */ @Override public void setEnabledCipherSuites(String[] suites) { sslParameters.setEnabledCipherSuites(suites); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method * documentation for more information */ @Override public String[] getSupportedProtocols() { return ProtocolVersion.supportedProtocols.clone(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method * documentation for more information */ @Override public String[] getEnabledProtocols() { return sslParameters.getEnabledProtocols(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String[]) method * documentation for more information */ @Override public void setEnabledProtocols(String[] protocols) { sslParameters.setEnabledProtocols(protocols); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method * documentation for more information */ @Override public void setUseClientMode(boolean mode) { if (handshake_started) { throw new IllegalArgumentException( "Could not change the mode after the initial handshake has begun."); } sslParameters.setUseClientMode(mode); peer_mode_was_set = true; } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getUseClientMode() method * documentation for more information */ @Override public boolean getUseClientMode() { return sslParameters.getUseClientMode(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method * documentation for more information */ @Override public void setNeedClientAuth(boolean need) { sslParameters.setNeedClientAuth(need); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method * documentation for more information */ @Override public boolean getNeedClientAuth() { return sslParameters.getNeedClientAuth(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method * documentation for more information */ @Override public void setWantClientAuth(boolean want) { sslParameters.setWantClientAuth(want); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getWantClientAuth() method * documentation for more information */ @Override public boolean getWantClientAuth() { return sslParameters.getWantClientAuth(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method * documentation for more information */ @Override public void setEnableSessionCreation(boolean flag) { sslParameters.setEnableSessionCreation(flag); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method * documentation for more information */ @Override public boolean getEnableSessionCreation() { return sslParameters.getEnableSessionCreation(); } // ----------------------------------------------------------------- /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method * documentation for more information */ @Override public SSLEngineResult.HandshakeStatus getHandshakeStatus() { if (!handshake_started || engine_was_shutteddown) { // initial handshake has not been started yet return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; } if (alertProtocol.hasAlert()) { // need to send an alert return SSLEngineResult.HandshakeStatus.NEED_WRAP; } if (close_notify_was_sent && !close_notify_was_received) { // waiting for "close_notify" response return SSLEngineResult.HandshakeStatus.NEED_UNWRAP; } return handshakeProtocol.getStatus(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#getSession() method * documentation for more information */ @Override public SSLSession getSession() { if (session != null) { return session; } return SSLSessionImpl.getNullSession(); } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#isInboundDone() method * documentation for more information */ @Override public boolean isInboundDone() { return isInboundDone || engine_was_closed; } /** * This method works according to the specification of implemented class. * @see javax.net.ssl.SSLEngine#isOutboundDone() method * documentation for more information */ @Override public boolean isOutboundDone() { return isOutboundDone; } /** * Decodes one complete SSL/TLS record provided in the source buffer. * If decoded record contained application data, this data will * be placed in the destination buffers. * For more information about TLS record fragmentation see * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2. * @param src source buffer containing SSL/TLS record. * @param dsts destination buffers to place received application data. * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int) * method documentation for more information */ @Override public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws SSLException { if (engine_was_shutteddown) { return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0); } if ((src == null) || (dsts == null)) { throw new IllegalStateException( "Some of the input parameters are null"); } if (!handshake_started) { beginHandshake(); } SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus(); // If is is initial handshake or connection closure stage, // check if this call was made in spite of handshake status if ((session == null || engine_was_closed) && ( handshakeStatus.equals( SSLEngineResult.HandshakeStatus.NEED_WRAP) || handshakeStatus.equals( SSLEngineResult.HandshakeStatus.NEED_TASK))) { return new SSLEngineResult( getEngineStatus(), handshakeStatus, 0, 0); } if (src.remaining() < recordProtocol.getMinRecordSize()) { return new SSLEngineResult( SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0); } try { src.mark(); // check the destination buffers and count their capacity int capacity = 0; for (int i=offset; i