Compare commits

...

25 Commits

Author SHA1 Message Date
Dimitrij
a8077149b9 Merge pull request #1662 from GreyCorbel/release/v1.76
Added support for password retrieval via AdmPwd.E 7.7.1
2021-11-01 12:51:33 +00:00
Dimitrij
51514d78e6 Merge branch 'release/v1.76' into release/v1.76 2021-11-01 12:51:22 +00:00
Jiri Formacek
42f60b0db3 Updated to use AdmPwd.E 7.7.2 2020-01-01 14:25:01 +01:00
Jiri Formacek
5aec72f164 Updated to 1.76.20 with AdmPwd.E 7.7.1 2019-12-25 23:31:04 +01:00
David Sparer
068f5942e7 updated changelog and bumped version to 1.76.20 2019-04-12 08:38:06 -05:00
David Sparer
703cefaf19 ensure rdcman import never sets properties to null. fixes #1401 2019-04-12 08:24:05 -05:00
Sean Kaim
ea1f72b8b7 bump version and changelog update 2019-04-04 15:44:34 -04:00
Sean Kaim
993b6759cc Update PuTTYNG.exe
Reference #1374
2019-04-04 15:41:55 -04:00
David Sparer
15f23769d6 bumped version to v1.76.18 and updated changelog 2019-03-20 09:33:21 -05:00
Sean Kaim
2d175fd575 border fix for puttyng 0.70.0.1+ -- backported from develop 2019-03-20 10:21:56 -04:00
David Sparer
bdeb4b4dcc bumped version to v1.76.17 and updated changelog 2019-03-20 07:32:27 -05:00
David Sparer
bdfbb57504 updated puttyng to 0.71
fixes #1362
2019-03-20 07:29:13 -05:00
David Sparer
6c89913bc6 updated changelog 2019-03-14 17:58:53 -05:00
David Sparer
efa72cb697 bumped version to 1.76.16 2019-03-14 17:58:43 -05:00
David Sparer
a31fec5fb9 encrypt correct db field
fixes #1347
2019-03-14 17:35:28 -05:00
David Sparer
ed81030976 bumped v1.76.15 release date 2019-03-09 11:21:21 -06:00
David Sparer
54322ca949 updated changelog 2019-03-08 10:41:57 -06:00
David Sparer
738b159e95 handle sql first-run case
fixes #1303
2019-03-08 10:31:39 -06:00
David Sparer
e8f2e4f50c bumped patch version 2019-03-04 11:45:07 -06:00
David Sparer
5cd201440e added tests for the disposable action class 2019-03-04 11:44:30 -06:00
David Sparer
5c6c76b898 one more parse bug fix 2019-03-04 11:30:34 -06:00
David Sparer
4847ce054b resolved several parsing bugs in the rdcman deserializer 2019-03-04 11:17:45 -06:00
David Sparer
ec42fe7d7d improved testability of the import class 2019-03-04 11:17:19 -06:00
David Sparer
07a20ed5ad batch saves when importing connection files
This reduces saves/backup overwrites when importing multiple files
2019-03-04 08:41:27 -06:00
Sean Kaim
08201b0f00 Port FIPS override back to 1.76
Fixes #222
2019-02-08 16:51:58 -05:00
62 changed files with 9518 additions and 8293 deletions

View File

@@ -1,3 +1,57 @@
1.76.20 (2019-04-12):
Fixes:
------
#1401: Connections corrupted when importing RDC Manager files that are missing certain fields
1.76.19 (2019-04-04):
Fixes:
------
#1374: Vertical Scroll Bar missing in PuTTYNG after 0.70.0.1 & 0.71 updates
1.76.18 (2019-03-20):
Fixes:
------
#1365: PuTTY window not centered after 0.71 update
1.76.17 (2019-03-20):
Fixes:
------
#1362: Updated PuTTYNG to 0.71
1.76.16 (2019-03-14):
Fixes:
------
#1347: Remote Desktop Gateway username field encrypted instead of password
1.76.15 (2019-03-09):
Fixes:
------
#1303: Exception on first connection with new SQL server database
#1304: Resolved several issues with importing multiple RDP Manager v2.7 files
Features/Enhancements:
----------------------
Importing multiple files now only causes 1 save event, rather than 1 per file imported.
1.76.14 (2019-02-08):
Features/Enhancements:
----------------------
#222: Allow FIPS to be enabled
1.76.13 (2018-12-22):
Changes:

View File

@@ -17,5 +17,7 @@ if (!(Test-Path -Path $DestinationDir))
$sourceFiles = Get-ChildItem -Path $SourcePath -Recurse | ?{$_.Extension -match "exe|msi"}
foreach ($item in $sourceFiles)
{
$item.Name
(Get-FileHash -Path $item.fullName -Algorithm SHA512).Hash
Copy-Item -Path $item.FullName -Destination $DestinationDir -Force
}

View File

@@ -56,6 +56,8 @@ if ($ConfigurationName -eq "Release Portable") {
Remove-Item -Force $PortableZip -ErrorAction SilentlyContinue
& $SEVENZIP a -bt -bd -bb1 -mx=9 -tzip -y -r $PortableZip (Join-Path -Path $tempFolderPath -ChildPath "*.*")
#& $SEVENZIP a -bt -mx=9 -tzip -y $PortableZip "$($SolutionDir)*.TXT"
(Get-FileHash -Path $PortableZip -Algorithm SHA512).Hash
}
else {
Write-Output "We will not zip anything - this isnt a portable release build."

View File

@@ -0,0 +1,47 @@
using System.IO;
using mRemoteNG.App;
using mRemoteNG.Config.Putty;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNGTests.Properties;
using mRemoteNGTests.TestHelpers;
using NUnit.Framework;
namespace mRemoteNGTests.App
{
public class ImportTests
{
[Test]
public void ErrorHandlerCalledWhenUnsupportedFileExtensionFound()
{
using (FileTestHelpers.DisposableTempFile(out var file, ".blah"))
{
var conService = new ConnectionsService(PuttySessionsManager.Instance);
var container = new ContainerInfo();
var exceptionOccurred = false;
Import.HeadlessFileImport(new []{file}, container, conService, s => exceptionOccurred = true);
Assert.That(exceptionOccurred);
}
}
[Test]
public void AnErrorInOneFileDoNotPreventOtherFilesFromProcessing()
{
using (FileTestHelpers.DisposableTempFile(out var badFile, ".blah"))
using (FileTestHelpers.DisposableTempFile(out var rdpFile, ".rdp"))
{
File.AppendAllText(rdpFile, Resources.test_remotedesktopconnection_rdp);
var conService = new ConnectionsService(PuttySessionsManager.Instance);
var container = new ContainerInfo();
var exceptionCount = 0;
Import.HeadlessFileImport(new[] { badFile, rdpFile }, container, conService, s => exceptionCount++);
Assert.That(exceptionCount, Is.EqualTo(1));
Assert.That(container.Children, Has.One.Items);
}
}
}
}

View File

@@ -1,20 +1,21 @@
using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Container;
using mRemoteNG.Tree;
using mRemoteNGTests.Properties;
using NUnit.Framework;
namespace mRemoteNGTests.Config.Serializers.MiscSerializers
{
public class RemoteDesktopConnectionManager27DeserializerTests
public class RemoteDesktopConnectionManager27DeserializerTests
{
private string _connectionFileContents;
private RemoteDesktopConnectionManagerDeserializer _deserializer;
private ConnectionTreeModel _connectionTreeModel;
private const string ExpectedName = "server1_displayname";
private const string ExpectedHostname = "server1";
private const string ExpectedDescription = "Comment text here";
@@ -44,265 +45,80 @@ namespace mRemoteNGTests.Config.Serializers.MiscSerializers
{
_connectionFileContents = Resources.test_rdcman_v2_7_schema3;
_deserializer = new RemoteDesktopConnectionManagerDeserializer();
_connectionTreeModel = _deserializer.Deserialize(_connectionFileContents);
}
[Test]
public void ConnectionTreeModelHasARootNode()
{
var numberOfRootNodes = _connectionTreeModel.RootNodes.Count;
var connectionTreeModel = _deserializer.Deserialize(_connectionFileContents);
var numberOfRootNodes = connectionTreeModel.RootNodes.Count;
Assert.That(numberOfRootNodes, Is.GreaterThan(0));
}
[Test]
public void RootNodeHasContents()
{
var rootNodeContents = _connectionTreeModel.RootNodes.First().Children;
var connectionTreeModel = _deserializer.Deserialize(_connectionFileContents);
var rootNodeContents = connectionTreeModel.RootNodes.First().Children;
Assert.That(rootNodeContents, Is.Not.Empty);
}
[Test]
public void AllSubRootFoldersImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var connectionTreeModel = _deserializer.Deserialize(_connectionFileContents);
var rootNode = connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var rootNodeContents = importedRdcmanRootNode.Children.Count(node => node.Name == "Group1" || node.Name == "Group2");
Assert.That(rootNodeContents, Is.EqualTo(2));
}
[Test]
public void ConnectionDisplayNameImported()
[TestCaseSource(nameof(ExpectedPropertyValues))]
public void PropertiesWithValuesAreCorrectlyImported(Func<ConnectionInfo, object> propSelector, object expectedValue)
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Name, Is.EqualTo(ExpectedName));
var connectionTreeModel = _deserializer.Deserialize(_connectionFileContents);
var connection = connectionTreeModel
.GetRecursiveChildList()
.OfType<ContainerInfo>()
.First(node => node.Name == "Group1")
.Children
.First();
Assert.That(propSelector(connection), Is.EqualTo(expectedValue));
}
[Test]
public void ConnectionHostnameImported()
[TestCaseSource(nameof(NullPropertyValues))]
public void PropertiesWithoutValuesAreIgnored(Func<ConnectionInfo, object> propSelector)
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Hostname, Is.EqualTo(ExpectedHostname));
var connectionTreeModel = _deserializer.Deserialize(Resources.test_rdcman_v2_7_schema3_empty_values);
var importedConnection = connectionTreeModel
.GetRecursiveChildList()
.OfType<ContainerInfo>()
.First(node => node.Name == "Group1")
.Children
.First();
Assert.That(propSelector(importedConnection), Is.EqualTo(propSelector(new ConnectionInfo())));
}
[Test]
public void ConnectionDescriptionImported()
[TestCaseSource(nameof(NullPropertyValues))]
public void NonExistantPropertiesAreIgnored(Func<ConnectionInfo, object> propSelector)
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Description, Is.EqualTo(ExpectedDescription));
var connectionTreeModel = _deserializer.Deserialize(Resources.test_rdcman_v2_7_schema3_null_values);
var importedConnection = connectionTreeModel
.GetRecursiveChildList()
.OfType<ContainerInfo>()
.First(node => node.Name == "Group1")
.Children
.First();
Assert.That(propSelector(importedConnection), Is.EqualTo(propSelector(new ConnectionInfo())));
}
[Test]
public void ConnectionUsernameImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Username, Is.EqualTo(ExpectedUsername));
}
[Test]
public void ConnectionDomainImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Domain, Is.EqualTo(ExpectedDomain));
}
// Since password is encrypted with a machine key, cant test decryption on another machine
//[Test]
//public void ConnectionPasswordImported()
//{
// var rootNode = _connectionTreeModel.RootNodes.First();
// var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
// var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
// var connection = group1.Children.First();
// Assert.That(connection.Password, Is.EqualTo(ExpectedPassword));
//}
[Test]
public void ConnectionProtocolSetToRdp()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Protocol, Is.EqualTo(ProtocolType.RDP));
}
[Test]
public void ConnectionUseConsoleSessionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.UseConsoleSession, Is.EqualTo(ExpectedUseConsoleSession));
}
[Test]
public void ConnectionPortImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Port, Is.EqualTo(ExpectedPort));
}
[Test]
public void ConnectionGatewayUsageMethodImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RDGatewayUsageMethod, Is.EqualTo(ExpectedGatewayUsageMethod));
}
[Test]
public void ConnectionGatewayHostnameImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RDGatewayHostname, Is.EqualTo(ExpectedGatewayHostname));
}
[Test]
public void ConnectionGatewayUsernameImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RDGatewayUsername, Is.EqualTo(ExpectedGatewayUsername));
}
// Since password is encrypted with a machine key, cant test decryption on another machine
//[Test]
//public void ConnectionGatewayPasswordImported()
//{
// var rootNode = _connectionTreeModel.RootNodes.First();
// var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
// var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
// var connection = group1.Children.First();
// Assert.That(connection.RDGatewayPassword, Is.EqualTo(ExpectedGatewayPassword));
//}
[Test]
public void ConnectionGatewayDomainImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RDGatewayDomain, Is.EqualTo(ExpectedGatewayDomain));
}
[Test]
public void ConnectionResolutionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Resolution, Is.EqualTo(ExpectedRdpResolution));
}
[Test]
public void ConnectionColorDepthImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.Colors, Is.EqualTo(ExpectedRdpColorDepth));
}
[Test]
public void ConnectionAudioRedirectionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RedirectSound, Is.EqualTo(ExpectedAudioRedirection));
}
[Test]
public void ConnectionKeyRedirectionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RedirectKeys, Is.EqualTo(ExpectedKeyRedirection));
}
[Test]
public void ConnectionDriveRedirectionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RedirectDiskDrives, Is.EqualTo(ExpectedDriveRedirection));
}
[Test]
public void ConnectionPortRedirectionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RedirectPorts, Is.EqualTo(ExpectedPortRedirection));
}
[Test]
public void ConnectionPrinterRedirectionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RedirectPrinters, Is.EqualTo(ExpectedPrinterRedirection));
}
[Test]
public void ConnectionSmartcardRedirectionImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RedirectSmartCards, Is.EqualTo(ExpectedSmartcardRedirection));
}
[Test]
public void ConnectionauthenticationLevelImported()
{
var rootNode = _connectionTreeModel.RootNodes.First();
var importedRdcmanRootNode = rootNode.Children.OfType<ContainerInfo>().First();
var group1 = importedRdcmanRootNode.Children.OfType<ContainerInfo>().First(node => node.Name == "Group1");
var connection = group1.Children.First();
Assert.That(connection.RDPAuthenticationLevel, Is.EqualTo(ExpectedAuthLevel));
}
[Test]
[Test]
public void ExceptionThrownOnBadSchemaVersion()
{
var badFileContents = Resources.test_rdcman_v2_2_badschemaversion;
@@ -322,5 +138,61 @@ namespace mRemoteNGTests.Config.Serializers.MiscSerializers
var badFileContents = Resources.test_rdcman_noversion;
Assert.That(() => _deserializer.Deserialize(badFileContents), Throws.TypeOf<FileFormatException>());
}
private static IEnumerable<TestCaseData> ExpectedPropertyValues()
{
return new[]
{
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Name), ExpectedName).SetName(nameof(ConnectionInfo.Name)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Hostname), ExpectedHostname).SetName(nameof(ConnectionInfo.Hostname)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Description), ExpectedDescription).SetName(nameof(ConnectionInfo.Description)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Username), ExpectedUsername).SetName(nameof(ConnectionInfo.Username)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Domain), ExpectedDomain).SetName(nameof(ConnectionInfo.Domain)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Protocol), ProtocolType.RDP).SetName(nameof(ConnectionInfo.Protocol)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.UseConsoleSession), ExpectedUseConsoleSession).SetName(nameof(ConnectionInfo.UseConsoleSession)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Port), ExpectedPort).SetName(nameof(ConnectionInfo.Port)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayUsageMethod), ExpectedGatewayUsageMethod).SetName(nameof(ConnectionInfo.RDGatewayUsageMethod)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayHostname), ExpectedGatewayHostname).SetName(nameof(ConnectionInfo.RDGatewayHostname)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayUsername), ExpectedGatewayUsername).SetName(nameof(ConnectionInfo.RDGatewayUsername)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayDomain), ExpectedGatewayDomain).SetName(nameof(ConnectionInfo.RDGatewayDomain)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Resolution), ExpectedRdpResolution).SetName(nameof(ConnectionInfo.Resolution)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Colors), ExpectedRdpColorDepth).SetName(nameof(ConnectionInfo.Colors)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectSound), ExpectedAudioRedirection).SetName(nameof(ConnectionInfo.RedirectSound)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectKeys), ExpectedKeyRedirection).SetName(nameof(ConnectionInfo.RedirectKeys)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDPAuthenticationLevel), ExpectedAuthLevel).SetName(nameof(ConnectionInfo.RDPAuthenticationLevel)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectSmartCards), ExpectedSmartcardRedirection).SetName(nameof(ConnectionInfo.RedirectSmartCards)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectPrinters), ExpectedPrinterRedirection).SetName(nameof(ConnectionInfo.RedirectPrinters)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectPorts), ExpectedPortRedirection).SetName(nameof(ConnectionInfo.RedirectPorts)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectDiskDrives), ExpectedDriveRedirection).SetName(nameof(ConnectionInfo.RedirectDiskDrives)),
};
}
private static IEnumerable<TestCaseData> NullPropertyValues()
{
return new[]
{
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Name)).SetName(nameof(ConnectionInfo.Name)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Hostname)).SetName(nameof(ConnectionInfo.Hostname)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Description)).SetName(nameof(ConnectionInfo.Description)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Username)).SetName(nameof(ConnectionInfo.Username)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Domain)).SetName(nameof(ConnectionInfo.Domain)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Protocol)).SetName(nameof(ConnectionInfo.Protocol)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.UseConsoleSession)).SetName(nameof(ConnectionInfo.UseConsoleSession)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Port)).SetName(nameof(ConnectionInfo.Port)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayUsageMethod)).SetName(nameof(ConnectionInfo.RDGatewayUsageMethod)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayHostname)).SetName(nameof(ConnectionInfo.RDGatewayHostname)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayUsername)).SetName(nameof(ConnectionInfo.RDGatewayUsername)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDGatewayDomain)).SetName(nameof(ConnectionInfo.RDGatewayDomain)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Resolution)).SetName(nameof(ConnectionInfo.Resolution)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.Colors)).SetName(nameof(ConnectionInfo.Colors)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectSound)).SetName(nameof(ConnectionInfo.RedirectSound)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectKeys)).SetName(nameof(ConnectionInfo.RedirectKeys)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RDPAuthenticationLevel)).SetName(nameof(ConnectionInfo.RDPAuthenticationLevel)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectSmartCards)).SetName(nameof(ConnectionInfo.RedirectSmartCards)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectPrinters)).SetName(nameof(ConnectionInfo.RedirectPrinters)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectPorts)).SetName(nameof(ConnectionInfo.RedirectPorts)),
new TestCaseData((Func<ConnectionInfo,object>)(con => con.RedirectDiskDrives)).SetName(nameof(ConnectionInfo.RedirectDiskDrives)),
};
}
}
}

