X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fprotocol_plugins%2Fopenflow%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fprotocol_plugin%2Fopenflow%2Fcore%2Finternal%2FSecureMessageReadWriteService.java;fp=opendaylight%2Fprotocol_plugins%2Fopenflow%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fprotocol_plugin%2Fopenflow%2Fcore%2Finternal%2FSecureMessageReadWriteService.java;h=0000000000000000000000000000000000000000;hp=f27d30eaae620312a75a7da2d47fa878936f5ccd;hb=42c32160bfd41de57189bb246fec5ffb48ed8e9e;hpb=edf5bfcee83c750853253ccfd991ba7000f5f65b diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java deleted file mode 100644 index f27d30eaae..0000000000 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ - -package org.opendaylight.controller.protocol_plugin.openflow.core.internal; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousCloseException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.util.List; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManagerFactory; - -import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageReadWrite; -import org.openflow.protocol.OFMessage; -import org.openflow.protocol.factory.BasicFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class implements methods to read/write messages over an established - * socket channel. The data exchange is encrypted/decrypted by SSLEngine. - */ -public class SecureMessageReadWriteService implements IMessageReadWrite { - private static final Logger logger = LoggerFactory - .getLogger(SecureMessageReadWriteService.class); - - private Selector selector; - private SocketChannel socket; - private BasicFactory factory; - - private SSLEngine sslEngine; - private SSLEngineResult sslEngineResult; // results from sslEngine last operation - private ByteBuffer myAppData; // clear text message to be sent - private ByteBuffer myNetData; // encrypted message to be sent - private ByteBuffer peerAppData; // clear text message received from the - // switch - private ByteBuffer peerNetData; // encrypted message from the switch - private FileInputStream kfd = null, tfd = null; - private final String keyStoreFileDefault = "./configuration/tlsKeyStore"; - private final String trustStoreFileDefault = "./configuration/tlsTrustStore"; - private final String keyStorePasswordPropName = "controllerKeyStorePassword"; - private final String trustStorePasswordPropName = "controllerTrustStorePassword"; - private static String keyStorePassword = null; - private static String trustStorePassword = null; - - public SecureMessageReadWriteService(SocketChannel socket, Selector selector) - throws Exception { - this.socket = socket; - this.selector = selector; - this.factory = new BasicFactory(); - - try { - createSecureChannel(socket); - createBuffers(sslEngine); - } catch (Exception e) { - logger.warn("Failed to setup TLS connection {} {}", socket, e); - stop(); - throw e; - } - } - - /** - * Bring up secure channel using SSL Engine - * - * @param socket - * TCP socket channel - * @throws Exception - */ - private void createSecureChannel(SocketChannel socket) throws Exception { - String keyStoreFile = System.getProperty("controllerKeyStore"); - String trustStoreFile = System.getProperty("controllerTrustStore"); - String keyStorePasswordProp = System.getProperty(keyStorePasswordPropName); - String trustStorePasswordProp = System.getProperty(trustStorePasswordPropName); - - if (keyStoreFile != null) { - keyStoreFile = keyStoreFile.trim(); - } else { - keyStoreFile = keyStoreFileDefault; - } - if ((keyStoreFile == null) || keyStoreFile.isEmpty()) { - throw new FileNotFoundException("TLS KeyStore file not found."); - } - - if ((keyStorePassword == null) || ((keyStorePasswordProp != null) && !keyStorePasswordProp.isEmpty())) { - keyStorePassword = keyStorePasswordProp; - } - if (keyStorePassword != null) { - keyStorePassword = keyStorePassword.trim(); - System.setProperty(keyStorePasswordPropName, ""); - } - if ((keyStorePassword == null) || keyStorePassword.isEmpty()) { - throw new FileNotFoundException("TLS KeyStore Password not provided."); - } - if (trustStoreFile != null) { - trustStoreFile = trustStoreFile.trim(); - } else { - trustStoreFile = trustStoreFileDefault; - } - if ((trustStoreFile == null) || trustStoreFile.isEmpty()) { - throw new FileNotFoundException("TLS TrustStore file not found"); - } - - if ((trustStorePassword == null) || ((trustStorePasswordProp != null) && !trustStorePasswordProp.isEmpty())) { - trustStorePassword = trustStorePasswordProp; - } - if (trustStorePassword != null) { - trustStorePassword = trustStorePassword.trim(); - System.setProperty(trustStorePasswordPropName, ""); - } - if ((trustStorePassword == null) || trustStorePassword.isEmpty()) { - throw new FileNotFoundException("TLS TrustStore Password not provided."); - } - - KeyStore ks = KeyStore.getInstance("JKS"); - KeyStore ts = KeyStore.getInstance("JKS"); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kfd = new FileInputStream(keyStoreFile); - tfd = new FileInputStream(trustStoreFile); - ks.load(kfd, keyStorePassword.toCharArray()); - ts.load(tfd, trustStorePassword.toCharArray()); - kmf.init(ks, keyStorePassword.toCharArray()); - tmf.init(ts); - - SecureRandom random = new SecureRandom(); - random.nextInt(); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), random); - sslEngine = sslContext.createSSLEngine(); - sslEngine.setUseClientMode(false); - sslEngine.setNeedClientAuth(true); - sslEngine.setEnabledCipherSuites(new String[] { - "SSL_RSA_WITH_RC4_128_MD5", - "SSL_RSA_WITH_RC4_128_SHA", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "SSL_RSA_WITH_3DES_EDE_CBC_SHA", - "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", - "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", - "SSL_RSA_WITH_DES_CBC_SHA", - "SSL_DHE_RSA_WITH_DES_CBC_SHA", - "SSL_DHE_DSS_WITH_DES_CBC_SHA", - "SSL_RSA_EXPORT_WITH_RC4_40_MD5", - "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", - "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"}); - - // Do initial handshake - doHandshake(socket, sslEngine); - - this.socket.register(this.selector, SelectionKey.OP_READ); - } - - /** - * Sends the OF message out over the socket channel. The message is - * encrypted by SSL Engine. - * - * @param msg - * OF message to be sent - * @throws Exception - */ - @Override - public void asyncSend(OFMessage msg) throws Exception { - synchronized (myAppData) { - int msgLen = msg.getLengthU(); - if (myAppData.remaining() < msgLen) { - // increase the buffer size so that it can contain this message - ByteBuffer newBuffer = ByteBuffer.allocateDirect(myAppData - .capacity() + msgLen); - myAppData.flip(); - newBuffer.put(myAppData); - myAppData = newBuffer; - } - } - synchronized (myAppData) { - msg.writeTo(myAppData); - myAppData.flip(); - sslEngineResult = sslEngine.wrap(myAppData, myNetData); - logger.trace("asyncSend sslEngine wrap: {}", sslEngineResult); - runDelegatedTasks(sslEngineResult, sslEngine); - - if (!socket.isOpen()) { - return; - } - - myNetData.flip(); - socket.write(myNetData); - if (myNetData.hasRemaining()) { - myNetData.compact(); - } else { - myNetData.clear(); - } - - if (myAppData.hasRemaining()) { - myAppData.compact(); - this.socket.register(this.selector, SelectionKey.OP_WRITE, this); - } else { - myAppData.clear(); - this.socket.register(this.selector, SelectionKey.OP_READ, this); - } - - logger.trace("Message sent: {}", msg); - } - } - - /** - * Resumes sending the remaining messages in the outgoing buffer - * - * @throws Exception - */ - @Override - public void resumeSend() throws Exception { - synchronized (myAppData) { - myAppData.flip(); - sslEngineResult = sslEngine.wrap(myAppData, myNetData); - logger.trace("resumeSend sslEngine wrap: {}", sslEngineResult); - runDelegatedTasks(sslEngineResult, sslEngine); - - if (!socket.isOpen()) { - return; - } - - myNetData.flip(); - socket.write(myNetData); - if (myNetData.hasRemaining()) { - myNetData.compact(); - } else { - myNetData.clear(); - } - - if (myAppData.hasRemaining()) { - myAppData.compact(); - this.socket.register(this.selector, SelectionKey.OP_WRITE, this); - } else { - myAppData.clear(); - this.socket.register(this.selector, SelectionKey.OP_READ, this); - } - } - } - - /** - * Reads the incoming network data from the socket, decryptes them and then - * retrieves the OF messages. - * - * @return list of OF messages - * @throws Exception - */ - @Override - public List readMessages() throws Exception { - if (!socket.isOpen()) { - return null; - } - - List msgs = null; - int bytesRead = -1; - int countDown = 50; - - bytesRead = socket.read(peerNetData); - if (bytesRead < 0) { - logger.debug("Message read operation failed"); - throw new AsynchronousCloseException(); - } - - do { - peerNetData.flip(); - sslEngineResult = sslEngine.unwrap(peerNetData, peerAppData); - if (peerNetData.hasRemaining()) { - peerNetData.compact(); - } else { - peerNetData.clear(); - } - logger.trace("sslEngine unwrap result: {}", sslEngineResult); - runDelegatedTasks(sslEngineResult, sslEngine); - } while ((sslEngineResult.getStatus() == SSLEngineResult.Status.OK) - && peerNetData.hasRemaining() && (--countDown > 0)); - - if (countDown == 0) { - logger.trace("countDown reaches 0. peerNetData pos {} lim {}", - peerNetData.position(), peerNetData.limit()); - } - - try { - peerAppData.flip(); - msgs = factory.parseMessages(peerAppData); - if (peerAppData.hasRemaining()) { - peerAppData.compact(); - } else { - peerAppData.clear(); - } - } catch (Exception e) { - peerAppData.clear(); - logger.debug("Caught exception: ", e); - } - - this.socket.register(this.selector, SelectionKey.OP_READ, this); - - return msgs; - } - - /** - * If the result indicates that we have outstanding tasks to do, go ahead - * and run them in this thread. - */ - private void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) - throws Exception { - - if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { - Runnable runnable; - while ((runnable = engine.getDelegatedTask()) != null) { - logger.debug("\trunning delegated task..."); - runnable.run(); - } - HandshakeStatus hsStatus = engine.getHandshakeStatus(); - if (hsStatus == HandshakeStatus.NEED_TASK) { - throw new Exception("handshake shouldn't need additional tasks"); - } - logger.debug("\tnew HandshakeStatus: {}", hsStatus); - } - } - - private void doHandshake(SocketChannel socket, SSLEngine engine) - throws Exception { - SSLSession session = engine.getSession(); - ByteBuffer myAppData = ByteBuffer.allocate(session - .getApplicationBufferSize()); - ByteBuffer peerAppData = ByteBuffer.allocate(session - .getApplicationBufferSize()); - ByteBuffer myNetData = ByteBuffer.allocate(session - .getPacketBufferSize()); - ByteBuffer peerNetData = ByteBuffer.allocate(session - .getPacketBufferSize()); - - // Begin handshake - engine.beginHandshake(); - SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); - - // Process handshaking message - while (hs != SSLEngineResult.HandshakeStatus.FINISHED - && hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { - switch (hs) { - case NEED_UNWRAP: - // Receive handshaking data from peer - if (socket.read(peerNetData) < 0) { - throw new AsynchronousCloseException(); - } - - // Process incoming handshaking data - peerNetData.flip(); - SSLEngineResult res = engine.unwrap(peerNetData, peerAppData); - peerNetData.compact(); - hs = res.getHandshakeStatus(); - - // Check status - switch (res.getStatus()) { - case OK: - // Handle OK status - break; - } - break; - - case NEED_WRAP: - // Empty the local network packet buffer. - myNetData.clear(); - - // Generate handshaking data - res = engine.wrap(myAppData, myNetData); - hs = res.getHandshakeStatus(); - - // Check status - switch (res.getStatus()) { - case OK: - myNetData.flip(); - - // Send the handshaking data to peer - while (myNetData.hasRemaining()) { - if (socket.write(myNetData) < 0) { - throw new AsynchronousCloseException(); - } - } - break; - } - break; - - case NEED_TASK: - // Handle blocking tasks - Runnable runnable; - while ((runnable = engine.getDelegatedTask()) != null) { - logger.debug("\trunning delegated task..."); - runnable.run(); - } - hs = engine.getHandshakeStatus(); - if (hs == HandshakeStatus.NEED_TASK) { - throw new Exception( - "handshake shouldn't need additional tasks"); - } - logger.debug("\tnew HandshakeStatus: {}", hs); - break; - } - } - } - - private void createBuffers(SSLEngine engine) { - SSLSession session = engine.getSession(); - this.myAppData = ByteBuffer - .allocate(session.getApplicationBufferSize()); - this.peerAppData = ByteBuffer.allocate(session - .getApplicationBufferSize() * 20); - this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize()); - this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize() * 20); - } - - @Override - public void stop() throws IOException { - this.sslEngine = null; - this.sslEngineResult = null; - this.myAppData = null; - this.myNetData = null; - this.peerAppData = null; - this.peerNetData = null; - - if (this.kfd != null) { - this.kfd.close(); - this.kfd = null; - } - if (this.tfd != null) { - this.tfd.close(); - this.tfd = null; - } - } -}