Merge pull request #2506 from Schmitti91/v1.77.3-dev

Enhancing the PowerShell Connection.
This commit is contained in:
Dimitrij
2023-10-16 09:56:50 +01:00
committed by GitHub
5 changed files with 240 additions and 7 deletions

View File

@@ -41,8 +41,157 @@ namespace mRemoteNG.Connection.Protocol.PowerShell
Padding = new Padding(0, 20, 0, 0) Padding = new Padding(0, 20, 0, 0)
}; };
_consoleControl.StartProcess(@"C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe", /*
$@"-NoExit -Command ""$password = ConvertTo-SecureString ""'{_connectionInfo.Password}'"" -AsPlainText -Force; $cred = New-Object System.Management.Automation.PSCredential -ArgumentList @('{_connectionInfo.Domain}\{_connectionInfo.Username}', $password); Enter-PSSession -ComputerName {_connectionInfo.Hostname} -Credential $cred"""); * Prepair powershell script parameter and create script
*/
// Path to the Windows PowerShell executable; can be configured through options.
string psExe = @"C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe";
// Maximum number of login attempts; can be configured through options.
int psLoginAttempts = 3;
string psUsername;
if (string.IsNullOrEmpty(_connectionInfo.Domain))
// Set the username without domain
psUsername = _connectionInfo.Username;
else
// Set the username to Domain\Username if Domain is not empty
psUsername = $"{_connectionInfo.Domain}\\{_connectionInfo.Username}";
/*
* The PowerShell script is designed to facilitate multiple login attempts to a remote host using user-provided credentials,
* with an option to specify the maximum number of attempts.
* It handles username and password entry, attempts to establish a PSSession, and reports on login outcomes, ensuring a graceful exit in case of repeated failures.
*/
string psScriptBlock = $@"
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[String] $Hostname, # The hostname you want to connect to (mandatory parameter)
[String] $Username, # The username, if provided
[String] $Password, # The password for authentication
[int] $LoginAttempts = 3 # The number of login attempts, default set to 3
)
# Dynamically parameters
DynamicParam {{
$RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary;
# SecurePassword
$ParameterName = 'SecurePassword';
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute];
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute;
$ParameterAttribute.Mandatory = $False;
$AttributeCollection.Add($ParameterAttribute);
try {{
# Try converting the stored password to a secure string
$PSBoundParameters.$($ParameterName) = ConvertTo-SecureString $Password -AsPlainText -Force -ErrorAction Stop;
}}
catch{{
# Create an empty SecureString if the password cannot be converted (if the password is empty)
$PSBoundParameters.$($ParameterName) = [SecureString]::new();
}}
$PSBoundParameters.Password = $null;
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [SecureString], $AttributeCollection);
$RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter);
return $RuntimeParameterDictionary;
}}
process {{
# Initialize the $cred variable
$cred = $null;
# Check if a username is provided.
# Please note that some logins may not require a password. Therefore, the first attempt can fail if a username is set and a password is not.
if (-not [string]::IsNullOrEmpty($PSBoundParameters.Username)) {{
# Create a PSCredential object with the provided username and password
$cred = New-Object System.Management.Automation.PSCredential ($PSBoundParameters.Username, $PSBoundParameters.SecurePassword);
# It will be needed to determine whether the login credentials were provided or not.
$providedCred = $true;
}}
# At least one login attempt is required to ensure functionality
if ($LoginAttempts -lt 0) {{$LoginAttempts = 1;}}
# Loop for connection attempts for $LoginAttempts
for ($i = 0; $i -lt $LoginAttempts; $i++) {{
<#
The cases for when 'Get-Credential' is needed:
1. `$i -gt 0`: Indicates the first login attempt has failed.
2. `-not $cred`: Implies that no credentials have been sent to the function.
3. `$cred -and $cred.UserName -match ""^([^\\]+\\)$""`: Implies that only the regular Windows domain name is parameterized.
NOTE:
If the regular expression is used in an if statement such as if (.... -match ""^[^\\]+\\$"")...,
there will be conversion problems with the string. This can then lead to errors when executing PowerShell.
To work around this problem, create the $regex variable and enclose the expression in single quotes.
Due to the use of variables, double quotes are no longer required in the if statement, and it can be written as follows: if (.... -match $Regex)....
This approach avoids possible string conversion problems caused by double quotes.
#>
[string] $regex = '^[^\\]+\\$'
if ($i -gt 0 -or (-not $cred) -or ($cred -and $cred.Username -match $regex)){{
# Prompt for credentials with a message and pre-fill username if available
try {{
if (-not [string]::IsNullOrEmpty($cred.UserName)) {{
$cred = Get-Credential -Message $Hostname -UserName $cred.UserName -ErrorAction Stop;
}}
else {{
$cred = Get-Credential -Message $Hostname -ErrorAction Stop;
}}
$providedCred = $false; # provided creds are overwritten
}}
catch {{
# If something is wrong for $cred
$cred = $null
}}
}}
# Try PSSession
try {{
# If credentials are not provided, abort the loop (mean Get-Credential is canceled)
if ( $cred ) {{
Enter-PSSession -ComputerName $Hostname -Credential $cred -ErrorAction Stop;
break; # Successfully entered PSSession, exit the loop
}}
else {{
write-Host '{Language.PsCanceled}';
exit;
}}
}}
# Handle the case when PSSession entry fails
catch [System.Management.Automation.Remoting.PSRemotingTransportException]{{
If (-not $providedCred) {{
Write-Host '{Language.PsConnectionFailed}';
Write-Host;
}}
else {{
$LoginAttempts++;
}}
}}
catch {{
# Handle other exceptions
Write-Host $_.Exception.Message;
Write-Host;
Write-Host '{Language.PsFailed}';
exit;
}}
}}
# Maximum login attempts reached
if ($i -ge $LoginAttempts) {{
Write-Host '{Language.PsLoginAttempts}';
exit;
}}
}}
";
// Setup process for script with arguments
//* The -NoProfile parameter would be a valuable addition but should be able to be deactivated.
_consoleControl.StartProcess(psExe, $@"-NoExit -Command ""& {{ {psScriptBlock} }}"" -Hostname ""'{_connectionInfo.Hostname}'"" -Username ""'{psUsername}'"" -Password ""'{_connectionInfo.Password}'"" -LoginAttempts {psLoginAttempts}");
while (!_consoleControl.IsHandleCreated) break; while (!_consoleControl.IsHandleCreated) break;
_handle = _consoleControl.Handle; _handle = _consoleControl.Handle;