View File

@@ -298,6 +298,58 @@ namespace mRemoteNGTests.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
///&lt;RDCMan programVersion=&quot;2.7&quot; schemaVersion=&quot;3&quot;&gt;
/// &lt;file&gt;
/// &lt;credentialsProfiles /&gt;
/// &lt;properties&gt;
/// &lt;expanded&gt;True&lt;/expanded&gt;
/// &lt;name&gt;test_RDCMan_connections&lt;/name&gt;
/// &lt;/properties&gt;
/// &lt;smartGroup&gt;
/// &lt;properties&gt;
/// &lt;expanded&gt;False&lt;/expanded&gt;
/// &lt;name&gt;AllServers&lt;/name&gt;
/// &lt;/properties&gt;
/// &lt;ruleGroup operator=&quot;All&quot;&gt;
/// &lt;rule&gt;
/// &lt;property&gt;DisplayName&lt;/property&gt;
/// &lt;operator&gt;Matches&lt;/operator&gt;
/// [rest of string was truncated]&quot;;.
/// </summary>
internal static string test_rdcman_v2_7_schema3_empty_values {
get {
return ResourceManager.GetString("test_rdcman_v2_7_schema3_empty_values", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
///&lt;RDCMan programVersion=&quot;2.7&quot; schemaVersion=&quot;3&quot;&gt;
/// &lt;file&gt;
/// &lt;credentialsProfiles /&gt;
/// &lt;properties&gt;
/// &lt;expanded&gt;True&lt;/expanded&gt;
/// &lt;name&gt;test_RDCMan_connections&lt;/name&gt;
/// &lt;/properties&gt;
/// &lt;smartGroup&gt;
/// &lt;properties&gt;
/// &lt;expanded&gt;False&lt;/expanded&gt;
/// &lt;name&gt;AllServers&lt;/name&gt;
/// &lt;/properties&gt;
/// &lt;ruleGroup operator=&quot;All&quot;&gt;
/// &lt;rule&gt;
/// &lt;property&gt;DisplayName&lt;/property&gt;
/// &lt;operator&gt;Matches&lt;/operator&gt;
/// [rest of string was truncated]&quot;;.
/// </summary>
internal static string test_rdcman_v2_7_schema3_null_values {
get {
return ResourceManager.GetString("test_rdcman_v2_7_schema3_null_values", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to screen mode id:i:1
///use multimon:i:0

View File

@@ -172,6 +172,12 @@
<data name="test_rdcman_v2_7_schema3" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test_RDCMan_v2_7_schema3.rdg;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="test_rdcman_v2_7_schema3_empty_values" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test_rdcman_v2_7_schema3_empty_values.rdg;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="test_rdcman_v2_7_schema3_null_values" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test_rdcman_v2_7_schema3_null_values.rdg;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="test_remotedesktopconnection_rdp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test_remotedesktopconnection.rdp;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
</data>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<RDCMan programVersion="2.7" schemaVersion="3">
<file>
<credentialsProfiles />
<properties>
<expanded>True</expanded>
<name>test_RDCMan_connections</name>
</properties>
<smartGroup>
<properties>
<expanded>False</expanded>
<name>AllServers</name>
</properties>
<ruleGroup operator="All">
<rule>
<property>DisplayName</property>
<operator>Matches</operator>
<value>server</value>
</rule>
</ruleGroup>
</smartGroup>
<group>
<properties>
<expanded>True</expanded>
<name>Group1</name>
</properties>
<server>
<properties>
<displayName></displayName>
<name></name>
<comment></comment>
</properties>
<logonCredentials inherit="None">
<profileName scope="Local"></profileName>
<userName></userName>
<password></password>
<domain></domain>
</logonCredentials>
<connectionSettings inherit="None">
<connectToConsole></connectToConsole>
<startProgram />
<workingDir />
<port></port>
<loadBalanceInfo />
</connectionSettings>
<gatewaySettings inherit="None">
<enabled></enabled>
<hostName></hostName>
<logonMethod></logonMethod>
<localBypass></localBypass>
<credSharing></credSharing>
<profileName scope="Local"></profileName>
<userName></userName>
<password />
<domain></domain>
</gatewaySettings>
<remoteDesktop inherit="None">
<sameSizeAsClientArea></sameSizeAsClientArea>
<fullScreen></fullScreen>
<colorDepth></colorDepth>
</remoteDesktop>
<localResources inherit="None">
<audioRedirection></audioRedirection>
<audioRedirectionQuality></audioRedirectionQuality>
<audioCaptureRedirection></audioCaptureRedirection>
<keyboardHook></keyboardHook>
<redirectClipboard></redirectClipboard>
<redirectDrives></redirectDrives>
<redirectDrivesList>
<item></item>
<item></item>
<item></item>
<item></item>
<item></item>
</redirectDrivesList>
<redirectPrinters></redirectPrinters>
<redirectPorts></redirectPorts>
<redirectSmartCards></redirectSmartCards>
<redirectPnpDevices></redirectPnpDevices>
</localResources>
<displaySettings inherit="None">
<thumbnailScale></thumbnailScale>
<smartSizeDockedWindows></smartSizeDockedWindows>
<smartSizeUndockedWindows></smartSizeUndockedWindows>
</displaySettings>
<securitySettings inherit="None">
<authentication></authentication>
</securitySettings>
</server>
</group>
</file>
<connected />
<favorites />
<recentlyUsed />
</RDCMan>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<RDCMan programVersion="2.7" schemaVersion="3">
<file>
<credentialsProfiles />
<properties>
<expanded>True</expanded>
<name>test_RDCMan_connections</name>
</properties>
<smartGroup>
<properties>
<expanded>False</expanded>
<name>AllServers</name>
</properties>
<ruleGroup operator="All">
<rule>
<property>DisplayName</property>
<operator>Matches</operator>
<value>server</value>
</rule>
</ruleGroup>
</smartGroup>
<group>
<properties>
<expanded>True</expanded>
<name>Group1</name>
</properties>
<server>
<properties>
</properties>
<logonCredentials inherit="None">
</logonCredentials>
<connectionSettings inherit="None">
</connectionSettings>
<gatewaySettings inherit="None">
</gatewaySettings>
<remoteDesktop inherit="None">
</remoteDesktop>
<localResources inherit="None">
</localResources>
<displaySettings inherit="None">
</displaySettings>
<securitySettings inherit="None">
</securitySettings>
</server>
</group>
</file>
<connected />
<favorites />
<recentlyUsed />
</RDCMan>

View File

@@ -1,4 +1,5 @@
using System.IO;
using mRemoteNG.Tools;
namespace mRemoteNGTests.TestHelpers
{
@@ -18,9 +19,17 @@ namespace mRemoteNGTests.TestHelpers
File.Delete(file);
}
public static string NewTempFilePath()
public static void DeleteDirectory(string directory)
{
if (Directory.Exists(directory))
Directory.Delete(directory, true);
}
public static string NewTempFilePath(string extension = "")
{
var newPath = Path.Combine(GetTestSpecificTempDirectory(), Path.GetRandomFileName());
if (!string.IsNullOrWhiteSpace(extension))
newPath = newPath + extension;
var folderPath = Path.GetDirectoryName(newPath);
if (!Directory.Exists(folderPath))
Directory.CreateDirectory(folderPath);
@@ -35,5 +44,15 @@ namespace mRemoteNGTests.TestHelpers
{
return Path.Combine(Path.GetTempPath(), "mRemoteNGTests", Path.GetRandomFileName());
}
public static DisposableAction DisposableTempFile(out string filePath, string extension = "")
{
var file = NewTempFilePath(extension);
filePath = file;
File.AppendAllText(file, "");
return new DisposableAction(
() => {},
() => DeleteDirectory(Path.GetDirectoryName(file)));
}
}
}

View File

@@ -0,0 +1,42 @@
using mRemoteNG.Tools;
using NUnit.Framework;
namespace mRemoteNGTests.Tools
{
public class DisposableActionTests
{
[Test]
public void InitializerActionRunsWhenObjectIsCreated()
{
var initializerRan = false;
new DisposableAction(() => initializerRan = true, () => { });
Assert.That(initializerRan);
}
[Test]
public void DisposalActionRunsWhenDisposeIsCalled()
{
var disposeActionRan = false;
var action = new DisposableAction(() => {}, () => disposeActionRan = true);
Assert.That(disposeActionRan, Is.False);
action.Dispose();
Assert.That(disposeActionRan, Is.True);
}
[Test]
public void DisposeActionOnlyExecutedOnceWhenCallingDisposeMultipleTimes()
{
var invokeCount = 0;
var action = new DisposableAction(() => { }, () => invokeCount++);
action.Dispose();
action.Dispose();
action.Dispose();
action.Dispose();
action.Dispose();
Assert.That(invokeCount, Is.EqualTo(1));
}
}
}

View File

@@ -17,6 +17,8 @@
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -108,6 +110,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="App\ImportTests.cs" />
<Compile Include="App\UpdaterTests.cs" />
<Compile Include="BinaryFileTests.cs" />
<Compile Include="Config\Connections\Multiuser\ConnectionsUpdateAvailableEventArgsTests.cs" />
@@ -175,6 +178,7 @@
<Compile Include="Security\XmlCryptoProviderBuilderTests.cs" />
<Compile Include="TestHelpers\FileTestHelpers.cs" />
<Compile Include="TestHelpers\SerializableConnectionInfoAllPropertiesOfType.cs" />
<Compile Include="Tools\DisposableActionTests.cs" />
<Compile Include="Tools\ExternalToolsArgumentParserTests.cs" />
<Compile Include="Tools\FullyObservableCollectionTests.cs" />
<Compile Include="Tools\OptionalTests.cs" />
@@ -265,6 +269,8 @@
<None Include="Resources\test_rdcman_v2_2_badschemaversion.rdg" />
<None Include="Resources\test_rdcman_v2_2_schema1.rdg" />
<None Include="Resources\test_RDCMan_v2_7_schema3.rdg" />
<None Include="Resources\test_rdcman_v2_7_schema3_empty_values.rdg" />
<None Include="Resources\test_rdcman_v2_7_schema3_null_values.rdg" />
<None Include="Resources\test_remotedesktopconnection.rdp" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2010
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 14.0.25420.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mRemoteV1", "mRemoteV1\mRemoteV1.csproj", "{4934A491-40BC-4E5B-9166-EA1169A220F6}"
EndProject
@@ -61,7 +61,6 @@ Global
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|x86.ActiveCfg = Debug|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Debug|x86.Build.0 = Debug|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|Any CPU.ActiveCfg = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|Any CPU.Build.0 = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|x86.ActiveCfg = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Installer|x86.Build.0 = Release|x86
{1453B37F-8621-499E-B0B2-6091F76DC0BB}.Release Portable|Any CPU.ActiveCfg = Release Portable|x86
@@ -106,7 +105,6 @@ Global
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Debug|x86.ActiveCfg = Debug|Any CPU
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|Any CPU.ActiveCfg = Release|Any CPU
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|Any CPU.Build.0 = Release|Any CPU
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|x86.ActiveCfg = Release|Any CPU
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Installer|x86.Build.0 = Release|Any CPU
{16AA21E2-D6B7-427D-AB7D-AA8C611B724E}.Release Portable|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -19,17 +19,29 @@ namespace mRemoteNG.App
private static void CheckFipsPolicy(MessageCollector messageCollector)
{
if (Settings.Default.OverrideFIPSCheck)
{
messageCollector.AddMessage(MessageClass.InformationMsg, "OverrideFIPSCheck is set. Will skip check...", true);
return;
}
messageCollector.AddMessage(MessageClass.InformationMsg, "Checking FIPS Policy...", true);
if (!FipsPolicyEnabledForServer2003() && !FipsPolicyEnabledForServer2008AndNewer()) return;
var errorText = string.Format(Language.strErrorFipsPolicyIncompatible, GeneralAppInfo.ProductName, GeneralAppInfo.ProductName);
var errorText = string.Format(Language.strErrorFipsPolicyIncompatible, GeneralAppInfo.ProductName);
messageCollector.AddMessage(MessageClass.ErrorMsg, errorText, true);
MessageBox.Show(FrmMain.Default, errorText, GeneralAppInfo.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
var CrashOverride = MessageBox.Show(FrmMain.Default, "TEST BUILD -- OK to test mRemoteNG with FIPS Enabled.\nCancel to Exit.", GeneralAppInfo.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
if (CrashOverride == DialogResult.OK)
var ShouldIStayOrShouldIGo = CTaskDialog.MessageBox(Application.ProductName, Language.strCompatibilityProblemDetected, errorText, "", "", Language.strCheckboxDoNotShowThisMessageAgain, ETaskDialogButtons.OkCancel, ESysIcons.Warning, ESysIcons.Warning);
if (CTaskDialog.VerificationChecked && ShouldIStayOrShouldIGo == DialogResult.OK)
{
messageCollector.AddMessage(MessageClass.ErrorMsg, "User requests that FIPS check be overridden", true);
Settings.Default.OverrideFIPSCheck = true;
Settings.Default.Save();
return;
}
Environment.Exit(1);
if (ShouldIStayOrShouldIGo == DialogResult.Cancel)
Environment.Exit(1);
}
private static bool FipsPolicyEnabledForServer2003()

View File

@@ -3,13 +3,14 @@ using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using mRemoteNG.Config.Import;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Container;
using mRemoteNG.Tools;
namespace mRemoteNG.App
{
public static class Import
public static class Import
{
public static void ImportFromFile(ContainerInfo importDestinationContainer)
{
@@ -35,22 +36,12 @@ namespace mRemoteNG.App
if (openFileDialog.ShowDialog() != DialogResult.OK)
return;
foreach (var fileName in openFileDialog.FileNames)
{
try
{
var importer = BuildConnectionImporterFromFileExtension(fileName);
importer.Import(fileName, importDestinationContainer);
}
catch (Exception ex)
{
MessageBox.Show(string.Format(Language.strImportFileFailedContent, fileName), Language.strImportFileFailedMainInstruction,
MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
Runtime.MessageCollector.AddExceptionMessage("Unable to import file.", ex);
}
}
Runtime.ConnectionsService.SaveConnectionsAsync();
HeadlessFileImport(
openFileDialog.FileNames,
importDestinationContainer,
Runtime.ConnectionsService,
fileName => MessageBox.Show(string.Format(Language.strImportFileFailedContent, fileName), Language.strImportFileFailedMainInstruction,
MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1));
}
}
catch (Exception ex)
@@ -59,12 +50,38 @@ namespace mRemoteNG.App
}
}
public static void HeadlessFileImport(
IEnumerable<string> filePaths,
ContainerInfo importDestinationContainer,
ConnectionsService connectionsService,
Action<string> exceptionAction = null)
{
using (connectionsService.BatchedSavingContext())
{
foreach (var fileName in filePaths)
{
try
{
var importer = BuildConnectionImporterFromFileExtension(fileName);
importer.Import(fileName, importDestinationContainer);
}
catch (Exception ex)
{
exceptionAction?.Invoke(fileName);
Runtime.MessageCollector.AddExceptionMessage($"Error occurred while importing file '{fileName}'.", ex);
}
}
}
}
public static void ImportFromActiveDirectory(string ldapPath, ContainerInfo importDestinationContainer, bool importSubOu)
{
try
{
ActiveDirectoryImporter.Import(ldapPath, importDestinationContainer, importSubOu);
Runtime.ConnectionsService.SaveConnectionsAsync();
using (Runtime.ConnectionsService.BatchedSavingContext())
{
ActiveDirectoryImporter.Import(ldapPath, importDestinationContainer, importSubOu);
}
}
catch (Exception ex)
{
@@ -76,9 +93,11 @@ namespace mRemoteNG.App
{
try
{
var importer = new PortScanImporter(protocol);
importer.Import(hosts, importDestinationContainer);
Runtime.ConnectionsService.SaveConnectionsAsync();
using (Runtime.ConnectionsService.BatchedSavingContext())
{
var importer = new PortScanImporter(protocol);
importer.Import(hosts, importDestinationContainer);
}
}
catch (Exception ex)
{

View File

@@ -1,23 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Config.Serializers.MsSql;
using mRemoteNG.Config.Serializers.Versioning;
using mRemoteNG.Container;
using mRemoteNG.Tools;
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;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Connections
{
public class SqlConnectionsLoader : IConnectionsLoader
public class SqlConnectionsLoader : IConnectionsLoader
{
private readonly IDeserializer<string, IEnumerable<LocalConnectionPropertiesModel>> _localConnectionPropertiesDeserializer;
private readonly IDataProvider<string> _dataProvider;
@@ -41,7 +41,8 @@ namespace mRemoteNG.Config.Connections
var databaseVersionVerifier = new SqlDatabaseVersionVerifier(connector);
var cryptoProvider = new LegacyRijndaelCryptographyProvider();
var metaData = metaDataRetriever.GetDatabaseMetaData(connector);
var metaData = metaDataRetriever.GetDatabaseMetaData(connector) ??
HandleFirstRun(metaDataRetriever, connector);
var decryptionKey = GetDecryptionKey(metaData);
if (!decryptionKey.Any())
@@ -85,5 +86,11 @@ namespace mRemoteNG.Config.Connections
container.IsExpanded = x.LocalProperties.Expanded;
});
}
private SqlConnectionListMetaData HandleFirstRun(SqlDatabaseMetaDataRetriever metaDataRetriever, SqlDatabaseConnector connector)
{
metaDataRetriever.WriteDatabaseMetaData(new RootNodeInfo(RootNodeType.Connection), connector);
return metaDataRetriever.GetDatabaseMetaData(connector);
}
}
}

View File

@@ -1,10 +1,16 @@
using mRemoteNG.App;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Config.Serializers.MsSql;
using mRemoteNG.Config.Serializers.Versioning;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using mRemoteNG.Security;
@@ -12,19 +18,11 @@ using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Security;
using mRemoteNG.Connection;
namespace mRemoteNG.Config.Connections
{
public class SqlConnectionsSaver : ISaver<ConnectionTreeModel>
public class SqlConnectionsSaver : ISaver<ConnectionTreeModel>
{
private SecureString _password = Runtime.EncryptionKey;
private readonly SaveFilter _saveFilter;
private readonly ISerializer<IEnumerable<LocalConnectionPropertiesModel>, string> _localPropertiesSerializer;
private readonly IDataProvider<string> _dataProvider;
@@ -73,7 +71,7 @@ namespace mRemoteNG.Config.Connections
return;
}
UpdateRootNodeTable(rootTreeNode, sqlConnector);
metaDataRetriever.WriteDatabaseMetaData(rootTreeNode, sqlConnector);
UpdateConnectionsTable(rootTreeNode, sqlConnector);
UpdateUpdatesTable(sqlConnector);
}
@@ -117,17 +115,17 @@ namespace mRemoteNG.Config.Connections
{
if (rootTreeNode.Password)
{
_password = rootTreeNode.PasswordString.ConvertToSecureString();
strProtected = cryptographyProvider.Encrypt("ThisIsProtected", _password);
var password = rootTreeNode.PasswordString.ConvertToSecureString();
strProtected = cryptographyProvider.Encrypt("ThisIsProtected", password);
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", _password);
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", Runtime.EncryptionKey);
}
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", _password);
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", Runtime.EncryptionKey);
}
var sqlQuery = new SqlCommand("DELETE FROM tblRoot", sqlDatabaseConnector.SqlConnection);

View File

@@ -266,8 +266,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
dataRow["RDGatewayUsageMethod"] = connectionInfo.RDGatewayUsageMethod;
dataRow["RDGatewayHostname"] = connectionInfo.RDGatewayHostname;
dataRow["RDGatewayUseConnectionCredentials"] = connectionInfo.RDGatewayUseConnectionCredentials;
dataRow["RDGatewayUsername"] = _cryptographyProvider.Encrypt(connectionInfo.RDGatewayUsername, _encryptionKey);
dataRow["RDGatewayPassword"] = connectionInfo.RDGatewayPassword;
dataRow["RDGatewayUsername"] = connectionInfo.RDGatewayUsername;
dataRow["RDGatewayPassword"] = _cryptographyProvider.Encrypt(connectionInfo.RDGatewayPassword, _encryptionKey);
dataRow["RDGatewayDomain"] = connectionInfo.RDGatewayDomain;
if (_saveFilter.SaveInheritance)
{

View File

@@ -2,12 +2,17 @@
using System.Data.SqlClient;
using System.Globalization;
using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Messages;
using mRemoteNG.Security;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers.MsSql
{
public class SqlDatabaseMetaDataRetriever
public class SqlDatabaseMetaDataRetriever
{
public SqlConnectionListMetaData GetDatabaseMetaData(SqlDatabaseConnector sqlDatabaseConnector)
{
@@ -44,5 +49,45 @@ namespace mRemoteNG.Config.Serializers.MsSql
}
return metaData;
}
public void WriteDatabaseMetaData(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector)
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
string strProtected;
if (rootTreeNode != null)
{
if (rootTreeNode.Password)
{
var password = rootTreeNode.PasswordString.ConvertToSecureString();
strProtected = cryptographyProvider.Encrypt("ThisIsProtected", password);
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", Runtime.EncryptionKey);
}
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", Runtime.EncryptionKey);
}
var sqlQuery = new SqlCommand("DELETE FROM tblRoot", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
if (rootTreeNode != null)
{
sqlQuery =
new SqlCommand(
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES(\'" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "\', 0, \'" + strProtected + "\'," +
ConnectionsFileInfo.ConnectionFileVersion.ToString(CultureInfo.InvariantCulture) + ")",
sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
}
else
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, $"UpdateRootNodeTable: rootTreeNode was null. Could not insert!");
}
}
}
}

View File

@@ -13,7 +13,7 @@ using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Serializers
{
public class RemoteDesktopConnectionManagerDeserializer : IDeserializer<string, ConnectionTreeModel>
public class RemoteDesktopConnectionManagerDeserializer : IDeserializer<string, ConnectionTreeModel>
{
private static int _schemaVersion; /* 1 = RDCMan v2.2
3 = RDCMan v2.7 */
@@ -40,11 +40,15 @@ namespace mRemoteNG.Config.Serializers
private static void VerifySchemaVersion(XmlNode rdcManNode)
{
_schemaVersion = Convert.ToInt32(rdcManNode?.Attributes?["schemaVersion"].Value);
if (_schemaVersion != 1 && _schemaVersion != 3)
if (!int.TryParse(rdcManNode?.Attributes?["schemaVersion"]?.Value, out var version))
throw new FileFormatException("Could not find schema version attribute.");
if (version != 1 && version != 3)
{
throw (new FileFormatException($"Unsupported schema version ({_schemaVersion})."));
throw new FileFormatException($"Unsupported schema version ({version}).");
}
_schemaVersion = version;
}
private static void VerifyFileVersion(XmlNode rdcManNode)
@@ -114,7 +118,8 @@ namespace mRemoteNG.Config.Serializers
containerPropertiesNode = containerPropertiesNode.SelectSingleNode("./properties");
}
newContainer.Name = containerPropertiesNode?.SelectSingleNode("./name")?.InnerText ?? Language.strNewFolder;
newContainer.IsExpanded = bool.Parse(containerPropertiesNode?.SelectSingleNode("./expanded")?.InnerText ?? "false");
if (bool.TryParse(containerPropertiesNode?.SelectSingleNode("./expanded")?.InnerText, out var expanded))
newContainer.IsExpanded = expanded;
parentContainer.AddChild(newContainer);
return newContainer;
}
@@ -129,17 +134,25 @@ namespace mRemoteNG.Config.Serializers
{
var connectionInfo = new ConnectionInfo {Protocol = ProtocolType.RDP};
var propertiesNode = xmlNode.SelectSingleNode("./properties");
if (_schemaVersion == 1) propertiesNode = xmlNode; // Version 2.2 defines the container name at the root instead
if (_schemaVersion == 1)
propertiesNode = xmlNode; // Version 2.2 defines the container name at the root instead
connectionInfo.Hostname = propertiesNode?.SelectSingleNode("./name")?.InnerText ?? "";
connectionInfo.Name = propertiesNode?.SelectSingleNode("./displayName")?.InnerText ?? connectionInfo.Hostname;
var connectionDisplayName = propertiesNode?.SelectSingleNode("./displayName")?.InnerText;
connectionInfo.Name = !string.IsNullOrWhiteSpace(connectionDisplayName)
? connectionDisplayName
: string.IsNullOrWhiteSpace(connectionInfo.Hostname)
? connectionInfo.Name
: connectionInfo.Hostname;
connectionInfo.Description = propertiesNode?.SelectSingleNode("./comment")?.InnerText ?? string.Empty;
var logonCredentialsNode = xmlNode.SelectSingleNode("./logonCredentials");
if (logonCredentialsNode?.Attributes?["inherit"]?.Value == "None")
{
connectionInfo.Username = logonCredentialsNode.SelectSingleNode("userName")?.InnerText;
connectionInfo.Username = logonCredentialsNode.SelectSingleNode("userName")?.InnerText ?? string.Empty;
var passwordNode = logonCredentialsNode.SelectSingleNode("./password");
if (_schemaVersion == 1) // Version 2.2 allows clear text passwords
@@ -153,7 +166,7 @@ namespace mRemoteNG.Config.Serializers
connectionInfo.Password = DecryptRdcManPassword(passwordNode?.InnerText);
}
connectionInfo.Domain = logonCredentialsNode.SelectSingleNode("./domain")?.InnerText;
connectionInfo.Domain = logonCredentialsNode.SelectSingleNode("./domain")?.InnerText ?? string.Empty;
}
else
{
@@ -165,10 +178,12 @@ namespace mRemoteNG.Config.Serializers
var connectionSettingsNode = xmlNode.SelectSingleNode("./connectionSettings");
if (connectionSettingsNode?.Attributes?["inherit"]?.Value == "None")
{
connectionInfo.UseConsoleSession = bool.Parse(connectionSettingsNode.SelectSingleNode("./connectToConsole")?.InnerText ?? "false");
if (bool.TryParse(connectionSettingsNode.SelectSingleNode("./connectToConsole")?.InnerText, out var useConsole))
connectionInfo.UseConsoleSession = useConsole;
// ./startProgram
// ./workingDir
connectionInfo.Port = Convert.ToInt32(connectionSettingsNode.SelectSingleNode("./port")?.InnerText);
if (int.TryParse(connectionSettingsNode.SelectSingleNode("./port")?.InnerText, out var port))
connectionInfo.Port = port;
}
else
{
@@ -179,14 +194,18 @@ namespace mRemoteNG.Config.Serializers
var gatewaySettingsNode = xmlNode.SelectSingleNode("./gatewaySettings");
if (gatewaySettingsNode?.Attributes?["inherit"]?.Value == "None")
{
connectionInfo.RDGatewayUsageMethod = gatewaySettingsNode.SelectSingleNode("./enabled")?.InnerText == "True" ? RdpProtocol.RDGatewayUsageMethod.Always : RdpProtocol.RDGatewayUsageMethod.Never;
connectionInfo.RDGatewayHostname = gatewaySettingsNode.SelectSingleNode("./hostName")?.InnerText;
connectionInfo.RDGatewayUsername = gatewaySettingsNode.SelectSingleNode("./userName")?.InnerText;
connectionInfo.RDGatewayUsageMethod = gatewaySettingsNode.SelectSingleNode("./enabled")?.InnerText == "True"
? RdpProtocol.RDGatewayUsageMethod.Always
: RdpProtocol.RDGatewayUsageMethod.Never;
connectionInfo.RDGatewayHostname = gatewaySettingsNode.SelectSingleNode("./hostName")?.InnerText ?? string.Empty;
connectionInfo.RDGatewayUsername = gatewaySettingsNode.SelectSingleNode("./userName")?.InnerText ?? string.Empty;
var passwordNode = gatewaySettingsNode.SelectSingleNode("./password");
connectionInfo.RDGatewayPassword = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True" ? passwordNode.InnerText : DecryptRdcManPassword(passwordNode?.InnerText);
connectionInfo.RDGatewayPassword = passwordNode?.Attributes?["storeAsClearText"]?.Value == "True"
? passwordNode.InnerText
: DecryptRdcManPassword(passwordNode?.InnerText);
connectionInfo.RDGatewayDomain = gatewaySettingsNode.SelectSingleNode("./domain")?.InnerText;
connectionInfo.RDGatewayDomain = gatewaySettingsNode.SelectSingleNode("./domain")?.InnerText ?? string.Empty;
// ./logonMethod
// ./localBypass
// ./credSharing
@@ -203,15 +222,10 @@ namespace mRemoteNG.Config.Serializers
var remoteDesktopNode = xmlNode.SelectSingleNode("./remoteDesktop");
if (remoteDesktopNode?.Attributes?["inherit"]?.Value == "None")
{
var resolutionString = remoteDesktopNode.SelectSingleNode("./size")?.InnerText.Replace(" ", "");
try
{
connectionInfo.Resolution = (RdpProtocol.RDPResolutions)Enum.Parse(typeof(RdpProtocol.RDPResolutions), "Res" + resolutionString);
}
catch (ArgumentException)
{
connectionInfo.Resolution = RdpProtocol.RDPResolutions.FitToWindow;
}
connectionInfo.Resolution =
Enum.TryParse<RdpProtocol.RDPResolutions>(remoteDesktopNode.SelectSingleNode("./size")?.InnerText.Replace(" ", ""), true, out var rdpResolution)
? rdpResolution
: RdpProtocol.RDPResolutions.FitToWindow;
if (remoteDesktopNode.SelectSingleNode("./sameSizeAsClientArea")?.InnerText == "True")
{
@@ -223,9 +237,8 @@ namespace mRemoteNG.Config.Serializers
connectionInfo.Resolution = RdpProtocol.RDPResolutions.Fullscreen;
}
var colorDepth = remoteDesktopNode.SelectSingleNode("./colorDepth")?.InnerText;
if (colorDepth != null)
connectionInfo.Colors = (RdpProtocol.RDPColors)Enum.Parse(typeof(RdpProtocol.RDPColors), colorDepth);
if (Enum.TryParse<RdpProtocol.RDPColors>(remoteDesktopNode.SelectSingleNode("./colorDepth")?.InnerText, true, out var rdpColors))
connectionInfo.Colors = rdpColors;
}
else
{
@@ -274,10 +287,17 @@ namespace mRemoteNG.Config.Serializers
}
// ./redirectClipboard
connectionInfo.RedirectDiskDrives = bool.Parse(localResourcesNode?.SelectSingleNode("./redirectDrives")?.InnerText ?? "false");
connectionInfo.RedirectPorts = bool.Parse(localResourcesNode?.SelectSingleNode("./redirectPorts")?.InnerText ?? "false");
connectionInfo.RedirectPrinters = bool.Parse(localResourcesNode?.SelectSingleNode("./redirectPrinters")?.InnerText ?? "false");
connectionInfo.RedirectSmartCards = bool.Parse(localResourcesNode?.SelectSingleNode("./redirectSmartCards")?.InnerText ?? "false");
if (bool.TryParse(localResourcesNode?.SelectSingleNode("./redirectDrives")?.InnerText, out var redirectDisks))
connectionInfo.RedirectDiskDrives = redirectDisks;
if (bool.TryParse(localResourcesNode?.SelectSingleNode("./redirectPorts")?.InnerText, out var redirectPorts))
connectionInfo.RedirectPorts = redirectPorts;
if (bool.TryParse(localResourcesNode?.SelectSingleNode("./redirectPrinters")?.InnerText, out var redirectPrinters))
connectionInfo.RedirectPrinters = redirectPrinters;
if (bool.TryParse(localResourcesNode?.SelectSingleNode("./redirectSmartCards")?.InnerText, out var redirectSmartCards))
connectionInfo.RedirectSmartCards = redirectSmartCards;
}
else
{

View File

@@ -1,5 +1,10 @@
using mRemoteNG.App;
using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.Config;
using mRemoteNG.Config.Connections;
using mRemoteNG.Config.Connections.Multiuser;
using mRemoteNG.Config.DataProviders;
@@ -12,15 +17,10 @@ using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI;
using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.Config;
namespace mRemoteNG.Connection
{
public class ConnectionsService
public class ConnectionsService
{
private static readonly object SaveLock = new object();
private readonly PuttySessionsManager _puttySessionsManager;
@@ -171,6 +171,19 @@ namespace mRemoteNG.Connection
SaveConnections();
}
/// <summary>
/// All calls to <see cref="SaveConnections()"/> or <see cref="SaveConnectionsAsync"/>
/// will be deferred until the returned <see cref="DisposableAction"/> is disposed.
/// Once disposed, this will immediately executes a single <see cref="SaveConnections()"/>
/// or <see cref="SaveConnectionsAsync"/> if one has been requested.
/// Place this call in a 'using' block to represent a batched saving context.
/// </summary>
/// <returns></returns>
public DisposableAction BatchedSavingContext()
{
return new DisposableAction(BeginBatchingSaves, EndBatchingSaves);
}
/// <summary>
/// Saves the currently loaded <see cref="ConnectionTreeModel"/> with
/// no <see cref="SaveFilter"/>.

View File

@@ -117,57 +117,55 @@ namespace mRemoteNG.Connection.Protocol.ICA
return;
}
var user = _info?.Username ?? "";
var pass = _info?.Password ?? "";
var dom = _info?.Domain ?? "";
var user = _info?.Username ?? "";
var pass = _info?.Password ?? "";
var dom = _info?.Domain ?? "";
if (string.IsNullOrEmpty(user))
{
if (Settings.Default.EmptyCredentials == "windows")
{
_icaClient.Username = Environment.UserName;
user = Environment.UserName;
}
else if (Settings.Default.EmptyCredentials == "custom")
else if (Settings.Default.EmptyCredentials == "custom" || Settings.Default.EmptyCredentials == "admpwd")
{
_icaClient.Username = Settings.Default.DefaultUsername;
user = Settings.Default.DefaultUsername;
}
}
else
if (string.IsNullOrEmpty(dom))
{
_icaClient.Username = user;
if (Settings.Default.EmptyCredentials == "windows")
{
dom = Environment.UserDomainName;
}
else if (Settings.Default.EmptyCredentials == "custom" || Settings.Default.EmptyCredentials == "admpwd")
{
dom = Settings.Default.DefaultDomain;
}
}
_icaClient.Username = user;
_icaClient.Domain = dom;
if (string.IsNullOrEmpty(pass))
{
if (Settings.Default.EmptyCredentials == "custom")
{
if (Settings.Default.DefaultPassword != "")
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
_icaClient.SetProp("ClearPassword", cryptographyProvider.Decrypt(Settings.Default.DefaultPassword, Runtime.EncryptionKey));
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
pass = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword, Runtime.EncryptionKey);
}
}
}
else
{
_icaClient.SetProp("ClearPassword", pass);
}
if (string.IsNullOrEmpty(dom))
{
if (Settings.Default.EmptyCredentials == "windows")
if (Settings.Default.EmptyCredentials == "admpwd")
{
_icaClient.Domain = Environment.UserDomainName;
}
else if (Settings.Default.EmptyCredentials == "custom")
{
_icaClient.Domain = Settings.Default.DefaultDomain;
if (dom == ".")
pass = AdmPwd.PDSUtils.PdsWrapper.GetPassword(null, _info.Hostname, AdmPwd.Types.IdentityType.LocalComputerAdmin, false, false).Password;
else
pass = AdmPwd.PDSUtils.PdsWrapper.GetPassword(dom, user, AdmPwd.Types.IdentityType.ManagedDomainAccount, false, false).Password;
}
}
else
{
_icaClient.Domain = dom;
}
_icaClient.SetProp("ClearPassword", pass);
}
catch (Exception ex)
{

View File

@@ -13,7 +13,7 @@ using mRemoteNG.Tools.Cmdline;
namespace mRemoteNG.Connection.Protocol
{
public class PuttyBase : ProtocolBase
{
{
private const int IDM_RECONF = 0x50; // PuTTY Settings Menu ID
private bool _isPuttyNg;
@@ -42,7 +42,7 @@ namespace mRemoteNG.Connection.Protocol
Event_Closed(this);
}
#endregion
#region Public Methods
public override bool Connect()
{
@@ -62,11 +62,11 @@ namespace mRemoteNG.Connection.Protocol
var arguments = new CommandLineArguments {EscapeForShell = false};
arguments.Add("-load", InterfaceControl.Info.PuttySession);
if (!(InterfaceControl.Info is PuttySessionInfo))
{
arguments.Add("-" + PuttyProtocol);
if (PuttyProtocol == Putty_Protocol.ssh)
{
var username = "";
@@ -89,7 +89,7 @@ namespace mRemoteNG.Connection.Protocol
break;
}
}
if (!string.IsNullOrEmpty(InterfaceControl.Info?.Password))
{
password = InterfaceControl.Info.Password;
@@ -102,9 +102,9 @@ namespace mRemoteNG.Connection.Protocol
password = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword, Runtime.EncryptionKey);
}
}
arguments.Add("-" + (int)PuttySSHVersion);
if (((int)Force & (int)ConnectionInfo.Force.NoCredentials) != (int)ConnectionInfo.Force.NoCredentials)
{
if (!string.IsNullOrEmpty(username))
@@ -117,24 +117,24 @@ namespace mRemoteNG.Connection.Protocol
}
}
}
arguments.Add("-P", InterfaceControl.Info.Port.ToString());
arguments.Add(InterfaceControl.Info.Hostname);
}
if (_isPuttyNg)
{
arguments.Add("-hwndparent", InterfaceControl.Handle.ToString());
}
PuttyProcess.StartInfo.Arguments = arguments.ToString();
PuttyProcess.EnableRaisingEvents = true;
PuttyProcess.Exited += ProcessExited;
PuttyProcess.Start();
PuttyProcess.WaitForInputIdle(Settings.Default.MaxPuttyWaitTime * 1000);
var startTicks = Environment.TickCount;
while (PuttyHandle.ToInt32() == 0 & Environment.TickCount < startTicks + Settings.Default.MaxPuttyWaitTime * 1000)
{
@@ -153,17 +153,17 @@ namespace mRemoteNG.Connection.Protocol
Thread.Sleep(0);
}
}
if (!_isPuttyNg)
{
NativeMethods.SetParent(PuttyHandle, InterfaceControl.Handle);
}
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.strPuttyStuff, true);
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, string.Format(Language.strPuttyHandle, PuttyHandle), true);
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, string.Format(Language.strPuttyTitle, PuttyProcess.MainWindowTitle), true);
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, string.Format(Language.strPuttyParentHandle, InterfaceControl.Parent.Handle), true);
Resize(this, new EventArgs());
base.Connect();
return true;
@@ -174,7 +174,7 @@ namespace mRemoteNG.Connection.Protocol
return false;
}
}
public override void Focus()
{
try
@@ -190,7 +190,7 @@ namespace mRemoteNG.Connection.Protocol
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strPuttyFocusFailed + Environment.NewLine + ex.Message, true);
}
}
public override void Resize(object sender, EventArgs e)
{
try
@@ -200,20 +200,28 @@ namespace mRemoteNG.Connection.Protocol
return;
}
var left = -(SystemInformation.FrameBorderSize.Width + SystemInformation.HorizontalResizeBorderThickness);
var top = -(SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height + SystemInformation.VerticalResizeBorderThickness);
var width = InterfaceControl.Width + (SystemInformation.FrameBorderSize.Width + SystemInformation.HorizontalResizeBorderThickness) * 2;
var height = InterfaceControl.Height + SystemInformation.CaptionHeight +
(SystemInformation.FrameBorderSize.Height + SystemInformation.VerticalResizeBorderThickness) * 2;
if (_isPuttyNg)
{
// PuTTYNG 0.70.0.1 and later doesn't have any window borders
NativeMethods.MoveWindow(PuttyHandle, 0, 0, InterfaceControl.Width, InterfaceControl.Height, true);
}
else
{
var left = -(SystemInformation.FrameBorderSize.Width + SystemInformation.HorizontalResizeBorderThickness);
var top = -(SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height + SystemInformation.VerticalResizeBorderThickness);
var width = InterfaceControl.Width + (SystemInformation.FrameBorderSize.Width + SystemInformation.HorizontalResizeBorderThickness) * 2;
var height = InterfaceControl.Height + SystemInformation.CaptionHeight + (SystemInformation.FrameBorderSize.Height +
SystemInformation.VerticalResizeBorderThickness) * 2;
NativeMethods.MoveWindow(PuttyHandle, left, top, width, height, true);
}
NativeMethods.MoveWindow(PuttyHandle, left, top, width, height, true);
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strPuttyResizeFailed + Environment.NewLine + ex.Message, true);
}
}
public override void Close()
{
try
@@ -227,7 +235,7 @@ namespace mRemoteNG.Connection.Protocol
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strPuttyKillFailed + Environment.NewLine + ex.Message, true);
}
try
{
PuttyProcess.Dispose();
@@ -236,10 +244,10 @@ namespace mRemoteNG.Connection.Protocol
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strPuttyDisposeFailed + Environment.NewLine + ex.Message, true);
}
base.Close();
}
public void ShowSettingsDialog()
{
try
@@ -253,7 +261,7 @@ namespace mRemoteNG.Connection.Protocol
}
}
#endregion
#region Enums
protected enum Putty_Protocol

