[ 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 = array('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       * Return the currently configured timeout
2865       *
2866       * @return int
2867       */
2868      function getTimeout()
2869      {
2870          return $this->timeout;
2871      }
2872  
2873      /**
2874       * Set Timeout
2875       *
2876       * $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.
2877       * Setting $timeout to false or 0 will mean there is no timeout.
2878       *
2879       * @param mixed $timeout
2880       * @access public
2881       */
2882      function setTimeout($timeout)
2883      {
2884          $this->timeout = $this->curTimeout = $timeout;
2885      }
2886  
2887      /**
2888       * Set Keep Alive
2889       *
2890       * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
2891       *
2892       * @param int $interval
2893       * @access public
2894       */
2895      function setKeepAlive($interval)
2896      {
2897          $this->keepAlive = $interval;
2898      }
2899  
2900      /**
2901       * Get the output from stdError
2902       *
2903       * @access public
2904       */
2905      function getStdError()
2906      {
2907          return $this->stdErrorLog;
2908      }
2909  
2910      /**
2911       * Execute Command
2912       *
2913       * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
2914       * In all likelihood, this is not a feature you want to be taking advantage of.
2915       *
2916       * @param string $command
2917       * @param Callback $callback
2918       * @return string
2919       * @access public
2920       */
2921      function exec($command, $callback = null)
2922      {
2923          $this->curTimeout = $this->timeout;
2924          $this->is_timeout = false;
2925          $this->stdErrorLog = '';
2926  
2927          if (!$this->isAuthenticated()) {
2928              return false;
2929          }
2930  
2931          if ($this->in_request_pty_exec) {
2932              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.');
2933              return false;
2934          }
2935  
2936          // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
2937          // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
2938          // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
2939          // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
2940          $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
2941          // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
2942          // uses 0x4000, that's what will be used here, as well.
2943          $packet_size = 0x4000;
2944  
2945          $packet = pack(
2946              'CNa*N3',
2947              NET_SSH2_MSG_CHANNEL_OPEN,
2948              strlen('session'),
2949              'session',
2950              self::CHANNEL_EXEC,
2951              $this->window_size_server_to_client[self::CHANNEL_EXEC],
2952              $packet_size
2953          );
2954  
2955          if (!$this->_send_binary_packet($packet)) {
2956              return false;
2957          }
2958  
2959          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
2960  
2961          $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2962          if ($response === false) {
2963              return false;
2964          }
2965  
2966          if ($this->request_pty === true) {
2967              $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2968              $packet = pack(
2969                  'CNNa*CNa*N5a*',
2970                  NET_SSH2_MSG_CHANNEL_REQUEST,
2971                  $this->server_channels[self::CHANNEL_EXEC],
2972                  strlen('pty-req'),
2973                  'pty-req',
2974                  1,
2975                  strlen('vt100'),
2976                  'vt100',
2977                  $this->windowColumns,
2978                  $this->windowRows,
2979                  0,
2980                  0,
2981                  strlen($terminal_modes),
2982                  $terminal_modes
2983              );
2984  
2985              if (!$this->_send_binary_packet($packet)) {
2986                  return false;
2987              }
2988  
2989              $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2990              if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) {
2991                  user_error('Unable to request pseudo-terminal');
2992                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2993              }
2994  
2995              $this->in_request_pty_exec = true;
2996          }
2997  
2998          // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
2999          // down.  the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &').
3000          // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
3001          // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
3002          // neither will your script.
3003  
3004          // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
3005          // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
3006          // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
3007          $packet = pack(
3008              'CNNa*CNa*',
3009              NET_SSH2_MSG_CHANNEL_REQUEST,
3010              $this->server_channels[self::CHANNEL_EXEC],
3011              strlen('exec'),
3012              'exec',
3013              1,
3014              strlen($command),
3015              $command
3016          );
3017          if (!$this->_send_binary_packet($packet)) {
3018              return false;
3019          }
3020  
3021          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
3022  
3023          $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
3024          if ($response === false) {
3025              return false;
3026          }
3027  
3028          $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
3029  
3030          if ($callback === false || $this->in_request_pty_exec) {
3031              return true;
3032          }
3033  
3034          $output = '';
3035          while (true) {
3036              $temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
3037              switch (true) {
3038                  case $temp === true:
3039                      return is_callable($callback) ? true : $output;
3040                  case $temp === false:
3041                      return false;
3042                  default:
3043                      if (is_callable($callback)) {
3044                          if (call_user_func($callback, $temp) === true) {
3045                              $this->_close_channel(self::CHANNEL_EXEC);
3046                              return true;
3047                          }
3048                      } else {
3049                          $output.= $temp;
3050                      }
3051              }
3052          }
3053      }
3054  
3055      /**
3056       * Creates an interactive shell
3057       *
3058       * @see self::read()
3059       * @see self::write()
3060       * @return bool
3061       * @access private
3062       */
3063      function _initShell()
3064      {
3065          if ($this->in_request_pty_exec === true) {
3066              return true;
3067          }
3068  
3069          $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
3070          $packet_size = 0x4000;
3071  
3072          $packet = pack(
3073              'CNa*N3',
3074              NET_SSH2_MSG_CHANNEL_OPEN,
3075              strlen('session'),
3076              'session',
3077              self::CHANNEL_SHELL,
3078              $this->window_size_server_to_client[self::CHANNEL_SHELL],
3079              $packet_size
3080          );
3081  
3082          if (!$this->_send_binary_packet($packet)) {
3083              return false;
3084          }
3085  
3086          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
3087  
3088          $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
3089          if ($response === false) {
3090              return false;
3091          }
3092  
3093          $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
3094          $packet = pack(
3095              'CNNa*CNa*N5a*',
3096              NET_SSH2_MSG_CHANNEL_REQUEST,
3097              $this->server_channels[self::CHANNEL_SHELL],
3098              strlen('pty-req'),
3099              'pty-req',
3100              1,
3101              strlen('vt100'),
3102              'vt100',
3103              $this->windowColumns,
3104              $this->windowRows,
3105              0,
3106              0,
3107              strlen($terminal_modes),
3108              $terminal_modes
3109          );
3110  
3111          if (!$this->_send_binary_packet($packet)) {
3112              return false;
3113          }
3114  
3115          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
3116  
3117          if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) {
3118              user_error('Unable to request pseudo-terminal');
3119              return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3120          }
3121  
3122          $packet = pack(
3123              'CNNa*C',
3124              NET_SSH2_MSG_CHANNEL_REQUEST,
3125              $this->server_channels[self::CHANNEL_SHELL],
3126              strlen('shell'),
3127              'shell',
3128              1
3129          );
3130          if (!$this->_send_binary_packet($packet)) {
3131              return false;
3132          }
3133  
3134          $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
3135          if ($response === false) {
3136              return false;
3137          }
3138  
3139          $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
3140  
3141          $this->bitmap |= self::MASK_SHELL;
3142  
3143          return true;
3144      }
3145  
3146      /**
3147       * Return the channel to be used with read() / write()
3148       *
3149       * @see self::read()
3150       * @see self::write()
3151       * @return int
3152       * @access public
3153       */
3154      function _get_interactive_channel()
3155      {
3156          switch (true) {
3157              case $this->in_subsystem:
3158                  return self::CHANNEL_SUBSYSTEM;
3159              case $this->in_request_pty_exec:
3160                  return self::CHANNEL_EXEC;
3161              default:
3162                  return self::CHANNEL_SHELL;
3163          }
3164      }
3165  
3166      /**
3167       * Return an available open channel
3168       *
3169       * @return int
3170       * @access public
3171       */
3172      function _get_open_channel()
3173      {
3174          $channel = self::CHANNEL_EXEC;
3175          do {
3176              if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
3177                  return $channel;
3178              }
3179          } while ($channel++ < self::CHANNEL_SUBSYSTEM);
3180  
3181          return false;
3182      }
3183  
3184      /**
3185       * Returns the output of an interactive shell
3186       *
3187       * Returns when there's a match for $expect, which can take the form of a string literal or,
3188       * if $mode == self::READ_REGEX, a regular expression.
3189       *
3190       * @see self::write()
3191       * @param string $expect
3192       * @param int $mode
3193       * @return string|bool
3194       * @access public
3195       */
3196      function read($expect = '', $mode = self::READ_SIMPLE)
3197      {
3198          $this->curTimeout = $this->timeout;
3199          $this->is_timeout = false;
3200  
3201          if (!$this->isAuthenticated()) {
3202              user_error('Operation disallowed prior to login()');
3203              return false;
3204          }
3205  
3206          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
3207              user_error('Unable to initiate an interactive shell session');
3208              return false;
3209          }
3210  
3211          $channel = $this->_get_interactive_channel();
3212  
3213          if ($mode == self::READ_NEXT) {
3214              return $this->_get_channel_packet($channel);
3215          }
3216  
3217          $match = $expect;
3218          while (true) {
3219              if ($mode == self::READ_REGEX) {
3220                  preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
3221                  $match = isset($matches[0]) ? $matches[0] : '';
3222              }
3223              $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
3224              if ($pos !== false) {
3225                  return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
3226              }
3227              $response = $this->_get_channel_packet($channel);
3228              if (is_bool($response)) {
3229                  $this->in_request_pty_exec = false;
3230                  return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
3231              }
3232  
3233              $this->interactiveBuffer.= $response;
3234          }
3235      }
3236  
3237      /**
3238       * Inputs a command into an interactive shell.
3239       *
3240       * @see self::read()
3241       * @param string $cmd
3242       * @return bool
3243       * @access public
3244       */
3245      function write($cmd)
3246      {
3247          if (!$this->isAuthenticated()) {
3248              user_error('Operation disallowed prior to login()');
3249              return false;
3250          }
3251  
3252          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
3253              user_error('Unable to initiate an interactive shell session');
3254              return false;
3255          }
3256  
3257          return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
3258      }
3259  
3260      /**
3261       * Start a subsystem.
3262       *
3263       * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
3264       * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
3265       * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
3266       * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
3267       * if there's sufficient demand for such a feature.
3268       *
3269       * @see self::stopSubsystem()
3270       * @param string $subsystem
3271       * @return bool
3272       * @access public
3273       */
3274      function startSubsystem($subsystem)
3275      {
3276          $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
3277  
3278          $packet = pack(
3279              'CNa*N3',
3280              NET_SSH2_MSG_CHANNEL_OPEN,
3281              strlen('session'),
3282              'session',
3283              self::CHANNEL_SUBSYSTEM,
3284              $this->window_size,
3285              0x4000
3286          );
3287  
3288          if (!$this->_send_binary_packet($packet)) {
3289              return false;
3290          }
3291  
3292          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
3293  
3294          $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
3295          if ($response === false) {
3296              return false;
3297          }
3298  
3299          $packet = pack(
3300              'CNNa*CNa*',
3301              NET_SSH2_MSG_CHANNEL_REQUEST,
3302              $this->server_channels[self::CHANNEL_SUBSYSTEM],
3303              strlen('subsystem'),
3304              'subsystem',
3305              1,
3306              strlen($subsystem),
3307              $subsystem
3308          );
3309          if (!$this->_send_binary_packet($packet)) {
3310              return false;
3311          }
3312  
3313          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
3314  
3315          $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
3316  
3317          if ($response === false) {
3318              return false;
3319          }
3320  
3321          $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
3322  
3323          $this->bitmap |= self::MASK_SHELL;
3324          $this->in_subsystem = true;
3325  
3326          return true;
3327      }
3328  
3329      /**
3330       * Stops a subsystem.
3331       *
3332       * @see self::startSubsystem()
3333       * @return bool
3334       * @access public
3335       */
3336      function stopSubsystem()
3337      {
3338          $this->in_subsystem = false;
3339          $this->_close_channel(self::CHANNEL_SUBSYSTEM);
3340          return true;
3341      }
3342  
3343      /**
3344       * Closes a channel
3345       *
3346       * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
3347       *
3348       * @access public
3349       */
3350      function reset()
3351      {
3352          $this->_close_channel($this->_get_interactive_channel());
3353      }
3354  
3355      /**
3356       * Is timeout?
3357       *
3358       * Did exec() or read() return because they timed out or because they encountered the end?
3359       *
3360       * @access public
3361       */
3362      function isTimeout()
3363      {
3364          return $this->is_timeout;
3365      }
3366  
3367      /**
3368       * Disconnect
3369       *
3370       * @access public
3371       */
3372      function disconnect()
3373      {
3374          $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3375          if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
3376              fclose($this->realtime_log_file);
3377          }
3378      }
3379  
3380      /**
3381       * Destructor.
3382       *
3383       * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
3384       * disconnect().
3385       *
3386       * @access public
3387       */
3388      function __destruct()
3389      {
3390          $this->disconnect();
3391      }
3392  
3393      /**
3394       * Is the connection still active?
3395       *
3396       * @return bool
3397       * @access public
3398       */
3399      function isConnected()
3400      {
3401          return (bool) ($this->bitmap & self::MASK_CONNECTED);
3402      }
3403  
3404      /**
3405       * Have you successfully been logged in?
3406       *
3407       * @return bool
3408       * @access public
3409       */
3410      function isAuthenticated()
3411      {
3412          return (bool) ($this->bitmap & self::MASK_LOGIN);
3413      }
3414  
3415      /**
3416       * Pings a server connection, or tries to reconnect if the connection has gone down
3417       *
3418       * Inspired by http://php.net/manual/en/mysqli.ping.php
3419       *
3420       * @return bool
3421       * @access public
3422       */
3423      function ping()
3424      {
3425          if (!$this->isAuthenticated()) {
3426              if (!empty($this->auth)) {
3427                  return $this->_reconnect();
3428              }
3429              return false;
3430          }
3431  
3432          $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
3433          $packet_size = 0x4000;
3434          $packet = pack(
3435              'CNa*N3',
3436              NET_SSH2_MSG_CHANNEL_OPEN,
3437              strlen('session'),
3438              'session',
3439              self::CHANNEL_KEEP_ALIVE,
3440              $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
3441              $packet_size
3442          );
3443  
3444          if (!@$this->_send_binary_packet($packet)) {
3445              return $this->_reconnect();
3446          }
3447  
3448          $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
3449  
3450          $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
3451          if ($response !== false) {
3452              $this->_close_channel(self::CHANNEL_KEEP_ALIVE);
3453              return true;
3454          }
3455  
3456          return $this->_reconnect();
3457      }
3458  
3459      /**
3460       * In situ reconnect method
3461       *
3462       * @return boolean
3463       * @access private
3464       */
3465      function _reconnect()
3466      {
3467          $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
3468          $this->retry_connect = true;
3469          if (!$this->_connect()) {
3470              return false;
3471          }
3472          foreach ($this->auth as $auth) {
3473              $result = call_user_func_array(array(&$this, 'login'), $auth);
3474          }
3475          return $result;
3476      }
3477  
3478      /**
3479       * Resets a connection for re-use
3480       *
3481       * @param int $reason
3482       * @access private
3483       */
3484      function _reset_connection($reason)
3485      {
3486          $this->_disconnect($reason);
3487          $this->decrypt = $this->encrypt = false;
3488          $this->decrypt_block_size = $this->encrypt_block_size = 8;
3489          $this->hmac_check = $this->hmac_create = false;
3490          $this->hmac_size = false;
3491          $this->session_id = false;
3492          $this->retry_connect = true;
3493          $this->get_seq_no = $this->send_seq_no = 0;
3494      }
3495  
3496      /**
3497       * Gets Binary Packets
3498       *
3499       * See '6. Binary Packet Protocol' of rfc4253 for more info.
3500       *
3501       * @see self::_send_binary_packet()
3502       * @return string
3503       * @access private
3504       */
3505      function _get_binary_packet($skip_channel_filter = false)
3506      {
3507          if ($skip_channel_filter) {
3508              $read = array($this->fsock);
3509              $write = $except = null;
3510  
3511              if (!$this->curTimeout) {
3512                  if ($this->keepAlive <= 0) {
3513                      @stream_select($read, $write, $except, null);
3514                  } else {
3515                      if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
3516                          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
3517                          return $this->_get_binary_packet(true);
3518                      }
3519                  }
3520              } else {
3521                  if ($this->curTimeout < 0) {
3522                      $this->is_timeout = true;
3523                      return true;
3524                  }
3525  
3526                  $read = array($this->fsock);
3527                  $write = $except = null;
3528  
3529                  $start = microtime(true);
3530  
3531                  if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
3532                      if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
3533                          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
3534                          $elapsed = microtime(true) - $start;
3535                          $this->curTimeout-= $elapsed;
3536                          return $this->_get_binary_packet(true);
3537                      }
3538                      $elapsed = microtime(true) - $start;
3539                      $this->curTimeout-= $elapsed;
3540                  }
3541  
3542                  $sec = (int)floor($this->curTimeout);
3543                  $usec = (int)(1000000 * ($this->curTimeout - $sec));
3544  
3545                  // on windows this returns a "Warning: Invalid CRT parameters detected" error
3546                  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3547                      $this->is_timeout = true;
3548                      return true;
3549                  }
3550                  $elapsed = microtime(true) - $start;
3551                  $this->curTimeout-= $elapsed;
3552              }
3553          }
3554  
3555          if (!is_resource($this->fsock) || feof($this->fsock)) {
3556              $this->bitmap = 0;
3557              $str = 'Connection closed (by server) prematurely';
3558              if (isset($elapsed)) {
3559                  $str.= ' ' . $elapsed . 's';
3560              }
3561              user_error($str);
3562              return false;
3563          }
3564  
3565          $start = microtime(true);
3566          $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
3567  
3568          if (!strlen($raw)) {
3569              user_error('No data received from server');
3570              return false;
3571          }
3572  
3573          if ($this->decrypt !== false) {
3574              $raw = $this->decrypt->decrypt($raw);
3575          }
3576          if ($raw === false) {
3577              user_error('Unable to decrypt content');
3578              return false;
3579          }
3580  
3581          if (strlen($raw) < 5) {
3582              return false;
3583          }
3584          extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
3585  
3586          $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
3587  
3588          // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
3589          // "implementations SHOULD check that the packet length is reasonable"
3590          // PuTTY uses 0x9000 as the actual max packet size and so to shall we
3591          if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
3592              if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) {
3593                  $this->bad_key_size_fix = true;
3594                  $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
3595                  return false;
3596              }
3597              user_error('Invalid size');
3598              return false;
3599          }
3600  
3601          $buffer = '';
3602          while ($remaining_length > 0) {
3603              $temp = stream_get_contents($this->fsock, $remaining_length);
3604              if ($temp === false || feof($this->fsock)) {
3605                  $this->bitmap = 0;
3606                  user_error('Error reading from socket');
3607                  return false;
3608              }
3609              $buffer.= $temp;
3610              $remaining_length-= strlen($temp);
3611          }
3612  
3613          $stop = microtime(true);
3614          if (strlen($buffer)) {
3615              $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
3616          }
3617  
3618          $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
3619          $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
3620  
3621          if ($this->hmac_check !== false) {
3622              $hmac = stream_get_contents($this->fsock, $this->hmac_size);
3623              if ($hmac === false || strlen($hmac) != $this->hmac_size) {
3624                  $this->bitmap = 0;
3625                  user_error('Error reading socket');
3626                  return false;
3627              } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
3628                  user_error('Invalid HMAC');
3629                  return false;
3630              }
3631          }
3632  
3633          switch ($this->decompress) {
3634              case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
3635                  if (!$this->isAuthenticated()) {
3636                      break;
3637                  }
3638              case self::NET_SSH2_COMPRESSION_ZLIB:
3639                  if ($this->regenerate_decompression_context) {
3640                      $this->regenerate_decompression_context = false;
3641  
3642                      $cmf = ord($payload[0]);
3643                      $cm = $cmf & 0x0F;
3644                      if ($cm != 8) { // deflate
3645                          user_error("Only CM = 8 ('deflate') is supported ($cm)");
3646                      }
3647                      $cinfo = ($cmf & 0xF0) >> 4;
3648                      if ($cinfo > 7) {
3649                          user_error("CINFO above 7 is not allowed ($cinfo)");
3650                      }
3651                      $windowSize = 1 << ($cinfo + 8);
3652  
3653                      $flg = ord($payload[1]);
3654                      //$fcheck = $flg && 0x0F;
3655                      if ((($cmf << 8) | $flg) % 31) {
3656                          user_error('fcheck failed');
3657                      }
3658                      $fdict = boolval($flg & 0x20);
3659                      $flevel = ($flg & 0xC0) >> 6;
3660  
3661                      $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8));
3662                      $payload = substr($payload, 2);
3663                  }
3664                  if ($this->decompress_context) {
3665                      $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
3666                  }
3667          }
3668  
3669          $this->get_seq_no++;
3670  
3671          if (defined('NET_SSH2_LOGGING')) {
3672              $current = microtime(true);
3673              $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
3674              $message_number = '<- ' . $message_number .
3675                                ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3676              $this->_append_log($message_number, $payload);
3677              $this->last_packet = $current;
3678          }
3679  
3680          return $this->_filter($payload, $skip_channel_filter);
3681      }
3682  
3683      /**
3684       * Filter Binary Packets
3685       *
3686       * Because some binary packets need to be ignored...
3687       *
3688       * @see self::_get_binary_packet()
3689       * @return string
3690       * @access private
3691       */
3692      function _filter($payload, $skip_channel_filter)
3693      {
3694          switch (ord($payload[0])) {
3695              case NET_SSH2_MSG_DISCONNECT:
3696                  $this->_string_shift($payload, 1);
3697                  if (strlen($payload) < 8) {
3698                      return false;
3699                  }
3700                  extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
3701                  $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
3702                  $this->bitmap = 0;
3703                  return false;
3704              case NET_SSH2_MSG_IGNORE:
3705                  $payload = $this->_get_binary_packet($skip_channel_filter);
3706                  break;
3707              case NET_SSH2_MSG_DEBUG:
3708                  $this->_string_shift($payload, 2);
3709                  if (strlen($payload) < 4) {
3710                      return false;
3711                  }
3712                  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3713                  $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
3714                  $payload = $this->_get_binary_packet($skip_channel_filter);
3715                  break;
3716              case NET_SSH2_MSG_UNIMPLEMENTED:
3717                  return false;
3718              case NET_SSH2_MSG_KEXINIT:
3719                  if ($this->session_id !== false) {
3720                      $this->send_kex_first = false;
3721                      if (!$this->_key_exchange($payload)) {
3722                          $this->bitmap = 0;
3723                          return false;
3724                      }
3725                      $payload = $this->_get_binary_packet($skip_channel_filter);
3726                  }
3727          }
3728  
3729          // 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
3730          if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
3731              $this->_string_shift($payload, 1);
3732              if (strlen($payload) < 4) {
3733                  return false;
3734              }
3735              extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3736              $this->banner_message = $this->_string_shift($payload, $length);
3737              $payload = $this->_get_binary_packet();
3738          }
3739  
3740          // only called when we've already logged in
3741          if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
3742              if (is_bool($payload)) {
3743                  return $payload;
3744              }
3745  
3746              switch (ord($payload[0])) {
3747                  case NET_SSH2_MSG_CHANNEL_REQUEST:
3748                      if (strlen($payload) == 31) {
3749                          extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
3750                          if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
3751                              if (ord(substr($payload, 9 + $length))) { // want reply
3752                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
3753                              }
3754                              $payload = $this->_get_binary_packet($skip_channel_filter);
3755                          }
3756                      }
3757                      break;
3758                  case NET_SSH2_MSG_CHANNEL_DATA:
3759                  case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3760                  case NET_SSH2_MSG_CHANNEL_CLOSE:
3761                  case NET_SSH2_MSG_CHANNEL_EOF:
3762                      if (!$skip_channel_filter && !empty($this->server_channels)) {
3763                          $this->binary_packet_buffer = $payload;
3764                          $this->_get_channel_packet(true);
3765                          $payload = $this->_get_binary_packet();
3766                      }
3767                      break;
3768                  case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
3769                      if (strlen($payload) < 4) {
3770                          return false;
3771                      }
3772                      extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3773                      $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);
3774  
3775                      if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
3776                          return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3777                      }
3778  
3779                      $payload = $this->_get_binary_packet($skip_channel_filter);
3780                      break;
3781                  case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
3782                      $this->_string_shift($payload, 1);
3783                      if (strlen($payload) < 4) {
3784                          return false;
3785                      }
3786                      extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3787                      $data = $this->_string_shift($payload, $length);
3788                      if (strlen($payload) < 4) {
3789                          return false;
3790                      }
3791                      extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
3792                      switch ($data) {
3793                          case 'auth-agent':
3794                          case 'auth-agent@openssh.com':
3795                              if (isset($this->agent)) {
3796                                  $new_channel = self::CHANNEL_AGENT_FORWARD;
3797  
3798                                  if (strlen($payload) < 8) {
3799                                      return false;
3800                                  }
3801                                  extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
3802                                  extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));
3803  
3804                                  $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
3805                                  $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
3806                                  $this->window_size_client_to_server[$new_channel] = $this->window_size;
3807  
3808                                  $packet_size = 0x4000;
3809  
3810                                  $packet = pack(
3811                                      'CN4',
3812                                      NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
3813                                      $server_channel,
3814                                      $new_channel,
3815                                      $packet_size,
3816                                      $packet_size
3817                                  );
3818  
3819                                  $this->server_channels[$new_channel] = $server_channel;
3820                                  $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
3821                                  if (!$this->_send_binary_packet($packet)) {
3822                                      return false;
3823                                  }
3824                              }
3825                              break;
3826                          default:
3827                              $packet = pack(
3828                                  'CN3a*Na*',
3829                                  NET_SSH2_MSG_REQUEST_FAILURE,
3830                                  $server_channel,
3831                                  NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
3832                                  0,
3833                                  '',
3834                                  0,
3835                                  ''
3836                              );
3837  
3838                              if (!$this->_send_binary_packet($packet)) {
3839                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3840                              }
3841                      }
3842                      $payload = $this->_get_binary_packet($skip_channel_filter);
3843                      break;
3844                  case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3845                      $this->_string_shift($payload, 1);
3846                      if (strlen($payload) < 8) {
3847                          return false;
3848                      }
3849                      extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
3850                      extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
3851                      $this->window_size_client_to_server[$channel]+= $window_size;
3852  
3853                      $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
3854              }
3855          }
3856  
3857          return $payload;
3858      }
3859  
3860      /**
3861       * Enable Quiet Mode
3862       *
3863       * Suppress stderr from output
3864       *
3865       * @access public
3866       */
3867      function enableQuietMode()
3868      {
3869          $this->quiet_mode = true;
3870      }
3871  
3872      /**
3873       * Disable Quiet Mode
3874       *
3875       * Show stderr in output
3876       *
3877       * @access public
3878       */
3879      function disableQuietMode()
3880      {
3881          $this->quiet_mode = false;
3882      }
3883  
3884      /**
3885       * Returns whether Quiet Mode is enabled or not
3886       *
3887       * @see self::enableQuietMode()
3888       * @see self::disableQuietMode()
3889       * @access public
3890       * @return bool
3891       */
3892      function isQuietModeEnabled()
3893      {
3894          return $this->quiet_mode;
3895      }
3896  
3897      /**
3898       * Enable request-pty when using exec()
3899       *
3900       * @access public
3901       */
3902      function enablePTY()
3903      {
3904          $this->request_pty = true;
3905      }
3906  
3907      /**
3908       * Disable request-pty when using exec()
3909       *
3910       * @access public
3911       */
3912      function disablePTY()
3913      {
3914          if ($this->in_request_pty_exec) {
3915              $this->_close_channel(self::CHANNEL_EXEC);
3916              $this->in_request_pty_exec = false;
3917          }
3918          $this->request_pty = false;
3919      }
3920  
3921      /**
3922       * Returns whether request-pty is enabled or not
3923       *
3924       * @see self::enablePTY()
3925       * @see self::disablePTY()
3926       * @access public
3927       * @return bool
3928       */
3929      function isPTYEnabled()
3930      {
3931          return $this->request_pty;
3932      }
3933  
3934      /**
3935       * Gets channel data
3936       *
3937       * Returns the data as a string if it's available and false if not.
3938       *
3939       * @param int $client_channel
3940       * @param bool $skip_extended
3941       * @return mixed|bool
3942       * @access private
3943       */
3944      function _get_channel_packet($client_channel, $skip_extended = false)
3945      {
3946          if (!empty($this->channel_buffers[$client_channel])) {
3947              switch ($this->channel_status[$client_channel]) {
3948                  case NET_SSH2_MSG_CHANNEL_REQUEST:
3949                      foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
3950                          switch (ord($packet[0])) {
3951                              case NET_SSH2_MSG_CHANNEL_SUCCESS:
3952                              case NET_SSH2_MSG_CHANNEL_FAILURE:
3953                                  unset($this->channel_buffers[$client_channel][$i]);
3954                                  return substr($packet, 1);
3955                          }
3956                      }
3957                      break;
3958                  default:
3959                      return substr(array_shift($this->channel_buffers[$client_channel]), 1);
3960              }
3961          }
3962  
3963          while (true) {
3964              if ($this->binary_packet_buffer !== false) {
3965                  $response = $this->binary_packet_buffer;
3966                  $this->binary_packet_buffer = false;
3967              } else {
3968                  $response = $this->_get_binary_packet(true);
3969                  if ($response === true && $this->is_timeout) {
3970                      if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) {
3971                          $this->_close_channel($client_channel);
3972                      }
3973                      return true;
3974                  }
3975                  if ($response === false) {
3976                      $this->bitmap = 0;
3977                      user_error('Connection closed by server');
3978                      return false;
3979                  }
3980              }
3981  
3982              if ($client_channel == -1 && $response === true) {
3983                  return true;
3984              }
3985              if (!strlen($response)) {
3986                  return false;
3987              }
3988              extract(unpack('Ctype', $this->_string_shift($response, 1)));
3989  
3990              if (strlen($response) < 4) {
3991                  return false;
3992              }
3993              if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
3994                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3995              } else {
3996                  extract(unpack('Nchannel', $this->_string_shift($response, 4)));
3997              }
3998  
3999              // will not be setup yet on incoming channel open request
4000              if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
4001                  $this->window_size_server_to_client[$channel]-= strlen($response);
4002  
4003                  // resize the window, if appropriate
4004                  if ($this->window_size_server_to_client[$channel] < 0) {
4005                  // PuTTY does something more analogous to the following:
4006                  //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
4007                      $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
4008                      if (!$this->_send_binary_packet($packet)) {
4009                          return false;
4010                      }
4011                      $this->window_size_server_to_client[$channel]+= $this->window_resize;
4012                  }
4013  
4014                  switch ($type) {
4015                      case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
4016                          /*
4017                          if ($client_channel == self::CHANNEL_EXEC) {
4018                              $this->_send_channel_packet($client_channel, chr(0));
4019                          }
4020                          */
4021                          // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
4022                          if (strlen($response) < 8) {
4023                              return false;
4024                          }
4025                          extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
4026                          $data = $this->_string_shift($response, $length);
4027                          $this->stdErrorLog.= $data;
4028                          if ($skip_extended || $this->quiet_mode) {
4029                              continue 2;
4030                          }
4031                          if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
4032                              return $data;
4033                          }
4034                          $this->channel_buffers[$channel][] = chr($type) . $data;
4035  
4036                          continue 2;
4037                      case NET_SSH2_MSG_CHANNEL_REQUEST:
4038                          if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
4039                              continue 2;
4040                          }
4041                          if (strlen($response) < 4) {
4042                              return false;
4043                          }
4044                          extract(unpack('Nlength', $this->_string_shift($response, 4)));
4045                          $value = $this->_string_shift($response, $length);
4046                          switch ($value) {
4047                              case 'exit-signal':
4048                                  $this->_string_shift($response, 1);
4049                                  if (strlen($response) < 4) {
4050                                      return false;
4051                                  }
4052                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
4053                                  $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
4054                                  $this->_string_shift($response, 1);
4055                                  if (strlen($response) < 4) {
4056                                      return false;
4057                                  }
4058                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
4059                                  if ($length) {
4060                                      $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
4061                                  }
4062  
4063                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
4064                                  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
4065  
4066                                  $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
4067  
4068                                  continue 3;
4069                              case 'exit-status':
4070                                  if (strlen($response) < 5) {
4071                                      return false;
4072                                  }
4073                                  extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
4074                                  $this->exit_status = $exit_status;
4075  
4076                                  // "The client MAY ignore these messages."
4077                                  // -- http://tools.ietf.org/html/rfc4254#section-6.10
4078  
4079                                  continue 3;
4080                              default:
4081                                  // "Some systems may not implement signals, in which case they SHOULD ignore this message."
4082                                  //  -- http://tools.ietf.org/html/rfc4254#section-6.9
4083                                  continue 3;
4084                          }
4085                  }
4086  
4087                  switch ($this->channel_status[$channel]) {
4088                      case NET_SSH2_MSG_CHANNEL_OPEN:
4089                          switch ($type) {
4090                              case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
4091                                  if (strlen($response) < 4) {
4092                                      return false;
4093                                  }
4094                                  extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
4095                                  $this->server_channels[$channel] = $server_channel;
4096                                  if (strlen($response) < 4) {
4097                                      return false;
4098                                  }
4099                                  extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
4100                                  if ($window_size < 0) {
4101                                      $window_size&= 0x7FFFFFFF;
4102                                      $window_size+= 0x80000000;
4103                                  }
4104                                  $this->window_size_client_to_server[$channel] = $window_size;
4105                                  if (strlen($response) < 4) {
4106                                       return false;
4107                                  }
4108                                  $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
4109                                  $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
4110                                  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
4111                                  $this->_on_channel_open();
4112                                  return $result;
4113                              case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
4114                                  user_error('Unable to open channel');
4115                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4116                              default:
4117                                  if ($client_channel == $channel) {
4118                                      user_error('Unexpected response to open request');
4119                                      return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4120                                  }
4121                                  return $this->_get_channel_packet($client_channel, $skip_extended);
4122                          }
4123                          break;
4124                      case NET_SSH2_MSG_CHANNEL_REQUEST:
4125                          switch ($type) {
4126                              case NET_SSH2_MSG_CHANNEL_SUCCESS:
4127                                  return true;
4128                              case NET_SSH2_MSG_CHANNEL_FAILURE:
4129                                  return false;
4130                              case NET_SSH2_MSG_CHANNEL_DATA:
4131                                  if (strlen($response) < 4) {
4132                                      return false;
4133                                  }
4134                                  extract(unpack('Nlength', $this->_string_shift($response, 4)));
4135                                  $data = $this->_string_shift($response, $length);
4136                                  $this->channel_buffers[$channel][] = chr($type) . $data;
4137                                  return $this->_get_channel_packet($client_channel, $skip_extended);
4138                              default:
4139                                  user_error('Unable to fulfill channel request');
4140                                  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4141                          }
4142                      case NET_SSH2_MSG_CHANNEL_CLOSE:
4143                          return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
4144                  }
4145              }
4146  
4147              // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
4148  
4149              switch ($type) {
4150                  case NET_SSH2_MSG_CHANNEL_DATA:
4151                      /*
4152                      if ($channel == self::CHANNEL_EXEC) {
4153                          // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
4154                          // this actually seems to make things twice as fast.  more to the point, the message right after
4155                          // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
4156                          // in OpenSSH it slows things down but only by a couple thousandths of a second.
4157                          $this->_send_channel_packet($channel, chr(0));
4158                      }
4159                      */
4160                      if (strlen($response) < 4) {
4161                          return false;
4162                      }
4163                      extract(unpack('Nlength', $this->_string_shift($response, 4)));
4164                      $data = $this->_string_shift($response, $length);
4165  
4166                      if ($channel == self::CHANNEL_AGENT_FORWARD) {
4167                          $agent_response = $this->agent->_forward_data($data);
4168                          if (!is_bool($agent_response)) {
4169                              $this->_send_channel_packet($channel, $agent_response);
4170                          }
4171                          break;
4172                      }
4173  
4174                      if ($client_channel == $channel) {
4175                          return $data;
4176                      }
4177                      $this->channel_buffers[$channel][] = chr($type) . $data;
4178                      break;
4179                  case NET_SSH2_MSG_CHANNEL_CLOSE:
4180                      $this->curTimeout = 5;
4181  
4182                      if ($this->bitmap & self::MASK_SHELL) {
4183                          $this->bitmap&= ~self::MASK_SHELL;
4184                      }
4185                      if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
4186                          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
4187                      }
4188  
4189                      $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4190                      if ($client_channel == $channel) {
4191                          return true;
4192                      }
4193                  case NET_SSH2_MSG_CHANNEL_EOF:
4194                      break;
4195                  default:
4196                      user_error("Error reading channel data ($type)");
4197                      return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4198              }
4199          }
4200      }
4201  
4202      /**
4203       * Sends Binary Packets
4204       *
4205       * See '6. Binary Packet Protocol' of rfc4253 for more info.
4206       *
4207       * @param string $data
4208       * @param string $logged
4209       * @see self::_get_binary_packet()
4210       * @return bool
4211       * @access private
4212       */
4213      function _send_binary_packet($data, $logged = null)
4214      {
4215          if (!is_resource($this->fsock) || feof($this->fsock)) {
4216              $this->bitmap = 0;
4217              user_error('Connection closed prematurely');
4218              return false;
4219          }
4220  
4221          if (!isset($logged)) {
4222              $logged = $data;
4223          }
4224  
4225          switch ($this->compress) {
4226              case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
4227                  if (!$this->isAuthenticated()) {
4228                      break;
4229                  }
4230              case self::NET_SSH2_COMPRESSION_ZLIB:
4231                  if (!$this->regenerate_compression_context) {
4232                      $header = '';
4233                  } else {
4234                      $this->regenerate_compression_context = false;
4235                      $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15));
4236                      $header = "\x78\x9C";
4237                  }
4238                  if ($this->compress_context) {
4239                      $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
4240                  }
4241          }
4242  
4243          // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
4244          $packet_length = strlen($data) + 9;
4245          // round up to the nearest $this->encrypt_block_size
4246          $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
4247          // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
4248          $padding_length = $packet_length - strlen($data) - 5;
4249          $padding = Random::string($padding_length);
4250  
4251          // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
4252          $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
4253  
4254          $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
4255          $this->send_seq_no++;
4256  
4257          if ($this->encrypt !== false) {
4258              $packet = $this->encrypt->encrypt($packet);
4259          }
4260  
4261          $packet.= $hmac;
4262  
4263          $start = microtime(true);
4264          $result = strlen($packet) == @fputs($this->fsock, $packet);
4265          $stop = microtime(true);
4266  
4267          if (defined('NET_SSH2_LOGGING')) {
4268              $current = microtime(true);
4269              $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
4270              $message_number = '-> ' . $message_number .
4271                                ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
4272              $this->_append_log($message_number, $logged);
4273              $this->last_packet = $current;
4274          }
4275  
4276          return $result;
4277      }
4278  
4279      /**
4280       * Logs data packets
4281       *
4282       * Makes sure that only the last 1MB worth of packets will be logged
4283       *
4284       * @param string $message_number
4285       * @param string $message
4286       * @access private
4287       */
4288      function _append_log($message_number, $message)
4289      {
4290          // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
4291          if (strlen($message_number) > 2) {
4292              $this->_string_shift($message);
4293          }
4294  
4295          switch (NET_SSH2_LOGGING) {
4296              // useful for benchmarks
4297              case self::LOG_SIMPLE:
4298                  $this->message_number_log[] = $message_number;
4299                  break;
4300              // the most useful log for SSH2
4301              case self::LOG_COMPLEX:
4302                  $this->message_number_log[] = $message_number;
4303                  $this->log_size+= strlen($message);
4304                  $this->message_log[] = $message;
4305                  while ($this->log_size > self::LOG_MAX_SIZE) {
4306                      $this->log_size-= strlen(array_shift($this->message_log));
4307                      array_shift($this->message_number_log);
4308                  }
4309                  break;
4310              // dump the output out realtime; packets may be interspersed with non packets,
4311              // passwords won't be filtered out and select other packets may not be correctly
4312              // identified
4313              case self::LOG_REALTIME:
4314                  switch (PHP_SAPI) {
4315                      case 'cli':
4316                          $start = $stop = "\r\n";
4317                          break;
4318                      default:
4319                          $start = '<pre>';
4320                          $stop = '</pre>';
4321                  }
4322                  echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
4323                  @flush();
4324                  @ob_flush();
4325                  break;
4326              // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
4327              // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
4328              // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
4329              // at the beginning of the file
4330              case self::LOG_REALTIME_FILE:
4331                  if (!isset($this->realtime_log_file)) {
4332                      // PHP doesn't seem to like using constants in fopen()
4333                      $filename = self::LOG_REALTIME_FILENAME;
4334                      $fp = fopen($filename, 'w');
4335                      $this->realtime_log_file = $fp;
4336                  }
4337                  if (!is_resource($this->realtime_log_file)) {
4338                      break;
4339                  }
4340                  $entry = $this->_format_log(array($message), array($message_number));
4341                  if ($this->realtime_log_wrap) {
4342                      $temp = "<<< START >>>\r\n";
4343                      $entry.= $temp;
4344                      fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
4345                  }
4346                  $this->realtime_log_size+= strlen($entry);
4347                  if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
4348                      fseek($this->realtime_log_file, 0);
4349                      $this->realtime_log_size = strlen($entry);
4350                      $this->realtime_log_wrap = true;
4351                  }
4352                  fputs($this->realtime_log_file, $entry);
4353          }
4354      }
4355  
4356      /**
4357       * Sends channel data
4358       *
4359       * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
4360       *
4361       * @param int $client_channel
4362       * @param string $data
4363       * @return bool
4364       * @access private
4365       */
4366      function _send_channel_packet($client_channel, $data)
4367      {
4368          while (strlen($data)) {
4369              if (!$this->window_size_client_to_server[$client_channel]) {
4370                  $this->bitmap^= self::MASK_WINDOW_ADJUST;
4371                  // using an invalid channel will let the buffers be built up for the valid channels
4372                  $this->_get_channel_packet(-1);
4373                  $this->bitmap^= self::MASK_WINDOW_ADJUST;
4374              }
4375  
4376              /* The maximum amount of data allowed is determined by the maximum
4377                 packet size for the channel, and the current window size, whichever
4378                 is smaller.
4379                   -- http://tools.ietf.org/html/rfc4254#section-5.2 */
4380              $max_size = min(
4381                  $this->packet_size_client_to_server[$client_channel],
4382                  $this->window_size_client_to_server[$client_channel]
4383              );
4384  
4385              $temp = $this->_string_shift($data, $max_size);
4386              $packet = pack(
4387                  'CN2a*',
4388                  NET_SSH2_MSG_CHANNEL_DATA,
4389                  $this->server_channels[$client_channel],
4390                  strlen($temp),
4391                  $temp
4392              );
4393              $this->window_size_client_to_server[$client_channel]-= strlen($temp);
4394              if (!$this->_send_binary_packet($packet)) {
4395                  return false;
4396              }
4397          }
4398  
4399          return true;
4400      }
4401  
4402      /**
4403       * Closes and flushes a channel
4404       *
4405       * \phpseclib\Net\SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
4406       * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
4407       * for SCP more than anything.
4408       *
4409       * @param int $client_channel
4410       * @param bool $want_reply
4411       * @return bool
4412       * @access private
4413       */
4414      function _close_channel($client_channel, $want_reply = false)
4415      {
4416          // see http://tools.ietf.org/html/rfc4254#section-5.3
4417  
4418          $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
4419  
4420          if (!$want_reply) {
4421              $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4422          }
4423  
4424          $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4425  
4426          $this->curTimeout = 5;
4427  
4428          while (!is_bool($this->_get_channel_packet($client_channel))) {
4429          }
4430  
4431          if ($this->is_timeout) {
4432              $this->disconnect();
4433          }
4434  
4435          if ($want_reply) {
4436              $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4437          }
4438  
4439          if ($this->bitmap & self::MASK_SHELL) {
4440              $this->bitmap&= ~self::MASK_SHELL;
4441          }
4442      }
4443  
4444      /**
4445       * Disconnect
4446       *
4447       * @param int $reason
4448       * @return bool
4449       * @access private
4450       */
4451      function _disconnect($reason)
4452      {
4453          if ($this->bitmap & self::MASK_CONNECTED) {
4454              $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
4455              $this->_send_binary_packet($data);
4456          }
4457  
4458          $this->bitmap = 0;
4459          if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
4460              fclose($this->fsock);
4461          }
4462  
4463          return false;
4464      }
4465  
4466      /**
4467       * String Shift
4468       *
4469       * Inspired by array_shift
4470       *
4471       * @param string $string
4472       * @param int $index
4473       * @return string
4474       * @access private
4475       */
4476      function _string_shift(&$string, $index = 1)
4477      {
4478          $substr = substr($string, 0, $index);
4479          $string = substr($string, $index);
4480          return $substr;
4481      }
4482  
4483      /**
4484       * Define Array
4485       *
4486       * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
4487       * named constants from it, using the value as the name of the constant and the index as the value of the constant.
4488       * If any of the constants that would be defined already exists, none of the constants will be defined.
4489       *
4490       * @access private
4491       */
4492      function _define_array()
4493      {
4494          $args = func_get_args();
4495          foreach ($args as $arg) {
4496              foreach ($arg as $key => $value) {
4497                  if (!defined($value)) {
4498                      define($value, $key);
4499                  } else {
4500                      break 2;
4501                  }
4502              }
4503          }
4504      }
4505  
4506      /**
4507       * Returns a log of the packets that have been sent and received.
4508       *
4509       * 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')
4510       *
4511       * @access public
4512       * @return array|false|string
4513       */
4514      function getLog()
4515      {
4516          if (!defined('NET_SSH2_LOGGING')) {
4517              return false;
4518          }
4519  
4520          switch (NET_SSH2_LOGGING) {
4521              case self::LOG_SIMPLE:
4522                  return $this->message_number_log;
4523              case self::LOG_COMPLEX:
4524                  $log = $this->_format_log($this->message_log, $this->message_number_log);
4525                  return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
4526              default:
4527                  return false;
4528          }
4529      }
4530  
4531      /**
4532       * Formats a log for printing
4533       *
4534       * @param array $message_log
4535       * @param array $message_number_log
4536       * @access private
4537       * @return string
4538       */
4539      function _format_log($message_log, $message_number_log)
4540      {
4541          $output = '';
4542          for ($i = 0; $i < count($message_log); $i++) {
4543              $output.= $message_number_log[$i] . "\r\n";
4544              $current_log = $message_log[$i];
4545              $j = 0;
4546              do {
4547                  if (strlen($current_log)) {
4548                      $output.=