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