View File

@@ -355,9 +355,12 @@ namespace mRemoteNG.Connection.Protocol.RDP
{
if (_connectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.Yes)
{
_rdpClient.TransportSettings2.GatewayUsername = _connectionInfo.Username;
_rdpClient.TransportSettings2.GatewayPassword = _connectionInfo.Password;
_rdpClient.TransportSettings2.GatewayDomain = _connectionInfo?.Domain;
var userName = GetUserName(_connectionInfo?.Username ?? "");
var domain = GetDomain(_connectionInfo?.Domain ?? "");
_rdpClient.TransportSettings2.GatewayUsername = userName;
_rdpClient.TransportSettings2.GatewayDomain = domain;
_rdpClient.TransportSettings2.GatewayPassword = GetPassword((_connectionInfo?.Password ?? ""), userName, domain, _connectionInfo.Hostname);
}
else if (_connectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.SmartCard)
{
@@ -365,11 +368,14 @@ namespace mRemoteNG.Connection.Protocol.RDP
}
else
{
_rdpClient.TransportSettings2.GatewayUsername = _connectionInfo.RDGatewayUsername;
_rdpClient.TransportSettings2.GatewayPassword = _connectionInfo.RDGatewayPassword;
_rdpClient.TransportSettings2.GatewayDomain = _connectionInfo.RDGatewayDomain;
_rdpClient.TransportSettings2.GatewayCredSharing = 0;
}
var userName = GetUserName(_connectionInfo.RDGatewayUsername);
var domain = GetDomain(_connectionInfo.RDGatewayDomain);
_rdpClient.TransportSettings2.GatewayUsername = userName;
_rdpClient.TransportSettings2.GatewayDomain = domain;
_rdpClient.TransportSettings2.GatewayPassword = GetPassword(_connectionInfo.RDGatewayPassword, userName, domain, _connectionInfo.Hostname);
_rdpClient.TransportSettings2.GatewayCredSharing = 0;
}
}
}
}
@@ -416,7 +422,60 @@ namespace mRemoteNG.Connection.Protocol.RDP
Runtime.MessageCollector.AddExceptionStackTrace(Language.strRdpSetConsoleSessionFailed, ex);
}
}
private string GetUserName(string userName)
{
if (string.IsNullOrEmpty(userName))
{
if (Settings.Default.EmptyCredentials == "windows")
{
userName = Environment.UserName;
}
else if (Settings.Default.EmptyCredentials == "custom" || Settings.Default.EmptyCredentials == "admpwd")
{
userName = Settings.Default.DefaultUsername;
}
}
return userName;
}
private string GetDomain(string domain)
{
if (string.IsNullOrEmpty(domain))
{
if (Settings.Default.EmptyCredentials == "windows")
{
domain = Environment.UserDomainName;
}
else if (Settings.Default.EmptyCredentials == "custom" || Settings.Default.EmptyCredentials == "admpwd")
{
domain = Settings.Default.DefaultDomain;
}
}
return domain;
}
private string GetPassword(string password, string userName, string domain, string host)
{
if (string.IsNullOrEmpty(password))
{
if (Settings.Default.EmptyCredentials == "custom")
{
if (Settings.Default.DefaultPassword != "")
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
password = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword, Runtime.EncryptionKey);
}
}
if (Settings.Default.EmptyCredentials == "admpwd")
{
if (domain == ".")
password = AdmPwd.PDSUtils.PdsWrapper.GetPassword(null, host, AdmPwd.Types.IdentityType.LocalComputerAdmin, false, false).Password;
else
password = AdmPwd.PDSUtils.PdsWrapper.GetPassword(domain, userName, AdmPwd.Types.IdentityType.ManagedDomainAccount, false, false).Password;
}
}
return password;
}
private void SetCredentials()
{
try
@@ -426,57 +485,13 @@ namespace mRemoteNG.Connection.Protocol.RDP
return;
}
var userName = _connectionInfo?.Username ?? "";
var password = _connectionInfo?.Password ?? "";
var domain = _connectionInfo?.Domain ?? "";
if (string.IsNullOrEmpty(userName))
{
if (Settings.Default.EmptyCredentials == "windows")
{
_rdpClient.UserName = Environment.UserName;
}
else if (Settings.Default.EmptyCredentials == "custom")
{
_rdpClient.UserName = Settings.Default.DefaultUsername;
}
}
else
{
_rdpClient.UserName = userName;
}
if (string.IsNullOrEmpty(password))
{
if (Settings.Default.EmptyCredentials == "custom")
{
if (Settings.Default.DefaultPassword != "")
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
_rdpClient.AdvancedSettings2.ClearTextPassword = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword, Runtime.EncryptionKey);
}
}
}
else
{
_rdpClient.AdvancedSettings2.ClearTextPassword = password;
}
if (string.IsNullOrEmpty(domain))
{
if (Settings.Default.EmptyCredentials == "windows")
{
_rdpClient.Domain = Environment.UserDomainName;
}
else if (Settings.Default.EmptyCredentials == "custom")
{
_rdpClient.Domain = Settings.Default.DefaultDomain;
}
}
else
{
_rdpClient.Domain = domain;
}
var userName = GetUserName(_connectionInfo?.Username ?? "");
var domain = GetDomain(_connectionInfo?.Domain ?? "");
_rdpClient.Domain = domain;
_rdpClient.UserName = userName;
_rdpClient.AdvancedSettings2.ClearTextPassword = GetPassword((_connectionInfo?.Password ?? ""), userName, domain, _connectionInfo.Hostname);
}
catch (Exception ex)
{

View File

@@ -0,0 +1,357 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--Management interface-->
<PdsConfigWrapper>
<GetPds>
<summary>
Returns list of PDS discovered either form DNS or from GPO
</summary>
<returns>Returns list of PDS instances discovered</returns>
<remarks>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetPds>
<GetSupportedForest>
<summary>
Calls PDS to get list of supported forests as configured in PDS configuration file.
</summary>
<param name="Pds">
Identifies PDS instance to query list of supported forests.
</param>
<returns>List of names of AD forests as known by given PDS instance (optionaly with connection credentials and id of enryption key that is used to protect them is configuration file), or null if no specific AD forests are configured and PDS just covers local AD forest.</returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</GetSupportedForest>
<AddSupportedForest>
<summary>
Calls PDS to add new AD forest to list of supported forests.
</summary>
<param name="Pds">
Identifies PDS instance that will be target of the operation. If operating more intances of PDS, supported AD forest must be added to each of them, so as PDS configuration is consistent across all instances.
</param>
<param name="Forest">
Specifies parameters of the newly supported forest
</param>
<returns>Newly added supported forest.</returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</AddSupportedForest>
<SetSupportedForest>
<summary>
Calls PDS to update supported forest parameters (typically connection credentials).
</summary>
<param name="Pds">
Identifies PDS instance to perform the operation on. If operating more intances of PDS, update must be performed on each of them to ensure consistency of configuration across all instances of PDS.
</param>
<param name="Forest">
Specifies parameters to be updated on the forest
</param>
<returns>Updated supported forest.</returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</SetSupportedForest>
<RemoveSupportedForest>
<summary>
Calls PDS to add remove AD forest from list of supported forests.
</summary>
<param name="Pds">
Identifies PDS instance to remove the forest from list of supported forests. If operating more intances of PDS, supported AD forest must be removed from each of them.
</param>
<param name="Forest">
Specifies parameters of the removed forest. Only DNS name of Forest is used to identify forest to be removed.
</param>
<returns>Removed forest.</returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</RemoveSupportedForest>
<AddSidMapping>
<summary>
Calls PDS to add maping of security principal from untrusted forest to security principal from trusted forest.
</summary>
<param name="Pds">
Identifies PDS instance to work with. If operating more intances of PDS, SID mapping must be added to each of them to ensure configuration consistency.
</param>
<param name="Mapping">
Specifies parameters of the SID mapping.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</AddSidMapping>
<UpdateSidMapping>
<summary>
Calls PDS to modify maping of security principal from untrusted forest to security principal from trusted forest.
</summary>
<param name="Pds">
Identifies PDS instance to work with. If operating more intances of PDS, SID mapping must be updated on each of them to ensure configuration consistency.
</param>
<param name="Mapping">
Specifies parameters of the SID mapping.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</UpdateSidMapping>
<GetSidMapping>
<summary>
Calls PDS to get configured mapings of security principal from untrusted forest to security principal from trusted forest.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<returns>List of configured SID mappings</returns>
<remarks>
<para>Can be called by any authenticated user</para>
<para>Call is not audited</para>
</remarks>
</GetSidMapping>
<RemoveSidMapping>
<summary>
Calls PDS to add remove SID mappings from list of mappings.
</summary>
<param name="Pds">
Identifies PDS instance to remove the mapping from. If operating more intances of PDS, mapping must be removed from each of them.
</param>
<param name="PrimarySid">
Specifies Primary SID of mapping to be removed.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</RemoveSidMapping>
<AddManagedAccountsContainer>
<summary>
Calls PDS to add AD container with accounts with automatically managed password.
</summary>
<param name="Pds">
Identifies PDS instance to work with. If operating more intances of PDS, container must be added to configuration of each of them to ensure configuration consistency.
</param>
<param name="Container">
Specifies parameters of password for accounts located in AD container.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</AddManagedAccountsContainer>
<GetManagedAccountsContainer>
<summary>
Calls PDS to retrieve configuration of AD containers with accounts with automatically managed password.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<returns>List of managed domain acocunts containers as defined in PDS configuration file.</returns>
<remarks>
<para>Can be called by any authenticated user</para>
<para>Call is not audited</para>
</remarks>
</GetManagedAccountsContainer>
<SetManagedAccountsContainer>
<summary>
Calls PDS to update configuration of AD container with accounts with automatically managed password.
</summary>
<param name="Pds">
Identifies PDS instance to work with. If operating more intances of PDS, container must be updated on each of them to ensure configuration consistency.
</param>
<param name="Container">
Specifies parameters of password for accounts located in given AD container.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</SetManagedAccountsContainer>
<RemoveManagedAccountsContainer>
<summary>
Calls PDS to remove AD container with accounts with automatically managed password from PDS configuration.
</summary>
<param name="Pds">
Identifies PDS instance to work with. If operating more intances of PDS, container must be removed from each of them to ensure configuration consistency.
</param>
<param name="DN">
Specifies distinguishedName of container to be removed from configuration.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</RemoveManagedAccountsContainer>
<TransferPdsAdminRole>
<summary>
Transfers PDS Admin role to security principal (user or group)
</summary>
<param name="Pds">
<para>Identifies PDS instance to work with.</para>
<para>Important: If operating more intances of PDS, configuration change must be performed on each of them to ensure configuration consistency.</para>
</param>
<param name="NewRoleHolder">
Name of new holder of PDS Admin role. Should include domain name.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</TransferPdsAdminRole>
<UpdateManagedAccountsParameters>
<summary>
Updates global configuration of managed domain accounts processing.<br/>
Note: Currently, only global parameter supported is interval of scanning of managed domain accounts for password expiration.
</summary>
<param name="Pds">
<para>Identifies PDS instance to work with.</para>
<para>Important: If operating more intances of PDS, configuration change must be performed on each of them to ensure configuration consistency.</para>
</param>
<param name="ManagementInterval">
Interval of scanning of managed domain accounts for password expiration.
</param>
<returns></returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</UpdateManagedAccountsParameters>
<GetManagedAccountsParameters>
<summary>
Returns global configuration of managed domain accounts processing.<br/>
Note: Currently, only global parameter supported is interval of scanning of managed domain accounts for password expiration.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<returns>Global configuration of managed domain accounts processing.</returns>
<remarks>
</remarks>
</GetManagedAccountsParameters>
<GetDnsParameters>
<summary>
Returns parameters of DNS registration for PDS autodiscover SRV records.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<returns>
<see cref="DnsParameters"/> object.
</returns>
<remarks>
</remarks>
</GetDnsParameters>
<UpdateDnsParameters>
<summary>
Updates parameters of DNS registration for PDS autodiscover SRV records.
</summary>
<param name="Pds">
<para>Identifies PDS instance to work with.</para>
<para>Important: If operating more intances of PDS, configuration change must be performed on each of them to ensure configuration consistency.</para>
</param>
<param name="Priority">Priority of SRV record. Null means default, which is 100.</param>
<param name="RegistrationInterval">How often PDS service re-registers autodiscover record to prevent its expiration. Null means default, which is 86400 seconds (1 day).</param>
<param name="Ttl">Time-to-live for registered SRV record. Null means default, which is 1200 seconds (20 minutes)</param>
<param name="UnregisterOnShutdown">Whether or not the SRV record shall be unregistered then services stops, to prevent autodiscover records pointing to non-operating PDS instance. Null means default, which is 'true'</param>
<param name="Weight">Weight of registered SRV record.<br/>
Note: Weight is not used by the solution.
</param>
<returns>
</returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</UpdateDnsParameters>
<UpdateAccessControlParameters>
<summary>
Updates parameters of access control process for password reads and resets.
</summary>
<param name="Pds">
<para>Identifies PDS instance to work with.</para>
<para>Important: If operating more intances of PDS, configuration change must be performed on each of them to ensure configuration consistency.</para>
</param>
<param name="HonorAllExtendedRightsPermission">Whether 'All extended rights' permission also includes 'Read password' and 'Reset password' permission.Null means default, which is 'false'.</param>
<param name="HonorFullControlPermission">Whether 'Full control' permission also includes 'Read password' and 'Reset password' permission.Null means default, which is 'false'.</param>
<param name="HonorLocalGroupsFromRemoteComputerDomain">
<para>
Whether PDS also evaluates membership in local groups from remote domain.
</para>
<para>
Consider the following scenario:
<list>
<item>PDS is installed in forest domain A</item>
<item>Computer account X we want know admin password for is in forest domain B</item>
<item>User U who wants to know admin password for computer X is also in forest domain B</item>
<item>User U is member of Domain local group DLG</item>
<item>Permission to read the admin password for computer X is delegated to group DLG</item>
</list>
In this case, when PDS is performing access check, it normally does NOT see user U being member of group DLG (because membership in Domain Local groups is not propagated to other domains in forest), and because of that, it replies with AccessDenied error. Setting this parameter to true makes PDS to perform direct lookup for local group membership of user in his home domain to make sure that complete membership is evaluated.
This additional lookup consumes PDS service resources and requires direct connection between PDS and domain controller of user's domain, so delegation using domain Local groups in multi-forest domains should be avoided.
</para>
</param>
<param name="MandatoryGroupSids">List of Security Identifiers (SIDs) that caller additionally must be member of for calls to password reads/resets to succeed. Membership in such groups is then additional gate that allows to perform active operations. If multiple groups specified, user must be member of at least 1 of them. This helps implement additional levels of access control, such as JIT of Authentication Mechanism Assurance (AMA)</param>
<returns>
</returns>
<remarks>
<para>PDS Admin role is required to successfully call this method</para>
<para>Call is audited</para>
</remarks>
</UpdateAccessControlParameters>
<GetAccessControlsParameters>
<summary>
Returns parameters of access control process for password reads and resets.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<returns>
<see cref="AccessControlParameters"/> object.
</returns>
<remarks>
</remarks>
</GetAccessControlsParameters>
<GetLicenseParameters>
<summary>
Returns parameters of current license, including path to license file.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<returns>
<see cref="LicenseParameters"/> object.
</returns>
<remarks>
</remarks>
</GetLicenseParameters>
<UpdateLicenseFilePath>
<summary>
Updates path to license file.
</summary>
<param name="Pds">
Identifies PDS instance to work with.
</param>
<param name="FilePath">
Absolute or relative path to license file that Pds instance shall use.
</param>
<returns>
<see cref="LicenseParameters"/> object.
</returns>
<remarks>
</remarks>
</UpdateLicenseFilePath>
</PdsConfigWrapper>

View File

@@ -0,0 +1,374 @@
<?xml version="1.0" encoding="utf-8" ?>
<PdsWrapper>
<UpsertSupportedForest>
<summary>
Calls PDS to request add or update supported forest to configuration file in PDS.
</summary>
<param name="ForestName">
Name of new or update supported forest.
</param>
<param name="User">
Name of account which use for ldap connection to supported forest.
Account name can be passed as one of the following:
<list type="bullet">
<item>sAMAccountName (with domain)</item>
<item>userPrincipalName</item>
</list>
<para>Together with parameter "User" must set parameters "Password" and "KeyId".</para>
</param>
<param name="Password">
Password for account which use for ldap connection to supported forest.
<para>Together with parameter "Password" must set parameters "User" and "KeyId".</para>
</param>
<param name="KeyId">
Identifies key pair to which the password is encrypted.
<para>Together with parameter "KeyId" must set parameters "User" and "Password".</para>
</param>
<returns>List of PDS instances where added or updated supported forest.</returns>
<para>When parameter User, Password and KeyID are not set, use default PDS credentials</para>
<para>
Caller must have PDS Admin role. For callers without role, AccessDenied exception is thrown. To find out, if caller has PDS Admin role, call method <see cref="IsPDSAdmin"/>.
</para>
<para>By default, PDS Admin role is assigned to Enterprise Admins group and can be changed via PDS configuration file.</para>
<para>Call is audited on PDS</para>
</UpsertSupportedForest>
<ResetManagedAccountPassword>
<summary>
Calls PDS to request reset of managed account password in given AD forest.
</summary>
<param name="ForestName">
Name of AD forest.
<para>
Forest must be marked as supported in PDS configuration.
For local forest (forest where PDS is installed), you can pass null as parameter value
</para>
</param>
<param name="AccountName">
Name of account where password shall be reset.
Account name can be passed as one of the following:
* sAMAccountName (without domain)
* userPrincipalName
* distinguishedName
</param>
<param name="WhenEffective">When password reset shall occur. Password will be reset during next PDS management cycle - see passwordManagementInterval in PDS config file.</param>
<returns>Information about result of operation along with account name and DN</returns>
<remarks>
<para>
PDS does not try to guess domain or forest name from account name - it searches against Global Catalog interface of AD forest passed in <paramref name="ForestName"/> parameter
</para>
<para>Caller must have Reset Local Admin Passsword permission on given managed account object</para>
<para>Call is audited on PDS</para>
</remarks>
</ResetManagedAccountPassword>
<GetEnvironmentStats>
<summary>
Calls PDs to retrieve environment status for managed environment:
* List of domains in each managed forest, along with number of managed machines and domain accounts in each domain
* Overall status for each AD forest, including license expiration and consumption
</summary>
<returns>Environment status for the solution</returns>
<remarks>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetEnvironmentStats>
<GetUserPermissions>
<summary>
Calls PDS to retrieve information about AdmPwd.E permissions that given user has on computer object, as seen by PDS
</summary>
<param name="ForestName">
Name of AD forest.
Forest must be marked as supported in PDS configuration.
For local forest, pass null as parameter value
</param>
<param name="ComputerName">Name of computer where password of local admin account shall be reset</param>
<param name="UserUpn">UserPrincipalName of user in question.</param>
<returns>List of solution specific permissions PDS finds for given user for given computer object </returns>
<remarks>
<para>This method uses Kerberos S2U4Self logon to obtain user's Kerberos ticket with security group membership of user account in question</para>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetUserPermissions>
<GetKeyAdminsRoleName>
<summary>
Calls PDS to retrieve name of AD group that implements Key Admin role. Members of the group hold Key Admin role on PDS
</summary>
<returns>Returns name of AD group that is configured on PDS as Key Admin role group</returns>
<remarks>
<para>By default, this role is held by Enterprise Admins group. Role assignment can be changed in PDS configuration file.</para>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetKeyAdminsRoleName>
<GetSupportedKeySizes>
<summary>
Calls PDS to retrieve key sizes supported by PDS.
</summary>
<returns>List of supported key sizes, in bits</returns>
<remarks>
<para>
PDS only creates key pairs of supported sizes; however it can decrypt passwords encrypted by any valid RSA key - see <see cref="GenerateKeyPair"/> method to see how to generate new key pair
</para>
<para>Supported key sizes are configured in PDS configuration file.</para>
<para>RSA CSP used by solution supports key sizes up to 16384 bits in 8-bit increments on Windows OS.</para>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetSupportedKeySizes>
<IsPDSAdmin>
<summary>
Calls PDS to return information whether or not the caller is in Key Admin role on PDS
</summary>
<returns>True if caller is in Key Admin role on PDS. Otherwise returns false.</returns>
<remarks>
<para>Can be used by various client tools to properly render UI and allow users in KeyAdmin role to see key pair management UI</para>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</IsPDSAdmin>
<GetManagedAccountPassword>
<summary>
Retrieves password of managed account in given AD forest.
</summary>
<param name="ForestName">
Name of AD forest.
<para>
Forest must be marked as supported in PDS configuration.
For local forest (forest where PDS is installed), you can pass null as parameter value
</para>
</param>
<param name="AccountName">
Name of account where password shall be read.
Account name can be passed as one of the following:
* sAMAccountName (without domain)
* userPrincipalName
* distinguishedName
</param>
<param name="IncludePasswordHistory">Whether or not to include password history</param>
<returns>Password, current password expiration time and optional password history</returns>
<remarks>
<para>
PDS does not try to guess domain or forest name from account name - it searches against Global Catalog interface of AD forest passed in <paramref name="ForestName"/> parameter for account as specified in <paramRef name="AccountName"/>
</para>
<para>Caller must have Read Local Admin Passsword permission on given managed account object</para>
<para>Call is audited on PDS</para>
</remarks>
</GetManagedAccountPassword>
<GetPassword>
<summary>
Calls PDS to retrieve password of managed account in given AD forest.
Managed account can be either:
* Domain computer local admin account
* Managed domain account
</summary>
<param name="ForestName">
Name of AD forest.
Forest must be marked as supported in PDS configuration.
For local forest, pass null as parameter value
</param>
<param name="Identity">
When retrieving password of computer local admin account, pass name of computer where password of local admin account shall be retrieved. Name of the computer can be passed as one of the following:
* Hostname, such as MyComputer
* FQDN, such as mycomputer.mydomain.com
* Distinguished name, such as cn=MyComputer,ou=MyComputers,dc=mydomain,dc=com
<para/>
When retrieving password of managed domain account, pas the name of domain account. Account name can be passed as one of the following:
* sAMAccountName (without domain)
* userPrincipalName
* distinguishedName
</param>
<param name="Type">
Type of the account to retrieve password for. Can be one of the supported account types:
* LocalComputerAdmin
* ManagedDomainAccount
</param>
<param name="IncludePasswordHistory">Whether or not to include password history</param>
<param name="IsDeleted">
Whether computer account or managed domain account is deleted or not.
Note: there may be multiple deleted objects with the same name. In such case, password for most recently deleted object is returned
</param>
<returns>Password, current password expiration time and optional password history. Passwords returned are plain text</returns>
<remarks>
<para>Name of local admin account is not stored by solution. Caller is expected to know name of local managed account</para>
<para>
PDS does not try to guess domain or forest name from computer name - it searches against Global Catalog interface of AD forest passed in <paramref name="ForestName"/> parameter
</para>
<para>Caller must have Read Admin Passsword permission on given computer or user object</para>
<para>Call is audited on PDS</para>
</remarks>
</GetPassword>
<GetSecurePassword>
<summary>
Calls PDS to retrieve password of managed account in given AD forest.
Managed account can be either:
* Domain computer local admin account
* Managed domain account
</summary>
<param name="ForestName">
Name of AD forest.
Forest must be marked as supported in PDS configuration.
For local forest, pass null as parameter value
</param>
<param name="Identity">
When retrieving password of computer local admin account, pass name of computer where password of local admin account shall be retrieved. Name of the computer can be passed as one of the following:
* Hostname, such as MyComputer
* FQDN, such as mycomputer.mydomain.com
* Distinguished name, such as cn=MyComputer,ou=MyComputers,dc=mydomain,dc=com
<para/>
When retrieving password of managed domain account, pas the name of domain account. Account name can be passed as one of the following:
* sAMAccountName (without domain)
* userPrincipalName
* distinguishedName
</param>
<param name="Type">
Type of the account to retrieve password for. Can be one of the supported account types:
* LocalComputerAdmin
* ManagedDomainAccount
</param>
<param name="IncludePasswordHistory">Whether or not to include password history</param>
<param name="IsDeleted">
Whether computer account or managed domain account is deleted or not.
Note: there may be multiple deleted objects with the same name. In such case, password for most recently deleted object is returned
</param>
<returns>Password, current password expiration time and optional password history. Passwords returned as secure strings</returns>
<remarks>
<para>Name of local admin account is not stored by solution. Caller is expected to know name of local managed account</para>
<para>
PDS does not try to guess domain or forest name from computer name - it searches against Global Catalog interface of AD forest passed in <paramref name="ForestName"/> parameter
</para>
<para>Caller must have Read Admin Passsword permission on given computer or user object</para>
<para>Call is audited on PDS</para>
</remarks>
</GetSecurePassword>
<ResetPassword>
<summary>
Calls PDS to request reset of for given managed account in given AD forest.
Managed account can be either:
* Domain computer local admin account
* Managed domain account
</summary>
<param name="ForestName">
Name of AD forest.
Forest must be marked as supported in PDS configuration.
For local forest, pass null as parameter value
</param>
<param name="Identity">Name of computer where password of local admin account shall be reset</param>
<param name="Type">
Type of the managed account to reset password for. Can be one of the supported account types:
* LocalComputerAdmin
* ManagedDomainAccount
</param>
<param name="WhenEffective">When password reset shall occur.
If type of account is local computer admin account, then password will be reset during next GPO update cycle on given computer after this time.
If type of account is managed domain account, and WhenEffective is in the past, then password is reset immediately. If WhenEffective is in the future, then password will be scheduled for reset according to WhenEffective parameter.
</param>
<returns>Information about result of operation along with computer name and computer DN</returns>
<remarks>
<para>Caller must have Reset Admin Passsword permission on given computer or user object</para>
<para>Call is audited on PDS</para>
</remarks>
</ResetPassword>
<GetLocalAdminPassword>
<summary>
Calls PDS to retrieve password of managed local admin account of given computer in given AD forest
</summary>
<param name="ForestName">
Name of AD forest.
Forest must be marked as supported in PDS configuration.
For local forest, pass null as parameter value
</param>
<param name="ComputerName">
Name of computer where password of local admin account shall be reset. Name of the computer can be passed as one of the following:
* Hostname, such as MyComputer
* FQDN, such as mycomputer.mydomain.com
* Distinguished name, such as cn=MyComputer,ou=MyComputers,dc=mydomain,dc=com
</param>
<param name="IncludePasswordHistory">Whether or not to include password history</param>
<param name="ComputerIsDeleted">
Whether computer is deleted or not.
Note: there may be multiple deleted computer objects with the same name. In such case, password for most recently deleted computer is returned
</param>
<returns>Password, current password expiration time and optional password history</returns>
<remarks>
<para>Name of local admin account is not stored by solution. Caller is expected to know name of local managed account</para>
<para>
PDS does not try to guess domain or forest name from computer name - it searches against Global Catalog interface of AD forest passed in <paramref name="ForestName"/> parameter
</para>
<para>Caller must have Read Local Admin Passsword permission on given computer object</para>
<para>Call is audited on PDS</para>
</remarks>
</GetLocalAdminPassword>
<ResetLocalAdminPassword>
<summary>
Calls PDS to request reset of managed local admin password for given computer in given AD forest.
</summary>
<param name="ForestName">
Name of AD forest.
Forest must be marked as supported in PDS configuration.
For local forest, pass null as parameter value
</param>
<param name="ComputerName">Name of computer where password of local admin account shall be reset</param>
<param name="WhenEffective">When password reset shall occur. Password will be reset during next GPO update cycle on given computer after this time </param>
<returns>Information about result of operation along with computer name and computer DN</returns>
<remarks>
<para>Caller must have Reset Local Admin Passsword permission on given computer object</para>
<para>Call is audited on PDS</para>
</remarks>
</ResetLocalAdminPassword>
<GetPublicKeys>
<summary>
Asks PDS to return public keys for all available key pairs.
</summary>
<returns>Returns list of all public keys managed by PDS, along with type of the key, size in bits, and key ID</returns>
<remarks>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetPublicKeys>
<GetSupportedForestNames>
<summary>
Asks PDS to return list of all supported AD forests.
</summary>
<returns>
Returns list of names of all supported AD forests.
<para>In single forest deployments, list is empty, meaning that only supported forest is the forest where solution is deployed.</para>
</returns>
<remarks>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetSupportedForestNames>
<GetPublicKey>
<summary>
Asks PDS to return public key with specified key ID
</summary>
<param name="KeyId">Identifies key pair for which public key is returned</param>
<returns>Returns public key of key pair with given KeyID, along with key ID</returns>
<remarks>
<para>No specific permissions required</para>
<para>Call is not audited</para>
</remarks>
</GetPublicKey>
<GenerateKeyPair>
<summary>
<para>Calls PDS to generate new RSA key pair.</para>
<para>
PDS keeps private key of key pair in own storage and uses it for password decryption.
Public key is put to GPO by an administrator and managed machines use it to encrypt the password of managed local admin account when reporting it to AD.
</para>
<para>PDS also uses public key to encrypt password of managed domain account - ID of key to use is specified in PDS configuration file.</para>
</summary>
<param name="KeySize">
Specifies desired RSA key size<br/>
Key size must be one of those allowed by PDS - see <see cref="GetSupportedKeySizes"/> method
</param>
<returns>Return public key of newly generated key pair, along with key ID assigned to newly generated key pair</returns>
<remarks>
<para>Caller must have Key Admin role. For callers without role, AccessDenied exception is thrown. To find out, if caller has Key Admin role, call method <see cref="IsPDSAdmin"/>.</para>
<para>By default, Key Admin role is assigned to Enterprise Admins group and can be changed via PDS configuration file.</para>
<para>Call is audited on PDS</para>
</remarks>
</GenerateKeyPair>
</PdsWrapper>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -14,7 +14,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Multi-protocol remote connections manager")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("mRemoteNG")]
[assembly: AssemblyCopyright("Copyright © 2018 mRemoteNG Dev Team; 2010-2013 Riley McArdle; 2007-2009 Felix Deimel")]
[assembly: AssemblyCopyright("Copyright © 2019 mRemoteNG Dev Team; 2010-2013 Riley McArdle; 2007-2009 Felix Deimel")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
@@ -33,5 +33,5 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below:
// <Assembly: AssemblyVersion("1.0.*")>
[assembly: AssemblyVersion("1.76.13.*")]
[assembly: AssemblyVersion("1.76.20.*")]
[assembly: NeutralResourcesLanguage("en")]

View File

@@ -12,7 +12,7 @@ namespace mRemoteNG {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -2103,7 +2103,7 @@ namespace mRemoteNG {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://mremoteng.org/")]
[global::System.Configuration.DefaultSettingValueAttribute("https://gcstoragedownload.blob.core.windows.net/download/mRemoteNG/")]
public string UpdateAddress {
get {
return ((string)(this["UpdateAddress"]));
@@ -2722,5 +2722,17 @@ namespace mRemoteNG {
this["StartUpPanelName"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool OverrideFIPSCheck {
get {
return ((bool)(this["OverrideFIPSCheck"]));
}
set {
this["OverrideFIPSCheck"] = value;
}
}
}
}

View File

@@ -522,7 +522,7 @@
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="UpdateAddress" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://mremoteng.org/</Value>
<Value Profile="(Default)">https://gcstoragedownload.blob.core.windows.net/download/mRemoteNG/</Value>
</Setting>
<Setting Name="ConDefaultLoadBalanceInfo" Type="System.String" Scope="User">
<Value Profile="(Default)" />
@@ -677,5 +677,8 @@
<Setting Name="StartUpPanelName" Type="System.String" Scope="User">
<Value Profile="(Default)">General</Value>
</Setting>
<Setting Name="OverrideFIPSCheck" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>

File diff suppressed because it is too large Load Diff

View File

@@ -655,11 +655,11 @@ Starting with new connections file.</value>
<value>Encryption failed. {0}</value>
</data>
<data name="strErrorFipsPolicyIncompatible" xml:space="preserve">
<value>The Windows security setting, "System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing", is enabled. This setting is not compatible with {0}.
<value>The Windows security setting, "System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing", is enabled.
See the Microsoft Support article at http://support.microsoft.com/kb/811833 for more information.
{0} will now close.</value>
{0} is not fully FIPS compliant. Click OK to proceed at your own discretion, or Cancel to Exit.</value>
</data>
<data name="strErrors" xml:space="preserve">
<value>Errors</value>
@@ -2700,4 +2700,7 @@ This page will walk you through the process of upgrading your connections file o
<data name="ChangeConnectionResolutionError" xml:space="preserve">
<value>An error occurred while trying to change the connection resolution to host '{0}'</value>
</data>
<data name="strUseAdmPwd" xml:space="preserve">
<value>Use AdmPwd.E to retrieve the password</value>
</data>
</root>

Binary file not shown.

View File

@@ -0,0 +1,45 @@
using System;
namespace mRemoteNG.Tools
{
/// <summary>
/// Represents an action that will be executed when the <see cref="Dispose"/>
/// method is called. Useful for creating Using blocks around logical start/end
/// actions.
/// </summary>
public class DisposableAction : IDisposable
{
private bool _isDisposed;
private readonly Action _disposeAction;
/// <summary>
///
/// </summary>
/// <param name="initializeAction">
/// An <see cref="Action"/> that should be performed immediately
/// when this object is initialized. It should return quickly.
/// </param>
/// <param name="disposeAction">
/// An <see cref="Action"/> to be executed when this object is disposed.
/// </param>
public DisposableAction(Action initializeAction, Action disposeAction)
{
initializeAction();
_disposeAction = disposeAction;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing || _isDisposed)
return;
_isDisposed = true;
_disposeAction();
}
}
}

View File

@@ -182,8 +182,24 @@ namespace mRemoteNG.Tools
replacement = _connectionInfo.Password;
if (string.IsNullOrEmpty(replacement) && Settings.Default.EmptyCredentials == "custom")
replacement = new LegacyRijndaelCryptographyProvider()
.Decrypt(Convert.ToString(Settings.Default.DefaultPassword),
Runtime.EncryptionKey);
.Decrypt(Convert.ToString(Settings.Default.DefaultPassword),
Runtime.EncryptionKey);
if (string.IsNullOrEmpty(replacement) && Settings.Default.EmptyCredentials == "admpwd")
{
if (_connectionInfo.Domain == ".")
replacement = AdmPwd.PDSUtils.PdsWrapper.GetPassword(null, _connectionInfo.Hostname, AdmPwd.Types.IdentityType.LocalComputerAdmin, false, false).Password;
else
{
var userName = _connectionInfo.Username;
if (string.IsNullOrEmpty(userName))
if (Settings.Default.EmptyCredentials == "windows")
userName = Environment.UserName;
else if (Settings.Default.EmptyCredentials == "custom")
userName = Settings.Default.DefaultUsername;
replacement = AdmPwd.PDSUtils.PdsWrapper.GetPassword(_connectionInfo.Domain, userName, AdmPwd.Types.IdentityType.ManagedDomainAccount, false, false).Password;
}
}
break;
case "domain":
replacement = _connectionInfo.Domain;

View File

@@ -68,7 +68,8 @@ namespace mRemoteNG.UI.Controls.Base
Color fore;
Color glyph;
Color checkBorder;
if (_themeManager.ActiveTheme == null)
return;
var back = _themeManager.ActiveTheme.ExtendedPalette.getColor("CheckBox_Background");
if (Enabled)
{

View File

@@ -25,10 +25,13 @@ namespace mRemoteNG.UI.Controls.Base
_themeManager = ThemeManager.getInstance();
if (!_themeManager.ThemingActive) return;
// Use the Dialog_* colors since Labels generally have the same colors as panels/dialogs/windows/etc...
BackColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("Dialog_Background");
ForeColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("Dialog_Foreground");
FontOverrider.FontOverride(this);
Invalidate();
if (_themeManager.ActiveTheme != null)
{
BackColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("Dialog_Background");
ForeColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("Dialog_Foreground");
FontOverrider.FontOverride(this);
Invalidate();
}
}

View File

@@ -78,6 +78,8 @@ namespace mRemoteNG.UI.Controls.Base
// Init
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
if (_themeManager.ActiveTheme == null)
return;
var fore = _themeManager.ActiveTheme.ExtendedPalette.getColor("CheckBox_Text");
var outline = _themeManager.ActiveTheme.ExtendedPalette.getColor("CheckBox_Border");

View File

@@ -20,9 +20,12 @@ namespace mRemoteNG.UI.Controls.Base
base.OnCreateControl();
_themeManager = ThemeManager.getInstance();
if (!_themeManager.ThemingActive) return;
ForeColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("TextBox_Foreground");
BackColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("TextBox_Background");
Invalidate();
if (_themeManager.ActiveTheme != null)
{
ForeColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("TextBox_Foreground");
BackColor = _themeManager.ActiveTheme.ExtendedPalette.getColor("TextBox_Background");
Invalidate();
}
}

View File

@@ -28,8 +28,8 @@
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CredentialsPage));
this.pnlDefaultCredentials = new System.Windows.Forms.Panel();
this.chkUseAdmPwd = new mRemoteNG.UI.Controls.Base.NGCheckBox();
this.radCredentialsCustom = new mRemoteNG.UI.Controls.Base.NGRadioButton();
this.lblDefaultCredentials = new mRemoteNG.UI.Controls.Base.NGLabel();
this.radCredentialsNoInfo = new mRemoteNG.UI.Controls.Base.NGRadioButton();
@@ -45,6 +45,7 @@
//
// pnlDefaultCredentials
//
this.pnlDefaultCredentials.Controls.Add(this.chkUseAdmPwd);
this.pnlDefaultCredentials.Controls.Add(this.radCredentialsCustom);
this.pnlDefaultCredentials.Controls.Add(this.lblDefaultCredentials);
this.pnlDefaultCredentials.Controls.Add(this.radCredentialsNoInfo);
@@ -57,18 +58,32 @@
this.pnlDefaultCredentials.Controls.Add(this.lblCredentialsDomain);
this.pnlDefaultCredentials.Location = new System.Drawing.Point(3, 3);
this.pnlDefaultCredentials.Name = "pnlDefaultCredentials";
this.pnlDefaultCredentials.Size = new System.Drawing.Size(596, 175);
this.pnlDefaultCredentials.Size = new System.Drawing.Size(596, 210);
this.pnlDefaultCredentials.TabIndex = 0;
//
// chkUseAdmPwd
//
this.chkUseAdmPwd._mice = mRemoteNG.UI.Controls.Base.NGCheckBox.MouseState.OUT;
this.chkUseAdmPwd.AutoSize = true;
this.chkUseAdmPwd.Enabled = false;
this.chkUseAdmPwd.Location = new System.Drawing.Point(140, 149);
this.chkUseAdmPwd.Name = "chkUseAdmPwd";
this.chkUseAdmPwd.Size = new System.Drawing.Size(214, 17);
this.chkUseAdmPwd.TabIndex = 10;
this.chkUseAdmPwd.Text = "use AdmPwd.E to retrieve the password";
this.chkUseAdmPwd.UseVisualStyleBackColor = true;
this.chkUseAdmPwd.CheckedChanged += new System.EventHandler(this.chkUseAdmPwd_CheckedChanged);
//
// radCredentialsCustom
//
this.radCredentialsCustom.AutoSize = true;
this.radCredentialsCustom.BackColor = System.Drawing.Color.Transparent;
this.radCredentialsCustom.Location = new System.Drawing.Point(16, 69);
this.radCredentialsCustom.Name = "radCredentialsCustom";
this.radCredentialsCustom.Size = new System.Drawing.Size(87, 17);
this.radCredentialsCustom.TabIndex = 3;
this.radCredentialsCustom.Text = "the following:";
this.radCredentialsCustom.UseVisualStyleBackColor = true;
this.radCredentialsCustom.UseVisualStyleBackColor = false;
this.radCredentialsCustom.CheckedChanged += new System.EventHandler(this.radCredentialsCustom_CheckedChanged);
//
// lblDefaultCredentials
@@ -83,6 +98,7 @@
// radCredentialsNoInfo
//
this.radCredentialsNoInfo.AutoSize = true;
this.radCredentialsNoInfo.BackColor = System.Drawing.Color.Transparent;
this.radCredentialsNoInfo.Checked = true;
this.radCredentialsNoInfo.Location = new System.Drawing.Point(16, 31);
this.radCredentialsNoInfo.Name = "radCredentialsNoInfo";
@@ -90,23 +106,24 @@
this.radCredentialsNoInfo.TabIndex = 1;
this.radCredentialsNoInfo.TabStop = true;
this.radCredentialsNoInfo.Text = "no information";
this.radCredentialsNoInfo.UseVisualStyleBackColor = true;
this.radCredentialsNoInfo.UseVisualStyleBackColor = false;
//
// radCredentialsWindows
//
this.radCredentialsWindows.AutoSize = true;
this.radCredentialsWindows.BackColor = System.Drawing.Color.Transparent;
this.radCredentialsWindows.Location = new System.Drawing.Point(16, 50);
this.radCredentialsWindows.Name = "radCredentialsWindows";
this.radCredentialsWindows.Size = new System.Drawing.Size(227, 17);
this.radCredentialsWindows.TabIndex = 2;
this.radCredentialsWindows.Text = "my current credentials (windows logon info)";
this.radCredentialsWindows.UseVisualStyleBackColor = true;
this.radCredentialsWindows.UseVisualStyleBackColor = false;
//
// txtCredentialsDomain
//
this.txtCredentialsDomain.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txtCredentialsDomain.Enabled = false;
this.txtCredentialsDomain.Location = new System.Drawing.Point(140, 147);
this.txtCredentialsDomain.Location = new System.Drawing.Point(140, 172);
this.txtCredentialsDomain.Name = "txtCredentialsDomain";
this.txtCredentialsDomain.Size = new System.Drawing.Size(150, 20);
this.txtCredentialsDomain.TabIndex = 9;
@@ -153,7 +170,7 @@
// lblCredentialsDomain
//
this.lblCredentialsDomain.Enabled = false;
this.lblCredentialsDomain.Location = new System.Drawing.Point(34, 150);
this.lblCredentialsDomain.Location = new System.Drawing.Point(34, 175);
this.lblCredentialsDomain.Name = "lblCredentialsDomain";
this.lblCredentialsDomain.Size = new System.Drawing.Size(100, 13);
this.lblCredentialsDomain.TabIndex = 8;
@@ -166,7 +183,6 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.pnlDefaultCredentials);
this.Name = "CredentialsPage";
this.PageIcon = ((System.Drawing.Icon)(resources.GetObject("$this.PageIcon")));
this.Size = new System.Drawing.Size(610, 489);
this.pnlDefaultCredentials.ResumeLayout(false);
this.pnlDefaultCredentials.PerformLayout();
@@ -186,5 +202,6 @@
internal Controls.Base.NGLabel lblCredentialsPassword;
internal Controls.Base.NGTextBox txtCredentialsUsername;
internal Controls.Base.NGLabel lblCredentialsDomain;
private Controls.Base.NGCheckBox chkUseAdmPwd;
}
}

