2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
\r
5 * Pure-PHP implementation of SSHv2.
\r
7 * PHP versions 4 and 5
\r
9 * Here are some examples of how to use this library:
\r
12 * include('Net/SSH2.php');
\r
14 * $ssh = new Net_SSH2('www.domain.tld');
\r
15 * if (!$ssh->login('username', 'password')) {
\r
16 * exit('Login Failed');
\r
19 * echo $ssh->exec('pwd');
\r
20 * echo $ssh->exec('ls -la');
\r
26 * include('Crypt/RSA.php');
\r
27 * include('Net/SSH2.php');
\r
29 * $key = new Crypt_RSA();
\r
30 * //$key->setPassword('whatever');
\r
31 * $key->loadKey(file_get_contents('privatekey'));
\r
33 * $ssh = new Net_SSH2('www.domain.tld');
\r
34 * if (!$ssh->login('username', $key)) {
\r
35 * exit('Login Failed');
\r
38 * echo $ssh->exec('pwd');
\r
39 * echo $ssh->exec('ls -la');
\r
43 * LICENSE: This library is free software; you can redistribute it and/or
\r
44 * modify it under the terms of the GNU Lesser General Public
\r
45 * License as published by the Free Software Foundation; either
\r
46 * version 2.1 of the License, or (at your option) any later version.
\r
48 * This library is distributed in the hope that it will be useful,
\r
49 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
50 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
51 * Lesser General Public License for more details.
\r
53 * You should have received a copy of the GNU Lesser General Public
\r
54 * License along with this library; if not, write to the Free Software
\r
55 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
\r
60 * @author Jim Wigginton <terrafrost@php.net>
\r
61 * @copyright MMVII Jim Wigginton
\r
62 * @license http://www.gnu.org/licenses/lgpl.txt
\r
63 * @version $Id: SSH2.php,v 1.46 2010/04/27 21:29:36 terrafrost Exp $
\r
64 * @link http://phpseclib.sourceforge.net
\r
68 * Include Math_BigInteger
\r
70 * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
\r
72 require_once('Math/BigInteger.php');
\r
75 * Include Crypt_Random
\r
77 require_once('Crypt/Random.php');
\r
80 * Include Crypt_Hash
\r
82 require_once('Crypt/Hash.php');
\r
85 * Include Crypt_TripleDES
\r
87 require_once('Crypt/TripleDES.php');
\r
92 require_once('Crypt/RC4.php');
\r
97 require_once('Crypt/AES.php');
\r
100 * Execution Bitmap Masks
\r
102 * @see Net_SSH2::bitmap
\r
105 define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
\r
106 define('NET_SSH2_MASK_LOGIN', 0x00000002);
\r
110 * Channel constants
\r
112 * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
\r
113 * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
\r
114 * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
\r
115 * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
\r
116 * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
\r
117 * The 'recipient channel' is the channel number given in the original
\r
118 * open request, and 'sender channel' is the channel number allocated by
\r
121 * @see Net_SSH2::_send_channel_packet()
\r
122 * @see Net_SSH2::_get_channel_packet()
\r
125 define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
\r
130 * @see Net_SSH2::getLog()
\r
133 * Returns the message numbers
\r
135 define('NET_SSH2_LOG_SIMPLE', 1);
\r
137 * Returns the message content
\r
139 define('NET_SSH2_LOG_COMPLEX', 2);
\r
143 * Pure-PHP implementation of SSHv2.
\r
145 * @author Jim Wigginton <terrafrost@php.net>
\r
148 * @package Net_SSH2
\r
152 * The SSH identifier
\r
157 var $identifier = 'SSH-2.0-phpseclib_0.2';
\r
160 * The Socket Object
\r
170 * The bits that are set reprsent functions that have been called already. This is used to determine
\r
171 * if a requisite function has been successfully executed. If not, an error should be thrown.
\r
179 * Error information
\r
181 * @see Net_SSH2::getErrors()
\r
182 * @see Net_SSH2::getLastError()
\r
186 var $errors = array();
\r
189 * Server Identifier
\r
191 * @see Net_SSH2::getServerIdentification()
\r
195 var $server_identifier = '';
\r
198 * Key Exchange Algorithms
\r
200 * @see Net_SSH2::getKexAlgorithims()
\r
204 var $kex_algorithms;
\r
207 * Server Host Key Algorithms
\r
209 * @see Net_SSH2::getServerHostKeyAlgorithms()
\r
213 var $server_host_key_algorithms;
\r
216 * Encryption Algorithms: Client to Server
\r
218 * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
\r
222 var $encryption_algorithms_client_to_server;
\r
225 * Encryption Algorithms: Server to Client
\r
227 * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
\r
231 var $encryption_algorithms_server_to_client;
\r
234 * MAC Algorithms: Client to Server
\r
236 * @see Net_SSH2::getMACAlgorithmsClient2Server()
\r
240 var $mac_algorithms_client_to_server;
\r
243 * MAC Algorithms: Server to Client
\r
245 * @see Net_SSH2::getMACAlgorithmsServer2Client()
\r
249 var $mac_algorithms_server_to_client;
\r
252 * Compression Algorithms: Client to Server
\r
254 * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
\r
258 var $compression_algorithms_client_to_server;
\r
261 * Compression Algorithms: Server to Client
\r
263 * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
\r
267 var $compression_algorithms_server_to_client;
\r
270 * Languages: Server to Client
\r
272 * @see Net_SSH2::getLanguagesServer2Client()
\r
276 var $languages_server_to_client;
\r
279 * Languages: Client to Server
\r
281 * @see Net_SSH2::getLanguagesClient2Server()
\r
285 var $languages_client_to_server;
\r
288 * Block Size for Server to Client Encryption
\r
290 * "Note that the length of the concatenation of 'packet_length',
\r
291 * 'padding_length', 'payload', and 'random padding' MUST be a multiple
\r
292 * of the cipher block size or 8, whichever is larger. This constraint
\r
293 * MUST be enforced, even when using stream ciphers."
\r
295 * -- http://tools.ietf.org/html/rfc4253#section-6
\r
297 * @see Net_SSH2::Net_SSH2()
\r
298 * @see Net_SSH2::_send_binary_packet()
\r
302 var $encrypt_block_size = 8;
\r
305 * Block Size for Client to Server Encryption
\r
307 * @see Net_SSH2::Net_SSH2()
\r
308 * @see Net_SSH2::_get_binary_packet()
\r
312 var $decrypt_block_size = 8;
\r
315 * Server to Client Encryption Object
\r
317 * @see Net_SSH2::_get_binary_packet()
\r
321 var $decrypt = false;
\r
324 * Client to Server Encryption Object
\r
326 * @see Net_SSH2::_send_binary_packet()
\r
330 var $encrypt = false;
\r
333 * Client to Server HMAC Object
\r
335 * @see Net_SSH2::_send_binary_packet()
\r
339 var $hmac_create = false;
\r
342 * Server to Client HMAC Object
\r
344 * @see Net_SSH2::_get_binary_packet()
\r
348 var $hmac_check = false;
\r
351 * Size of server to client HMAC
\r
353 * 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.
\r
354 * 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
\r
357 * @see Net_SSH2::_get_binary_packet()
\r
361 var $hmac_size = false;
\r
364 * Server Public Host Key
\r
366 * @see Net_SSH2::getServerPublicHostKey()
\r
370 var $server_public_host_key;
\r
373 * Session identifer
\r
375 * "The exchange hash H from the first key exchange is additionally
\r
376 * used as the session identifier, which is a unique identifier for
\r
377 * this connection."
\r
379 * -- http://tools.ietf.org/html/rfc4253#section-7.2
\r
381 * @see Net_SSH2::_key_exchange()
\r
385 var $session_id = false;
\r
390 * @see Net_SSH2::Net_SSH2()
\r
394 var $message_numbers = array();
\r
397 * Disconnection Message 'reason codes' defined in RFC4253
\r
399 * @see Net_SSH2::Net_SSH2()
\r
403 var $disconnect_reasons = array();
\r
406 * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
\r
408 * @see Net_SSH2::Net_SSH2()
\r
412 var $channel_open_failure_reasons = array();
\r
417 * @link http://tools.ietf.org/html/rfc4254#section-8
\r
418 * @see Net_SSH2::Net_SSH2()
\r
422 var $terminal_modes = array();
\r
425 * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
\r
427 * @link http://tools.ietf.org/html/rfc4254#section-5.2
\r
428 * @see Net_SSH2::Net_SSH2()
\r
432 var $channel_extended_data_type_codes = array();
\r
435 * Send Sequence Number
\r
437 * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
\r
439 * @see Net_SSH2::_send_binary_packet()
\r
443 var $send_seq_no = 0;
\r
446 * Get Sequence Number
\r
448 * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
\r
450 * @see Net_SSH2::_get_binary_packet()
\r
454 var $get_seq_no = 0;
\r
459 * Maps client channels to server channels
\r
461 * @see Net_SSH2::_get_channel_packet()
\r
462 * @see Net_SSH2::exec()
\r
466 var $server_channels = array();
\r
471 * If a client requests a packet from one channel but receives two packets from another those packets should
\r
472 * be placed in a buffer
\r
474 * @see Net_SSH2::_get_channel_packet()
\r
475 * @see Net_SSH2::exec()
\r
479 var $channel_buffers = array();
\r
484 * Contains the type of the last sent message
\r
486 * @see Net_SSH2::_get_channel_packet()
\r
490 var $channel_status = array();
\r
495 * Maximum packet size indexed by channel
\r
497 * @see Net_SSH2::_send_channel_packet()
\r
501 var $packet_size_client_to_server = array();
\r
504 * Message Number Log
\r
506 * @see Net_SSH2::getLog()
\r
510 var $message_number_log = array();
\r
515 * @see Net_SSH2::getLog()
\r
519 var $message_log = array();
\r
524 * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB)
\r
527 * @see Net_SSH2::_send_channel_packet()
\r
528 * @see Net_SSH2::exec()
\r
531 var $window_size = 0x7FFFFFFF;
\r
536 * Window size indexed by channel
\r
538 * @see Net_SSH2::_send_channel_packet()
\r
542 var $window_size_client_to_server = array();
\r
547 * Verified against $this->session_id
\r
549 * @see Net_SSH2::getServerPublicHostKey()
\r
553 var $signature = '';
\r
556 * Server signature format
\r
558 * ssh-rsa or ssh-dss.
\r
560 * @see Net_SSH2::getServerPublicHostKey()
\r
564 var $signature_format = '';
\r
567 * Default Constructor.
\r
569 * Connects to an SSHv2 server
\r
571 * @param String $host
\r
572 * @param optional Integer $port
\r
573 * @param optional Integer $timeout
\r
577 function Net_SSH2($host, $port = 22, $timeout = 10)
\r
579 $this->message_numbers = array(
\r
580 1 => 'NET_SSH2_MSG_DISCONNECT',
\r
581 2 => 'NET_SSH2_MSG_IGNORE',
\r
582 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
\r
583 4 => 'NET_SSH2_MSG_DEBUG',
\r
584 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
\r
585 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
\r
586 20 => 'NET_SSH2_MSG_KEXINIT',
\r
587 21 => 'NET_SSH2_MSG_NEWKEYS',
\r
588 30 => 'NET_SSH2_MSG_KEXDH_INIT',
\r
589 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
\r
590 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
\r
591 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
\r
592 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
\r
593 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
\r
595 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
\r
596 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
\r
597 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
\r
598 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
\r
599 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
\r
600 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
\r
601 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
\r
602 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
\r
603 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
\r
604 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
\r
605 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
\r
606 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
\r
607 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
\r
608 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
\r
610 $this->disconnect_reasons = array(
\r
611 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
\r
612 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
\r
613 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
\r
614 4 => 'NET_SSH2_DISCONNECT_RESERVED',
\r
615 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
\r
616 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
\r
617 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
\r
618 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
\r
619 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
\r
620 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
\r
621 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
\r
622 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
\r
623 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
\r
624 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
\r
625 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
\r
627 $this->channel_open_failure_reasons = array(
\r
628 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
\r
630 $this->terminal_modes = array(
\r
631 0 => 'NET_SSH2_TTY_OP_END'
\r
633 $this->channel_extended_data_type_codes = array(
\r
634 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
\r
637 $this->_define_array(
\r
638 $this->message_numbers,
\r
639 $this->disconnect_reasons,
\r
640 $this->channel_open_failure_reasons,
\r
641 $this->terminal_modes,
\r
642 $this->channel_extended_data_type_codes,
\r
643 array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
\r
644 array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK')
\r
647 $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
\r
648 if (!$this->fsock) {
\r
649 user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
\r
653 /* According to the SSH2 specs,
\r
655 "The server MAY send other lines of data before sending the version
\r
656 string. Each line SHOULD be terminated by a Carriage Return and Line
\r
657 Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
\r
658 in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
\r
659 MUST be able to process such lines." */
\r
662 while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
\r
663 if (substr($temp, -2) == "\r\n") {
\r
667 $temp.= fgets($this->fsock, 255);
\r
671 if (extension_loaded('mcrypt')) {
\r
674 if (extension_loaded('gmp')) {
\r
676 } else if (extension_loaded('bcmath')) {
\r
680 if (!empty($ext)) {
\r
681 $this->identifier.= ' (' . implode(', ', $ext) . ')';
\r
684 if (defined('NET_SSH2_LOGGING')) {
\r
685 $this->message_number_log[] = '<-';
\r
686 $this->message_number_log[] = '->';
\r
688 if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
\r
689 $this->message_log[] = $temp;
\r
690 $this->message_log[] = $this->identifier . "\r\n";
\r
694 $this->server_identifier = trim($temp, "\r\n");
\r
695 if (!empty($extra)) {
\r
696 $this->errors[] = utf8_decode($extra);
\r
699 if ($matches[1] != '1.99' && $matches[1] != '2.0') {
\r
700 user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
\r
704 fputs($this->fsock, $this->identifier . "\r\n");
\r
706 $response = $this->_get_binary_packet();
\r
707 if ($response === false) {
\r
708 user_error('Connection closed by server', E_USER_NOTICE);
\r
712 if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
\r
713 user_error('Expected SSH_MSG_KEXINIT', E_USER_NOTICE);
\r
717 if (!$this->_key_exchange($response)) {
\r
721 $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
\r
727 * @param String $kexinit_payload_server
\r
730 function _key_exchange($kexinit_payload_server)
\r
732 static $kex_algorithms = array(
\r
733 'diffie-hellman-group1-sha1', // REQUIRED
\r
734 'diffie-hellman-group14-sha1' // REQUIRED
\r
737 static $server_host_key_algorithms = array(
\r
738 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
\r
739 'ssh-dss' // REQUIRED sign Raw DSS Key
\r
742 static $encryption_algorithms = array(
\r
743 // from <http://tools.ietf.org/html/rfc4345#section-4>:
\r
747 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
\r
749 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
\r
750 'aes192-cbc', // OPTIONAL AES with a 192-bit key
\r
751 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
\r
753 // from <http://tools.ietf.org/html/rfc4344#section-4>:
\r
754 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
\r
755 'aes192-ctr', // RECOMMENDED AES with 192-bit key
\r
756 'aes256-ctr', // RECOMMENDED AES with 256-bit key
\r
757 '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
\r
759 '3des-cbc', // REQUIRED three-key 3DES in CBC mode
\r
760 'none' // OPTIONAL no encryption; NOT RECOMMENDED
\r
763 static $mac_algorithms = array(
\r
764 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
\r
765 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
\r
766 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
\r
767 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
\r
768 'none' // OPTIONAL no MAC; NOT RECOMMENDED
\r
771 static $compression_algorithms = array(
\r
772 'none' // REQUIRED no compression
\r
773 //'zlib' // OPTIONAL ZLIB (LZ77) compression
\r
776 static $str_kex_algorithms, $str_server_host_key_algorithms,
\r
777 $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
\r
778 $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
\r
780 if (empty($str_kex_algorithms)) {
\r
781 $str_kex_algorithms = implode(',', $kex_algorithms);
\r
782 $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
\r
783 $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
\r
784 $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
\r
785 $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
\r
788 $client_cookie = '';
\r
789 for ($i = 0; $i < 16; $i++) {
\r
790 $client_cookie.= chr(crypt_random(0, 255));
\r
793 $response = $kexinit_payload_server;
\r
794 $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
\r
795 $server_cookie = $this->_string_shift($response, 16);
\r
797 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
798 $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
\r
800 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
801 $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
\r
803 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
804 $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
\r
806 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
807 $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
\r
809 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
810 $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
\r
812 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
813 $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
\r
815 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
816 $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
\r
818 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
819 $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
\r
821 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
822 $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
\r
824 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
825 $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
\r
827 extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
\r
828 $first_kex_packet_follows = $first_kex_packet_follows != 0;
\r
830 // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
\r
831 $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
\r
832 NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
\r
833 strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
\r
834 $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
\r
835 strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
\r
836 $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
\r
837 strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
\r
841 if (!$this->_send_binary_packet($kexinit_payload_client)) {
\r
844 // here ends the second place.
\r
846 // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
\r
847 for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
\r
848 if ($i == count($encryption_algorithms)) {
\r
849 user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
\r
850 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
853 // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
\r
854 // diffie-hellman key exchange as fast as possible
\r
855 $decrypt = $encryption_algorithms[$i];
\r
856 switch ($decrypt) {
\r
859 $decryptKeyLength = 24; // eg. 192 / 8
\r
863 $decryptKeyLength = 32; // eg. 256 / 8
\r
867 $decryptKeyLength = 24; // eg. 192 / 8
\r
871 $decryptKeyLength = 16; // eg. 128 / 8
\r
875 $decryptKeyLength = 16; // eg. 128 / 8
\r
878 $decryptKeyLength = 32; // eg. 128 / 8
\r
881 $decryptKeyLength = 0;
\r
884 for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
\r
885 if ($i == count($encryption_algorithms)) {
\r
886 user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
\r
887 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
890 $encrypt = $encryption_algorithms[$i];
\r
891 switch ($encrypt) {
\r
894 $encryptKeyLength = 24;
\r
898 $encryptKeyLength = 32;
\r
902 $encryptKeyLength = 24;
\r
906 $encryptKeyLength = 16;
\r
910 $encryptKeyLength = 16;
\r
913 $encryptKeyLength = 32;
\r
916 $encryptKeyLength = 0;
\r
919 $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
\r
921 // through diffie-hellman key exchange a symmetric key is obtained
\r
922 for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
\r
923 if ($i == count($kex_algorithms)) {
\r
924 user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
\r
925 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
928 switch ($kex_algorithms[$i]) {
\r
929 // see http://tools.ietf.org/html/rfc2409#section-6.2 and
\r
930 // http://tools.ietf.org/html/rfc2412, appendex E
\r
931 case 'diffie-hellman-group1-sha1':
\r
932 $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
\r
933 '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
\r
934 '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
\r
935 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
\r
936 $keyLength = $keyLength < 160 ? $keyLength : 160;
\r
939 // see http://tools.ietf.org/html/rfc3526#section-3
\r
940 case 'diffie-hellman-group14-sha1':
\r
941 $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
\r
942 '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
\r
943 '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
\r
944 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
\r
945 '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
\r
946 '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
\r
947 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
\r
948 '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
\r
949 $keyLength = $keyLength < 160 ? $keyLength : 160;
\r
953 $p = new Math_BigInteger($p, 256);
\r
954 //$q = $p->bitwise_rightShift(1);
\r
956 /* To increase the speed of the key exchange, both client and server may
\r
957 reduce the size of their private exponents. It should be at least
\r
958 twice as long as the key material that is generated from the shared
\r
959 secret. For more details, see the paper by van Oorschot and Wiener
\r
962 -- http://tools.ietf.org/html/rfc4419#section-6.2 */
\r
963 $q = new Math_BigInteger(1);
\r
964 $q = $q->bitwise_leftShift(2 * $keyLength);
\r
965 $q = $q->subtract(new Math_BigInteger(1));
\r
967 $g = new Math_BigInteger(2);
\r
968 $x = new Math_BigInteger();
\r
969 $x->setRandomGenerator('crypt_random');
\r
970 $x = $x->random(new Math_BigInteger(1), $q);
\r
971 $e = $g->modPow($x, $p);
\r
973 $eBytes = $e->toBytes(true);
\r
974 $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
\r
976 if (!$this->_send_binary_packet($data)) {
\r
977 user_error('Connection closed by server', E_USER_NOTICE);
\r
981 $response = $this->_get_binary_packet();
\r
982 if ($response === false) {
\r
983 user_error('Connection closed by server', E_USER_NOTICE);
\r
986 extract(unpack('Ctype', $this->_string_shift($response, 1)));
\r
988 if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
\r
989 user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
\r
993 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
994 $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
\r
996 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
997 $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
\r
999 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
1000 $fBytes = $this->_string_shift($response, $temp['length']);
\r
1001 $f = new Math_BigInteger($fBytes, -256);
\r
1003 $temp = unpack('Nlength', $this->_string_shift($response, 4));
\r
1004 $this->signature = $this->_string_shift($response, $temp['length']);
\r
1006 $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
\r
1007 $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
\r
1009 $key = $f->modPow($x, $p);
\r
1010 $keyBytes = $key->toBytes(true);
\r
1012 if ($this->session_id === false) {
\r
1013 $source = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
\r
1014 strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
\r
1015 strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
\r
1016 $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
\r
1017 $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
\r
1020 $source = pack('H*', $hash($source));
\r
1022 $this->session_id = $source;
\r
1025 for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
\r
1026 if ($i == count($server_host_key_algorithms)) {
\r
1027 user_error('No compatible server host key algorithms found', E_USER_NOTICE);
\r
1028 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
1031 if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
\r
1032 user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
\r
1033 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
1036 $packet = pack('C',
\r
1037 NET_SSH2_MSG_NEWKEYS
\r
1040 if (!$this->_send_binary_packet($packet)) {
\r
1044 $response = $this->_get_binary_packet();
\r
1046 if ($response === false) {
\r
1047 user_error('Connection closed by server', E_USER_NOTICE);
\r
1051 extract(unpack('Ctype', $this->_string_shift($response, 1)));
\r
1053 if ($type != NET_SSH2_MSG_NEWKEYS) {
\r
1054 user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
\r
1058 switch ($encrypt) {
\r
1060 $this->encrypt = new Crypt_TripleDES();
\r
1061 // $this->encrypt_block_size = 64 / 8 == the default
\r
1064 $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
\r
1065 // $this->encrypt_block_size = 64 / 8 == the default
\r
1067 case 'aes256-cbc':
\r
1068 case 'aes192-cbc':
\r
1069 case 'aes128-cbc':
\r
1070 $this->encrypt = new Crypt_AES();
\r
1071 $this->encrypt_block_size = 16; // eg. 128 / 8
\r
1073 case 'aes256-ctr':
\r
1074 case 'aes192-ctr':
\r
1075 case 'aes128-ctr':
\r
1076 $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
\r
1077 $this->encrypt_block_size = 16; // eg. 128 / 8
\r
1080 case 'arcfour128':
\r
1081 case 'arcfour256':
\r
1082 $this->encrypt = new Crypt_RC4();
\r
1085 //$this->encrypt = new Crypt_Null();
\r
1088 switch ($decrypt) {
\r
1090 $this->decrypt = new Crypt_TripleDES();
\r
1093 $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
\r
1095 case 'aes256-cbc':
\r
1096 case 'aes192-cbc':
\r
1097 case 'aes128-cbc':
\r
1098 $this->decrypt = new Crypt_AES();
\r
1099 $this->decrypt_block_size = 16;
\r
1101 case 'aes256-ctr':
\r
1102 case 'aes192-ctr':
\r
1103 case 'aes128-ctr':
\r
1104 $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
\r
1105 $this->decrypt_block_size = 16;
\r
1108 case 'arcfour128':
\r
1109 case 'arcfour256':
\r
1110 $this->decrypt = new Crypt_RC4();
\r
1113 //$this->decrypt = new Crypt_Null();
\r
1116 $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
\r
1118 if ($this->encrypt) {
\r
1119 $this->encrypt->enableContinuousBuffer();
\r
1120 $this->encrypt->disablePadding();
\r
1122 $iv = pack('H*', $hash($keyBytes . $this->session_id . 'A' . $this->session_id));
\r
1123 while ($this->encrypt_block_size > strlen($iv)) {
\r
1124 $iv.= pack('H*', $hash($keyBytes . $this->session_id . $iv));
\r
1126 $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
\r
1128 $key = pack('H*', $hash($keyBytes . $this->session_id . 'C' . $this->session_id));
\r
1129 while ($encryptKeyLength > strlen($key)) {
\r
1130 $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
\r
1132 $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
\r
1135 if ($this->decrypt) {
\r
1136 $this->decrypt->enableContinuousBuffer();
\r
1137 $this->decrypt->disablePadding();
\r
1139 $iv = pack('H*', $hash($keyBytes . $this->session_id . 'B' . $this->session_id));
\r
1140 while ($this->decrypt_block_size > strlen($iv)) {
\r
1141 $iv.= pack('H*', $hash($keyBytes . $this->session_id . $iv));
\r
1143 $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
\r
1145 $key = pack('H*', $hash($keyBytes . $this->session_id . 'D' . $this->session_id));
\r
1146 while ($decryptKeyLength > strlen($key)) {
\r
1147 $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
\r
1149 $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
\r
1152 /* The "arcfour128" algorithm is the RC4 cipher, as described in
\r
1153 [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
\r
1154 generated by the cipher MUST be discarded, and the first byte of the
\r
1155 first encrypted packet MUST be encrypted using the 1537th byte of
\r
1158 -- http://tools.ietf.org/html/rfc4345#section-4 */
\r
1159 if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
\r
1160 $this->encrypt->encrypt(str_repeat("\0", 1536));
\r
1162 if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
\r
1163 $this->decrypt->decrypt(str_repeat("\0", 1536));
\r
1166 for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
\r
1167 if ($i == count($mac_algorithms)) {
\r
1168 user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
\r
1169 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
1172 $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
\r
1173 switch ($mac_algorithms[$i]) {
\r
1175 $this->hmac_create = new Crypt_Hash('sha1');
\r
1176 $createKeyLength = 20;
\r
1178 case 'hmac-sha1-96':
\r
1179 $this->hmac_create = new Crypt_Hash('sha1-96');
\r
1180 $createKeyLength = 20;
\r
1183 $this->hmac_create = new Crypt_Hash('md5');
\r
1184 $createKeyLength = 16;
\r
1186 case 'hmac-md5-96':
\r
1187 $this->hmac_create = new Crypt_Hash('md5-96');
\r
1188 $createKeyLength = 16;
\r
1191 for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
\r
1192 if ($i == count($mac_algorithms)) {
\r
1193 user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
\r
1194 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
1197 $checkKeyLength = 0;
\r
1198 $this->hmac_size = 0;
\r
1199 switch ($mac_algorithms[$i]) {
\r
1201 $this->hmac_check = new Crypt_Hash('sha1');
\r
1202 $checkKeyLength = 20;
\r
1203 $this->hmac_size = 20;
\r
1205 case 'hmac-sha1-96':
\r
1206 $this->hmac_check = new Crypt_Hash('sha1-96');
\r
1207 $checkKeyLength = 20;
\r
1208 $this->hmac_size = 12;
\r
1211 $this->hmac_check = new Crypt_Hash('md5');
\r
1212 $checkKeyLength = 16;
\r
1213 $this->hmac_size = 16;
\r
1215 case 'hmac-md5-96':
\r
1216 $this->hmac_check = new Crypt_Hash('md5-96');
\r
1217 $checkKeyLength = 16;
\r
1218 $this->hmac_size = 12;
\r
1221 $key = pack('H*', $hash($keyBytes . $this->session_id . 'E' . $this->session_id));
\r
1222 while ($createKeyLength > strlen($key)) {
\r
1223 $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
\r
1225 $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
\r
1227 $key = pack('H*', $hash($keyBytes . $this->session_id . 'F' . $this->session_id));
\r
1228 while ($checkKeyLength > strlen($key)) {
\r
1229 $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
\r
1231 $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
\r
1233 for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
\r
1234 if ($i == count($compression_algorithms)) {
\r
1235 user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
\r
1236 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
1238 $this->decompress = $compression_algorithms[$i] == 'zlib';
\r
1240 for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
\r
1241 if ($i == count($compression_algorithms)) {
\r
1242 user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
\r
1243 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
1245 $this->compress = $compression_algorithms[$i] == 'zlib';
\r
1253 * @param String $username
\r
1254 * @param optional String $password
\r
1257 * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
\r
1258 * by sending dummy SSH_MSG_IGNORE messages.
\r
1260 function login($username, $password = '')
\r
1262 if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
\r
1266 $packet = pack('CNa*',
\r
1267 NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
\r
1270 if (!$this->_send_binary_packet($packet)) {
\r
1274 $response = $this->_get_binary_packet();
\r
1275 if ($response === false) {
\r
1276 user_error('Connection closed by server', E_USER_NOTICE);
\r
1280 extract(unpack('Ctype', $this->_string_shift($response, 1)));
\r
1282 if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
\r
1283 user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE);
\r
1287 // although PHP5's get_class() preserves the case, PHP4's does not
\r
1288 if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
\r
1289 return $this->_privatekey_login($username, $password);
\r
1292 $utf8_password = utf8_encode($password);
\r
1293 $packet = pack('CNa*Na*Na*CNa*',
\r
1294 NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
\r
1295 strlen('password'), 'password', 0, strlen($utf8_password), $utf8_password
\r
1298 if (!$this->_send_binary_packet($packet)) {
\r
1302 // remove the username and password from the last logged packet
\r
1303 if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
\r
1304 $packet = pack('CNa*Na*Na*CNa*',
\r
1305 NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
\r
1306 strlen('password'), 'password', 0, strlen('password'), 'password'
\r
1308 $this->message_log[count($this->message_log) - 1] = $packet;
\r
1311 $response = $this->_get_binary_packet();
\r
1312 if ($response === false) {
\r
1313 user_error('Connection closed by server', E_USER_NOTICE);
\r
1317 extract(unpack('Ctype', $this->_string_shift($response, 1)));
\r
1320 case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
\r
1321 if (defined('NET_SSH2_LOGGING')) {
\r
1322 $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
\r
1324 extract(unpack('Nlength', $this->_string_shift($response, 4)));
\r
1325 $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
\r
1326 return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
\r
1327 case NET_SSH2_MSG_USERAUTH_FAILURE:
\r
1328 // either the login is bad or the server employees multi-factor authentication
\r
1330 case NET_SSH2_MSG_USERAUTH_SUCCESS:
\r
1331 $this->bitmap |= NET_SSH2_MASK_LOGIN;
\r
1339 * Login with an RSA private key
\r
1341 * @param String $username
\r
1342 * @param Crypt_RSA $password
\r
1345 * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
\r
1346 * by sending dummy SSH_MSG_IGNORE messages.
\r
1348 function _privatekey_login($username, $privatekey)
\r
1350 // see http://tools.ietf.org/html/rfc4253#page-15
\r
1351 $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
\r
1352 if ($publickey === false) {
\r
1356 $publickey = array(
\r
1357 'e' => $publickey['e']->toBytes(true),
\r
1358 'n' => $publickey['n']->toBytes(true)
\r
1360 $publickey = pack('Na*Na*Na*',
\r
1361 strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
\r
1364 $part1 = pack('CNa*Na*Na*',
\r
1365 NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
\r
1366 strlen('publickey'), 'publickey'
\r
1368 $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
\r
1370 $packet = $part1 . chr(0) . $part2;
\r
1372 if (!$this->_send_binary_packet($packet)) {
\r
1376 $response = $this->_get_binary_packet();
\r
1377 if ($response === false) {
\r
1378 user_error('Connection closed by server', E_USER_NOTICE);
\r
1382 extract(unpack('Ctype', $this->_string_shift($response, 1)));
\r
1385 case NET_SSH2_MSG_USERAUTH_FAILURE:
\r
1386 extract(unpack('Nlength', $this->_string_shift($response, 4)));
\r
1387 $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
\r
1388 return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
\r
1389 case NET_SSH2_MSG_USERAUTH_PK_OK:
\r
1390 // we'll just take it on faith that the public key blob and the public key algorithm name are as
\r
1392 if (defined('NET_SSH2_LOGGING')) {
\r
1393 $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PK_OK';
\r
1397 $packet = $part1 . chr(1) . $part2;
\r
1398 $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
\r
1399 $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
\r
1400 $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
\r
1401 $packet.= pack('Na*', strlen($signature), $signature);
\r
1403 if (!$this->_send_binary_packet($packet)) {
\r
1407 $response = $this->_get_binary_packet();
\r
1408 if ($response === false) {
\r
1409 user_error('Connection closed by server', E_USER_NOTICE);
\r
1413 extract(unpack('Ctype', $this->_string_shift($response, 1)));
\r
1416 case NET_SSH2_MSG_USERAUTH_FAILURE:
\r
1417 // either the login is bad or the server employees multi-factor authentication
\r
1419 case NET_SSH2_MSG_USERAUTH_SUCCESS:
\r
1420 $this->bitmap |= NET_SSH2_MASK_LOGIN;
\r
1430 * @param String $command
\r
1434 function exec($command)
\r
1436 if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
\r
1440 // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
\r
1441 // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
\r
1442 // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
\r
1443 // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
\r
1444 $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF;
\r
1445 // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
\r
1446 // uses 0x4000, that's what will be used here, as well.
\r
1447 $packet_size = 0x4000;
\r
1449 $packet = pack('CNa*N3',
\r
1450 NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size);
\r
1452 if (!$this->_send_binary_packet($packet)) {
\r
1456 $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
\r
1458 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
\r
1459 if ($response === false) {
\r
1463 // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
\r
1464 // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
\r
1465 // with a pty-req SSH_MSG_cHANNEL_REQUEST, exec() will return immediately and the ping process will then
\r
1466 // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
\r
1467 // neither will your script.
\r
1469 // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
\r
1470 // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
\r
1471 // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
\r
1472 $packet = pack('CNNa*CNa*',
\r
1473 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
\r
1474 if (!$this->_send_binary_packet($packet)) {
\r
1478 $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
\r
1480 $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
\r
1481 if ($response === false) {
\r
1485 $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
\r
1489 $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
\r
1491 case $temp === true:
\r
1493 case $temp === false:
\r
1506 function disconnect()
\r
1508 $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
\r
1514 * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
\r
1519 function __destruct()
\r
1521 $this->disconnect();
\r
1525 * Gets Binary Packets
\r
1527 * See '6. Binary Packet Protocol' of rfc4253 for more info.
\r
1529 * @see Net_SSH2::_send_binary_packet()
\r
1533 function _get_binary_packet()
\r
1535 if (feof($this->fsock)) {
\r
1536 user_error('Connection closed prematurely', E_USER_NOTICE);
\r
1540 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
\r
1541 $raw = fread($this->fsock, $this->decrypt_block_size);
\r
1542 $stop = strtok(microtime(), ' ') + strtok('');
\r
1544 if ($this->decrypt !== false) {
\r
1545 $raw = $this->decrypt->decrypt($raw);
\r
1548 extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
\r
1550 $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
\r
1552 while ($remaining_length > 0) {
\r
1553 $temp = fread($this->fsock, $remaining_length);
\r
1555 $remaining_length-= strlen($temp);
\r
1557 if (!empty($buffer)) {
\r
1558 $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
\r
1559 $buffer = $temp = '';
\r
1562 $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
\r
1563 $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
\r
1565 if ($this->hmac_check !== false) {
\r
1566 $hmac = fread($this->fsock, $this->hmac_size);
\r
1567 if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
\r
1568 user_error('Invalid HMAC', E_USER_NOTICE);
\r
1573 //if ($this->decompress) {
\r
1574 // $payload = gzinflate(substr($payload, 2));
\r
1577 $this->get_seq_no++;
\r
1579 if (defined('NET_SSH2_LOGGING')) {
\r
1580 $temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN';
\r
1581 $this->message_number_log[] = '<- ' . $temp .
\r
1582 ' (' . round($stop - $start, 4) . 's)';
\r
1583 if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
\r
1584 $this->message_log[] = substr($payload, 1);
\r
1588 return $this->_filter($payload);
\r
1592 * Filter Binary Packets
\r
1594 * Because some binary packets need to be ignored...
\r
1596 * @see Net_SSH2::_get_binary_packet()
\r
1600 function _filter($payload)
\r
1602 switch (ord($payload[0])) {
\r
1603 case NET_SSH2_MSG_DISCONNECT:
\r
1604 $this->_string_shift($payload, 1);
\r
1605 extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
\r
1606 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
\r
1607 $this->bitmask = 0;
\r
1609 case NET_SSH2_MSG_IGNORE:
\r
1610 $payload = $this->_get_binary_packet();
\r
1612 case NET_SSH2_MSG_DEBUG:
\r
1613 $this->_string_shift($payload, 2);
\r
1614 extract(unpack('Nlength', $this->_string_shift($payload, 4)));
\r
1615 $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
\r
1616 $payload = $this->_get_binary_packet();
\r
1618 case NET_SSH2_MSG_UNIMPLEMENTED:
\r
1620 case NET_SSH2_MSG_KEXINIT:
\r
1621 if ($this->session_id !== false) {
\r
1622 if (!$this->_key_exchange($payload)) {
\r
1623 $this->bitmask = 0;
\r
1626 $payload = $this->_get_binary_packet();
\r
1630 // 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
\r
1631 if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
\r
1632 $this->_string_shift($payload, 1);
\r
1633 extract(unpack('Nlength', $this->_string_shift($payload, 4)));
\r
1634 $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
\r
1635 $payload = $this->_get_binary_packet();
\r
1638 // only called when we've already logged in
\r
1639 if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
\r
1640 switch (ord($payload[0])) {
\r
1641 case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
\r
1642 $this->_string_shift($payload, 1);
\r
1643 extract(unpack('Nlength', $this->_string_shift($payload)));
\r
1644 $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
\r
1646 if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
\r
1647 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
\r
1650 $payload = $this->_get_binary_packet();
\r
1652 case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
\r
1653 $this->_string_shift($payload, 1);
\r
1654 extract(unpack('N', $this->_string_shift($payload, 4)));
\r
1655 $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
\r
1657 $this->_string_shift($payload, 4); // skip over client channel
\r
1658 extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
\r
1660 $packet = pack('CN3a*Na*',
\r
1661 NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
\r
1663 if (!$this->_send_binary_packet($packet)) {
\r
1664 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
\r
1667 $payload = $this->_get_binary_packet();
\r
1669 case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
\r
1670 $payload = $this->_get_binary_packet();
\r
1678 * Gets channel data
\r
1680 * Returns the data as a string if it's available and false if not.
\r
1682 * @param $client_channel
\r
1686 function _get_channel_packet($client_channel)
\r
1688 if (!empty($this->channel_buffers[$client_channel])) {
\r
1689 return array_shift($this->channel_buffers[$client_channel]);
\r
1693 $response = $this->_get_binary_packet();
\r
1694 if ($response === false) {
\r
1695 user_error('Connection closed by server', E_USER_NOTICE);
\r
1699 extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
\r
1701 switch ($this->channel_status[$channel]) {
\r
1702 case NET_SSH2_MSG_CHANNEL_OPEN:
\r
1704 case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
\r
1705 extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
\r
1706 $this->server_channels[$client_channel] = $server_channel;
\r
1707 $this->_string_shift($response, 4); // skip over (server) window size
\r
1708 $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
\r
1709 $this->packet_size_client_to_server[$client_channel] = $temp['packet_size_client_to_server'];
\r
1711 //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
\r
1713 user_error('Unable to open channel', E_USER_NOTICE);
\r
1714 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
\r
1717 case NET_SSH2_MSG_CHANNEL_REQUEST:
\r
1719 case NET_SSH2_MSG_CHANNEL_SUCCESS:
\r
1721 //case NET_SSH2_MSG_CHANNEL_FAILURE:
\r
1723 user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
\r
1724 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
\r
1730 case NET_SSH2_MSG_CHANNEL_DATA:
\r
1731 if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
\r
1732 // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
\r
1733 // this actually seems to make things twice as fast. more to the point, the message right after
\r
1734 // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
\r
1735 // in OpenSSH it slows things down but only by a couple thousandths of a second.
\r
1736 $this->_send_channel_packet($client_channel, chr(0));
\r
1738 extract(unpack('Nlength', $this->_string_shift($response, 4)));
\r
1739 $data = $this->_string_shift($response, $length);
\r
1740 if ($client_channel == $channel) {
\r
1743 if (!isset($this->channel_buffers[$client_channel])) {
\r
1744 $this->channel_buffers[$client_channel] = array();
\r
1746 $this->channel_buffers[$client_channel][] = $data;
\r
1748 case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
\r
1749 if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
\r
1750 $this->_send_channel_packet($client_channel, chr(0));
\r
1752 // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
\r
1753 extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
\r
1754 $data = $this->_string_shift($response, $length);
\r
1755 if ($client_channel == $channel) {
\r
1758 if (!isset($this->channel_buffers[$client_channel])) {
\r
1759 $this->channel_buffers[$client_channel] = array();
\r
1761 $this->channel_buffers[$client_channel][] = $data;
\r
1763 case NET_SSH2_MSG_CHANNEL_REQUEST:
\r
1764 extract(unpack('Nlength', $this->_string_shift($response, 4)));
\r
1765 $value = $this->_string_shift($response, $length);
\r
1767 case 'exit-signal':
\r
1768 $this->_string_shift($response, 1);
\r
1769 extract(unpack('Nlength', $this->_string_shift($response, 4)));
\r
1770 $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
\r
1771 $this->_string_shift($response, 1);
\r
1772 extract(unpack('Nlength', $this->_string_shift($response, 4)));
\r
1774 $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
\r
1776 //case 'exit-status':
\r
1778 // "Some systems may not implement signals, in which case they SHOULD ignore this message."
\r
1779 // -- http://tools.ietf.org/html/rfc4254#section-6.9
\r
1783 case NET_SSH2_MSG_CHANNEL_CLOSE:
\r
1784 $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
\r
1786 case NET_SSH2_MSG_CHANNEL_EOF:
\r
1789 user_error('Error reading channel data', E_USER_NOTICE);
\r
1790 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
\r
1796 * Sends Binary Packets
\r
1798 * See '6. Binary Packet Protocol' of rfc4253 for more info.
\r
1800 * @param String $data
\r
1801 * @see Net_SSH2::_get_binary_packet()
\r
1805 function _send_binary_packet($data)
\r
1807 if (feof($this->fsock)) {
\r
1808 user_error('Connection closed prematurely', E_USER_NOTICE);
\r
1812 //if ($this->compress) {
\r
1813 // // the -4 removes the checksum:
\r
1814 // // http://php.net/function.gzcompress#57710
\r
1815 // $data = substr(gzcompress($data), 0, -4);
\r
1818 // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
\r
1819 $packet_length = strlen($data) + 9;
\r
1820 // round up to the nearest $this->encrypt_block_size
\r
1821 $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
\r
1822 // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
\r
1823 $padding_length = $packet_length - strlen($data) - 5;
\r
1826 for ($i = 0; $i < $padding_length; $i++) {
\r
1827 $padding.= chr(crypt_random(0, 255));
\r
1830 // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
\r
1831 $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
\r
1833 $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
\r
1834 $this->send_seq_no++;
\r
1836 if ($this->encrypt !== false) {
\r
1837 $packet = $this->encrypt->encrypt($packet);
\r
1842 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
\r
1843 $result = strlen($packet) == fputs($this->fsock, $packet);
\r
1844 $stop = strtok(microtime(), ' ') + strtok('');
\r
1846 if (defined('NET_SSH2_LOGGING')) {
\r
1847 $temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN';
\r
1848 $this->message_number_log[] = '-> ' . $temp .
\r
1849 ' (' . round($stop - $start, 4) . 's)';
\r
1850 if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
\r
1851 $this->message_log[] = substr($data, 1);
\r
1859 * Sends channel data
\r
1861 * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
\r
1863 * @param Integer $client_channel
\r
1864 * @param String $data
\r
1868 function _send_channel_packet($client_channel, $data)
\r
1870 while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
\r
1871 // resize the window, if appropriate
\r
1872 $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel];
\r
1873 if ($this->window_size_client_to_server[$client_channel] < 0) {
\r
1874 $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
\r
1875 if (!$this->_send_binary_packet($packet)) {
\r
1878 $this->window_size_client_to_server[$client_channel]+= $this->window_size;
\r
1881 $packet = pack('CN2a*',
\r
1882 NET_SSH2_MSG_CHANNEL_DATA,
\r
1883 $this->server_channels[$client_channel],
\r
1884 $this->packet_size_client_to_server[$client_channel],
\r
1885 $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
\r
1888 if (!$this->_send_binary_packet($packet)) {
\r
1893 // resize the window, if appropriate
\r
1894 $this->window_size_client_to_server[$client_channel]-= strlen($data);
\r
1895 if ($this->window_size_client_to_server[$client_channel] < 0) {
\r
1896 $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
\r
1897 if (!$this->_send_binary_packet($packet)) {
\r
1900 $this->window_size_client_to_server[$client_channel]+= $this->window_size;
\r
1903 return $this->_send_binary_packet(pack('CN2a*',
\r
1904 NET_SSH2_MSG_CHANNEL_DATA,
\r
1905 $this->server_channels[$client_channel],
\r
1913 * @param Integer $reason
\r
1917 function _disconnect($reason)
\r
1919 if ($this->bitmap) {
\r
1920 $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
\r
1921 $this->_send_binary_packet($data);
\r
1922 $this->bitmap = 0;
\r
1923 fclose($this->fsock);
\r
1931 * Inspired by array_shift
\r
1933 * @param String $string
\r
1934 * @param optional Integer $index
\r
1938 function _string_shift(&$string, $index = 1)
\r
1940 $substr = substr($string, 0, $index);
\r
1941 $string = substr($string, $index);
\r
1948 * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
\r
1949 * named constants from it, using the value as the name of the constant and the index as the value of the constant.
\r
1950 * If any of the constants that would be defined already exists, none of the constants will be defined.
\r
1952 * @param Array $array
\r
1955 function _define_array()
\r
1957 $args = func_get_args();
\r
1958 foreach ($args as $arg) {
\r
1959 foreach ($arg as $key=>$value) {
\r
1960 if (!defined($value)) {
\r
1961 define($value, $key);
\r
1970 * Returns a log of the packets that have been sent and received.
\r
1972 * 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')
\r
1975 * @return String or Array
\r
1979 if (!defined('NET_SSH2_LOGGING')) {
\r
1983 switch (NET_SSH2_LOGGING) {
\r
1984 case NET_SSH2_LOG_SIMPLE:
\r
1985 return $this->message_number_log;
\r
1987 case NET_SSH2_LOG_COMPLEX:
\r
1988 return $this->_format_log($this->message_log, $this->message_number_log);
\r
1996 * Formats a log for printing
\r
1998 * @param Array $message_log
\r
1999 * @param Array $message_number_log
\r
2003 function _format_log($message_log, $message_number_log)
\r
2005 static $boundary = ':', $long_width = 65, $short_width = 16;
\r
2008 for ($i = 0; $i < count($message_log); $i++) {
\r
2009 $output.= $message_number_log[$i] . "\r\n";
\r
2010 $current_log = $message_log[$i];
\r
2013 if (!empty($current_log)) {
\r
2014 $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
\r
2016 $fragment = $this->_string_shift($current_log, $short_width);
\r
2020 '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
\r
2024 // replace non ASCII printable characters with dots
\r
2025 // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
\r
2026 // also replace < with a . since < messes up the output on web browsers
\r
2027 $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
\r
2028 $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
\r
2030 } while (!empty($current_log));
\r
2038 * Returns all errors
\r
2043 function getErrors()
\r
2045 return $this->errors;
\r
2049 * Returns the last error
\r
2054 function getLastError()
\r
2056 return $this->errors[count($this->errors) - 1];
\r
2060 * Return the server identification.
\r
2065 function getServerIdentification()
\r
2067 return $this->server_identifier;
\r
2071 * Return a list of the key exchange algorithms the server supports.
\r
2076 function getKexAlgorithms()
\r
2078 return $this->kex_algorithms;
\r
2082 * Return a list of the host key (public key) algorithms the server supports.
\r
2087 function getServerHostKeyAlgorithms()
\r
2089 return $this->server_host_key_algorithms;
\r
2093 * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
\r
2098 function getEncryptionAlgorithmsClient2Server()
\r
2100 return $this->encryption_algorithms_client_to_server;
\r
2104 * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
\r
2109 function getEncryptionAlgorithmsServer2Client()
\r
2111 return $this->encryption_algorithms_server_to_client;
\r
2115 * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
\r
2120 function getMACAlgorithmsClient2Server()
\r
2122 return $this->mac_algorithms_client_to_server;
\r
2126 * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
\r
2131 function getMACAlgorithmsServer2Client()
\r
2133 return $this->mac_algorithms_server_to_client;
\r
2137 * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
\r
2142 function getCompressionAlgorithmsClient2Server()
\r
2144 return $this->compression_algorithms_client_to_server;
\r
2148 * Return a list of the compression algorithms the server supports, when sending stuff to the client.
\r
2153 function getCompressionAlgorithmsServer2Client()
\r
2155 return $this->compression_algorithms_server_to_client;
\r
2159 * Return a list of the languages the server supports, when sending stuff to the client.
\r
2164 function getLanguagesServer2Client()
\r
2166 return $this->languages_server_to_client;
\r
2170 * Return a list of the languages the server supports, when receiving stuff from the client.
\r
2175 function getLanguagesClient2Server()
\r
2177 return $this->languages_client_to_server;
\r
2181 * Returns the server public host key.
\r
2183 * Caching this the first time you connect to a server and checking the result on subsequent connections
\r
2184 * is recommended. Returns false if the server signature is not signed correctly with the public host key.
\r
2189 function getServerPublicHostKey()
\r
2191 $signature = $this->signature;
\r
2192 $server_public_host_key = $this->server_public_host_key;
\r
2194 extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
\r
2195 $this->_string_shift($server_public_host_key, $length);
\r
2197 switch ($this->signature_format) {
\r
2199 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
2200 $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
\r
2202 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
2203 $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
\r
2205 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
2206 $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
\r
2208 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
2209 $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
\r
2211 /* The value for 'dss_signature_blob' is encoded as a string containing
\r
2212 r, followed by s (which are 160-bit integers, without lengths or
\r
2213 padding, unsigned, and in network byte order). */
\r
2214 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
\r
2215 if ($temp['length'] != 40) {
\r
2216 user_error('Invalid signature', E_USER_NOTICE);
\r
2217 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
2220 $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
\r
2221 $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
\r
2223 if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
\r
2224 user_error('Invalid signature', E_USER_NOTICE);
\r
2225 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
2228 $w = $s->modInverse($q);
\r
2230 $u1 = $w->multiply(new Math_BigInteger(sha1($this->session_id), 16));
\r
2231 list(, $u1) = $u1->divide($q);
\r
2233 $u2 = $w->multiply($r);
\r
2234 list(, $u2) = $u2->divide($q);
\r
2236 $g = $g->modPow($u1, $p);
\r
2237 $y = $y->modPow($u2, $p);
\r
2239 $v = $g->multiply($y);
\r
2240 list(, $v) = $v->divide($p);
\r
2241 list(, $v) = $v->divide($q);
\r
2243 if (!$v->equals($r)) {
\r
2244 user_error('Bad server signature', E_USER_NOTICE);
\r
2245 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
\r
2250 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
2251 $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
\r
2253 $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
\r
2254 $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
\r
2255 $nLength = $temp['length'];
\r
2258 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
\r
2259 $signature = $this->_string_shift($signature, $temp['length']);
\r
2261 if (!class_exists('Crypt_RSA')) {
\r
2262 require_once('Crypt/RSA.php');
\r
2265 $rsa = new Crypt_RSA();
\r
2266 $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
\r
2267 $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
\r
2268 if (!$rsa->verify($this->session_id, $signature)) {
\r
2269 user_error('Bad server signature', E_USER_NOTICE);
\r
2270 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
\r
2274 $temp = unpack('Nlength', $this->_string_shift($signature, 4));
\r
2275 $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
\r
2277 // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
\r
2279 // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
\r
2281 // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
\r
2283 if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
\r
2284 user_error('Invalid signature', E_USER_NOTICE);
\r
2285 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
\r
2288 $s = $s->modPow($e, $n);
\r
2289 $s = $s->toBytes();
\r
2291 $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->session_id));
\r
2292 $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
\r
2295 user_error('Bad server signature', E_USER_NOTICE);
\r
2296 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
\r
2300 return $this->server_public_host_key;
\r