PageRenderTime 76ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/phpseclib/Net/SSH2.php

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