View File

@@ -27,6 +27,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
lblCredentialsUsername.Text = Language.strLabelUsername;
lblCredentialsPassword.Text = Language.strLabelPassword;
lblCredentialsDomain.Text = Language.strLabelDomain;
chkUseAdmPwd.Text = Language.strUseAdmPwd;
}
public override void LoadSettings()
@@ -45,6 +46,10 @@ namespace mRemoteNG.UI.Forms.OptionsPages
case "custom":
radCredentialsCustom.Checked = true;
break;
case "admpwd":
radCredentialsCustom.Checked = true;
chkUseAdmPwd.Checked = true;
break;
}
txtCredentialsUsername.Text = Settings.Default.DefaultUsername;
@@ -65,7 +70,10 @@ namespace mRemoteNG.UI.Forms.OptionsPages
}
else if (radCredentialsCustom.Checked)
{
Settings.Default.EmptyCredentials = "custom";
if (chkUseAdmPwd.Checked)
Settings.Default.EmptyCredentials = "admpwd";
else
Settings.Default.EmptyCredentials = "custom";
}
Settings.Default.DefaultUsername = txtCredentialsUsername.Text;
@@ -84,6 +92,13 @@ namespace mRemoteNG.UI.Forms.OptionsPages
txtCredentialsUsername.Enabled = radCredentialsCustom.Checked;
txtCredentialsPassword.Enabled = radCredentialsCustom.Checked;
txtCredentialsDomain.Enabled = radCredentialsCustom.Checked;
chkUseAdmPwd.Enabled = radCredentialsCustom.Checked;
}
private void chkUseAdmPwd_CheckedChanged(object sender, EventArgs e)
{
lblCredentialsPassword.Enabled = !chkUseAdmPwd.Checked;
txtCredentialsPassword.Enabled = !chkUseAdmPwd.Checked;
}
}
}

