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=1a9dfdad95d94ea00ad733de6cca8987ced0b535;hp=b41156147f7d3ea04066903e4794820437ab9099;hb=8a6695d355f0d84ff1af5eaf958f735b5f86cd50;hpb=889a586b7db63f36ea608caa54bba8d7a222ef45 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 b41156147f..1a9dfdad95 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. * @@ -10,6 +9,8 @@ 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; @@ -40,308 +41,390 @@ 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 - - public SecureMessageReadWriteService(SocketChannel socket, Selector selector) throws Exception { - this.socket = socket; - this.selector = selector; - this.factory = new BasicFactory(); - - createSecureChannel(socket); - createBuffers(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; + + 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"); + 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(); + } + if ((trustStorePassword == null) || trustStorePassword.isEmpty()) { + throw new FileNotFoundException( + "controllerTrustStorePassword not specified in ./configuration/config.ini"); + } KeyStore ks = KeyStore.getInstance("JKS"); KeyStore ts = KeyStore.getInstance("JKS"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - ks.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray()); - ts.load(new FileInputStream(trustStoreFile), trustStorePassword.toCharArray()); + 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 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; - } - 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()); + } + + peerAppData.flip(); + msgs = factory.parseMessages(peerAppData); + if (peerAppData.hasRemaining()) { + peerAppData.compact(); + } else { + peerAppData.clear(); + } + + 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()); + this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize()); + this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize()); + } + + @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; + } } }