PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Phpseclib/Net/SSH2.php

https://bitbucket.org/appstrakt/lib_php_phpseclib
PHP | 2142 lines | 1143 code | 270 blank | 729 comment | 163 complexity | 0c1201376b7672a10d6569adefef1482 MD5 | raw file
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP implementation of SSHv2.
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * Here are some examples of how to use this library:
  9. * <code>
  10. * <?php
  11. * include('Net/SSH2.php');
  12. *
  13. * $ssh = new SSH2('www.domain.tld');
  14. * if (!$ssh->login('username', 'password')) {
  15. * exit('Login Failed');
  16. * }
  17. *
  18. * echo $ssh->exec('pwd');
  19. * echo $ssh->exec('ls -la');
  20. * ?>
  21. * </code>
  22. *
  23. * <code>
  24. * <?php
  25. * include('Crypt/RSA.php');
  26. * include('Net/SSH2.php');
  27. *
  28. * $key = new Crypt_RSA();
  29. * //$key->setPassword('whatever');
  30. * $key->loadKey(file_get_contents('privatekey'));
  31. *
  32. * $ssh = new SSH2('www.domain.tld');
  33. * if (!$ssh->login('username', $key)) {
  34. * exit('Login Failed');
  35. * }
  36. *
  37. * echo $ssh->exec('pwd');
  38. * echo $ssh->exec('ls -la');
  39. * ?>
  40. * </code>
  41. *
  42. * LICENSE: This library is free software; you can redistribute it and/or
  43. * modify it under the terms of the GNU Lesser General Public
  44. * License as published by the Free Software Foundation; either
  45. * version 2.1 of the License, or (at your option) any later version.
  46. *
  47. * This library is distributed in the hope that it will be useful,
  48. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  49. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  50. * Lesser General Public License for more details.
  51. *
  52. * You should have received a copy of the GNU Lesser General Public
  53. * License along with this library; if not, write to the Free Software
  54. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  55. * MA 02111-1307 USA
  56. *
  57. * @category Net
  58. * @package SSH2
  59. * @author Jim Wigginton <terrafrost@php.net>
  60. * @copyright MMVII Jim Wigginton
  61. * @license http://www.gnu.org/licenses/lgpl.txt
  62. * @version $Id: SSH2.php 81 2010-05-19 21:56:16Z admin $
  63. * @link http://phpseclib.sourceforge.net
  64. */
  65. namespace Phpseclib\Net;
  66. /**
  67. * Include Math_BigInteger
  68. *
  69. * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  70. */
  71. use Phpseclib\Math\BigInteger;
  72. /**
  73. * Include Crypt_Random
  74. */
  75. use Phpseclib\Crypt\Random;
  76. /**
  77. * Include Crypt_Hash
  78. */
  79. use Phpseclib\Crypt\Hash;
  80. /**
  81. * Include Crypt_TripleDES
  82. */
  83. use Phpseclib\Crypt\TripleDES;
  84. /**
  85. * Include Crypt_RC4
  86. */
  87. use Phpseclib\Crypt\RC4;
  88. /**
  89. * Include Crypt_AES
  90. */
  91. use Phpseclib\Crypt\AES;
  92. /**#@+
  93. * Execution Bitmap Masks
  94. *
  95. * @see SSH2::bitmap
  96. * @access private
  97. */
  98. //define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
  99. //define('NET_SSH2_MASK_LOGIN', 0x00000002);
  100. /**#@-*/
  101. /**#@+
  102. * @access public
  103. * @see SSH2::getLog()
  104. */
  105. /**
  106. * Returns the message numbers
  107. */
  108. //define('NET_SSH2_LOG_SIMPLE', 1);
  109. /**
  110. * Returns the message content
  111. */
  112. //define('NET_SSH2_LOG_COMPLEX', 2);
  113. /**#@-*/
  114. /**
  115. * Pure-PHP implementation of SSHv2.
  116. *
  117. * @author Jim Wigginton <terrafrost@php.net>
  118. * @version 0.1.0
  119. * @access public
  120. * @package SSH2
  121. */
  122. class SSH2 {
  123. /**
  124. * The SSH identifier
  125. *
  126. * @var String
  127. * @access private
  128. */
  129. var $identifier = 'SSH-2.0-phpseclib_0.1';
  130. /**
  131. * The Socket Object
  132. *
  133. * @var Object
  134. * @access private
  135. */
  136. var $fsock;
  137. /**
  138. * Execution Bitmap
  139. *
  140. * The bits that are set reprsent functions that have been called already. This is used to determine
  141. * if a requisite function has been successfully executed. If not, an error should be thrown.
  142. *
  143. * @var Integer
  144. * @access private
  145. */
  146. var $bitmap = 0;
  147. /**
  148. * Debug Info
  149. *
  150. * @see SSH2::getDebugInfo()
  151. * @var String
  152. * @access private
  153. */
  154. var $debug_info = '';
  155. /**
  156. * Server Identifier
  157. *
  158. * @see SSH2::getServerIdentification()
  159. * @var String
  160. * @access private
  161. */
  162. var $server_identifier = '';
  163. /**
  164. * Key Exchange Algorithms
  165. *
  166. * @see SSH2::getKexAlgorithims()
  167. * @var Array
  168. * @access private
  169. */
  170. var $kex_algorithms;
  171. /**
  172. * Server Host Key Algorithms
  173. *
  174. * @see SSH2::getServerHostKeyAlgorithms()
  175. * @var Array
  176. * @access private
  177. */
  178. var $server_host_key_algorithms;
  179. /**
  180. * Encryption Algorithms: Client to Server
  181. *
  182. * @see SSH2::getEncryptionAlgorithmsClient2Server()
  183. * @var Array
  184. * @access private
  185. */
  186. var $encryption_algorithms_client_to_server;
  187. /**
  188. * Encryption Algorithms: Server to Client
  189. *
  190. * @see SSH2::getEncryptionAlgorithmsServer2Client()
  191. * @var Array
  192. * @access private
  193. */
  194. var $encryption_algorithms_server_to_client;
  195. /**
  196. * MAC Algorithms: Client to Server
  197. *
  198. * @see SSH2::getMACAlgorithmsClient2Server()
  199. * @var Array
  200. * @access private
  201. */
  202. var $mac_algorithms_client_to_server;
  203. /**
  204. * MAC Algorithms: Server to Client
  205. *
  206. * @see SSH2::getMACAlgorithmsServer2Client()
  207. * @var Array
  208. * @access private
  209. */
  210. var $mac_algorithms_server_to_client;
  211. /**
  212. * Compression Algorithms: Client to Server
  213. *
  214. * @see SSH2::getCompressionAlgorithmsClient2Server()
  215. * @var Array
  216. * @access private
  217. */
  218. var $compression_algorithms_client_to_server;
  219. /**
  220. * Compression Algorithms: Server to Client
  221. *
  222. * @see SSH2::getCompressionAlgorithmsServer2Client()
  223. * @var Array
  224. * @access private
  225. */
  226. var $compression_algorithms_server_to_client;
  227. /**
  228. * Languages: Server to Client
  229. *
  230. * @see SSH2::getLanguagesServer2Client()
  231. * @var Array
  232. * @access private
  233. */
  234. var $languages_server_to_client;
  235. /**
  236. * Languages: Client to Server
  237. *
  238. * @see SSH2::getLanguagesClient2Server()
  239. * @var Array
  240. * @access private
  241. */
  242. var $languages_client_to_server;
  243. /**
  244. * Block Size for Server to Client Encryption
  245. *
  246. * "Note that the length of the concatenation of 'packet_length',
  247. * 'padding_length', 'payload', and 'random padding' MUST be a multiple
  248. * of the cipher block size or 8, whichever is larger. This constraint
  249. * MUST be enforced, even when using stream ciphers."
  250. *
  251. * -- http://tools.ietf.org/html/rfc4253#section-6
  252. *
  253. * @see SSH2::SSH2()
  254. * @see SSH2::_send_binary_packet()
  255. * @var Integer
  256. * @access private
  257. */
  258. var $encrypt_block_size = 8;
  259. /**
  260. * Block Size for Client to Server Encryption
  261. *
  262. * @see SSH2::SSH2()
  263. * @see SSH2::_get_binary_packet()
  264. * @var Integer
  265. * @access private
  266. */
  267. var $decrypt_block_size = 8;
  268. /**
  269. * Server to Client Encryption Object
  270. *
  271. * @see SSH2::_get_binary_packet()
  272. * @var Object
  273. * @access private
  274. */
  275. var $decrypt = false;
  276. /**
  277. * Client to Server Encryption Object
  278. *
  279. * @see SSH2::_send_binary_packet()
  280. * @var Object
  281. * @access private
  282. */
  283. var $encrypt = false;
  284. /**
  285. * Client to Server HMAC Object
  286. *
  287. * @see SSH2::_send_binary_packet()
  288. * @var Object
  289. * @access private
  290. */
  291. var $hmac_create = false;
  292. /**
  293. * Server to Client HMAC Object
  294. *
  295. * @see SSH2::_get_binary_packet()
  296. * @var Object
  297. * @access private
  298. */
  299. var $hmac_check = false;
  300. /**
  301. * Size of server to client HMAC
  302. *
  303. * 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.
  304. * 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
  305. * append it.
  306. *
  307. * @see SSH2::_get_binary_packet()
  308. * @var Integer
  309. * @access private
  310. */
  311. var $hmac_size = false;
  312. /**
  313. * Server Public Host Key
  314. *
  315. * @see SSH2::getServerPublicHostKey()
  316. * @var String
  317. * @access private
  318. */
  319. var $server_public_host_key;
  320. /**
  321. * Session identifer
  322. *
  323. * "The exchange hash H from the first key exchange is additionally
  324. * used as the session identifier, which is a unique identifier for
  325. * this connection."
  326. *
  327. * -- http://tools.ietf.org/html/rfc4253#section-7.2
  328. *
  329. * @see SSH2::_key_exchange()
  330. * @var String
  331. * @access private
  332. */
  333. var $session_id = false;
  334. /**
  335. * Message Numbers
  336. *
  337. * @see SSH2::SSH2()
  338. * @var Array
  339. * @access private
  340. */
  341. var $message_numbers = array();
  342. /**
  343. * Disconnection Message 'reason codes' defined in RFC4253
  344. *
  345. * @see SSH2::SSH2()
  346. * @var Array
  347. * @access private
  348. */
  349. var $disconnect_reasons = array();
  350. /**
  351. * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  352. *
  353. * @see SSH2::SSH2()
  354. * @var Array
  355. * @access private
  356. */
  357. var $channel_open_failure_reasons = array();
  358. /**
  359. * Terminal Modes
  360. *
  361. * @link http://tools.ietf.org/html/rfc4254#section-8
  362. * @see SSH2::SSH2()
  363. * @var Array
  364. * @access private
  365. */
  366. var $terminal_modes = array();
  367. /**
  368. * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  369. *
  370. * @link http://tools.ietf.org/html/rfc4254#section-5.2
  371. * @see SSH2::SSH2()
  372. * @var Array
  373. * @access private
  374. */
  375. var $channel_extended_data_type_codes = array();
  376. /**
  377. * Send Sequence Number
  378. *
  379. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  380. *
  381. * @see SSH2::_send_binary_packet()
  382. * @var Integer
  383. * @access private
  384. */
  385. var $send_seq_no = 0;
  386. /**
  387. * Get Sequence Number
  388. *
  389. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  390. *
  391. * @see SSH2::_get_binary_packet()
  392. * @var Integer
  393. * @access private
  394. */
  395. var $get_seq_no = 0;
  396. /**
  397. * The Client Channel
  398. *
  399. * SSH2::exec() uses 0, although since SSH2::exec() doesn't send NET_SSH2_CHANNEL_DATA packets,
  400. * SSH2::_send_channel_packet() isn't actually called by any function in Net/SSH2.php. Classes that
  401. * extend SSH2, however, might call that function, hence it's existence.
  402. *
  403. * @var Integer
  404. * @see SSH2::_send_channel_packet
  405. * @access private
  406. */
  407. var $client_channel = 1;
  408. /**
  409. * Server Channels
  410. *
  411. * Maps client channels to server channels
  412. *
  413. * @see SSH2::_get_channel_packet()
  414. * @see SSH2::exec()
  415. * @var Array
  416. * @access private
  417. */
  418. var $server_channels = array();
  419. /**
  420. * Packet Size
  421. *
  422. * Maximum packet size
  423. *
  424. * @see SSH2::_send_channel_packet()
  425. * @see SSH2::exec()
  426. * @var Integer
  427. * @access private
  428. */
  429. var $packet_size_client_to_server = 0;
  430. /**
  431. * Message Number Log
  432. *
  433. * @see SSH2::getLog()
  434. * @var Array
  435. * @access private
  436. */
  437. var $message_number_log = array();
  438. /**
  439. * Message Log
  440. *
  441. * @see SSH2::getLog()
  442. * @var Array
  443. * @access private
  444. */
  445. var $message_log = array();
  446. /**
  447. * Default Constructor.
  448. *
  449. * Connects to an SSHv2 server
  450. *
  451. * @param String $host
  452. * @param optional Integer $port
  453. * @param optional Integer $timeout
  454. * @return SSH2
  455. * @access public
  456. */
  457. function __construct($host, $port = 22, $timeout = 10)
  458. {
  459. define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
  460. define('NET_SSH2_MASK_LOGIN', 0x00000002);
  461. define('NET_SSH2_LOG_SIMPLE', 1);
  462. define('NET_SSH2_LOG_COMPLEX', 2);
  463. $this->message_numbers = array(
  464. 1 => 'NET_SSH2_MSG_DISCONNECT',
  465. 2 => 'NET_SSH2_MSG_IGNORE',
  466. 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
  467. 4 => 'NET_SSH2_MSG_DEBUG',
  468. 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
  469. 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  470. 20 => 'NET_SSH2_MSG_KEXINIT',
  471. 21 => 'NET_SSH2_MSG_NEWKEYS',
  472. 30 => 'NET_SSH2_MSG_KEXDH_INIT',
  473. 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  474. 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  475. 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  476. 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  477. 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  478. 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  479. 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  480. 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  481. 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  482. 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  483. 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  484. 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  485. 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  486. 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  487. 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  488. 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  489. 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  490. 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  491. 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  492. );
  493. $this->disconnect_reasons = array(
  494. 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  495. 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  496. 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  497. 4 => 'NET_SSH2_DISCONNECT_RESERVED',
  498. 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  499. 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  500. 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  501. 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  502. 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  503. 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  504. 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  505. 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  506. 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  507. 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  508. 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  509. );
  510. $this->channel_open_failure_reasons = array(
  511. 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  512. );
  513. $this->terminal_modes = array(
  514. 0 => 'NET_SSH2_TTY_OP_END'
  515. );
  516. $this->channel_extended_data_type_codes = array(
  517. 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
  518. );
  519. $this->_define_array(
  520. $this->message_numbers,
  521. $this->disconnect_reasons,
  522. $this->channel_open_failure_reasons,
  523. $this->terminal_modes,
  524. $this->channel_extended_data_type_codes,
  525. array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
  526. array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK')
  527. );
  528. //var_dump('before fsockopen');
  529. $this->fsock = fsockopen($host, $port, $errno, $errstr, $timeout);
  530. //var_dump($this->fsock, $host, $port, $errno, $errstr, $timeout);
  531. if (!$this->fsock) {
  532. user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
  533. return;
  534. }
  535. /* According to the SSH2 specs,
  536. "The server MAY send other lines of data before sending the version
  537. string. Each line SHOULD be terminated by a Carriage Return and Line
  538. Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  539. in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
  540. MUST be able to process such lines." */
  541. $temp = '';
  542. while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
  543. if (substr($temp, -2) == "\r\n") {
  544. $this->debug_info.= $temp;
  545. $temp = '';
  546. }
  547. $temp.= fgets($this->fsock, 255);
  548. }
  549. if (defined('NET_SSH2_LOGGING')) {
  550. $this->message_number_log[] = '<-';
  551. $this->message_log[] = $temp;
  552. $this->message_number_log[] = '->';
  553. $this->message_log[] = $this->identifier . "\r\n";
  554. }
  555. $this->server_identifier = trim($temp);
  556. $this->debug_info = utf8_decode($this->debug_info);
  557. if ($matches[1] != '1.99' && $matches[1] != '2.0') {
  558. user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
  559. return;
  560. }
  561. fputs($this->fsock, $this->identifier . "\r\n");
  562. $response = $this->_get_binary_packet();
  563. if ($response === false) {
  564. user_error('Connection closed by server', E_USER_NOTICE);
  565. return;
  566. }
  567. if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  568. user_error('Expected SSH_MSG_KEXINIT', E_USER_NOTICE);
  569. return;
  570. }
  571. if (!$this->_key_exchange($response)) {
  572. return;
  573. }
  574. $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
  575. }
  576. /**
  577. * Key Exchange
  578. *
  579. * @param String $kexinit_payload_server
  580. * @access private
  581. */
  582. function _key_exchange($kexinit_payload_server)
  583. {
  584. static $kex_algorithms = array(
  585. 'diffie-hellman-group1-sha1', // REQUIRED
  586. 'diffie-hellman-group14-sha1' // REQUIRED
  587. );
  588. static $server_host_key_algorithms = array(
  589. 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
  590. 'ssh-dss' // REQUIRED sign Raw DSS Key
  591. );
  592. static $encryption_algorithms = array(
  593. 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
  594. 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
  595. 'aes192-cbc', // OPTIONAL AES with a 192-bit key
  596. 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
  597. '3des-cbc', // REQUIRED three-key 3DES in CBC mode
  598. 'none' // OPTIONAL no encryption; NOT RECOMMENDED
  599. );
  600. static $mac_algorithms = array(
  601. 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
  602. 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
  603. 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
  604. 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
  605. 'none' // OPTIONAL no MAC; NOT RECOMMENDED
  606. );
  607. static $compression_algorithms = array(
  608. 'none' // REQUIRED no compression
  609. //'zlib' // OPTIONAL ZLIB (LZ77) compression
  610. );
  611. static $str_kex_algorithms, $str_server_host_key_algorithms,
  612. $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
  613. $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
  614. if (empty($str_kex_algorithms)) {
  615. $str_kex_algorithms = implode(',', $kex_algorithms);
  616. $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
  617. $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
  618. $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
  619. $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
  620. }
  621. $client_cookie = '';
  622. for ($i = 0; $i < 16; $i++) {
  623. $client_cookie.= chr(self::crypt_random(0, 255));
  624. }
  625. $response = $kexinit_payload_server;
  626. $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  627. $server_cookie = $this->_string_shift($response, 16);
  628. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  629. $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  630. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  631. $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  632. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  633. $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  634. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  635. $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  636. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  637. $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  638. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  639. $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  640. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  641. $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  642. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  643. $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  644. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  645. $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  646. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  647. $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  648. extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
  649. $first_kex_packet_follows = $first_kex_packet_follows != 0;
  650. // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
  651. $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
  652. NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
  653. strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
  654. $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
  655. strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
  656. $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
  657. strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
  658. 0, 0
  659. );
  660. if (!$this->_send_binary_packet($kexinit_payload_client)) {
  661. return false;
  662. }
  663. // here ends the second place.
  664. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  665. for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
  666. if ($i == count($encryption_algorithms)) {
  667. user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
  668. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  669. }
  670. // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  671. // diffie-hellman key exchange as fast as possible
  672. $decrypt = $encryption_algorithms[$i];
  673. switch ($decrypt) {
  674. case '3des-cbc':
  675. $decryptKeyLength = 24; // eg. 192 / 8
  676. break;
  677. case 'aes256-cbc':
  678. $decryptKeyLength = 32; // eg. 256 / 8
  679. break;
  680. case 'aes192-cbc':
  681. $decryptKeyLength = 24; // eg. 192 / 8
  682. break;
  683. case 'aes128-cbc':
  684. $decryptKeyLength = 16; // eg. 128 / 8
  685. break;
  686. case 'arcfour':
  687. $decryptKeyLength = 16; // eg. 128 / 8
  688. break;
  689. case 'none';
  690. $decryptKeyLength = 0;
  691. }
  692. for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
  693. if ($i == count($encryption_algorithms)) {
  694. user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
  695. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  696. }
  697. $encrypt = $encryption_algorithms[$i];
  698. switch ($encrypt) {
  699. case '3des-cbc':
  700. $encryptKeyLength = 24;
  701. break;
  702. case 'aes256-cbc':
  703. $encryptKeyLength = 32;
  704. break;
  705. case 'aes192-cbc':
  706. $encryptKeyLength = 24;
  707. break;
  708. case 'aes128-cbc':
  709. $encryptKeyLength = 16;
  710. break;
  711. case 'arcfour':
  712. $encryptKeyLength = 16;
  713. break;
  714. case 'none';
  715. $encryptKeyLength = 0;
  716. }
  717. $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
  718. // through diffie-hellman key exchange a symmetric key is obtained
  719. for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
  720. if ($i == count($kex_algorithms)) {
  721. user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
  722. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  723. }
  724. switch ($kex_algorithms[$i]) {
  725. // see http://tools.ietf.org/html/rfc2409#section-6.2 and
  726. // http://tools.ietf.org/html/rfc2412, appendex E
  727. case 'diffie-hellman-group1-sha1':
  728. $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  729. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  730. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  731. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
  732. $keyLength = $keyLength < 160 ? $keyLength : 160;
  733. $hash = 'sha1';
  734. break;
  735. // see http://tools.ietf.org/html/rfc3526#section-3
  736. case 'diffie-hellman-group14-sha1':
  737. $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  738. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  739. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  740. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
  741. '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
  742. '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
  743. 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
  744. '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
  745. $keyLength = $keyLength < 160 ? $keyLength : 160;
  746. $hash = 'sha1';
  747. }
  748. $p = new BigInteger($p, 256);
  749. //$q = $p->bitwise_rightShift(1);
  750. /* To increase the speed of the key exchange, both client and server may
  751. reduce the size of their private exponents. It should be at least
  752. twice as long as the key material that is generated from the shared
  753. secret. For more details, see the paper by van Oorschot and Wiener
  754. [VAN-OORSCHOT].
  755. -- http://tools.ietf.org/html/rfc4419#section-6.2 */
  756. $q = new BigInteger(1);
  757. $q = $q->bitwise_leftShift(2 * $keyLength);
  758. $q = $q->subtract(new BigInteger(1));
  759. $g = new BigInteger(2);
  760. $x = new BigInteger();
  761. $x->setRandomGenerator(function($a, $b) {
  762. return SSH2::crypt_random($a, $b);
  763. });
  764. $x = $x->random(new BigInteger(1), $q);
  765. $e = $g->modPow($x, $p);
  766. $eBytes = $e->toBytes(true);
  767. $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
  768. if (!$this->_send_binary_packet($data)) {
  769. user_error('Connection closed by server', E_USER_NOTICE);
  770. return false;
  771. }
  772. $response = $this->_get_binary_packet();
  773. if ($response === false) {
  774. user_error('Connection closed by server', E_USER_NOTICE);
  775. return false;
  776. }
  777. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  778. if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
  779. user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
  780. return false;
  781. }
  782. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  783. $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
  784. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  785. $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
  786. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  787. $fBytes = $this->_string_shift($response, $temp['length']);
  788. $f = new BigInteger($fBytes, -256);
  789. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  790. $signature = $this->_string_shift($response, $temp['length']);
  791. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  792. $signature_format = $this->_string_shift($signature, $temp['length']);
  793. $key = $f->modPow($x, $p);
  794. $keyBytes = $key->toBytes(true);
  795. $source = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
  796. strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
  797. strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
  798. $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
  799. $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
  800. );
  801. $source = pack('H*', $hash($source));
  802. if ($this->session_id === false) {
  803. $this->session_id = $source;
  804. }
  805. // if you the server's assymetric key matches the one you have on file, then you should be able to decrypt the
  806. // "signature" and get something that should equal the "exchange hash", as defined in the SSH-2 specs.
  807. // here, we just check to see if the "signature" is good. you can verify whether or not the assymetric key is good,
  808. // later, with the getServerHostKeyAlgorithm() function
  809. for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
  810. if ($i == count($server_host_key_algorithms)) {
  811. user_error('No compatible server host key algorithms found', E_USER_NOTICE);
  812. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  813. }
  814. if ($public_key_format != $server_host_key_algorithms[$i] || $signature_format != $server_host_key_algorithms[$i]) {
  815. user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
  816. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  817. }
  818. switch ($server_host_key_algorithms[$i]) {
  819. case 'ssh-dss':
  820. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  821. $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  822. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  823. $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  824. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  825. $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  826. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  827. $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  828. /* The value for 'dss_signature_blob' is encoded as a string containing
  829. r, followed by s (which are 160-bit integers, without lengths or
  830. padding, unsigned, and in network byte order). */
  831. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  832. if ($temp['length'] != 40) {
  833. user_error('Invalid signature', E_USER_NOTICE);
  834. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  835. }
  836. $r = new BigInteger($this->_string_shift($signature, 20), 256);
  837. $s = new BigInteger($this->_string_shift($signature, 20), 256);
  838. if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
  839. user_error('Invalid signature', E_USER_NOTICE);
  840. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  841. }
  842. $w = $s->modInverse($q);
  843. $u1 = $w->multiply(new BigInteger(sha1($source), 16));
  844. list(, $u1) = $u1->divide($q);
  845. $u2 = $w->multiply($r);
  846. list(, $u2) = $u2->divide($q);
  847. $g = $g->modPow($u1, $p);
  848. $y = $y->modPow($u2, $p);
  849. $v = $g->multiply($y);
  850. list(, $v) = $v->divide($p);
  851. list(, $v) = $v->divide($q);
  852. if (!$v->equals($r)) {
  853. user_error('Invalid signature', E_USER_NOTICE);
  854. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  855. }
  856. break;
  857. case 'ssh-rsa':
  858. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  859. $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  860. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  861. $n = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  862. $nLength = $temp['length'];
  863. /*
  864. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  865. $signature = $this->_string_shift($signature, $temp['length']);
  866. if (!class_exists('Crypt_RSA')) {
  867. require_once('Crypt/RSA.php');
  868. }
  869. $rsa = new Crypt_RSA();
  870. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  871. $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
  872. if (!$rsa->verify($source, $signature)) {
  873. user_error('Bad server signature', E_USER_NOTICE);
  874. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  875. }
  876. */
  877. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  878. $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256);
  879. // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
  880. // following URL:
  881. // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
  882. // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
  883. if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) {
  884. user_error('Invalid signature', E_USER_NOTICE);
  885. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  886. }
  887. $s = $s->modPow($e, $n);
  888. $s = $s->toBytes();
  889. $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($source));
  890. $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
  891. if ($s != $h) {
  892. user_error('Bad server signature', E_USER_NOTICE);
  893. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  894. }
  895. }
  896. $packet = pack('C',
  897. NET_SSH2_MSG_NEWKEYS
  898. );
  899. if (!$this->_send_binary_packet($packet)) {
  900. return false;
  901. }
  902. $response = $this->_get_binary_packet();
  903. if ($response === false) {
  904. user_error('Connection closed by server', E_USER_NOTICE);
  905. return false;
  906. }
  907. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  908. if ($type != NET_SSH2_MSG_NEWKEYS) {
  909. user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
  910. return false;
  911. }
  912. switch ($encrypt) {
  913. case '3des-cbc':
  914. $this->encrypt = new TripleDES();
  915. // $this->encrypt_block_size = 64 / 8 == the default
  916. break;
  917. case 'aes256-cbc':
  918. $this->encrypt = new AES();
  919. $this->encrypt_block_size = 16; // eg. 128 / 8
  920. break;
  921. case 'aes192-cbc':
  922. $this->encrypt = new AES();
  923. $this->encrypt_block_size = 16;
  924. break;
  925. case 'aes128-cbc':
  926. $this->encrypt = new AES();
  927. $this->encrypt_block_size = 16;
  928. break;
  929. case 'arcfour':
  930. $this->encrypt = new RC4();
  931. break;
  932. case 'none';
  933. //$this->encrypt = new Crypt_Null();
  934. }
  935. switch ($decrypt) {
  936. case '3des-cbc':
  937. $this->decrypt = new TripleDES();
  938. break;
  939. case 'aes256-cbc':
  940. $this->decrypt = new AES();
  941. $this->decrypt_block_size = 16;
  942. break;
  943. case 'aes192-cbc':
  944. $this->decrypt = new AES();
  945. $this->decrypt_block_size = 16;
  946. break;
  947. case 'aes128-cbc':
  948. $this->decrypt = new AES();
  949. $this->decrypt_block_size = 16;
  950. break;
  951. case 'arcfour':
  952. $this->decrypt = new RC4();
  953. break;
  954. case 'none';
  955. //$this->decrypt = new Crypt_Null();
  956. }
  957. $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
  958. if ($this->encrypt) {
  959. $this->encrypt->enableContinuousBuffer();
  960. $this->encrypt->disablePadding();
  961. $iv = pack('H*', $hash($keyBytes . $source . 'A' . $this->session_id));
  962. while ($this->encrypt_block_size > strlen($iv)) {
  963. $iv.= pack('H*', $hash($keyBytes . $source . $iv));
  964. }
  965. $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
  966. $key = pack('H*', $hash($keyBytes . $source . 'C' . $this->session_id));
  967. while ($encryptKeyLength > strlen($key)) {
  968. $key.= pack('H*', $hash($keyBytes . $source . $key));
  969. }
  970. $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
  971. }
  972. if ($this->decrypt) {
  973. $this->decrypt->enableContinuousBuffer();
  974. $this->decrypt->disablePadding();
  975. $iv = pack('H*', $hash($keyBytes . $source . 'B' . $this->session_id));
  976. while ($this->decrypt_block_size > strlen($iv)) {
  977. $iv.= pack('H*', $hash($keyBytes . $source . $iv));
  978. }
  979. $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
  980. $key = pack('H*', $hash($keyBytes . $source . 'D' . $this->session_id));
  981. while ($decryptKeyLength > strlen($key)) {
  982. $key.= pack('H*', $hash($keyBytes . $source . $key));
  983. }
  984. $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
  985. }
  986. for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
  987. if ($i == count($mac_algorithms)) {
  988. user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
  989. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  990. }
  991. $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
  992. switch ($mac_algorithms[$i]) {
  993. case 'hmac-sha1':
  994. $this->hmac_create = new Hash('sha1');
  995. $createKeyLength = 20;
  996. break;
  997. case 'hmac-sha1-96':
  998. $this->hmac_create = new Hash('sha1-96');
  999. $createKeyLength = 20;
  1000. break;
  1001. case 'hmac-md5':
  1002. $this->hmac_create = new Hash('md5');
  1003. $createKeyLength = 16;
  1004. break;
  1005. case 'hmac-md5-96':
  1006. $this->hmac_create = new Hash('md5-96');
  1007. $createKeyLength = 16;
  1008. }
  1009. for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
  1010. if ($i == count($mac_algorithms)) {
  1011. user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
  1012. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1013. }
  1014. $checkKeyLength = 0;
  1015. $this->hmac_size = 0;
  1016. switch ($mac_algorithms[$i]) {
  1017. case 'hmac-sha1':
  1018. $this->hmac_check = new Hash('sha1');
  1019. $checkKeyLength = 20;
  1020. $this->hmac_size = 20;
  1021. break;
  1022. case 'hmac-sha1-96':
  1023. $this->hmac_check = new Hash('sha1-96');
  1024. $checkKeyLength = 20;
  1025. $this->hmac_size = 12;
  1026. break;
  1027. case 'hmac-md5':
  1028. $this->hmac_check = new Hash('md5');
  1029. $checkKeyLength = 16;
  1030. $this->hmac_size = 16;
  1031. break;
  1032. case 'hmac-md5-96':
  1033. $this->hmac_check = new Hash('md5-96');
  1034. $checkKeyLength = 16;
  1035. $this->hmac_size = 12;
  1036. }
  1037. $key = pack('H*', $hash($keyBytes . $source . 'E' . $this->session_id));
  1038. while ($createKeyLength > strlen($key)) {
  1039. $key.= pack('H*', $hash($keyBytes . $source . $key));
  1040. }
  1041. $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
  1042. $key = pack('H*', $hash($keyBytes . $source . 'F' . $this->session_id));
  1043. while ($checkKeyLength > strlen($key)) {
  1044. $key.= pack('H*', $hash($keyBytes . $source . $key));
  1045. }
  1046. $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
  1047. for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
  1048. if ($i == count($compression_algorithms)) {
  1049. user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
  1050. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1051. }
  1052. $this->decompress = $compression_algorithms[$i] == 'zlib';
  1053. for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
  1054. if ($i == count($compression_algorithms)) {
  1055. user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
  1056. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1057. }
  1058. $this->compress = $compression_algorithms[$i] == 'zlib';
  1059. return true;
  1060. }
  1061. /**
  1062. * Login
  1063. *
  1064. * @param String $username
  1065. * @param optional String $password
  1066. * @return Boolean
  1067. * @access public
  1068. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1069. * by sending dummy SSH_MSG_IGNORE messages.
  1070. */
  1071. function login($username, $password = '')
  1072. {
  1073. if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
  1074. //var_dump('1233');
  1075. return false;
  1076. }
  1077. $packet = pack('CNa*',
  1078. NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
  1079. );
  1080. if (!$this->_send_binary_packet($packet)) {
  1081. //var_dump('1243');
  1082. return false;
  1083. }
  1084. //var_dump('1245');
  1085. $response = $this->_get_binary_packet();
  1086. if ($response === false) {
  1087. user_error('Connection closed by server', E_USER_NOTICE);
  1088. return false;
  1089. }
  1090. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1091. if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
  1092. user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE);
  1093. return false;
  1094. }
  1095. // although PHP5's get_class() preserves the case, PHP4's does not
  1096. if (strtolower(get_class($password)) == 'phpseclib\crypt\rsa') {
  1097. return $this->_privatekey_login($username, $password);
  1098. }
  1099. $utf8_password = utf8_encode($password);
  1100. $packet = pack('CNa*Na*Na*CNa*',
  1101. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1102. strlen('password'), 'password', 0, strlen($utf8_password), $utf8_password
  1103. );
  1104. if (!$this->_send_binary_packet($packet)) {
  1105. return false;
  1106. }
  1107. // remove the username and password from the last logged packet
  1108. if (defined('NET_SSH2_LOGGING')) {
  1109. $packet = pack('CNa*Na*Na*CNa*',
  1110. NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
  1111. strlen('password'), 'password', 0, strlen('password'), 'password'
  1112. );
  1113. $this->message_log[count($this->message_log) - 1] = $packet;
  1114. }
  1115. $response = $this->_get_binary_packet();
  1116. if ($response === false) {
  1117. user_error('Connection closed by server', E_USER_NOTICE);
  1118. return false;
  1119. }
  1120. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1121. switch ($type) {
  1122. case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
  1123. if (defined('NET_SSH2_LOGGING')) {
  1124. $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
  1125. }
  1126. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1127. $this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_PASSWD_CHANGEREQ:\r\n" . utf8_decode($this->_string_shift($response, $length));
  1128. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  1129. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1130. // either the login is bad or the server employees multi-factor authentication
  1131. return false;
  1132. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1133. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1134. return true;
  1135. }
  1136. return false;
  1137. }
  1138. /**
  1139. * Login with an RSA private key
  1140. *
  1141. * @param String $username
  1142. * @param Crypt_RSA $password
  1143. * @return Boolean
  1144. * @access private
  1145. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1146. * by sending dummy SSH_MSG_IGNORE messages.
  1147. */
  1148. function _privatekey_login($username, $privatekey)
  1149. {
  1150. // see http://tools.ietf.org/html/rfc4253#page-15
  1151. $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  1152. $publickey = array(
  1153. 'e' => $publickey['e']->toBytes(true),
  1154. 'n' => $publickey['n']->toBytes(true)
  1155. );
  1156. $publickey = pack('Na*Na*Na*',
  1157. strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
  1158. );
  1159. $part1 = pack('CNa*Na*Na*',
  1160. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1161. strlen('publickey'), 'publickey'
  1162. );
  1163. $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
  1164. $packet = $part1 . chr(0) . $part2;
  1165. if (!$this->_send_binary_packet($packet)) {
  1166. return false;
  1167. }
  1168. $response = $this->_get_binary_packet();
  1169. if ($response === false) {
  1170. user_error('Connection closed by server', E_USER_NOTICE);
  1171. return false;
  1172. }
  1173. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1174. switch ($type) {
  1175. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1176. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1177. $this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_FAILURE:\r\n" . $this->_string_shift($response, $length);
  1178. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  1179. case NET_SSH2_MSG_USERAUTH_PK_OK:
  1180. // we'll just take it on faith that the public key blob and the public key algorithm name are as
  1181. // they should be
  1182. if (defined('NET_SSH2_LOGGING')) {
  1183. $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PK_OK';
  1184. }
  1185. }
  1186. $packet = $part1 . chr(1) . $part2;
  1187. $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  1188. $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
  1189. $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
  1190. $packet.= pack('Na*', strlen($signature), $signature);
  1191. if (!$this->_send_binary_packet($packet)) {
  1192. return false;
  1193. }
  1194. $response = $this->_get_binary_packet();
  1195. if ($response === false) {
  1196. user_error('Connection closed by server', E_USER_NOTICE);
  1197. return false;
  1198. }
  1199. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1200. switch ($type) {
  1201. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1202. // either the login is bad or the server employees multi-factor authentication
  1203. return false;
  1204. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1205. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1206. return true;
  1207. }
  1208. return false;
  1209. }
  1210. /**
  1211. * Execute Command
  1212. *
  1213. * @param String $command
  1214. * @return String
  1215. * @access public
  1216. */
  1217. function exec($command)
  1218. {
  1219. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1220. return false;
  1221. }
  1222. // RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
  1223. // to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  1224. // a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  1225. // recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  1226. // would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  1227. // The 'recipient channel' is the channel number given in the original
  1228. // open request, and 'sender channel' is the channel number allocated by
  1229. // the other side.
  1230. $client_channel = 0; // PuTTy uses 0x100
  1231. // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
  1232. // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
  1233. // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
  1234. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
  1235. $window_size = 0x7FFFFFFF;
  1236. // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
  1237. // uses 0x4000, that's what will be used here, as well.
  1238. $packet_size = 0x4000;
  1239. $packet = pack('CNa*N3',
  1240. NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', $client_channel, $window_size, $packet_size);
  1241. if (!$this->_send_binary_packet($packet)) {
  1242. return false;
  1243. }
  1244. $response = $this->_get_binary_packet();
  1245. if ($response === false) {
  1246. user_error('Connection closed by server', E_USER_NOTICE);
  1247. return false;
  1248. }
  1249. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1250. switch ($type) {
  1251. case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
  1252. $this->_string_shift($response, 4); // skip over client channel
  1253. extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
  1254. $this->server_channels[$client_channel] = $server_channel;
  1255. $this->_string_shift($response, 4); // skip over (server) window size
  1256. $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
  1257. $this->packet_size_client_to_server = $temp['packet_size_client_to_server'];
  1258. break;
  1259. case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
  1260. user_error('Unable to open channel', E_USER_NOTICE);
  1261. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1262. }
  1263. $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  1264. $packet = pack('CNNa*CNa*N5a*',
  1265. NET_SSH2_MSG_CHANNEL_REQUEST, $server_channel, strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
  1266. 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
  1267. if (!$this->_send_binary_packet($packet)) {
  1268. return false;
  1269. }
  1270. $response = $this->_get_binary_packet();
  1271. if ($response === false) {
  1272. user_error('Connection closed by server', E_USER_NOTICE);
  1273. return false;
  1274. }
  1275. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1276. switch ($type) {
  1277. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  1278. break;
  1279. case NET_SSH2_MSG_CHANNEL_FAILURE:
  1280. default:
  1281. user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
  1282. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1283. }
  1284. // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
  1285. // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
  1286. // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
  1287. $packet = pack('CNNa*CNa*',
  1288. NET_SSH2_MSG_CHANNEL_REQUEST, $server_channel, strlen('exec'), 'exec', 1, strlen($command), $command);
  1289. if (!$this->_send_binary_packet($packet)) {
  1290. return false;
  1291. }
  1292. $response = $this->_get_binary_packet();
  1293. if ($response === false) {
  1294. user_error('Connection closed by server', E_USER_NOTICE);
  1295. return false;
  1296. }
  1297. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1298. switch ($type) {
  1299. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  1300. break;
  1301. case NET_SSH2_MSG_CHANNEL_FAILURE:
  1302. default:
  1303. user_error('Unable to start execution of command', E_USER_NOTICE);
  1304. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1305. }
  1306. $output = '';
  1307. while (true) {
  1308. $temp = $this->_get_channel_packet();
  1309. switch (true) {
  1310. case $temp === true:
  1311. return $output;
  1312. case $temp === false:
  1313. return false;
  1314. default:
  1315. $output.= $temp;
  1316. }
  1317. }
  1318. }
  1319. /**
  1320. * Disconnect
  1321. *
  1322. * @access public
  1323. */
  1324. function disconnect()
  1325. {
  1326. $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1327. }
  1328. /**
  1329. * Destructor.
  1330. *
  1331. * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
  1332. * disconnect().
  1333. *
  1334. * @access public
  1335. */
  1336. function __destruct()
  1337. {
  1338. $this->disconnect();
  1339. }
  1340. /**
  1341. * Gets Binary Packets
  1342. *
  1343. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  1344. *
  1345. * @see SSH2::_send_binary_packet()
  1346. * @return String
  1347. * @access private
  1348. */
  1349. function _get_binary_packet()
  1350. {
  1351. if (feof($this->fsock)) {
  1352. user_error('Connection closed prematurely', E_USER_NOTICE);
  1353. return false;
  1354. }
  1355. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1356. $raw = fread($this->fsock, $this->decrypt_block_size);
  1357. $stop = strtok(microtime(), ' ') + strtok('');
  1358. if ($this->decrypt !== false) {
  1359. $raw = $this->decrypt->decrypt($raw);
  1360. }
  1361. extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
  1362. $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
  1363. $buffer = '';
  1364. while ($remaining_length > 0) {
  1365. $temp = fread($this->fsock, $remaining_length);
  1366. $buffer.= $temp;
  1367. $remaining_length-= strlen($temp);
  1368. }
  1369. if (!empty($buffer)) {
  1370. $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
  1371. $buffer = $temp = '';
  1372. }
  1373. $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
  1374. $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
  1375. if ($this->hmac_check !== false) {
  1376. $hmac = fread($this->fsock, $this->hmac_size);
  1377. if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
  1378. user_error('Invalid HMAC', E_USER_NOTICE);
  1379. return false;
  1380. }
  1381. }
  1382. //if ($this->decompress) {
  1383. // $payload = gzinflate(substr($payload, 2));
  1384. //}
  1385. $this->get_seq_no++;
  1386. if (defined('NET_SSH2_LOGGING')) {
  1387. $temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN';
  1388. $this->message_number_log[] = '<- ' . $temp .
  1389. ' (' . round($stop - $start, 4) . 's)';
  1390. $this->message_log[] = $payload;
  1391. }
  1392. return $this->_filter($payload);
  1393. }
  1394. /**
  1395. * Filter Binary Packets
  1396. *
  1397. * Because some binary packets need to be ignored...
  1398. *
  1399. * @see SSH2::_get_binary_packet()
  1400. * @return String
  1401. * @access private
  1402. */
  1403. function _filter($payload)
  1404. {
  1405. switch (ord($payload[0])) {
  1406. case NET_SSH2_MSG_DISCONNECT:
  1407. $this->_string_shift($payload, 1);
  1408. extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
  1409. $this->debug_info.= "\r\n\r\nSSH_MSG_DISCONNECT:\r\n" . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1410. $this->bitmask = 0;
  1411. return false;
  1412. case NET_SSH2_MSG_IGNORE:
  1413. $payload = $this->_get_binary_packet();
  1414. break;
  1415. case NET_SSH2_MSG_DEBUG:
  1416. $this->_string_shift($payload, 2);
  1417. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  1418. $this->debug_info.= "\r\n\r\nSSH_MSG_DEBUG:\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1419. $payload = $this->_get_binary_packet();
  1420. break;
  1421. case NET_SSH2_MSG_UNIMPLEMENTED:
  1422. return false;
  1423. case NET_SSH2_MSG_KEXINIT:
  1424. if ($this->session_id !== false) {
  1425. if (!$this->_key_exchange($payload)) {
  1426. $this->bitmask = 0;
  1427. return false;
  1428. }
  1429. $payload = $this->_get_binary_packet();
  1430. }
  1431. }
  1432. // 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
  1433. if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
  1434. $this->_string_shift($payload, 1);
  1435. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  1436. $this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_BANNER:\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1437. $payload = $this->_get_binary_packet();
  1438. }
  1439. // only called when we've already logged in
  1440. if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1441. switch (ord($payload[0])) {
  1442. case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
  1443. $this->_string_shift($payload, 1);
  1444. extract(unpack('Nlength', $this->_string_shift($payload)));
  1445. $this->debug_info.= "\r\n\r\nSSH_MSG_GLOBAL_REQUEST:\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1446. if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
  1447. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1448. }
  1449. $payload = $this->_get_binary_packet();
  1450. break;
  1451. case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
  1452. $this->_string_shift($payload, 1);
  1453. extract(unpack('N', $this->_string_shift($payload, 4)));
  1454. $this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_OPEN:\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1455. $this->_string_shift($payload, 4); // skip over client channel
  1456. extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
  1457. $packet = pack('CN3a*Na*',
  1458. NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
  1459. if (!$this->_send_binary_packet($packet)) {
  1460. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1461. }
  1462. $payload = $this->_get_binary_packet();
  1463. break;
  1464. case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
  1465. $payload = $this->_get_binary_packet();
  1466. }
  1467. }
  1468. return $payload;
  1469. }
  1470. /**
  1471. * Gets channel data
  1472. *
  1473. * Returns the data as a string if it's available and false if not.
  1474. *
  1475. * @return Mixed
  1476. * @access private
  1477. */
  1478. function _get_channel_packet()
  1479. {
  1480. while (true) {
  1481. $response = $this->_get_binary_packet();
  1482. if ($response === false) {
  1483. user_error('Connection closed by server', E_USER_NOTICE);
  1484. return false;
  1485. }
  1486. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1487. switch ($type) {
  1488. case NET_SSH2_MSG_CHANNEL_DATA:
  1489. $this->_string_shift($response, 4); // skip over client channel
  1490. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1491. return $this->_string_shift($response, $length);
  1492. case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  1493. $this->_string_shift($response, 4); // skip over client channel
  1494. extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
  1495. $data = $this->_string_shift($response, $length);
  1496. switch ($data_type_code) {
  1497. case NET_SSH2_EXTENDED_DATA_STDERR:
  1498. $this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_EXTENDED_DATA (SSH_EXTENDED_DATA_STDERR):\r\n" . $data;
  1499. }
  1500. break;
  1501. case NET_SSH2_MSG_CHANNEL_REQUEST:
  1502. $this->_string_shift($response, 4); // skip over client channel
  1503. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1504. $value = $this->_string_shift($response, $length);
  1505. switch ($value) {
  1506. case 'exit-signal':
  1507. $this->_string_shift($response, 1);
  1508. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1509. $this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_REQUEST (exit-signal):\r\nSIG" . $this->_string_shift($response, $length);
  1510. $this->_string_shift($response, 1);
  1511. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1512. $this->debug_info.= "\r\n" . $this->_string_shift($response, $length);
  1513. case 'exit-status':
  1514. default:
  1515. // "Some systems may not implement signals, in which case they SHOULD ignore this message."
  1516. // -- http://tools.ietf.org/html/rfc4254#section-6.9
  1517. //break 2;
  1518. break;
  1519. }
  1520. break;
  1521. case NET_SSH2_MSG_CHANNEL_CLOSE:
  1522. extract(unpack('Nclient_channel', $this->_string_shift($response, 4)));
  1523. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
  1524. return true;
  1525. case NET_SSH2_MSG_CHANNEL_EOF:
  1526. break;
  1527. default:
  1528. user_error('Error reading channel data', E_USER_NOTICE);
  1529. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1530. }
  1531. }
  1532. }
  1533. /**
  1534. * Sends Binary Packets
  1535. *
  1536. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  1537. *
  1538. * @param String $data
  1539. * @see SSH2::_get_binary_packet()
  1540. * @return Boolean
  1541. * @access private
  1542. */
  1543. function _send_binary_packet($data)
  1544. {
  1545. if (feof($this->fsock)) {
  1546. user_error('Connection closed prematurely', E_USER_NOTICE);
  1547. return false;
  1548. }
  1549. //if ($this->compress) {
  1550. // // the -4 removes the checksum:
  1551. // // http://php.net/function.gzcompress#57710
  1552. // $data = substr(gzcompress($data), 0, -4);
  1553. //}
  1554. // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
  1555. $packet_length = strlen($data) + 9;
  1556. // round up to the nearest $this->encrypt_block_size
  1557. $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
  1558. // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
  1559. $padding_length = $packet_length - strlen($data) - 5;
  1560. $padding = '';
  1561. for ($i = 0; $i < $padding_length; $i++) {
  1562. $padding.= chr(self::crypt_random(0, 255));
  1563. }
  1564. // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
  1565. $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
  1566. $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
  1567. $this->send_seq_no++;
  1568. if ($this->encrypt !== false) {
  1569. $packet = $this->encrypt->encrypt($packet);
  1570. }
  1571. $packet.= $hmac;
  1572. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1573. $result = strlen($packet) == fputs($this->fsock, $packet);
  1574. $stop = strtok(microtime(), ' ') + strtok('');
  1575. if (defined('NET_SSH2_LOGGING')) {
  1576. $temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN';
  1577. $this->message_number_log[] = '-> ' . $temp .
  1578. ' (' . round($stop - $start, 4) . 's)';
  1579. $this->message_log[] = $data;
  1580. }
  1581. return $result;
  1582. }
  1583. /**
  1584. * Sends channel data
  1585. *
  1586. * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
  1587. *
  1588. * @return Boolean
  1589. * @access private
  1590. */
  1591. function _send_channel_packet($data)
  1592. {
  1593. while (strlen($data) > $this->packet_size_client_to_server) {
  1594. $packet = pack('CN2a*',
  1595. NET_SSH2_MSG_CHANNEL_DATA,
  1596. $this->server_channels[$this->client_channel],
  1597. $this->packet_size_client_to_server,
  1598. $this->_string_shift($data, $this->packet_size_client_to_server)
  1599. );
  1600. if (!$this->_send_binary_packet($packet)) {
  1601. return false;
  1602. }
  1603. }
  1604. return $this->_send_binary_packet(pack('CN2a*',
  1605. NET_SSH2_MSG_CHANNEL_DATA,
  1606. $this->server_channels[$this->client_channel],
  1607. strlen($data),
  1608. $data));
  1609. }
  1610. /**
  1611. * Disconnect
  1612. *
  1613. * @param Integer $reason
  1614. * @return Boolean
  1615. * @access private
  1616. */
  1617. function _disconnect($reason)
  1618. {
  1619. if ($this->bitmap) {
  1620. $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
  1621. $this->_send_binary_packet($data);
  1622. $this->bitmap = 0;
  1623. fclose($this->fsock);
  1624. return false;
  1625. }
  1626. }
  1627. /**
  1628. * String Shift
  1629. *
  1630. * Inspired by array_shift
  1631. *
  1632. * @param String $string
  1633. * @param optional Integer $index
  1634. * @return String
  1635. * @access private
  1636. */
  1637. function _string_shift(&$string, $index = 1)
  1638. {
  1639. $substr = substr($string, 0, $index);
  1640. $string = substr($string, $index);
  1641. return $substr;
  1642. }
  1643. /**
  1644. * Define Array
  1645. *
  1646. * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
  1647. * named constants from it, using the value as the name of the constant and the index as the value of the constant.
  1648. * If any of the constants that would be defined already exists, none of the constants will be defined.
  1649. *
  1650. * @param Array $array
  1651. * @access private
  1652. */
  1653. function _define_array()
  1654. {
  1655. $args = func_get_args();
  1656. foreach ($args as $arg) {
  1657. foreach ($arg as $key=>$value) {
  1658. /*if (!isset($this->defined[$value])) {
  1659. $this->defined[$value] = $key;
  1660. }
  1661. else {
  1662. break 2;
  1663. }*/
  1664. if (!defined($value)) {
  1665. define($value, $key);
  1666. } else {
  1667. break 2;
  1668. }
  1669. }
  1670. }
  1671. }
  1672. private $defined = array();
  1673. /**
  1674. * Returns a log of the packets that have been sent and received.
  1675. *
  1676. * $type can be either NET_SSH2_LOG_SIMPLE or NET_SSH2_LOG_COMPLEX. Enable by defining NET_SSH2_LOGGING.
  1677. *
  1678. * @param Integer $type
  1679. * @access public
  1680. * @return String or Array
  1681. */
  1682. function getLog($type = NET_SSH2_LOG_COMPLEX)
  1683. {
  1684. if ($type == NET_SSH2_LOG_SIMPLE) {
  1685. return $this->message_number_log;
  1686. }
  1687. static $boundary = ':', $long_width = 65, $short_width = 15;
  1688. $output = '';
  1689. for ($i = 0; $i < count($this->message_log); $i++) {
  1690. $output.= $this->message_number_log[$i] . "\r\n";
  1691. $current_log = $this->message_log[$i];
  1692. do {
  1693. $fragment = $this->_string_shift($current_log, $short_width);
  1694. $hex = substr(
  1695. preg_replace(
  1696. '#(.)#es',
  1697. '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
  1698. $fragment),
  1699. strlen($boundary)
  1700. );
  1701. // replace non ASCII printable characters with dots
  1702. // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
  1703. $raw = preg_replace('#[^\x20-\x7E]#', '.', $fragment);
  1704. $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
  1705. } while (!empty($current_log));
  1706. $output.= "\r\n";
  1707. }
  1708. return $output;
  1709. }
  1710. /**
  1711. * Returns Debug Information
  1712. *
  1713. * If any debug information is sent by the server, this function can be used to access it.
  1714. *
  1715. * @return String
  1716. * @access public
  1717. */
  1718. function getDebugInfo()
  1719. {
  1720. return $this->debug_info;
  1721. }
  1722. /**
  1723. * Return the server identification.
  1724. *
  1725. * @return String
  1726. * @access public
  1727. */
  1728. function getServerIdentification()
  1729. {
  1730. return $this->server_identifier;
  1731. }
  1732. /**
  1733. * Return a list of the key exchange algorithms the server supports.
  1734. *
  1735. * @return Array
  1736. * @access public
  1737. */
  1738. function getKexAlgorithms()
  1739. {
  1740. return $this->kex_algorithms;
  1741. }
  1742. /**
  1743. * Return a list of the host key (public key) algorithms the server supports.
  1744. *
  1745. * @return Array
  1746. * @access public
  1747. */
  1748. function getServerHostKeyAlgorithms()
  1749. {
  1750. return $this->server_host_key_algorithms;
  1751. }
  1752. /**
  1753. * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
  1754. *
  1755. * @return Array
  1756. * @access public
  1757. */
  1758. function getEncryptionAlgorithmsClient2Server()
  1759. {
  1760. return $this->encryption_algorithms_client_to_server;
  1761. }
  1762. /**
  1763. * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
  1764. *
  1765. * @return Array
  1766. * @access public
  1767. */
  1768. function getEncryptionAlgorithmsServer2Client()
  1769. {
  1770. return $this->encryption_algorithms_server_to_client;
  1771. }
  1772. /**
  1773. * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
  1774. *
  1775. * @return Array
  1776. * @access public
  1777. */
  1778. function getMACAlgorithmsClient2Server()
  1779. {
  1780. return $this->mac_algorithms_client_to_server;
  1781. }
  1782. /**
  1783. * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
  1784. *
  1785. * @return Array
  1786. * @access public
  1787. */
  1788. function getMACAlgorithmsServer2Client()
  1789. {
  1790. return $this->mac_algorithms_server_to_client;
  1791. }
  1792. /**
  1793. * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
  1794. *
  1795. * @return Array
  1796. * @access public
  1797. */
  1798. function getCompressionAlgorithmsClient2Server()
  1799. {
  1800. return $this->compression_algorithms_client_to_server;
  1801. }
  1802. /**
  1803. * Return a list of the compression algorithms the server supports, when sending stuff to the client.
  1804. *
  1805. * @return Array
  1806. * @access public
  1807. */
  1808. function getCompressionAlgorithmsServer2Client()
  1809. {
  1810. return $this->compression_algorithms_server_to_client;
  1811. }
  1812. /**
  1813. * Return a list of the languages the server supports, when sending stuff to the client.
  1814. *
  1815. * @return Array
  1816. * @access public
  1817. */
  1818. function getLanguagesServer2Client()
  1819. {
  1820. return $this->languages_server_to_client;
  1821. }
  1822. /**
  1823. * Return a list of the languages the server supports, when receiving stuff from the client.
  1824. *
  1825. * @return Array
  1826. * @access public
  1827. */
  1828. function getLanguagesClient2Server()
  1829. {
  1830. return $this->languages_client_to_server;
  1831. }
  1832. /**
  1833. * Returns the server public host key.
  1834. *
  1835. * Caching this the first time you connect to a server and checking the result on subsequent connections
  1836. * is recommended.
  1837. *
  1838. * @return Array
  1839. * @access public
  1840. */
  1841. function getServerPublicHostKey()
  1842. {
  1843. return $this->server_public_host_key;
  1844. }
  1845. static function crypt_random($min = 0, $max = 0x7FFFFFFF, $randomness_path = '/dev/urandom')
  1846. {
  1847. static $seeded = false;
  1848. if (!$seeded) {
  1849. $seeded = true;
  1850. try {
  1851. if (file_exists($randomness_path)) {
  1852. $fp = fopen($randomness_path, 'r');
  1853. $temp = unpack('Nint', fread($fp, 4));
  1854. mt_srand($temp['int']);
  1855. fclose($fp);
  1856. } else {
  1857. list($sec, $usec) = explode(' ', microtime());
  1858. mt_srand((float) $sec + ((float) $usec * 100000));
  1859. }
  1860. }
  1861. catch (\Exception $e) {
  1862. list($sec, $usec) = explode(' ', microtime());
  1863. mt_srand((float) $sec + ((float) $usec * 100000));
  1864. }
  1865. }
  1866. return mt_rand($min, $max);
  1867. }
  1868. }