blob: 77f823885b8c567d71e4a92feb108b7c51235f12 [file] [log] [blame]
/*
* 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.apache.harmony.xnet.provider.jsse;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import org.apache.harmony.nio.AddressUtil;
/**
* 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 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 shut down (it means that
// all cleaning work had been done and the engine is not operable)
private boolean engine_was_shutdown = 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;
private boolean need_alert_wrap = false;
// active session object
private SSLSessionImpl session;
// peer configuration parameters
protected SSLParameters sslParameters;
// logger
private Logger.Stream logger = Logger.getStream("engine");
// Pointer to the SSL struct
private long SSL;
// Pointer to the custom struct used for this engine
private long SSLEngineAddress;
private HandshakeStatus handshakeStatus;
static {
initImpl();
}
private static native void initImpl();
private static native long initSSL(long context);
private static native long initSSLEngine(long context);
private static native HandshakeStatus connectImpl(long ssl);
private static native HandshakeStatus acceptImpl(long ssl);
private static native SSLEngineResult wrapImpl(long ssl, long sslEngineAddress,
long src_address, int src_len, long dst_address, int dst_len);
private static native SSLEngineResult unwrapImpl(long ssl, long sslEngineAddress,
long src_address, int src_len, long dst_address, int dst_len);
/**
* Ctor
* @param sslParameters: SSLParameters
*/
protected SSLEngineImpl(SSLParameters sslParameters) {
super();
this.sslParameters = sslParameters;
SSL = initSSL(sslParameters.getSSLContextAddress());
SSLEngineAddress = initSSLEngine(SSL);
}
/**
* Ctor
* @param host: String
* @param port: int
* @param sslParameters: SSLParameters
*/
protected SSLEngineImpl(String host, int port, SSLParameters sslParameters) {
super(host, port);
this.sslParameters = sslParameters;
SSL = initSSL(sslParameters.getSSLContextAddress());
SSLEngineAddress = initSSLEngine(SSL);
}
/**
* 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 (!getEnableSessionCreation()) {
handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
throw new SSLException("New session creation is disabled");
}
// TODO: need to repeat connect/accept if status was waiting on wrap/unwrap previously?
if (!handshake_started) {
handshake_started = true;
SSLSessionContextImpl sessionContext;
if (sslParameters.getUseClientMode()) {
if (logger != null) {
logger.println("SSLEngineImpl: CLIENT connecting");
}
handshakeStatus = connectImpl(SSL);
sessionContext = sslParameters.getClientSessionContext();
} else {
if (logger != null) {
logger.println("SSLEngineImpl: SERVER accepting connection");
}
handshakeStatus = acceptImpl(SSL);
sessionContext = sslParameters.getServerSessionContext();
}
session = new SSLSessionImpl(sslParameters, SSL);
sessionContext.putSession(session);
}
}
private static native void shutdownImpl(long SSL);
private static native void closeInboundImpl(long SSLEngineAddress);
/**
* 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();
}
if (!close_notify_was_sent) {
shutdownImpl(SSL);
close_notify_was_sent = true;
}
need_alert_wrap = true;
throw new SSLException("Inbound is closed before close_notify alert has been received.");
}
closeInboundImpl(SSLEngineAddress);
} 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;
engine_was_closed = true;
if (handshake_started) {
if (!close_notify_was_sent) {
if (!close_notify_was_sent) {
shutdownImpl(SSL);
close_notify_was_sent = true;
}
need_alert_wrap = true;
}
} else {
// engine is closing before initial handshake has been made
shutdown();
}
}
/**
* 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 null;
//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 sslParameters.getSupportedCipherSuites();
}
// --------------- 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(SSL, 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 sslParameters.getSupportedProtocols();
}
/**
* 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(SSL, 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(SSL, 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(SSL, 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 HandshakeStatus getHandshakeStatus() {
if (!handshake_started || engine_was_shutdown) {
// initial handshake has not been started yet
return HandshakeStatus.NOT_HANDSHAKING;
}
if (need_alert_wrap) {
return HandshakeStatus.NEED_WRAP;
}
if (close_notify_was_sent && !close_notify_was_received) {
// waiting for "close_notify" response
return HandshakeStatus.NEED_UNWRAP;
}
return handshakeStatus;
}
/**
* 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.NULL_SESSION;
}
/**
* 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_shutdown) {
return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
HandshakeStatus.NOT_HANDSHAKING, 0, 0);
}
if ((src == null) || (dsts == null)) {
throw new IllegalStateException(
"Some of the input parameters are null");
}
if (!handshake_started) {
beginHandshake();
}
// get direct addresses to the buffers
// only use the first buffer at the moment
ByteBuffer src_temp_buffer = null, dst_temp_buffer = null;
long src_address, dst_address;
int src_length = src.remaining();
int dst_length = dsts[0].remaining();
if (src.isDirect()) {
src_address = AddressUtil.getDirectBufferAddress(src) + src.position();
} else {
// create a temporary buffer and copy the contents
src_temp_buffer = ByteBuffer.allocateDirect(src_length);
src_temp_buffer.put(src.array(), src.position(), src_length);
src_temp_buffer.rewind();
src_address = AddressUtil.getDirectBufferAddress(src_temp_buffer);
}
if (dsts[0].isDirect()) {
dst_address = AddressUtil.getDirectBufferAddress(dsts[0]) + dsts[0].position();
} else {
dst_temp_buffer = ByteBuffer.allocateDirect(dst_length);
dst_address = AddressUtil.getDirectBufferAddress(dst_temp_buffer);
}
SSLEngineResult result = unwrapImpl(SSL, SSLEngineAddress, src_address, src_length, dst_address, dst_length);
// update the buffers contents and positions
src.position(src.position() + result.bytesConsumed());
if (dst_temp_buffer == null) {
dsts[0].position(dsts[0].position() + result.bytesConsumed());
} else {
// if a temporary buffer was used, copy buffer contents
int position = dsts[0].position();
dsts[0].put(dst_temp_buffer);
// adjust position as not all bytes may have been written
dsts[0].position(position + result.bytesProduced());
}
// update handshake status
handshakeStatus = result.getHandshakeStatus();
if (handshakeStatus == HandshakeStatus.FINISHED) {
// If we've just completed the handshake, refresh the data in the SSLSession
session.refreshSessionData(null, sslParameters, SSL);
handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
}
return result;
}
/**
* Encodes the application data into SSL/TLS record. If handshake status
* of the engine differs from NOT_HANDSHAKING the operation can work
* without consuming of the source data.
* For more information about TLS record fragmentation see
* TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
* @param srcs the source buffers with application data to be encoded
* into SSL/TLS record.
* @param offset the offset in the destination buffers array pointing to
* the first buffer with the source data.
* @param len specifies the maximum number of buffers to be procesed.
* @param dst the destination buffer where encoded data will be placed.
* @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method
* documentation for more information
*/
@Override
public SSLEngineResult wrap(ByteBuffer[] srcs, int offset,
int len, ByteBuffer dst) throws SSLException {
if (engine_was_shutdown) {
return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
HandshakeStatus.NOT_HANDSHAKING, 0, 0);
}
if ((srcs == null) || (dst == null)) {
throw new IllegalStateException(
"Some of the input parameters are null");
}
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
if (!handshake_started) {
beginHandshake();
}
if (need_alert_wrap) {
need_alert_wrap = false;
}
// get direct addresses to the buffers
// only use the first buffer at the moment
ByteBuffer src_temp_buffer = null, dst_temp_buffer = null;
long src_address, dst_address;
int src_length = srcs[0].remaining();
int dst_length = dst.remaining();
if (srcs[0].isDirect()) {
src_address = AddressUtil.getDirectBufferAddress(srcs[0]) + srcs[0].position();
} else {
// create a temporary buffer and copy the contents
src_temp_buffer = ByteBuffer.allocateDirect(src_length);
src_temp_buffer.put(srcs[0].array(), srcs[0].position(), src_length);
src_temp_buffer.rewind();
src_address = AddressUtil.getDirectBufferAddress(src_temp_buffer);
}
if (dst.isDirect()) {
dst_address = AddressUtil.getDirectBufferAddress(dst) + dst.position();
} else {
dst_temp_buffer = ByteBuffer.allocateDirect(dst_length);
dst_address = AddressUtil.getDirectBufferAddress(dst_temp_buffer);
}
SSLEngineResult result = wrapImpl(SSL, SSLEngineAddress, src_address, src_length, dst_address, dst_length);
// update the buffers contents and positions
srcs[0].position(srcs[0].position() + result.bytesConsumed());
if (dst_temp_buffer == null) {
dst.position(dst.position() + result.bytesConsumed());
} else {
// if a temporary buffer was used, copy buffer contents
int position = dst.position();
dst.put(dst_temp_buffer);
// adjust position as not all bytes may have been written
dst.position(position + result.bytesProduced());
}
// update handshake status
handshakeStatus = result.getHandshakeStatus();
if (handshakeStatus == HandshakeStatus.FINISHED) {
// If we've just completed the handshake, refresh the data in
// the SSLSession and set the handshake status to NOT_HANDSHAKING
session.refreshSessionData(null, sslParameters, SSL);
handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
}
return result;
}
// Shutdown the engine and makes all cleanup work.
private void shutdown() {
engine_was_closed = true;
engine_was_shutdown = true;
isOutboundDone = true;
isInboundDone = true;
}
private SSLEngineResult.Status getEngineStatus() {
return (engine_was_closed)
? SSLEngineResult.Status.CLOSED
: SSLEngineResult.Status.OK;
}
}