PageRenderTime 37ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/drupal/profiles/medienlabor/modules/contrib/aes/phpseclib/Net/SSH2.php

https://bitbucket.org/cbeier/test
PHP | 2901 lines | 1538 code | 348 blank | 1015 comment | 253 complexity | 96d43c659ee7f81da4d0b1a7ce4d3886 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1
  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->read('username@username:~$');
  38. * $ssh->write("ls -la\n");
  39. * echo $ssh->read('username@username:~$');
  40. * ?>
  41. * </code>
  42. *
  43. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  44. * of this software and associated documentation files (the "Software"), to deal
  45. * in the Software without restriction, including without limitation the rights
  46. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  47. * copies of the Software, and to permit persons to whom the Software is
  48. * furnished to do so, subject to the following conditions:
  49. *
  50. * The above copyright notice and this permission notice shall be included in
  51. * all copies or substantial portions of the Software.
  52. *
  53. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  54. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  55. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  56. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  57. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  58. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  59. * THE SOFTWARE.
  60. *
  61. * @category Net
  62. * @package Net_SSH2
  63. * @author Jim Wigginton <terrafrost@php.net>
  64. * @copyright MMVII Jim Wigginton
  65. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  66. * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $
  67. * @link http://phpseclib.sourceforge.net
  68. */
  69. /**
  70. * Include Math_BigInteger
  71. *
  72. * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  73. */
  74. if (!class_exists('Math_BigInteger')) {
  75. require_once('Math/BigInteger.php');
  76. }
  77. /**
  78. * Include Crypt_Random
  79. */
  80. // the class_exists() will only be called if the crypt_random function hasn't been defined and
  81. // will trigger a call to __autoload() if you're wanting to auto-load classes
  82. // call function_exists() a second time to stop the require_once from being called outside
  83. // of the auto loader
  84. if (!function_exists('crypt_random') && !class_exists('Crypt_Random') && !function_exists('crypt_random')) {
  85. require_once('Crypt/Random.php');
  86. }
  87. /**
  88. * Include Crypt_Hash
  89. */
  90. if (!class_exists('Crypt_Hash')) {
  91. require_once('Crypt/Hash.php');
  92. }
  93. /**
  94. * Include Crypt_TripleDES
  95. */
  96. if (!class_exists('Crypt_TripleDES')) {
  97. require_once('Crypt/TripleDES.php');
  98. }
  99. /**
  100. * Include Crypt_RC4
  101. */
  102. if (!class_exists('Crypt_RC4')) {
  103. require_once('Crypt/RC4.php');
  104. }
  105. /**
  106. * Include Crypt_AES
  107. */
  108. if (!class_exists('Crypt_AES')) {
  109. require_once('Crypt/AES.php');
  110. }
  111. /**#@+
  112. * Execution Bitmap Masks
  113. *
  114. * @see Net_SSH2::bitmap
  115. * @access private
  116. */
  117. define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
  118. define('NET_SSH2_MASK_LOGIN', 0x00000002);
  119. define('NET_SSH2_MASK_SHELL', 0x00000004);
  120. /**#@-*/
  121. /**#@+
  122. * Channel constants
  123. *
  124. * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
  125. * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  126. * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  127. * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  128. * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  129. * The 'recipient channel' is the channel number given in the original
  130. * open request, and 'sender channel' is the channel number allocated by
  131. * the other side.
  132. *
  133. * @see Net_SSH2::_send_channel_packet()
  134. * @see Net_SSH2::_get_channel_packet()
  135. * @access private
  136. */
  137. define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
  138. define('NET_SSH2_CHANNEL_SHELL',1);
  139. /**#@-*/
  140. /**#@+
  141. * @access public
  142. * @see Net_SSH2::getLog()
  143. */
  144. /**
  145. * Returns the message numbers
  146. */
  147. define('NET_SSH2_LOG_SIMPLE', 1);
  148. /**
  149. * Returns the message content
  150. */
  151. define('NET_SSH2_LOG_COMPLEX', 2);
  152. /**
  153. * Outputs the content real-time
  154. */
  155. define('NET_SSH2_LOG_REALTIME', 3);
  156. /**#@-*/
  157. /**#@+
  158. * @access public
  159. * @see Net_SSH2::read()
  160. */
  161. /**
  162. * Returns when a string matching $expect exactly is found
  163. */
  164. define('NET_SSH2_READ_SIMPLE', 1);
  165. /**
  166. * Returns when a string matching the regular expression $expect is found
  167. */
  168. define('NET_SSH2_READ_REGEX', 2);
  169. /**
  170. * Make sure that the log never gets larger than this
  171. */
  172. define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024);
  173. /**#@-*/
  174. /**
  175. * Pure-PHP implementation of SSHv2.
  176. *
  177. * @author Jim Wigginton <terrafrost@php.net>
  178. * @version 0.1.0
  179. * @access public
  180. * @package Net_SSH2
  181. */
  182. class Net_SSH2 {
  183. /**
  184. * The SSH identifier
  185. *
  186. * @var String
  187. * @access private
  188. */
  189. var $identifier = 'SSH-2.0-phpseclib_0.3';
  190. /**
  191. * The Socket Object
  192. *
  193. * @var Object
  194. * @access private
  195. */
  196. var $fsock;
  197. /**
  198. * Execution Bitmap
  199. *
  200. * The bits that are set reprsent functions that have been called already. This is used to determine
  201. * if a requisite function has been successfully executed. If not, an error should be thrown.
  202. *
  203. * @var Integer
  204. * @access private
  205. */
  206. var $bitmap = 0;
  207. /**
  208. * Error information
  209. *
  210. * @see Net_SSH2::getErrors()
  211. * @see Net_SSH2::getLastError()
  212. * @var String
  213. * @access private
  214. */
  215. var $errors = array();
  216. /**
  217. * Server Identifier
  218. *
  219. * @see Net_SSH2::getServerIdentification()
  220. * @var String
  221. * @access private
  222. */
  223. var $server_identifier = '';
  224. /**
  225. * Key Exchange Algorithms
  226. *
  227. * @see Net_SSH2::getKexAlgorithims()
  228. * @var Array
  229. * @access private
  230. */
  231. var $kex_algorithms;
  232. /**
  233. * Server Host Key Algorithms
  234. *
  235. * @see Net_SSH2::getServerHostKeyAlgorithms()
  236. * @var Array
  237. * @access private
  238. */
  239. var $server_host_key_algorithms;
  240. /**
  241. * Encryption Algorithms: Client to Server
  242. *
  243. * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
  244. * @var Array
  245. * @access private
  246. */
  247. var $encryption_algorithms_client_to_server;
  248. /**
  249. * Encryption Algorithms: Server to Client
  250. *
  251. * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
  252. * @var Array
  253. * @access private
  254. */
  255. var $encryption_algorithms_server_to_client;
  256. /**
  257. * MAC Algorithms: Client to Server
  258. *
  259. * @see Net_SSH2::getMACAlgorithmsClient2Server()
  260. * @var Array
  261. * @access private
  262. */
  263. var $mac_algorithms_client_to_server;
  264. /**
  265. * MAC Algorithms: Server to Client
  266. *
  267. * @see Net_SSH2::getMACAlgorithmsServer2Client()
  268. * @var Array
  269. * @access private
  270. */
  271. var $mac_algorithms_server_to_client;
  272. /**
  273. * Compression Algorithms: Client to Server
  274. *
  275. * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
  276. * @var Array
  277. * @access private
  278. */
  279. var $compression_algorithms_client_to_server;
  280. /**
  281. * Compression Algorithms: Server to Client
  282. *
  283. * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
  284. * @var Array
  285. * @access private
  286. */
  287. var $compression_algorithms_server_to_client;
  288. /**
  289. * Languages: Server to Client
  290. *
  291. * @see Net_SSH2::getLanguagesServer2Client()
  292. * @var Array
  293. * @access private
  294. */
  295. var $languages_server_to_client;
  296. /**
  297. * Languages: Client to Server
  298. *
  299. * @see Net_SSH2::getLanguagesClient2Server()
  300. * @var Array
  301. * @access private
  302. */
  303. var $languages_client_to_server;
  304. /**
  305. * Block Size for Server to Client Encryption
  306. *
  307. * "Note that the length of the concatenation of 'packet_length',
  308. * 'padding_length', 'payload', and 'random padding' MUST be a multiple
  309. * of the cipher block size or 8, whichever is larger. This constraint
  310. * MUST be enforced, even when using stream ciphers."
  311. *
  312. * -- http://tools.ietf.org/html/rfc4253#section-6
  313. *
  314. * @see Net_SSH2::Net_SSH2()
  315. * @see Net_SSH2::_send_binary_packet()
  316. * @var Integer
  317. * @access private
  318. */
  319. var $encrypt_block_size = 8;
  320. /**
  321. * Block Size for Client to Server Encryption
  322. *
  323. * @see Net_SSH2::Net_SSH2()
  324. * @see Net_SSH2::_get_binary_packet()
  325. * @var Integer
  326. * @access private
  327. */
  328. var $decrypt_block_size = 8;
  329. /**
  330. * Server to Client Encryption Object
  331. *
  332. * @see Net_SSH2::_get_binary_packet()
  333. * @var Object
  334. * @access private
  335. */
  336. var $decrypt = false;
  337. /**
  338. * Client to Server Encryption Object
  339. *
  340. * @see Net_SSH2::_send_binary_packet()
  341. * @var Object
  342. * @access private
  343. */
  344. var $encrypt = false;
  345. /**
  346. * Client to Server HMAC Object
  347. *
  348. * @see Net_SSH2::_send_binary_packet()
  349. * @var Object
  350. * @access private
  351. */
  352. var $hmac_create = false;
  353. /**
  354. * Server to Client HMAC Object
  355. *
  356. * @see Net_SSH2::_get_binary_packet()
  357. * @var Object
  358. * @access private
  359. */
  360. var $hmac_check = false;
  361. /**
  362. * Size of server to client HMAC
  363. *
  364. * 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.
  365. * 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
  366. * append it.
  367. *
  368. * @see Net_SSH2::_get_binary_packet()
  369. * @var Integer
  370. * @access private
  371. */
  372. var $hmac_size = false;
  373. /**
  374. * Server Public Host Key
  375. *
  376. * @see Net_SSH2::getServerPublicHostKey()
  377. * @var String
  378. * @access private
  379. */
  380. var $server_public_host_key;
  381. /**
  382. * Session identifer
  383. *
  384. * "The exchange hash H from the first key exchange is additionally
  385. * used as the session identifier, which is a unique identifier for
  386. * this connection."
  387. *
  388. * -- http://tools.ietf.org/html/rfc4253#section-7.2
  389. *
  390. * @see Net_SSH2::_key_exchange()
  391. * @var String
  392. * @access private
  393. */
  394. var $session_id = false;
  395. /**
  396. * Exchange hash
  397. *
  398. * The current exchange hash
  399. *
  400. * @see Net_SSH2::_key_exchange()
  401. * @var String
  402. * @access private
  403. */
  404. var $exchange_hash = false;
  405. /**
  406. * Message Numbers
  407. *
  408. * @see Net_SSH2::Net_SSH2()
  409. * @var Array
  410. * @access private
  411. */
  412. var $message_numbers = array();
  413. /**
  414. * Disconnection Message 'reason codes' defined in RFC4253
  415. *
  416. * @see Net_SSH2::Net_SSH2()
  417. * @var Array
  418. * @access private
  419. */
  420. var $disconnect_reasons = array();
  421. /**
  422. * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  423. *
  424. * @see Net_SSH2::Net_SSH2()
  425. * @var Array
  426. * @access private
  427. */
  428. var $channel_open_failure_reasons = array();
  429. /**
  430. * Terminal Modes
  431. *
  432. * @link http://tools.ietf.org/html/rfc4254#section-8
  433. * @see Net_SSH2::Net_SSH2()
  434. * @var Array
  435. * @access private
  436. */
  437. var $terminal_modes = array();
  438. /**
  439. * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  440. *
  441. * @link http://tools.ietf.org/html/rfc4254#section-5.2
  442. * @see Net_SSH2::Net_SSH2()
  443. * @var Array
  444. * @access private
  445. */
  446. var $channel_extended_data_type_codes = array();
  447. /**
  448. * Send Sequence Number
  449. *
  450. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  451. *
  452. * @see Net_SSH2::_send_binary_packet()
  453. * @var Integer
  454. * @access private
  455. */
  456. var $send_seq_no = 0;
  457. /**
  458. * Get Sequence Number
  459. *
  460. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  461. *
  462. * @see Net_SSH2::_get_binary_packet()
  463. * @var Integer
  464. * @access private
  465. */
  466. var $get_seq_no = 0;
  467. /**
  468. * Server Channels
  469. *
  470. * Maps client channels to server channels
  471. *
  472. * @see Net_SSH2::_get_channel_packet()
  473. * @see Net_SSH2::exec()
  474. * @var Array
  475. * @access private
  476. */
  477. var $server_channels = array();
  478. /**
  479. * Channel Buffers
  480. *
  481. * If a client requests a packet from one channel but receives two packets from another those packets should
  482. * be placed in a buffer
  483. *
  484. * @see Net_SSH2::_get_channel_packet()
  485. * @see Net_SSH2::exec()
  486. * @var Array
  487. * @access private
  488. */
  489. var $channel_buffers = array();
  490. /**
  491. * Channel Status
  492. *
  493. * Contains the type of the last sent message
  494. *
  495. * @see Net_SSH2::_get_channel_packet()
  496. * @var Array
  497. * @access private
  498. */
  499. var $channel_status = array();
  500. /**
  501. * Packet Size
  502. *
  503. * Maximum packet size indexed by channel
  504. *
  505. * @see Net_SSH2::_send_channel_packet()
  506. * @var Array
  507. * @access private
  508. */
  509. var $packet_size_client_to_server = array();
  510. /**
  511. * Message Number Log
  512. *
  513. * @see Net_SSH2::getLog()
  514. * @var Array
  515. * @access private
  516. */
  517. var $message_number_log = array();
  518. /**
  519. * Message Log
  520. *
  521. * @see Net_SSH2::getLog()
  522. * @var Array
  523. * @access private
  524. */
  525. var $message_log = array();
  526. /**
  527. * The Window Size
  528. *
  529. * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB)
  530. *
  531. * @var Integer
  532. * @see Net_SSH2::_send_channel_packet()
  533. * @see Net_SSH2::exec()
  534. * @access private
  535. */
  536. var $window_size = 0x7FFFFFFF;
  537. /**
  538. * Window size
  539. *
  540. * Window size indexed by channel
  541. *
  542. * @see Net_SSH2::_send_channel_packet()
  543. * @var Array
  544. * @access private
  545. */
  546. var $window_size_client_to_server = array();
  547. /**
  548. * Server signature
  549. *
  550. * Verified against $this->session_id
  551. *
  552. * @see Net_SSH2::getServerPublicHostKey()
  553. * @var String
  554. * @access private
  555. */
  556. var $signature = '';
  557. /**
  558. * Server signature format
  559. *
  560. * ssh-rsa or ssh-dss.
  561. *
  562. * @see Net_SSH2::getServerPublicHostKey()
  563. * @var String
  564. * @access private
  565. */
  566. var $signature_format = '';
  567. /**
  568. * Interactive Buffer
  569. *
  570. * @see Net_SSH2::read()
  571. * @var Array
  572. * @access private
  573. */
  574. var $interactiveBuffer = '';
  575. /**
  576. * Current log size
  577. *
  578. * Should never exceed NET_SSH2_LOG_MAX_SIZE
  579. *
  580. * @see Net_SSH2::_send_binary_packet()
  581. * @see Net_SSH2::_get_binary_packet()
  582. * @var Integer
  583. * @access private
  584. */
  585. var $log_size;
  586. /**
  587. * Timeout
  588. *
  589. * @see Net_SSH2::setTimeout()
  590. * @access private
  591. */
  592. var $timeout;
  593. /**
  594. * Current Timeout
  595. *
  596. * @see Net_SSH2::_get_channel_packet()
  597. * @access private
  598. */
  599. var $curTimeout;
  600. /**
  601. * Real-time log file pointer
  602. *
  603. * @see Net_SSH2::_append_log()
  604. * @access private
  605. */
  606. var $realtime_log_file;
  607. /**
  608. * Real-time log file size
  609. *
  610. * @see Net_SSH2::_append_log()
  611. * @access private
  612. */
  613. var $realtime_log_size;
  614. /**
  615. * Has the signature been validated?
  616. *
  617. * @see Net_SSH2::getServerPublicHostKey()
  618. * @access private
  619. */
  620. var $signature_validated = false;
  621. /**
  622. * Real-time log file wrap boolean
  623. *
  624. * @see Net_SSH2::_append_log()
  625. * @access private
  626. */
  627. var $realtime_log_wrap;
  628. /**
  629. * Default Constructor.
  630. *
  631. * Connects to an SSHv2 server
  632. *
  633. * @param String $host
  634. * @param optional Integer $port
  635. * @param optional Integer $timeout
  636. * @return Net_SSH2
  637. * @access public
  638. */
  639. function Net_SSH2($host, $port = 22, $timeout = 10)
  640. {
  641. $this->message_numbers = array(
  642. 1 => 'NET_SSH2_MSG_DISCONNECT',
  643. 2 => 'NET_SSH2_MSG_IGNORE',
  644. 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
  645. 4 => 'NET_SSH2_MSG_DEBUG',
  646. 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
  647. 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  648. 20 => 'NET_SSH2_MSG_KEXINIT',
  649. 21 => 'NET_SSH2_MSG_NEWKEYS',
  650. 30 => 'NET_SSH2_MSG_KEXDH_INIT',
  651. 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  652. 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  653. 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  654. 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  655. 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  656. 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  657. 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  658. 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  659. 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  660. 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  661. 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  662. 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  663. 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  664. 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  665. 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  666. 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  667. 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  668. 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  669. 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  670. );
  671. $this->disconnect_reasons = array(
  672. 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  673. 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  674. 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  675. 4 => 'NET_SSH2_DISCONNECT_RESERVED',
  676. 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  677. 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  678. 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  679. 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  680. 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  681. 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  682. 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  683. 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  684. 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  685. 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  686. 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  687. );
  688. $this->channel_open_failure_reasons = array(
  689. 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  690. );
  691. $this->terminal_modes = array(
  692. 0 => 'NET_SSH2_TTY_OP_END'
  693. );
  694. $this->channel_extended_data_type_codes = array(
  695. 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
  696. );
  697. $this->_define_array(
  698. $this->message_numbers,
  699. $this->disconnect_reasons,
  700. $this->channel_open_failure_reasons,
  701. $this->terminal_modes,
  702. $this->channel_extended_data_type_codes,
  703. array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
  704. array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
  705. array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  706. 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE')
  707. );
  708. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  709. $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
  710. if (!$this->fsock) {
  711. user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
  712. return;
  713. }
  714. $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
  715. if ($timeout - $elapsed <= 0) {
  716. user_error(rtrim("Cannot connect to $host. Timeout error"), E_USER_NOTICE);
  717. return;
  718. }
  719. $read = array($this->fsock);
  720. $write = $except = NULL;
  721. stream_set_blocking($this->fsock, false);
  722. // on windows this returns a "Warning: Invalid CRT parameters detected" error
  723. if (!@stream_select($read, $write, $except, $timeout - $elapsed)) {
  724. user_error(rtrim("Cannot connect to $host. Banner timeout"), E_USER_NOTICE);
  725. return;
  726. }
  727. stream_set_blocking($this->fsock, true);
  728. /* According to the SSH2 specs,
  729. "The server MAY send other lines of data before sending the version
  730. string. Each line SHOULD be terminated by a Carriage Return and Line
  731. Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  732. in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
  733. MUST be able to process such lines." */
  734. $temp = '';
  735. $extra = '';
  736. while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
  737. if (substr($temp, -2) == "\r\n") {
  738. $extra.= $temp;
  739. $temp = '';
  740. }
  741. $temp.= fgets($this->fsock, 255);
  742. }
  743. if (feof($this->fsock)) {
  744. user_error('Connection closed by server', E_USER_NOTICE);
  745. return false;
  746. }
  747. $ext = array();
  748. if (extension_loaded('mcrypt')) {
  749. $ext[] = 'mcrypt';
  750. }
  751. if (extension_loaded('gmp')) {
  752. $ext[] = 'gmp';
  753. } else if (extension_loaded('bcmath')) {
  754. $ext[] = 'bcmath';
  755. }
  756. if (!empty($ext)) {
  757. $this->identifier.= ' (' . implode(', ', $ext) . ')';
  758. }
  759. if (defined('NET_SSH2_LOGGING')) {
  760. $this->message_number_log[] = '<-';
  761. $this->message_number_log[] = '->';
  762. if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  763. $this->message_log[] = $temp;
  764. $this->message_log[] = $this->identifier . "\r\n";
  765. }
  766. }
  767. $this->server_identifier = trim($temp, "\r\n");
  768. if (!empty($extra)) {
  769. $this->errors[] = utf8_decode($extra);
  770. }
  771. if ($matches[1] != '1.99' && $matches[1] != '2.0') {
  772. user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
  773. return;
  774. }
  775. fputs($this->fsock, $this->identifier . "\r\n");
  776. $response = $this->_get_binary_packet();
  777. if ($response === false) {
  778. user_error('Connection closed by server', E_USER_NOTICE);
  779. return;
  780. }
  781. if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  782. user_error('Expected SSH_MSG_KEXINIT', E_USER_NOTICE);
  783. return;
  784. }
  785. if (!$this->_key_exchange($response)) {
  786. return;
  787. }
  788. $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
  789. }
  790. /**
  791. * Key Exchange
  792. *
  793. * @param String $kexinit_payload_server
  794. * @access private
  795. */
  796. function _key_exchange($kexinit_payload_server)
  797. {
  798. static $kex_algorithms = array(
  799. 'diffie-hellman-group1-sha1', // REQUIRED
  800. 'diffie-hellman-group14-sha1' // REQUIRED
  801. );
  802. static $server_host_key_algorithms = array(
  803. 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
  804. 'ssh-dss' // REQUIRED sign Raw DSS Key
  805. );
  806. static $encryption_algorithms = array(
  807. // from <http://tools.ietf.org/html/rfc4345#section-4>:
  808. 'arcfour256',
  809. 'arcfour128',
  810. 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
  811. 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
  812. 'aes192-cbc', // OPTIONAL AES with a 192-bit key
  813. 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
  814. // from <http://tools.ietf.org/html/rfc4344#section-4>:
  815. 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
  816. 'aes192-ctr', // RECOMMENDED AES with 192-bit key
  817. 'aes256-ctr', // RECOMMENDED AES with 256-bit key
  818. '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
  819. '3des-cbc', // REQUIRED three-key 3DES in CBC mode
  820. 'none' // OPTIONAL no encryption; NOT RECOMMENDED
  821. );
  822. static $mac_algorithms = array(
  823. 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
  824. 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
  825. 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
  826. 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
  827. 'none' // OPTIONAL no MAC; NOT RECOMMENDED
  828. );
  829. static $compression_algorithms = array(
  830. 'none' // REQUIRED no compression
  831. //'zlib' // OPTIONAL ZLIB (LZ77) compression
  832. );
  833. static $str_kex_algorithms, $str_server_host_key_algorithms,
  834. $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
  835. $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
  836. if (empty($str_kex_algorithms)) {
  837. $str_kex_algorithms = implode(',', $kex_algorithms);
  838. $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
  839. $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
  840. $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
  841. $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
  842. }
  843. $client_cookie = '';
  844. for ($i = 0; $i < 16; $i++) {
  845. $client_cookie.= chr(crypt_random(0, 255));
  846. }
  847. $response = $kexinit_payload_server;
  848. $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  849. $server_cookie = $this->_string_shift($response, 16);
  850. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  851. $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  852. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  853. $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  854. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  855. $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  856. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  857. $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  858. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  859. $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  860. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  861. $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  862. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  863. $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  864. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  865. $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  866. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  867. $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  868. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  869. $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  870. extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
  871. $first_kex_packet_follows = $first_kex_packet_follows != 0;
  872. // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
  873. $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
  874. NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
  875. strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
  876. $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
  877. strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
  878. $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
  879. strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
  880. 0, 0
  881. );
  882. if (!$this->_send_binary_packet($kexinit_payload_client)) {
  883. return false;
  884. }
  885. // here ends the second place.
  886. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  887. for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
  888. if ($i == count($encryption_algorithms)) {
  889. user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
  890. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  891. }
  892. // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  893. // diffie-hellman key exchange as fast as possible
  894. $decrypt = $encryption_algorithms[$i];
  895. switch ($decrypt) {
  896. case '3des-cbc':
  897. case '3des-ctr':
  898. $decryptKeyLength = 24; // eg. 192 / 8
  899. break;
  900. case 'aes256-cbc':
  901. case 'aes256-ctr':
  902. $decryptKeyLength = 32; // eg. 256 / 8
  903. break;
  904. case 'aes192-cbc':
  905. case 'aes192-ctr':
  906. $decryptKeyLength = 24; // eg. 192 / 8
  907. break;
  908. case 'aes128-cbc':
  909. case 'aes128-ctr':
  910. $decryptKeyLength = 16; // eg. 128 / 8
  911. break;
  912. case 'arcfour':
  913. case 'arcfour128':
  914. $decryptKeyLength = 16; // eg. 128 / 8
  915. break;
  916. case 'arcfour256':
  917. $decryptKeyLength = 32; // eg. 128 / 8
  918. break;
  919. case 'none';
  920. $decryptKeyLength = 0;
  921. }
  922. for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
  923. if ($i == count($encryption_algorithms)) {
  924. user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
  925. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  926. }
  927. $encrypt = $encryption_algorithms[$i];
  928. switch ($encrypt) {
  929. case '3des-cbc':
  930. case '3des-ctr':
  931. $encryptKeyLength = 24;
  932. break;
  933. case 'aes256-cbc':
  934. case 'aes256-ctr':
  935. $encryptKeyLength = 32;
  936. break;
  937. case 'aes192-cbc':
  938. case 'aes192-ctr':
  939. $encryptKeyLength = 24;
  940. break;
  941. case 'aes128-cbc':
  942. case 'aes128-ctr':
  943. $encryptKeyLength = 16;
  944. break;
  945. case 'arcfour':
  946. case 'arcfour128':
  947. $encryptKeyLength = 16;
  948. break;
  949. case 'arcfour256':
  950. $encryptKeyLength = 32;
  951. break;
  952. case 'none';
  953. $encryptKeyLength = 0;
  954. }
  955. $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
  956. // through diffie-hellman key exchange a symmetric key is obtained
  957. for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
  958. if ($i == count($kex_algorithms)) {
  959. user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
  960. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  961. }
  962. switch ($kex_algorithms[$i]) {
  963. // see http://tools.ietf.org/html/rfc2409#section-6.2 and
  964. // http://tools.ietf.org/html/rfc2412, appendex E
  965. case 'diffie-hellman-group1-sha1':
  966. $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  967. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  968. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  969. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
  970. $keyLength = $keyLength < 160 ? $keyLength : 160;
  971. $hash = 'sha1';
  972. break;
  973. // see http://tools.ietf.org/html/rfc3526#section-3
  974. case 'diffie-hellman-group14-sha1':
  975. $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  976. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  977. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  978. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
  979. '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
  980. '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
  981. 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
  982. '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
  983. $keyLength = $keyLength < 160 ? $keyLength : 160;
  984. $hash = 'sha1';
  985. }
  986. $p = new Math_BigInteger($p, 256);
  987. //$q = $p->bitwise_rightShift(1);
  988. /* To increase the speed of the key exchange, both client and server may
  989. reduce the size of their private exponents. It should be at least
  990. twice as long as the key material that is generated from the shared
  991. secret. For more details, see the paper by van Oorschot and Wiener
  992. [VAN-OORSCHOT].
  993. -- http://tools.ietf.org/html/rfc4419#section-6.2 */
  994. $q = new Math_BigInteger(1);
  995. $q = $q->bitwise_leftShift(2 * $keyLength);
  996. $q = $q->subtract(new Math_BigInteger(1));
  997. $g = new Math_BigInteger(2);
  998. $x = new Math_BigInteger();
  999. $x->setRandomGenerator('crypt_random');
  1000. $x = $x->random(new Math_BigInteger(1), $q);
  1001. $e = $g->modPow($x, $p);
  1002. $eBytes = $e->toBytes(true);
  1003. $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
  1004. if (!$this->_send_binary_packet($data)) {
  1005. user_error('Connection closed by server', E_USER_NOTICE);
  1006. return false;
  1007. }
  1008. $response = $this->_get_binary_packet();
  1009. if ($response === false) {
  1010. user_error('Connection closed by server', E_USER_NOTICE);
  1011. return false;
  1012. }
  1013. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1014. if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
  1015. user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
  1016. return false;
  1017. }
  1018. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1019. $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
  1020. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  1021. $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
  1022. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1023. $fBytes = $this->_string_shift($response, $temp['length']);
  1024. $f = new Math_BigInteger($fBytes, -256);
  1025. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1026. $this->signature = $this->_string_shift($response, $temp['length']);
  1027. $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
  1028. $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
  1029. $key = $f->modPow($x, $p);
  1030. $keyBytes = $key->toBytes(true);
  1031. $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
  1032. strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
  1033. strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
  1034. $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
  1035. $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
  1036. );
  1037. $this->exchange_hash = pack('H*', $hash($this->exchange_hash));
  1038. if ($this->session_id === false) {
  1039. $this->session_id = $this->exchange_hash;
  1040. }
  1041. for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
  1042. if ($i == count($server_host_key_algorithms)) {
  1043. user_error('No compatible server host key algorithms found', E_USER_NOTICE);
  1044. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1045. }
  1046. if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
  1047. user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
  1048. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1049. }
  1050. $packet = pack('C',
  1051. NET_SSH2_MSG_NEWKEYS
  1052. );
  1053. if (!$this->_send_binary_packet($packet)) {
  1054. return false;
  1055. }
  1056. $response = $this->_get_binary_packet();
  1057. if ($response === false) {
  1058. user_error('Connection closed by server', E_USER_NOTICE);
  1059. return false;
  1060. }
  1061. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1062. if ($type != NET_SSH2_MSG_NEWKEYS) {
  1063. user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
  1064. return false;
  1065. }
  1066. switch ($encrypt) {
  1067. case '3des-cbc':
  1068. $this->encrypt = new Crypt_TripleDES();
  1069. // $this->encrypt_block_size = 64 / 8 == the default
  1070. break;
  1071. case '3des-ctr':
  1072. $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  1073. // $this->encrypt_block_size = 64 / 8 == the default
  1074. break;
  1075. case 'aes256-cbc':
  1076. case 'aes192-cbc':
  1077. case 'aes128-cbc':
  1078. $this->encrypt = new Crypt_AES();
  1079. $this->encrypt_block_size = 16; // eg. 128 / 8
  1080. break;
  1081. case 'aes256-ctr':
  1082. case 'aes192-ctr':
  1083. case 'aes128-ctr':
  1084. $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
  1085. $this->encrypt_block_size = 16; // eg. 128 / 8
  1086. break;
  1087. case 'arcfour':
  1088. case 'arcfour128':
  1089. case 'arcfour256':
  1090. $this->encrypt = new Crypt_RC4();
  1091. break;
  1092. case 'none';
  1093. //$this->encrypt = new Crypt_Null();
  1094. }
  1095. switch ($decrypt) {
  1096. case '3des-cbc':
  1097. $this->decrypt = new Crypt_TripleDES();
  1098. break;
  1099. case '3des-ctr':
  1100. $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  1101. break;
  1102. case 'aes256-cbc':
  1103. case 'aes192-cbc':
  1104. case 'aes128-cbc':
  1105. $this->decrypt = new Crypt_AES();
  1106. $this->decrypt_block_size = 16;
  1107. break;
  1108. case 'aes256-ctr':
  1109. case 'aes192-ctr':
  1110. case 'aes128-ctr':
  1111. $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
  1112. $this->decrypt_block_size = 16;
  1113. break;
  1114. case 'arcfour':
  1115. case 'arcfour128':
  1116. case 'arcfour256':
  1117. $this->decrypt = new Crypt_RC4();
  1118. break;
  1119. case 'none';
  1120. //$this->decrypt = new Crypt_Null();
  1121. }
  1122. $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
  1123. if ($this->encrypt) {
  1124. $this->encrypt->enableContinuousBuffer();
  1125. $this->encrypt->disablePadding();
  1126. $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id));
  1127. while ($this->encrypt_block_size > strlen($iv)) {
  1128. $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
  1129. }
  1130. $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
  1131. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id));
  1132. while ($encryptKeyLength > strlen($key)) {
  1133. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1134. }
  1135. $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
  1136. }
  1137. if ($this->decrypt) {
  1138. $this->decrypt->enableContinuousBuffer();
  1139. $this->decrypt->disablePadding();
  1140. $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id));
  1141. while ($this->decrypt_block_size > strlen($iv)) {
  1142. $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
  1143. }
  1144. $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
  1145. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id));
  1146. while ($decryptKeyLength > strlen($key)) {
  1147. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1148. }
  1149. $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
  1150. }
  1151. /* The "arcfour128" algorithm is the RC4 cipher, as described in
  1152. [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
  1153. generated by the cipher MUST be discarded, and the first byte of the
  1154. first encrypted packet MUST be encrypted using the 1537th byte of
  1155. keystream.
  1156. -- http://tools.ietf.org/html/rfc4345#section-4 */
  1157. if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
  1158. $this->encrypt->encrypt(str_repeat("\0", 1536));
  1159. }
  1160. if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
  1161. $this->decrypt->decrypt(str_repeat("\0", 1536));
  1162. }
  1163. for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
  1164. if ($i == count($mac_algorithms)) {
  1165. user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
  1166. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1167. }
  1168. $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
  1169. switch ($mac_algorithms[$i]) {
  1170. case 'hmac-sha1':
  1171. $this->hmac_create = new Crypt_Hash('sha1');
  1172. $createKeyLength = 20;
  1173. break;
  1174. case 'hmac-sha1-96':
  1175. $this->hmac_create = new Crypt_Hash('sha1-96');
  1176. $createKeyLength = 20;
  1177. break;
  1178. case 'hmac-md5':
  1179. $this->hmac_create = new Crypt_Hash('md5');
  1180. $createKeyLength = 16;
  1181. break;
  1182. case 'hmac-md5-96':
  1183. $this->hmac_create = new Crypt_Hash('md5-96');
  1184. $createKeyLength = 16;
  1185. }
  1186. for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
  1187. if ($i == count($mac_algorithms)) {
  1188. user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
  1189. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1190. }
  1191. $checkKeyLength = 0;
  1192. $this->hmac_size = 0;
  1193. switch ($mac_algorithms[$i]) {
  1194. case 'hmac-sha1':
  1195. $this->hmac_check = new Crypt_Hash('sha1');
  1196. $checkKeyLength = 20;
  1197. $this->hmac_size = 20;
  1198. break;
  1199. case 'hmac-sha1-96':
  1200. $this->hmac_check = new Crypt_Hash('sha1-96');
  1201. $checkKeyLength = 20;
  1202. $this->hmac_size = 12;
  1203. break;
  1204. case 'hmac-md5':
  1205. $this->hmac_check = new Crypt_Hash('md5');
  1206. $checkKeyLength = 16;
  1207. $this->hmac_size = 16;
  1208. break;
  1209. case 'hmac-md5-96':
  1210. $this->hmac_check = new Crypt_Hash('md5-96');
  1211. $checkKeyLength = 16;
  1212. $this->hmac_size = 12;
  1213. }
  1214. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id));
  1215. while ($createKeyLength > strlen($key)) {
  1216. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1217. }
  1218. $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
  1219. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id));
  1220. while ($checkKeyLength > strlen($key)) {
  1221. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1222. }
  1223. $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
  1224. for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
  1225. if ($i == count($compression_algorithms)) {
  1226. user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
  1227. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1228. }
  1229. $this->decompress = $compression_algorithms[$i] == 'zlib';
  1230. for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
  1231. if ($i == count($compression_algorithms)) {
  1232. user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
  1233. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1234. }
  1235. $this->compress = $compression_algorithms[$i] == 'zlib';
  1236. return true;
  1237. }
  1238. /**
  1239. * Login
  1240. *
  1241. * The $password parameter can be a plaintext password or a Crypt_RSA object.
  1242. *
  1243. * @param String $username
  1244. * @param optional String $password
  1245. * @return Boolean
  1246. * @access public
  1247. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1248. * by sending dummy SSH_MSG_IGNORE messages.
  1249. */
  1250. function login($username, $password = '')
  1251. {
  1252. if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
  1253. return false;
  1254. }
  1255. $packet = pack('CNa*',
  1256. NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
  1257. );
  1258. if (!$this->_send_binary_packet($packet)) {
  1259. return false;
  1260. }
  1261. $response = $this->_get_binary_packet();
  1262. if ($response === false) {
  1263. user_error('Connection closed by server', E_USER_NOTICE);
  1264. return false;
  1265. }
  1266. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1267. if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
  1268. user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE);
  1269. return false;
  1270. }
  1271. // although PHP5's get_class() preserves the case, PHP4's does not
  1272. if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
  1273. return $this->_privatekey_login($username, $password);
  1274. }
  1275. $packet = pack('CNa*Na*Na*CNa*',
  1276. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1277. strlen('password'), 'password', 0, strlen($password), $password
  1278. );
  1279. if (!$this->_send_binary_packet($packet)) {
  1280. return false;
  1281. }
  1282. // remove the username and password from the last logged packet
  1283. if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  1284. $packet = pack('CNa*Na*Na*CNa*',
  1285. NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
  1286. strlen('password'), 'password', 0, strlen('password'), 'password'
  1287. );
  1288. $this->message_log[count($this->message_log) - 1] = $packet;
  1289. }
  1290. $response = $this->_get_binary_packet();
  1291. if ($response === false) {
  1292. user_error('Connection closed by server', E_USER_NOTICE);
  1293. return false;
  1294. }
  1295. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1296. switch ($type) {
  1297. case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
  1298. if (defined('NET_SSH2_LOGGING')) {
  1299. $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
  1300. }
  1301. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1302. $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
  1303. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  1304. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1305. // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
  1306. // multi-factor authentication
  1307. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1308. $auth_methods = explode(',', $this->_string_shift($response, $length));
  1309. if (in_array('keyboard-interactive', $auth_methods)) {
  1310. if ($this->_keyboard_interactive_login($username, $password)) {
  1311. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1312. return true;
  1313. }
  1314. return false;
  1315. }
  1316. return false;
  1317. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1318. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1319. return true;
  1320. }
  1321. return false;
  1322. }
  1323. /**
  1324. * Login via keyboard-interactive authentication
  1325. *
  1326. * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
  1327. *
  1328. * @param String $username
  1329. * @param String $password
  1330. * @return Boolean
  1331. * @access private
  1332. */
  1333. function _keyboard_interactive_login($username, $password)
  1334. {
  1335. $packet = pack('CNa*Na*Na*Na*Na*',
  1336. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1337. strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, ''
  1338. );
  1339. if (!$this->_send_binary_packet($packet)) {
  1340. return false;
  1341. }
  1342. return $this->_keyboard_interactive_process($password);
  1343. }
  1344. /**
  1345. * Handle the keyboard-interactive requests / responses.
  1346. *
  1347. * @param String $responses...
  1348. * @return Boolean
  1349. * @access private
  1350. */
  1351. function _keyboard_interactive_process()
  1352. {
  1353. $responses = func_get_args();
  1354. $response = $this->_get_binary_packet();
  1355. if ($response === false) {
  1356. user_error('Connection closed by server', E_USER_NOTICE);
  1357. return false;
  1358. }
  1359. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1360. switch ($type) {
  1361. case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
  1362. // see http://tools.ietf.org/html/rfc4256#section-3.2
  1363. if (defined('NET_SSH2_LOGGING')) {
  1364. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  1365. 'UNKNOWN',
  1366. 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  1367. $this->message_number_log[count($this->message_number_log) - 1]
  1368. );
  1369. }
  1370. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1371. $this->_string_shift($response, $length); // name; may be empty
  1372. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1373. $this->_string_shift($response, $length); // instruction; may be empty
  1374. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1375. $this->_string_shift($response, $length); // language tag; may be empty
  1376. extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
  1377. /*
  1378. for ($i = 0; $i < $num_prompts; $i++) {
  1379. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1380. // prompt - ie. "Password: "; must not be empty
  1381. $this->_string_shift($response, $length);
  1382. $echo = $this->_string_shift($response) != chr(0);
  1383. }
  1384. */
  1385. /*
  1386. After obtaining the requested information from the user, the client
  1387. MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
  1388. */
  1389. // see http://tools.ietf.org/html/rfc4256#section-3.4
  1390. $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
  1391. for ($i = 0; $i < count($responses); $i++) {
  1392. $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
  1393. $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
  1394. }
  1395. if (!$this->_send_binary_packet($packet)) {
  1396. return false;
  1397. }
  1398. if (defined('NET_SSH2_LOGGING')) {
  1399. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  1400. 'UNKNOWN',
  1401. 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
  1402. $this->message_number_log[count($this->message_number_log) - 1]
  1403. );
  1404. $this->message_log[count($this->message_log) - 1] = $logged;
  1405. }
  1406. /*
  1407. After receiving the response, the server MUST send either an
  1408. SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
  1409. SSH_MSG_USERAUTH_INFO_REQUEST message.
  1410. */
  1411. // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
  1412. // there could be an infinite loop of request / responses.
  1413. return $this->_keyboard_interactive_process();
  1414. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1415. return true;
  1416. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1417. return false;
  1418. }
  1419. return false;
  1420. }
  1421. /**
  1422. * Login with an RSA private key
  1423. *
  1424. * @param String $username
  1425. * @param Crypt_RSA $password
  1426. * @return Boolean
  1427. * @access private
  1428. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1429. * by sending dummy SSH_MSG_IGNORE messages.
  1430. */
  1431. function _privatekey_login($username, $privatekey)
  1432. {
  1433. // see http://tools.ietf.org/html/rfc4253#page-15
  1434. $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  1435. if ($publickey === false) {
  1436. return false;
  1437. }
  1438. $publickey = array(
  1439. 'e' => $publickey['e']->toBytes(true),
  1440. 'n' => $publickey['n']->toBytes(true)
  1441. );
  1442. $publickey = pack('Na*Na*Na*',
  1443. strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
  1444. );
  1445. $part1 = pack('CNa*Na*Na*',
  1446. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1447. strlen('publickey'), 'publickey'
  1448. );
  1449. $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
  1450. $packet = $part1 . chr(0) . $part2;
  1451. if (!$this->_send_binary_packet($packet)) {
  1452. return false;
  1453. }
  1454. $response = $this->_get_binary_packet();
  1455. if ($response === false) {
  1456. user_error('Connection closed by server', E_USER_NOTICE);
  1457. return false;
  1458. }
  1459. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1460. switch ($type) {
  1461. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1462. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1463. $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
  1464. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  1465. case NET_SSH2_MSG_USERAUTH_PK_OK:
  1466. // we'll just take it on faith that the public key blob and the public key algorithm name are as
  1467. // they should be
  1468. if (defined('NET_SSH2_LOGGING')) {
  1469. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  1470. 'UNKNOWN',
  1471. 'NET_SSH2_MSG_USERAUTH_PK_OK',
  1472. $this->message_number_log[count($this->message_number_log) - 1]
  1473. );
  1474. }
  1475. }
  1476. $packet = $part1 . chr(1) . $part2;
  1477. $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  1478. $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
  1479. $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
  1480. $packet.= pack('Na*', strlen($signature), $signature);
  1481. if (!$this->_send_binary_packet($packet)) {
  1482. return false;
  1483. }
  1484. $response = $this->_get_binary_packet();
  1485. if ($response === false) {
  1486. user_error('Connection closed by server', E_USER_NOTICE);
  1487. return false;
  1488. }
  1489. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1490. switch ($type) {
  1491. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1492. // either the login is bad or the server employees multi-factor authentication
  1493. return false;
  1494. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1495. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1496. return true;
  1497. }
  1498. return false;
  1499. }
  1500. /**
  1501. * Set Timeout
  1502. *
  1503. * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
  1504. * Setting $timeout to false or 0 will mean there is no timeout.
  1505. *
  1506. * @param Mixed $timeout
  1507. */
  1508. function setTimeout($timeout)
  1509. {
  1510. $this->timeout = $this->curTimeout = $timeout;
  1511. }
  1512. /**
  1513. * Execute Command
  1514. *
  1515. * If $block is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
  1516. * In all likelihood, this is not a feature you want to be taking advantage of.
  1517. *
  1518. * @param String $command
  1519. * @param optional Boolean $block
  1520. * @return String
  1521. * @access public
  1522. */
  1523. function exec($command, $block = true)
  1524. {
  1525. $this->curTimeout = $this->timeout;
  1526. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1527. return false;
  1528. }
  1529. // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
  1530. // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
  1531. // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
  1532. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
  1533. $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF;
  1534. // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
  1535. // uses 0x4000, that's what will be used here, as well.
  1536. $packet_size = 0x4000;
  1537. $packet = pack('CNa*N3',
  1538. NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size);
  1539. if (!$this->_send_binary_packet($packet)) {
  1540. return false;
  1541. }
  1542. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
  1543. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  1544. if ($response === false) {
  1545. return false;
  1546. }
  1547. // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
  1548. // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
  1549. // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
  1550. // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
  1551. // neither will your script.
  1552. // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
  1553. // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
  1554. // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
  1555. $packet = pack('CNNa*CNa*',
  1556. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
  1557. if (!$this->_send_binary_packet($packet)) {
  1558. return false;
  1559. }
  1560. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
  1561. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  1562. if ($response === false) {
  1563. return false;
  1564. }
  1565. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
  1566. if (!$block) {
  1567. return true;
  1568. }
  1569. $output = '';
  1570. while (true) {
  1571. $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  1572. switch (true) {
  1573. case $temp === true:
  1574. return $output;
  1575. case $temp === false:
  1576. return false;
  1577. default:
  1578. $output.= $temp;
  1579. }
  1580. }
  1581. }
  1582. /**
  1583. * Creates an interactive shell
  1584. *
  1585. * @see Net_SSH2::read()
  1586. * @see Net_SSH2::write()
  1587. * @return Boolean
  1588. * @access private
  1589. */
  1590. function _initShell()
  1591. {
  1592. $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF;
  1593. $packet_size = 0x4000;
  1594. $packet = pack('CNa*N3',
  1595. NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size);
  1596. if (!$this->_send_binary_packet($packet)) {
  1597. return false;
  1598. }
  1599. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
  1600. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  1601. if ($response === false) {
  1602. return false;
  1603. }
  1604. $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  1605. $packet = pack('CNNa*CNa*N5a*',
  1606. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
  1607. 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
  1608. if (!$this->_send_binary_packet($packet)) {
  1609. return false;
  1610. }
  1611. $response = $this->_get_binary_packet();
  1612. if ($response === false) {
  1613. user_error('Connection closed by server', E_USER_NOTICE);
  1614. return false;
  1615. }
  1616. list(, $type) = unpack('C', $this->_string_shift($response, 1));
  1617. switch ($type) {
  1618. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  1619. break;
  1620. case NET_SSH2_MSG_CHANNEL_FAILURE:
  1621. default:
  1622. user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
  1623. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1624. }
  1625. $packet = pack('CNNa*C',
  1626. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
  1627. if (!$this->_send_binary_packet($packet)) {
  1628. return false;
  1629. }
  1630. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  1631. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  1632. if ($response === false) {
  1633. return false;
  1634. }
  1635. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
  1636. $this->bitmap |= NET_SSH2_MASK_SHELL;
  1637. return true;
  1638. }
  1639. /**
  1640. * Returns the output of an interactive shell
  1641. *
  1642. * Returns when there's a match for $expect, which can take the form of a string literal or,
  1643. * if $mode == NET_SSH2_READ_REGEX, a regular expression.
  1644. *
  1645. * @see Net_SSH2::read()
  1646. * @param String $expect
  1647. * @param Integer $mode
  1648. * @return String
  1649. * @access public
  1650. */
  1651. function read($expect = '', $mode = NET_SSH2_READ_SIMPLE)
  1652. {
  1653. $this->curTimeout = $this->timeout;
  1654. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1655. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  1656. return false;
  1657. }
  1658. if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  1659. user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
  1660. return false;
  1661. }
  1662. $match = $expect;
  1663. while (true) {
  1664. if ($mode == NET_SSH2_READ_REGEX) {
  1665. preg_match($expect, $this->interactiveBuffer, $matches);
  1666. $match = $matches[0];
  1667. }
  1668. $pos = !empty($match) ? strpos($this->interactiveBuffer, $match) : false;
  1669. if ($pos !== false) {
  1670. return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
  1671. }
  1672. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  1673. if (is_bool($response)) {
  1674. return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
  1675. }
  1676. $this->interactiveBuffer.= $response;
  1677. }
  1678. }
  1679. /**
  1680. * Inputs a command into an interactive shell.
  1681. *
  1682. * @see Net_SSH1::interactiveWrite()
  1683. * @param String $cmd
  1684. * @return Boolean
  1685. * @access public
  1686. */
  1687. function write($cmd)
  1688. {
  1689. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1690. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  1691. return false;
  1692. }
  1693. if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  1694. user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
  1695. return false;
  1696. }
  1697. return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd);
  1698. }
  1699. /**
  1700. * Disconnect
  1701. *
  1702. * @access public
  1703. */
  1704. function disconnect()
  1705. {
  1706. $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1707. if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
  1708. fclose($this->realtime_log_file);
  1709. }
  1710. }
  1711. /**
  1712. * Destructor.
  1713. *
  1714. * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
  1715. * disconnect().
  1716. *
  1717. * @access public
  1718. */
  1719. function __destruct()
  1720. {
  1721. $this->disconnect();
  1722. }
  1723. /**
  1724. * Gets Binary Packets
  1725. *
  1726. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  1727. *
  1728. * @see Net_SSH2::_send_binary_packet()
  1729. * @return String
  1730. * @access private
  1731. */
  1732. function _get_binary_packet()
  1733. {
  1734. if (!is_resource($this->fsock) || feof($this->fsock)) {
  1735. user_error('Connection closed prematurely', E_USER_NOTICE);
  1736. return false;
  1737. }
  1738. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1739. $raw = fread($this->fsock, $this->decrypt_block_size);
  1740. $stop = strtok(microtime(), ' ') + strtok('');
  1741. if (empty($raw)) {
  1742. return '';
  1743. }
  1744. if ($this->decrypt !== false) {
  1745. $raw = $this->decrypt->decrypt($raw);
  1746. }
  1747. if ($raw === false) {
  1748. user_error('Unable to decrypt content', E_USER_NOTICE);
  1749. return false;
  1750. }
  1751. extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
  1752. $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
  1753. $buffer = '';
  1754. while ($remaining_length > 0) {
  1755. $temp = fread($this->fsock, $remaining_length);
  1756. $buffer.= $temp;
  1757. $remaining_length-= strlen($temp);
  1758. }
  1759. if (!empty($buffer)) {
  1760. $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
  1761. $buffer = $temp = '';
  1762. }
  1763. $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
  1764. $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
  1765. if ($this->hmac_check !== false) {
  1766. $hmac = fread($this->fsock, $this->hmac_size);
  1767. if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
  1768. user_error('Invalid HMAC', E_USER_NOTICE);
  1769. return false;
  1770. }
  1771. }
  1772. //if ($this->decompress) {
  1773. // $payload = gzinflate(substr($payload, 2));
  1774. //}
  1775. $this->get_seq_no++;
  1776. if (defined('NET_SSH2_LOGGING')) {
  1777. $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
  1778. $message_number = '<- ' . $message_number .
  1779. ' (' . round($stop - $start, 4) . 's)';
  1780. $this->_append_log($message_number, $payload);
  1781. }
  1782. return $this->_filter($payload);
  1783. }
  1784. /**
  1785. * Filter Binary Packets
  1786. *
  1787. * Because some binary packets need to be ignored...
  1788. *
  1789. * @see Net_SSH2::_get_binary_packet()
  1790. * @return String
  1791. * @access private
  1792. */
  1793. function _filter($payload)
  1794. {
  1795. switch (ord($payload[0])) {
  1796. case NET_SSH2_MSG_DISCONNECT:
  1797. $this->_string_shift($payload, 1);
  1798. extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
  1799. $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1800. $this->bitmask = 0;
  1801. return false;
  1802. case NET_SSH2_MSG_IGNORE:
  1803. $payload = $this->_get_binary_packet();
  1804. break;
  1805. case NET_SSH2_MSG_DEBUG:
  1806. $this->_string_shift($payload, 2);
  1807. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  1808. $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
  1809. $payload = $this->_get_binary_packet();
  1810. break;
  1811. case NET_SSH2_MSG_UNIMPLEMENTED:
  1812. return false;
  1813. case NET_SSH2_MSG_KEXINIT:
  1814. if ($this->session_id !== false) {
  1815. if (!$this->_key_exchange($payload)) {
  1816. $this->bitmask = 0;
  1817. return false;
  1818. }
  1819. $payload = $this->_get_binary_packet();
  1820. }
  1821. }
  1822. // 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
  1823. if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
  1824. $this->_string_shift($payload, 1);
  1825. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  1826. $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
  1827. $payload = $this->_get_binary_packet();
  1828. }
  1829. // only called when we've already logged in
  1830. if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1831. switch (ord($payload[0])) {
  1832. case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
  1833. $this->_string_shift($payload, 1);
  1834. extract(unpack('Nlength', $this->_string_shift($payload)));
  1835. $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
  1836. if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
  1837. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1838. }
  1839. $payload = $this->_get_binary_packet();
  1840. break;
  1841. case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
  1842. $this->_string_shift($payload, 1);
  1843. extract(unpack('N', $this->_string_shift($payload, 4)));
  1844. $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
  1845. $this->_string_shift($payload, 4); // skip over client channel
  1846. extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
  1847. $packet = pack('CN3a*Na*',
  1848. NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
  1849. if (!$this->_send_binary_packet($packet)) {
  1850. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1851. }
  1852. $payload = $this->_get_binary_packet();
  1853. break;
  1854. case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
  1855. $payload = $this->_get_binary_packet();
  1856. }
  1857. }
  1858. return $payload;
  1859. }
  1860. /**
  1861. * Gets channel data
  1862. *
  1863. * Returns the data as a string if it's available and false if not.
  1864. *
  1865. * @param $client_channel
  1866. * @return Mixed
  1867. * @access private
  1868. */
  1869. function _get_channel_packet($client_channel, $skip_extended = false)
  1870. {
  1871. if (!empty($this->channel_buffers[$client_channel])) {
  1872. return array_shift($this->channel_buffers[$client_channel]);
  1873. }
  1874. while (true) {
  1875. if ($this->curTimeout) {
  1876. $read = array($this->fsock);
  1877. $write = $except = NULL;
  1878. stream_set_blocking($this->fsock, false);
  1879. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1880. // on windows this returns a "Warning: Invalid CRT parameters detected" error
  1881. if (!@stream_select($read, $write, $except, $this->curTimeout)) {
  1882. stream_set_blocking($this->fsock, true);
  1883. $this->_close_channel($client_channel);
  1884. return true;
  1885. }
  1886. $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
  1887. $this->curTimeout-= $elapsed;
  1888. stream_set_blocking($this->fsock, true);
  1889. }
  1890. $response = $this->_get_binary_packet();
  1891. if ($response === false) {
  1892. user_error('Connection closed by server', E_USER_NOTICE);
  1893. return false;
  1894. }
  1895. if (empty($response)) {
  1896. return '';
  1897. }
  1898. extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
  1899. switch ($this->channel_status[$channel]) {
  1900. case NET_SSH2_MSG_CHANNEL_OPEN:
  1901. switch ($type) {
  1902. case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
  1903. extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
  1904. $this->server_channels[$channel] = $server_channel;
  1905. $this->_string_shift($response, 4); // skip over (server) window size
  1906. $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
  1907. $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
  1908. return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
  1909. //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
  1910. default:
  1911. user_error('Unable to open channel', E_USER_NOTICE);
  1912. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1913. }
  1914. break;
  1915. case NET_SSH2_MSG_CHANNEL_REQUEST:
  1916. switch ($type) {
  1917. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  1918. return true;
  1919. //case NET_SSH2_MSG_CHANNEL_FAILURE:
  1920. default:
  1921. user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
  1922. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1923. }
  1924. case NET_SSH2_MSG_CHANNEL_CLOSE:
  1925. return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
  1926. }
  1927. switch ($type) {
  1928. case NET_SSH2_MSG_CHANNEL_DATA:
  1929. /*
  1930. if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
  1931. // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
  1932. // this actually seems to make things twice as fast. more to the point, the message right after
  1933. // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
  1934. // in OpenSSH it slows things down but only by a couple thousandths of a second.
  1935. $this->_send_channel_packet($client_channel, chr(0));
  1936. }
  1937. */
  1938. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1939. $data = $this->_string_shift($response, $length);
  1940. if ($client_channel == $channel) {
  1941. return $data;
  1942. }
  1943. if (!isset($this->channel_buffers[$client_channel])) {
  1944. $this->channel_buffers[$client_channel] = array();
  1945. }
  1946. $this->channel_buffers[$client_channel][] = $data;
  1947. break;
  1948. case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  1949. if ($skip_extended) {
  1950. break;
  1951. }
  1952. /*
  1953. if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
  1954. $this->_send_channel_packet($client_channel, chr(0));
  1955. }
  1956. */
  1957. // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
  1958. extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
  1959. $data = $this->_string_shift($response, $length);
  1960. if ($client_channel == $channel) {
  1961. return $data;
  1962. }
  1963. if (!isset($this->channel_buffers[$client_channel])) {
  1964. $this->channel_buffers[$client_channel] = array();
  1965. }
  1966. $this->channel_buffers[$client_channel][] = $data;
  1967. break;
  1968. case NET_SSH2_MSG_CHANNEL_REQUEST:
  1969. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1970. $value = $this->_string_shift($response, $length);
  1971. switch ($value) {
  1972. case 'exit-signal':
  1973. $this->_string_shift($response, 1);
  1974. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1975. $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
  1976. $this->_string_shift($response, 1);
  1977. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1978. if ($length) {
  1979. $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
  1980. }
  1981. case 'exit-status':
  1982. // "The channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after this message."
  1983. // -- http://tools.ietf.org/html/rfc4254#section-6.10
  1984. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
  1985. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  1986. $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
  1987. default:
  1988. // "Some systems may not implement signals, in which case they SHOULD ignore this message."
  1989. // -- http://tools.ietf.org/html/rfc4254#section-6.9
  1990. break;
  1991. }
  1992. break;
  1993. case NET_SSH2_MSG_CHANNEL_CLOSE:
  1994. $this->curTimeout = 0;
  1995. if ($this->bitmap & NET_SSH2_MASK_SHELL) {
  1996. $this->bitmap&= ~NET_SSH2_MASK_SHELL;
  1997. }
  1998. if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
  1999. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  2000. }
  2001. $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  2002. return true;
  2003. case NET_SSH2_MSG_CHANNEL_EOF:
  2004. break;
  2005. default:
  2006. user_error('Error reading channel data', E_USER_NOTICE);
  2007. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2008. }
  2009. }
  2010. }
  2011. /**
  2012. * Sends Binary Packets
  2013. *
  2014. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  2015. *
  2016. * @param String $data
  2017. * @see Net_SSH2::_get_binary_packet()
  2018. * @return Boolean
  2019. * @access private
  2020. */
  2021. function _send_binary_packet($data)
  2022. {
  2023. if (!is_resource($this->fsock) || feof($this->fsock)) {
  2024. user_error('Connection closed prematurely', E_USER_NOTICE);
  2025. return false;
  2026. }
  2027. //if ($this->compress) {
  2028. // // the -4 removes the checksum:
  2029. // // http://php.net/function.gzcompress#57710
  2030. // $data = substr(gzcompress($data), 0, -4);
  2031. //}
  2032. // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
  2033. $packet_length = strlen($data) + 9;
  2034. // round up to the nearest $this->encrypt_block_size
  2035. $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
  2036. // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
  2037. $padding_length = $packet_length - strlen($data) - 5;
  2038. $padding = '';
  2039. for ($i = 0; $i < $padding_length; $i++) {
  2040. $padding.= chr(crypt_random(0, 255));
  2041. }
  2042. // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
  2043. $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
  2044. $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
  2045. $this->send_seq_no++;
  2046. if ($this->encrypt !== false) {
  2047. $packet = $this->encrypt->encrypt($packet);
  2048. }
  2049. $packet.= $hmac;
  2050. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  2051. $result = strlen($packet) == fputs($this->fsock, $packet);
  2052. $stop = strtok(microtime(), ' ') + strtok('');
  2053. if (defined('NET_SSH2_LOGGING')) {
  2054. $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
  2055. $message_number = '-> ' . $message_number .
  2056. ' (' . round($stop - $start, 4) . 's)';
  2057. $this->_append_log($message_number, $data);
  2058. }
  2059. return $result;
  2060. }
  2061. /**
  2062. * Logs data packets
  2063. *
  2064. * Makes sure that only the last 1MB worth of packets will be logged
  2065. *
  2066. * @param String $data
  2067. * @access private
  2068. */
  2069. function _append_log($message_number, $message)
  2070. {
  2071. switch (NET_SSH2_LOGGING) {
  2072. // useful for benchmarks
  2073. case NET_SSH2_LOG_SIMPLE:
  2074. $this->message_number_log[] = $message_number;
  2075. break;
  2076. // the most useful log for SSH2
  2077. case NET_SSH2_LOG_COMPLEX:
  2078. $this->message_number_log[] = $message_number;
  2079. $this->_string_shift($message);
  2080. $this->log_size+= strlen($message);
  2081. $this->message_log[] = $message;
  2082. while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
  2083. $this->log_size-= strlen(array_shift($this->message_log));
  2084. array_shift($this->message_number_log);
  2085. }
  2086. break;
  2087. // dump the output out realtime; packets may be interspersed with non packets,
  2088. // passwords won't be filtered out and select other packets may not be correctly
  2089. // identified
  2090. case NET_SSH2_LOG_REALTIME:
  2091. echo "<pre>\r\n" . $this->_format_log(array($message), array($message_number)) . "\r\n</pre>\r\n";
  2092. flush();
  2093. ob_flush();
  2094. break;
  2095. // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE
  2096. // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE.
  2097. // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
  2098. // at the beginning of the file
  2099. case NET_SSH2_LOG_REALTIME_FILE:
  2100. if (!isset($this->realtime_log_file)) {
  2101. // PHP doesn't seem to like using constants in fopen()
  2102. $filename = NET_SSH2_LOG_REALTIME_FILE;
  2103. $fp = fopen($filename, 'w');
  2104. $this->realtime_log_file = $fp;
  2105. }
  2106. if (!is_resource($this->realtime_log_file)) {
  2107. break;
  2108. }
  2109. $entry = $this->_format_log(array($message), array($message_number));
  2110. if ($this->realtime_log_wrap) {
  2111. $temp = "<<< START >>>\r\n";
  2112. $entry.= $temp;
  2113. fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
  2114. }
  2115. $this->realtime_log_size+= strlen($entry);
  2116. if ($this->realtime_log_size > NET_SSH2_LOG_MAX_SIZE) {
  2117. fseek($this->realtime_log_file, 0);
  2118. $this->realtime_log_size = strlen($entry);
  2119. $this->realtime_log_wrap = true;
  2120. }
  2121. fputs($this->realtime_log_file, $entry);
  2122. }
  2123. }
  2124. /**
  2125. * Sends channel data
  2126. *
  2127. * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
  2128. *
  2129. * @param Integer $client_channel
  2130. * @param String $data
  2131. * @return Boolean
  2132. * @access private
  2133. */
  2134. function _send_channel_packet($client_channel, $data)
  2135. {
  2136. while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
  2137. // resize the window, if appropriate
  2138. $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel];
  2139. if ($this->window_size_client_to_server[$client_channel] < 0) {
  2140. $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
  2141. if (!$this->_send_binary_packet($packet)) {
  2142. return false;
  2143. }
  2144. $this->window_size_client_to_server[$client_channel]+= $this->window_size;
  2145. }
  2146. $packet = pack('CN2a*',
  2147. NET_SSH2_MSG_CHANNEL_DATA,
  2148. $this->server_channels[$client_channel],
  2149. $this->packet_size_client_to_server[$client_channel],
  2150. $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
  2151. );
  2152. if (!$this->_send_binary_packet($packet)) {
  2153. return false;
  2154. }
  2155. }
  2156. // resize the window, if appropriate
  2157. $this->window_size_client_to_server[$client_channel]-= strlen($data);
  2158. if ($this->window_size_client_to_server[$client_channel] < 0) {
  2159. $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
  2160. if (!$this->_send_binary_packet($packet)) {
  2161. return false;
  2162. }
  2163. $this->window_size_client_to_server[$client_channel]+= $this->window_size;
  2164. }
  2165. return $this->_send_binary_packet(pack('CN2a*',
  2166. NET_SSH2_MSG_CHANNEL_DATA,
  2167. $this->server_channels[$client_channel],
  2168. strlen($data),
  2169. $data));
  2170. }
  2171. /**
  2172. * Closes and flushes a channel
  2173. *
  2174. * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
  2175. * and for SFTP channels are presumably closed when the client disconnects. This functions is intended
  2176. * for SCP more than anything.
  2177. *
  2178. * @param Integer $client_channel
  2179. * @return Boolean
  2180. * @access private
  2181. */
  2182. function _close_channel($client_channel)
  2183. {
  2184. // see http://tools.ietf.org/html/rfc4254#section-5.3
  2185. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
  2186. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
  2187. $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  2188. $this->curTimeout = 0;
  2189. while (!is_bool($this->_get_channel_packet($client_channel)));
  2190. if ($this->bitmap & NET_SSH2_MASK_SHELL) {
  2191. $this->bitmap&= ~NET_SSH2_MASK_SHELL;
  2192. }
  2193. }
  2194. /**
  2195. * Disconnect
  2196. *
  2197. * @param Integer $reason
  2198. * @return Boolean
  2199. * @access private
  2200. */
  2201. function _disconnect($reason)
  2202. {
  2203. if ($this->bitmap) {
  2204. $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
  2205. $this->_send_binary_packet($data);
  2206. $this->bitmap = 0;
  2207. fclose($this->fsock);
  2208. return false;
  2209. }
  2210. }
  2211. /**
  2212. * String Shift
  2213. *
  2214. * Inspired by array_shift
  2215. *
  2216. * @param String $string
  2217. * @param optional Integer $index
  2218. * @return String
  2219. * @access private
  2220. */
  2221. function _string_shift(&$string, $index = 1)
  2222. {
  2223. $substr = substr($string, 0, $index);
  2224. $string = substr($string, $index);
  2225. return $substr;
  2226. }
  2227. /**
  2228. * Define Array
  2229. *
  2230. * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
  2231. * named constants from it, using the value as the name of the constant and the index as the value of the constant.
  2232. * If any of the constants that would be defined already exists, none of the constants will be defined.
  2233. *
  2234. * @param Array $array
  2235. * @access private
  2236. */
  2237. function _define_array()
  2238. {
  2239. $args = func_get_args();
  2240. foreach ($args as $arg) {
  2241. foreach ($arg as $key=>$value) {
  2242. if (!defined($value)) {
  2243. define($value, $key);
  2244. } else {
  2245. break 2;
  2246. }
  2247. }
  2248. }
  2249. }
  2250. /**
  2251. * Returns a log of the packets that have been sent and received.
  2252. *
  2253. * 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')
  2254. *
  2255. * @access public
  2256. * @return String or Array
  2257. */
  2258. function getLog()
  2259. {
  2260. if (!defined('NET_SSH2_LOGGING')) {
  2261. return false;
  2262. }
  2263. switch (NET_SSH2_LOGGING) {
  2264. case NET_SSH2_LOG_SIMPLE:
  2265. return $this->message_number_log;
  2266. break;
  2267. case NET_SSH2_LOG_COMPLEX:
  2268. return $this->_format_log($this->message_log, $this->message_number_log);
  2269. break;
  2270. default:
  2271. return false;
  2272. }
  2273. }
  2274. /**
  2275. * Formats a log for printing
  2276. *
  2277. * @param Array $message_log
  2278. * @param Array $message_number_log
  2279. * @access private
  2280. * @return String
  2281. */
  2282. function _format_log($message_log, $message_number_log)
  2283. {
  2284. static $boundary = ':', $long_width = 65, $short_width = 16;
  2285. $output = '';
  2286. for ($i = 0; $i < count($message_log); $i++) {
  2287. $output.= $message_number_log[$i] . "\r\n";
  2288. $current_log = $message_log[$i];
  2289. $j = 0;
  2290. do {
  2291. if (!empty($current_log)) {
  2292. $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
  2293. }
  2294. $fragment = $this->_string_shift($current_log, $short_width);
  2295. $hex = substr(
  2296. preg_replace(
  2297. '#(.)#es',
  2298. '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
  2299. $fragment),
  2300. strlen($boundary)
  2301. );
  2302. // replace non ASCII printable characters with dots
  2303. // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
  2304. // also replace < with a . since < messes up the output on web browsers
  2305. $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
  2306. $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
  2307. $j++;
  2308. } while (!empty($current_log));
  2309. $output.= "\r\n";
  2310. }
  2311. return $output;
  2312. }
  2313. /**
  2314. * Returns all errors
  2315. *
  2316. * @return String
  2317. * @access public
  2318. */
  2319. function getErrors()
  2320. {
  2321. return $this->errors;
  2322. }
  2323. /**
  2324. * Returns the last error
  2325. *
  2326. * @return String
  2327. * @access public
  2328. */
  2329. function getLastError()
  2330. {
  2331. return $this->errors[count($this->errors) - 1];
  2332. }
  2333. /**
  2334. * Return the server identification.
  2335. *
  2336. * @return String
  2337. * @access public
  2338. */
  2339. function getServerIdentification()
  2340. {
  2341. return $this->server_identifier;
  2342. }
  2343. /**
  2344. * Return a list of the key exchange algorithms the server supports.
  2345. *
  2346. * @return Array
  2347. * @access public
  2348. */
  2349. function getKexAlgorithms()
  2350. {
  2351. return $this->kex_algorithms;
  2352. }
  2353. /**
  2354. * Return a list of the host key (public key) algorithms the server supports.
  2355. *
  2356. * @return Array
  2357. * @access public
  2358. */
  2359. function getServerHostKeyAlgorithms()
  2360. {
  2361. return $this->server_host_key_algorithms;
  2362. }
  2363. /**
  2364. * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
  2365. *
  2366. * @return Array
  2367. * @access public
  2368. */
  2369. function getEncryptionAlgorithmsClient2Server()
  2370. {
  2371. return $this->encryption_algorithms_client_to_server;
  2372. }
  2373. /**
  2374. * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
  2375. *
  2376. * @return Array
  2377. * @access public
  2378. */
  2379. function getEncryptionAlgorithmsServer2Client()
  2380. {
  2381. return $this->encryption_algorithms_server_to_client;
  2382. }
  2383. /**
  2384. * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
  2385. *
  2386. * @return Array
  2387. * @access public
  2388. */
  2389. function getMACAlgorithmsClient2Server()
  2390. {
  2391. return $this->mac_algorithms_client_to_server;
  2392. }
  2393. /**
  2394. * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
  2395. *
  2396. * @return Array
  2397. * @access public
  2398. */
  2399. function getMACAlgorithmsServer2Client()
  2400. {
  2401. return $this->mac_algorithms_server_to_client;
  2402. }
  2403. /**
  2404. * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
  2405. *
  2406. * @return Array
  2407. * @access public
  2408. */
  2409. function getCompressionAlgorithmsClient2Server()
  2410. {
  2411. return $this->compression_algorithms_client_to_server;
  2412. }
  2413. /**
  2414. * Return a list of the compression algorithms the server supports, when sending stuff to the client.
  2415. *
  2416. * @return Array
  2417. * @access public
  2418. */
  2419. function getCompressionAlgorithmsServer2Client()
  2420. {
  2421. return $this->compression_algorithms_server_to_client;
  2422. }
  2423. /**
  2424. * Return a list of the languages the server supports, when sending stuff to the client.
  2425. *
  2426. * @return Array
  2427. * @access public
  2428. */
  2429. function getLanguagesServer2Client()
  2430. {
  2431. return $this->languages_server_to_client;
  2432. }
  2433. /**
  2434. * Return a list of the languages the server supports, when receiving stuff from the client.
  2435. *
  2436. * @return Array
  2437. * @access public
  2438. */
  2439. function getLanguagesClient2Server()
  2440. {
  2441. return $this->languages_client_to_server;
  2442. }
  2443. /**
  2444. * Returns the server public host key.
  2445. *
  2446. * Caching this the first time you connect to a server and checking the result on subsequent connections
  2447. * is recommended. Returns false if the server signature is not signed correctly with the public host key.
  2448. *
  2449. * @return Mixed
  2450. * @access public
  2451. */
  2452. function getServerPublicHostKey()
  2453. {
  2454. $signature = $this->signature;
  2455. $server_public_host_key = $this->server_public_host_key;
  2456. extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
  2457. $this->_string_shift($server_public_host_key, $length);
  2458. if ($this->signature_validated) {
  2459. return $this->bitmap ?
  2460. $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
  2461. false;
  2462. }
  2463. $this->signature_validated = true;
  2464. switch ($this->signature_format) {
  2465. case 'ssh-dss':
  2466. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2467. $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2468. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2469. $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2470. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2471. $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2472. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2473. $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2474. /* The value for 'dss_signature_blob' is encoded as a string containing
  2475. r, followed by s (which are 160-bit integers, without lengths or
  2476. padding, unsigned, and in network byte order). */
  2477. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  2478. if ($temp['length'] != 40) {
  2479. user_error('Invalid signature', E_USER_NOTICE);
  2480. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2481. }
  2482. $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
  2483. $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
  2484. if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
  2485. user_error('Invalid signature', E_USER_NOTICE);
  2486. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2487. }
  2488. $w = $s->modInverse($q);
  2489. $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
  2490. list(, $u1) = $u1->divide($q);
  2491. $u2 = $w->multiply($r);
  2492. list(, $u2) = $u2->divide($q);
  2493. $g = $g->modPow($u1, $p);
  2494. $y = $y->modPow($u2, $p);
  2495. $v = $g->multiply($y);
  2496. list(, $v) = $v->divide($p);
  2497. list(, $v) = $v->divide($q);
  2498. if (!$v->equals($r)) {
  2499. user_error('Bad server signature', E_USER_NOTICE);
  2500. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2501. }
  2502. break;
  2503. case 'ssh-rsa':
  2504. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2505. $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2506. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2507. $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2508. $nLength = $temp['length'];
  2509. /*
  2510. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  2511. $signature = $this->_string_shift($signature, $temp['length']);
  2512. if (!class_exists('Crypt_RSA')) {
  2513. require_once('Crypt/RSA.php');
  2514. }
  2515. $rsa = new Crypt_RSA();
  2516. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  2517. $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
  2518. if (!$rsa->verify($this->exchange_hash, $signature)) {
  2519. user_error('Bad server signature', E_USER_NOTICE);
  2520. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2521. }
  2522. */
  2523. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  2524. $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
  2525. // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
  2526. // following URL:
  2527. // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
  2528. // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
  2529. if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
  2530. user_error('Invalid signature', E_USER_NOTICE);
  2531. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2532. }
  2533. $s = $s->modPow($e, $n);
  2534. $s = $s->toBytes();
  2535. $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
  2536. $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
  2537. if ($s != $h) {
  2538. user_error('Bad server signature', E_USER_NOTICE);
  2539. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2540. }
  2541. break;
  2542. default:
  2543. user_error('Unsupported signature format', E_USER_NOTICE);
  2544. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2545. }
  2546. return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
  2547. }
  2548. }