PluginProbe
ManageWP Worker / 4.9.25
ManageWP Worker v4.9.25
4.9.38 4.9.37 4.9.36 4.9.35 4.9.34 3.8.7 3.8.8 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.18 3.9.19 3.9.2 3.9.20 3.9.21 3.9.22 3.9.23 3.9.24 All 73 releases
worker / src / PHPSecLib / Net / SSH2.php

SSH2.php in ManageWP Worker 4.9.25, at src/PHPSecLib/Net/SSH2.php

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