[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Net/ -> SSH2.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP implementation of SSHv2.
   5   *
   6   * PHP version 5
   7   *
   8   * Here are some examples of how to use this library:
   9   * <code>
  10   * <?php
  11   *    include 'vendor/autoload.php';
  12   *
  13   *    $ssh = new \phpseclib\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 'vendor/autoload.php';
  26   *
  27   *    $key = new \phpseclib\Crypt\RSA();
  28   *    //$key->setPassword('whatever');
  29   *    $key->loadKey(file_get_contents('privatekey'));
  30   *
  31   *    $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
  32   *    if (!$ssh->login('username', $key)) {
  33   *        exit('Login Failed');
  34   *    }
  35   *
  36   *    echo $ssh->read('username@username:~$');
  37   *    $ssh->write("ls -la\n");
  38   *    echo $ssh->read('username@username:~$');
  39   * ?>
  40   * </code>
  41   *
  42   * @category  Net
  43   * @package   SSH2
  44   * @author    Jim Wigginton <terrafrost@php.net>
  45   * @copyright 2007 Jim Wigginton
  46   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  47   * @link      http://phpseclib.sourceforge.net
  48   */
  49  
  50  namespace phpseclib\Net;
  51  
  52  use phpseclib\Crypt\Base;
  53  use phpseclib\Crypt\Blowfish;
  54  use phpseclib\Crypt\Hash;
  55  use phpseclib\Crypt\Random;
  56  use phpseclib\Crypt\RC4;
  57  use phpseclib\Crypt\Rijndael;
  58  use phpseclib\Crypt\RSA;
  59  use phpseclib\Crypt\TripleDES;
  60  use phpseclib\Crypt\Twofish;
  61  use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  62  use phpseclib\System\SSH\Agent;
  63  
  64  /**
  65   * Pure-PHP implementation of SSHv2.
  66   *
  67   * @package SSH2
  68   * @author  Jim Wigginton <terrafrost@php.net>
  69   * @access  public
  70   */
  71  class SSH2
  72  {
  73      /**#@+
  74       * Execution Bitmap Masks
  75       *
  76       * @see \phpseclib\Net\SSH2::bitmap
  77       * @access private
  78       */
  79      const MASK_CONSTRUCTOR   = 0x00000001;
  80      const MASK_CONNECTED     = 0x00000002;
  81      const MASK_LOGIN_REQ     = 0x00000004;
  82      const MASK_LOGIN         = 0x00000008;
  83      const MASK_SHELL         = 0x00000010;
  84      const MASK_WINDOW_ADJUST = 0x00000020;
  85      /**#@-*/
  86  
  87      /**#@+
  88       * Channel constants
  89       *
  90       * RFC4254 refers not to client and server channels but rather to sender and recipient channels.  we don't refer
  91       * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  92       * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  93       * recepient channel.  at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  94       * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  95       *     The 'recipient channel' is the channel number given in the original
  96       *     open request, and 'sender channel' is the channel number allocated by
  97       *     the other side.
  98       *
  99       * @see \phpseclib\Net\SSH2::_send_channel_packet()
 100       * @see \phpseclib\Net\SSH2::_get_channel_packet()
 101       * @access private
 102      */
 103      const CHANNEL_EXEC          = 1; // PuTTy uses 0x100
 104      const CHANNEL_SHELL         = 2;
 105      const CHANNEL_SUBSYSTEM     = 3;
 106      const CHANNEL_AGENT_FORWARD = 4;
 107      const CHANNEL_KEEP_ALIVE    = 5;
 108      /**#@-*/
 109  
 110      /**#@+
 111       * @access public
 112       * @see \phpseclib\Net\SSH2::getLog()
 113      */
 114      /**
 115       * Returns the message numbers
 116       */
 117      const LOG_SIMPLE = 1;
 118      /**
 119       * Returns the message content
 120       */
 121      const LOG_COMPLEX = 2;
 122      /**
 123       * Outputs the content real-time
 124       */
 125      const LOG_REALTIME = 3;
 126      /**
 127       * Dumps the content real-time to a file
 128       */
 129      const LOG_REALTIME_FILE = 4;
 130      /**
 131       * Make sure that the log never gets larger than this
 132       */
 133      const LOG_MAX_SIZE = 1048576; // 1024 * 1024
 134      /**#@-*/
 135  
 136      /**#@+
 137       * @access public
 138       * @see \phpseclib\Net\SSH2::read()
 139      */
 140      /**
 141       * Returns when a string matching $expect exactly is found
 142       */
 143      const READ_SIMPLE = 1;
 144      /**
 145       * Returns when a string matching the regular expression $expect is found
 146       */
 147      const READ_REGEX = 2;
 148      /**
 149       * Returns whenever a data packet is received.
 150       *
 151       * Some data packets may only contain a single character so it may be necessary
 152       * to call read() multiple times when using this option
 153       */
 154      const READ_NEXT = 3;
 155      /**#@-*/
 156  
 157      /**
 158       * The SSH identifier
 159       *
 160       * @var string
 161       * @access private
 162       */
 163      var $identifier;
 164  
 165      /**
 166       * The Socket Object
 167       *
 168       * @var object
 169       * @access private
 170       */
 171      var $fsock;
 172  
 173      /**
 174       * Execution Bitmap
 175       *
 176       * The bits that are set represent functions that have been called already.  This is used to determine
 177       * if a requisite function has been successfully executed.  If not, an error should be thrown.
 178       *
 179       * @var int
 180       * @access private
 181       */
 182      var $bitmap = 0;
 183  
 184      /**
 185       * Error information
 186       *
 187       * @see self::getErrors()
 188       * @see self::getLastError()
 189       * @var string
 190       * @access private
 191       */
 192      var $errors = array();
 193  
 194      /**
 195       * Server Identifier
 196       *
 197       * @see self::getServerIdentification()
 198       * @var array|false
 199       * @access private
 200       */
 201      var $server_identifier = false;
 202  
 203      /**
 204       * Key Exchange Algorithms
 205       *
 206       * @see self::getKexAlgorithims()
 207       * @var array|false
 208       * @access private
 209       */
 210      var $kex_algorithms = false;
 211  
 212      /**
 213       * Key Exchange Algorithm
 214       *
 215       * @see self::getMethodsNegotiated()
 216       * @var string|false
 217       * @access private
 218       */
 219      var $kex_algorithm = false;
 220  
 221      /**
 222       * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
 223       *
 224       * @see self::_key_exchange()
 225       * @var int
 226       * @access private
 227       */
 228      var $kex_dh_group_size_min = 1536;
 229  
 230      /**
 231       * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
 232       *
 233       * @see self::_key_exchange()
 234       * @var int
 235       * @access private
 236       */
 237      var $kex_dh_group_size_preferred = 2048;
 238  
 239      /**
 240       * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
 241       *
 242       * @see self::_key_exchange()
 243       * @var int
 244       * @access private
 245       */
 246      var $kex_dh_group_size_max = 4096;
 247  
 248      /**
 249       * Server Host Key Algorithms
 250       *
 251       * @see self::getServerHostKeyAlgorithms()
 252       * @var array|false
 253       * @access private
 254       */
 255      var $server_host_key_algorithms = false;
 256  
 257      /**
 258       * Encryption Algorithms: Client to Server
 259       *
 260       * @see self::getEncryptionAlgorithmsClient2Server()
 261       * @var array|false
 262       * @access private
 263       */
 264      var $encryption_algorithms_client_to_server = false;
 265  
 266      /**
 267       * Encryption Algorithms: Server to Client
 268       *
 269       * @see self::getEncryptionAlgorithmsServer2Client()
 270       * @var array|false
 271       * @access private
 272       */
 273      var $encryption_algorithms_server_to_client = false;
 274  
 275      /**
 276       * MAC Algorithms: Client to Server
 277       *
 278       * @see self::getMACAlgorithmsClient2Server()
 279       * @var array|false
 280       * @access private
 281       */
 282      var $mac_algorithms_client_to_server = false;
 283  
 284      /**
 285       * MAC Algorithms: Server to Client
 286       *
 287       * @see self::getMACAlgorithmsServer2Client()
 288       * @var array|false
 289       * @access private
 290       */
 291      var $mac_algorithms_server_to_client = false;
 292  
 293      /**
 294       * Compression Algorithms: Client to Server
 295       *
 296       * @see self::getCompressionAlgorithmsClient2Server()
 297       * @var array|false
 298       * @access private
 299       */
 300      var $compression_algorithms_client_to_server = false;
 301  
 302      /**
 303       * Compression Algorithms: Server to Client
 304       *
 305       * @see self::getCompressionAlgorithmsServer2Client()
 306       * @var array|false
 307       * @access private
 308       */
 309      var $compression_algorithms_server_to_client = false;
 310  
 311      /**
 312       * Languages: Server to Client
 313       *
 314       * @see self::getLanguagesServer2Client()
 315       * @var array|false
 316       * @access private
 317       */
 318      var $languages_server_to_client = false;
 319  
 320      /**
 321       * Languages: Client to Server
 322       *
 323       * @see self::getLanguagesClient2Server()
 324       * @var array|false
 325       * @access private
 326       */
 327      var $languages_client_to_server = false;
 328  
 329      /**
 330       * Preferred Algorithms
 331       *
 332       * @see self::setPreferredAlgorithms()
 333       * @var array
 334       * @access private
 335       */
 336      var $preferred = array();
 337  
 338      /**
 339       * Block Size for Server to Client Encryption
 340       *
 341       * "Note that the length of the concatenation of 'packet_length',
 342       *  'padding_length', 'payload', and 'random padding' MUST be a multiple
 343       *  of the cipher block size or 8, whichever is larger.  This constraint
 344       *  MUST be enforced, even when using stream ciphers."
 345       *
 346       *  -- http://tools.ietf.org/html/rfc4253#section-6
 347       *
 348       * @see self::__construct()
 349       * @see self::_send_binary_packet()
 350       * @var int
 351       * @access private
 352       */
 353      var $encrypt_block_size = 8;
 354  
 355      /**
 356       * Block Size for Client to Server Encryption
 357       *
 358       * @see self::__construct()
 359       * @see self::_get_binary_packet()
 360       * @var int
 361       * @access private
 362       */
 363      var $decrypt_block_size = 8;
 364  
 365      /**
 366       * Server to Client Encryption Object
 367       *
 368       * @see self::_get_binary_packet()
 369       * @var object
 370       * @access private
 371       */
 372      var $decrypt = false;
 373  
 374      /**
 375       * Client to Server Encryption Object
 376       *
 377       * @see self::_send_binary_packet()
 378       * @var object
 379       * @access private
 380       */
 381      var $encrypt = false;
 382  
 383      /**
 384       * Client to Server HMAC Object
 385       *
 386       * @see self::_send_binary_packet()
 387       * @var object
 388       * @access private
 389       */
 390      var $hmac_create = false;
 391  
 392      /**
 393       * Server to Client HMAC Object
 394       *
 395       * @see self::_get_binary_packet()
 396       * @var object
 397       * @access private
 398       */
 399      var $hmac_check = false;
 400  
 401      /**
 402       * Size of server to client HMAC
 403       *
 404       * 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.
 405       * 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
 406       * append it.
 407       *
 408       * @see self::_get_binary_packet()
 409       * @var int
 410       * @access private
 411       */
 412      var $hmac_size = false;
 413  
 414      /**
 415       * Server Public Host Key
 416       *
 417       * @see self::getServerPublicHostKey()
 418       * @var string
 419       * @access private
 420       */
 421      var $server_public_host_key;
 422  
 423      /**
 424       * Session identifier
 425       *
 426       * "The exchange hash H from the first key exchange is additionally
 427       *  used as the session identifier, which is a unique identifier for
 428       *  this connection."
 429       *
 430       *  -- http://tools.ietf.org/html/rfc4253#section-7.2
 431       *
 432       * @see self::_key_exchange()
 433       * @var string
 434       * @access private
 435       */
 436      var $session_id = false;
 437  
 438      /**
 439       * Exchange hash
 440       *
 441       * The current exchange hash
 442       *
 443       * @see self::_key_exchange()
 444       * @var string
 445       * @access private
 446       */
 447      var $exchange_hash = false;
 448  
 449      /**
 450       * Message Numbers
 451       *
 452       * @see self::__construct()
 453       * @var array
 454       * @access private
 455       */
 456      var $message_numbers = array();
 457  
 458      /**
 459       * Disconnection Message 'reason codes' defined in RFC4253
 460       *
 461       * @see self::__construct()
 462       * @var array
 463       * @access private
 464       */
 465      var $disconnect_reasons = array();
 466  
 467      /**
 468       * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
 469       *
 470       * @see self::__construct()
 471       * @var array
 472       * @access private
 473       */
 474      var $channel_open_failure_reasons = array();
 475  
 476      /**
 477       * Terminal Modes
 478       *
 479       * @link http://tools.ietf.org/html/rfc4254#section-8
 480       * @see self::__construct()
 481       * @var array
 482       * @access private
 483       */
 484      var $terminal_modes = array();
 485  
 486      /**
 487       * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
 488       *
 489       * @link http://tools.ietf.org/html/rfc4254#section-5.2
 490       * @see self::__construct()
 491       * @var array
 492       * @access private
 493       */
 494      var $channel_extended_data_type_codes = array();
 495  
 496      /**
 497       * Send Sequence Number
 498       *
 499       * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
 500       *
 501       * @see self::_send_binary_packet()
 502       * @var int
 503       * @access private
 504       */
 505      var $send_seq_no = 0;
 506  
 507      /**
 508       * Get Sequence Number
 509       *
 510       * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
 511       *
 512       * @see self::_get_binary_packet()
 513       * @var int
 514       * @access private
 515       */
 516      var $get_seq_no = 0;
 517  
 518      /**
 519       * Server Channels
 520       *
 521       * Maps client channels to server channels
 522       *
 523       * @see self::_get_channel_packet()
 524       * @see self::exec()
 525       * @var array
 526       * @access private
 527       */
 528      var $server_channels = array();
 529  
 530      /**
 531       * Channel Buffers
 532       *
 533       * If a client requests a packet from one channel but receives two packets from another those packets should
 534       * be placed in a buffer
 535       *
 536       * @see self::_get_channel_packet()
 537       * @see self::exec()
 538       * @var array
 539       * @access private
 540       */
 541      var $channel_buffers = array();
 542  
 543      /**
 544       * Channel Status
 545       *
 546       * Contains the type of the last sent message
 547       *
 548       * @see self::_get_channel_packet()
 549       * @var array
 550       * @access private
 551       */
 552      var $channel_status = array();
 553  
 554      /**
 555       * Packet Size
 556       *
 557       * Maximum packet size indexed by channel
 558       *
 559       * @see self::_send_channel_packet()
 560       * @var array
 561       * @access private
 562       */
 563      var $packet_size_client_to_server = array();
 564  
 565      /**
 566       * Message Number Log
 567       *
 568       * @see self::getLog()
 569       * @var array
 570       * @access private
 571       */
 572      var $message_number_log = array();
 573  
 574      /**
 575       * Message Log
 576       *
 577       * @see self::getLog()
 578       * @var array
 579       * @access private
 580       */
 581      var $message_log = array();
 582  
 583      /**
 584       * The Window Size
 585       *
 586       * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
 587       *
 588       * @var int
 589       * @see self::_send_channel_packet()
 590       * @see self::exec()
 591       * @access private
 592       */
 593      var $window_size = 0x7FFFFFFF;
 594  
 595      /**
 596       * What we resize the window to
 597       *
 598       * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes.
 599       * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so
 600       * we'll just do what PuTTY does
 601       *
 602       * @var int
 603       * @see self::_send_channel_packet()
 604       * @see self::exec()
 605       * @access private
 606       */
 607      var $window_resize = 0x40000000;
 608  
 609      /**
 610       * Window size, server to client
 611       *
 612       * Window size indexed by channel
 613       *
 614       * @see self::_send_channel_packet()
 615       * @var array
 616       * @access private
 617       */
 618      var $window_size_server_to_client = array();
 619  
 620      /**
 621       * Window size, client to server
 622       *
 623       * Window size indexed by channel
 624       *
 625       * @see self::_get_channel_packet()
 626       * @var array
 627       * @access private
 628       */
 629      var $window_size_client_to_server = array();
 630  
 631      /**
 632       * Server signature
 633       *
 634       * Verified against $this->session_id
 635       *
 636       * @see self::getServerPublicHostKey()
 637       * @var string
 638       * @access private
 639       */
 640      var $signature = '';
 641  
 642      /**
 643       * Server signature format
 644       *
 645       * ssh-rsa or ssh-dss.
 646       *
 647       * @see self::getServerPublicHostKey()
 648       * @var string
 649       * @access private
 650       */
 651      var $signature_format = '';
 652  
 653      /**
 654       * Interactive Buffer
 655       *
 656       * @see self::read()
 657       * @var array
 658       * @access private
 659       */
 660      var $interactiveBuffer = '';
 661  
 662      /**
 663       * Current log size
 664       *
 665       * Should never exceed self::LOG_MAX_SIZE
 666       *
 667       * @see self::_send_binary_packet()
 668       * @see self::_get_binary_packet()
 669       * @var int
 670       * @access private
 671       */
 672      var $log_size;
 673  
 674      /**
 675       * Timeout
 676       *
 677       * @see self::setTimeout()
 678       * @access private
 679       */
 680      var $timeout;
 681  
 682      /**
 683       * Current Timeout
 684       *
 685       * @see self::_get_channel_packet()
 686       * @access private
 687       */
 688      var $curTimeout;
 689  
 690      /**
 691       * Real-time log file pointer
 692       *
 693       * @see self::_append_log()
 694       * @var resource
 695       * @access private
 696       */
 697      var $realtime_log_file;
 698  
 699      /**
 700       * Real-time log file size
 701       *
 702       * @see self::_append_log()
 703       * @var int
 704       * @access private
 705       */
 706      var $realtime_log_size;
 707  
 708      /**
 709       * Has the signature been validated?
 710       *
 711       * @see self::getServerPublicHostKey()
 712       * @var bool
 713       * @access private
 714       */
 715      var $signature_validated = false;
 716  
 717      /**
 718       * Real-time log file wrap boolean
 719       *
 720       * @see self::_append_log()
 721       * @access private
 722       */
 723      var $realtime_log_wrap;
 724  
 725      /**
 726       * Flag to suppress stderr from output
 727       *
 728       * @see self::enableQuietMode()
 729       * @access private
 730       */
 731      var $quiet_mode = false;
 732  
 733      /**
 734       * Time of first network activity
 735       *
 736       * @var int
 737       * @access private
 738       */
 739      var $last_packet;
 740  
 741      /**
 742       * Exit status returned from ssh if any
 743       *
 744       * @var int
 745       * @access private
 746       */
 747      var $exit_status;
 748  
 749      /**
 750       * Flag to request a PTY when using exec()
 751       *
 752       * @var bool
 753       * @see self::enablePTY()
 754       * @access private
 755       */
 756      var $request_pty = false;
 757  
 758      /**
 759       * Flag set while exec() is running when using enablePTY()
 760       *
 761       * @var bool
 762       * @access private
 763       */
 764      var $in_request_pty_exec = false;
 765  
 766      /**
 767       * Flag set after startSubsystem() is called
 768       *
 769       * @var bool
 770       * @access private
 771       */
 772      var $in_subsystem;
 773  
 774      /**
 775       * Contents of stdError
 776       *
 777       * @var string
 778       * @access private
 779       */
 780      var $stdErrorLog;
 781  
 782      /**
 783       * The Last Interactive Response
 784       *
 785       * @see self::_keyboard_interactive_process()
 786       * @var string
 787       * @access private
 788       */
 789      var $last_interactive_response = '';
 790  
 791      /**
 792       * Keyboard Interactive Request / Responses
 793       *
 794       * @see self::_keyboard_interactive_process()
 795       * @var array
 796       * @access private
 797       */
 798      var $keyboard_requests_responses = array();
 799  
 800      /**
 801       * Banner Message
 802       *
 803       * Quoting from the RFC, "in some jurisdictions, sending a warning message before
 804       * authentication may be relevant for getting legal protection."
 805       *
 806       * @see self::_filter()
 807       * @see self::getBannerMessage()
 808       * @var string
 809       * @access private
 810       */
 811      var $banner_message = '';
 812  
 813      /**
 814       * Did read() timeout or return normally?
 815       *
 816       * @see self::isTimeout()
 817       * @var bool
 818       * @access private
 819       */
 820      var $is_timeout = false;
 821  
 822      /**
 823       * Log Boundary
 824       *
 825       * @see self::_format_log()
 826       * @var string
 827       * @access private
 828       */
 829      var $log_boundary = ':';
 830  
 831      /**
 832       * Log Long Width
 833       *
 834       * @see self::_format_log()
 835       * @var int
 836       * @access private
 837       */
 838      var $log_long_width = 65;
 839  
 840      /**
 841       * Log Short Width
 842       *
 843       * @see self::_format_log()
 844       * @var int
 845       * @access private
 846       */
 847      var $log_short_width = 16;
 848  
 849      /**
 850       * Hostname
 851       *
 852       * @see self::__construct()
 853       * @see self::_connect()
 854       * @var string
 855       * @access private
 856       */
 857      var $host;
 858  
 859      /**
 860       * Port Number
 861       *
 862       * @see self::__construct()
 863       * @see self::_connect()
 864       * @var int
 865       * @access private
 866       */
 867      var $port;
 868  
 869      /**
 870       * Number of columns for terminal window size
 871       *
 872       * @see self::getWindowColumns()
 873       * @see self::setWindowColumns()
 874       * @see self::setWindowSize()
 875       * @var int
 876       * @access private
 877       */
 878      var $windowColumns = 80;
 879  
 880      /**
 881       * Number of columns for terminal window size
 882       *
 883       * @see self::getWindowRows()
 884       * @see self::setWindowRows()
 885       * @see self::setWindowSize()
 886       * @var int
 887       * @access private
 888       */
 889      var $windowRows = 24;
 890  
 891      /**
 892       * Crypto Engine
 893       *
 894       * @see self::setCryptoEngine()
 895       * @see self::_key_exchange()
 896       * @var int
 897       * @access private
 898       */
 899      var $crypto_engine = false;
 900  
 901      /**
 902       * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
 903       *
 904       * @var System_SSH_Agent
 905       * @access private
 906       */
 907      var $agent;
 908  
 909      /**
 910       * Send the identification string first?
 911       *
 912       * @var bool
 913       * @access private
 914       */
 915      var $send_id_string_first = true;
 916  
 917      /**
 918       * Send the key exchange initiation packet first?
 919       *
 920       * @var bool
 921       * @access private
 922       */
 923      var $send_kex_first = true;
 924  
 925      /**
 926       * Some versions of OpenSSH incorrectly calculate the key size
 927       *
 928       * @var bool
 929       * @access private
 930       */
 931      var $bad_key_size_fix = false;
 932  
 933      /**
 934       * Should we try to re-connect to re-establish keys?
 935       *
 936       * @var bool
 937       * @access private
 938       */
 939      var $retry_connect = false;
 940  
 941      /**
 942       * Binary Packet Buffer
 943       *
 944       * @var string|false
 945       * @access private
 946       */
 947      var $binary_packet_buffer = false;
 948  
 949      /**
 950       * Preferred Signature Format
 951       *
 952       * @var string|false
 953       * @access private
 954       */
 955      var $preferred_signature_format = false;
 956  
 957      /**
 958       * Authentication Credentials
 959       *
 960       * @var array
 961       * @access private
 962       */
 963      var $auth = array();
 964  
 965      /**
 966       * Default Constructor.
 967       *
 968       * $host can either be a string, representing the host, or a stream resource.
 969       *
 970       * @param mixed $host
 971       * @param int $port
 972       * @param int $timeout
 973       * @see self::login()
 974       * @return \phpseclib\Net\SSH2
 975       * @access public
 976       */
 977      function __construct($host, $port = 22, $timeout = 10)
 978      {
 979          $this->message_numbers = array(
 980              1 => 'NET_SSH2_MSG_DISCONNECT',
 981              2 => 'NET_SSH2_MSG_IGNORE',
 982              3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
 983              4 => 'NET_SSH2_MSG_DEBUG',
 984              5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
 985              6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
 986              20 => 'NET_SSH2_MSG_KEXINIT',
 987              21 => 'NET_SSH2_MSG_NEWKEYS',
 988              30 => 'NET_SSH2_MSG_KEXDH_INIT',
 989              31 => 'NET_SSH2_MSG_KEXDH_REPLY',
 990              50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
 991              51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
 992              52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
 993              53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
 994  
 995              80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
 996              81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
 997              82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
 998              90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
 999              91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
1000              92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
1001              93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
1002              94 => 'NET_SSH2_MSG_CHANNEL_DATA',
1003              95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
1004              96 => 'NET_SSH2_MSG_CHANNEL_EOF',
1005              97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
1006              98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
1007              99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
1008              100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
1009          );
1010          $this->disconnect_reasons = array(
1011              1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
1012              2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
1013              3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
1014              4 => 'NET_SSH2_DISCONNECT_RESERVED',
1015              5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
1016              6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
1017              7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
1018              8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
1019              9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
1020              10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
1021              11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
1022              12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
1023              13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
1024              14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
1025              15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
1026          );
1027          $this->channel_open_failure_reasons = array(
1028              1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
1029          );
1030          $this->terminal_modes = array(
1031              0 => 'NET_SSH2_TTY_OP_END'
1032          );
1033          $this->channel_extended_data_type_codes = array(
1034              1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
1035          );
1036  
1037          $this->_define_array(
1038              $this->message_numbers,
1039              $this->disconnect_reasons,
1040              $this->channel_open_failure_reasons,
1041              $this->terminal_modes,
1042              $this->channel_extended_data_type_codes,
1043              array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
1044              array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
1045              array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
1046                    61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
1047              // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
1048              array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
1049                    31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
1050                    32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
1051                    33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
1052                    34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
1053              // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
1054              array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
1055                    31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
1056          );
1057  
1058          if (is_resource($host)) {
1059              $this->fsock = $host;
1060              return;
1061          }
1062  
1063          if (is_string($host)) {
1064              $this->host = $host;
1065              $this->port = $port;
1066              $this->timeout = $timeout;
1067          }
1068      }
1069  
1070      /**
1071       * Set Crypto Engine Mode
1072       *
1073       * Possible $engine values:
1074       * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
1075       *
1076       * @param int $engine
1077       * @access public
1078       */
1079      function setCryptoEngine($engine)
1080      {
1081          $this->crypto_engine = $engine;
1082      }
1083  
1084      /**
1085       * Send Identification String First
1086       *
1087       * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1088       * both sides MUST send an identification string". It does not say which side sends it first. In
1089       * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1090       *
1091       * @access public
1092       */
1093      function sendIdentificationStringFirst()
1094      {
1095          $this->send_id_string_first = true;
1096      }
1097  
1098      /**
1099       * Send Identification String Last
1100       *
1101       * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
1102       * both sides MUST send an identification string". It does not say which side sends it first. In
1103       * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1104       *
1105       * @access public
1106       */
1107      function sendIdentificationStringLast()
1108      {
1109          $this->send_id_string_first = false;
1110      }
1111  
1112      /**
1113       * Send SSH_MSG_KEXINIT First
1114       *
1115       * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1116       * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1117       * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1118       *
1119       * @access public
1120       */
1121      function sendKEXINITFirst()
1122      {
1123          $this->send_kex_first = true;
1124      }
1125  
1126      /**
1127       * Send SSH_MSG_KEXINIT Last
1128       *
1129       * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
1130       * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
1131       * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
1132       *
1133       * @access public
1134       */
1135      function sendKEXINITLast()
1136      {
1137          $this->send_kex_first = false;
1138      }
1139  
1140      /**
1141       * Connect to an SSHv2 server
1142       *
1143       * @return bool
1144       * @access private
1145       */
1146      function _connect()
1147      {
1148          if ($this->bitmap & self::MASK_CONSTRUCTOR) {
1149              return false;
1150          }
1151  
1152          $this->bitmap |= self::MASK_CONSTRUCTOR;
1153  
1154          $this->curTimeout = $this->timeout;
1155  
1156          $this->last_packet = microtime(true);
1157  
1158          if (!is_resource($this->fsock)) {
1159              $start = microtime(true);
1160              // with stream_select a timeout of 0 means that no timeout takes place;
1161              // with fsockopen a timeout of 0 means that you instantly timeout
1162              // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
1163              $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
1164              if (!$this->fsock) {
1165                  $host = $this->host . ':' . $this->port;
1166                  user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
1167                  return false;
1168              }
1169              $elapsed = microtime(true) - $start;
1170  
1171              if ($this->curTimeout) {
1172                  $this->curTimeout-= $elapsed;
1173                  if ($this->curTimeout < 0) {
1174                      $this->is_timeout = true;
1175                      return false;
1176                  }
1177              }
1178          }
1179  
1180          $this->identifier = $this->_generate_identifier();
1181  
1182          if ($this->send_id_string_first) {
1183              fputs($this->fsock, $this->identifier . "\r\n");
1184          }
1185  
1186          /* According to the SSH2 specs,
1187  
1188            "The server MAY send other lines of data before sending the version
1189             string.  Each line SHOULD be terminated by a Carriage Return and Line
1190             Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
1191             in ISO-10646 UTF-8 [RFC3629] (language is not specified).  Clients
1192             MUST be able to process such lines." */
1193          $data = '';
1194          while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
1195              $line = '';
1196              while (true) {
1197                  if ($this->curTimeout) {
1198                      if ($this->curTimeout < 0) {
1199                          $this->is_timeout = true;
1200                          return false;
1201                      }
1202                      $read = array($this->fsock);
1203                      $write = $except = null;
1204                      $start = microtime(true);
1205                      $sec = floor($this->curTimeout);
1206                      $usec = 1000000 * ($this->curTimeout - $sec);
1207                      // on windows this returns a "Warning: Invalid CRT parameters detected" error
1208                      // the !count() is done as a workaround for <https://bugs.php.net/42682>
1209                      if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
1210                          $this->is_timeout = true;
1211                          return false;
1212                      }
1213                      $elapsed = microtime(true) - $start;
1214                      $this->curTimeout-= $elapsed;
1215                  }
1216  
1217                  $temp = stream_get_line($this->fsock, 255, "\n");
1218                  if (strlen($temp) == 255) {
1219                      continue;
1220                  }
1221                  if ($temp === false) {
1222                      return false;
1223                  }
1224  
1225                  $line.= "$temp\n";
1226  
1227                  // quoting RFC4253, "Implementers who wish to maintain
1228                  // compatibility with older, undocumented versions of this protocol may
1229                  // want to process the identification string without expecting the
1230                  // presence of the carriage return character for reasons described in
1231                  // Section 5 of this document."
1232  
1233                  //if (substr($line, -2) == "\r\n") {
1234                  //    break;
1235                  //}
1236  
1237                  break;
1238              }
1239  
1240              $data.= $line;
1241          }
1242  
1243          if (feof($this->fsock)) {
1244              $this->bitmap = 0;
1245              user_error('Connection closed by server');
1246              return false;
1247          }
1248  
1249          $extra = $matches[1];
1250  
1251          if (defined('NET_SSH2_LOGGING')) {
1252              $this->_append_log('<-', $matches[0]);
1253              $this->_append_log('->', $this->identifier . "\r\n");
1254          }
1255  
1256          $this->server_identifier = trim($temp, "\r\n");
1257          if (strlen($extra)) {
1258              $this->errors[] = $data;
1259          }
1260  
1261          if (version_compare($matches[3], '1.99', '<')) {
1262              user_error("Cannot connect to SSH $matches[3] servers");
1263              return false;
1264          }
1265  
1266          if (!$this->send_id_string_first) {
1267              fputs($this->fsock, $this->identifier . "\r\n");
1268          }
1269  
1270          if (!$this->send_kex_first) {
1271              $response = $this->_get_binary_packet();
1272              if ($response === false) {
1273                  $this->bitmap = 0;
1274                  user_error('Connection closed by server');
1275                  return false;
1276              }
1277  
1278              if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
1279                  user_error('Expected SSH_MSG_KEXINIT');
1280                  return false;
1281              }
1282  
1283              if (!$this->_key_exchange($response)) {
1284                  return false;
1285              }
1286          }
1287  
1288          if ($this->send_kex_first && !$this->_key_exchange()) {
1289              return false;
1290          }
1291  
1292          $this->bitmap|= self::MASK_CONNECTED;
1293  
1294          return true;
1295      }
1296  
1297      /**
1298       * Generates the SSH identifier
1299       *
1300       * You should overwrite this method in your own class if you want to use another identifier
1301       *
1302       * @access protected
1303       * @return string
1304       */
1305      function _generate_identifier()
1306      {
1307          $identifier = 'SSH-2.0-phpseclib_2.0';
1308  
1309          $ext = array();
1310          if (function_exists('sodium_crypto_box_publickey_from_secretkey')) {
1311              $ext[] = 'libsodium';
1312          }
1313  
1314          if (extension_loaded('openssl')) {
1315              $ext[] = 'openssl';
1316          } elseif (extension_loaded('mcrypt')) {
1317              $ext[] = 'mcrypt';
1318          }
1319  
1320          if (extension_loaded('gmp')) {
1321              $ext[] = 'gmp';
1322          } elseif (extension_loaded('bcmath')) {
1323              $ext[] = 'bcmath';
1324          }
1325  
1326          if (!empty($ext)) {
1327              $identifier .= ' (' . implode(', ', $ext) . ')';
1328          }
1329  
1330          return $identifier;
1331      }
1332  
1333      /**
1334       * Key Exchange
1335       *
1336       * @param string $kexinit_payload_server optional
1337       * @access private
1338       */
1339      function _key_exchange($kexinit_payload_server = false)
1340      {
1341          $preferred = $this->preferred;
1342  
1343          $kex_algorithms = isset($preferred['kex']) ?
1344              $preferred['kex'] :
1345              $this->getSupportedKEXAlgorithms();
1346          $server_host_key_algorithms = isset($preferred['hostkey']) ?
1347              $preferred['hostkey'] :
1348              $this->getSupportedHostKeyAlgorithms();
1349          $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
1350              $preferred['server_to_client']['crypt'] :
1351              $this->getSupportedEncryptionAlgorithms();
1352          $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
1353              $preferred['client_to_server']['crypt'] :
1354              $this->getSupportedEncryptionAlgorithms();
1355          $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
1356              $preferred['server_to_client']['mac'] :
1357              $this->getSupportedMACAlgorithms();
1358          $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
1359              $preferred['client_to_server']['mac'] :
1360              $this->getSupportedMACAlgorithms();
1361          $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
1362              $preferred['server_to_client']['comp'] :
1363              $this->getSupportedCompressionAlgorithms();
1364          $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
1365              $preferred['client_to_server']['comp'] :
1366              $this->getSupportedCompressionAlgorithms();
1367  
1368          // some SSH servers have buggy implementations of some of the above algorithms
1369          switch (true) {
1370              case $this->server_identifier == 'SSH-2.0-SSHD':
1371              case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
1372                  if (!isset($preferred['server_to_client']['mac'])) {
1373                      $s2c_mac_algorithms = array_values(array_diff(
1374                          $s2c_mac_algorithms,
1375                          array('hmac-sha1-96', 'hmac-md5-96')
1376                      ));
1377                  }
1378                  if (!isset($preferred['client_to_server']['mac'])) {
1379                      $c2s_mac_algorithms = array_values(array_diff(
1380                          $c2s_mac_algorithms,
1381                          array('hmac-sha1-96', 'hmac-md5-96')
1382                      ));
1383                  }
1384          }
1385  
1386          $str_kex_algorithms = implode(',', $kex_algorithms);
1387          $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
1388          $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
1389          $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
1390          $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
1391          $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
1392          $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
1393          $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);
1394  
1395          $client_cookie = Random::string(16);
1396  
1397          $kexinit_payload_client = pack(
1398              'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
1399              NET_SSH2_MSG_KEXINIT,
1400              $client_cookie,
1401              strlen($str_kex_algorithms),
1402              $str_kex_algorithms,
1403              strlen($str_server_host_key_algorithms),
1404              $str_server_host_key_algorithms,
1405              strlen($encryption_algorithms_client_to_server),
1406              $encryption_algorithms_client_to_server,
1407              strlen($encryption_algorithms_server_to_client),
1408              $encryption_algorithms_server_to_client,
1409              strlen($mac_algorithms_client_to_server),
1410              $mac_algorithms_client_to_server,
1411              strlen($mac_algorithms_server_to_client),
1412              $mac_algorithms_server_to_client,
1413              strlen($compression_algorithms_client_to_server),
1414              $compression_algorithms_client_to_server,
1415              strlen($compression_algorithms_server_to_client),
1416              $compression_algorithms_server_to_client,
1417              0,
1418              '',
1419              0,
1420              '',
1421              0,
1422              0
1423          );
1424  
1425          if ($this->send_kex_first) {
1426              if (!$this->_send_binary_packet($kexinit_payload_client)) {
1427                  return false;
1428              }
1429  
1430              $kexinit_payload_server = $this->_get_binary_packet();
1431              if ($kexinit_payload_server === false) {
1432                  $this->bitmap = 0;
1433                  user_error('Connection closed by server');
1434                  return false;
1435              }
1436  
1437              if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
1438                  user_error('Expected SSH_MSG_KEXINIT');
1439                  return false;
1440              }
1441          }
1442  
1443          $response = $kexinit_payload_server;
1444          $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
1445          $server_cookie = $this->_string_shift($response, 16);
1446  
1447          if (strlen($response) < 4) {
1448              return false;
1449          }
1450          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1451          $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1452  
1453          if (strlen($response) < 4) {
1454              return false;
1455          }
1456          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1457          $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1458  
1459          if (strlen($response) < 4) {
1460              return false;
1461          }
1462          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1463          $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1464  
1465          if (strlen($response) < 4) {
1466              return false;
1467          }
1468          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1469          $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1470  
1471          if (strlen($response) < 4) {
1472              return false;
1473          }
1474          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1475          $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1476  
1477          if (strlen($response) < 4) {
1478              return false;
1479          }
1480          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1481          $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1482  
1483          if (strlen($response) < 4) {
1484              return false;
1485          }
1486          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1487          $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1488  
1489          if (strlen($response) < 4) {
1490              return false;
1491          }
1492          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1493          $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1494  
1495          if (strlen($response) < 4) {
1496              return false;
1497          }
1498          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1499          $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1500  
1501          if (strlen($response) < 4) {
1502              return false;
1503          }
1504          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1505          $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1506  
1507          if (!strlen($response)) {
1508              return false;
1509          }
1510          extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1511          $first_kex_packet_follows = $first_kex_packet_follows != 0;
1512  
1513          if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
1514              return false;
1515          }
1516  
1517          // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
1518          // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
1519          // diffie-hellman key exchange as fast as possible
1520          $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
1521          $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
1522          if ($decryptKeyLength === null) {
1523              user_error('No compatible server to client encryption algorithms found');
1524              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1525          }
1526  
1527          $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
1528          $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
1529          if ($encryptKeyLength === null) {
1530              user_error('No compatible client to server encryption algorithms found');
1531              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1532          }
1533  
1534          // through diffie-hellman key exchange a symmetric key is obtained
1535          $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
1536          if ($kex_algorithm === false) {
1537              user_error('No compatible key exchange algorithms found');
1538              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1539          }
1540  
1541          // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
1542          $exchange_hash_rfc4419 = '';
1543  
1544          if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
1545              $x = Random::string(32);
1546              $eBytes = sodium_crypto_box_publickey_from_secretkey($x);
1547              $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
1548              $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY';
1549              $kexHash = new Hash('sha256');
1550          } else {
1551              if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1552                  $dh_group_sizes_packed = pack(
1553                      'NNN',
1554                      $this->kex_dh_group_size_min,
1555                      $this->kex_dh_group_size_preferred,
1556                      $this->kex_dh_group_size_max
1557                  );
1558                  $packet = pack(
1559                      'Ca*',
1560                      NET_SSH2_MSG_KEXDH_GEX_REQUEST,
1561                      $dh_group_sizes_packed
1562                  );
1563                  if (!$this->_send_binary_packet($packet)) {
1564                      return false;
1565                  }
1566                  $this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
1567  
1568                  $response = $this->_get_binary_packet();
1569                  if ($response === false) {
1570                      $this->bitmap = 0;
1571                      user_error('Connection closed by server');
1572                      return false;
1573                  }
1574                  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1575                  if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
1576                      user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
1577                      return false;
1578                  }
1579                  $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
1580  
1581                  if (strlen($response) < 4) {
1582                      return false;
1583                  }
1584                  extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
1585                  $primeBytes = $this->_string_shift($response, $primeLength);
1586                  $prime = new BigInteger($primeBytes, -256);
1587  
1588                  if (strlen($response) < 4) {
1589                      return false;
1590                  }
1591                  extract(unpack('NgLength', $this->_string_shift($response, 4)));
1592                  $gBytes = $this->_string_shift($response, $gLength);
1593                  $g = new BigInteger($gBytes, -256);
1594  
1595                  $exchange_hash_rfc4419 = pack(
1596                      'a*Na*Na*',
1597                      $dh_group_sizes_packed,
1598                      $primeLength,
1599                      $primeBytes,
1600                      $gLength,
1601                      $gBytes
1602                  );
1603  
1604                  $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT';
1605                  $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY';
1606              } else {
1607                  switch ($kex_algorithm) {
1608                      // see http://tools.ietf.org/html/rfc2409#section-6.2 and
1609                      // http://tools.ietf.org/html/rfc2412, appendex E
1610                      case 'diffie-hellman-group1-sha1':
1611                          $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1612                                  '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1613                                  '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1614                                  'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
1615                          break;
1616                      // see http://tools.ietf.org/html/rfc3526#section-3
1617                      case 'diffie-hellman-group14-sha1':
1618                          $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1619                                  '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1620                                  '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1621                                  'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
1622                                  '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
1623                                  '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
1624                                  'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
1625                                  '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
1626                          break;
1627                  }
1628                  // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
1629                  // the generator field element is 2 (decimal) and the hash function is sha1.
1630                  $g = new BigInteger(2);
1631                  $prime = new BigInteger($prime, 16);
1632                  $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT';
1633                  $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY';
1634              }
1635  
1636              switch ($kex_algorithm) {
1637                  case 'diffie-hellman-group-exchange-sha256':
1638                      $kexHash = new Hash('sha256');
1639                      break;
1640                  default:
1641                      $kexHash = new Hash('sha1');
1642              }
1643  
1644              /* To increase the speed of the key exchange, both client and server may
1645              reduce the size of their private exponents.  It should be at least
1646              twice as long as the key material that is generated from the shared
1647              secret.  For more details, see the paper by van Oorschot and Wiener
1648              [VAN-OORSCHOT].
1649  
1650              -- http://tools.ietf.org/html/rfc4419#section-6.2 */
1651              $one = new BigInteger(1);
1652              $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
1653              $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
1654              $max = $max->subtract($one);
1655  
1656              $x = $one->random($one, $max);
1657              $e = $g->modPow($x, $prime);
1658  
1659              $eBytes = $e->toBytes(true);
1660          }
1661          $data = pack('CNa*', constant($clientKexInitMessage), strlen($eBytes), $eBytes);
1662  
1663          if (!$this->_send_binary_packet($data)) {
1664              $this->bitmap = 0;
1665              user_error('Connection closed by server');
1666              return false;
1667          }
1668          switch ($clientKexInitMessage) {
1669              case 'NET_SSH2_MSG_KEX_ECDH_INIT':
1670                  $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT');
1671                  break;
1672              case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
1673                  $this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
1674          }
1675  
1676          $response = $this->_get_binary_packet();
1677          if ($response === false) {
1678              $this->bitmap = 0;
1679              user_error('Connection closed by server');
1680              return false;
1681          }
1682          if (!strlen($response)) {
1683              return false;
1684          }
1685          extract(unpack('Ctype', $this->_string_shift($response, 1)));
1686  
1687          if ($type != constant($serverKexReplyMessage)) {
1688              user_error("Expected $serverKexReplyMessage");
1689              return false;
1690          }
1691          switch ($serverKexReplyMessage) {
1692              case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
1693                  $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY');
1694                  break;
1695              case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
1696                  $this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
1697          }
1698  
1699          if (strlen($response) < 4) {
1700              return false;
1701          }
1702          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1703          $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
1704  
1705          if (strlen($server_public_host_key) < 4) {
1706              return false;
1707          }
1708          $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
1709          $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
1710  
1711          if (strlen($response) < 4) {
1712              return false;
1713          }
1714          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1715          $fBytes = $this->_string_shift($response, $temp['length']);
1716  
1717          if (strlen($response) < 4) {
1718              return false;
1719          }
1720          $temp = unpack('Nlength', $this->_string_shift($response, 4));
1721          $this->signature = $this->_string_shift($response, $temp['length']);
1722  
1723          if (strlen($this->signature) < 4) {
1724              return false;
1725          }
1726          $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
1727          $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
1728  
1729          if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
1730              if (strlen($fBytes) !== 32) {
1731                  user_error('Received curve25519 public key of invalid length.');
1732                  return false;
1733              }
1734              $key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256);
1735              // sodium_compat doesn't emulate sodium_memzero
1736              // also, with v1 of libsodium API the extension identifies itself as
1737              // libsodium whereas v2 of the libsodium API (what PHP 7.2+ includes)
1738              // identifies itself as sodium. sodium_compat uses the v1 API to
1739              // emulate the v2 API if it's the v1 API that's available
1740              if (extension_loaded('sodium') || extension_loaded('libsodium')) {
1741                  sodium_memzero($x);
1742              }
1743          } else {
1744              $f = new BigInteger($fBytes, -256);
1745              $key = $f->modPow($x, $prime);
1746          }
1747          $keyBytes = $key->toBytes(true);
1748  
1749          $this->exchange_hash = pack(
1750              'Na*Na*Na*Na*Na*a*Na*Na*Na*',
1751              strlen($this->identifier),
1752              $this->identifier,
1753              strlen($this->server_identifier),
1754              $this->server_identifier,
1755              strlen($kexinit_payload_client),
1756              $kexinit_payload_client,
1757              strlen($kexinit_payload_server),
1758              $kexinit_payload_server,
1759              strlen($this->server_public_host_key),
1760              $this->server_public_host_key,
1761              $exchange_hash_rfc4419,
1762              strlen($eBytes),
1763              $eBytes,
1764              strlen($fBytes),
1765              $fBytes,
1766              strlen($keyBytes),
1767              $keyBytes
1768          );
1769  
1770          $this->exchange_hash = $kexHash->hash($this->exchange_hash);
1771  
1772          if ($this->session_id === false) {
1773              $this->session_id = $this->exchange_hash;
1774          }
1775  
1776          $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1777          if ($server_host_key_algorithm === false) {
1778              user_error('No compatible server host key algorithms found');
1779              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1780          }
1781  
1782          switch ($server_host_key_algorithm) {
1783              case 'ssh-dss':
1784                  $expected_key_format = 'ssh-dss';
1785                  break;
1786              //case 'rsa-sha2-256':
1787              //case 'rsa-sha2-512':
1788              //case 'ssh-rsa':
1789              default:
1790                  $expected_key_format = 'ssh-rsa';
1791          }
1792  
1793          if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
1794              switch (true) {
1795                  case $this->signature_format == $server_host_key_algorithm:
1796                  case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
1797                  case $this->signature_format != 'ssh-rsa':
1798                      user_error('Server Host Key Algorithm Mismatch');
1799                      return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1800              }
1801          }
1802  
1803          $packet = pack(
1804              'C',
1805              NET_SSH2_MSG_NEWKEYS
1806          );
1807  
1808          if (!$this->_send_binary_packet($packet)) {
1809              return false;
1810          }
1811  
1812          $response = $this->_get_binary_packet();
1813  
1814          if ($response === false) {
1815              $this->bitmap = 0;
1816              user_error('Connection closed by server');
1817              return false;
1818          }
1819  
1820          if (!strlen($response)) {
1821              return false;
1822          }
1823          extract(unpack('Ctype', $this->_string_shift($response, 1)));
1824  
1825          if ($type != NET_SSH2_MSG_NEWKEYS) {
1826              user_error('Expected SSH_MSG_NEWKEYS');
1827              return false;
1828          }
1829  
1830          $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1831  
1832          $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
1833          if ($this->encrypt) {
1834              if ($this->crypto_engine) {
1835                  $this->encrypt->setPreferredEngine($this->crypto_engine);
1836              }
1837              if ($this->encrypt->block_size) {
1838                  $this->encrypt_block_size = $this->encrypt->block_size;
1839              }
1840              $this->encrypt->enableContinuousBuffer();
1841              $this->encrypt->disablePadding();
1842  
1843              if ($this->encrypt->getBlockLength()) {
1844                  $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3;
1845              }
1846  
1847              $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
1848              while ($this->encrypt_block_size > strlen($iv)) {
1849                  $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1850              }
1851              $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
1852  
1853              $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
1854              while ($encryptKeyLength > strlen($key)) {
1855                  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1856              }
1857              $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
1858  
1859              $this->encrypt->name = $decrypt;
1860          }
1861  
1862          $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
1863          if ($this->decrypt) {
1864              if ($this->crypto_engine) {
1865                  $this->decrypt->setPreferredEngine($this->crypto_engine);
1866              }
1867              if ($this->decrypt->block_size) {
1868                  $this->decrypt_block_size = $this->decrypt->block_size;
1869              }
1870              $this->decrypt->enableContinuousBuffer();
1871              $this->decrypt->disablePadding();
1872  
1873              if ($this->decrypt->getBlockLength()) {
1874                  $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3;
1875              }
1876  
1877              $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
1878              while ($this->decrypt_block_size > strlen($iv)) {
1879                  $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1880              }
1881              $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
1882  
1883              $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
1884              while ($decryptKeyLength > strlen($key)) {
1885                  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1886              }
1887              $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
1888  
1889              $this->decrypt->name = $decrypt;
1890          }
1891  
1892          /* The "arcfour128" algorithm is the RC4 cipher, as described in
1893             [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
1894             generated by the cipher MUST be discarded, and the first byte of the
1895             first encrypted packet MUST be encrypted using the 1537th byte of
1896             keystream.
1897  
1898             -- http://tools.ietf.org/html/rfc4345#section-4 */
1899          if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
1900              $this->encrypt->encrypt(str_repeat("\0", 1536));
1901          }
1902          if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
1903              $this->decrypt->decrypt(str_repeat("\0", 1536));
1904          }
1905  
1906          $mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
1907          if ($mac_algorithm === false) {
1908              user_error('No compatible client to server message authentication algorithms found');
1909              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1910          }
1911  
1912          $createKeyLength = 0; // ie. $mac_algorithm == 'none'
1913          switch ($mac_algorithm) {
1914              case 'hmac-sha2-256':
1915                  $this->hmac_create = new Hash('sha256');
1916                  $createKeyLength = 32;
1917                  break;
1918              case 'hmac-sha1':
1919                  $this->hmac_create = new Hash('sha1');
1920                  $createKeyLength = 20;
1921                  break;
1922              case 'hmac-sha1-96':
1923                  $this->hmac_create = new Hash('sha1-96');
1924                  $createKeyLength = 20;
1925                  break;
1926              case 'hmac-md5':
1927                  $this->hmac_create = new Hash('md5');
1928                  $createKeyLength = 16;
1929                  break;
1930              case 'hmac-md5-96':
1931                  $this->hmac_create = new Hash('md5-96');
1932                  $createKeyLength = 16;
1933          }
1934          $this->hmac_create->name = $mac_algorithm;
1935  
1936          $mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
1937          if ($mac_algorithm === false) {
1938              user_error('No compatible server to client message authentication algorithms found');
1939              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1940          }
1941  
1942          $checkKeyLength = 0;
1943          $this->hmac_size = 0;
1944          switch ($mac_algorithm) {
1945              case 'hmac-sha2-256':
1946                  $this->hmac_check = new Hash('sha256');
1947                  $checkKeyLength = 32;
1948                  $this->hmac_size = 32;
1949                  break;
1950              case 'hmac-sha1':
1951                  $this->hmac_check = new Hash('sha1');
1952                  $checkKeyLength = 20;
1953                  $this->hmac_size = 20;
1954                  break;
1955              case 'hmac-sha1-96':
1956                  $this->hmac_check = new Hash('sha1-96');
1957                  $checkKeyLength = 20;
1958                  $this->hmac_size = 12;
1959                  break;
1960              case 'hmac-md5':
1961                  $this->hmac_check = new Hash('md5');
1962                  $checkKeyLength = 16;
1963                  $this->hmac_size = 16;
1964                  break;
1965              case 'hmac-md5-96':
1966                  $this->hmac_check = new Hash('md5-96');
1967                  $checkKeyLength = 16;
1968                  $this->hmac_size = 12;
1969          }
1970          $this->hmac_check->name = $mac_algorithm;
1971  
1972          $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
1973          while ($createKeyLength > strlen($key)) {
1974              $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1975          }
1976          $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
1977  
1978          $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
1979          while ($checkKeyLength > strlen($key)) {
1980              $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1981          }
1982          $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1983  
1984          $compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1985          if ($compression_algorithm === false) {
1986              user_error('No compatible client to server compression algorithms found');
1987              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1988          }
1989          //$this->decompress = $compression_algorithm == 'zlib';
1990  
1991          $compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
1992          if ($compression_algorithm === false) {
1993              user_error('No compatible server to client compression algorithms found');
1994              return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1995          }
1996          //$this->compress = $compression_algorithm == 'zlib';
1997  
1998          return true;
1999      }
2000  
2001      /**
2002       * Maps an encryption algorithm name to the number of key bytes.
2003       *
2004       * @param string $algorithm Name of the encryption algorithm
2005       * @return int|null Number of bytes as an integer or null for unknown
2006       * @access private
2007       */
2008      function _encryption_algorithm_to_key_size($algorithm)
2009      {
2010          if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
2011              return 16;
2012          }
2013  
2014          switch ($algorithm) {
2015              case 'none':
2016                  return 0;
2017              case 'aes128-cbc':
2018              case 'aes128-ctr':
2019              case 'arcfour':
2020              case 'arcfour128':
2021              case 'blowfish-cbc':
2022              case 'blowfish-ctr':
2023              case 'twofish128-cbc':
2024              case 'twofish128-ctr':
2025                  return 16;
2026              case '3des-cbc':
2027              case '3des-ctr':
2028              case 'aes192-cbc':
2029              case 'aes192-ctr':
2030              case 'twofish192-cbc':
2031              case 'twofish192-ctr':
2032                  return 24;
2033              case 'aes256-cbc':
2034              case 'aes256-ctr':
2035              case 'arcfour256':
2036              case 'twofish-cbc':
2037              case 'twofish256-cbc':
2038              case 'twofish256-ctr':
2039                  return 32;
2040          }
2041          return null;
2042      }
2043  
2044      /**
2045       * Maps an encryption algorithm name to an instance of a subclass of
2046       * \phpseclib\Crypt\Base.
2047       *
2048       * @param string $algorithm Name of the encryption algorithm
2049       * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
2050       * @access private
2051       */
2052      function _encryption_algorithm_to_crypt_instance($algorithm)
2053      {
2054          switch ($algorithm) {
2055              case '3des-cbc':
2056                  return new TripleDES();
2057              case '3des-ctr':
2058                  return new TripleDES(Base::MODE_CTR);
2059              case 'aes256-cbc':
2060              case 'aes192-cbc':
2061              case 'aes128-cbc':
2062                  return new Rijndael();
2063              case 'aes256-ctr':
2064              case 'aes192-ctr':
2065              case 'aes128-ctr':
2066                  return new Rijndael(Base::MODE_CTR);
2067              case 'blowfish-cbc':
2068                  return new Blowfish();
2069              case 'blowfish-ctr':
2070                  return new Blowfish(Base::MODE_CTR);
2071              case 'twofish128-cbc':
2072              case 'twofish192-cbc':
2073              case 'twofish256-cbc':
2074              case 'twofish-cbc':
2075                  return new Twofish();
2076              case 'twofish128-ctr':
2077              case 'twofish192-ctr':
2078              case 'twofish256-ctr':
2079                  return new Twofish(Base::MODE_CTR);
2080              case 'arcfour':
2081              case 'arcfour128':
2082              case 'arcfour256':
2083                  return new RC4();
2084          }
2085          return null;
2086      }
2087  
2088      /**
2089       * Tests whether or not proposed algorithm has a potential for issues
2090       *
2091       * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
2092       * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
2093       * @param string $algorithm Name of the encryption algorithm
2094       * @return bool
2095       * @access private
2096       */
2097      function _bad_algorithm_candidate($algorithm)
2098      {
2099          switch ($algorithm) {
2100              case 'arcfour256':
2101              case 'aes192-ctr':
2102              case 'aes256-ctr':
2103                  return true;
2104          }
2105  
2106          return false;
2107      }
2108  
2109      /**
2110       * Login
2111       *
2112       * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array
2113       *
2114       * @param string $username
2115       * @param mixed $password
2116       * @param mixed $...
2117       * @return bool
2118       * @see self::_login()
2119       * @access public
2120       */
2121      function login($username)
2122      {
2123          $args = func_get_args();
2124          $this->auth[] = $args;
2125  
2126          // try logging with 'none' as an authentication method first since that's what
2127          // PuTTY does
2128          if ($this->_login($username)) {
2129              return true;
2130          }
2131          if (count($args) == 1) {
2132              return false;
2133          }
2134          return call_user_func_array(array(&$this, '_login'), $args);
2135      }
2136  
2137      /**
2138       * Login Helper
2139       *
2140       * @param string $username
2141       * @param mixed $password
2142       * @param mixed $...
2143       * @return bool
2144       * @see self::_login_helper()
2145       * @access private
2146       */
2147      function _login($username)
2148      {
2149          if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
2150              if (!$this->_connect()) {
2151                  return false;
2152              }
2153          }
2154  
2155          $args = array_slice(func_get_args(), 1);
2156          if (empty($args)) {
2157              return $this->_login_helper($username);
2158          }
2159  
2160          foreach ($args as $arg) {
2161              if ($this->_login_helper($username, $arg)) {
2162                  return true;
2163              }
2164          }
2165          return false;
2166      }
2167  
2168      /**
2169       * Login Helper
2170       *
2171       * @param string $username
2172       * @param string $password
2173       * @return bool
2174       * @access private
2175       * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
2176       *           by sending dummy SSH_MSG_IGNORE messages.
2177       */
2178      function _login_helper($username, $password = null)
2179      {
2180          if (!($this->bitmap & self::MASK_CONNECTED)) {
2181              return false;
2182          }
2183  
2184          if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
2185              $packet = pack(
2186                  'CNa*',
2187                  NET_SSH2_MSG_SERVICE_REQUEST,
2188                  strlen('ssh-userauth'),
2189                  'ssh-userauth'
2190              );
2191  
2192              if (!$this->_send_binary_packet($packet)) {
2193                  return false;
2194              }
2195  
2196              $response = $this->_get_binary_packet();
2197              if ($response === false) {
2198                  if ($this->retry_connect) {
2199                      $this->retry_connect = false;
2200                      if (!$this->_connect()) {
2201                          return false;
2202                      }
2203                      return $this->_login_helper($username, $password);
2204                  }
2205                  $this->bitmap = 0;
2206                  user_error('Connection closed by server');
2207                  return false;
2208              }
2209  
2210              if (strlen($response) < 4) {
2211                  return false;
2212              }
2213              extract(unpack('Ctype', $this->_string_shift($response, 1)));
2214  
2215              if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
2216                  user_error('Expected SSH_MSG_SERVICE_ACCEPT');
2217                  return false;
2218              }
2219              $this->bitmap |= self::MASK_LOGIN_REQ;
2220          }
2221  
2222          if (strlen($this->last_interactive_response)) {
2223              return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
2224          }
2225  
2226          if ($password instanceof RSA) {
2227              return $this->_privatekey_login($username, $password);
2228          } elseif ($password instanceof Agent) {
2229              return $this->_ssh_agent_login($username, $password);
2230          }
2231  
2232          if (is_array($password)) {
2233              if ($this->_keyboard_interactive_login($username, $password)) {
2234                  $this->bitmap |= self::MASK_LOGIN;
2235                  return true;
2236              }
2237              return false;
2238          }
2239  
2240          if (!isset($password)) {
2241              $packet = pack(
2242                  'CNa*Na*Na*',
2243                  NET_SSH2_MSG_USERAUTH_REQUEST,
2244                  strlen($username),
2245                  $username,
2246                  strlen('ssh-connection'),
2247                  'ssh-connection',
2248                  strlen('none'),
2249                  'none'
2250              );
2251  
2252              if (!$this->_send_binary_packet($packet)) {
2253                  return false;
2254              }
2255  
2256              $response = $this->_get_binary_packet();
2257              if ($response === false) {
2258                  $this->bitmap = 0;
2259                  user_error('Connection closed by server');
2260                  return false;
2261              }
2262  
2263              if (!strlen($response)) {
2264                  return false;
2265              }
2266              extract(unpack('Ctype', $this->_string_shift($response, 1)));
2267  
2268              switch ($type) {
2269                  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2270                      $this->bitmap |= self::MASK_LOGIN;
2271                      return true;
2272                  //case NET_SSH2_MSG_USERAUTH_FAILURE:
2273                  default:
2274                      return false;
2275              }
2276          }
2277  
2278          $packet = pack(
2279              'CNa*Na*Na*CNa*',
2280              NET_SSH2_MSG_USERAUTH_REQUEST,
2281              strlen($username),
2282              $username,
2283              strlen('ssh-connection'),
2284              'ssh-connection',
2285              strlen('password'),
2286              'password',
2287              0,
2288              strlen($password),
2289              $password
2290          );
2291  
2292          // remove the username and password from the logged packet
2293          if (!defined('NET_SSH2_LOGGING')) {
2294              $logged = null;
2295          } else {
2296              $logged = pack(
2297                  'CNa*Na*Na*CNa*',
2298                  NET_SSH2_MSG_USERAUTH_REQUEST,
2299                  strlen('username'),
2300                  'username',
2301                  strlen('ssh-connection'),
2302                  'ssh-connection',
2303                  strlen('password'),
2304                  'password',
2305                  0,
2306                  strlen('password'),
2307                  'password'
2308              );
2309          }
2310  
2311          if (!$this->_send_binary_packet($packet, $logged)) {
2312              return false;
2313          }
2314  
2315          $response = $this->_get_binary_packet();
2316          if ($response === false) {
2317              $this->bitmap = 0;
2318              user_error('Connection closed by server');
2319              return false;
2320          }
2321  
2322          if (!strlen($response)) {
2323              return false;
2324          }
2325          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2326  
2327          switch ($type) {
2328              case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
2329                  $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
2330                  if (strlen($response) < 4) {
2331                      return false;
2332                  }
2333                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2334                  $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
2335                  return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
2336              case NET_SSH2_MSG_USERAUTH_FAILURE:
2337                  // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
2338                  // multi-factor authentication
2339                  if (strlen($response) < 4) {
2340                      return false;
2341                  }
2342                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2343                  $auth_methods = explode(',', $this->_string_shift($response, $length));
2344                  if (!strlen($response)) {
2345                      return false;
2346                  }
2347                  extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
2348                  $partial_success = $partial_success != 0;
2349  
2350                  if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
2351                      if ($this->_keyboard_interactive_login($username, $password)) {
2352                          $this->bitmap |= self::MASK_LOGIN;
2353                          return true;
2354                      }
2355                      return false;
2356                  }
2357                  return false;
2358              case NET_SSH2_MSG_USERAUTH_SUCCESS:
2359                  $this->bitmap |= self::MASK_LOGIN;
2360                  return true;
2361          }
2362  
2363          return false;
2364      }
2365  
2366      /**
2367       * Login via keyboard-interactive authentication
2368       *
2369       * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
2370       *
2371       * @param string $username
2372       * @param string $password
2373       * @return bool
2374       * @access private
2375       */
2376      function _keyboard_interactive_login($username, $password)
2377      {
2378          $packet = pack(
2379              'CNa*Na*Na*Na*Na*',
2380              NET_SSH2_MSG_USERAUTH_REQUEST,
2381              strlen($username),
2382              $username,
2383              strlen('ssh-connection'),
2384              'ssh-connection',
2385              strlen('keyboard-interactive'),
2386              'keyboard-interactive',
2387              0,
2388              '',
2389              0,
2390              ''
2391          );
2392  
2393          if (!$this->_send_binary_packet($packet)) {
2394              return false;
2395          }
2396  
2397          return $this->_keyboard_interactive_process($password);
2398      }
2399  
2400      /**
2401       * Handle the keyboard-interactive requests / responses.
2402       *
2403       * @param string $responses...
2404       * @return bool
2405       * @access private
2406       */
2407      function _keyboard_interactive_process()
2408      {
2409          $responses = func_get_args();
2410  
2411          if (strlen($this->last_interactive_response)) {
2412              $response = $this->last_interactive_response;
2413          } else {
2414              $orig = $response = $this->_get_binary_packet();
2415              if ($response === false) {
2416                  $this->bitmap = 0;
2417                  user_error('Connection closed by server');
2418                  return false;
2419              }
2420          }
2421  
2422          if (!strlen($response)) {
2423              return false;
2424          }
2425          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2426  
2427          switch ($type) {
2428              case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
2429                  if (strlen($response) < 4) {
2430                      return false;
2431                  }
2432                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2433                  $this->_string_shift($response, $length); // name; may be empty
2434                  if (strlen($response) < 4) {
2435                      return false;
2436                  }
2437                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2438                  $this->_string_shift($response, $length); // instruction; may be empty
2439                  if (strlen($response) < 4) {
2440                      return false;
2441                  }
2442                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2443                  $this->_string_shift($response, $length); // language tag; may be empty
2444                  if (strlen($response) < 4) {
2445                      return false;
2446                  }
2447                  extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
2448  
2449                  for ($i = 0; $i < count($responses); $i++) {
2450                      if (is_array($responses[$i])) {
2451                          foreach ($responses[$i] as $key => $value) {
2452                              $this->keyboard_requests_responses[$key] = $value;
2453                          }
2454                          unset($responses[$i]);
2455                      }
2456                  }
2457                  $responses = array_values($responses);
2458  
2459                  if (isset($this->keyboard_requests_responses)) {
2460                      for ($i = 0; $i < $num_prompts; $i++) {
2461                          if (strlen($response) < 4) {
2462                              return false;
2463                          }
2464                          extract(unpack('Nlength', $this->_string_shift($response, 4)));
2465                          // prompt - ie. "Password: "; must not be empty
2466                          $prompt = $this->_string_shift($response, $length);
2467                          //$echo = $this->_string_shift($response) != chr(0);
2468                          foreach ($this->keyboard_requests_responses as $key => $value) {
2469                              if (substr($prompt, 0, strlen($key)) == $key) {
2470                                  $responses[] = $value;
2471                                  break;
2472                              }
2473                          }
2474                      }
2475                  }
2476  
2477                  // see http://tools.ietf.org/html/rfc4256#section-3.2
2478                  if (strlen($this->last_interactive_response)) {
2479                      $this->last_interactive_response = '';
2480                  } else {
2481                      $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
2482                  }
2483  
2484                  if (!count($responses) && $num_prompts) {
2485                      $this->last_interactive_response = $orig;
2486                      return false;
2487                  }
2488  
2489                  /*
2490                     After obtaining the requested information from the user, the client
2491                     MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
2492                  */
2493                  // see http://tools.ietf.org/html/rfc4256#section-3.4
2494                  $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
2495                  for ($i = 0; $i < count($responses); $i++) {
2496                      $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
2497                      $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
2498                  }
2499  
2500                  if (!$this->_send_binary_packet($packet, $logged)) {
2501                      return false;
2502                  }
2503  
2504                  $this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
2505  
2506                  /*
2507                     After receiving the response, the server MUST send either an
2508                     SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
2509                     SSH_MSG_USERAUTH_INFO_REQUEST message.
2510                  */
2511                  // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
2512                  // there could be an infinite loop of request / responses.
2513                  return $this->_keyboard_interactive_process();
2514              case NET_SSH2_MSG_USERAUTH_SUCCESS:
2515                  return true;
2516              case NET_SSH2_MSG_USERAUTH_FAILURE:
2517                  return false;
2518          }
2519  
2520          return false;
2521      }
2522  
2523      /**
2524       * Login with an ssh-agent provided key
2525       *
2526       * @param string $username
2527       * @param \phpseclib\System\SSH\Agent $agent
2528       * @return bool
2529       * @access private
2530       */
2531      function _ssh_agent_login($username, $agent)
2532      {
2533          $this->agent = $agent;
2534          $keys = $agent->requestIdentities();
2535          foreach ($keys as $key) {
2536              if ($this->_privatekey_login($username, $key)) {
2537                  return true;
2538              }
2539          }
2540  
2541          return false;
2542      }
2543  
2544      /**
2545       * Login with an RSA private key
2546       *
2547       * @param string $username
2548       * @param \phpseclib\Crypt\RSA $password
2549       * @return bool
2550       * @access private
2551       * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
2552       *           by sending dummy SSH_MSG_IGNORE messages.
2553       */
2554      function _privatekey_login($username, $privatekey)
2555      {
2556          // see http://tools.ietf.org/html/rfc4253#page-15
2557          $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
2558          if ($publickey === false) {
2559              return false;
2560          }
2561  
2562          $publickey = array(
2563              'e' => $publickey['e']->toBytes(true),
2564              'n' => $publickey['n']->toBytes(true)
2565          );
2566          $publickey = pack(
2567              'Na*Na*Na*',
2568              strlen('ssh-rsa'),
2569              'ssh-rsa',
2570              strlen($publickey['e']),
2571              $publickey['e'],
2572              strlen($publickey['n']),
2573              $publickey['n']
2574          );
2575  
2576          switch ($this->signature_format) {
2577              case 'rsa-sha2-512':
2578                  $hash = 'sha512';
2579                  $signatureType = 'rsa-sha2-512';
2580                  break;
2581              case 'rsa-sha2-256':
2582                  $hash = 'sha256';
2583                  $signatureType = 'rsa-sha2-256';
2584                  break;
2585              //case 'ssh-rsa':
2586              default:
2587                  $hash = 'sha1';
2588                  $signatureType = 'ssh-rsa';
2589          }
2590  
2591          $part1 = pack(
2592              'CNa*Na*Na*',
2593              NET_SSH2_MSG_USERAUTH_REQUEST,
2594              strlen($username),
2595              $username,
2596              strlen('ssh-connection'),
2597              'ssh-connection',
2598              strlen('publickey'),
2599              'publickey'
2600          );
2601          $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);
2602  
2603          $packet = $part1 . chr(0) . $part2;
2604          if (!$this->_send_binary_packet($packet)) {
2605              return false;
2606          }
2607  
2608          $response = $this->_get_binary_packet();
2609          if ($response === false) {
2610              $this->bitmap = 0;
2611              user_error('Connection closed by server');
2612              return false;
2613          }
2614  
2615          if (!strlen($response)) {
2616              return false;
2617          }
2618          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2619  
2620          switch ($type) {
2621              case NET_SSH2_MSG_USERAUTH_FAILURE:
2622                  if (strlen($response) < 4) {
2623                      return false;
2624                  }
2625                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2626                  $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
2627                  return false;
2628              case NET_SSH2_MSG_USERAUTH_PK_OK:
2629                  // we'll just take it on faith that the public key blob and the public key algorithm name are as
2630                  // they should be
2631                  $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
2632          }
2633  
2634          $packet = $part1 . chr(1) . $part2;
2635          $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
2636          $privatekey->setHash($hash);
2637          $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
2638          $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
2639          $packet.= pack('Na*', strlen($signature), $signature);
2640  
2641          if (!$this->_send_binary_packet($packet)) {
2642              return false;
2643          }
2644  
2645          $response = $this->_get_binary_packet();
2646          if ($response === false) {
2647              $this->bitmap = 0;
2648              user_error('Connection closed by server');
2649              return false;
2650          }
2651  
2652          if (!strlen($response)) {
2653              return false;
2654          }
2655          extract(unpack('Ctype', $this->_string_shift($response, 1)));
2656  
2657          switch ($type) {
2658              case NET_SSH2_MSG_USERAUTH_FAILURE:
2659                  // either the login is bad or the server employs multi-factor authentication
2660                  return false;
2661              case NET_SSH2_MSG_USERAUTH_SUCCESS:
2662                  $this->bitmap |= self::MASK_LOGIN;
2663                  return true;
2664          }
2665  
2666          return false;
2667      }
2668  
2669      /**
2670       * Set Timeout
2671       *
2672       * $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.
2673       * Setting $timeout to false or 0 will mean there is no timeout.
2674       *
2675       * @param mixed $timeout
2676       * @access public
2677       */
2678      function setTimeout($timeout)
2679      {
2680          $this->timeout = $this->curTimeout = $timeout;
2681      }
2682  
2683      /**
2684       * Get the output from stdError
2685       *
2686       * @access public
2687       */
2688      function getStdError()
2689      {
2690          return $this->stdErrorLog;
2691      }
2692  
2693      /**
2694       * Execute Command
2695       *
2696       * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
2697       * In all likelihood, this is not a feature you want to be taking advantage of.
2698       *
2699       * @param string $command
2700       * @param Callback $callback
2701       * @return string
2702       * @access public
2703       */
2704      function exec($command, $callback = null)
2705      {
2706          $this->curTimeout = $this->timeout;
2707          $this->is_timeout = false;
2708          $this->stdErrorLog = '';
2709  
2710          if (!$this->isAuthenticated()) {
2711              return false;
2712          }
2713  
2714          if ($this->in_request_pty_exec) {
2715              user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
2716              return false;
2717          }
2718  
2719          // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
2720          // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
2721          // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
2722          // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
2723          $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
2724          // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
2725          // uses 0x4000, that's what will be used here, as well.
2726          $packet_size = 0x4000;
2727  
2728          $packet = pack(
2729              'CNa*N3',
2730              NET_SSH2_MSG_CHANNEL_OPEN,
2731              strlen('session'),
2732              'session',
2733              self::CHANNEL_EXEC,
2734              $this->window_size_server_to_client[self::CHANNEL_EXEC],
2735              $packet_size
2736          );
2737  
2738          if (!$this->_send_binary_packet($packet)) {
2739              return false;
2740          }
2741  
2742          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
2743  
2744          $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2745          if ($response === false) {
2746              return false;
2747          }
2748  
2749          if ($this->request_pty === true) {
2750              $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2751              $packet = pack(
2752                  'CNNa*CNa*N5a*',
2753                  NET_SSH2_MSG_CHANNEL_REQUEST,
2754                  $this->server_channels[self::CHANNEL_EXEC],
2755                  strlen('pty-req'),
2756                  'pty-req',
2757                  1,
2758                  strlen('vt100'),
2759                  'vt100',
2760                  $this->windowColumns,
2761                  $this->windowRows,
2762                  0,
2763                  0,
2764                  strlen($terminal_modes),
2765                  $terminal_modes
2766              );
2767  
2768              if (!$this->_send_binary_packet($packet)) {
2769                  return false;
2770              }
2771  
2772              $response = $this->_get_binary_packet();
2773              if ($response === false) {
2774                  $this->bitmap = 0;
2775                  user_error('Connection closed by server');
2776                  return false;
2777              }
2778  
2779              if (!strlen($response)) {
2780                  return false;
2781              }
2782              list(, $type) = unpack('C', $this->_string_shift($response, 1));
2783  
2784              switch ($type) {
2785                  case NET_SSH2_MSG_CHANNEL_SUCCESS:
2786                      break;
2787                  case NET_SSH2_MSG_CHANNEL_FAILURE:
2788                  default:
2789                      user_error('Unable to request pseudo-terminal');
2790                      return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2791              }
2792              $this->in_request_pty_exec = true;
2793          }
2794  
2795          // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
2796          // down.  the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &').
2797          // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
2798          // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
2799          // neither will your script.
2800  
2801          // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
2802          // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
2803          // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
2804          $packet = pack(
2805              'CNNa*CNa*',
2806              NET_SSH2_MSG_CHANNEL_REQUEST,
2807              $this->server_channels[self::CHANNEL_EXEC],
2808              strlen('exec'),
2809              'exec',
2810              1,
2811              strlen($command),
2812              $command
2813          );
2814          if (!$this->_send_binary_packet($packet)) {
2815              return false;
2816          }
2817  
2818          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2819  
2820          $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2821          if ($response === false) {
2822              return false;
2823          }
2824  
2825          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
2826  
2827          if ($callback === false || $this->in_request_pty_exec) {
2828              return true;
2829          }
2830  
2831          $output = '';
2832          while (true) {
2833              $temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
2834              switch (true) {
2835                  case $temp === true:
2836                      return is_callable($callback) ? true : $output;
2837                  case $temp === false:
2838                      return false;
2839                  default:
2840                      if (is_callable($callback)) {
2841                          if (call_user_func($callback, $temp) === true) {
2842                              $this->_close_channel(self::CHANNEL_EXEC);
2843                              return true;
2844                          }
2845                      } else {
2846                          $output.= $temp;
2847                      }
2848              }
2849          }
2850      }
2851  
2852      /**
2853       * Creates an interactive shell
2854       *
2855       * @see self::read()
2856       * @see self::write()
2857       * @return bool
2858       * @access private
2859       */
2860      function _initShell()
2861      {
2862          if ($this->in_request_pty_exec === true) {
2863              return true;
2864          }
2865  
2866          $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
2867          $packet_size = 0x4000;
2868  
2869          $packet = pack(
2870              'CNa*N3',
2871              NET_SSH2_MSG_CHANNEL_OPEN,
2872              strlen('session'),
2873              'session',
2874              self::CHANNEL_SHELL,
2875              $this->window_size_server_to_client[self::CHANNEL_SHELL],
2876              $packet_size
2877          );
2878  
2879          if (!$this->_send_binary_packet($packet)) {
2880              return false;
2881          }
2882  
2883          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
2884  
2885          $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
2886          if ($response === false) {
2887              return false;
2888          }
2889  
2890          $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2891          $packet = pack(
2892              'CNNa*CNa*N5a*',
2893              NET_SSH2_MSG_CHANNEL_REQUEST,
2894              $this->server_channels[self::CHANNEL_SHELL],
2895              strlen('pty-req'),
2896              'pty-req',
2897              1,
2898              strlen('vt100'),
2899              'vt100',
2900              $this->windowColumns,
2901              $this->windowRows,
2902              0,
2903              0,
2904              strlen($terminal_modes),
2905              $terminal_modes
2906          );
2907  
2908          if (!$this->_send_binary_packet($packet)) {
2909              return false;
2910          }
2911  
2912          $response = $this->_get_binary_packet();
2913          if ($response === false) {
2914              $this->bitmap = 0;
2915              user_error('Connection closed by server');
2916              return false;
2917          }
2918  
2919          if (!strlen($response)) {
2920              return false;
2921          }
2922          list(, $type) = unpack('C', $this->_string_shift($response, 1));
2923  
2924          switch ($type) {
2925              case NET_SSH2_MSG_CHANNEL_SUCCESS:
2926              // if a pty can't be opened maybe commands can still be executed
2927              case NET_SSH2_MSG_CHANNEL_FAILURE:
2928                  break;
2929              default:
2930                  user_error('Unable to request pseudo-terminal');
2931                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2932          }
2933  
2934          $packet = pack(
2935              'CNNa*C',
2936              NET_SSH2_MSG_CHANNEL_REQUEST,
2937              $this->server_channels[self::CHANNEL_SHELL],
2938              strlen('shell'),
2939              'shell',
2940              1
2941          );
2942          if (!$this->_send_binary_packet($packet)) {
2943              return false;
2944          }
2945  
2946          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
2947  
2948          $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
2949          if ($response === false) {
2950              return false;
2951          }
2952  
2953          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
2954  
2955          $this->bitmap |= self::MASK_SHELL;
2956  
2957          return true;
2958      }
2959  
2960      /**
2961       * Return the channel to be used with read() / write()
2962       *
2963       * @see self::read()
2964       * @see self::write()
2965       * @return int
2966       * @access public
2967       */
2968      function _get_interactive_channel()
2969      {
2970          switch (true) {
2971              case $this->in_subsystem:
2972                  return self::CHANNEL_SUBSYSTEM;
2973              case $this->in_request_pty_exec:
2974                  return self::CHANNEL_EXEC;
2975              default:
2976                  return self::CHANNEL_SHELL;
2977          }
2978      }
2979  
2980      /**
2981       * Return an available open channel
2982       *
2983       * @return int
2984       * @access public
2985       */
2986      function _get_open_channel()
2987      {
2988          $channel = self::CHANNEL_EXEC;
2989          do {
2990              if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
2991                  return $channel;
2992              }
2993          } while ($channel++ < self::CHANNEL_SUBSYSTEM);
2994  
2995          return false;
2996      }
2997  
2998      /**
2999       * Returns the output of an interactive shell
3000       *
3001       * Returns when there's a match for $expect, which can take the form of a string literal or,
3002       * if $mode == self::READ_REGEX, a regular expression.
3003       *
3004       * @see self::write()
3005       * @param string $expect
3006       * @param int $mode
3007       * @return string|bool
3008       * @access public
3009       */
3010      function read($expect = '', $mode = self::READ_SIMPLE)
3011      {
3012          $this->curTimeout = $this->timeout;
3013          $this->is_timeout = false;
3014  
3015          if (!$this->isAuthenticated()) {
3016              user_error('Operation disallowed prior to login()');
3017              return false;
3018          }
3019  
3020          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
3021              user_error('Unable to initiate an interactive shell session');
3022              return false;
3023          }
3024  
3025          $channel = $this->_get_interactive_channel();
3026  
3027          if ($mode == self::READ_NEXT) {
3028              return $this->_get_channel_packet($channel);
3029          }
3030  
3031          $match = $expect;
3032          while (true) {
3033              if ($mode == self::READ_REGEX) {
3034                  preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
3035                  $match = isset($matches[0]) ? $matches[0] : '';
3036              }
3037              $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
3038              if ($pos !== false) {
3039                  return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
3040              }
3041              $response = $this->_get_channel_packet($channel);
3042              if (is_bool($response)) {
3043                  $this->in_request_pty_exec = false;
3044                  return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
3045              }
3046  
3047              $this->interactiveBuffer.= $response;
3048          }
3049      }
3050  
3051      /**
3052       * Inputs a command into an interactive shell.
3053       *
3054       * @see self::read()
3055       * @param string $cmd
3056       * @return bool
3057       * @access public
3058       */
3059      function write($cmd)
3060      {
3061          if (!$this->isAuthenticated()) {
3062              user_error('Operation disallowed prior to login()');
3063              return false;
3064          }
3065  
3066          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
3067              user_error('Unable to initiate an interactive shell session');
3068              return false;
3069          }
3070  
3071          return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
3072      }
3073  
3074      /**
3075       * Start a subsystem.
3076       *
3077       * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
3078       * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
3079       * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
3080       * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
3081       * if there's sufficient demand for such a feature.
3082       *
3083       * @see self::stopSubsystem()
3084       * @param string $subsystem
3085       * @return bool
3086       * @access public
3087       */
3088      function startSubsystem($subsystem)
3089      {
3090          $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
3091  
3092          $packet = pack(
3093              'CNa*N3',
3094              NET_SSH2_MSG_CHANNEL_OPEN,
3095              strlen('session'),
3096              'session',
3097              self::CHANNEL_SUBSYSTEM,
3098              $this->window_size,
3099              0x4000
3100          );
3101  
3102          if (!$this->_send_binary_packet($packet)) {
3103              return false;
3104          }
3105  
3106          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
3107  
3108          $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
3109          if ($response === false) {
3110              return false;
3111          }
3112  
3113          $packet = pack(
3114              'CNNa*CNa*',
3115              NET_SSH2_MSG_CHANNEL_REQUEST,
3116              $this->server_channels[self::CHANNEL_SUBSYSTEM],
3117              strlen('subsystem'),
3118              'subsystem',
3119              1,
3120              strlen($subsystem),
3121              $subsystem
3122          );
3123          if (!$this->_send_binary_packet($packet)) {
3124              return false;
3125          }
3126  
3127          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
3128  
3129          $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
3130  
3131          if ($response === false) {
3132              return false;
3133          }
3134  
3135          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
3136  
3137          $this->bitmap |= self::MASK_SHELL;
3138          $this->in_subsystem = true;
3139  
3140          return true;
3141      }
3142  
3143      /**
3144       * Stops a subsystem.
3145       *
3146       * @see self::startSubsystem()
3147       * @return bool
3148       * @access public
3149       */
3150      function stopSubsystem()
3151      {
3152          $this->in_subsystem = false;
3153          $this->_close_channel(self::CHANNEL_SUBSYSTEM);
3154          return true;
3155      }
3156  
3157      /**
3158       * Closes a channel
3159       *
3160       * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
3161       *
3162       * @access public
3163       */
3164      function reset()
3165      {
3166          $this->_close_channel($this->_get_interactive_channel());
3167      }
3168  
3169      /**
3170       * Is timeout?
3171       *
3172       * Did exec() or read() return because they timed out or because they encountered the end?
3173       *
3174       * @access public
3175       */
3176      function isTimeout()
3177      {
3178          return $this->is_timeout;
3179      }
3180  
3181      /**
3182       * Disconnect
3183       *
3184       * @access public
3185       */
3186      function disconnect()
3187      {
3188          $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3189          if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
3190              fclose($this->realtime_log_file);
3191          }
3192      }
3193  
3194      /**
3195       * Destructor.
3196       *
3197       * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
3198       * disconnect().
3199       *
3200       * @access public
3201       */
3202      function __destruct()
3203      {
3204          $this->disconnect();
3205      }
3206  
3207      /**
3208       * Is the connection still active?
3209       *
3210       * @return bool
3211       * @access public
3212       */
3213      function isConnected()
3214      {
3215          return (bool) ($this->bitmap & self::MASK_CONNECTED);
3216      }
3217  
3218      /**
3219       * Have you successfully been logged in?
3220       *
3221       * @return bool
3222       * @access public
3223       */
3224      function isAuthenticated()
3225      {
3226          return (bool) ($this->bitmap & self::MASK_LOGIN);
3227      }
3228  
3229      /**
3230       * Pings a server connection, or tries to reconnect if the connection has gone down
3231       *
3232       * Inspired by http://php.net/manual/en/mysqli.ping.php
3233       *
3234       * @return bool
3235       * @access public
3236       */
3237      function ping()
3238      {
3239          if (!$this->isAuthenticated()) {
3240              if (!empty($this->auth)) {
3241                  return $this->_reconnect();
3242              }
3243              return false;
3244          }
3245  
3246          $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
3247          $packet_size = 0x4000;
3248          $packet = pack(
3249              'CNa*N3',
3250              NET_SSH2_MSG_CHANNEL_OPEN,
3251              strlen('session'),
3252              'session',
3253              self::CHANNEL_KEEP_ALIVE,
3254              $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
3255              $packet_size
3256          );
3257  
3258          if (!@$this->_send_binary_packet($packet)) {
3259              return $this->_reconnect();
3260          }
3261  
3262          $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
3263  
3264          $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
3265          if ($response !== false) {
3266              $this->_close_channel(self::CHANNEL_KEEP_ALIVE);
3267              return true;
3268          }
3269  
3270          return $this->_reconnect();
3271      }
3272  
3273      /**
3274       * In situ reconnect method
3275       *
3276       * @return boolean
3277       * @access private
3278       */
3279      function _reconnect()
3280      {
3281          $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
3282          $this->retry_connect = true;
3283          if (!$this->_connect()) {
3284              return false;
3285          }
3286          foreach ($this->auth as $auth) {
3287              $result = call_user_func_array(array(&$this, 'login'), $auth);
3288          }
3289          return $result;
3290      }
3291  
3292      /**
3293       * Resets a connection for re-use
3294       *
3295       * @param int $reason
3296       * @access private
3297       */
3298      function _reset_connection($reason)
3299      {
3300          $this->_disconnect($reason);
3301          $this->decrypt = $this->encrypt = false;
3302          $this->decrypt_block_size = $this->encrypt_block_size = 8;
3303          $this->hmac_check = $this->hmac_create = false;
3304          $this->hmac_size = false;
3305          $this->session_id = false;
3306          $this->retry_connect = true;
3307          $this->get_seq_no = $this->send_seq_no = 0;
3308      }
3309  
3310      /**
3311       * Gets Binary Packets
3312       *
3313       * See '6. Binary Packet Protocol' of rfc4253 for more info.
3314       *
3315       * @see self::_send_binary_packet()
3316       * @return string
3317       * @access private
3318       */
3319      function _get_binary_packet($skip_channel_filter = false)
3320      {
3321          if (!is_resource($this->fsock) || feof($this->fsock)) {
3322              $this->bitmap = 0;
3323              user_error('Connection closed prematurely');
3324              return false;
3325          }
3326  
3327          $start = microtime(true);
3328          $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
3329  
3330          if (!strlen($raw)) {
3331              return '';
3332          }
3333  
3334          if ($this->decrypt !== false) {
3335              $raw = $this->decrypt->decrypt($raw);
3336          }
3337          if ($raw === false) {
3338              user_error('Unable to decrypt content');
3339              return false;
3340          }
3341  
3342          if (strlen($raw) < 5) {
3343              return false;
3344          }
3345          extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
3346  
3347          $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
3348  
3349          // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
3350          // "implementations SHOULD check that the packet length is reasonable"
3351          // PuTTY uses 0x9000 as the actual max packet size and so to shall we
3352          if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
3353              if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) {
3354                  $this->bad_key_size_fix = true;
3355                  $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3356                  return false;
3357              }
3358              user_error('Invalid size');
3359              return false;
3360          }
3361  
3362          $buffer = '';
3363          while ($remaining_length > 0) {
3364              $temp = stream_get_contents($this->fsock, $remaining_length);
3365              if ($temp === false || feof($this->fsock)) {
3366                  $this->bitmap = 0;
3367                  user_error('Error reading from socket');
3368                  return false;
3369              }
3370              $buffer.= $temp;
3371              $remaining_length-= strlen($temp);
3372          }
3373  
3374          $stop = microtime(true);
3375          if (strlen($buffer)) {
3376              $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
3377          }
3378  
3379          $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
3380          $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
3381  
3382          if ($this->hmac_check !== false) {
3383              $hmac = stream_get_contents($this->fsock, $this->hmac_size);
3384              if ($hmac === false || strlen($hmac) != $this->hmac_size) {
3385                  $this->bitmap = 0;
3386                  user_error('Error reading socket');
3387                  return false;
3388              } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
3389                  user_error('Invalid HMAC');
3390                  return false;
3391              }
3392          }
3393  
3394          //if ($this->decompress) {
3395          //    $payload = gzinflate(substr($payload, 2));
3396          //}
3397  
3398          $this->get_seq_no++;
3399  
3400          if (defined('NET_SSH2_LOGGING')) {
3401              $current = microtime(true);
3402              $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
3403              $message_number = '<- ' . $message_number .
3404                                ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3405              $this->_append_log($message_number, $payload);
3406              $this->last_packet = $current;
3407          }
3408  
3409          return $this->_filter($payload, $skip_channel_filter);
3410      }
3411  
3412      /**
3413       * Filter Binary Packets
3414       *
3415       * Because some binary packets need to be ignored...
3416       *
3417       * @see self::_get_binary_packet()
3418       * @return string
3419       * @access private
3420       */
3421      function _filter($payload, $skip_channel_filter)
3422      {
3423          switch (ord($payload[0])) {
3424              case NET_SSH2_MSG_DISCONNECT:
3425                  $this->_string_shift($payload, 1);
3426                  if (strlen($payload) < 8) {
3427                      return false;
3428                  }
3429                  extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
3430                  $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
3431                  $this->bitmap = 0;
3432                  return false;
3433              case NET_SSH2_MSG_IGNORE:
3434                  $payload = $this->_get_binary_packet($skip_channel_filter);
3435                  break;
3436              case NET_SSH2_MSG_DEBUG:
3437                  $this->_string_shift($payload, 2);
3438                  if (strlen($payload) < 4) {
3439                      return false;
3440                  }
3441                  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3442                  $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
3443                  $payload = $this->_get_binary_packet($skip_channel_filter);
3444                  break;
3445              case NET_SSH2_MSG_UNIMPLEMENTED:
3446                  return false;
3447              case NET_SSH2_MSG_KEXINIT:
3448                  if ($this->session_id !== false) {
3449                      $this->send_kex_first = false;
3450                      if (!$this->_key_exchange($payload)) {
3451                          $this->bitmap = 0;
3452                          return false;
3453                      }
3454                      $payload = $this->_get_binary_packet($skip_channel_filter);
3455                  }
3456          }
3457  
3458          // 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
3459          if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
3460              $this->_string_shift($payload, 1);
3461              if (strlen($payload) < 4) {
3462                  return false;
3463              }
3464              extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3465              $this->banner_message = $this->_string_shift($payload, $length);
3466              $payload = $this->_get_binary_packet();
3467          }
3468  
3469          // only called when we've already logged in
3470          if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
3471              switch (ord($payload[0])) {
3472                  case NET_SSH2_MSG_CHANNEL_DATA:
3473                  case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3474                  case NET_SSH2_MSG_CHANNEL_REQUEST:
3475                  case NET_SSH2_MSG_CHANNEL_CLOSE:
3476                  case NET_SSH2_MSG_CHANNEL_EOF:
3477                      if (!$skip_channel_filter && !empty($this->server_channels)) {
3478                          $this->binary_packet_buffer = $payload;
3479                          $this->_get_channel_packet(true);
3480                          $payload = $this->_get_binary_packet();
3481                      }
3482                      break;
3483                  case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
3484                      if (strlen($payload) < 4) {
3485                          return false;
3486                      }
3487                      extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3488                      $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);
3489  
3490                      if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
3491                          return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3492                      }
3493  
3494                      $payload = $this->_get_binary_packet($skip_channel_filter);
3495                      break;
3496                  case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
3497                      $this->_string_shift($payload, 1);
3498                      if (strlen($payload) < 4) {
3499                          return false;
3500                      }
3501                      extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3502                      $data = $this->_string_shift($payload, $length);
3503                      if (strlen($payload) < 4) {
3504                          return false;
3505                      }
3506                      extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
3507                      switch ($data) {
3508                          case 'auth-agent':
3509                          case 'auth-agent@openssh.com':
3510                              if (isset($this->agent)) {
3511                                  $new_channel = self::CHANNEL_AGENT_FORWARD;
3512  
3513                                  if (strlen($payload) < 8) {
3514                                      return false;
3515                                  }
3516                                  extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
3517                                  extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));
3518  
3519                                  $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
3520                                  $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
3521                                  $this->window_size_client_to_server[$new_channel] = $this->window_size;
3522  
3523                                  $packet_size = 0x4000;
3524  
3525                                  $packet = pack(
3526                                      'CN4',
3527                                      NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
3528                                      $server_channel,
3529                                      $new_channel,
3530                                      $packet_size,
3531                                      $packet_size
3532                                  );
3533  
3534                                  $this->server_channels[$new_channel] = $server_channel;
3535                                  $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
3536                                  if (!$this->_send_binary_packet($packet)) {
3537                                      return false;
3538                                  }
3539                              }
3540                              break;
3541                          default:
3542                              $packet = pack(
3543                                  'CN3a*Na*',
3544                                  NET_SSH2_MSG_REQUEST_FAILURE,
3545                                  $server_channel,
3546                                  NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
3547                                  0,
3548                                  '',
3549                                  0,
3550                                  ''
3551                              );
3552  
3553                              if (!$this->_send_binary_packet($packet)) {
3554                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3555                              }
3556                      }
3557                      $payload = $this->_get_binary_packet($skip_channel_filter);
3558                      break;
3559                  case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3560                      $this->_string_shift($payload, 1);
3561                      if (strlen($payload) < 8) {
3562                          return false;
3563                      }
3564                      extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
3565                      extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
3566                      $this->window_size_client_to_server[$channel]+= $window_size;
3567  
3568                      $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
3569              }
3570          }
3571  
3572          return $payload;
3573      }
3574  
3575      /**
3576       * Enable Quiet Mode
3577       *
3578       * Suppress stderr from output
3579       *
3580       * @access public
3581       */
3582      function enableQuietMode()
3583      {
3584          $this->quiet_mode = true;
3585      }
3586  
3587      /**
3588       * Disable Quiet Mode
3589       *
3590       * Show stderr in output
3591       *
3592       * @access public
3593       */
3594      function disableQuietMode()
3595      {
3596          $this->quiet_mode = false;
3597      }
3598  
3599      /**
3600       * Returns whether Quiet Mode is enabled or not
3601       *
3602       * @see self::enableQuietMode()
3603       * @see self::disableQuietMode()
3604       * @access public
3605       * @return bool
3606       */
3607      function isQuietModeEnabled()
3608      {
3609          return $this->quiet_mode;
3610      }
3611  
3612      /**
3613       * Enable request-pty when using exec()
3614       *
3615       * @access public
3616       */
3617      function enablePTY()
3618      {
3619          $this->request_pty = true;
3620      }
3621  
3622      /**
3623       * Disable request-pty when using exec()
3624       *
3625       * @access public
3626       */
3627      function disablePTY()
3628      {
3629          if ($this->in_request_pty_exec) {
3630              $this->_close_channel(self::CHANNEL_EXEC);
3631              $this->in_request_pty_exec = false;
3632          }
3633          $this->request_pty = false;
3634      }
3635  
3636      /**
3637       * Returns whether request-pty is enabled or not
3638       *
3639       * @see self::enablePTY()
3640       * @see self::disablePTY()
3641       * @access public
3642       * @return bool
3643       */
3644      function isPTYEnabled()
3645      {
3646          return $this->request_pty;
3647      }
3648  
3649      /**
3650       * Gets channel data
3651       *
3652       * Returns the data as a string if it's available and false if not.
3653       *
3654       * @param $client_channel
3655       * @return mixed
3656       * @access private
3657       */
3658      function _get_channel_packet($client_channel, $skip_extended = false)
3659      {
3660          if (!empty($this->channel_buffers[$client_channel])) {
3661              return array_shift($this->channel_buffers[$client_channel]);
3662          }
3663  
3664          while (true) {
3665              if ($this->binary_packet_buffer !== false) {
3666                  $response = $this->binary_packet_buffer;
3667                  $this->binary_packet_buffer = false;
3668              } else {
3669                  $read = array($this->fsock);
3670                  $write = $except = null;
3671  
3672                  if (!$this->curTimeout) {
3673                      @stream_select($read, $write, $except, null);
3674                  } else {
3675                      if ($this->curTimeout < 0) {
3676                          $this->is_timeout = true;
3677                          return true;
3678                      }
3679  
3680                      $read = array($this->fsock);
3681                      $write = $except = null;
3682  
3683                      $start = microtime(true);
3684                      $sec = floor($this->curTimeout);
3685                      $usec = 1000000 * ($this->curTimeout - $sec);
3686                      // on windows this returns a "Warning: Invalid CRT parameters detected" error
3687                      if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3688                          $this->is_timeout = true;
3689                          if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) {
3690                              $this->_close_channel($client_channel);
3691                          }
3692                          return true;
3693                      }
3694                      $elapsed = microtime(true) - $start;
3695                      $this->curTimeout-= $elapsed;
3696                  }
3697  
3698                  $response = $this->_get_binary_packet(true);
3699                  if ($response === false) {
3700                      $this->bitmap = 0;
3701                      user_error('Connection closed by server');
3702                      return false;
3703                  }
3704              }
3705  
3706              if ($client_channel == -1 && $response === true) {
3707                  return true;
3708              }
3709              if (!strlen($response)) {
3710                  return false;
3711              }
3712              extract(unpack('Ctype', $this->_string_shift($response, 1)));
3713  
3714              if (strlen($response) < 4) {
3715                  return false;
3716              }
3717              if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
3718                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3719              } else {
3720                  extract(unpack('Nchannel', $this->_string_shift($response, 4)));
3721              }
3722  
3723              // will not be setup yet on incoming channel open request
3724              if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
3725                  $this->window_size_server_to_client[$channel]-= strlen($response);
3726  
3727                  // resize the window, if appropriate
3728                  if ($this->window_size_server_to_client[$channel] < 0) {
3729                  // PuTTY does something more analogous to the following:
3730                  //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
3731                      $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
3732                      if (!$this->_send_binary_packet($packet)) {
3733                          return false;
3734                      }
3735                      $this->window_size_server_to_client[$channel]+= $this->window_resize;
3736                  }
3737  
3738                  switch ($type) {
3739                      case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3740                          /*
3741                          if ($client_channel == self::CHANNEL_EXEC) {
3742                              $this->_send_channel_packet($client_channel, chr(0));
3743                          }
3744                          */
3745                          // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
3746                          if (strlen($response) < 8) {
3747                              return false;
3748                          }
3749                          extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
3750                          $data = $this->_string_shift($response, $length);
3751                          $this->stdErrorLog.= $data;
3752                          if ($skip_extended || $this->quiet_mode) {
3753                              continue 2;
3754                          }
3755                          if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
3756                              return $data;
3757                          }
3758                          if (!isset($this->channel_buffers[$channel])) {
3759                              $this->channel_buffers[$channel] = array();
3760                          }
3761                          $this->channel_buffers[$channel][] = $data;
3762  
3763                          continue 2;
3764                      case NET_SSH2_MSG_CHANNEL_REQUEST:
3765                          if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
3766                              continue 2;
3767                          }
3768                          if (strlen($response) < 4) {
3769                              return false;
3770                          }
3771                          extract(unpack('Nlength', $this->_string_shift($response, 4)));
3772                          $value = $this->_string_shift($response, $length);
3773                          switch ($value) {
3774                              case 'exit-signal':
3775                                  $this->_string_shift($response, 1);
3776                                  if (strlen($response) < 4) {
3777                                      return false;
3778                                  }
3779                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3780                                  $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
3781                                  $this->_string_shift($response, 1);
3782                                  if (strlen($response) < 4) {
3783                                      return false;
3784                                  }
3785                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3786                                  if ($length) {
3787                                      $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
3788                                  }
3789  
3790                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3791                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3792  
3793                                  $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
3794  
3795                                  continue 3;
3796                              case 'exit-status':
3797                                  if (strlen($response) < 5) {
3798                                      return false;
3799                                  }
3800                                  extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
3801                                  $this->exit_status = $exit_status;
3802  
3803                                  // "The client MAY ignore these messages."
3804                                  // -- http://tools.ietf.org/html/rfc4254#section-6.10
3805  
3806                                  continue 3;
3807                              default:
3808                                  // "Some systems may not implement signals, in which case they SHOULD ignore this message."
3809                                  //  -- http://tools.ietf.org/html/rfc4254#section-6.9
3810                                  continue 3;
3811                          }
3812                  }
3813  
3814                  switch ($this->channel_status[$channel]) {
3815                      case NET_SSH2_MSG_CHANNEL_OPEN:
3816                          switch ($type) {
3817                              case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3818                                  if (strlen($response) < 4) {
3819                                      return false;
3820                                  }
3821                                  extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
3822                                  $this->server_channels[$channel] = $server_channel;
3823                                  if (strlen($response) < 4) {
3824                                      return false;
3825                                  }
3826                                  extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
3827                                  if ($window_size < 0) {
3828                                      $window_size&= 0x7FFFFFFF;
3829                                      $window_size+= 0x80000000;
3830                                  }
3831                                  $this->window_size_client_to_server[$channel] = $window_size;
3832                                  if (strlen($response) < 4) {
3833                                       return false;
3834                                  }
3835                                  $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
3836                                  $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
3837                                  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3838                                  $this->_on_channel_open();
3839                                  return $result;
3840                              //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3841                              default:
3842                                  user_error('Unable to open channel');
3843                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3844                          }
3845                          break;
3846                      case NET_SSH2_MSG_CHANNEL_REQUEST:
3847                          switch ($type) {
3848                              case NET_SSH2_MSG_CHANNEL_SUCCESS:
3849                                  return true;
3850                              case NET_SSH2_MSG_CHANNEL_FAILURE:
3851                                  return false;
3852                              default:
3853                                  user_error('Unable to fulfill channel request');
3854                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3855                          }
3856                      case NET_SSH2_MSG_CHANNEL_CLOSE:
3857                          return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3858                  }
3859              }
3860  
3861              // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
3862  
3863              switch ($type) {
3864                  case NET_SSH2_MSG_CHANNEL_DATA:
3865                      /*
3866                      if ($channel == self::CHANNEL_EXEC) {
3867                          // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
3868                          // this actually seems to make things twice as fast.  more to the point, the message right after
3869                          // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
3870                          // in OpenSSH it slows things down but only by a couple thousandths of a second.
3871                          $this->_send_channel_packet($channel, chr(0));
3872                      }
3873                      */
3874                      if (strlen($response) < 4) {
3875                          return false;
3876                      }
3877                      extract(unpack('Nlength', $this->_string_shift($response, 4)));
3878                      $data = $this->_string_shift($response, $length);
3879  
3880                      if ($channel == self::CHANNEL_AGENT_FORWARD) {
3881                          $agent_response = $this->agent->_forward_data($data);
3882                          if (!is_bool($agent_response)) {
3883                              $this->_send_channel_packet($channel, $agent_response);
3884                          }
3885                          break;
3886                      }
3887  
3888                      if ($client_channel == $channel) {
3889                          return $data;
3890                      }
3891                      if (!isset($this->channel_buffers[$channel])) {
3892                          $this->channel_buffers[$channel] = array();
3893                      }
3894                      $this->channel_buffers[$channel][] = $data;
3895                      break;
3896                  case NET_SSH2_MSG_CHANNEL_CLOSE:
3897                      $this->curTimeout = 5;
3898  
3899                      if ($this->bitmap & self::MASK_SHELL) {
3900                          $this->bitmap&= ~self::MASK_SHELL;
3901                      }
3902                      if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
3903                          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3904                      }
3905  
3906                      $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3907                      if ($client_channel == $channel) {
3908                          return true;
3909                      }
3910                  case NET_SSH2_MSG_CHANNEL_EOF:
3911                      break;
3912                  default:
3913                      user_error('Error reading channel data');
3914                      return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3915              }
3916          }
3917      }
3918  
3919      /**
3920       * Sends Binary Packets
3921       *
3922       * See '6. Binary Packet Protocol' of rfc4253 for more info.
3923       *
3924       * @param string $data
3925       * @param string $logged
3926       * @see self::_get_binary_packet()
3927       * @return bool
3928       * @access private
3929       */
3930      function _send_binary_packet($data, $logged = null)
3931      {
3932          if (!is_resource($this->fsock) || feof($this->fsock)) {
3933              $this->bitmap = 0;
3934              user_error('Connection closed prematurely');
3935              return false;
3936          }
3937  
3938          //if ($this->compress) {
3939          //    // the -4 removes the checksum:
3940          //    // http://php.net/function.gzcompress#57710
3941          //    $data = substr(gzcompress($data), 0, -4);
3942          //}
3943  
3944          // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
3945          $packet_length = strlen($data) + 9;
3946          // round up to the nearest $this->encrypt_block_size
3947          $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
3948          // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
3949          $padding_length = $packet_length - strlen($data) - 5;
3950          $padding = Random::string($padding_length);
3951  
3952          // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
3953          $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
3954  
3955          $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
3956          $this->send_seq_no++;
3957  
3958          if ($this->encrypt !== false) {
3959              $packet = $this->encrypt->encrypt($packet);
3960          }
3961  
3962          $packet.= $hmac;
3963  
3964          $start = microtime(true);
3965          $result = strlen($packet) == fputs($this->fsock, $packet);
3966          $stop = microtime(true);
3967  
3968          if (defined('NET_SSH2_LOGGING')) {
3969              $current = microtime(true);
3970              $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
3971              $message_number = '-> ' . $message_number .
3972                                ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3973              $this->_append_log($message_number, isset($logged) ? $logged : $data);
3974              $this->last_packet = $current;
3975          }
3976  
3977          return $result;
3978      }
3979  
3980      /**
3981       * Logs data packets
3982       *
3983       * Makes sure that only the last 1MB worth of packets will be logged
3984       *
3985       * @param string $data
3986       * @access private
3987       */
3988      function _append_log($message_number, $message)
3989      {
3990          // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
3991          if (strlen($message_number) > 2) {
3992              $this->_string_shift($message);
3993          }
3994  
3995          switch (NET_SSH2_LOGGING) {
3996              // useful for benchmarks
3997              case self::LOG_SIMPLE:
3998                  $this->message_number_log[] = $message_number;
3999                  break;
4000              // the most useful log for SSH2
4001              case self::LOG_COMPLEX:
4002                  $this->message_number_log[] = $message_number;
4003                  $this->log_size+= strlen($message);
4004                  $this->message_log[] = $message;
4005                  while ($this->log_size > self::LOG_MAX_SIZE) {
4006                      $this->log_size-= strlen(array_shift($this->message_log));
4007                      array_shift($this->message_number_log);
4008                  }
4009                  break;
4010              // dump the output out realtime; packets may be interspersed with non packets,
4011              // passwords won't be filtered out and select other packets may not be correctly
4012              // identified
4013              case self::LOG_REALTIME:
4014                  switch (PHP_SAPI) {
4015                      case 'cli':
4016                          $start = $stop = "\r\n";
4017                          break;
4018                      default:
4019                          $start = '<pre>';
4020                          $stop = '</pre>';
4021                  }
4022                  echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
4023                  @flush();
4024                  @ob_flush();
4025                  break;
4026              // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
4027              // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
4028              // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
4029              // at the beginning of the file
4030              case self::LOG_REALTIME_FILE:
4031                  if (!isset($this->realtime_log_file)) {
4032                      // PHP doesn't seem to like using constants in fopen()
4033                      $filename = self::LOG_REALTIME_FILENAME;
4034                      $fp = fopen($filename, 'w');
4035                      $this->realtime_log_file = $fp;
4036                  }
4037                  if (!is_resource($this->realtime_log_file)) {
4038                      break;
4039                  }
4040                  $entry = $this->_format_log(array($message), array($message_number));
4041                  if ($this->realtime_log_wrap) {
4042                      $temp = "<<< START >>>\r\n";
4043                      $entry.= $temp;
4044                      fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
4045                  }
4046                  $this->realtime_log_size+= strlen($entry);
4047                  if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
4048                      fseek($this->realtime_log_file, 0);
4049                      $this->realtime_log_size = strlen($entry);
4050                      $this->realtime_log_wrap = true;
4051                  }
4052                  fputs($this->realtime_log_file, $entry);
4053          }
4054      }
4055  
4056      /**
4057       * Sends channel data
4058       *
4059       * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
4060       *
4061       * @param int $client_channel
4062       * @param string $data
4063       * @return bool
4064       * @access private
4065       */
4066      function _send_channel_packet($client_channel, $data)
4067      {
4068          while (strlen($data)) {
4069              if (!$this->window_size_client_to_server[$client_channel]) {
4070                  $this->bitmap^= self::MASK_WINDOW_ADJUST;
4071                  // using an invalid channel will let the buffers be built up for the valid channels
4072                  $this->_get_channel_packet(-1);
4073                  $this->bitmap^= self::MASK_WINDOW_ADJUST;
4074              }
4075  
4076              /* The maximum amount of data allowed is determined by the maximum
4077                 packet size for the channel, and the current window size, whichever
4078                 is smaller.
4079                   -- http://tools.ietf.org/html/rfc4254#section-5.2 */
4080              $max_size = min(
4081                  $this->packet_size_client_to_server[$client_channel],
4082                  $this->window_size_client_to_server[$client_channel]
4083              );
4084  
4085              $temp = $this->_string_shift($data, $max_size);
4086              $packet = pack(
4087                  'CN2a*',
4088                  NET_SSH2_MSG_CHANNEL_DATA,
4089                  $this->server_channels[$client_channel],
4090                  strlen($temp),
4091                  $temp
4092              );
4093              $this->window_size_client_to_server[$client_channel]-= strlen($temp);
4094              if (!$this->_send_binary_packet($packet)) {
4095                  return false;
4096              }
4097          }
4098  
4099          return true;
4100      }
4101  
4102      /**
4103       * Closes and flushes a channel
4104       *
4105       * \phpseclib\Net\SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
4106       * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
4107       * for SCP more than anything.
4108       *
4109       * @param int $client_channel
4110       * @param bool $want_reply
4111       * @return bool
4112       * @access private
4113       */
4114      function _close_channel($client_channel, $want_reply = false)
4115      {
4116          // see http://tools.ietf.org/html/rfc4254#section-5.3
4117  
4118          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
4119  
4120          if (!$want_reply) {
4121              $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4122          }
4123  
4124          $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4125  
4126          $this->curTimeout = 5;
4127  
4128          while (!is_bool($this->_get_channel_packet($client_channel))) {
4129          }
4130  
4131          if ($this->is_timeout) {
4132              $this->disconnect();
4133          }
4134  
4135          if ($want_reply) {
4136              $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4137          }
4138  
4139          if ($this->bitmap & self::MASK_SHELL) {
4140              $this->bitmap&= ~self::MASK_SHELL;
4141          }
4142      }
4143  
4144      /**
4145       * Disconnect
4146       *
4147       * @param int $reason
4148       * @return bool
4149       * @access private
4150       */
4151      function _disconnect($reason)
4152      {
4153          if ($this->bitmap & self::MASK_CONNECTED) {
4154              $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
4155              $this->_send_binary_packet($data);
4156          }
4157  
4158          $this->bitmap = 0;
4159          if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
4160              fclose($this->fsock);
4161          }
4162  
4163          return false;
4164      }
4165  
4166      /**
4167       * String Shift
4168       *
4169       * Inspired by array_shift
4170       *
4171       * @param string $string
4172       * @param int $index
4173       * @return string
4174       * @access private
4175       */
4176      function _string_shift(&$string, $index = 1)
4177      {
4178          $substr = substr($string, 0, $index);
4179          $string = substr($string, $index);
4180          return $substr;
4181      }
4182  
4183      /**
4184       * Define Array
4185       *
4186       * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
4187       * named constants from it, using the value as the name of the constant and the index as the value of the constant.
4188       * If any of the constants that would be defined already exists, none of the constants will be defined.
4189       *
4190       * @param array $array
4191       * @access private
4192       */
4193      function _define_array()
4194      {
4195          $args = func_get_args();
4196          foreach ($args as $arg) {
4197              foreach ($arg as $key => $value) {
4198                  if (!defined($value)) {
4199                      define($value, $key);
4200                  } else {
4201                      break 2;
4202                  }
4203              }
4204          }
4205      }
4206  
4207      /**
4208       * Returns a log of the packets that have been sent and received.
4209       *
4210       * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
4211       *
4212       * @access public
4213       * @return array|false|string
4214       */
4215      function getLog()
4216      {
4217          if (!defined('NET_SSH2_LOGGING')) {
4218              return false;
4219          }
4220  
4221          switch (NET_SSH2_LOGGING) {
4222              case self::LOG_SIMPLE:
4223                  return $this->message_number_log;
4224              case self::LOG_COMPLEX:
4225                  $log = $this->_format_log($this->message_log, $this->message_number_log);
4226                  return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
4227              default:
4228                  return false;
4229          }
4230      }
4231  
4232      /**
4233       * Formats a log for printing
4234       *
4235       * @param array $message_log
4236       * @param array $message_number_log
4237       * @access private
4238       * @return string
4239       */
4240      function _format_log($message_log, $message_number_log)
4241      {
4242          $output = '';
4243          for ($i = 0; $i < count($message_log); $i++) {
4244              $output.= $message_number_log[$i] . "\r\n";
4245              $current_log = $message_log[$i];
4246              $j = 0;
4247              do {
4248                  if (strlen($current_log)) {
4249                      $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
4250                  }
4251                  $fragment = $this->_string_shift($current_log, $this->log_short_width);
4252                  $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
4253                  // replace non ASCII printable characters with dots
4254                  // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
4255                  // also replace < with a . since < messes up the output on web browsers
4256                  $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
4257                  $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
4258                  $j++;
4259              } while (strlen($current_log));
4260              $output.= "\r\n";
4261          }
4262  
4263          return $output;
4264      }
4265  
4266      /**
4267       * Helper function for _format_log
4268       *
4269       * For use with preg_replace_callback()
4270       *
4271       * @param array $matches
4272       * @access private
4273       * @return string
4274       */
4275      function _format_log_helper($matches)
4276      {
4277          return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
4278      }
4279  
4280      /**
4281       * Helper function for agent->_on_channel_open()
4282       *
4283       * Used when channels are created to inform agent
4284       * of said channel opening. Must be called after
4285       * channel open confirmation received
4286       *
4287       * @access private
4288       */
4289      function _on_channel_open()
4290      {
4291          if (isset($this->agent)) {
4292              $this->agent->_on_channel_open($this);
4293          }
4294      }
4295  
4296      /**
4297       * Returns the first value of the intersection of two arrays or false if
4298       * the intersection is empty. The order is defined by the first parameter.
4299       *
4300       * @param array $array1
4301       * @param array $array2
4302       * @return mixed False if intersection is empty, else intersected value.
4303       * @access private
4304       */
4305      function _array_intersect_first($array1, $array2)
4306      {
4307          foreach ($array1 as $value) {
4308              if (in_array($value, $array2)) {
4309                  return $value;
4310              }
4311          }
4312          return false;
4313      }
4314  
4315      /**
4316       * Returns all errors
4317       *
4318       * @return string[]
4319       * @access public
4320       */
4321      function getErrors()
4322      {
4323          return $this->errors;
4324      }
4325  
4326      /**
4327       * Returns the last error
4328       *
4329       * @return string
4330       * @access public
4331       */
4332      function getLastError()
4333      {
4334          $count = count($this->errors);
4335  
4336          if ($count > 0) {
4337              return $this->errors[$count - 1];
4338          }
4339      }
4340  
4341      /**
4342       * Return the server identification.
4343       *
4344       * @return string
4345       * @access public
4346       */
4347      function getServerIdentification()
4348      {
4349          $this->_connect();
4350  
4351          return $this->server_identifier;
4352      }
4353  
4354      /**
4355       * Return a list of the key exchange algorithms the server supports.
4356       *
4357       * @return array
4358       * @access public
4359       */
4360      function getKexAlgorithms()
4361      {
4362          $this->_connect();
4363  
4364          return $this->kex_algorithms;
4365      }
4366  
4367      /**
4368       * Return a list of the host key (public key) algorithms the server supports.
4369       *
4370       * @return array
4371       * @access public
4372       */
4373      function getServerHostKeyAlgorithms()
4374      {
4375          $this->_connect();
4376  
4377          return $this->server_host_key_algorithms;
4378      }
4379  
4380      /**
4381       * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
4382       *
4383       * @return array
4384       * @access public
4385       */
4386      function getEncryptionAlgorithmsClient2Server()
4387      {
4388          $this->_connect();
4389  
4390          return $this->encryption_algorithms_client_to_server;
4391      }
4392  
4393      /**
4394       * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
4395       *
4396       * @return array
4397       * @access public
4398       */
4399      function getEncryptionAlgorithmsServer2Client()
4400      {
4401          $this->_connect();
4402  
4403          return $this->encryption_algorithms_server_to_client;
4404      }
4405  
4406      /**
4407       * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
4408       *
4409       * @return array
4410       * @access public
4411       */
4412      function getMACAlgorithmsClient2Server()
4413      {
4414          $this->_connect();
4415  
4416          return $this->mac_algorithms_client_to_server;
4417      }
4418  
4419      /**
4420       * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
4421       *
4422       * @return array
4423       * @access public
4424       */
4425      function getMACAlgorithmsServer2Client()
4426      {
4427          $this->_connect();
4428  
4429          return $this->mac_algorithms_server_to_client;
4430      }
4431  
4432      /**
4433       * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
4434       *
4435       * @return array
4436       * @access public
4437       */
4438      function getCompressionAlgorithmsClient2Server()
4439      {
4440          $this->_connect();
4441  
4442          return $this->compression_algorithms_client_to_server;
4443      }
4444  
4445      /**
4446       * Return a list of the compression algorithms the server supports, when sending stuff to the client.
4447       *
4448       * @return array
4449       * @access public
4450       */
4451      function getCompressionAlgorithmsServer2Client()
4452      {
4453          $this->_connect();
4454  
4455          return $this->compression_algorithms_server_to_client;
4456      }
4457  
4458      /**
4459       * Return a list of the languages the server supports, when sending stuff to the client.
4460       *
4461       * @return array
4462       * @access public
4463       */
4464      function getLanguagesServer2Client()
4465      {
4466          $this->_connect();
4467  
4468          return $this->languages_server_to_client;
4469      }
4470  
4471      /**
4472       * Return a list of the languages the server supports, when receiving stuff from the client.
4473       *
4474       * @return array
4475       * @access public
4476       */
4477      function getLanguagesClient2Server()
4478      {
4479          $this->_connect();
4480  
4481          return $this->languages_client_to_server;
4482      }
4483  
4484      /**
4485       * Returns a list of algorithms the server supports
4486       *
4487       * @return array
4488       * @access public
4489       */
4490      function getServerAlgorithms()
4491      {
4492          $this->_connect();
4493  
4494          return array(
4495              'kex' => $this->kex_algorithms,
4496              'hostkey' => $this->server_host_key_algorithms,
4497              'client_to_server' => array(
4498                  'crypt' => $this->encryption_algorithms_client_to_server,
4499                  'mac' => $this->mac_algorithms_client_to_server,
4500                  'comp' => $this->compression_algorithms_client_to_server,
4501                  'lang' => $this->languages_client_to_server
4502              ),
4503              'server_to_client' => array(
4504                  'crypt' => $this->encryption_algorithms_server_to_client,
4505                  'mac' => $this->mac_algorithms_server_to_client,
4506                  'comp' => $this->compression_algorithms_server_to_client,
4507                  'lang' => $this->languages_server_to_client
4508              )
4509          );
4510      }
4511  
4512      /**
4513       * Returns a list of KEX algorithms that phpseclib supports
4514       *
4515       * @return array
4516       * @access public
4517       */
4518      function getSupportedKEXAlgorithms()
4519      {
4520          $kex_algorithms = array(
4521              // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
4522              // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
4523              // libssh repository for more information.
4524              'curve25519-sha256@libssh.org',
4525  
4526              'diffie-hellman-group-exchange-sha256',// RFC 4419
4527              'diffie-hellman-group-exchange-sha1',  // RFC 4419
4528  
4529              // Diffie-Hellman Key Agreement (DH) using integer modulo prime
4530              // groups.
4531              'diffie-hellman-group14-sha1', // REQUIRED
4532              'diffie-hellman-group1-sha1', // REQUIRED
4533          );
4534  
4535          if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) {
4536              $kex_algorithms = array_diff(
4537                  $kex_algorithms,
4538                  array('curve25519-sha256@libssh.org')
4539              );
4540          }
4541  
4542          return $kex_algorithms;
4543      }
4544  
4545      /**
4546       * Returns a list of host key algorithms that phpseclib supports
4547       *
4548       * @return array
4549       * @access public
4550       */
4551      function getSupportedHostKeyAlgorithms()
4552      {
4553          return array(
4554              'rsa-sha2-256', // RFC 8332
4555              'rsa-sha2-512', // RFC 8332
4556              'ssh-rsa', // RECOMMENDED  sign   Raw RSA Key
4557              'ssh-dss'  // REQUIRED     sign   Raw DSS Key
4558          );
4559      }
4560  
4561      /**
4562       * Returns a list of symmetric key algorithms that phpseclib supports
4563       *
4564       * @return array
4565       * @access public
4566       */
4567      function getSupportedEncryptionAlgorithms()
4568      {
4569          $algos = array(
4570              // from <http://tools.ietf.org/html/rfc4345#section-4>:
4571              'arcfour256',
4572              'arcfour128',
4573  
4574              //'arcfour',      // OPTIONAL          the ARCFOUR stream cipher with a 128-bit key
4575  
4576              // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
4577              'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
4578              'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
4579              'aes256-ctr',     // RECOMMENDED       AES with 256-bit key
4580  
4581              'twofish128-ctr', // OPTIONAL          Twofish in SDCTR mode, with 128-bit key
4582              'twofish192-ctr', // OPTIONAL          Twofish with 192-bit key
4583              'twofish256-ctr', // OPTIONAL          Twofish with 256-bit key
4584  
4585              'aes128-cbc',     // RECOMMENDED       AES with a 128-bit key
4586              'aes192-cbc',     // OPTIONAL          AES with a 192-bit key
4587              'aes256-cbc',     // OPTIONAL          AES in CBC mode, with a 256-bit key
4588  
4589              'twofish128-cbc', // OPTIONAL          Twofish with a 128-bit key
4590              'twofish192-cbc', // OPTIONAL          Twofish with a 192-bit key
4591              'twofish256-cbc',
4592              'twofish-cbc',    // OPTIONAL          alias for "twofish256-cbc"
4593                                //                   (this is being retained for historical reasons)
4594  
4595              'blowfish-ctr',   // OPTIONAL          Blowfish in SDCTR mode
4596  
4597              'blowfish-cbc',   // OPTIONAL          Blowfish in CBC mode
4598  
4599              '3des-ctr',       // RECOMMENDED       Three-key 3DES in SDCTR mode
4600  
4601              '3des-cbc',       // REQUIRED          three-key 3DES in CBC mode
4602  
4603               //'none'           // OPTIONAL          no encryption; NOT RECOMMENDED
4604          );
4605  
4606          $engines = array(
4607              Base::ENGINE_OPENSSL,
4608              Base::ENGINE_MCRYPT,
4609              Base::ENGINE_INTERNAL
4610          );
4611  
4612          $ciphers = array();
4613          foreach ($engines as $engine) {
4614              foreach ($algos as $algo) {
4615                  $obj = $this->_encryption_algorithm_to_crypt_instance($algo);
4616                  if ($obj instanceof Rijndael) {
4617                      $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
4618                  }
4619                  switch ($algo) {
4620                      case 'arcfour128':
4621                      case 'arcfour256':
4622                          if ($engine != Base::ENGINE_INTERNAL) {
4623                              continue 2;
4624                          }
4625                  }
4626                  if ($obj->isValidEngine($engine)) {
4627                      $algos = array_diff($algos, array($algo));
4628                      $ciphers[] = $algo;
4629                  }
4630              }
4631          }
4632  
4633          return $ciphers;
4634      }
4635  
4636      /**
4637       * Returns a list of MAC algorithms that phpseclib supports
4638       *
4639       * @return array
4640       * @access public
4641       */
4642      function getSupportedMACAlgorithms()
4643      {
4644          return array(
4645              // from <http://www.ietf.org/rfc/rfc6668.txt>:
4646              'hmac-sha2-256',// RECOMMENDED     HMAC-SHA256 (digest length = key length = 32)
4647  
4648              'hmac-sha1-96', // RECOMMENDED     first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
4649              'hmac-sha1',    // REQUIRED        HMAC-SHA1 (digest length = key length = 20)
4650              'hmac-md5-96',  // OPTIONAL        first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
4651              'hmac-md5',     // OPTIONAL        HMAC-MD5 (digest length = key length = 16)
4652              //'none'          // OPTIONAL        no MAC; NOT RECOMMENDED
4653          );
4654      }
4655  
4656      /**
4657       * Returns a list of compression algorithms that phpseclib supports
4658       *
4659       * @return array
4660       * @access public
4661       */
4662      function getSupportedCompressionAlgorithms()
4663      {
4664          return array(
4665              'none'   // REQUIRED        no compression
4666              //'zlib' // OPTIONAL        ZLIB (LZ77) compression
4667          );
4668      }
4669  
4670      /**
4671       * Return list of negotiated algorithms
4672       *
4673       * Uses the same format as https://www.php.net/ssh2-methods-negotiated
4674       *
4675       * @return array
4676       * @access public
4677       */
4678      function getAlgorithmsNegotiated()
4679      {
4680          $this->_connect();
4681  
4682          return array(
4683              'kex' => $this->kex_algorithm,
4684              'hostkey' => $this->signature_format,
4685              'client_to_server' => array(
4686                  'crypt' => $this->encrypt->name,
4687                  'mac' => $this->hmac_create->name,
4688                  'comp' => 'none',
4689              ),
4690              'server_to_client' => array(
4691                  'crypt' => $this->decrypt->name,
4692                  'mac' => $this->hmac_check->name,
4693                  'comp' => 'none',
4694              )
4695          );
4696      }
4697  
4698      /**
4699       * Accepts an associative array with up to four parameters as described at
4700       * <https://www.php.net/manual/en/function.ssh2-connect.php>
4701       *
4702       * @param array $methods
4703       * @access public
4704       */
4705      function setPreferredAlgorithms($methods)
4706      {
4707          $preferred = $methods;
4708  
4709          if (isset($preferred['kex'])) {
4710              $preferred['kex'] = array_intersect(
4711                  $preferred['kex'],
4712                  $this->getSupportedKEXAlgorithms()
4713              );
4714          }
4715  
4716          if (isset($preferred['hostkey'])) {
4717              $preferred['hostkey'] = array_intersect(
4718                  $preferred['hostkey'],
4719                  $this->getSupportedHostKeyAlgorithms()
4720              );
4721          }
4722  
4723          $keys = array('client_to_server', 'server_to_client');
4724          foreach ($keys as $key) {
4725              if (isset($preferred[$key])) {
4726                  $a = &$preferred[$key];
4727                  if (isset($a['crypt'])) {
4728                      $a['crypt'] = array_intersect(
4729                          $a['crypt'],
4730                          $this->getSupportedEncryptionAlgorithms()
4731                      );
4732                  }
4733                  if (isset($a['comp'])) {
4734                      $a['comp'] =