PageRenderTime 54ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Sinner/Phpseclib/Net/SSH2.php

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