From 99d2c8a0e2a8ea4a819ee31d658be6e01dfcd1df Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Fri, 2 May 2014 10:43:07 +0200 Subject: [PATCH] BUG-938 Fix negotiation error when using exi EXIConfirmation handler is now added to pipeline before start-exi is sent. Add test with secure netconf server + exi Change-Id: Ie975a2ecf31cfbb8c1fd4e25e8f41876e0552a00 Signed-off-by: Maros Marsalek --- .../NetconfClientSessionNegotiator.java | 10 +- .../netconf/it/NetconfITSecureTest.java | 107 +++++++++++++----- .../netconf/ssh/NetconfSSHServer.java | 2 +- .../netconf/ssh/threads/SocketThread.java | 2 +- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java index 0c5935b571..4e2393171a 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java @@ -69,7 +69,7 @@ public class NetconfClientSessionNegotiator extends tryToInitiateExi(session, startExiMessage); // Exi is not supported, release session immediately } else { - logger.debug("Netconf session {} isn't capable using exi.", session); + logger.debug("Netconf session {} isn't capable of using exi.", session); negotiationSuccessful(session); } } @@ -80,16 +80,18 @@ public class NetconfClientSessionNegotiator extends * @param startExiMessage */ void tryToInitiateExi(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) { + channel.pipeline().addAfter(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, + ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER, + new ExiConfirmationInboundHandler(session, startExiMessage)); + session.sendMessage(startExiMessage).addListener(new ChannelFutureListener() { @Override public void operationComplete(final ChannelFuture f) { if (!f.isSuccess()) { logger.warn("Failed to send start-exi message {} on session {}", startExiMessage, this, f.cause()); + channel.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER); } else { logger.trace("Start-exi message {} sent to socket on session {}", startExiMessage, this); - channel.pipeline().addAfter( - AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER, - new ExiConfirmationInboundHandler(session, startExiMessage)); } } }); diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index cc9e8c367a..44d8420ba4 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -8,45 +8,67 @@ package org.opendaylight.controller.netconf.it; -import io.netty.channel.ChannelFuture; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.util.Collection; +import java.util.List; + +import junit.framework.Assert; + import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; import org.opendaylight.controller.config.spi.ModuleFactory; +import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.client.NetconfSshClientDispatcher; import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import java.io.IOException; -import java.io.InputStream; -import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.Collection; -import java.util.List; +import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; +import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; +import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; +import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.controller.sal.authorization.AuthResultEnum; +import org.opendaylight.controller.usermanager.IUserManager; + +import ch.ethz.ssh2.Connection; +import io.netty.channel.ChannelFuture; public class NetconfITSecureTest extends AbstractNetconfConfigTest { private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024); + private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); private DefaultCommitNotificationProducer commitNot; private NetconfServerDispatcher dispatchS; - + private NetconfSSHServer sshServer; + private NetconfMessage getConfig; @Before public void setUp() throws Exception { - super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext,getModuleFactories().toArray( + this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); + + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, getModuleFactories().toArray( new ModuleFactory[0]))); NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); @@ -56,8 +78,13 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { dispatchS = createDispatcher(factoriesListener); - ChannelFuture s = dispatchS.createServer(tlsAddress); + ChannelFuture s = dispatchS.createServer(tcpAddress); s.await(); + + sshServer = NetconfSSHServer.start(tlsAddress.getPort(), tcpAddress, getAuthProvider()); + Thread thread = new Thread(sshServer); + thread.setDaemon(true); + thread.start(); } private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) { @@ -66,19 +93,10 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { @After public void tearDown() throws Exception { + sshServer.stop(); commitNot.close(); } - private SSLContext getSslContext() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, - IOException, UnrecoverableKeyException, KeyManagementException { - final InputStream keyStore = getClass().getResourceAsStream("/keystore.jks"); - final InputStream trustStore = getClass().getResourceAsStream("/keystore.jks"); - SSLContext sslContext = SSLUtil.initializeSecureContext("password", keyStore, trustStore, KeyManagerFactory.getDefaultAlgorithm()); - keyStore.close(); - trustStore.close(); - return sslContext; - } - private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { final Collection yangDependencies = NetconfITTest.getBasicYangs(); return new HardcodedYangStoreService(yangDependencies); @@ -90,9 +108,36 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { @Test public void testSecure() throws Exception { - NetconfClientDispatcher dispatch = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup, 5000); - try (TestingNetconfClient netconfClient = new TestingNetconfClient("tls-client", tlsAddress, 4000, dispatch)) { - + NetconfClientDispatcher dispatch = new NetconfSshClientDispatcher(getAuthHandler(), nettyThreadgroup, nettyThreadgroup, 5000); + try (TestingNetconfClient netconfClient = new TestingNetconfClient("tls-client", tlsAddress, 4000, dispatch)) { + NetconfMessage response = netconfClient.sendMessage(getConfig); + Assert.assertFalse("Unexpected error message " + XmlUtil.toString(response.getDocument()), + NetconfMessageUtil.isErrorMessage(response)); } + + dispatch.close(); + } + + public AuthProvider getAuthProvider() throws Exception { + final IUserManager userManager = mock(IUserManager.class); + doReturn(AuthResultEnum.AUTH_ACCEPT).when(userManager).authenticate(anyString(), anyString()); + + final File privateKeyFile = Files.createTempFile("tmp-netconf-test", "pk").toFile(); + privateKeyFile.deleteOnExit(); + String privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile); + return new AuthProvider(userManager, privateKeyPEMString); + } + + public AuthenticationHandler getAuthHandler() throws IOException { + final AuthenticationHandler authHandler = mock(AuthenticationHandler.class); + doAnswer(new Answer() { + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + Connection conn = (Connection) invocation.getArguments()[0]; + conn.authenticateWithPassword("user", "pwd"); + return null; + } + }).when(authHandler).authenticate(any(Connection.class)); + return authHandler; } } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java index 51054dd938..adba548b91 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java @@ -70,7 +70,7 @@ public final class NetconfSSHServer implements Runnable { try { SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet(),authProvider); } catch (IOException e) { - logger.error("Exception occurred during socket thread initialization {}",e); + logger.error("Exception occurred during socket thread initialization {}", e); } } } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java index d6566c8ffa..04639cb36f 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java @@ -66,7 +66,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser try { conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf"); } catch (Exception e) { - logger.debug("Server authentication setup failed."); + logger.warn("Server authentication setup failed.", e); } conn.setAuthenticationCallback(this); conn.setServerConnectionCallback(this); -- 2.36.6