Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

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