PageRenderTime 71ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/includes/src/phpseclib/Net/SSH2.php

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