diff --git a/mRemoteNG/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs b/mRemoteNG/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs
index 6864a7ee..cb8d0da9 100644
--- a/mRemoteNG/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs
+++ b/mRemoteNG/Connection/Protocol/PowerShell/Connection.Protocol.PowerShell.cs
@@ -41,8 +41,157 @@ namespace mRemoteNG.Connection.Protocol.PowerShell
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;
_handle = _consoleControl.Handle;
diff --git a/mRemoteNG/Language/Language.Designer.cs b/mRemoteNG/Language/Language.Designer.cs
index bbca400d..39bcb24a 100644
--- a/mRemoteNG/Language/Language.Designer.cs
+++ b/mRemoteNG/Language/Language.Designer.cs
@@ -4513,6 +4513,42 @@ namespace mRemoteNG.Resources.Language {
}
}
+ ///
+ /// Looks up a localized string similar to Login canceled! Restart if necessary..
+ ///
+ internal static string PsCanceled {
+ get {
+ return ResourceManager.GetString("PsCanceled", resourceCulture);
+ }
+ }
+
+ ///
+ /// 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..
+ ///
+ internal static string PsConnectionFailed {
+ get {
+ return ResourceManager.GetString("PsConnectionFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Login failed!.
+ ///
+ internal static string PsFailed {
+ get {
+ return ResourceManager.GetString("PsFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Maximum login attempts exceeded. Please connect again..
+ ///
+ internal static string PsLoginAttempts {
+ get {
+ return ResourceManager.GetString("PsLoginAttempts", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Dispose of Putty process failed!.
///
diff --git a/mRemoteNG/Language/Language.de.resx b/mRemoteNG/Language/Language.de.resx
index 9ffeed4a..8810ce90 100644
--- a/mRemoteNG/Language/Language.de.resx
+++ b/mRemoteNG/Language/Language.de.resx
@@ -2031,4 +2031,20 @@ Nightly umfasst Alphas, Betas und Release Candidates.
Vererbung auf Kinder anwenden
+
+ Anmeldung abgebrochen! Bei bedarf neu einleiten.
+ C# to Powershell transfer issue with encoding possible
+
+
+ Bitte Benutzernamen und Kennwort abgleichen und erneut versuchen. Wenn PSRemoting auf dem Server nicht aktiviert ist, bitte zuerst aktivieren.
+ C# to Powershell transfer issue with encoding possible
+
+
+ Anmeldung fehlgeschlagen!
+ C# to Powershell transfer issue with encoding possible
+
+
+ Maximale Anmeldeversuche erreicht. Verbindung erneut initiieren.
+ C# to Powershell transfer issue with encoding possible
+
\ No newline at end of file
diff --git a/mRemoteNG/Language/Language.nl.resx b/mRemoteNG/Language/Language.nl.resx
index b35f4082..195e808f 100644
--- a/mRemoteNG/Language/Language.nl.resx
+++ b/mRemoteNG/Language/Language.nl.resx
@@ -59,7 +59,7 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
-
+
@@ -105,17 +105,17 @@
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
text/microsoft-resx
2.0
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Over
@@ -1572,4 +1572,20 @@ mRemoteNG zal nu worden gesloten en beginnen met de installatie.
Ja
+
+ Inloggen geannuleerd! Herstart indien nodig.
+ C# to Powershell transfer issue with encoding possible
+
+
+ Controleer de gebruikersnaam en wachtwoord en probeer het opnieuw. Als PSRemoting niet is ingeschakeld op de server, schakel dit dan eerst in.
+ C# to Powershell transfer issue with encoding possible
+
+
+ Aanmelding mislukt!
+ C# to Powershell transfer issue with encoding possible
+
+
+ Maximaal aantal inlogpogingen overschreden. Maak opnieuw verbinding.
+ C# to Powershell transfer issue with encoding possible
+
\ No newline at end of file
diff --git a/mRemoteNG/Language/Language.resx b/mRemoteNG/Language/Language.resx
index 9c6c99bd..d2cbc20f 100644
--- a/mRemoteNG/Language/Language.resx
+++ b/mRemoteNG/Language/Language.resx
@@ -2343,4 +2343,20 @@ Nightly Channel includes Alphas, Betas & Release Candidates.
Use RD Gateway access token
+
+ Login canceled! Restart if necessary.
+ C# to Powershell transfer issue with encoding possible
+
+
+ Please verify username and password and try again.If PSRemoting is not enabled on the server, enable it first.
+ C# to Powershell transfer issue with encoding possible
+
+
+ Login failed!
+ C# to Powershell transfer issue with encoding possible
+
+
+ Maximum login attempts exceeded. Please connect again.
+ C# to Powershell transfer issue with encoding possible
+
\ No newline at end of file