Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1917,7 +1917,7 @@ public SSHConnectionInfo(
this.UserName = userName;
this.ComputerName = computerName;
this.KeyFilePath = keyFilePath;
this.Port = DefaultPort;
this.Port = 0;
this.Subsystem = DefaultSubsystem;
}

Expand All @@ -1936,7 +1936,7 @@ public SSHConnectionInfo(
{
ValidatePortInRange(port);

this.Port = (port != 0) ? port : DefaultPort;
this.Port = port;
}

/// <summary>
Expand All @@ -1956,7 +1956,7 @@ public SSHConnectionInfo(
{
ValidatePortInRange(port);

this.Port = (port != 0) ? port : DefaultPort;
this.Port = port;
this.Subsystem = (string.IsNullOrEmpty(subsystem)) ? DefaultSubsystem : subsystem;
}

Expand Down Expand Up @@ -2063,28 +2063,27 @@ internal int StartSSHProcess(
}
}

// Extract an optional domain name if provided.
string domainName = null;
string userName = this.UserName ?? GetCurrentUserName();
#if !UNIX
var parts = userName.Split(Utils.Separators.Backslash);
if (parts.Length == 2)
{
domainName = parts[0];
userName = parts[1];
}
#endif

// Create client ssh process that hosts powershell as a subsystem and is configured
// to be in server mode for PSRP over SSHD:
// powershell -sshs -NoLogo -NoProfile
// See sshd_configuration file, subsystems section and it will have this entry:
// Subsystem powershell C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -sshs -NoLogo -NoProfile
string arguments;

// ssh.exe expects ipv6 not to have square brackets so we have to remove
string hostname = this.ComputerName.TrimStart('[').TrimEnd(']');

// Create a local ssh process (client) that conects to a remote sshd process (server) using a 'powershell' subsystem.
//
// Local ssh invoked as:
// windows:
// ssh.exe [-i identity_file] [-l login_name] [-p port] -s <destination> <command>
// linux|macos:
// ssh [-i identity_file] [-l login_name] [-p port] -s <destination> <command>
// where <command> is interpreted as the subsystem due to the -s flag.
//
// Remote sshd configured for PowerShell Remoting Protocol (PSRP) over Secure Shell Protocol (SSH)
// by adding one of the following Subsystem directives to sshd_config on the remote machine:
// windows:
// Subsystem powershell C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -SSHServerMode -NoLogo -NoProfile
// Subsystem powershell C:\Program Files\PowerShell\6\pwsh.exe -SSHServerMode -NoLogo -NoProfile
// linux|macos:
// Subsystem powershell /usr/local/bin/pwsh -SSHServerMode -NoLogo -NoProfile

System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(filePath);

// pass "-i identity_file" command line argument to ssh if KeyFilePath is set
// if KeyFilePath is not set, then ssh will use IdentityFile / IdentityAgent from ssh_config if defined else none by default
if (!string.IsNullOrEmpty(this.KeyFilePath))
{
if (!System.IO.File.Exists(this.KeyFilePath))
Expand All @@ -2093,20 +2092,38 @@ internal int StartSSHProcess(
StringUtil.Format(RemotingErrorIdStrings.KeyFileNotFound, this.KeyFilePath));
}

arguments = (string.IsNullOrEmpty(domainName)) ?
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" {1}@{2} -p {3} -s {4}", this.KeyFilePath, userName, hostname, this.Port, this.Subsystem) :
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" -l {1}@{2} {3} -p {4} -s {5}", this.KeyFilePath, userName, domainName, hostname, this.Port, this.Subsystem);
startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-i ""{0}""", this.KeyFilePath));
}
else

// pass "-l login_name" commmand line argument to ssh if UserName is set
// if UserName is not set, then ssh will use User from ssh_config if defined else the environment user by default
if (!string.IsNullOrEmpty(this.UserName))
{
var parts = this.UserName.Split(Utils.Separators.Backslash);
if (parts.Length == 2)
{
// convert DOMAIN\user to user@DOMAIN
var domainName = parts[0];
var userName = parts[1];
startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-l {0}@{1}", userName, domainName));
}
else
{
startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-l {0}", this.UserName));
}
}

// pass "-p port" command line argument to ssh if Port is set
// if Port is not set, then ssh will use Port from ssh_config if defined else 22 by default
if (this.Port != 0)
{
arguments = (string.IsNullOrEmpty(domainName)) ?
string.Format(CultureInfo.InvariantCulture, @"{0}@{1} -p {2} -s {3}", userName, hostname, this.Port, this.Subsystem) :
string.Format(CultureInfo.InvariantCulture, @"-l {0}@{1} {2} -p {3} -s {4}", userName, domainName, hostname, this.Port, this.Subsystem);
startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-p {0}", this.Port));
}

System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(
filePath,
arguments);
// pass "-s destination command" command line arguments to ssh where command is the subsystem to invoke on the destination
// note that ssh expects IPv6 addresses to not be enclosed in square brackets so trim them if present
startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-s {0} {1}", this.ComputerName.TrimStart('[').TrimEnd(']'), this.Subsystem));

startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(filePath);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Expand All @@ -2116,26 +2133,8 @@ internal int StartSSHProcess(

#endregion

#region Private Methods

private string GetCurrentUserName()
{
#if UNIX
return System.Environment.GetEnvironmentVariable("USER") ?? string.Empty;
#else
return System.Security.Principal.WindowsIdentity.GetCurrent().Name;
#endif
}

#endregion

#region Constants

/// <summary>
/// Default value for port.
/// </summary>
private const int DefaultPort = 22;

/// <summary>
/// Default value for subsystem.
/// </summary>
Expand Down Expand Up @@ -2286,7 +2285,7 @@ private static string[] ParseArgv(ProcessStartInfo psi)
var argvList = new List<string>();
argvList.Add(psi.FileName);

var argsToParse = psi.Arguments.Trim();
var argsToParse = String.Join(" ", psi.ArgumentList).Trim();
var argsLength = argsToParse.Length;
for (int i=0; i<argsLength; )
{
Expand Down