Merge branch '3.0-openchannel' into master-openchannel

This commit is contained in:
terrafrost 2023-03-30 20:52:26 -05:00
commit 55578577c5
2 changed files with 68 additions and 85 deletions

View File

@ -351,20 +351,7 @@ class SFTP extends SSH2
{ {
$this->window_size_server_to_client[self::CHANNEL] = $this->window_size; $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
$packet = Strings::packSSH2( $response = $this->openChannel(self::CHANNEL, true);
'CsN3',
SSH2MessageType::CHANNEL_OPEN,
'session',
self::CHANNEL,
$this->window_size,
0x4000
);
$this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_OPEN;
$response = $this->get_channel_packet(self::CHANNEL, true);
if ($response === true && $this->isTimeout()) { if ($response === true && $this->isTimeout()) {
return false; return false;
} }

View File

@ -922,6 +922,21 @@ class SSH2
*/ */
private bool $smartMFA = true; private bool $smartMFA = true;
/**
* How many channels are currently opened
*
* @var int
*/
private $channelCount = 0;
/**
* Does the server support multiple channels? If not then error out
* when multiple channels are attempted to be opened
*
* @var bool
*/
private $errorOnMultipleChannels;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -1123,6 +1138,18 @@ class SSH2
throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers"); throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers");
} }
// Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see
// https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info.
// https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses
// when consolekit was incorporated.
// https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the
// issues with how Ubuntu incorporated consolekit
$pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#';
$match = preg_match($pattern, $this->server_identifier, $matches);
$match = $match && version_compare('5.8', $matches[1], '<=');
$match = $match && version_compare('6.9', $matches[1], '>=');
$this->errorOnMultipleChannels = $match;
if (!$this->send_id_string_first) { if (!$this->send_id_string_first) {
fwrite($this->fsock, $this->identifier . "\r\n"); fwrite($this->fsock, $this->identifier . "\r\n");
} }
@ -2432,28 +2459,7 @@ class SSH2
throw new RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); throw new RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
} }
// RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to $this->openChannel(self::CHANNEL_EXEC);
// be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
// honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
// see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
$this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
// 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
// uses 0x4000, that's what will be used here, as well.
$packet_size = 0x4000;
$packet = Strings::packSSH2(
'CsN3',
MessageType::CHANNEL_OPEN,
'session',
self::CHANNEL_EXEC,
$this->window_size_server_to_client[self::CHANNEL_EXEC],
$packet_size
);
$this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_OPEN;
$this->get_channel_packet(self::CHANNEL_EXEC);
if ($this->request_pty === true) { if ($this->request_pty === true) {
$terminal_modes = pack('C', TerminalMode::TTY_OP_END); $terminal_modes = pack('C', TerminalMode::TTY_OP_END);
@ -2533,6 +2539,42 @@ class SSH2
} }
} }
/**
* Opens a channel
*/
protected function openChannel(string $channel, bool $skip_extended = false): bool
{
$this->channelCount++;
if ($this->channelCount > 1 && $this->errorOnMultipleChannels) {
throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels");
}
// RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
// be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
// honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
// see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
$this->window_size_server_to_client[$channel] = $this->window_size;
// 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
// uses 0x4000, that's what will be used here, as well.
$packet_size = 0x4000;
$packet = Strings::packSSH2(
'CsN3',
SSH2MessageType::CHANNEL_OPEN,
'session',
$channel,
$this->window_size_server_to_client[$channel],
$packet_size
);
$this->send_binary_packet($packet);
$this->channel_status[$channel] = SSH2MessageType::CHANNEL_OPEN;
return $this->get_channel_packet($channel, $skip_extended);
}
/** /**
* Creates an interactive shell * Creates an interactive shell
* *
@ -2556,23 +2598,7 @@ class SSH2
throw new InsufficientSetupException('Operation disallowed prior to login()'); throw new InsufficientSetupException('Operation disallowed prior to login()');
} }
$this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; $this->openChannel(self::CHANNEL_SHELL);
$packet_size = 0x4000;
$packet = Strings::packSSH2(
'CsN3',
MessageType::CHANNEL_OPEN,
'session',
self::CHANNEL_SHELL,
$this->window_size_server_to_client[self::CHANNEL_SHELL],
$packet_size
);
$this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_OPEN;
$this->get_channel_packet(self::CHANNEL_SHELL);
$terminal_modes = pack('C', TerminalMode::TTY_OP_END); $terminal_modes = pack('C', TerminalMode::TTY_OP_END);
$packet = Strings::packSSH2( $packet = Strings::packSSH2(
@ -2801,22 +2827,7 @@ class SSH2
*/ */
public function startSubsystem(string $subsystem): bool public function startSubsystem(string $subsystem): bool
{ {
$this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; $this->openChannel(self::CHANNEL_SUBSYSTEM);
$packet = Strings::packSSH2(
'CsN3',
MessageType::CHANNEL_OPEN,
'session',
self::CHANNEL_SUBSYSTEM,
$this->window_size,
0x4000
);
$this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_OPEN;
$this->get_channel_packet(self::CHANNEL_SUBSYSTEM);
$packet = Strings::packSSH2( $packet = Strings::packSSH2(
'CNsCs', 'CNsCs',
@ -2977,23 +2988,8 @@ class SSH2
return false; return false;
} }
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
$packet_size = 0x4000;
$packet = Strings::packSSH2(
'CsN3',
MessageType::CHANNEL_OPEN,
'session',
self::CHANNEL_KEEP_ALIVE,
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
$packet_size
);
try { try {
$this->send_binary_packet($packet); $this->openChannel(self::CHANNEL_KEEP_ALIVE);
$this->channel_status[self::CHANNEL_KEEP_ALIVE] = MessageType::CHANNEL_OPEN;
$response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE);
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
return $this->reconnect(); return $this->reconnect();
} }