From 43fcd744d0b7237fef50c85eca0d62245584aa7d Mon Sep 17 00:00:00 2001 From: Sebastien Pouliot Date: Wed, 19 Feb 2014 08:50:05 -0500 Subject: [PATCH] [SSL/TLS] Expose Client/Server callbacks that can be used to select/reorder SSL/TLS cipher suites used (for client or server) and add unit tests for them [#16327] --- mcs/class/System/System.Net/ServicePointManager.cs | 2 +- .../System/System.Net/ServicePointManager.extra.cs | 23 +++++ mcs/class/System/System.dll.sources | 1 + .../Test/System.Net.Security/SslStreamTest.cs | 105 +++++++++++++++++++-- mcs/class/System/mobile_System.dll.sources | 1 + 5 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 mcs/class/System/System.Net/ServicePointManager.extra.cs diff --git a/mcs/class/System/System.Net/ServicePointManager.cs b/mcs/class/System/System.Net/ServicePointManager.cs index 6e23af5ce04..ba9e9dc8750 100644 --- a/mcs/class/System/System.Net/ServicePointManager.cs +++ b/mcs/class/System/System.Net/ServicePointManager.cs @@ -78,7 +78,7 @@ using System.Diagnostics; namespace System.Net { - public class ServicePointManager { + public partial class ServicePointManager { class SPKey { Uri uri; // schema/host/port Uri proxy; diff --git a/mcs/class/System/System.Net/ServicePointManager.extra.cs b/mcs/class/System/System.Net/ServicePointManager.extra.cs new file mode 100644 index 00000000000..30443637854 --- /dev/null +++ b/mcs/class/System/System.Net/ServicePointManager.extra.cs @@ -0,0 +1,23 @@ +// +// Extra Mono-specific API for ServicePointManager +// +// Authors +// Sebastien Pouliot +// +// Copyright 2013-2014 Xamarin Inc. +// + +using System; +using System.Collections.Generic; + +namespace System.Net { + + public delegate IEnumerable CipherSuitesCallback (SecurityProtocolType protocol, IEnumerable allCiphers); + + public partial class ServicePointManager { + + public static CipherSuitesCallback ClientCipherSuitesCallback { get; set; } + + public static CipherSuitesCallback ServerCipherSuitesCallback { get; set; } + } +} \ No newline at end of file diff --git a/mcs/class/System/System.dll.sources b/mcs/class/System/System.dll.sources index 707ce3e5692..55cb9d9fb68 100644 --- a/mcs/class/System/System.dll.sources +++ b/mcs/class/System/System.dll.sources @@ -833,6 +833,7 @@ System.Net.Security/SslStream.cs System.Net.Security/SslPolicyErrors.cs System.Net/ServicePoint.cs System.Net/ServicePointManager.cs +System.Net/ServicePointManager.extra.cs System.Net/SocketAddress.cs System.Net/SocketPermissionAttribute.cs System.Net/SocketPermission.cs diff --git a/mcs/class/System/Test/System.Net.Security/SslStreamTest.cs b/mcs/class/System/Test/System.Net.Security/SslStreamTest.cs index 5bc31399b26..c9b067ef6ba 100644 --- a/mcs/class/System/Test/System.Net.Security/SslStreamTest.cs +++ b/mcs/class/System/Test/System.Net.Security/SslStreamTest.cs @@ -1,11 +1,13 @@ // -// SslStream.cs +// SslStreamTest.cs // - Unit tests for System.Net.Security.SslStream // -// Author: +// Authors: // Maciej Paszta (maciej.paszta@gmail.com) +// Sebastien Pouliot // // Copyright (C) Maciej Paszta, 2012 +// Copyright 2014 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -29,6 +31,8 @@ using NUnit.Framework; using System; +using System.Collections.Generic; +using System.IO; using System.Net.Sockets; using System.Net; using System.Net.Security; @@ -53,7 +57,13 @@ public class SslStreamTest { } [Test] //bug https://bugzilla.novell.com/show_bug.cgi?id=457120 - public void AuthenticateClientAndServer_ClientSendsNoData () { + public void AuthenticateClientAndServer_ClientSendsNoData () + { + AuthenticateClientAndServer (true, true); + } + + void AuthenticateClientAndServer (bool server, bool client) + { IPEndPoint endPoint = new IPEndPoint (IPAddress.Parse ("127.0.0.1"), 10000); ClientServerState state = new ClientServerState (); state.Client = new TcpClient (); @@ -61,14 +71,15 @@ public class SslStreamTest { state.Listener.Start (); state.ServerAuthenticated = new AutoResetEvent (false); state.ClientAuthenticated = new AutoResetEvent (false); + state.ServerIOException = !server; try { Thread serverThread = new Thread (() => StartServerAndAuthenticate (state)); serverThread.Start (null); Thread clientThread = new Thread (() => StartClientAndAuthenticate (state, endPoint)); clientThread.Start (null); - Assert.IsTrue (state.ServerAuthenticated.WaitOne (TimeSpan.FromSeconds (2)), + Assert.AreEqual (server, state.ServerAuthenticated.WaitOne (TimeSpan.FromSeconds (2)), "server not authenticated"); - Assert.IsTrue (state.ClientAuthenticated.WaitOne (TimeSpan.FromSeconds (2)), + Assert.AreEqual (client, state.ClientAuthenticated.WaitOne (TimeSpan.FromSeconds (2)), "client not authenticated"); } finally { if (state.ClientStream != null) @@ -82,6 +93,80 @@ public class SslStreamTest { } } + [Test] + public void ClientCipherSuitesCallback () + { + try { + ServicePointManager.ClientCipherSuitesCallback += (SecurityProtocolType p, IEnumerable allCiphers) => { + string prefix = p == SecurityProtocolType.Tls ? "TLS_" : "SSL_"; + return new List { prefix + "RSA_WITH_AES_128_CBC_SHA" }; + }; + // client will only offers AES 128 - that's fine since the server support it (and many more ciphers) + AuthenticateClientAndServer_ClientSendsNoData (); + } + finally { + ServicePointManager.ClientCipherSuitesCallback = null; + } + } + + [Test] + public void ServerCipherSuitesCallback () + { + try { + ServicePointManager.ServerCipherSuitesCallback += (SecurityProtocolType p, IEnumerable allCiphers) => { + string prefix = p == SecurityProtocolType.Tls ? "TLS_" : "SSL_"; + return new List { prefix + "RSA_WITH_AES_256_CBC_SHA" }; + }; + // server only accept AES 256 - that's fine since the client support it (and many more ciphers) + AuthenticateClientAndServer_ClientSendsNoData (); + } + finally { + ServicePointManager.ServerCipherSuitesCallback = null; + } + } + + [Test] + public void CipherSuitesCallbacks () + { + try { + ServicePointManager.ClientCipherSuitesCallback += (SecurityProtocolType p, IEnumerable allCiphers) => { + string prefix = p == SecurityProtocolType.Tls ? "TLS_" : "SSL_"; + return new List { prefix + "RSA_WITH_AES_128_CBC_SHA", prefix + "RSA_WITH_AES_256_CBC_SHA" }; + }; + ServicePointManager.ServerCipherSuitesCallback += (SecurityProtocolType p, IEnumerable allCiphers) => { + string prefix = p == SecurityProtocolType.Tls ? "TLS_" : "SSL_"; + return new List { prefix + "RSA_WITH_AES_128_CBC_SHA", prefix + "RSA_WITH_AES_256_CBC_SHA" }; + }; + // both client and server supports AES (128 and 256) - server will select 128 (first choice) + AuthenticateClientAndServer_ClientSendsNoData (); + } + finally { + ServicePointManager.ClientCipherSuitesCallback = null; + ServicePointManager.ServerCipherSuitesCallback = null; + } + } + + [Test] + public void MismatchedCipherSuites () + { + try { + ServicePointManager.ClientCipherSuitesCallback += (SecurityProtocolType p, IEnumerable allCiphers) => { + string prefix = p == SecurityProtocolType.Tls ? "TLS_" : "SSL_"; + return new List { prefix + "RSA_WITH_AES_128_CBC_SHA" }; + }; + ServicePointManager.ServerCipherSuitesCallback += (SecurityProtocolType p, IEnumerable allCiphers) => { + string prefix = p == SecurityProtocolType.Tls ? "TLS_" : "SSL_"; + return new List { prefix + "RSA_WITH_AES_256_CBC_SHA" }; + }; + // mismatch! server will refuse and send back an alert + AuthenticateClientAndServer (false, false); + } + finally { + ServicePointManager.ClientCipherSuitesCallback = null; + ServicePointManager.ServerCipherSuitesCallback = null; + } + } + private void StartClientAndAuthenticate (ClientServerState state, IPEndPoint endPoint) { try { @@ -104,7 +189,14 @@ public class SslStreamTest { (a1, a2, a3, a4, a5) => m_serverCert); state.ServerStream.AuthenticateAsServer (m_serverCert); state.ServerAuthenticated.Set (); - } catch (ObjectDisposedException) { /* this can happen when closing connection it's irrelevant for the test result*/} + } catch (ObjectDisposedException) { /* this can happen when closing connection it's irrelevant for the test result*/ + } catch (IOException) { + // The authentication or decryption has failed. + // ---> Mono.Security.Protocol.Tls.TlsException: Insuficient Security + // that's fine for MismatchedCipherSuites + if (!state.ServerIOException) + throw; + } } private class ClientServerState { @@ -115,6 +207,7 @@ public class SslStreamTest { public SslStream ClientStream { get; set; } public AutoResetEvent ServerAuthenticated { get; set; } public AutoResetEvent ClientAuthenticated { get; set; } + public bool ServerIOException { get; set; } } } } diff --git a/mcs/class/System/mobile_System.dll.sources b/mcs/class/System/mobile_System.dll.sources index fe99902b8fb..1ab985a9c05 100644 --- a/mcs/class/System/mobile_System.dll.sources +++ b/mcs/class/System/mobile_System.dll.sources @@ -525,6 +525,7 @@ System.Net/ResponseStream.cs System.Net/SecurityProtocolType.cs System.Net/ServicePoint.cs System.Net/ServicePointManager.cs +System.Net/ServicePointManager.extra.cs System.Net/SocketAddress.cs System.Net/TransportContext.cs System.Net/TransportType.cs -- 2.11.4.GIT