View File

@@ -117,29 +117,4 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.PageIcon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAEBAAAAAAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAA
AAA+ncp7NpbR5TOQzOsyi8vtO5XCh////wH///8B////Af///wH///8B////Af///wH///8B////Af//
/wH///8BQqzhzcTr9/9/4fb/n+b3/zKKye86kLqR////Af///wH///8B////Af///wH///8B////Af//
/wH///8B////AUSw4//G9Pv/Q9bx/0jb9f+C4fX/MYjI8TmNt5n///8B////Af///wH///8B////Af//
/wH///8B////Af///wFEsOPzu+/6/znR8f8oxe7/Ttz2/4Xi9/8yi8rvOYu1m////wH///8B////Af//
/wH///8B////Af///wH///8BRLDj//D8/v+w7vr/Q9j0/yjI7v9B1/T/ieL3/zKLy+04iLKj////Af//
/wH///8B////Af///wH///8B////AUSw45dEsOP/RLDj/6vq+f9O2PP/K8nv/z3W8/+K4ff/MorK7zOD
uN0pfdb/LIXY/zaOwcv///8B////Af///wH///8B////AUSw4//x/P7/u/H7/3vk9v8o0vD/N9T1/4Pg
9v8+qeP/oPP8/6n1/P8rgtf/NYu/z////wH///8B////Af///wFEsOOXRLDj/0Wy4/92xer/rO76/znW
8v9N2/X/ZeT3/zzO8v8yye//he/7/yuB1/81iLvV////Af///wH///8B////Af///wFEsOMDRLDjl2/E
6v+A5ff/PdHx/13b9f9p3/b/UNfz/zTN7/+F7/v/KX/W/zSGutn///8B////Af///wH///8B////AUSw
4//V9/z/ief4/37k9/9+5Pf/fuT3/4Ll9/9H1vL/OM7w/671/P8pfNb/////Af///wH///8B////Af//
/wFEsOP/vvL7/37k9/9+5Pf/geX3/5Tp+P+88fv/i9rz/0nd9f/B+P3/MJDa/////wH///8B////Af//
/wH///8BRLDj/974/P+N5/j/fuT3/5Tp+P+86fj/RLDj/0Ks4//u/P7/Mpjd/zmVyL////8B////Af//
/wH///8B////AUSw45dEsOP/zvX8/43n+P+h7Pn/RLDj/0Sw4///////OaHf/zmVyL////8B////Af//
/wH///8B////Af///wH///8BRLDjl0Sw4//O9fz/nuv5/77y+//+////RLDj/0Kr353///8B////Af//
/wH///8B////Af///wH///8B////Af///wFEsOOXRLDj/974/P/e+Pz/RLDj/0Sw45f///8B////Af//
/wH///8B////Af///wH///8B////Af///wH///8B////AUSw45dEsOP/RLDj/0Sw45f///8B////Af//
/wH///8BAAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA
//8AAP//AAD//w==
</value>
</data>
</root>

