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