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