View File

@@ -698,16 +698,19 @@
<setting name="StartUpPanelName" serializeAs="String">
<value>General</value>
</setting>
<setting name="OverrideFIPSCheck" serializeAs="String">
<value>False</value>
</setting>
</mRemoteNG.Settings>
</userSettings>
<applicationSettings>
<mRemoteNG.Settings>
<setting name="UpdateAddress" serializeAs="String">
<value>https://mremoteng.org/</value>
</setting>
<setting name="SupportedUICultures" serializeAs="String">
<value>cs-CZ,de,el,en,en-US,es-AR,es,fr,hu,it,ja-JP,ko-KR,nb-NO,nl,pt,pt-BR,pl,ru,uk,tr-TR,zh-CN,zh-TW</value>
</setting>
<setting name="UpdateAddress" serializeAs="String">
<value>https://gcstoragedownload.blob.core.windows.net/download/mRemoteNG/</value>
</setting>
<setting name="SupportedUICultures" serializeAs="String">
<value>cs-CZ,de,el,en,en-US,es-AR,es,fr,hu,it,ja-JP,ko-KR,nb-NO,nl,pt,pt-BR,pl,ru,uk,tr-TR,zh-CN,zh-TW</value>
</setting>
</mRemoteNG.Settings>
</applicationSettings>

