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

SSH2.php in ManageWP Worker 3.9.28, at lib/PHPSecLib/Net/SSH2.php

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