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;h=d60bcab8b32fdf299d68505c348fe5b4fc5ec831;hp=bb8ba04fb8cab97ede7cd7d03d3c4aa25a4800fc;hb=5c851155dce76a47bcc89c1a8ce40989737dbaf9;hpb=9cdfa8361e3b4d3e969821aa4de5c4862e22a025 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 index bb8ba04fb8..d60bcab8b3 100644 --- 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 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * @@ -20,13 +19,15 @@ 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 javax.net.ssl.SSLEngineResult.HandshakeStatus; + import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageReadWrite; import org.openflow.protocol.OFMessage; import org.openflow.protocol.factory.BasicFactory; @@ -42,73 +43,78 @@ public class SecureMessageReadWriteService implements IMessageReadWrite { .getLogger(SecureMessageReadWriteService.class); private Selector selector; - private SelectionKey clientSelectionKey; 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 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; - 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) { - stop(); - throw e; - } + 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 - */ + /** + * 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 keyStorePassword = System.getProperty("controllerKeyStorePassword"); - String trustStoreFile = System.getProperty("controllerTrustStore"); - String trustStorePassword = System.getProperty("controllerTrustStorePassword"); - - if (keyStoreFile != null) { - keyStoreFile = keyStoreFile.trim(); - } - if ((keyStoreFile == null) || keyStoreFile.isEmpty()) { - throw new FileNotFoundException("controllerKeyStore not specified in ./configuration/config.ini"); - } - if (keyStorePassword != null) { - keyStorePassword = keyStorePassword.trim(); - } - if ((keyStorePassword == null) || keyStorePassword.isEmpty()) { - throw new FileNotFoundException("controllerKeyStorePassword not specified in ./configuration/config.ini"); - } - if (trustStoreFile != null) { - trustStoreFile = trustStoreFile.trim(); - } - if ((trustStoreFile == null) || trustStoreFile.isEmpty()) { - throw new FileNotFoundException("controllerTrustStore not specified in ./configuration/config.ini"); - } - if (trustStorePassword != null) { - trustStorePassword = trustStorePassword.trim(); - } + String keyStoreFile = System.getProperty("controllerKeyStore"); + String keyStorePassword = System + .getProperty("controllerKeyStorePassword"); + String trustStoreFile = System.getProperty("controllerTrustStore"); + String trustStorePassword = System + .getProperty("controllerTrustStorePassword"); + + if (keyStoreFile != null) { + keyStoreFile = keyStoreFile.trim(); + } + if ((keyStoreFile == null) || keyStoreFile.isEmpty()) { + throw new FileNotFoundException("TLS KeyStore file not found."); + } + if (keyStorePassword != null) { + keyStorePassword = keyStorePassword.trim(); + } + if ((keyStorePassword == null) || keyStorePassword.isEmpty()) { + throw new FileNotFoundException("TLS KeyStore Password not provided."); + } + if (trustStoreFile != null) { + trustStoreFile = trustStoreFile.trim(); + } + if ((trustStoreFile == null) || trustStoreFile.isEmpty()) { + throw new FileNotFoundException("TLS TrustStore file not found"); + } + if (trustStorePassword != null) { + trustStorePassword = trustStorePassword.trim(); + } if ((trustStorePassword == null) || trustStorePassword.isEmpty()) { - throw new FileNotFoundException("controllerTrustStorePassword not specified in ./configuration/config.ini"); - } - + throw new FileNotFoundException("TLS TrustStore Password not provided."); + } + KeyStore ks = KeyStore.getInstance("JKS"); KeyStore ts = KeyStore.getInstance("JKS"); - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + 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()); @@ -119,285 +125,309 @@ public class SecureMessageReadWriteService implements IMessageReadWrite { SecureRandom random = new SecureRandom(); random.nextInt(); - SSLContext sslContext = SSLContext.getInstance("TLS"); + SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), random); - sslEngine = sslContext.createSSLEngine(); - sslEngine.setUseClientMode(false); - sslEngine.setNeedClientAuth(true); - - // Do initial handshake - doHandshake(socket, sslEngine); - - this.clientSelectionKey = this.socket.register(this.selector, - SelectionKey.OP_READ); + 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 - */ + /** + * 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.clientSelectionKey = this.socket.register( - this.selector, SelectionKey.OP_WRITE, this); - } else { - myAppData.clear(); - this.clientSelectionKey = this.socket.register( - this.selector, SelectionKey.OP_READ, this); - } - - logger.trace("Message sent: {}", msg.toString()); - } + 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 - */ + /** + * 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.clientSelectionKey = this.socket.register(this.selector, - SelectionKey.OP_WRITE, this); - } else { - myAppData.clear(); - this.clientSelectionKey = this.socket.register(this.selector, - SelectionKey.OP_READ, this); - } - } + 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 - */ + /** + * 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; - } + if (!socket.isOpen()) { + return null; + } - List msgs = 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()); - } - - peerAppData.flip(); - msgs = factory.parseMessages(peerAppData); - if (peerAppData.hasRemaining()) { - peerAppData.compact(); - } else { - peerAppData.clear(); - } - - this.clientSelectionKey = this.socket.register( - this.selector, SelectionKey.OP_READ, this); - + 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. + * 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 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 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()); - this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize()); - this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize()); + SSLSession session = engine.getSession(); + this.myAppData = ByteBuffer + .allocate(session.getApplicationBufferSize()); + this.peerAppData = ByteBuffer.allocate(session + .getApplicationBufferSize() * 2); + this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize()); + this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize() * 2); } - @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; - } - } + @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; + } + } }