/phpseclib/Net/SSH2.php

http://github.com/phpseclib/phpseclib · PHP · 5160 lines · 2879 code · 546 blank · 1735 comment · 453 complexity · b048711bdddb15f44d6466fb86e06fe4 MD5 · raw file

Large files are truncated click here to view the full file

  1. <?php
  2. /**
  3. * Pure-PHP implementation of SSHv2.
  4. *
  5. * PHP version 5
  6. *
  7. * Here are some examples of how to use this library:
  8. * <code>
  9. * <?php
  10. * include 'vendor/autoload.php';
  11. *
  12. * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
  13. * if (!$ssh->login('username', 'password')) {
  14. * exit('Login Failed');
  15. * }
  16. *
  17. * echo $ssh->exec('pwd');
  18. * echo $ssh->exec('ls -la');
  19. * ?>
  20. * </code>
  21. *
  22. * <code>
  23. * <?php
  24. * include 'vendor/autoload.php';
  25. *
  26. * $key = \phpseclib3\Crypt\PublicKeyLoader::load('...', '(optional) password');
  27. *
  28. * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
  29. * if (!$ssh->login('username', $key)) {
  30. * exit('Login Failed');
  31. * }
  32. *
  33. * echo $ssh->read('username@username:~$');
  34. * $ssh->write("ls -la\n");
  35. * echo $ssh->read('username@username:~$');
  36. * ?>
  37. * </code>
  38. *
  39. * @category Net
  40. * @package SSH2
  41. * @author Jim Wigginton <terrafrost@php.net>
  42. * @copyright 2007 Jim Wigginton
  43. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  44. * @link http://phpseclib.sourceforge.net
  45. */
  46. namespace phpseclib3\Net;
  47. use phpseclib3\Crypt\Blowfish;
  48. use phpseclib3\Crypt\Hash;
  49. use phpseclib3\Crypt\Random;
  50. use phpseclib3\Crypt\RC4;
  51. use phpseclib3\Crypt\Rijndael;
  52. use phpseclib3\Crypt\Common\PrivateKey;
  53. use phpseclib3\Crypt\RSA;
  54. use phpseclib3\Crypt\DSA;
  55. use phpseclib3\Crypt\EC;
  56. use phpseclib3\Crypt\DH;
  57. use phpseclib3\Crypt\TripleDES;
  58. use phpseclib3\Crypt\Twofish;
  59. use phpseclib3\Crypt\ChaCha20;
  60. use phpseclib3\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  61. use phpseclib3\System\SSH\Agent;
  62. use phpseclib3\System\SSH\Agent\Identity as AgentIdentity;
  63. use phpseclib3\Exception\NoSupportedAlgorithmsException;
  64. use phpseclib3\Exception\UnsupportedAlgorithmException;
  65. use phpseclib3\Exception\UnsupportedCurveException;
  66. use phpseclib3\Exception\ConnectionClosedException;
  67. use phpseclib3\Exception\UnableToConnectException;
  68. use phpseclib3\Exception\InsufficientSetupException;
  69. use phpseclib3\Common\Functions\Strings;
  70. use phpseclib3\Crypt\Common\AsymmetricKey;
  71. /**#@+
  72. * @access private
  73. */
  74. /**
  75. * No compression
  76. */
  77. define('NET_SSH2_COMPRESSION_NONE', 1);
  78. /**
  79. * zlib compression
  80. */
  81. define('NET_SSH2_COMPRESSION_ZLIB', 2);
  82. /**
  83. * zlib@openssh.com
  84. */
  85. define('NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH', 3);
  86. /**#@-*/
  87. /**
  88. * Pure-PHP implementation of SSHv2.
  89. *
  90. * @package SSH2
  91. * @author Jim Wigginton <terrafrost@php.net>
  92. * @access public
  93. */
  94. class SSH2
  95. {
  96. // Execution Bitmap Masks
  97. const MASK_CONSTRUCTOR = 0x00000001;
  98. const MASK_CONNECTED = 0x00000002;
  99. const MASK_LOGIN_REQ = 0x00000004;
  100. const MASK_LOGIN = 0x00000008;
  101. const MASK_SHELL = 0x00000010;
  102. const MASK_WINDOW_ADJUST = 0x00000020;
  103. /*
  104. * Channel constants
  105. *
  106. * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
  107. * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  108. * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  109. * recipient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  110. * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snippet:
  111. * The 'recipient channel' is the channel number given in the original
  112. * open request, and 'sender channel' is the channel number allocated by
  113. * the other side.
  114. *
  115. * @see \phpseclib3\Net\SSH2::send_channel_packet()
  116. * @see \phpseclib3\Net\SSH2::get_channel_packet()
  117. * @access private
  118. */
  119. const CHANNEL_EXEC = 1; // PuTTy uses 0x100
  120. const CHANNEL_SHELL = 2;
  121. const CHANNEL_SUBSYSTEM = 3;
  122. const CHANNEL_AGENT_FORWARD = 4;
  123. const CHANNEL_KEEP_ALIVE = 5;
  124. /**
  125. * Returns the message numbers
  126. *
  127. * @access public
  128. * @see \phpseclib3\Net\SSH2::getLog()
  129. */
  130. const LOG_SIMPLE = 1;
  131. /**
  132. * Returns the message content
  133. *
  134. * @access public
  135. * @see \phpseclib3\Net\SSH2::getLog()
  136. */
  137. const LOG_COMPLEX = 2;
  138. /**
  139. * Outputs the content real-time
  140. *
  141. * @access public
  142. * @see \phpseclib3\Net\SSH2::getLog()
  143. */
  144. const LOG_REALTIME = 3;
  145. /**
  146. * Dumps the content real-time to a file
  147. *
  148. * @access public
  149. * @see \phpseclib3\Net\SSH2::getLog()
  150. */
  151. const LOG_REALTIME_FILE = 4;
  152. /**
  153. * Make sure that the log never gets larger than this
  154. *
  155. * @access public
  156. * @see \phpseclib3\Net\SSH2::getLog()
  157. */
  158. const LOG_MAX_SIZE = 1048576; // 1024 * 1024
  159. /**
  160. * Returns when a string matching $expect exactly is found
  161. *
  162. * @access public
  163. * @see \phpseclib3\Net\SSH2::read()
  164. */
  165. const READ_SIMPLE = 1;
  166. /**
  167. * Returns when a string matching the regular expression $expect is found
  168. *
  169. * @access public
  170. * @see \phpseclib3\Net\SSH2::read()
  171. */
  172. const READ_REGEX = 2;
  173. /**
  174. * Returns whenever a data packet is received.
  175. *
  176. * Some data packets may only contain a single character so it may be necessary
  177. * to call read() multiple times when using this option
  178. *
  179. * @access public
  180. * @see \phpseclib3\Net\SSH2::read()
  181. */
  182. const READ_NEXT = 3;
  183. /**
  184. * The SSH identifier
  185. *
  186. * @var string
  187. * @access private
  188. */
  189. private $identifier;
  190. /**
  191. * The Socket Object
  192. *
  193. * @var object
  194. * @access private
  195. */
  196. public $fsock;
  197. /**
  198. * Execution Bitmap
  199. *
  200. * The bits that are set represent 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 int
  204. * @access private
  205. */
  206. protected $bitmap = 0;
  207. /**
  208. * Error information
  209. *
  210. * @see self::getErrors()
  211. * @see self::getLastError()
  212. * @var array
  213. * @access private
  214. */
  215. private $errors = [];
  216. /**
  217. * Server Identifier
  218. *
  219. * @see self::getServerIdentification()
  220. * @var array|false
  221. * @access private
  222. */
  223. protected $server_identifier = false;
  224. /**
  225. * Key Exchange Algorithms
  226. *
  227. * @see self::getKexAlgorithims()
  228. * @var array|false
  229. * @access private
  230. */
  231. private $kex_algorithms = false;
  232. /**
  233. * Key Exchange Algorithm
  234. *
  235. * @see self::getMethodsNegotiated()
  236. * @var string|false
  237. * @access private
  238. */
  239. private $kex_algorithm = false;
  240. /**
  241. * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  242. *
  243. * @see self::_key_exchange()
  244. * @var int
  245. * @access private
  246. */
  247. private $kex_dh_group_size_min = 1536;
  248. /**
  249. * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  250. *
  251. * @see self::_key_exchange()
  252. * @var int
  253. * @access private
  254. */
  255. private $kex_dh_group_size_preferred = 2048;
  256. /**
  257. * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  258. *
  259. * @see self::_key_exchange()
  260. * @var int
  261. * @access private
  262. */
  263. private $kex_dh_group_size_max = 4096;
  264. /**
  265. * Server Host Key Algorithms
  266. *
  267. * @see self::getServerHostKeyAlgorithms()
  268. * @var array|false
  269. * @access private
  270. */
  271. private $server_host_key_algorithms = false;
  272. /**
  273. * Encryption Algorithms: Client to Server
  274. *
  275. * @see self::getEncryptionAlgorithmsClient2Server()
  276. * @var array|false
  277. * @access private
  278. */
  279. private $encryption_algorithms_client_to_server = false;
  280. /**
  281. * Encryption Algorithms: Server to Client
  282. *
  283. * @see self::getEncryptionAlgorithmsServer2Client()
  284. * @var array|false
  285. * @access private
  286. */
  287. private $encryption_algorithms_server_to_client = false;
  288. /**
  289. * MAC Algorithms: Client to Server
  290. *
  291. * @see self::getMACAlgorithmsClient2Server()
  292. * @var array|false
  293. * @access private
  294. */
  295. private $mac_algorithms_client_to_server = false;
  296. /**
  297. * MAC Algorithms: Server to Client
  298. *
  299. * @see self::getMACAlgorithmsServer2Client()
  300. * @var array|false
  301. * @access private
  302. */
  303. private $mac_algorithms_server_to_client = false;
  304. /**
  305. * Compression Algorithms: Client to Server
  306. *
  307. * @see self::getCompressionAlgorithmsClient2Server()
  308. * @var array|false
  309. * @access private
  310. */
  311. private $compression_algorithms_client_to_server = false;
  312. /**
  313. * Compression Algorithms: Server to Client
  314. *
  315. * @see self::getCompressionAlgorithmsServer2Client()
  316. * @var array|false
  317. * @access private
  318. */
  319. private $compression_algorithms_server_to_client = false;
  320. /**
  321. * Languages: Server to Client
  322. *
  323. * @see self::getLanguagesServer2Client()
  324. * @var array|false
  325. * @access private
  326. */
  327. private $languages_server_to_client = false;
  328. /**
  329. * Languages: Client to Server
  330. *
  331. * @see self::getLanguagesClient2Server()
  332. * @var array|false
  333. * @access private
  334. */
  335. private $languages_client_to_server = false;
  336. /**
  337. * Preferred Algorithms
  338. *
  339. * @see self::setPreferredAlgorithms()
  340. * @var array
  341. * @access private
  342. */
  343. private $preferred = [];
  344. /**
  345. * Block Size for Server to Client Encryption
  346. *
  347. * "Note that the length of the concatenation of 'packet_length',
  348. * 'padding_length', 'payload', and 'random padding' MUST be a multiple
  349. * of the cipher block size or 8, whichever is larger. This constraint
  350. * MUST be enforced, even when using stream ciphers."
  351. *
  352. * -- http://tools.ietf.org/html/rfc4253#section-6
  353. *
  354. * @see self::__construct()
  355. * @see self::_send_binary_packet()
  356. * @var int
  357. * @access private
  358. */
  359. private $encrypt_block_size = 8;
  360. /**
  361. * Block Size for Client to Server Encryption
  362. *
  363. * @see self::__construct()
  364. * @see self::_get_binary_packet()
  365. * @var int
  366. * @access private
  367. */
  368. private $decrypt_block_size = 8;
  369. /**
  370. * Server to Client Encryption Object
  371. *
  372. * @see self::_get_binary_packet()
  373. * @var object
  374. * @access private
  375. */
  376. private $decrypt = false;
  377. /**
  378. * Server to Client Length Encryption Object
  379. *
  380. * @see self::_get_binary_packet()
  381. * @var object
  382. * @access private
  383. */
  384. private $lengthDecrypt = false;
  385. /**
  386. * Client to Server Encryption Object
  387. *
  388. * @see self::_send_binary_packet()
  389. * @var object
  390. * @access private
  391. */
  392. private $encrypt = false;
  393. /**
  394. * Client to Server Length Encryption Object
  395. *
  396. * @see self::_send_binary_packet()
  397. * @var object
  398. * @access private
  399. */
  400. private $lengthEncrypt = false;
  401. /**
  402. * Client to Server HMAC Object
  403. *
  404. * @see self::_send_binary_packet()
  405. * @var object
  406. * @access private
  407. */
  408. private $hmac_create = false;
  409. /**
  410. * Server to Client HMAC Object
  411. *
  412. * @see self::_get_binary_packet()
  413. * @var object
  414. * @access private
  415. */
  416. private $hmac_check = false;
  417. /**
  418. * Size of server to client HMAC
  419. *
  420. * 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.
  421. * 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
  422. * append it.
  423. *
  424. * @see self::_get_binary_packet()
  425. * @var int
  426. * @access private
  427. */
  428. private $hmac_size = false;
  429. /**
  430. * Server Public Host Key
  431. *
  432. * @see self::getServerPublicHostKey()
  433. * @var string
  434. * @access private
  435. */
  436. private $server_public_host_key;
  437. /**
  438. * Session identifier
  439. *
  440. * "The exchange hash H from the first key exchange is additionally
  441. * used as the session identifier, which is a unique identifier for
  442. * this connection."
  443. *
  444. * -- http://tools.ietf.org/html/rfc4253#section-7.2
  445. *
  446. * @see self::_key_exchange()
  447. * @var string
  448. * @access private
  449. */
  450. private $session_id = false;
  451. /**
  452. * Exchange hash
  453. *
  454. * The current exchange hash
  455. *
  456. * @see self::_key_exchange()
  457. * @var string
  458. * @access private
  459. */
  460. private $exchange_hash = false;
  461. /**
  462. * Message Numbers
  463. *
  464. * @see self::__construct()
  465. * @var array
  466. * @access private
  467. */
  468. private $message_numbers = [];
  469. /**
  470. * Disconnection Message 'reason codes' defined in RFC4253
  471. *
  472. * @see self::__construct()
  473. * @var array
  474. * @access private
  475. */
  476. private $disconnect_reasons = [];
  477. /**
  478. * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  479. *
  480. * @see self::__construct()
  481. * @var array
  482. * @access private
  483. */
  484. private $channel_open_failure_reasons = [];
  485. /**
  486. * Terminal Modes
  487. *
  488. * @link http://tools.ietf.org/html/rfc4254#section-8
  489. * @see self::__construct()
  490. * @var array
  491. * @access private
  492. */
  493. private $terminal_modes = [];
  494. /**
  495. * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  496. *
  497. * @link http://tools.ietf.org/html/rfc4254#section-5.2
  498. * @see self::__construct()
  499. * @var array
  500. * @access private
  501. */
  502. private $channel_extended_data_type_codes = [];
  503. /**
  504. * Send Sequence Number
  505. *
  506. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  507. *
  508. * @see self::_send_binary_packet()
  509. * @var int
  510. * @access private
  511. */
  512. private $send_seq_no = 0;
  513. /**
  514. * Get Sequence Number
  515. *
  516. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  517. *
  518. * @see self::_get_binary_packet()
  519. * @var int
  520. * @access private
  521. */
  522. private $get_seq_no = 0;
  523. /**
  524. * Server Channels
  525. *
  526. * Maps client channels to server channels
  527. *
  528. * @see self::get_channel_packet()
  529. * @see self::exec()
  530. * @var array
  531. * @access private
  532. */
  533. protected $server_channels = [];
  534. /**
  535. * Channel Buffers
  536. *
  537. * If a client requests a packet from one channel but receives two packets from another those packets should
  538. * be placed in a buffer
  539. *
  540. * @see self::get_channel_packet()
  541. * @see self::exec()
  542. * @var array
  543. * @access private
  544. */
  545. private $channel_buffers = [];
  546. /**
  547. * Channel Status
  548. *
  549. * Contains the type of the last sent message
  550. *
  551. * @see self::get_channel_packet()
  552. * @var array
  553. * @access private
  554. */
  555. protected $channel_status = [];
  556. /**
  557. * Packet Size
  558. *
  559. * Maximum packet size indexed by channel
  560. *
  561. * @see self::send_channel_packet()
  562. * @var array
  563. * @access private
  564. */
  565. private $packet_size_client_to_server = [];
  566. /**
  567. * Message Number Log
  568. *
  569. * @see self::getLog()
  570. * @var array
  571. * @access private
  572. */
  573. private $message_number_log = [];
  574. /**
  575. * Message Log
  576. *
  577. * @see self::getLog()
  578. * @var array
  579. * @access private
  580. */
  581. private $message_log = [];
  582. /**
  583. * The Window Size
  584. *
  585. * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
  586. *
  587. * @var int
  588. * @see self::send_channel_packet()
  589. * @see self::exec()
  590. * @access private
  591. */
  592. protected $window_size = 0x7FFFFFFF;
  593. /**
  594. * What we resize the window to
  595. *
  596. * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes.
  597. * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so
  598. * we'll just do what PuTTY does
  599. *
  600. * @var int
  601. * @see self::_send_channel_packet()
  602. * @see self::exec()
  603. * @access private
  604. */
  605. private $window_resize = 0x40000000;
  606. /**
  607. * Window size, server to client
  608. *
  609. * Window size indexed by channel
  610. *
  611. * @see self::send_channel_packet()
  612. * @var array
  613. * @access private
  614. */
  615. protected $window_size_server_to_client = [];
  616. /**
  617. * Window size, client to server
  618. *
  619. * Window size indexed by channel
  620. *
  621. * @see self::get_channel_packet()
  622. * @var array
  623. * @access private
  624. */
  625. private $window_size_client_to_server = [];
  626. /**
  627. * Server signature
  628. *
  629. * Verified against $this->session_id
  630. *
  631. * @see self::getServerPublicHostKey()
  632. * @var string
  633. * @access private
  634. */
  635. private $signature = '';
  636. /**
  637. * Server signature format
  638. *
  639. * ssh-rsa or ssh-dss.
  640. *
  641. * @see self::getServerPublicHostKey()
  642. * @var string
  643. * @access private
  644. */
  645. private $signature_format = '';
  646. /**
  647. * Interactive Buffer
  648. *
  649. * @see self::read()
  650. * @var array
  651. * @access private
  652. */
  653. private $interactiveBuffer = '';
  654. /**
  655. * Current log size
  656. *
  657. * Should never exceed self::LOG_MAX_SIZE
  658. *
  659. * @see self::_send_binary_packet()
  660. * @see self::_get_binary_packet()
  661. * @var int
  662. * @access private
  663. */
  664. private $log_size;
  665. /**
  666. * Timeout
  667. *
  668. * @see self::setTimeout()
  669. * @access private
  670. */
  671. protected $timeout;
  672. /**
  673. * Current Timeout
  674. *
  675. * @see self::get_channel_packet()
  676. * @access private
  677. */
  678. protected $curTimeout;
  679. /**
  680. * Keep Alive Interval
  681. *
  682. * @see self::setKeepAlive()
  683. * @access private
  684. */
  685. private $keepAlive;
  686. /**
  687. * Real-time log file pointer
  688. *
  689. * @see self::_append_log()
  690. * @var resource
  691. * @access private
  692. */
  693. private $realtime_log_file;
  694. /**
  695. * Real-time log file size
  696. *
  697. * @see self::_append_log()
  698. * @var int
  699. * @access private
  700. */
  701. private $realtime_log_size;
  702. /**
  703. * Has the signature been validated?
  704. *
  705. * @see self::getServerPublicHostKey()
  706. * @var bool
  707. * @access private
  708. */
  709. private $signature_validated = false;
  710. /**
  711. * Real-time log file wrap boolean
  712. *
  713. * @see self::_append_log()
  714. * @access private
  715. */
  716. private $realtime_log_wrap;
  717. /**
  718. * Flag to suppress stderr from output
  719. *
  720. * @see self::enableQuietMode()
  721. * @access private
  722. */
  723. private $quiet_mode = false;
  724. /**
  725. * Time of first network activity
  726. *
  727. * @var int
  728. * @access private
  729. */
  730. private $last_packet;
  731. /**
  732. * Exit status returned from ssh if any
  733. *
  734. * @var int
  735. * @access private
  736. */
  737. private $exit_status;
  738. /**
  739. * Flag to request a PTY when using exec()
  740. *
  741. * @var bool
  742. * @see self::enablePTY()
  743. * @access private
  744. */
  745. private $request_pty = false;
  746. /**
  747. * Flag set while exec() is running when using enablePTY()
  748. *
  749. * @var bool
  750. * @access private
  751. */
  752. private $in_request_pty_exec = false;
  753. /**
  754. * Flag set after startSubsystem() is called
  755. *
  756. * @var bool
  757. * @access private
  758. */
  759. private $in_subsystem;
  760. /**
  761. * Contents of stdError
  762. *
  763. * @var string
  764. * @access private
  765. */
  766. private $stdErrorLog;
  767. /**
  768. * The Last Interactive Response
  769. *
  770. * @see self::_keyboard_interactive_process()
  771. * @var string
  772. * @access private
  773. */
  774. private $last_interactive_response = '';
  775. /**
  776. * Keyboard Interactive Request / Responses
  777. *
  778. * @see self::_keyboard_interactive_process()
  779. * @var array
  780. * @access private
  781. */
  782. private $keyboard_requests_responses = [];
  783. /**
  784. * Banner Message
  785. *
  786. * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  787. * authentication may be relevant for getting legal protection."
  788. *
  789. * @see self::_filter()
  790. * @see self::getBannerMessage()
  791. * @var string
  792. * @access private
  793. */
  794. private $banner_message = '';
  795. /**
  796. * Did read() timeout or return normally?
  797. *
  798. * @see self::isTimeout()
  799. * @var bool
  800. * @access private
  801. */
  802. private $is_timeout = false;
  803. /**
  804. * Log Boundary
  805. *
  806. * @see self::_format_log()
  807. * @var string
  808. * @access private
  809. */
  810. private $log_boundary = ':';
  811. /**
  812. * Log Long Width
  813. *
  814. * @see self::_format_log()
  815. * @var int
  816. * @access private
  817. */
  818. private $log_long_width = 65;
  819. /**
  820. * Log Short Width
  821. *
  822. * @see self::_format_log()
  823. * @var int
  824. * @access private
  825. */
  826. private $log_short_width = 16;
  827. /**
  828. * Hostname
  829. *
  830. * @see self::__construct()
  831. * @see self::_connect()
  832. * @var string
  833. * @access private
  834. */
  835. private $host;
  836. /**
  837. * Port Number
  838. *
  839. * @see self::__construct()
  840. * @see self::_connect()
  841. * @var int
  842. * @access private
  843. */
  844. private $port;
  845. /**
  846. * Number of columns for terminal window size
  847. *
  848. * @see self::getWindowColumns()
  849. * @see self::setWindowColumns()
  850. * @see self::setWindowSize()
  851. * @var int
  852. * @access private
  853. */
  854. private $windowColumns = 80;
  855. /**
  856. * Number of columns for terminal window size
  857. *
  858. * @see self::getWindowRows()
  859. * @see self::setWindowRows()
  860. * @see self::setWindowSize()
  861. * @var int
  862. * @access private
  863. */
  864. private $windowRows = 24;
  865. /**
  866. * Crypto Engine
  867. *
  868. * @see self::setCryptoEngine()
  869. * @see self::_key_exchange()
  870. * @var int
  871. * @access private
  872. */
  873. private static $crypto_engine = false;
  874. /**
  875. * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
  876. *
  877. * @var \phpseclib3\System\Ssh\Agent
  878. * @access private
  879. */
  880. private $agent;
  881. /**
  882. * Connection storage to replicates ssh2 extension functionality:
  883. * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
  884. *
  885. * @var SSH2[]
  886. */
  887. private static $connections;
  888. /**
  889. * Send the identification string first?
  890. *
  891. * @var bool
  892. * @access private
  893. */
  894. private $send_id_string_first = true;
  895. /**
  896. * Send the key exchange initiation packet first?
  897. *
  898. * @var bool
  899. * @access private
  900. */
  901. private $send_kex_first = true;
  902. /**
  903. * Some versions of OpenSSH incorrectly calculate the key size
  904. *
  905. * @var bool
  906. * @access private
  907. */
  908. private $bad_key_size_fix = false;
  909. /**
  910. * Should we try to re-connect to re-establish keys?
  911. *
  912. * @var bool
  913. * @access private
  914. */
  915. private $retry_connect = false;
  916. /**
  917. * Binary Packet Buffer
  918. *
  919. * @var string|false
  920. * @access private
  921. */
  922. private $binary_packet_buffer = false;
  923. /**
  924. * Preferred Signature Format
  925. *
  926. * @var string|false
  927. * @access private
  928. */
  929. protected $preferred_signature_format = false;
  930. /**
  931. * Authentication Credentials
  932. *
  933. * @var array
  934. * @access private
  935. */
  936. protected $auth = [];
  937. /**
  938. * Terminal
  939. *
  940. * @var string
  941. * @access private
  942. */
  943. private $term = 'vt100';
  944. /**
  945. * The authentication methods that may productively continue authentication.
  946. *
  947. * @see https://tools.ietf.org/html/rfc4252#section-5.1
  948. * @var array|null
  949. * @access private
  950. */
  951. private $auth_methods_to_continue = null;
  952. /**
  953. * Compression method
  954. *
  955. * @var int
  956. * @access private
  957. */
  958. private $compress = NET_SSH2_COMPRESSION_NONE;
  959. /**
  960. * Decompression method
  961. *
  962. * @var resource|object
  963. * @access private
  964. */
  965. private $decompress = NET_SSH2_COMPRESSION_NONE;
  966. /**
  967. * Compression context
  968. *
  969. * @var int
  970. * @access private
  971. */
  972. private $compress_context;
  973. /**
  974. * Decompression context
  975. *
  976. * @var resource|object
  977. * @access private
  978. */
  979. private $decompress_context;
  980. /**
  981. * Regenerate Compression Context
  982. *
  983. * @var bool
  984. * @access private
  985. */
  986. private $regenerate_compression_context = false;
  987. /**
  988. * Regenerate Decompression Context
  989. *
  990. * @var bool
  991. * @access private
  992. */
  993. private $regenerate_decompression_context = false;
  994. /**
  995. * Smart multi-factor authentication flag
  996. *
  997. * @var bool
  998. * @access private
  999. */
  1000. private $smartMFA = true;
  1001. /**
  1002. * Default Constructor.
  1003. *
  1004. * $host can either be a string, representing the host, or a stream resource.
  1005. *
  1006. * @param mixed $host
  1007. * @param int $port
  1008. * @param int $timeout
  1009. * @see self::login()
  1010. * @return SSH2|void
  1011. * @access public
  1012. */
  1013. public function __construct($host, $port = 22, $timeout = 10)
  1014. {
  1015. $this->message_numbers = [
  1016. 1 => 'NET_SSH2_MSG_DISCONNECT',
  1017. 2 => 'NET_SSH2_MSG_IGNORE',
  1018. 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
  1019. 4 => 'NET_SSH2_MSG_DEBUG',
  1020. 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
  1021. 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  1022. 20 => 'NET_SSH2_MSG_KEXINIT',
  1023. 21 => 'NET_SSH2_MSG_NEWKEYS',
  1024. 30 => 'NET_SSH2_MSG_KEXDH_INIT',
  1025. 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  1026. 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  1027. 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  1028. 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  1029. 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  1030. 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  1031. 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  1032. 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  1033. 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  1034. 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  1035. 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  1036. 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  1037. 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  1038. 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  1039. 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  1040. 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  1041. 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  1042. 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  1043. 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  1044. ];
  1045. $this->disconnect_reasons = [
  1046. 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  1047. 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  1048. 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  1049. 4 => 'NET_SSH2_DISCONNECT_RESERVED',
  1050. 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  1051. 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  1052. 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  1053. 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  1054. 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  1055. 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  1056. 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  1057. 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  1058. 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  1059. 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  1060. 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  1061. ];
  1062. $this->channel_open_failure_reasons = [
  1063. 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  1064. ];
  1065. $this->terminal_modes = [
  1066. 0 => 'NET_SSH2_TTY_OP_END'
  1067. ];
  1068. $this->channel_extended_data_type_codes = [
  1069. 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
  1070. ];
  1071. $this->define_array(
  1072. $this->message_numbers,
  1073. $this->disconnect_reasons,
  1074. $this->channel_open_failure_reasons,
  1075. $this->terminal_modes,
  1076. $this->channel_extended_data_type_codes,
  1077. [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'],
  1078. [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'],
  1079. [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  1080. 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'],
  1081. // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
  1082. [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
  1083. 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
  1084. 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
  1085. 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
  1086. 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'],
  1087. // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
  1088. [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
  1089. 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY']
  1090. );
  1091. self::$connections[$this->getResourceId()] = class_exists('WeakReference') ? \WeakReference::create($this) : $this;
  1092. if (is_resource($host)) {
  1093. $this->fsock = $host;
  1094. return;
  1095. }
  1096. if (is_string($host)) {
  1097. $this->host = $host;
  1098. $this->port = $port;
  1099. $this->timeout = $timeout;
  1100. }
  1101. }
  1102. /**
  1103. * Set Crypto Engine Mode
  1104. *
  1105. * Possible $engine values:
  1106. * OpenSSL, mcrypt, Eval, PHP
  1107. *
  1108. * @param int $engine
  1109. * @access public
  1110. */
  1111. public static function setCryptoEngine($engine)
  1112. {
  1113. self::$crypto_engine = $engine;
  1114. }
  1115. /**
  1116. * Send Identification String First
  1117. *
  1118. * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1119. * both sides MUST send an identification string". It does not say which side sends it first. In
  1120. * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1121. *
  1122. * @access public
  1123. */
  1124. public function sendIdentificationStringFirst()
  1125. {
  1126. $this->send_id_string_first = true;
  1127. }
  1128. /**
  1129. * Send Identification String Last
  1130. *
  1131. * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1132. * both sides MUST send an identification string". It does not say which side sends it first. In
  1133. * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1134. *
  1135. * @access public
  1136. */
  1137. public function sendIdentificationStringLast()
  1138. {
  1139. $this->send_id_string_first = false;
  1140. }
  1141. /**
  1142. * Send SSH_MSG_KEXINIT First
  1143. *
  1144. * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1145. * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1146. * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1147. *
  1148. * @access public
  1149. */
  1150. public function sendKEXINITFirst()
  1151. {
  1152. $this->send_kex_first = true;
  1153. }
  1154. /**
  1155. * Send SSH_MSG_KEXINIT Last
  1156. *
  1157. * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1158. * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1159. * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1160. *
  1161. * @access public
  1162. */
  1163. public function sendKEXINITLast()
  1164. {
  1165. $this->send_kex_first = false;
  1166. }
  1167. /**
  1168. * Connect to an SSHv2 server
  1169. *
  1170. * @throws \UnexpectedValueException on receipt of unexpected packets
  1171. * @throws \RuntimeException on other errors
  1172. * @access private
  1173. */
  1174. private function connect()
  1175. {
  1176. if ($this->bitmap & self::MASK_CONSTRUCTOR) {
  1177. return;
  1178. }
  1179. $this->bitmap |= self::MASK_CONSTRUCTOR;
  1180. $this->curTimeout = $this->timeout;
  1181. $this->last_packet = microtime(true);
  1182. if (!is_resource($this->fsock)) {
  1183. $start = microtime(true);
  1184. // with stream_select a timeout of 0 means that no timeout takes place;
  1185. // with fsockopen a timeout of 0 means that you instantly timeout
  1186. // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
  1187. $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
  1188. if (!$this->fsock) {
  1189. $host = $this->host . ':' . $this->port;
  1190. throw new UnableToConnectException(rtrim("Cannot connect to $host. Error $errno. $errstr"));
  1191. }
  1192. $elapsed = microtime(true) - $start;
  1193. if ($this->curTimeout) {
  1194. $this->curTimeout-= $elapsed;
  1195. if ($this->curTimeout < 0) {
  1196. throw new \RuntimeException('Connection timed out whilst attempting to open socket connection');
  1197. }
  1198. }
  1199. }
  1200. $this->identifier = $this->generate_identifier();
  1201. if ($this->send_id_string_first) {
  1202. fputs($this->fsock, $this->identifier . "\r\n");
  1203. }
  1204. /* According to the SSH2 specs,
  1205. "The server MAY send other lines of data before sending the version
  1206. string. Each line SHOULD be terminated by a Carriage Return and Line
  1207. Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  1208. in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
  1209. MUST be able to process such lines." */
  1210. $data = '';
  1211. while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
  1212. $line = '';
  1213. while (true) {
  1214. if ($this->curTimeout) {
  1215. if ($this->curTimeout < 0) {
  1216. throw new \RuntimeException('Connection timed out whilst receiving server identification string');
  1217. }
  1218. $read = [$this->fsock];
  1219. $write = $except = null;
  1220. $start = microtime(true);
  1221. $sec = floor($this->curTimeout);
  1222. $usec = 1000000 * ($this->curTimeout - $sec);
  1223. if (@stream_select($read, $write, $except, $sec, $usec) === false) {
  1224. throw new \RuntimeException('Connection timed out whilst receiving server identification string');
  1225. }
  1226. $elapsed = microtime(true) - $start;
  1227. $this->curTimeout-= $elapsed;
  1228. }
  1229. $temp = stream_get_line($this->fsock, 255, "\n");
  1230. if ($temp === false) {
  1231. throw new \RuntimeException('Error reading from socket');
  1232. }
  1233. if (strlen($temp) == 255) {
  1234. continue;
  1235. }
  1236. $line.= "$temp\n";
  1237. // quoting RFC4253, "Implementers who wish to maintain
  1238. // compatibility with older, undocumented versions of this protocol may
  1239. // want to process the identification string without expecting the
  1240. // presence of the carriage return character for reasons described in
  1241. // Section 5 of this document."
  1242. //if (substr($line, -2) == "\r\n") {
  1243. // break;
  1244. //}
  1245. break;
  1246. }
  1247. $data.= $line;
  1248. }
  1249. if (feof($this->fsock)) {
  1250. $this->bitmap = 0;
  1251. throw new ConnectionClosedException('Connection closed by server');
  1252. }
  1253. $extra = $matches[1];
  1254. if (defined('NET_SSH2_LOGGING')) {
  1255. $this->append_log('<-', $matches[0]);
  1256. $this->append_log('->', $this->identifier . "\r\n");
  1257. }
  1258. $this->server_identifier = trim($temp, "\r\n");
  1259. if (strlen($extra)) {
  1260. $this->errors[] = $data;
  1261. }
  1262. if (version_compare($matches[3], '1.99', '<')) {
  1263. $this->bitmap = 0;
  1264. throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers");
  1265. }
  1266. if (!$this->send_id_string_first) {
  1267. fputs($this->fsock, $this->identifier . "\r\n");
  1268. }
  1269. if (!$this->send_kex_first) {
  1270. $response = $this->get_binary_packet();
  1271. if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  1272. $this->bitmap = 0;
  1273. throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
  1274. }
  1275. $this->key_exchange($response);
  1276. }
  1277. if ($this->send_kex_first) {
  1278. $this->key_exchange();
  1279. }
  1280. $this->bitmap|= self::MASK_CONNECTED;
  1281. return true;
  1282. }
  1283. /**
  1284. * Generates the SSH identifier
  1285. *
  1286. * You should overwrite this method in your own class if you want to use another identifier
  1287. *
  1288. * @access protected
  1289. * @return string
  1290. */
  1291. private function generate_identifier()
  1292. {
  1293. $identifier = 'SSH-2.0-phpseclib_3.0';
  1294. $ext = [];
  1295. if (extension_loaded('sodium')) {
  1296. $ext[] = 'libsodium';
  1297. }
  1298. if (extension_loaded('openssl')) {
  1299. $ext[] = 'openssl';
  1300. } elseif (extension_loaded('mcrypt')) {
  1301. $ext[] = 'mcrypt';
  1302. }
  1303. if (extension_loaded('gmp')) {
  1304. $ext[] = 'gmp';
  1305. } elseif (extension_loaded('bcmath')) {
  1306. $ext[] = 'bcmath';
  1307. }
  1308. if (!empty($ext)) {
  1309. $identifier .= ' (' . implode(', ', $ext) . ')';
  1310. }
  1311. return $identifier;
  1312. }
  1313. /**
  1314. * Key Exchange
  1315. *
  1316. * @return bool
  1317. * @param string|bool $kexinit_payload_server optional
  1318. * @throws \UnexpectedValueException on receipt of unexpected packets
  1319. * @throws \RuntimeException on other errors
  1320. * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
  1321. * @access private
  1322. */
  1323. private function key_exchange($kexinit_payload_server = false)
  1324. {
  1325. $preferred = $this->preferred;
  1326. $send_kex = true;
  1327. $kex_algorithms = isset($preferred['kex']) ?
  1328. $preferred['kex'] :
  1329. SSH2::getSupportedKEXAlgorithms();
  1330. $server_host_key_algorithms = isset($preferred['hostkey']) ?
  1331. $preferred['hostkey'] :
  1332. SSH2::getSupportedHostKeyAlgorithms();
  1333. $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
  1334. $preferred['server_to_client']['crypt'] :
  1335. SSH2::getSupportedEncryptionAlgorithms();
  1336. $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
  1337. $preferred['client_to_server']['crypt'] :
  1338. SSH2::getSupportedEncryptionAlgorithms();
  1339. $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
  1340. $preferred['server_to_client']['mac'] :
  1341. SSH2::getSupportedMACAlgorithms();
  1342. $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
  1343. $preferred['client_to_server']['mac'] :
  1344. SSH2::getSupportedMACAlgorithms();
  1345. $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
  1346. $preferred['server_to_client']['comp'] :
  1347. SSH2::getSupportedCompressionAlgorithms();
  1348. $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
  1349. $preferred['client_to_server']['comp'] :
  1350. SSH2::getSupportedCompressionAlgorithms();
  1351. // some SSH servers have buggy implementations of some of the above algorithms
  1352. switch (true) {
  1353. case $this->server_identifier == 'SSH-2.0-SSHD':
  1354. case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
  1355. if (!isset($preferred['server_to_client']['mac'])) {
  1356. $s2c_mac_algorithms = array_values(array_diff(
  1357. $s2c_mac_algorithms,
  1358. ['hmac-sha1-96', 'hmac-md5-96']
  1359. ));
  1360. }
  1361. if (!isset($preferred['client_to_server']['mac'])) {
  1362. $c2s_mac_algorithms = array_values(array_diff(
  1363. $c2s_mac_algorithms,
  1364. ['hmac-sha1-96', 'hmac-md5-96']
  1365. ));
  1366. }
  1367. }
  1368. $client_cookie = Random::string(16);
  1369. $kexinit_payload_client = pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie);
  1370. $kexinit_payload_client.= Strings::packSSH2(
  1371. 'L10bN',
  1372. $kex_algorithms,
  1373. $server_host_key_algorithms,
  1374. $c2s_encryption_algorithms,
  1375. $s2c_encryption_algorithms,
  1376. $c2s_mac_algorithms,
  1377. $s2c_mac_algorithms,
  1378. $c2s_compression_algorithms,
  1379. $s2c_compression_algorithms,
  1380. [], // language, client to server
  1381. [], // language, server to client
  1382. false, // first_kex_packet_follows
  1383. 0 // reserved for future extension
  1384. );
  1385. if ($kexinit_payload_server === false) {
  1386. $this->send_binary_packet($kexinit_payload_client);
  1387. $kexinit_payload_server = $this->get_binary_packet();
  1388. if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
  1389. $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  1390. throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
  1391. }
  1392. $send_kex = false;
  1393. }
  1394. $response = $kexinit_payload_server;
  1395. Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  1396. $server_cookie = Strings::shift($response, 16);
  1397. list(
  1398. $this->kex_algorithms,
  1399. $this->server_host_key_algorithms,
  1400. $this->encryption_algorithms_client_to_server,
  1401. $this->encryption_algorithms_server_to_client,
  1402. $this->mac_algorithms_client_to_server,
  1403. $this->mac_algorithms_server_to_client,
  1404. $this->compression_algorithms_client_to_server,
  1405. $this->compression_algorithms_server_to_client,
  1406. $this->languages_client_to_server,
  1407. $this->languages_server_to_client,
  1408. $first_kex_packet_follows
  1409. ) = Strings::unpackSSH2('L10C', $response);
  1410. if ($send_kex) {
  1411. $this->send_binary_packet($kexinit_payload_client);
  1412. }
  1413. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  1414. // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  1415. // diffie-hellman key exchange as fast as possible
  1416. $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
  1417. $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt);
  1418. if ($decryptKeyLength === null) {
  1419. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1420. throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
  1421. }
  1422. $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
  1423. $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt);
  1424. if ($encryptKeyLength === null) {
  1425. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1426. throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
  1427. }
  1428. // through diffie-hellman key exchange a symmetric key is obtained
  1429. $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms);
  1430. if ($this->kex_algorithm === false) {
  1431. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1432. throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
  1433. }
  1434. $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
  1435. if ($server_host_key_algorithm === false) {
  1436. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1437. throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
  1438. }
  1439. $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
  1440. if ($mac_algorithm_out === false) {
  1441. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1442. throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
  1443. }
  1444. $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
  1445. if ($mac_algorithm_in === false) {
  1446. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1447. throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
  1448. }
  1449. $compression_map = [
  1450. 'none' => NET_SSH2_COMPRESSION_NONE,
  1451. 'zlib' => NET_SSH2_COMPRESSION_ZLIB,
  1452. 'zlib@openssh.com' => NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
  1453. ];
  1454. $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
  1455. if ($compression_algorithm_in === false) {
  1456. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1457. throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
  1458. }
  1459. $this->decompress = $compression_map[$compression_algorithm_in];
  1460. $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
  1461. if ($compression_algorithm_out === false) {
  1462. $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1463. throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
  1464. }
  1465. $this->compress = $compression_map[$compression_algorithm_out];
  1466. switch ($this->kex_algorithm) {
  1467. case 'diffie-hellman-group15-sha512':
  1468. case 'diffie-hellman-group16-sha512':
  1469. case 'diffie-hellman-group17-sha512':
  1470. case 'diffie-hellman-group18-sha512':
  1471. case 'ecdh-sha2-nistp521':
  1472. $kexHash = new Hash('sha512');
  1473. break;
  1474. case 'ecdh-sha2-nistp384':
  1475. $kexHash = new Hash('sha384');
  1476. break;
  1477. case 'diffie-hellman-group-exchange-sha256':
  1478. case 'diffie-hellman-group14-sha256':
  1479. case 'ecdh-sha2-nistp256':
  1480. case 'curve25519-sha256@libssh.org':
  1481. case 'curve25519-sha256':
  1482. $kexHash = new Hash('sha256');
  1483. break;
  1484. default:
  1485. $kexHash = new Hash('sha1');
  1486. }
  1487. // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
  1488. $exchange_hash_rfc4419 = '';
  1489. if (strpos($this->kex_algorithm, 'curve25519-sha256') === 0 || strpos($this->kex_algorithm, 'ecdh-sha2-nistp') === 0) {
  1490. $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ?
  1491. 'Curve25519' :
  1492. substr($this->kex_algorithm, 10);
  1493. $ourPrivate = EC::createKey($curve);
  1494. $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
  1495. $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
  1496. $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY';
  1497. } else {
  1498. if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
  1499. $dh_group_sizes_packed = pack(
  1500. 'NNN',
  1501. $this->kex_dh_group_size_min,
  1502. $this->kex_dh_group_size_preferred,
  1503. $this->kex_dh_group_size_max
  1504. );
  1505. $packet = pack(
  1506. 'Ca*',
  1507. NET_SSH2_MSG_KEXDH_GEX_REQUEST,
  1508. $dh_group_sizes_packed
  1509. );
  1510. $this->send_binary_packet($packet);
  1511. $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
  1512. $response = $this->get_binary_packet();
  1513. list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response);
  1514. if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
  1515. $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
  1516. throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP');
  1517. }
  1518. $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
  1519. $prime = new BigInteger($primeBytes, -256);
  1520. $g = new BigInteger($gBytes, -256);
  1521. $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2(
  1522. 'ss',
  1523. $primeBytes,
  1524. $gBytes
  1525. );
  1526. $params = DH::createParameters($prime, $g);
  1527. $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT';
  1528. $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY';
  1529. } else {
  1530. $params = DH::createParameters($this->kex_algorithm);
  1531. $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT';
  1532. $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY';
  1533. }
  1534. $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLen