mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 14:07:46 +08:00
Merge pull request #2506 from Schmitti91/v1.77.3-dev
Enhancing the PowerShell Connection.
This commit is contained in:
@@ -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;
|
||||
|
||||
36
mRemoteNG/Language/Language.Designer.cs
generated
36
mRemoteNG/Language/Language.Designer.cs
generated
@@ -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>
|
||||
/// Looks up a localized string similar to Dispose of Putty process failed!.
|
||||
/// </summary>
|
||||
|
||||
@@ -2031,4 +2031,20 @@ Nightly umfasst Alphas, Betas und Release Candidates.</value>
|
||||
<data name="ApplyInheritanceToChildren" xml:space="preserve">
|
||||
<value>Vererbung auf Kinder anwenden</value>
|
||||
</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>
|
||||
@@ -59,7 +59,7 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: 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:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
@@ -105,17 +105,17 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</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">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</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">
|
||||
<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>
|
||||
<data name="About" xml:space="preserve">
|
||||
<value>Over</value>
|
||||
@@ -1572,4 +1572,20 @@ mRemoteNG zal nu worden gesloten en beginnen met de installatie.</value>
|
||||
<data name="Yes" xml:space="preserve">
|
||||
<value>Ja</value>
|
||||
</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>
|
||||
@@ -2343,4 +2343,20 @@ Nightly Channel includes Alphas, Betas & Release Candidates.</value>
|
||||
<data name="UseAccessToken" xml:space="preserve">
|
||||
<value>Use RD Gateway access token</value>
|
||||
</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>
|
||||
Reference in New Issue
Block a user