PageRenderTime 63ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/vendors/phpseclib/Net/SSH2.php

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