View File

@@ -47,17 +47,20 @@
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<ItemGroup>
<Reference Include="AdmPwd.PDSWrapper, Version=7.7.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Greycorbel.AdmPwd-E.PDSWrapper.7.7.2\lib\net45\AdmPwd.PDSWrapper.dll</HintPath>
</Reference>
<Reference Include="ADTree">
<HintPath>References\ADTree.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
<Reference Include="BouncyCastle.Crypto, Version=1.8.5.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Geckofx-Core, Version=45.0.32.0, Culture=neutral, PublicKeyToken=3209ac31600d1857, processorArchitecture=x86">
<HintPath>..\packages\Geckofx45.45.0.32\lib\net45\Geckofx-Core.dll</HintPath>
<Reference Include="Geckofx-Core, Version=45.0.34.0, Culture=neutral, PublicKeyToken=3209ac31600d1857, processorArchitecture=x86">
<HintPath>..\packages\Geckofx45.45.0.34\lib\net45\Geckofx-Core.dll</HintPath>
</Reference>
<Reference Include="Geckofx-Winforms, Version=45.0.32.0, Culture=neutral, PublicKeyToken=3209ac31600d1857, processorArchitecture=x86">
<HintPath>..\packages\Geckofx45.45.0.32\lib\net45\Geckofx-Winforms.dll</HintPath>
<Reference Include="Geckofx-Winforms, Version=45.0.34.0, Culture=neutral, PublicKeyToken=3209ac31600d1857, processorArchitecture=x86">
<HintPath>..\packages\Geckofx45.45.0.34\lib\net45\Geckofx-Winforms.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
@@ -71,8 +74,8 @@
<Reference Include="ObjectListView, Version=2.9.1.1072, Culture=neutral, PublicKeyToken=b1c5bf581481bcd4, processorArchitecture=MSIL">
<HintPath>..\packages\ObjectListView.Official.2.9.1\lib\net20\ObjectListView.dll</HintPath>
</Reference>
<Reference Include="Renci.SshNet, Version=2016.0.0.0, Culture=neutral, PublicKeyToken=1cee9f8bde3db106, processorArchitecture=MSIL">
<HintPath>..\packages\SSH.NET.2016.0.0\lib\net40\Renci.SshNet.dll</HintPath>
<Reference Include="Renci.SshNet, Version=2016.1.0.0, Culture=neutral, PublicKeyToken=1cee9f8bde3db106, processorArchitecture=MSIL">
<HintPath>..\packages\SSH.NET.2016.1.0\lib\net40\Renci.SshNet.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
@@ -89,20 +92,20 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>References\VncSharp.dll</HintPath>
</Reference>
<Reference Include="WeifenLuo.WinFormsUI.Docking, Version=2.16.0.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.2.16.1\lib\net40\WeifenLuo.WinFormsUI.Docking.dll</HintPath>
<Reference Include="WeifenLuo.WinFormsUI.Docking, Version=3.0.6.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.dll</HintPath>
</Reference>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2003, Version=2.16.0.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2003.2.16.1\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2003.dll</HintPath>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2003, Version=3.0.6.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2003.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2003.dll</HintPath>
</Reference>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2012, Version=2.16.0.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2012.2.16.1\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2012.dll</HintPath>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2012, Version=3.0.6.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2012.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2012.dll</HintPath>
</Reference>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2013, Version=2.16.0.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2013.2.16.1\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2013.dll</HintPath>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2013, Version=3.0.6.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2013.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2013.dll</HintPath>
</Reference>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2015, Version=2.16.0.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2015.2.16.1\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll</HintPath>
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2015, Version=3.0.6.0, Culture=neutral, PublicKeyToken=5cded1a1a0a7b481, processorArchitecture=MSIL">
<HintPath>..\packages\DockPanelSuite.ThemeVS2015.3.0.6\lib\net40\WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll</HintPath>
</Reference>
<Reference Include="WindowsBase" />
</ItemGroup>
@@ -318,6 +321,7 @@
<Compile Include="Tools\Cmdline\StartupArgumentsInterpreter.cs" />
<Compile Include="Tools\CustomCollections\CollectionUpdatedEventArgs.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Tools\DisposableAction.cs" />
<Compile Include="Tools\Extensions.cs" />
<Compile Include="Tools\ExternalToolArgumentParser.cs" />
<Compile Include="Tools\Cmdline\CmdArgumentsInterpreter.cs" />
@@ -968,25 +972,15 @@
<None Include="Schemas\mremoteng_confcons_v2_7.xsd">
<SubType>Designer</SubType>
</None>
<Content Include="Schemas\mremoteng_confcons_v2_6.xsd">
<SubType>Designer</SubType>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Schemas\mremoteng_credrepo_list_v1_0.xsd">
<SubType>Designer</SubType>
</None>
<None Include="Schemas\mremoteng_creds_v1_0.xsd">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="Docs\PDSConfigWrapper.xml" />
<Content Include="Docs\PdsWrapper.xml" />
<Content Include="Firefox\AccessibleMarshal.dll" />
<Content Include="Firefox\breakpadinjector.dll" />
<Content Include="Firefox\d3dcompiler_47.dll" />
<Content Include="Firefox\freebl3.dll" />
<Content Include="Firefox\icudt56.dll" />
<Content Include="Firefox\icuin56.dll" />
<Content Include="Firefox\icuuc56.dll" />
<Content Include="Firefox\icudt58.dll" />
<Content Include="Firefox\icuin58.dll" />
<Content Include="Firefox\icuuc58.dll" />
<Content Include="Firefox\lgpllibs.dll" />
<Content Include="Firefox\libEGL.dll" />
<Content Include="Firefox\libGLESv2.dll" />
@@ -999,6 +993,18 @@
<Content Include="Firefox\sandboxbroker.dll" />
<Content Include="Firefox\softokn3.dll" />
<Content Include="Firefox\xul.dll" />
<Content Include="Schemas\mremoteng_confcons_v2_6.xsd">
<SubType>Designer</SubType>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Schemas\mremoteng_credrepo_list_v1_0.xsd">
<SubType>Designer</SubType>
</None>
<None Include="Schemas\mremoteng_creds_v1_0.xsd">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="Resources\Help\CMDSwitches.htm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1613,11 +1619,11 @@ powershell.exe -ExecutionPolicy Bypass -File "%25psScriptsDir%25\postbuild_mremo
<PropertyGroup>
<PreBuildEvent>echo $(ConfigurationName) &gt; buildenv.tmp</PreBuildEvent>
</PropertyGroup>
<Import Project="..\packages\Geckofx45.45.0.32\build\Geckofx45.targets" Condition="Exists('..\packages\Geckofx45.45.0.32\build\Geckofx45.targets')" />
<Import Project="..\packages\Geckofx45.45.0.34\build\Geckofx45.targets" Condition="Exists('..\packages\Geckofx45.45.0.34\build\Geckofx45.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Geckofx45.45.0.32\build\Geckofx45.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Geckofx45.45.0.32\build\Geckofx45.targets'))" />
<Error Condition="!Exists('..\packages\Geckofx45.45.0.34\build\Geckofx45.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Geckofx45.45.0.34\build\Geckofx45.targets'))" />
</Target>
</Project>

View File

@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.8.1" targetFramework="net46" />
<package id="DockPanelSuite" version="2.16.1" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2003" version="2.16.1" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2012" version="2.16.1" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2013" version="2.16.1" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2015" version="2.16.1" targetFramework="net46" />
<package id="Geckofx45" version="45.0.32" targetFramework="net46" />
<package id="BouncyCastle" version="1.8.5" targetFramework="net46" />
<package id="DockPanelSuite" version="3.0.6" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2003" version="3.0.6" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2012" version="3.0.6" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2013" version="3.0.6" targetFramework="net46" />
<package id="DockPanelSuite.ThemeVS2015" version="3.0.6" targetFramework="net46" />
<package id="Geckofx45" version="45.0.34" targetFramework="net46" />
<package id="Greycorbel.AdmPwd-E.PDSWrapper" version="7.7.2" targetFramework="net46" />
<package id="log4net" version="2.0.8" targetFramework="net46" />
<package id="ObjectListView.Official" version="2.9.1" targetFramework="net46" />
<package id="SSH.NET" version="2016.0.0" targetFramework="net46" />
<package id="SSH.NET" version="2016.1.0" targetFramework="net46" />
</packages>