4 * Pure-PHP implementation of SSHv2.
8 * Here are some examples of how to use this library:
11 * include 'Net/SSH2.php';
13 * $ssh = new Net_SSH2('www.domain.tld');
14 * if (!$ssh->login('username', 'password')) {
15 * exit('Login Failed');
18 * echo $ssh->exec('pwd');
19 * echo $ssh->exec('ls -la');
25 * include 'Crypt/RSA.php';
26 * include 'Net/SSH2.php';
28 * $key = new Crypt_RSA();
29 * //$key->setPassword('whatever');
30 * $key->loadKey(file_get_contents('privatekey'));
32 * $ssh = new Net_SSH2('www.domain.tld');
33 * if (!$ssh->login('username', $key)) {
34 * exit('Login Failed');
37 * echo $ssh->read('username@username:~$');
38 * $ssh->write("ls -la\n");
39 * echo $ssh->read('username@username:~$');
43 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
44 * of this software and associated documentation files (the "Software"), to deal
45 * in the Software without restriction, including without limitation the rights
46 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
47 * copies of the Software, and to permit persons to whom the Software is
48 * furnished to do so, subject to the following conditions:
50 * The above copyright notice and this permission notice shall be included in
51 * all copies or substantial portions of the Software.
53 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
54 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
55 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
56 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
57 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
58 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
63 * @author Jim Wigginton <terrafrost@php.net>
64 * @copyright 2007 Jim Wigginton
65 * @license http://www.opensource.org/licenses/mit-license.html MIT License
66 * @link http://phpseclib.sourceforge.net
70 * Execution Bitmap Masks
72 * @see Net_SSH2::bitmap
75 define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
76 define('NET_SSH2_MASK_CONNECTED', 0x00000002);
77 define('NET_SSH2_MASK_LOGIN_REQ', 0x00000004);
78 define('NET_SSH2_MASK_LOGIN', 0x00000008);
79 define('NET_SSH2_MASK_SHELL', 0x00000010);
80 define('NET_SSH2_MASK_WINDOW_ADJUST', 0x00000020);
86 * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
87 * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
88 * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
89 * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
90 * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
91 * The 'recipient channel' is the channel number given in the original
92 * open request, and 'sender channel' is the channel number allocated by
95 * @see Net_SSH2::_send_channel_packet()
96 * @see Net_SSH2::_get_channel_packet()
99 define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
100 define('NET_SSH2_CHANNEL_SHELL', 1);
101 define('NET_SSH2_CHANNEL_SUBSYSTEM', 2);
106 * @see Net_SSH2::getLog()
109 * Returns the message numbers
111 define('NET_SSH2_LOG_SIMPLE', 1);
113 * Returns the message content
115 define('NET_SSH2_LOG_COMPLEX', 2);
117 * Outputs the content real-time
119 define('NET_SSH2_LOG_REALTIME', 3);
121 * Dumps the content real-time to a file
123 define('NET_SSH2_LOG_REALTIME_FILE', 4);
128 * @see Net_SSH2::read()
131 * Returns when a string matching $expect exactly is found
133 define('NET_SSH2_READ_SIMPLE', 1);
135 * Returns when a string matching the regular expression $expect is found
137 define('NET_SSH2_READ_REGEX', 2);
139 * Make sure that the log never gets larger than this
141 define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024);
145 * Pure-PHP implementation of SSHv2.
148 * @author Jim Wigginton <terrafrost@php.net>
172 * The bits that are set represent functions that have been called already. This is used to determine
173 * if a requisite function has been successfully executed. If not, an error should be thrown.
183 * @see Net_SSH2::getErrors()
184 * @see Net_SSH2::getLastError()
188 var $errors = array();
193 * @see Net_SSH2::getServerIdentification()
194 * @var mixed false or Array
197 var $server_identifier = false;
200 * Key Exchange Algorithms
202 * @see Net_SSH2::getKexAlgorithims()
203 * @var mixed false or Array
206 var $kex_algorithms = false;
209 * Server Host Key Algorithms
211 * @see Net_SSH2::getServerHostKeyAlgorithms()
212 * @var mixed false or Array
215 var $server_host_key_algorithms = false;
218 * Encryption Algorithms: Client to Server
220 * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
221 * @var mixed false or Array
224 var $encryption_algorithms_client_to_server = false;
227 * Encryption Algorithms: Server to Client
229 * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
230 * @var mixed false or Array
233 var $encryption_algorithms_server_to_client = false;
236 * MAC Algorithms: Client to Server
238 * @see Net_SSH2::getMACAlgorithmsClient2Server()
239 * @var mixed false or Array
242 var $mac_algorithms_client_to_server = false;
245 * MAC Algorithms: Server to Client
247 * @see Net_SSH2::getMACAlgorithmsServer2Client()
248 * @var mixed false or Array
251 var $mac_algorithms_server_to_client = false;
254 * Compression Algorithms: Client to Server
256 * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
257 * @var mixed false or Array
260 var $compression_algorithms_client_to_server = false;
263 * Compression Algorithms: Server to Client
265 * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
266 * @var mixed false or Array
269 var $compression_algorithms_server_to_client = false;
272 * Languages: Server to Client
274 * @see Net_SSH2::getLanguagesServer2Client()
275 * @var mixed false or Array
278 var $languages_server_to_client = false;
281 * Languages: Client to Server
283 * @see Net_SSH2::getLanguagesClient2Server()
284 * @var mixed false or Array
287 var $languages_client_to_server = false;
290 * Block Size for Server to Client Encryption
292 * "Note that the length of the concatenation of 'packet_length',
293 * 'padding_length', 'payload', and 'random padding' MUST be a multiple
294 * of the cipher block size or 8, whichever is larger. This constraint
295 * MUST be enforced, even when using stream ciphers."
297 * -- http://tools.ietf.org/html/rfc4253#section-6
299 * @see Net_SSH2::Net_SSH2()
300 * @see Net_SSH2::_send_binary_packet()
304 var $encrypt_block_size = 8;
307 * Block Size for Client to Server Encryption
309 * @see Net_SSH2::Net_SSH2()
310 * @see Net_SSH2::_get_binary_packet()
314 var $decrypt_block_size = 8;
317 * Server to Client Encryption Object
319 * @see Net_SSH2::_get_binary_packet()
323 var $decrypt = false;
326 * Client to Server Encryption Object
328 * @see Net_SSH2::_send_binary_packet()
332 var $encrypt = false;
335 * Client to Server HMAC Object
337 * @see Net_SSH2::_send_binary_packet()
341 var $hmac_create = false;
344 * Server to Client HMAC Object
346 * @see Net_SSH2::_get_binary_packet()
350 var $hmac_check = false;
353 * Size of server to client HMAC
355 * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
356 * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is
359 * @see Net_SSH2::_get_binary_packet()
363 var $hmac_size = false;
366 * Server Public Host Key
368 * @see Net_SSH2::getServerPublicHostKey()
372 var $server_public_host_key;
377 * "The exchange hash H from the first key exchange is additionally
378 * used as the session identifier, which is a unique identifier for
381 * -- http://tools.ietf.org/html/rfc4253#section-7.2
383 * @see Net_SSH2::_key_exchange()
387 var $session_id = false;
392 * The current exchange hash
394 * @see Net_SSH2::_key_exchange()
398 var $exchange_hash = false;
403 * @see Net_SSH2::Net_SSH2()
407 var $message_numbers = array();
410 * Disconnection Message 'reason codes' defined in RFC4253
412 * @see Net_SSH2::Net_SSH2()
416 var $disconnect_reasons = array();
419 * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
421 * @see Net_SSH2::Net_SSH2()
425 var $channel_open_failure_reasons = array();
430 * @link http://tools.ietf.org/html/rfc4254#section-8
431 * @see Net_SSH2::Net_SSH2()
435 var $terminal_modes = array();
438 * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
440 * @link http://tools.ietf.org/html/rfc4254#section-5.2
441 * @see Net_SSH2::Net_SSH2()
445 var $channel_extended_data_type_codes = array();
448 * Send Sequence Number
450 * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
452 * @see Net_SSH2::_send_binary_packet()
456 var $send_seq_no = 0;
459 * Get Sequence Number
461 * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
463 * @see Net_SSH2::_get_binary_packet()
472 * Maps client channels to server channels
474 * @see Net_SSH2::_get_channel_packet()
475 * @see Net_SSH2::exec()
479 var $server_channels = array();
484 * If a client requests a packet from one channel but receives two packets from another those packets should
485 * be placed in a buffer
487 * @see Net_SSH2::_get_channel_packet()
488 * @see Net_SSH2::exec()
492 var $channel_buffers = array();
497 * Contains the type of the last sent message
499 * @see Net_SSH2::_get_channel_packet()
503 var $channel_status = array();
508 * Maximum packet size indexed by channel
510 * @see Net_SSH2::_send_channel_packet()
514 var $packet_size_client_to_server = array();
519 * @see Net_SSH2::getLog()
523 var $message_number_log = array();
528 * @see Net_SSH2::getLog()
532 var $message_log = array();
537 * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
540 * @see Net_SSH2::_send_channel_packet()
541 * @see Net_SSH2::exec()
544 var $window_size = 0x7FFFFFFF;
547 * Window size, server to client
549 * Window size indexed by channel
551 * @see Net_SSH2::_send_channel_packet()
555 var $window_size_server_to_client = array();
558 * Window size, client to server
560 * Window size indexed by channel
562 * @see Net_SSH2::_get_channel_packet()
566 var $window_size_client_to_server = array();
571 * Verified against $this->session_id
573 * @see Net_SSH2::getServerPublicHostKey()
580 * Server signature format
582 * ssh-rsa or ssh-dss.
584 * @see Net_SSH2::getServerPublicHostKey()
588 var $signature_format = '';
593 * @see Net_SSH2::read()
597 var $interactiveBuffer = '';
602 * Should never exceed NET_SSH2_LOG_MAX_SIZE
604 * @see Net_SSH2::_send_binary_packet()
605 * @see Net_SSH2::_get_binary_packet()
614 * @see Net_SSH2::setTimeout()
622 * @see Net_SSH2::_get_channel_packet()
628 * Real-time log file pointer
630 * @see Net_SSH2::_append_log()
634 var $realtime_log_file;
637 * Real-time log file size
639 * @see Net_SSH2::_append_log()
643 var $realtime_log_size;
646 * Has the signature been validated?
648 * @see Net_SSH2::getServerPublicHostKey()
652 var $signature_validated = false;
655 * Real-time log file wrap boolean
657 * @see Net_SSH2::_append_log()
660 var $realtime_log_wrap;
663 * Flag to suppress stderr from output
665 * @see Net_SSH2::enableQuietMode()
668 var $quiet_mode = false;
671 * Time of first network activity
679 * Exit status returned from ssh if any
687 * Flag to request a PTY when using exec()
690 * @see Net_SSH2::enablePTY()
693 var $request_pty = false;
696 * Flag set while exec() is running when using enablePTY()
701 var $in_request_pty_exec = false;
704 * Flag set after startSubsystem() is called
712 * Contents of stdError
720 * The Last Interactive Response
722 * @see Net_SSH2::_keyboard_interactive_process()
726 var $last_interactive_response = '';
729 * Keyboard Interactive Request / Responses
731 * @see Net_SSH2::_keyboard_interactive_process()
735 var $keyboard_requests_responses = array();
740 * Quoting from the RFC, "in some jurisdictions, sending a warning message before
741 * authentication may be relevant for getting legal protection."
743 * @see Net_SSH2::_filter()
744 * @see Net_SSH2::getBannerMessage()
748 var $banner_message = '';
751 * Did read() timeout or return normally?
753 * @see Net_SSH2::isTimeout()
757 var $is_timeout = false;
762 * @see Net_SSH2::_format_log()
766 var $log_boundary = ':';
771 * @see Net_SSH2::_format_log()
775 var $log_long_width = 65;
780 * @see Net_SSH2::_format_log()
784 var $log_short_width = 16;
789 * @see Net_SSH2::Net_SSH2()
790 * @see Net_SSH2::_connect()
799 * @see Net_SSH2::Net_SSH2()
800 * @see Net_SSH2::_connect()
807 * Timeout for initial connection
809 * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like
810 * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor,
811 * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be
812 * 10 seconds. It is used by fsockopen() and the initial stream_select in that function.
814 * @see Net_SSH2::Net_SSH2()
815 * @see Net_SSH2::_connect()
819 var $connectionTimeout;
822 * Number of columns for terminal window size
824 * @see Net_SSH2::getWindowColumns()
825 * @see Net_SSH2::setWindowColumns()
826 * @see Net_SSH2::setWindowSize()
830 var $windowColumns = 80;
833 * Number of columns for terminal window size
835 * @see Net_SSH2::getWindowRows()
836 * @see Net_SSH2::setWindowRows()
837 * @see Net_SSH2::setWindowSize()
841 var $windowRows = 24;
844 * Default Constructor.
846 * @param String $host
847 * @param optional Integer $port
848 * @param optional Integer $timeout
849 * @see Net_SSH2::login()
853 function Net_SSH2($host, $port = 22, $timeout = 10)
855 // Include Math_BigInteger
856 // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
857 if (!class_exists('Math_BigInteger')) {
858 include_once 'Math/BigInteger.php';
861 if (!function_exists('crypt_random_string')) {
862 include_once 'Crypt/Random.php';
865 if (!class_exists('Crypt_Hash')) {
866 include_once 'Crypt/Hash.php';
869 $this->message_numbers = array(
870 1 => 'NET_SSH2_MSG_DISCONNECT',
871 2 => 'NET_SSH2_MSG_IGNORE',
872 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
873 4 => 'NET_SSH2_MSG_DEBUG',
874 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
875 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
876 20 => 'NET_SSH2_MSG_KEXINIT',
877 21 => 'NET_SSH2_MSG_NEWKEYS',
878 30 => 'NET_SSH2_MSG_KEXDH_INIT',
879 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
880 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
881 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
882 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
883 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
885 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
886 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
887 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
888 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
889 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
890 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
891 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
892 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
893 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
894 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
895 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
896 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
897 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
898 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
900 $this->disconnect_reasons = array(
901 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
902 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
903 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
904 4 => 'NET_SSH2_DISCONNECT_RESERVED',
905 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
906 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
907 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
908 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
909 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
910 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
911 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
912 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
913 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
914 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
915 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
917 $this->channel_open_failure_reasons = array(
918 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
920 $this->terminal_modes = array(
921 0 => 'NET_SSH2_TTY_OP_END'
923 $this->channel_extended_data_type_codes = array(
924 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
927 $this->_define_array(
928 $this->message_numbers,
929 $this->disconnect_reasons,
930 $this->channel_open_failure_reasons,
931 $this->terminal_modes,
932 $this->channel_extended_data_type_codes,
933 array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
934 array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
935 array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
936 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE')
941 $this->connectionTimeout = $timeout;
945 * Connect to an SSHv2 server
952 if ($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) {
956 $this->bitmap |= NET_SSH2_MASK_CONSTRUCTOR;
958 $timeout = $this->connectionTimeout;
959 $host = $this->host . ':' . $this->port;
961 $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5
963 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
964 $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $timeout);
966 user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
969 $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
974 user_error("Cannot connect to $host. Timeout error");
978 $read = array($this->fsock);
979 $write = $except = null;
981 $sec = floor($timeout);
982 $usec = 1000000 * ($timeout - $sec);
984 // on windows this returns a "Warning: Invalid CRT parameters detected" error
985 // the !count() is done as a workaround for <https://bugs.php.net/42682>
986 if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
987 user_error("Cannot connect to $host. Banner timeout");
991 /* According to the SSH2 specs,
993 "The server MAY send other lines of data before sending the version
994 string. Each line SHOULD be terminated by a Carriage Return and Line
995 Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
996 in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
997 MUST be able to process such lines." */
1000 while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
1001 if (substr($temp, -2) == "\r\n") {
1005 $temp.= fgets($this->fsock, 255);
1008 if (feof($this->fsock)) {
1009 user_error('Connection closed by server');
1013 $this->identifier = $this->_generate_identifier();
1015 if (defined('NET_SSH2_LOGGING')) {
1016 $this->_append_log('<-', $extra . $temp);
1017 $this->_append_log('->', $this->identifier . "\r\n");
1020 $this->server_identifier = trim($temp, "\r\n");
1021 if (strlen($extra)) {
1022 $this->errors[] = utf8_decode($extra);
1025 if ($matches[1] != '1.99' && $matches[1] != '2.0') {
1026 user_error("Cannot connect to SSH $matches[1] servers");
1030 fputs($this->fsock, $this->identifier . "\r\n");
1032 $response = $this->_get_binary_packet();
1033 if ($response === false) {
1034 user_error('Connection closed by server');
1038 if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
1039 user_error('Expected SSH_MSG_KEXINIT');
1043 if (!$this->_key_exchange($response)) {
1047 $this->bitmap|= NET_SSH2_MASK_CONNECTED;
1053 * Generates the SSH identifier
1055 * You should overwrite this method in your own class if you want to use another identifier
1060 function _generate_identifier()
1062 $identifier = 'SSH-2.0-phpseclib_0.3';
1065 if (extension_loaded('mcrypt')) {
1069 if (extension_loaded('gmp')) {
1071 } elseif (extension_loaded('bcmath')) {
1076 $identifier .= ' (' . implode(', ', $ext) . ')';
1085 * @param String $kexinit_payload_server
1088 function _key_exchange($kexinit_payload_server)
1090 static $kex_algorithms = array(
1091 'diffie-hellman-group1-sha1', // REQUIRED
1092 'diffie-hellman-group14-sha1' // REQUIRED
1095 static $server_host_key_algorithms = array(
1096 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
1097 'ssh-dss' // REQUIRED sign Raw DSS Key
1100 static $encryption_algorithms = false;
1101 if ($encryption_algorithms === false) {
1102 $encryption_algorithms = array(
1103 // from <http://tools.ietf.org/html/rfc4345#section-4>:
1107 //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
1109 // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
1110 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
1111 'aes192-ctr', // RECOMMENDED AES with 192-bit key
1112 'aes256-ctr', // RECOMMENDED AES with 256-bit key
1114 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
1115 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
1116 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
1118 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
1119 'aes192-cbc', // OPTIONAL AES with a 192-bit key
1120 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
1122 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key
1123 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key
1125 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc"
1126 // (this is being retained for historical reasons)
1128 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode
1130 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode
1132 '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
1134 '3des-cbc', // REQUIRED three-key 3DES in CBC mode
1135 //'none' // OPTIONAL no encryption; NOT RECOMMENDED
1138 if (phpseclib_resolve_include_path('Crypt/RC4.php') === false) {
1139 $encryption_algorithms = array_diff(
1140 $encryption_algorithms,
1141 array('arcfour256', 'arcfour128', 'arcfour')
1144 if (phpseclib_resolve_include_path('Crypt/Rijndael.php') === false) {
1145 $encryption_algorithms = array_diff(
1146 $encryption_algorithms,
1147 array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc')
1150 if (phpseclib_resolve_include_path('Crypt/Twofish.php') === false) {
1151 $encryption_algorithms = array_diff(
1152 $encryption_algorithms,
1153 array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc')
1156 if (phpseclib_resolve_include_path('Crypt/Blowfish.php') === false) {
1157 $encryption_algorithms = array_diff(
1158 $encryption_algorithms,
1159 array('blowfish-ctr', 'blowfish-cbc')
1162 if (phpseclib_resolve_include_path('Crypt/TripleDES.php') === false) {
1163 $encryption_algorithms = array_diff(
1164 $encryption_algorithms,
1165 array('3des-ctr', '3des-cbc')
1168 $encryption_algorithms = array_values($encryption_algorithms);
1171 $mac_algorithms = array(
1172 // from <http://www.ietf.org/rfc/rfc6668.txt>:
1173 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
1175 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
1176 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
1177 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
1178 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
1179 //'none' // OPTIONAL no MAC; NOT RECOMMENDED
1182 static $compression_algorithms = array(
1183 'none' // REQUIRED no compression
1184 //'zlib' // OPTIONAL ZLIB (LZ77) compression
1187 // some SSH servers have buggy implementations of some of the above algorithms
1188 switch ($this->server_identifier) {
1189 case 'SSH-2.0-SSHD':
1190 $mac_algorithms = array_values(array_diff(
1192 array('hmac-sha1-96', 'hmac-md5-96')
1196 static $str_kex_algorithms, $str_server_host_key_algorithms,
1197 $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
1198 $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
1200 if (empty($str_kex_algorithms)) {
1201 $str_kex_algorithms = implode(',', $kex_algorithms);
1202 $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
1203 $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
1204 $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
1205 $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
1208 $client_cookie = crypt_random_string(16);
1210 $response = $kexinit_payload_server;
1211 $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
1212 $server_cookie = $this->_string_shift($response, 16);
1214 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1215 $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1217 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1218 $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1220 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1221 $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1223 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1224 $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1226 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1227 $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1229 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1230 $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1232 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1233 $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1235 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1236 $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1238 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1239 $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1241 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1242 $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1244 extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1245 $first_kex_packet_follows = $first_kex_packet_follows != 0;
1247 // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
1248 $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
1249 NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
1250 strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
1251 $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
1252 strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
1253 $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
1254 strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
1258 if (!$this->_send_binary_packet($kexinit_payload_client)) {
1261 // here ends the second place.
1263 // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
1264 for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
1265 if ($i == count($encryption_algorithms)) {
1266 user_error('No compatible server to client encryption algorithms found');
1267 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1270 // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
1271 // diffie-hellman key exchange as fast as possible
1272 $decrypt = $encryption_algorithms[$i];
1276 $decryptKeyLength = 24; // eg. 192 / 8
1281 case 'twofish256-cbc':
1282 case 'twofish256-ctr':
1283 $decryptKeyLength = 32; // eg. 256 / 8
1287 case 'twofish192-cbc':
1288 case 'twofish192-ctr':
1289 $decryptKeyLength = 24; // eg. 192 / 8
1293 case 'twofish128-cbc':
1294 case 'twofish128-ctr':
1295 case 'blowfish-cbc':
1296 case 'blowfish-ctr':
1297 $decryptKeyLength = 16; // eg. 128 / 8
1301 $decryptKeyLength = 16; // eg. 128 / 8
1304 $decryptKeyLength = 32; // eg. 128 / 8
1307 $decryptKeyLength = 0;
1310 for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
1311 if ($i == count($encryption_algorithms)) {
1312 user_error('No compatible client to server encryption algorithms found');
1313 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1316 $encrypt = $encryption_algorithms[$i];
1320 $encryptKeyLength = 24;
1325 case 'twofish256-cbc':
1326 case 'twofish256-ctr':
1327 $encryptKeyLength = 32;
1331 case 'twofish192-cbc':
1332 case 'twofish192-ctr':
1333 $encryptKeyLength = 24;
1337 case 'twofish128-cbc':
1338 case 'twofish128-ctr':
1339 case 'blowfish-cbc':
1340 case 'blowfish-ctr':
1341 $encryptKeyLength = 16;
1345 $encryptKeyLength = 16;
1348 $encryptKeyLength = 32;
1351 $encryptKeyLength = 0;
1354 $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
1356 // through diffie-hellman key exchange a symmetric key is obtained
1357 for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
1358 if ($i == count($kex_algorithms)) {
1359 user_error('No compatible key exchange algorithms found');
1360 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1363 switch ($kex_algorithms[$i]) {
1364 // see http://tools.ietf.org/html/rfc2409#section-6.2 and
1365 // http://tools.ietf.org/html/rfc2412, appendex E
1366 case 'diffie-hellman-group1-sha1':
1367 $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1368 '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1369 '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1370 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
1372 // see http://tools.ietf.org/html/rfc3526#section-3
1373 case 'diffie-hellman-group14-sha1':
1374 $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1375 '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1376 '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1377 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
1378 '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
1379 '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
1380 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
1381 '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
1385 // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
1386 // the generator field element is 2 (decimal) and the hash function is sha1.
1387 $g = new Math_BigInteger(2);
1388 $prime = new Math_BigInteger($prime, 16);
1389 $kexHash = new Crypt_Hash('sha1');
1390 //$q = $p->bitwise_rightShift(1);
1392 /* To increase the speed of the key exchange, both client and server may
1393 reduce the size of their private exponents. It should be at least
1394 twice as long as the key material that is generated from the shared
1395 secret. For more details, see the paper by van Oorschot and Wiener
1398 -- http://tools.ietf.org/html/rfc4419#section-6.2 */
1399 $one = new Math_BigInteger(1);
1400 $keyLength = min($keyLength, $kexHash->getLength());
1401 $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
1402 $max = $max->subtract($one);
1404 $x = $one->random($one, $max);
1405 $e = $g->modPow($x, $prime);
1407 $eBytes = $e->toBytes(true);
1408 $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
1410 if (!$this->_send_binary_packet($data)) {
1411 user_error('Connection closed by server');
1415 $response = $this->_get_binary_packet();
1416 if ($response === false) {
1417 user_error('Connection closed by server');
1420 extract(unpack('Ctype', $this->_string_shift($response, 1)));
1422 if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
1423 user_error('Expected SSH_MSG_KEXDH_REPLY');
1427 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1428 $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
1430 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
1431 $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
1433 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1434 $fBytes = $this->_string_shift($response, $temp['length']);
1435 $f = new Math_BigInteger($fBytes, -256);
1437 $temp = unpack('Nlength', $this->_string_shift($response, 4));
1438 $this->signature = $this->_string_shift($response, $temp['length']);
1440 $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
1441 $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
1443 $key = $f->modPow($x, $prime);
1444 $keyBytes = $key->toBytes(true);
1446 $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
1447 strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
1448 strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
1449 $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
1450 $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
1453 $this->exchange_hash = $kexHash->hash($this->exchange_hash);
1455 if ($this->session_id === false) {
1456 $this->session_id = $this->exchange_hash;
1459 for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
1460 if ($i == count($server_host_key_algorithms)) {
1461 user_error('No compatible server host key algorithms found');
1462 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1465 if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
1466 user_error('Server Host Key Algorithm Mismatch');
1467 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1471 NET_SSH2_MSG_NEWKEYS
1474 if (!$this->_send_binary_packet($packet)) {
1478 $response = $this->_get_binary_packet();
1480 if ($response === false) {
1481 user_error('Connection closed by server');
1485 extract(unpack('Ctype', $this->_string_shift($response, 1)));
1487 if ($type != NET_SSH2_MSG_NEWKEYS) {
1488 user_error('Expected SSH_MSG_NEWKEYS');
1494 if (!class_exists('Crypt_TripleDES')) {
1495 include_once 'Crypt/TripleDES.php';
1497 $this->encrypt = new Crypt_TripleDES();
1498 // $this->encrypt_block_size = 64 / 8 == the default
1501 if (!class_exists('Crypt_TripleDES')) {
1502 include_once 'Crypt/TripleDES.php';
1504 $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
1505 // $this->encrypt_block_size = 64 / 8 == the default
1510 if (!class_exists('Crypt_Rijndael')) {
1511 include_once 'Crypt/Rijndael.php';
1513 $this->encrypt = new Crypt_Rijndael();
1514 $this->encrypt_block_size = 16; // eg. 128 / 8
1519 if (!class_exists('Crypt_Rijndael')) {
1520 include_once 'Crypt/Rijndael.php';
1522 $this->encrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
1523 $this->encrypt_block_size = 16; // eg. 128 / 8
1525 case 'blowfish-cbc':
1526 if (!class_exists('Crypt_Blowfish')) {
1527 include_once 'Crypt/Blowfish.php';
1529 $this->encrypt = new Crypt_Blowfish();
1530 $this->encrypt_block_size = 8;
1532 case 'blowfish-ctr':
1533 if (!class_exists('Crypt_Blowfish')) {
1534 include_once 'Crypt/Blowfish.php';
1536 $this->encrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
1537 $this->encrypt_block_size = 8;
1539 case 'twofish128-cbc':
1540 case 'twofish192-cbc':
1541 case 'twofish256-cbc':
1543 if (!class_exists('Crypt_Twofish')) {
1544 include_once 'Crypt/Twofish.php';
1546 $this->encrypt = new Crypt_Twofish();
1547 $this->encrypt_block_size = 16;
1549 case 'twofish128-ctr':
1550 case 'twofish192-ctr':
1551 case 'twofish256-ctr':
1552 if (!class_exists('Crypt_Twofish')) {
1553 include_once 'Crypt/Twofish.php';
1555 $this->encrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
1556 $this->encrypt_block_size = 16;
1561 if (!class_exists('Crypt_RC4')) {
1562 include_once 'Crypt/RC4.php';
1564 $this->encrypt = new Crypt_RC4();
1567 //$this->encrypt = new Crypt_Null();
1572 if (!class_exists('Crypt_TripleDES')) {
1573 include_once 'Crypt/TripleDES.php';
1575 $this->decrypt = new Crypt_TripleDES();
1578 if (!class_exists('Crypt_TripleDES')) {
1579 include_once 'Crypt/TripleDES.php';
1581 $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
1586 if (!class_exists('Crypt_Rijndael')) {
1587 include_once 'Crypt/Rijndael.php';
1589 $this->decrypt = new Crypt_Rijndael();
1590 $this->decrypt_block_size = 16;
1595 if (!class_exists('Crypt_Rijndael')) {
1596 include_once 'Crypt/Rijndael.php';
1598 $this->decrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
1599 $this->decrypt_block_size = 16;
1601 case 'blowfish-cbc':
1602 if (!class_exists('Crypt_Blowfish')) {
1603 include_once 'Crypt/Blowfish.php';
1605 $this->decrypt = new Crypt_Blowfish();
1606 $this->decrypt_block_size = 8;
1608 case 'blowfish-ctr':
1609 if (!class_exists('Crypt_Blowfish')) {
1610 include_once 'Crypt/Blowfish.php';
1612 $this->decrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
1613 $this->decrypt_block_size = 8;
1615 case 'twofish128-cbc':
1616 case 'twofish192-cbc':
1617 case 'twofish256-cbc':
1619 if (!class_exists('Crypt_Twofish')) {
1620 include_once 'Crypt/Twofish.php';
1622 $this->decrypt = new Crypt_Twofish();
1623 $this->decrypt_block_size = 16;
1625 case 'twofish128-ctr':
1626 case 'twofish192-ctr':
1627 case 'twofish256-ctr':
1628 if (!class_exists('Crypt_Twofish')) {
1629 include_once 'Crypt/Twofish.php';
1631 $this->decrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
1632 $this->decrypt_block_size = 16;
1637 if (!class_exists('Crypt_RC4')) {
1638 include_once 'Crypt/RC4.php';
1640 $this->decrypt = new Crypt_RC4();
1643 //$this->decrypt = new Crypt_Null();
1646 $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1648 if ($this->encrypt) {
1649 $this->encrypt->enableContinuousBuffer();
1650 $this->encrypt->disablePadding();
1652 $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
1653 while ($this->encrypt_block_size > strlen($iv)) {
1654 $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1656 $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
1658 $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
1659 while ($encryptKeyLength > strlen($key)) {
1660 $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1662 $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
1665 if ($this->decrypt) {
1666 $this->decrypt->enableContinuousBuffer();
1667 $this->decrypt->disablePadding();
1669 $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
1670 while ($this->decrypt_block_size > strlen($iv)) {
1671 $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1673 $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
1675 $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
1676 while ($decryptKeyLength > strlen($key)) {
1677 $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1679 $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
1682 /* The "arcfour128" algorithm is the RC4 cipher, as described in
1683 [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
1684 generated by the cipher MUST be discarded, and the first byte of the
1685 first encrypted packet MUST be encrypted using the 1537th byte of
1688 -- http://tools.ietf.org/html/rfc4345#section-4 */
1689 if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
1690 $this->encrypt->encrypt(str_repeat("\0", 1536));
1692 if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
1693 $this->decrypt->decrypt(str_repeat("\0", 1536));
1696 for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
1697 if ($i == count($mac_algorithms)) {
1698 user_error('No compatible client to server message authentication algorithms found');
1699 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1702 $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
1703 switch ($mac_algorithms[$i]) {
1704 case 'hmac-sha2-256':
1705 $this->hmac_create = new Crypt_Hash('sha256');
1706 $createKeyLength = 32;
1709 $this->hmac_create = new Crypt_Hash('sha1');
1710 $createKeyLength = 20;
1712 case 'hmac-sha1-96':
1713 $this->hmac_create = new Crypt_Hash('sha1-96');
1714 $createKeyLength = 20;
1717 $this->hmac_create = new Crypt_Hash('md5');
1718 $createKeyLength = 16;
1721 $this->hmac_create = new Crypt_Hash('md5-96');
1722 $createKeyLength = 16;
1725 for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
1726 if ($i == count($mac_algorithms)) {
1727 user_error('No compatible server to client message authentication algorithms found');
1728 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1731 $checkKeyLength = 0;
1732 $this->hmac_size = 0;
1733 switch ($mac_algorithms[$i]) {
1734 case 'hmac-sha2-256':
1735 $this->hmac_check = new Crypt_Hash('sha256');
1736 $checkKeyLength = 32;
1737 $this->hmac_size = 32;
1740 $this->hmac_check = new Crypt_Hash('sha1');
1741 $checkKeyLength = 20;
1742 $this->hmac_size = 20;
1744 case 'hmac-sha1-96':
1745 $this->hmac_check = new Crypt_Hash('sha1-96');
1746 $checkKeyLength = 20;
1747 $this->hmac_size = 12;
1750 $this->hmac_check = new Crypt_Hash('md5');
1751 $checkKeyLength = 16;
1752 $this->hmac_size = 16;
1755 $this->hmac_check = new Crypt_Hash('md5-96');
1756 $checkKeyLength = 16;
1757 $this->hmac_size = 12;
1760 $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
1761 while ($createKeyLength > strlen($key)) {
1762 $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1764 $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
1766 $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
1767 while ($checkKeyLength > strlen($key)) {
1768 $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1770 $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1772 for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
1773 if ($i == count($compression_algorithms)) {
1774 user_error('No compatible server to client compression algorithms found');
1775 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1777 $this->decompress = $compression_algorithms[$i] == 'zlib';
1779 for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
1780 if ($i == count($compression_algorithms)) {
1781 user_error('No compatible client to server compression algorithms found');
1782 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1784 $this->compress = $compression_algorithms[$i] == 'zlib';
1792 * The $password parameter can be a plaintext password, a Crypt_RSA object or an array
1794 * @param String $username
1795 * @param Mixed $password
1801 function login($username)
1803 $args = func_get_args();
1804 return call_user_func_array(array(&$this, '_login'), $args);
1810 * @param String $username
1811 * @param Mixed $password
1814 * @see _login_helper
1817 function _login($username)
1819 if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
1820 if (!$this->_connect()) {
1825 $args = array_slice(func_get_args(), 1);
1827 return $this->_login_helper($username);
1830 foreach ($args as $arg) {
1831 if ($this->_login_helper($username, $arg)) {
1841 * @param String $username
1842 * @param optional String $password
1845 * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
1846 * by sending dummy SSH_MSG_IGNORE messages.
1848 function _login_helper($username, $password = null)
1850 if (!($this->bitmap & NET_SSH2_MASK_CONNECTED)) {
1854 if (!($this->bitmap & NET_SSH2_MASK_LOGIN_REQ)) {
1855 $packet = pack('CNa*',
1856 NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
1859 if (!$this->_send_binary_packet($packet)) {
1863 $response = $this->_get_binary_packet();
1864 if ($response === false) {
1865 user_error('Connection closed by server');
1869 extract(unpack('Ctype', $this->_string_shift($response, 1)));
1871 if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
1872 user_error('Expected SSH_MSG_SERVICE_ACCEPT');
1875 $this->bitmap |= NET_SSH2_MASK_LOGIN_REQ;
1878 if (strlen($this->last_interactive_response)) {
1879 return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
1882 // although PHP5's get_class() preserves the case, PHP4's does not
1883 if (is_object($password)) {
1884 switch (strtolower(get_class($password))) {
1886 return $this->_privatekey_login($username, $password);
1887 case 'system_ssh_agent':
1888 return $this->_ssh_agent_login($username, $password);
1892 if (is_array($password)) {
1893 if ($this->_keyboard_interactive_login($username, $password)) {
1894 $this->bitmap |= NET_SSH2_MASK_LOGIN;
1900 if (!isset($password)) {
1901 $packet = pack('CNa*Na*Na*',
1902 NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1903 strlen('none'), 'none'
1906 if (!$this->_send_binary_packet($packet)) {
1910 $response = $this->_get_binary_packet();
1911 if ($response === false) {
1912 user_error('Connection closed by server');
1916 extract(unpack('Ctype', $this->_string_shift($response, 1)));
1919 case NET_SSH2_MSG_USERAUTH_SUCCESS:
1920 $this->bitmap |= NET_SSH2_MASK_LOGIN;
1922 //case NET_SSH2_MSG_USERAUTH_FAILURE:
1928 $packet = pack('CNa*Na*Na*CNa*',
1929 NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1930 strlen('password'), 'password', 0, strlen($password), $password
1933 // remove the username and password from the logged packet
1934 if (!defined('NET_SSH2_LOGGING')) {
1937 $logged = pack('CNa*Na*Na*CNa*',
1938 NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
1939 strlen('password'), 'password', 0, strlen('password'), 'password'
1943 if (!$this->_send_binary_packet($packet, $logged)) {
1947 $response = $this->_get_binary_packet();
1948 if ($response === false) {
1949 user_error('Connection closed by server');
1953 extract(unpack('Ctype', $this->_string_shift($response, 1)));
1956 case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
1957 if (defined('NET_SSH2_LOGGING')) {
1958 $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
1960 extract(unpack('Nlength', $this->_string_shift($response, 4)));
1961 $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
1962 return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
1963 case NET_SSH2_MSG_USERAUTH_FAILURE:
1964 // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
1965 // multi-factor authentication
1966 extract(unpack('Nlength', $this->_string_shift($response, 4)));
1967 $auth_methods = explode(',', $this->_string_shift($response, $length));
1968 extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
1969 $partial_success = $partial_success != 0;
1971 if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
1972 if ($this->_keyboard_interactive_login($username, $password)) {
1973 $this->bitmap |= NET_SSH2_MASK_LOGIN;
1979 case NET_SSH2_MSG_USERAUTH_SUCCESS:
1980 $this->bitmap |= NET_SSH2_MASK_LOGIN;
1988 * Login via keyboard-interactive authentication
1990 * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
1992 * @param String $username
1993 * @param String $password
1997 function _keyboard_interactive_login($username, $password)
1999 $packet = pack('CNa*Na*Na*Na*Na*',
2000 NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
2001 strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, ''
2004 if (!$this->_send_binary_packet($packet)) {
2008 return $this->_keyboard_interactive_process($password);
2012 * Handle the keyboard-interactive requests / responses.
2014 * @param String $responses...
2018 function _keyboard_interactive_process()
2020 $responses = func_get_args();
2022 if (strlen($this->last_interactive_response)) {
2023 $response = $this->last_interactive_response;
2025 $orig = $response = $this->_get_binary_packet();
2026 if ($response === false) {
2027 user_error('Connection closed by server');
2032 extract(unpack('Ctype', $this->_string_shift($response, 1)));
2035 case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
2036 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2037 $this->_string_shift($response, $length); // name; may be empty
2038 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2039 $this->_string_shift($response, $length); // instruction; may be empty
2040 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2041 $this->_string_shift($response, $length); // language tag; may be empty
2042 extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
2044 for ($i = 0; $i < count($responses); $i++) {
2045 if (is_array($responses[$i])) {
2046 foreach ($responses[$i] as $key => $value) {
2047 $this->keyboard_requests_responses[$key] = $value;
2049 unset($responses[$i]);
2052 $responses = array_values($responses);
2054 if (isset($this->keyboard_requests_responses)) {
2055 for ($i = 0; $i < $num_prompts; $i++) {
2056 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2057 // prompt - ie. "Password: "; must not be empty
2058 $prompt = $this->_string_shift($response, $length);
2059 //$echo = $this->_string_shift($response) != chr(0);
2060 foreach ($this->keyboard_requests_responses as $key => $value) {
2061 if (substr($prompt, 0, strlen($key)) == $key) {
2062 $responses[] = $value;
2069 // see http://tools.ietf.org/html/rfc4256#section-3.2
2070 if (strlen($this->last_interactive_response)) {
2071 $this->last_interactive_response = '';
2072 } else if (defined('NET_SSH2_LOGGING')) {
2073 $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2075 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
2076 $this->message_number_log[count($this->message_number_log) - 1]
2080 if (!count($responses) && $num_prompts) {
2081 $this->last_interactive_response = $orig;
2086 After obtaining the requested information from the user, the client
2087 MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
2089 // see http://tools.ietf.org/html/rfc4256#section-3.4
2090 $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
2091 for ($i = 0; $i < count($responses); $i++) {
2092 $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
2093 $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
2096 if (!$this->_send_binary_packet($packet, $logged)) {
2100 if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
2101 $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2103 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
2104 $this->message_number_log[count($this->message_number_log) - 1]
2109 After receiving the response, the server MUST send either an
2110 SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
2111 SSH_MSG_USERAUTH_INFO_REQUEST message.
2113 // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
2114 // there could be an infinite loop of request / responses.
2115 return $this->_keyboard_interactive_process();
2116 case NET_SSH2_MSG_USERAUTH_SUCCESS:
2118 case NET_SSH2_MSG_USERAUTH_FAILURE:
2126 * Login with an ssh-agent provided key
2128 * @param String $username
2129 * @param System_SSH_Agent $agent
2133 function _ssh_agent_login($username, $agent)
2135 $keys = $agent->requestIdentities();
2136 foreach ($keys as $key) {
2137 if ($this->_privatekey_login($username, $key)) {
2146 * Login with an RSA private key
2148 * @param String $username
2149 * @param Crypt_RSA $password
2152 * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
2153 * by sending dummy SSH_MSG_IGNORE messages.
2155 function _privatekey_login($username, $privatekey)
2157 // see http://tools.ietf.org/html/rfc4253#page-15
2158 $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
2159 if ($publickey === false) {
2164 'e' => $publickey['e']->toBytes(true),
2165 'n' => $publickey['n']->toBytes(true)
2167 $publickey = pack('Na*Na*Na*',
2168 strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
2171 $part1 = pack('CNa*Na*Na*',
2172 NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
2173 strlen('publickey'), 'publickey'
2175 $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
2177 $packet = $part1 . chr(0) . $part2;
2178 if (!$this->_send_binary_packet($packet)) {
2182 $response = $this->_get_binary_packet();
2183 if ($response === false) {
2184 user_error('Connection closed by server');
2188 extract(unpack('Ctype', $this->_string_shift($response, 1)));
2191 case NET_SSH2_MSG_USERAUTH_FAILURE:
2192 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2193 $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
2195 case NET_SSH2_MSG_USERAUTH_PK_OK:
2196 // we'll just take it on faith that the public key blob and the public key algorithm name are as
2198 if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
2199 $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2201 'NET_SSH2_MSG_USERAUTH_PK_OK',
2202 $this->message_number_log[count($this->message_number_log) - 1]
2207 $packet = $part1 . chr(1) . $part2;
2208 $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
2209 $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
2210 $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
2211 $packet.= pack('Na*', strlen($signature), $signature);
2213 if (!$this->_send_binary_packet($packet)) {
2217 $response = $this->_get_binary_packet();
2218 if ($response === false) {
2219 user_error('Connection closed by server');
2223 extract(unpack('Ctype', $this->_string_shift($response, 1)));
2226 case NET_SSH2_MSG_USERAUTH_FAILURE:
2227 // either the login is bad or the server employs multi-factor authentication
2229 case NET_SSH2_MSG_USERAUTH_SUCCESS:
2230 $this->bitmap |= NET_SSH2_MASK_LOGIN;
2240 * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
2241 * Setting $timeout to false or 0 will mean there is no timeout.
2243 * @param Mixed $timeout
2246 function setTimeout($timeout)
2248 $this->timeout = $this->curTimeout = $timeout;
2252 * Get the output from stdError
2256 function getStdError()
2258 return $this->stdErrorLog;
2264 * If $callback is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
2265 * In all likelihood, this is not a feature you want to be taking advantage of.
2267 * @param String $command
2268 * @param optional Callback $callback
2272 function exec($command, $callback = null)
2274 $this->curTimeout = $this->timeout;
2275 $this->is_timeout = false;
2276 $this->stdErrorLog = '';
2278 if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2282 // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
2283 // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
2284 // honestly, if you're transfering more than 2GB, you probably shouldn't be using phpseclib, anyway.
2285 // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
2286 $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC] = $this->window_size;
2287 // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
2288 // uses 0x4000, that's what will be used here, as well.
2289 $packet_size = 0x4000;
2291 $packet = pack('CNa*N3',
2292 NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC], $packet_size);
2294 if (!$this->_send_binary_packet($packet)) {
2298 $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
2300 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
2301 if ($response === false) {
2305 if ($this->request_pty === true) {
2306 $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2307 $packet = pack('CNNa*CNa*N5a*',
2308 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
2309 $this->windowColumns, $this->windowRows, 0, 0, strlen($terminal_modes), $terminal_modes);
2311 if (!$this->_send_binary_packet($packet)) {
2314 $response = $this->_get_binary_packet();
2315 if ($response === false) {
2316 user_error('Connection closed by server');
2320 list(, $type) = unpack('C', $this->_string_shift($response, 1));
2323 case NET_SSH2_MSG_CHANNEL_SUCCESS:
2325 case NET_SSH2_MSG_CHANNEL_FAILURE:
2327 user_error('Unable to request pseudo-terminal');
2328 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2330 $this->in_request_pty_exec = true;
2333 // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
2334 // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
2335 // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
2336 // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
2337 // neither will your script.
2339 // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
2340 // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
2341 // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
2342 $packet = pack('CNNa*CNa*',
2343 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
2344 if (!$this->_send_binary_packet($packet)) {
2348 $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2350 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
2351 if ($response === false) {
2355 $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
2357 if ($callback === false || $this->in_request_pty_exec) {
2363 $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
2365 case $temp === true:
2366 return is_callable($callback) ? true : $output;
2367 case $temp === false:
2370 if (is_callable($callback)) {
2371 if (call_user_func($callback, $temp) === true) {
2372 $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
2383 * Creates an interactive shell
2385 * @see Net_SSH2::read()
2386 * @see Net_SSH2::write()
2390 function _initShell()
2392 if ($this->in_request_pty_exec === true) {
2396 $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL] = $this->window_size;
2397 $packet_size = 0x4000;
2399 $packet = pack('CNa*N3',
2400 NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL], $packet_size);
2402 if (!$this->_send_binary_packet($packet)) {
2406 $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
2408 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
2409 if ($response === false) {
2413 $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2414 $packet = pack('CNNa*CNa*N5a*',
2415 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
2416 $this->windowColumns, $this->windowRows, 0, 0, strlen($terminal_modes), $terminal_modes);
2418 if (!$this->_send_binary_packet($packet)) {
2422 $response = $this->_get_binary_packet();
2423 if ($response === false) {
2424 user_error('Connection closed by server');
2428 list(, $type) = unpack('C', $this->_string_shift($response, 1));
2431 case NET_SSH2_MSG_CHANNEL_SUCCESS:
2432 // if a pty can't be opened maybe commands can still be executed
2433 case NET_SSH2_MSG_CHANNEL_FAILURE:
2436 user_error('Unable to request pseudo-terminal');
2437 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2440 $packet = pack('CNNa*C',
2441 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
2442 if (!$this->_send_binary_packet($packet)) {
2446 $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
2448 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
2449 if ($response === false) {
2453 $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
2455 $this->bitmap |= NET_SSH2_MASK_SHELL;
2461 * Return the channel to be used with read() / write()
2463 * @see Net_SSH2::read()
2464 * @see Net_SSH2::write()
2468 function _get_interactive_channel()
2471 case $this->in_subsystem:
2472 return NET_SSH2_CHANNEL_SUBSYSTEM;
2473 case $this->in_request_pty_exec:
2474 return NET_SSH2_CHANNEL_EXEC;
2476 return NET_SSH2_CHANNEL_SHELL;
2481 * Returns the output of an interactive shell
2483 * Returns when there's a match for $expect, which can take the form of a string literal or,
2484 * if $mode == NET_SSH2_READ_REGEX, a regular expression.
2486 * @see Net_SSH2::write()
2487 * @param String $expect
2488 * @param Integer $mode
2492 function read($expect = '', $mode = NET_SSH2_READ_SIMPLE)
2494 $this->curTimeout = $this->timeout;
2495 $this->is_timeout = false;
2497 if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2498 user_error('Operation disallowed prior to login()');
2502 if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
2503 user_error('Unable to initiate an interactive shell session');
2507 $channel = $this->_get_interactive_channel();
2511 if ($mode == NET_SSH2_READ_REGEX) {
2512 preg_match($expect, $this->interactiveBuffer, $matches);
2513 $match = isset($matches[0]) ? $matches[0] : '';
2515 $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
2516 if ($pos !== false) {
2517 return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
2519 $response = $this->_get_channel_packet($channel);
2520 if (is_bool($response)) {
2521 $this->in_request_pty_exec = false;
2522 return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
2525 $this->interactiveBuffer.= $response;
2530 * Inputs a command into an interactive shell.
2532 * @see Net_SSH2::read()
2533 * @param String $cmd
2537 function write($cmd)
2539 if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2540 user_error('Operation disallowed prior to login()');
2544 if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
2545 user_error('Unable to initiate an interactive shell session');
2549 return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
2553 * Start a subsystem.
2555 * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
2556 * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
2557 * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
2558 * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
2559 * if there's sufficient demand for such a feature.
2561 * @see Net_SSH2::stopSubsystem()
2562 * @param String $subsystem
2566 function startSubsystem($subsystem)
2568 $this->window_size_server_to_client[NET_SSH2_CHANNEL_SUBSYSTEM] = $this->window_size;
2570 $packet = pack('CNa*N3',
2571 NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SUBSYSTEM, $this->window_size, 0x4000);
2573 if (!$this->_send_binary_packet($packet)) {
2577 $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
2579 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
2580 if ($response === false) {
2584 $packet = pack('CNNa*CNa*',
2585 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SUBSYSTEM], strlen('subsystem'), 'subsystem', 1, strlen($subsystem), $subsystem);
2586 if (!$this->_send_binary_packet($packet)) {
2590 $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
2592 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
2594 if ($response === false) {
2598 $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
2600 $this->bitmap |= NET_SSH2_MASK_SHELL;
2601 $this->in_subsystem = true;
2607 * Stops a subsystem.
2609 * @see Net_SSH2::startSubsystem()
2613 function stopSubsystem()
2615 $this->in_subsystem = false;
2616 $this->_close_channel(NET_SSH2_CHANNEL_SUBSYSTEM);
2623 * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
2629 $this->_close_channel($this->_get_interactive_channel());
2635 * Did exec() or read() return because they timed out or because they encountered the end?
2639 function isTimeout()
2641 return $this->is_timeout;
2649 function disconnect()
2651 $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2652 if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
2653 fclose($this->realtime_log_file);
2660 * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
2665 function __destruct()
2667 $this->disconnect();
2671 * Is the connection still active?
2676 function isConnected()
2678 return (bool) ($this->bitmap & NET_SSH2_MASK_CONNECTED);
2682 * Gets Binary Packets
2684 * See '6. Binary Packet Protocol' of rfc4253 for more info.
2686 * @see Net_SSH2::_send_binary_packet()
2690 function _get_binary_packet()
2692 if (!is_resource($this->fsock) || feof($this->fsock)) {
2693 user_error('Connection closed prematurely');
2698 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
2699 $raw = fread($this->fsock, $this->decrypt_block_size);
2701 if (!strlen($raw)) {
2705 if ($this->decrypt !== false) {
2706 $raw = $this->decrypt->decrypt($raw);
2708 if ($raw === false) {
2709 user_error('Unable to decrypt content');
2713 extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
2715 $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
2717 // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
2718 // "implementations SHOULD check that the packet length is reasonable"
2719 // PuTTY uses 0x9000 as the actual max packet size and so to shall we
2720 if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
2721 user_error('Invalid size');
2726 while ($remaining_length > 0) {
2727 $temp = fread($this->fsock, $remaining_length);
2728 if ($temp === false || feof($this->fsock)) {
2729 user_error('Error reading from socket');
2734 $remaining_length-= strlen($temp);
2736 $stop = strtok(microtime(), ' ') + strtok('');
2737 if (strlen($buffer)) {
2738 $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
2741 $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
2742 $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
2744 if ($this->hmac_check !== false) {
2745 $hmac = fread($this->fsock, $this->hmac_size);
2746 if ($hmac === false || strlen($hmac) != $this->hmac_size) {
2747 user_error('Error reading socket');
2750 } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
2751 user_error('Invalid HMAC');
2756 //if ($this->decompress) {
2757 // $payload = gzinflate(substr($payload, 2));
2760 $this->get_seq_no++;
2762 if (defined('NET_SSH2_LOGGING')) {
2763 $current = strtok(microtime(), ' ') + strtok('');
2764 $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
2765 $message_number = '<- ' . $message_number .
2766 ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
2767 $this->_append_log($message_number, $payload);
2768 $this->last_packet = $current;
2771 return $this->_filter($payload);
2775 * Filter Binary Packets
2777 * Because some binary packets need to be ignored...
2779 * @see Net_SSH2::_get_binary_packet()
2783 function _filter($payload)
2785 switch (ord($payload[0])) {
2786 case NET_SSH2_MSG_DISCONNECT:
2787 $this->_string_shift($payload, 1);
2788 extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
2789 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
2792 case NET_SSH2_MSG_IGNORE:
2793 $payload = $this->_get_binary_packet();
2795 case NET_SSH2_MSG_DEBUG:
2796 $this->_string_shift($payload, 2);
2797 extract(unpack('Nlength', $this->_string_shift($payload, 4)));
2798 $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
2799 $payload = $this->_get_binary_packet();
2801 case NET_SSH2_MSG_UNIMPLEMENTED:
2803 case NET_SSH2_MSG_KEXINIT:
2804 if ($this->session_id !== false) {
2805 if (!$this->_key_exchange($payload)) {
2809 $payload = $this->_get_binary_packet();
2813 // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
2814 if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
2815 $this->_string_shift($payload, 1);
2816 extract(unpack('Nlength', $this->_string_shift($payload, 4)));
2817 $this->banner_message = utf8_decode($this->_string_shift($payload, $length));
2818 $payload = $this->_get_binary_packet();
2821 // only called when we've already logged in
2822 if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2823 switch (ord($payload[0])) {
2824 case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
2825 $this->_string_shift($payload, 1);
2826 extract(unpack('Nlength', $this->_string_shift($payload)));
2827 $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
2829 if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
2830 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2833 $payload = $this->_get_binary_packet();
2835 case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
2836 $this->_string_shift($payload, 1);
2837 extract(unpack('Nlength', $this->_string_shift($payload, 4)));
2838 $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
2840 $this->_string_shift($payload, 4); // skip over client channel
2841 extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
2843 $packet = pack('CN3a*Na*',
2844 NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
2846 if (!$this->_send_binary_packet($packet)) {
2847 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2850 $payload = $this->_get_binary_packet();
2852 case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
2853 $this->_string_shift($payload, 1);
2854 extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
2855 extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
2856 $this->window_size_client_to_server[$channel]+= $window_size;
2858 $payload = ($this->bitmap & NET_SSH2_MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet();
2868 * Suppress stderr from output
2872 function enableQuietMode()
2874 $this->quiet_mode = true;
2878 * Disable Quiet Mode
2880 * Show stderr in output
2884 function disableQuietMode()
2886 $this->quiet_mode = false;
2890 * Returns whether Quiet Mode is enabled or not
2892 * @see Net_SSH2::enableQuietMode()
2893 * @see Net_SSH2::disableQuietMode()
2898 function isQuietModeEnabled()
2900 return $this->quiet_mode;
2904 * Enable request-pty when using exec()
2908 function enablePTY()
2910 $this->request_pty = true;
2914 * Disable request-pty when using exec()
2918 function disablePTY()
2920 $this->request_pty = false;
2924 * Returns whether request-pty is enabled or not
2926 * @see Net_SSH2::enablePTY()
2927 * @see Net_SSH2::disablePTY()
2932 function isPTYEnabled()
2934 return $this->request_pty;
2940 * Returns the data as a string if it's available and false if not.
2942 * @param $client_channel
2946 function _get_channel_packet($client_channel, $skip_extended = false)
2948 if (!empty($this->channel_buffers[$client_channel])) {
2949 return array_shift($this->channel_buffers[$client_channel]);
2953 if ($this->curTimeout) {
2954 if ($this->curTimeout < 0) {
2955 $this->is_timeout = true;
2959 $read = array($this->fsock);
2960 $write = $except = null;
2962 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
2963 $sec = floor($this->curTimeout);
2964 $usec = 1000000 * ($this->curTimeout - $sec);
2965 // on windows this returns a "Warning: Invalid CRT parameters detected" error
2966 if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
2967 $this->is_timeout = true;
2970 $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
2971 $this->curTimeout-= $elapsed;
2974 $response = $this->_get_binary_packet();
2975 if ($response === false) {
2976 user_error('Connection closed by server');
2979 if ($client_channel == -1 && $response === true) {
2982 if (!strlen($response)) {
2986 extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
2988 $this->window_size_server_to_client[$channel]-= strlen($response);
2990 // resize the window, if appropriate
2991 if ($this->window_size_server_to_client[$channel] < 0) {
2992 $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size);
2993 if (!$this->_send_binary_packet($packet)) {
2996 $this->window_size_server_to_client[$channel]+= $this->window_size;
2999 switch ($this->channel_status[$channel]) {
3000 case NET_SSH2_MSG_CHANNEL_OPEN:
3002 case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3003 extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
3004 $this->server_channels[$channel] = $server_channel;
3005 extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
3006 $this->window_size_client_to_server[$channel] = $window_size;
3007 $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
3008 $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
3009 return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3010 //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3012 user_error('Unable to open channel');
3013 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3016 case NET_SSH2_MSG_CHANNEL_REQUEST:
3018 case NET_SSH2_MSG_CHANNEL_SUCCESS:
3020 case NET_SSH2_MSG_CHANNEL_FAILURE:
3023 user_error('Unable to fulfill channel request');
3024 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3026 case NET_SSH2_MSG_CHANNEL_CLOSE:
3027 return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3030 // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
3033 case NET_SSH2_MSG_CHANNEL_DATA:
3035 if ($channel == NET_SSH2_CHANNEL_EXEC) {
3036 // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
3037 // this actually seems to make things twice as fast. more to the point, the message right after
3038 // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
3039 // in OpenSSH it slows things down but only by a couple thousandths of a second.
3040 $this->_send_channel_packet($channel, chr(0));
3043 extract(unpack('Nlength', $this->_string_shift($response, 4)));
3044 $data = $this->_string_shift($response, $length);
3045 if ($client_channel == $channel) {
3048 if (!isset($this->channel_buffers[$channel])) {
3049 $this->channel_buffers[$channel] = array();
3051 $this->channel_buffers[$channel][] = $data;
3053 case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3055 if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
3056 $this->_send_channel_packet($client_channel, chr(0));
3059 // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
3060 extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
3061 $data = $this->_string_shift($response, $length);
3062 $this->stdErrorLog.= $data;
3063 if ($skip_extended || $this->quiet_mode) {
3066 if ($client_channel == $channel) {
3069 if (!isset($this->channel_buffers[$channel])) {
3070 $this->channel_buffers[$channel] = array();
3072 $this->channel_buffers[$channel][] = $data;
3074 case NET_SSH2_MSG_CHANNEL_REQUEST:
3075 extract(unpack('Nlength', $this->_string_shift($response, 4)));
3076 $value = $this->_string_shift($response, $length);
3079 $this->_string_shift($response, 1);
3080 extract(unpack('Nlength', $this->_string_shift($response, 4)));
3081 $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
3082 $this->_string_shift($response, 1);
3083 extract(unpack('Nlength', $this->_string_shift($response, 4)));
3085 $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
3088 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3089 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3091 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
3095 extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
3096 $this->exit_status = $exit_status;
3098 // "The client MAY ignore these messages."
3099 // -- http://tools.ietf.org/html/rfc4254#section-6.10
3103 // "Some systems may not implement signals, in which case they SHOULD ignore this message."
3104 // -- http://tools.ietf.org/html/rfc4254#section-6.9
3108 case NET_SSH2_MSG_CHANNEL_CLOSE:
3109 $this->curTimeout = 0;
3111 if ($this->bitmap & NET_SSH2_MASK_SHELL) {
3112 $this->bitmap&= ~NET_SSH2_MASK_SHELL;
3114 if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
3115 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3118 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3120 case NET_SSH2_MSG_CHANNEL_EOF:
3123 user_error('Error reading channel data');
3124 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3130 * Sends Binary Packets
3132 * See '6. Binary Packet Protocol' of rfc4253 for more info.
3134 * @param String $data
3135 * @param optional String $logged
3136 * @see Net_SSH2::_get_binary_packet()
3140 function _send_binary_packet($data, $logged = null)
3142 if (!is_resource($this->fsock) || feof($this->fsock)) {
3143 user_error('Connection closed prematurely');
3148 //if ($this->compress) {
3149 // // the -4 removes the checksum:
3150 // // http://php.net/function.gzcompress#57710
3151 // $data = substr(gzcompress($data), 0, -4);
3154 // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
3155 $packet_length = strlen($data) + 9;
3156 // round up to the nearest $this->encrypt_block_size
3157 $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
3158 // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
3159 $padding_length = $packet_length - strlen($data) - 5;
3160 $padding = crypt_random_string($padding_length);
3162 // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
3163 $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
3165 $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
3166 $this->send_seq_no++;
3168 if ($this->encrypt !== false) {
3169 $packet = $this->encrypt->encrypt($packet);
3174 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
3175 $result = strlen($packet) == fputs($this->fsock, $packet);
3176 $stop = strtok(microtime(), ' ') + strtok('');
3178 if (defined('NET_SSH2_LOGGING')) {
3179 $current = strtok(microtime(), ' ') + strtok('');
3180 $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
3181 $message_number = '-> ' . $message_number .
3182 ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3183 $this->_append_log($message_number, isset($logged) ? $logged : $data);
3184 $this->last_packet = $current;
3193 * Makes sure that only the last 1MB worth of packets will be logged
3195 * @param String $data
3198 function _append_log($message_number, $message)
3200 // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
3201 if (strlen($message_number) > 2) {
3202 $this->_string_shift($message);
3205 switch (NET_SSH2_LOGGING) {
3206 // useful for benchmarks
3207 case NET_SSH2_LOG_SIMPLE:
3208 $this->message_number_log[] = $message_number;
3210 // the most useful log for SSH2
3211 case NET_SSH2_LOG_COMPLEX:
3212 $this->message_number_log[] = $message_number;
3213 $this->log_size+= strlen($message);
3214 $this->message_log[] = $message;
3215 while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
3216 $this->log_size-= strlen(array_shift($this->message_log));
3217 array_shift($this->message_number_log);
3220 // dump the output out realtime; packets may be interspersed with non packets,
3221 // passwords won't be filtered out and select other packets may not be correctly
3223 case NET_SSH2_LOG_REALTIME:
3226 $start = $stop = "\r\n";
3232 echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
3236 // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE
3237 // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE.
3238 // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
3239 // at the beginning of the file
3240 case NET_SSH2_LOG_REALTIME_FILE:
3241 if (!isset($this->realtime_log_file)) {
3242 // PHP doesn't seem to like using constants in fopen()
3243 $filename = NET_SSH2_LOG_REALTIME_FILENAME;
3244 $fp = fopen($filename, 'w');
3245 $this->realtime_log_file = $fp;
3247 if (!is_resource($this->realtime_log_file)) {
3250 $entry = $this->_format_log(array($message), array($message_number));
3251 if ($this->realtime_log_wrap) {
3252 $temp = "<<< START >>>\r\n";
3254 fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
3256 $this->realtime_log_size+= strlen($entry);
3257 if ($this->realtime_log_size > NET_SSH2_LOG_MAX_SIZE) {
3258 fseek($this->realtime_log_file, 0);
3259 $this->realtime_log_size = strlen($entry);
3260 $this->realtime_log_wrap = true;
3262 fputs($this->realtime_log_file, $entry);
3267 * Sends channel data
3269 * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
3271 * @param Integer $client_channel
3272 * @param String $data
3276 function _send_channel_packet($client_channel, $data)
3278 while (strlen($data)) {
3279 if (!$this->window_size_client_to_server[$client_channel]) {
3280 $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
3281 // using an invalid channel will let the buffers be built up for the valid channels
3282 $this->_get_channel_packet(-1);
3283 $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
3286 /* The maximum amount of data allowed is determined by the maximum
3287 packet size for the channel, and the current window size, whichever
3289 -- http://tools.ietf.org/html/rfc4254#section-5.2 */
3291 $this->packet_size_client_to_server[$client_channel],
3292 $this->window_size_client_to_server[$client_channel]
3295 $temp = $this->_string_shift($data, $max_size);
3296 $packet = pack('CN2a*',
3297 NET_SSH2_MSG_CHANNEL_DATA,
3298 $this->server_channels[$client_channel],
3302 $this->window_size_client_to_server[$client_channel]-= strlen($temp);
3303 if (!$this->_send_binary_packet($packet)) {
3312 * Closes and flushes a channel
3314 * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
3315 * and for SFTP channels are presumably closed when the client disconnects. This functions is intended
3316 * for SCP more than anything.
3318 * @param Integer $client_channel
3319 * @param Boolean $want_reply
3323 function _close_channel($client_channel, $want_reply = false)
3325 // see http://tools.ietf.org/html/rfc4254#section-5.3
3327 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3330 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
3333 $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3335 $this->curTimeout = 0;
3337 while (!is_bool($this->_get_channel_packet($client_channel)));
3340 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
3343 if ($this->bitmap & NET_SSH2_MASK_SHELL) {
3344 $this->bitmap&= ~NET_SSH2_MASK_SHELL;
3351 * @param Integer $reason
3355 function _disconnect($reason)
3357 if ($this->bitmap & NET_SSH2_MASK_CONNECTED) {
3358 $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
3359 $this->_send_binary_packet($data);
3361 fclose($this->fsock);
3369 * Inspired by array_shift
3371 * @param String $string
3372 * @param optional Integer $index
3376 function _string_shift(&$string, $index = 1)
3378 $substr = substr($string, 0, $index);
3379 $string = substr($string, $index);
3386 * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
3387 * named constants from it, using the value as the name of the constant and the index as the value of the constant.
3388 * If any of the constants that would be defined already exists, none of the constants will be defined.
3390 * @param Array $array
3393 function _define_array()
3395 $args = func_get_args();
3396 foreach ($args as $arg) {
3397 foreach ($arg as $key=>$value) {
3398 if (!defined($value)) {
3399 define($value, $key);
3408 * Returns a log of the packets that have been sent and received.
3410 * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
3413 * @return String or Array
3417 if (!defined('NET_SSH2_LOGGING')) {
3421 switch (NET_SSH2_LOGGING) {
3422 case NET_SSH2_LOG_SIMPLE:
3423 return $this->message_number_log;
3425 case NET_SSH2_LOG_COMPLEX:
3426 return $this->_format_log($this->message_log, $this->message_number_log);
3434 * Formats a log for printing
3436 * @param Array $message_log
3437 * @param Array $message_number_log
3441 function _format_log($message_log, $message_number_log)
3444 for ($i = 0; $i < count($message_log); $i++) {
3445 $output.= $message_number_log[$i] . "\r\n";
3446 $current_log = $message_log[$i];
3449 if (strlen($current_log)) {
3450 $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
3452 $fragment = $this->_string_shift($current_log, $this->log_short_width);
3453 $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
3454 // replace non ASCII printable characters with dots
3455 // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
3456 // also replace < with a . since < messes up the output on web browsers
3457 $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
3458 $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
3460 } while (strlen($current_log));
3468 * Helper function for _format_log
3470 * For use with preg_replace_callback()
3472 * @param Array $matches
3476 function _format_log_helper($matches)
3478 return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
3482 * Returns all errors
3487 function getErrors()
3489 return $this->errors;
3493 * Returns the last error
3498 function getLastError()
3500 return $this->errors[count($this->errors) - 1];
3504 * Return the server identification.
3509 function getServerIdentification()
3513 return $this->server_identifier;
3517 * Return a list of the key exchange algorithms the server supports.
3522 function getKexAlgorithms()
3526 return $this->kex_algorithms;
3530 * Return a list of the host key (public key) algorithms the server supports.
3535 function getServerHostKeyAlgorithms()
3539 return $this->server_host_key_algorithms;
3543 * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
3548 function getEncryptionAlgorithmsClient2Server()
3552 return $this->encryption_algorithms_client_to_server;
3556 * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
3561 function getEncryptionAlgorithmsServer2Client()
3565 return $this->encryption_algorithms_server_to_client;
3569 * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
3574 function getMACAlgorithmsClient2Server()
3578 return $this->mac_algorithms_client_to_server;
3582 * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
3587 function getMACAlgorithmsServer2Client()
3591 return $this->mac_algorithms_server_to_client;
3595 * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
3600 function getCompressionAlgorithmsClient2Server()
3604 return $this->compression_algorithms_client_to_server;
3608 * Return a list of the compression algorithms the server supports, when sending stuff to the client.
3613 function getCompressionAlgorithmsServer2Client()
3617 return $this->compression_algorithms_server_to_client;
3621 * Return a list of the languages the server supports, when sending stuff to the client.
3626 function getLanguagesServer2Client()
3630 return $this->languages_server_to_client;
3634 * Return a list of the languages the server supports, when receiving stuff from the client.
3639 function getLanguagesClient2Server()
3643 return $this->languages_client_to_server;
3647 * Returns the banner message.
3649 * Quoting from the RFC, "in some jurisdictions, sending a warning message before
3650 * authentication may be relevant for getting legal protection."
3655 function getBannerMessage()
3657 return $this->banner_message;
3661 * Returns the server public host key.
3663 * Caching this the first time you connect to a server and checking the result on subsequent connections
3664 * is recommended. Returns false if the server signature is not signed correctly with the public host key.
3669 function getServerPublicHostKey()
3671 if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
3672 if (!$this->_connect()) {
3677 $signature = $this->signature;
3678 $server_public_host_key = $this->server_public_host_key;
3680 extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
3681 $this->_string_shift($server_public_host_key, $length);
3683 if ($this->signature_validated) {
3684 return $this->bitmap ?
3685 $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
3689 $this->signature_validated = true;
3691 switch ($this->signature_format) {
3693 $zero = new Math_BigInteger();
3695 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3696 $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3698 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3699 $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3701 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3702 $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3704 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3705 $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3707 /* The value for 'dss_signature_blob' is encoded as a string containing
3708 r, followed by s (which are 160-bit integers, without lengths or
3709 padding, unsigned, and in network byte order). */
3710 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
3711 if ($temp['length'] != 40) {
3712 user_error('Invalid signature');
3713 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3716 $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
3717 $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
3720 case $r->equals($zero):
3721 case $r->compare($q) >= 0:
3722 case $s->equals($zero):
3723 case $s->compare($q) >= 0:
3724 user_error('Invalid signature');
3725 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3728 $w = $s->modInverse($q);
3730 $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
3731 list(, $u1) = $u1->divide($q);
3733 $u2 = $w->multiply($r);
3734 list(, $u2) = $u2->divide($q);
3736 $g = $g->modPow($u1, $p);
3737 $y = $y->modPow($u2, $p);
3739 $v = $g->multiply($y);
3740 list(, $v) = $v->divide($p);
3741 list(, $v) = $v->divide($q);
3743 if (!$v->equals($r)) {
3744 user_error('Bad server signature');
3745 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
3750 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3751 $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3753 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3754 $rawN = $this->_string_shift($server_public_host_key, $temp['length']);
3755 $n = new Math_BigInteger($rawN, -256);
3756 $nLength = strlen(ltrim($rawN, "\0"));
3759 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
3760 $signature = $this->_string_shift($signature, $temp['length']);
3762 if (!class_exists('Crypt_RSA')) {
3763 include_once 'Crypt/RSA.php';
3766 $rsa = new Crypt_RSA();
3767 $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
3768 $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
3769 if (!$rsa->verify($this->exchange_hash, $signature)) {
3770 user_error('Bad server signature');
3771 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
3775 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
3776 $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
3778 // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
3780 // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
3782 // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
3784 if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
3785 user_error('Invalid signature');
3786 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3789 $s = $s->modPow($e, $n);
3792 $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
3793 $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;
3796 user_error('Bad server signature');
3797 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
3801 user_error('Unsupported signature format');
3802 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
3805 return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
3809 * Returns the exit status of an SSH command or false.
3811 * @return Integer or false
3814 function getExitStatus()
3816 if (is_null($this->exit_status)) {
3819 return $this->exit_status;
3823 * Returns the number of columns for the terminal window size.
3828 function getWindowColumns()
3830 return $this->windowColumns;
3834 * Returns the number of rows for the terminal window size.
3839 function getWindowRows()
3841 return $this->windowRows;
3845 * Sets the number of columns for the terminal window size.
3847 * @param Integer $value
3850 function setWindowColumns($value)
3852 $this->windowColumns = $value;
3856 * Sets the number of rows for the terminal window size.
3858 * @param Integer $value
3861 function setWindowRows($value)
3863 $this->windowRows = $value;
3867 * Sets the number of columns and rows for the terminal window size.
3869 * @param Integer $columns
3870 * @param Integer $rows
3873 function setWindowSize($columns = 80, $rows = 24)
3875 $this->windowColumns = $columns;
3876 $this->windowRows = $rows;