[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

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

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