View File

@@ -4513,6 +4513,42 @@ namespace mRemoteNG.Resources.Language {
} }
} }
/// <summary>
/// Looks up a localized string similar to Login canceled! Restart if necessary..
/// </summary>
internal static string PsCanceled {
get {
return ResourceManager.GetString("PsCanceled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Please verify username and password and try again.If PSRemoting is not enabled on the server, enable it first..
/// </summary>
internal static string PsConnectionFailed {
get {
return ResourceManager.GetString("PsConnectionFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login failed!.
/// </summary>
internal static string PsFailed {
get {
return ResourceManager.GetString("PsFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Maximum login attempts exceeded. Please connect again..
/// </summary>
internal static string PsLoginAttempts {
get {
return ResourceManager.GetString("PsLoginAttempts", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Dispose of Putty process failed!. /// Looks up a localized string similar to Dispose of Putty process failed!.
/// </summary> /// </summary>

View File

@@ -2031,4 +2031,20 @@ Nightly umfasst Alphas, Betas und Release Candidates.</value>
<data name="ApplyInheritanceToChildren" xml:space="preserve"> <data name="ApplyInheritanceToChildren" xml:space="preserve">
<value>Vererbung auf Kinder anwenden</value> <value>Vererbung auf Kinder anwenden</value>
</data> </data>
<data name="PsCanceled" xml:space="preserve">
<value>Anmeldung abgebrochen! Bei bedarf neu einleiten.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsConnectionFailed" xml:space="preserve">
<value>Bitte Benutzernamen und Kennwort abgleichen und erneut versuchen. Wenn PSRemoting auf dem Server nicht aktiviert ist, bitte zuerst aktivieren.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsFailed" xml:space="preserve">
<value>Anmeldung fehlgeschlagen!</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsLoginAttempts" xml:space="preserve">
<value>Maximale Anmeldeversuche erreicht. Verbindung erneut initiieren.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
</root> </root>

View File

@@ -59,7 +59,7 @@
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true"> <xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType> <xsd:complexType>
@@ -105,17 +105,17 @@
</xsd:complexType> </xsd:complexType>
</xsd:element> </xsd:element>
</xsd:schema> </xsd:schema>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
</resheader> </resheader>
<resheader name="version"> <resheader name="version">
<value>2.0</value> <value>2.0</value>
</resheader> </resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="About" xml:space="preserve"> <data name="About" xml:space="preserve">
<value>Over</value> <value>Over</value>
@@ -1572,4 +1572,20 @@ mRemoteNG zal nu worden gesloten en beginnen met de installatie.</value>
<data name="Yes" xml:space="preserve"> <data name="Yes" xml:space="preserve">
<value>Ja</value> <value>Ja</value>
</data> </data>
<data name="PsCanceled" xml:space="preserve">
<value>Inloggen geannuleerd! Herstart indien nodig.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsConnectionFailed" xml:space="preserve">
<value>Controleer de gebruikersnaam en wachtwoord en probeer het opnieuw. Als PSRemoting niet is ingeschakeld op de server, schakel dit dan eerst in.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsFailed" xml:space="preserve">
<value>Aanmelding mislukt!</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsLoginAttempts" xml:space="preserve">
<value>Maximaal aantal inlogpogingen overschreden. Maak opnieuw verbinding.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
</root> </root>

View File

@@ -2343,4 +2343,20 @@ Nightly Channel includes Alphas, Betas &amp; Release Candidates.</value>
<data name="UseAccessToken" xml:space="preserve"> <data name="UseAccessToken" xml:space="preserve">
<value>Use RD Gateway access token</value> <value>Use RD Gateway access token</value>
</data> </data>
<data name="PsCanceled" xml:space="preserve">
<value>Login canceled! Restart if necessary.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsConnectionFailed" xml:space="preserve">
<value>Please verify username and password and try again.If PSRemoting is not enabled on the server, enable it first.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsFailed" xml:space="preserve">
<value>Login failed!</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="PsLoginAttempts" xml:space="preserve">
<value>Maximum login attempts exceeded. Please connect again.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
</root> </root>