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