From abcab2aadf197a1ec4de0b7887e9f44dd29695a3 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sat, 3 Nov 2018 18:28:53 -0500 Subject: [PATCH] sql now encrypts and decrypts passwords --- .../Serializers/DataTableDeserializerTests.cs | 22 +++++++---- .../Serializers/DataTableSerializerTests.cs | 8 +++- mRemoteV1/App/Info/SettingsFileInfo.cs | 3 +- .../Connections/SqlConnectionsLoader.cs | 30 ++++++++++++++- .../Config/Connections/SqlConnectionsSaver.cs | 10 +++-- .../MsSql/DataTableDeserializer.cs | 37 +++++++++++++++++-- .../MsSql/DataTableSerializer.cs | 18 ++++++--- .../MsSql/SqlConnectionListMetaData.cs | 1 + mRemoteV1/Connection/ConnectionsService.cs | 3 +- mRemoteV1/mRemoteV1.csproj | 2 +- 10 files changed, 108 insertions(+), 26 deletions(-) diff --git a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs index 85d149db..8d7cc77f 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs @@ -1,11 +1,12 @@ using System.Data; -using System.Linq; -using mRemoteNG.Config.Serializers; +using System.Security; using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Security; +using mRemoteNG.Security.SymmetricEncryption; using mRemoteNG.Tree; using mRemoteNGTests.TestHelpers; +using NSubstitute; using NUnit.Framework; namespace mRemoteNGTests.Config.Serializers @@ -13,30 +14,37 @@ namespace mRemoteNGTests.Config.Serializers public class DataTableDeserializerTests { private DataTableDeserializer _deserializer; + private ICryptographyProvider _cryptographyProvider; + + [SetUp] + public void Setup() + { + _cryptographyProvider = new LegacyRijndaelCryptographyProvider(); + } [Test] public void WeCanDeserializeATree() { var model = CreateConnectionTreeModel(); var dataTable = CreateDataTable(model.RootNodes[0]); - _deserializer = new DataTableDeserializer(); + _deserializer = new DataTableDeserializer(_cryptographyProvider, new SecureString()); var output = _deserializer.Deserialize(dataTable); - Assert.That(output.GetRecursiveChildList().Count(), Is.EqualTo(model.GetRecursiveChildList().Count())); + Assert.That(output.GetRecursiveChildList().Count, Is.EqualTo(model.GetRecursiveChildList().Count)); } [Test] public void WeCanDeserializeASingleEntry() { var dataTable = CreateDataTable(new ConnectionInfo()); - _deserializer = new DataTableDeserializer(); + _deserializer = new DataTableDeserializer(_cryptographyProvider, new SecureString()); var output = _deserializer.Deserialize(dataTable); - Assert.That(output.GetRecursiveChildList().Count(), Is.EqualTo(1)); + Assert.That(output.GetRecursiveChildList().Count, Is.EqualTo(1)); } private DataTable CreateDataTable(ConnectionInfo tableContent) { - var serializer = new DataTableSerializer(new SaveFilter()); + var serializer = new DataTableSerializer(new SaveFilter(), _cryptographyProvider, new SecureString()); return serializer.Serialize(tableContent); } diff --git a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs index ab42493e..6292419a 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs @@ -1,12 +1,15 @@ using System.Linq; +using System.Security; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Security; +using mRemoteNG.Security.SymmetricEncryption; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using mRemoteNGTests.TestHelpers; +using NSubstitute; using NUnit.Framework; namespace mRemoteNGTests.Config.Serializers @@ -20,7 +23,10 @@ namespace mRemoteNGTests.Config.Serializers public void Setup() { _saveFilter = new SaveFilter(); - _dataTableSerializer = new DataTableSerializer(_saveFilter); + _dataTableSerializer = new DataTableSerializer( + _saveFilter, + new LegacyRijndaelCryptographyProvider(), + new SecureString()); } [Test] diff --git a/mRemoteV1/App/Info/SettingsFileInfo.cs b/mRemoteV1/App/Info/SettingsFileInfo.cs index 19776123..a91d9497 100644 --- a/mRemoteV1/App/Info/SettingsFileInfo.cs +++ b/mRemoteV1/App/Info/SettingsFileInfo.cs @@ -2,12 +2,13 @@ using System.IO; using System.Reflection; using System.Windows.Forms; +using mRemoteNG.Connection; namespace mRemoteNG.App.Info { public static class SettingsFileInfo { - private static readonly string ExePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + private static readonly string ExePath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(ConnectionInfo))?.Location); public static string SettingsPath => Runtime.IsPortableEdition ? ExePath : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + Application.ProductName; public static string LayoutFileName { get; } = "pnlLayout.xml"; diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index 4ecb47f9..ed566424 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -1,4 +1,5 @@ -using mRemoteNG.Config.DatabaseConnectors; +using System; +using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.MsSql; @@ -9,6 +10,10 @@ using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using System.Collections.Generic; using System.Linq; +using System.Security; +using mRemoteNG.Security; +using mRemoteNG.Security.Authentication; +using mRemoteNG.Security.SymmetricEncryption; namespace mRemoteNG.Config.Connections { @@ -17,6 +22,9 @@ namespace mRemoteNG.Config.Connections private readonly IDeserializer> _localConnectionPropertiesDeserializer; private readonly IDataProvider _dataProvider; + public Func> AuthenticationRequestor { get; set; } = + () => MiscTools.PasswordDialog("", false); + public SqlConnectionsLoader( IDeserializer> localConnectionPropertiesDeserializer, IDataProvider dataProvider) @@ -31,16 +39,34 @@ namespace mRemoteNG.Config.Connections var dataProvider = new SqlDataProvider(connector); var metaDataRetriever = new SqlDatabaseMetaDataRetriever(); var databaseVersionVerifier = new SqlDatabaseVersionVerifier(connector); - var deserializer = new DataTableDeserializer(); + var cryptoProvider = new LegacyRijndaelCryptographyProvider(); var metaData = metaDataRetriever.GetDatabaseMetaData(connector); + var decryptionKey = GetDecryptionKey(metaData); + + if (!decryptionKey.Any()) + throw new Exception("Could not load SQL connections"); + databaseVersionVerifier.VerifyDatabaseVersion(metaData.ConfVersion); var dataTable = dataProvider.Load(); + var deserializer = new DataTableDeserializer(cryptoProvider, decryptionKey.First()); var connectionTree = deserializer.Deserialize(dataTable); ApplyLocalConnectionProperties(connectionTree.RootNodes.First(i => i is RootNodeInfo)); return connectionTree; } + private Optional GetDecryptionKey(SqlConnectionListMetaData metaData) + { + var cryptographyProvider = new LegacyRijndaelCryptographyProvider(); + var cipherText = metaData.Protected; + var authenticator = new PasswordAuthenticator(cryptographyProvider, cipherText, AuthenticationRequestor); + var authenticated = authenticator.Authenticate(new RootNodeInfo(RootNodeType.Connection).DefaultPassword.ConvertToSecureString()); + + if (authenticated) + return authenticator.LastAuthenticatedPassword; + return Optional.Empty; + } + private void ApplyLocalConnectionProperties(ContainerInfo rootNode) { var localPropertiesXml = _dataProvider.Load(); diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index 08f89bc3..922bee10 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -149,13 +149,15 @@ namespace mRemoteNG.Config.Connections } } - private void UpdateConnectionsTable(ContainerInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector) + private void UpdateConnectionsTable(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector) { - var sqlQuery = new SqlCommand("DELETE FROM tblCons", sqlDatabaseConnector.SqlConnection); - sqlQuery.ExecuteNonQuery(); - var serializer = new DataTableSerializer(_saveFilter); + var cryptoProvider = new LegacyRijndaelCryptographyProvider(); + var serializer = new DataTableSerializer(_saveFilter, cryptoProvider, rootTreeNode.PasswordString.ConvertToSecureString()); var dataTable = serializer.Serialize(rootTreeNode); var dataProvider = new SqlDataProvider(sqlDatabaseConnector); + + var sqlQuery = new SqlCommand("DELETE FROM tblCons", sqlDatabaseConnector.SqlConnection); + sqlQuery.ExecuteNonQuery(); dataProvider.Save(dataTable); } diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs index f3a58d18..1938ad0d 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs @@ -12,11 +12,24 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Security; +using mRemoteNG.Security; +using mRemoteNG.Security.SymmetricEncryption; +using mRemoteNG.Tools; namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableDeserializer : IDeserializer { + private readonly ICryptographyProvider _cryptographyProvider; + private readonly SecureString _decryptionKey; + + public DataTableDeserializer(ICryptographyProvider cryptographyProvider, SecureString decryptionKey) + { + _cryptographyProvider = cryptographyProvider.ThrowIfNull(nameof(cryptographyProvider)); + _decryptionKey = decryptionKey.ThrowIfNull(nameof(decryptionKey)); + } + public ConnectionTreeModel Deserialize(DataTable table) { var connectionList = CreateNodesFromTable(table); @@ -73,7 +86,7 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.Panel = (string)dataRow["Panel"]; connectionInfo.Username = (string)dataRow["Username"]; connectionInfo.Domain = (string)dataRow["DomainName"]; - connectionInfo.Password = (string)dataRow["Password"]; + connectionInfo.Password = DecryptValue((string)dataRow["Password"]); connectionInfo.Hostname = (string)dataRow["Hostname"]; connectionInfo.Protocol = (ProtocolType)Enum.Parse(typeof(ProtocolType), (string)dataRow["Protocol"]); connectionInfo.PuttySession = (string)dataRow["PuttySession"]; @@ -113,7 +126,7 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.VNCProxyIP = (string)dataRow["VNCProxyIP"]; connectionInfo.VNCProxyPort = (int)dataRow["VNCProxyPort"]; connectionInfo.VNCProxyUsername = (string)dataRow["VNCProxyUsername"]; - connectionInfo.VNCProxyPassword = (string)dataRow["VNCProxyPassword"]; + connectionInfo.VNCProxyPassword = DecryptValue((string)dataRow["VNCProxyPassword"]); connectionInfo.VNCColors = (ProtocolVNC.Colors)Enum.Parse(typeof(ProtocolVNC.Colors), (string)dataRow["VNCColors"]); connectionInfo.VNCSmartSizeMode = (ProtocolVNC.SmartSizeMode)Enum.Parse(typeof(ProtocolVNC.SmartSizeMode), (string)dataRow["VNCSmartSizeMode"]); connectionInfo.VNCViewOnly = (bool)dataRow["VNCViewOnly"]; @@ -121,7 +134,7 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.RDGatewayHostname = (string)dataRow["RDGatewayHostname"]; connectionInfo.RDGatewayUseConnectionCredentials = (RdpProtocol.RDGatewayUseConnectionCredentials)Enum.Parse(typeof(RdpProtocol.RDGatewayUseConnectionCredentials), (string)dataRow["RDGatewayUseConnectionCredentials"]); connectionInfo.RDGatewayUsername = (string)dataRow["RDGatewayUsername"]; - connectionInfo.RDGatewayPassword = (string)dataRow["RDGatewayPassword"]; + connectionInfo.RDGatewayPassword = DecryptValue((string)dataRow["RDGatewayPassword"]); connectionInfo.RDGatewayDomain = (string)dataRow["RDGatewayDomain"]; connectionInfo.Inheritance.CacheBitmaps = (bool)dataRow["InheritCacheBitmaps"]; @@ -180,10 +193,26 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.Inheritance.RDGatewayDomain = (bool)dataRow["InheritRDGatewayDomain"]; } + private string DecryptValue(string cipherText) + { + try + { + return _cryptographyProvider.Decrypt(cipherText, _decryptionKey); + } + catch (EncryptionException) + { + // value may not be encrypted + return cipherText; + } + } + private ConnectionTreeModel CreateNodeHierarchy(List connectionList, DataTable dataTable) { var connectionTreeModel = new ConnectionTreeModel(); - var rootNode = new RootNodeInfo(RootNodeType.Connection, "0"); + var rootNode = new RootNodeInfo(RootNodeType.Connection, "0") + { + PasswordString = _decryptionKey.ConvertToUnsecureString() + }; connectionTreeModel.AddRootNode(rootNode); foreach (DataRow row in dataTable.Rows) diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs index c829deaf..c059afd7 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs @@ -7,19 +7,25 @@ using System; using System.Data; using System.Data.SqlTypes; using System.Linq; +using System.Security; +using mRemoteNG.Tools; namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableSerializer : ISerializer { + private readonly ICryptographyProvider _cryptographyProvider; + private readonly SecureString _encryptionKey; private DataTable _dataTable; private const string TableName = "tblCons"; private readonly SaveFilter _saveFilter; private int _currentNodeIndex; - public DataTableSerializer(SaveFilter saveFilter) + public DataTableSerializer(SaveFilter saveFilter, ICryptographyProvider cryptographyProvider, SecureString encryptionKey) { - _saveFilter = saveFilter; + _saveFilter = saveFilter.ThrowIfNull(nameof(saveFilter)); + _cryptographyProvider = cryptographyProvider.ThrowIfNull(nameof(cryptographyProvider)); + _encryptionKey = encryptionKey.ThrowIfNull(nameof(encryptionKey)); } @@ -210,7 +216,9 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["Panel"] = connectionInfo.Panel; dataRow["Username"] = _saveFilter.SaveUsername ? connectionInfo.Username : ""; dataRow["DomainName"] = _saveFilter.SaveDomain ? connectionInfo.Domain : ""; - dataRow["Password"] = _saveFilter.SavePassword ? connectionInfo.Password : ""; + dataRow["Password"] = _saveFilter.SavePassword + ? _cryptographyProvider.Encrypt(connectionInfo.Password, _encryptionKey) + : ""; dataRow["Hostname"] = connectionInfo.Hostname; dataRow["Protocol"] = connectionInfo.Protocol; dataRow["PuttySession"] = connectionInfo.PuttySession; @@ -251,14 +259,14 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["VNCProxyIP"] = connectionInfo.VNCProxyIP; dataRow["VNCProxyPort"] = connectionInfo.VNCProxyPort; dataRow["VNCProxyUsername"] = connectionInfo.VNCProxyUsername; - dataRow["VNCProxyPassword"] = connectionInfo.VNCProxyPassword; + dataRow["VNCProxyPassword"] = _cryptographyProvider.Encrypt(connectionInfo.VNCProxyPassword, _encryptionKey); dataRow["VNCColors"] = connectionInfo.VNCColors; dataRow["VNCSmartSizeMode"] = connectionInfo.VNCSmartSizeMode; dataRow["VNCViewOnly"] = connectionInfo.VNCViewOnly; dataRow["RDGatewayUsageMethod"] = connectionInfo.RDGatewayUsageMethod; dataRow["RDGatewayHostname"] = connectionInfo.RDGatewayHostname; dataRow["RDGatewayUseConnectionCredentials"] = connectionInfo.RDGatewayUseConnectionCredentials; - dataRow["RDGatewayUsername"] = connectionInfo.RDGatewayUsername; + dataRow["RDGatewayUsername"] = _cryptographyProvider.Encrypt(connectionInfo.RDGatewayUsername, _encryptionKey); dataRow["RDGatewayPassword"] = connectionInfo.RDGatewayPassword; dataRow["RDGatewayDomain"] = connectionInfo.RDGatewayDomain; if (_saveFilter.SaveInheritance) diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs index 8cf28d0e..12e8ce86 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs @@ -1,4 +1,5 @@ using System; +using System.Security; namespace mRemoteNG.Config.Serializers.MsSql { diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index 93fa7bcc..41221a02 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -44,7 +44,8 @@ namespace mRemoteNG.Connection throw new ArgumentNullException(nameof(puttySessionsManager)); _puttySessionsManager = puttySessionsManager; - _localConnectionPropertiesDataProvider = new FileDataProvider(Path.Combine(SettingsFileInfo.SettingsPath, "LocalConnectionProperties.xml")); + var path = SettingsFileInfo.SettingsPath; + _localConnectionPropertiesDataProvider = new FileDataProvider(Path.Combine(path, "LocalConnectionProperties.xml")); _localConnectionPropertiesSerializer = new LocalConnectionPropertiesXmlSerializer(); } diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index b29cd171..896383b6 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -180,7 +180,7 @@ - +