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