[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Net/ -> SSH1.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP implementation of SSHv1.
   5   *
   6   * PHP version 5
   7   *
   8   * Here's a short example of how to use this library:
   9   * <code>
  10   * <?php
  11   *    include 'vendor/autoload.php';
  12   *
  13   *    $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
  14   *    if (!$ssh->login('username', 'password')) {
  15   *        exit('Login Failed');
  16   *    }
  17   *
  18   *    echo $ssh->exec('ls -la');
  19   * ?>
  20   * </code>
  21   *
  22   * Here's another short example:
  23   * <code>
  24   * <?php
  25   *    include 'vendor/autoload.php';
  26   *
  27   *    $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
  28   *    if (!$ssh->login('username', 'password')) {
  29   *        exit('Login Failed');
  30   *    }
  31   *
  32   *    echo $ssh->read('username@username:~$');
  33   *    $ssh->write("ls -la\n");
  34   *    echo $ssh->read('username@username:~$');
  35   * ?>
  36   * </code>
  37   *
  38   * More information on the SSHv1 specification can be found by reading
  39   * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
  40   *
  41   * @category  Net
  42   * @package   SSH1
  43   * @author    Jim Wigginton <terrafrost@php.net>
  44   * @copyright 2007 Jim Wigginton
  45   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  46   * @link      http://phpseclib.sourceforge.net
  47   */
  48  
  49  namespace phpseclib\Net;
  50  
  51  use phpseclib\Crypt\DES;
  52  use phpseclib\Crypt\Random;
  53  use phpseclib\Crypt\TripleDES;
  54  use phpseclib\Math\BigInteger;
  55  
  56  /**
  57   * Pure-PHP implementation of SSHv1.
  58   *
  59   * @package SSH1
  60   * @author  Jim Wigginton <terrafrost@php.net>
  61   * @access  public
  62   */
  63  class SSH1
  64  {
  65      /**#@+
  66       * Encryption Methods
  67       *
  68       * @see \phpseclib\Net\SSH1::getSupportedCiphers()
  69       * @access public
  70       */
  71      /**
  72       * No encryption
  73       *
  74       * Not supported.
  75       */
  76      const CIPHER_NONE = 0;
  77      /**
  78       * IDEA in CFB mode
  79       *
  80       * Not supported.
  81       */
  82      const CIPHER_IDEA = 1;
  83      /**
  84       * DES in CBC mode
  85       */
  86      const CIPHER_DES = 2;
  87      /**
  88       * Triple-DES in CBC mode
  89       *
  90       * All implementations are required to support this
  91       */
  92      const CIPHER_3DES = 3;
  93      /**
  94       * TRI's Simple Stream encryption CBC
  95       *
  96       * Not supported nor is it defined in the official SSH1 specs.  OpenSSH, however, does define it (see cipher.h),
  97       * although it doesn't use it (see cipher.c)
  98       */
  99      const CIPHER_BROKEN_TSS = 4;
 100      /**
 101       * RC4
 102       *
 103       * Not supported.
 104       *
 105       * @internal According to the SSH1 specs:
 106       *
 107       *        "The first 16 bytes of the session key are used as the key for
 108       *         the server to client direction.  The remaining 16 bytes are used
 109       *         as the key for the client to server direction.  This gives
 110       *         independent 128-bit keys for each direction."
 111       *
 112       *     This library currently only supports encryption when the same key is being used for both directions.  This is
 113       *     because there's only one $crypto object.  Two could be added ($encrypt and $decrypt, perhaps).
 114       */
 115      const CIPHER_RC4 = 5;
 116      /**
 117       * Blowfish
 118       *
 119       * Not supported nor is it defined in the official SSH1 specs.  OpenSSH, however, defines it (see cipher.h) and
 120       * uses it (see cipher.c)
 121       */
 122      const CIPHER_BLOWFISH = 6;
 123      /**#@-*/
 124  
 125      /**#@+
 126       * Authentication Methods
 127       *
 128       * @see \phpseclib\Net\SSH1::getSupportedAuthentications()
 129       * @access public
 130      */
 131      /**
 132       * .rhosts or /etc/hosts.equiv
 133       */
 134      const AUTH_RHOSTS = 1;
 135      /**
 136       * pure RSA authentication
 137       */
 138      const AUTH_RSA = 2;
 139      /**
 140       * password authentication
 141       *
 142       * This is the only method that is supported by this library.
 143       */
 144      const AUTH_PASSWORD = 3;
 145      /**
 146       * .rhosts with RSA host authentication
 147       */
 148      const AUTH_RHOSTS_RSA = 4;
 149      /**#@-*/
 150  
 151      /**#@+
 152       * Terminal Modes
 153       *
 154       * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
 155       * @access private
 156      */
 157      const TTY_OP_END = 0;
 158      /**#@-*/
 159  
 160      /**
 161       * The Response Type
 162       *
 163       * @see \phpseclib\Net\SSH1::_get_binary_packet()
 164       * @access private
 165       */
 166      const RESPONSE_TYPE = 1;
 167  
 168      /**
 169       * The Response Data
 170       *
 171       * @see \phpseclib\Net\SSH1::_get_binary_packet()
 172       * @access private
 173       */
 174      const RESPONSE_DATA = 2;
 175  
 176      /**#@+
 177       * Execution Bitmap Masks
 178       *
 179       * @see \phpseclib\Net\SSH1::bitmap
 180       * @access private
 181      */
 182      const MASK_CONSTRUCTOR = 0x00000001;
 183      const MASK_CONNECTED   = 0x00000002;
 184      const MASK_LOGIN       = 0x00000004;
 185      const MASK_SHELL       = 0x00000008;
 186      /**#@-*/
 187  
 188      /**#@+
 189       * @access public
 190       * @see \phpseclib\Net\SSH1::getLog()
 191      */
 192      /**
 193       * Returns the message numbers
 194       */
 195      const LOG_SIMPLE = 1;
 196      /**
 197       * Returns the message content
 198       */
 199      const LOG_COMPLEX = 2;
 200      /**
 201       * Outputs the content real-time
 202       */
 203      const LOG_REALTIME = 3;
 204      /**
 205       * Dumps the content real-time to a file
 206       */
 207      const LOG_REALTIME_FILE = 4;
 208      /**
 209       * Make sure that the log never gets larger than this
 210       */
 211      const LOG_MAX_SIZE = 1048576; // 1024 * 1024
 212      /**#@-*/
 213  
 214      /**#@+
 215       * @access public
 216       * @see \phpseclib\Net\SSH1::read()
 217      */
 218      /**
 219       * Returns when a string matching $expect exactly is found
 220       */
 221      const READ_SIMPLE = 1;
 222      /**
 223       * Returns when a string matching the regular expression $expect is found
 224       */
 225      const READ_REGEX = 2;
 226      /**#@-*/
 227  
 228      /**
 229       * The SSH identifier
 230       *
 231       * @var string
 232       * @access private
 233       */
 234      var $identifier = 'SSH-1.5-phpseclib';
 235  
 236      /**
 237       * The Socket Object
 238       *
 239       * @var object
 240       * @access private
 241       */
 242      var $fsock;
 243  
 244      /**
 245       * The cryptography object
 246       *
 247       * @var object
 248       * @access private
 249       */
 250      var $crypto = false;
 251  
 252      /**
 253       * Execution Bitmap
 254       *
 255       * The bits that are set represent functions that have been called already.  This is used to determine
 256       * if a requisite function has been successfully executed.  If not, an error should be thrown.
 257       *
 258       * @var int
 259       * @access private
 260       */
 261      var $bitmap = 0;
 262  
 263      /**
 264       * The Server Key Public Exponent
 265       *
 266       * Logged for debug purposes
 267       *
 268       * @see self::getServerKeyPublicExponent()
 269       * @var string
 270       * @access private
 271       */
 272      var $server_key_public_exponent;
 273  
 274      /**
 275       * The Server Key Public Modulus
 276       *
 277       * Logged for debug purposes
 278       *
 279       * @see self::getServerKeyPublicModulus()
 280       * @var string
 281       * @access private
 282       */
 283      var $server_key_public_modulus;
 284  
 285      /**
 286       * The Host Key Public Exponent
 287       *
 288       * Logged for debug purposes
 289       *
 290       * @see self::getHostKeyPublicExponent()
 291       * @var string
 292       * @access private
 293       */
 294      var $host_key_public_exponent;
 295  
 296      /**
 297       * The Host Key Public Modulus
 298       *
 299       * Logged for debug purposes
 300       *
 301       * @see self::getHostKeyPublicModulus()
 302       * @var string
 303       * @access private
 304       */
 305      var $host_key_public_modulus;
 306  
 307      /**
 308       * Supported Ciphers
 309       *
 310       * Logged for debug purposes
 311       *
 312       * @see self::getSupportedCiphers()
 313       * @var array
 314       * @access private
 315       */
 316      var $supported_ciphers = array(
 317          self::CIPHER_NONE       => 'No encryption',
 318          self::CIPHER_IDEA       => 'IDEA in CFB mode',
 319          self::CIPHER_DES        => 'DES in CBC mode',
 320          self::CIPHER_3DES       => 'Triple-DES in CBC mode',
 321          self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
 322          self::CIPHER_RC4        => 'RC4',
 323          self::CIPHER_BLOWFISH   => 'Blowfish'
 324      );
 325  
 326      /**
 327       * Supported Authentications
 328       *
 329       * Logged for debug purposes
 330       *
 331       * @see self::getSupportedAuthentications()
 332       * @var array
 333       * @access private
 334       */
 335      var $supported_authentications = array(
 336          self::AUTH_RHOSTS     => '.rhosts or /etc/hosts.equiv',
 337          self::AUTH_RSA        => 'pure RSA authentication',
 338          self::AUTH_PASSWORD   => 'password authentication',
 339          self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
 340      );
 341  
 342      /**
 343       * Server Identification
 344       *
 345       * @see self::getServerIdentification()
 346       * @var string
 347       * @access private
 348       */
 349      var $server_identification = '';
 350  
 351      /**
 352       * Protocol Flags
 353       *
 354       * @see self::__construct()
 355       * @var array
 356       * @access private
 357       */
 358      var $protocol_flags = array();
 359  
 360      /**
 361       * Protocol Flag Log
 362       *
 363       * @see self::getLog()
 364       * @var array
 365       * @access private
 366       */
 367      var $protocol_flags_log = array();
 368  
 369      /**
 370       * Message Log
 371       *
 372       * @see self::getLog()
 373       * @var array
 374       * @access private
 375       */
 376      var $message_log = array();
 377  
 378      /**
 379       * Real-time log file pointer
 380       *
 381       * @see self::_append_log()
 382       * @var resource
 383       * @access private
 384       */
 385      var $realtime_log_file;
 386  
 387      /**
 388       * Real-time log file size
 389       *
 390       * @see self::_append_log()
 391       * @var int
 392       * @access private
 393       */
 394      var $realtime_log_size;
 395  
 396      /**
 397       * Real-time log file wrap boolean
 398       *
 399       * @see self::_append_log()
 400       * @var bool
 401       * @access private
 402       */
 403      var $realtime_log_wrap;
 404  
 405      /**
 406       * Interactive Buffer
 407       *
 408       * @see self::read()
 409       * @var array
 410       * @access private
 411       */
 412      var $interactiveBuffer = '';
 413  
 414      /**
 415       * Current log size
 416       *
 417       * Should never exceed self::LOG_MAX_SIZE
 418       *
 419       * @see self::_send_binary_packet()
 420       * @see self::_get_binary_packet()
 421       * @var int
 422       * @access private
 423       */
 424      var $log_size;
 425  
 426      /**
 427       * Timeout
 428       *
 429       * @see self::setTimeout()
 430       * @access private
 431       */
 432      var $timeout;
 433  
 434      /**
 435       * Current Timeout
 436       *
 437       * @see self::_get_channel_packet()
 438       * @access private
 439       */
 440      var $curTimeout;
 441  
 442      /**
 443       * Log Boundary
 444       *
 445       * @see self::_format_log()
 446       * @access private
 447       */
 448      var $log_boundary = ':';
 449  
 450      /**
 451       * Log Long Width
 452       *
 453       * @see self::_format_log()
 454       * @access private
 455       */
 456      var $log_long_width = 65;
 457  
 458      /**
 459       * Log Short Width
 460       *
 461       * @see self::_format_log()
 462       * @access private
 463       */
 464      var $log_short_width = 16;
 465  
 466      /**
 467       * Hostname
 468       *
 469       * @see self::__construct()
 470       * @see self::_connect()
 471       * @var string
 472       * @access private
 473       */
 474      var $host;
 475  
 476      /**
 477       * Port Number
 478       *
 479       * @see self::__construct()
 480       * @see self::_connect()
 481       * @var int
 482       * @access private
 483       */
 484      var $port;
 485  
 486      /**
 487       * Timeout for initial connection
 488       *
 489       * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like
 490       * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor,
 491       * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be
 492       * 10 seconds. It is used by fsockopen() in that function.
 493       *
 494       * @see self::__construct()
 495       * @see self::_connect()
 496       * @var int
 497       * @access private
 498       */
 499      var $connectionTimeout;
 500  
 501      /**
 502       * Default cipher
 503       *
 504       * @see self::__construct()
 505       * @see self::_connect()
 506       * @var int
 507       * @access private
 508       */
 509      var $cipher;
 510  
 511      /**
 512       * Default Constructor.
 513       *
 514       * Connects to an SSHv1 server
 515       *
 516       * @param string $host
 517       * @param int $port
 518       * @param int $timeout
 519       * @param int $cipher
 520       * @return \phpseclib\Net\SSH1
 521       * @access public
 522       */
 523      function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES)
 524      {
 525          $this->protocol_flags = array(
 526              1  => 'NET_SSH1_MSG_DISCONNECT',
 527              2  => 'NET_SSH1_SMSG_PUBLIC_KEY',
 528              3  => 'NET_SSH1_CMSG_SESSION_KEY',
 529              4  => 'NET_SSH1_CMSG_USER',
 530              9  => 'NET_SSH1_CMSG_AUTH_PASSWORD',
 531              10 => 'NET_SSH1_CMSG_REQUEST_PTY',
 532              12 => 'NET_SSH1_CMSG_EXEC_SHELL',
 533              13 => 'NET_SSH1_CMSG_EXEC_CMD',
 534              14 => 'NET_SSH1_SMSG_SUCCESS',
 535              15 => 'NET_SSH1_SMSG_FAILURE',
 536              16 => 'NET_SSH1_CMSG_STDIN_DATA',
 537              17 => 'NET_SSH1_SMSG_STDOUT_DATA',
 538              18 => 'NET_SSH1_SMSG_STDERR_DATA',
 539              19 => 'NET_SSH1_CMSG_EOF',
 540              20 => 'NET_SSH1_SMSG_EXITSTATUS',
 541              33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
 542          );
 543  
 544          $this->_define_array($this->protocol_flags);
 545  
 546          $this->host = $host;
 547          $this->port = $port;
 548          $this->connectionTimeout = $timeout;
 549          $this->cipher = $cipher;
 550      }
 551  
 552      /**
 553       * Connect to an SSHv1 server
 554       *
 555       * @return bool
 556       * @access private
 557       */
 558      function _connect()
 559      {
 560          $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
 561          if (!$this->fsock) {
 562              user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr"));
 563              return false;
 564          }
 565  
 566          $this->server_identification = $init_line = fgets($this->fsock, 255);
 567  
 568          if (defined('NET_SSH1_LOGGING')) {
 569              $this->_append_log('<-', $this->server_identification);
 570              $this->_append_log('->', $this->identifier . "\r\n");
 571          }
 572  
 573          if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
 574              user_error('Can only connect to SSH servers');
 575              return false;
 576          }
 577          if ($parts[1][0] != 1) {
 578              user_error("Cannot connect to SSH $parts[1] servers");
 579              return false;
 580          }
 581  
 582          fputs($this->fsock, $this->identifier."\r\n");
 583  
 584          $response = $this->_get_binary_packet();
 585          if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
 586              user_error('Expected SSH_SMSG_PUBLIC_KEY');
 587              return false;
 588          }
 589  
 590          $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8);
 591  
 592          $this->_string_shift($response[self::RESPONSE_DATA], 4);
 593  
 594          if (strlen($response[self::RESPONSE_DATA]) < 2) {
 595              return false;
 596          }
 597          $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
 598          $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
 599          $this->server_key_public_exponent = $server_key_public_exponent;
 600  
 601          if (strlen($response[self::RESPONSE_DATA]) < 2) {
 602              return false;
 603          }
 604          $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
 605          $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
 606  
 607          $this->server_key_public_modulus = $server_key_public_modulus;
 608  
 609          $this->_string_shift($response[self::RESPONSE_DATA], 4);
 610  
 611          if (strlen($response[self::RESPONSE_DATA]) < 2) {
 612              return false;
 613          }
 614          $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
 615          $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
 616          $this->host_key_public_exponent = $host_key_public_exponent;
 617  
 618          if (strlen($response[self::RESPONSE_DATA]) < 2) {
 619              return false;
 620          }
 621          $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
 622          $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
 623  
 624          $this->host_key_public_modulus = $host_key_public_modulus;
 625  
 626          $this->_string_shift($response[self::RESPONSE_DATA], 4);
 627  
 628          // get a list of the supported ciphers
 629          if (strlen($response[self::RESPONSE_DATA]) < 4) {
 630              return false;
 631          }
 632          extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
 633  
 634          foreach ($this->supported_ciphers as $mask => $name) {
 635              if (($supported_ciphers_mask & (1 << $mask)) == 0) {
 636                  unset($this->supported_ciphers[$mask]);
 637              }
 638          }
 639  
 640          // get a list of the supported authentications
 641          if (strlen($response[self::RESPONSE_DATA]) < 4) {
 642              return false;
 643          }
 644          extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
 645          foreach ($this->supported_authentications as $mask => $name) {
 646              if (($supported_authentications_mask & (1 << $mask)) == 0) {
 647                  unset($this->supported_authentications[$mask]);
 648              }
 649          }
 650  
 651          $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
 652  
 653          $session_key = Random::string(32);
 654          $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
 655  
 656          if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
 657              $double_encrypted_session_key = $this->_rsa_crypt(
 658                  $double_encrypted_session_key,
 659                  array(
 660                      $server_key_public_exponent,
 661                      $server_key_public_modulus
 662                  )
 663              );
 664              $double_encrypted_session_key = $this->_rsa_crypt(
 665                  $double_encrypted_session_key,
 666                  array(
 667                      $host_key_public_exponent,
 668                      $host_key_public_modulus
 669                  )
 670              );
 671          } else {
 672              $double_encrypted_session_key = $this->_rsa_crypt(
 673                  $double_encrypted_session_key,
 674                  array(
 675                      $host_key_public_exponent,
 676                      $host_key_public_modulus
 677                  )
 678              );
 679              $double_encrypted_session_key = $this->_rsa_crypt(
 680                  $double_encrypted_session_key,
 681                  array(
 682                      $server_key_public_exponent,
 683                      $server_key_public_modulus
 684                  )
 685              );
 686          }
 687  
 688          $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES;
 689          $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
 690  
 691          if (!$this->_send_binary_packet($data)) {
 692              user_error('Error sending SSH_CMSG_SESSION_KEY');
 693              return false;
 694          }
 695  
 696          switch ($cipher) {
 697              //case self::CIPHER_NONE:
 698              //    $this->crypto = new \phpseclib\Crypt\Null();
 699              //    break;
 700              case self::CIPHER_DES:
 701                  $this->crypto = new DES();
 702                  $this->crypto->disablePadding();
 703                  $this->crypto->enableContinuousBuffer();
 704                  $this->crypto->setKey(substr($session_key, 0,  8));
 705                  break;
 706              case self::CIPHER_3DES:
 707                  $this->crypto = new TripleDES(TripleDES::MODE_3CBC);
 708                  $this->crypto->disablePadding();
 709                  $this->crypto->enableContinuousBuffer();
 710                  $this->crypto->setKey(substr($session_key, 0, 24));
 711                  break;
 712              //case self::CIPHER_RC4:
 713              //    $this->crypto = new RC4();
 714              //    $this->crypto->enableContinuousBuffer();
 715              //    $this->crypto->setKey(substr($session_key, 0,  16));
 716              //    break;
 717          }
 718  
 719          $response = $this->_get_binary_packet();
 720  
 721          if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
 722              user_error('Expected SSH_SMSG_SUCCESS');
 723              return false;
 724          }
 725  
 726          $this->bitmap = self::MASK_CONNECTED;
 727  
 728          return true;
 729      }
 730  
 731      /**
 732       * Login
 733       *
 734       * @param string $username
 735       * @param string $password
 736       * @return bool
 737       * @access public
 738       */
 739      function login($username, $password = '')
 740      {
 741          if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
 742              $this->bitmap |= self::MASK_CONSTRUCTOR;
 743              if (!$this->_connect()) {
 744                  return false;
 745              }
 746          }
 747  
 748          if (!($this->bitmap & self::MASK_CONNECTED)) {
 749              return false;
 750          }
 751  
 752          $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
 753  
 754          if (!$this->_send_binary_packet($data)) {
 755              user_error('Error sending SSH_CMSG_USER');
 756              return false;
 757          }
 758  
 759          $response = $this->_get_binary_packet();
 760  
 761          if ($response === true) {
 762              return false;
 763          }
 764          if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
 765              $this->bitmap |= self::MASK_LOGIN;
 766              return true;
 767          } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
 768              user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
 769              return false;
 770          }
 771  
 772          $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
 773  
 774          if (!$this->_send_binary_packet($data)) {
 775              user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
 776              return false;
 777          }
 778  
 779          // remove the username and password from the last logged packet
 780          if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) {
 781              $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
 782              $this->message_log[count($this->message_log) - 1] = $data;
 783          }
 784  
 785          $response = $this->_get_binary_packet();
 786  
 787          if ($response === true) {
 788              return false;
 789          }
 790          if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
 791              $this->bitmap |= self::MASK_LOGIN;
 792              return true;
 793          } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
 794              return false;
 795          } else {
 796              user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
 797              return false;
 798          }
 799      }
 800  
 801      /**
 802       * Set Timeout
 803       *
 804       * $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.
 805       * Setting $timeout to false or 0 will mean there is no timeout.
 806       *
 807       * @param mixed $timeout
 808       */
 809      function setTimeout($timeout)
 810      {
 811          $this->timeout = $this->curTimeout = $timeout;
 812      }
 813  
 814      /**
 815       * Executes a command on a non-interactive shell, returns the output, and quits.
 816       *
 817       * An SSH1 server will close the connection after a command has been executed on a non-interactive shell.  SSH2
 818       * servers don't, however, this isn't an SSH2 client.  The way this works, on the server, is by initiating a
 819       * shell with the -s option, as discussed in the following links:
 820       *
 821       * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
 822       * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
 823       *
 824       * To execute further commands, a new \phpseclib\Net\SSH1 object will need to be created.
 825       *
 826       * Returns false on failure and the output, otherwise.
 827       *
 828       * @see self::interactiveRead()
 829       * @see self::interactiveWrite()
 830       * @param string $cmd
 831       * @param bool $block
 832       * @return mixed
 833       * @access public
 834       */
 835      function exec($cmd, $block = true)
 836      {
 837          if (!($this->bitmap & self::MASK_LOGIN)) {
 838              user_error('Operation disallowed prior to login()');
 839              return false;
 840          }
 841  
 842          $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
 843  
 844          if (!$this->_send_binary_packet($data)) {
 845              user_error('Error sending SSH_CMSG_EXEC_CMD');
 846              return false;
 847          }
 848  
 849          if (!$block) {
 850              return true;
 851          }
 852  
 853          $output = '';
 854          $response = $this->_get_binary_packet();
 855  
 856          if ($response !== false) {
 857              do {
 858                  $output.= substr($response[self::RESPONSE_DATA], 4);
 859                  $response = $this->_get_binary_packet();
 860              } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
 861          }
 862  
 863          $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
 864  
 865          // i don't think it's really all that important if this packet gets sent or not.
 866          $this->_send_binary_packet($data);
 867  
 868          fclose($this->fsock);
 869  
 870          // reset the execution bitmap - a new \phpseclib\Net\SSH1 object needs to be created.
 871          $this->bitmap = 0;
 872  
 873          return $output;
 874      }
 875  
 876      /**
 877       * Creates an interactive shell
 878       *
 879       * @see self::interactiveRead()
 880       * @see self::interactiveWrite()
 881       * @return bool
 882       * @access private
 883       */
 884      function _initShell()
 885      {
 886          // connect using the sample parameters in protocol-1.5.txt.
 887          // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
 888          // terminal is a command line interpreter or shell".  thus, opening a terminal session to run the shell.
 889          $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END);
 890  
 891          if (!$this->_send_binary_packet($data)) {
 892              user_error('Error sending SSH_CMSG_REQUEST_PTY');
 893              return false;
 894          }
 895  
 896          $response = $this->_get_binary_packet();
 897  
 898          if ($response === true) {
 899              return false;
 900          }
 901          if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
 902              user_error('Expected SSH_SMSG_SUCCESS');
 903              return false;
 904          }
 905  
 906          $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
 907  
 908          if (!$this->_send_binary_packet($data)) {
 909              user_error('Error sending SSH_CMSG_EXEC_SHELL');
 910              return false;
 911          }
 912  
 913          $this->bitmap |= self::MASK_SHELL;
 914  
 915          //stream_set_blocking($this->fsock, 0);
 916  
 917          return true;
 918      }
 919  
 920      /**
 921       * Inputs a command into an interactive shell.
 922       *
 923       * @see self::interactiveWrite()
 924       * @param string $cmd
 925       * @return bool
 926       * @access public
 927       */
 928      function write($cmd)
 929      {
 930          return $this->interactiveWrite($cmd);
 931      }
 932  
 933      /**
 934       * Returns the output of an interactive shell when there's a match for $expect
 935       *
 936       * $expect can take the form of a string literal or, if $mode == self::READ_REGEX,
 937       * a regular expression.
 938       *
 939       * @see self::write()
 940       * @param string $expect
 941       * @param int $mode
 942       * @return bool
 943       * @access public
 944       */
 945      function read($expect, $mode = self::READ_SIMPLE)
 946      {
 947          if (!($this->bitmap & self::MASK_LOGIN)) {
 948              user_error('Operation disallowed prior to login()');
 949              return false;
 950          }
 951  
 952          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
 953              user_error('Unable to initiate an interactive shell session');
 954              return false;
 955          }
 956  
 957          $match = $expect;
 958          while (true) {
 959              if ($mode == self::READ_REGEX) {
 960                  preg_match($expect, $this->interactiveBuffer, $matches);
 961                  $match = isset($matches[0]) ? $matches[0] : '';
 962              }
 963              $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
 964              if ($pos !== false) {
 965                  return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
 966              }
 967              $response = $this->_get_binary_packet();
 968  
 969              if ($response === true) {
 970                  return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
 971              }
 972              $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4);
 973          }
 974      }
 975  
 976      /**
 977       * Inputs a command into an interactive shell.
 978       *
 979       * @see self::interactiveRead()
 980       * @param string $cmd
 981       * @return bool
 982       * @access public
 983       */
 984      function interactiveWrite($cmd)
 985      {
 986          if (!($this->bitmap & self::MASK_LOGIN)) {
 987              user_error('Operation disallowed prior to login()');
 988              return false;
 989          }
 990  
 991          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
 992              user_error('Unable to initiate an interactive shell session');
 993              return false;
 994          }
 995  
 996          $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
 997  
 998          if (!$this->_send_binary_packet($data)) {
 999              user_error('Error sending SSH_CMSG_STDIN');
1000              return false;
1001          }
1002  
1003          return true;
1004      }
1005  
1006      /**
1007       * Returns the output of an interactive shell when no more output is available.
1008       *
1009       * Requires PHP 4.3.0 or later due to the use of the stream_select() function.  If you see stuff like
1010       * "^[[00m", you're seeing ANSI escape codes.  According to
1011       * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
1012       * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
1013       * there's not going to be much recourse.
1014       *
1015       * @see self::interactiveRead()
1016       * @return string
1017       * @access public
1018       */
1019      function interactiveRead()
1020      {
1021          if (!($this->bitmap & self::MASK_LOGIN)) {
1022              user_error('Operation disallowed prior to login()');
1023              return false;
1024          }
1025  
1026          if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
1027              user_error('Unable to initiate an interactive shell session');
1028              return false;
1029          }
1030  
1031          $read = array($this->fsock);
1032          $write = $except = null;
1033          if (stream_select($read, $write, $except, 0)) {
1034              $response = $this->_get_binary_packet();
1035              return substr($response[self::RESPONSE_DATA], 4);
1036          } else {
1037              return '';
1038          }
1039      }
1040  
1041      /**
1042       * Disconnect
1043       *
1044       * @access public
1045       */
1046      function disconnect()
1047      {
1048          $this->_disconnect();
1049      }
1050  
1051      /**
1052       * Destructor.
1053       *
1054       * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
1055       * disconnect().
1056       *
1057       * @access public
1058       */
1059      function __destruct()
1060      {
1061          $this->_disconnect();
1062      }
1063  
1064      /**
1065       * Disconnect
1066       *
1067       * @param string $msg
1068       * @access private
1069       */
1070      function _disconnect($msg = 'Client Quit')
1071      {
1072          if ($this->bitmap) {
1073              $data = pack('C', NET_SSH1_CMSG_EOF);
1074              $this->_send_binary_packet($data);
1075              /*
1076              $response = $this->_get_binary_packet();
1077              if ($response === true) {
1078                  $response = array(self::RESPONSE_TYPE => -1);
1079              }
1080              switch ($response[self::RESPONSE_TYPE]) {
1081                  case NET_SSH1_SMSG_EXITSTATUS:
1082                      $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
1083                      break;
1084                  default:
1085                      $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
1086              }
1087              */
1088              $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
1089  
1090              $this->_send_binary_packet($data);
1091              fclose($this->fsock);
1092              $this->bitmap = 0;
1093          }
1094      }
1095  
1096      /**
1097       * Gets Binary Packets
1098       *
1099       * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
1100       *
1101       * Also, this function could be improved upon by adding detection for the following exploit:
1102       * http://www.securiteam.com/securitynews/5LP042K3FY.html
1103       *
1104       * @see self::_send_binary_packet()
1105       * @return array
1106       * @access private
1107       */
1108      function _get_binary_packet()
1109      {
1110          if (feof($this->fsock)) {
1111              //user_error('connection closed prematurely');
1112              return false;
1113          }
1114  
1115          if ($this->curTimeout) {
1116              $read = array($this->fsock);
1117              $write = $except = null;
1118  
1119              $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1120              $sec = floor($this->curTimeout);
1121              $usec = 1000000 * ($this->curTimeout - $sec);
1122              // on windows this returns a "Warning: Invalid CRT parameters detected" error
1123              if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
1124                  //$this->_disconnect('Timeout');
1125                  return true;
1126              }
1127              $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
1128              $this->curTimeout-= $elapsed;
1129          }
1130  
1131          $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1132          $data = fread($this->fsock, 4);
1133          if (strlen($data) < 4) {
1134              return false;
1135          }
1136          $temp = unpack('Nlength', $data);
1137  
1138          $padding_length = 8 - ($temp['length'] & 7);
1139          $length = $temp['length'] + $padding_length;
1140          $raw = '';
1141  
1142          while ($length > 0) {
1143              $temp = fread($this->fsock, $length);
1144              if (strlen($temp) != $length) {
1145                  return false;
1146              }
1147              $raw.= $temp;
1148              $length-= strlen($temp);
1149          }
1150          $stop = strtok(microtime(), ' ') + strtok('');
1151  
1152          if (strlen($raw) && $this->crypto !== false) {
1153              $raw = $this->crypto->decrypt($raw);
1154          }
1155  
1156          $padding = substr($raw, 0, $padding_length);
1157          $type = $raw[$padding_length];
1158          $data = substr($raw, $padding_length + 1, -4);
1159  
1160          if (strlen($raw) < 4) {
1161              return false;
1162          }
1163          $temp = unpack('Ncrc', substr($raw, -4));
1164  
1165          //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
1166          //    user_error('Bad CRC in packet from server');
1167          //    return false;
1168          //}
1169  
1170          $type = ord($type);
1171  
1172          if (defined('NET_SSH1_LOGGING')) {
1173              $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
1174              $temp = '<- ' . $temp .
1175                      ' (' . round($stop - $start, 4) . 's)';
1176              $this->_append_log($temp, $data);
1177          }
1178  
1179          return array(
1180              self::RESPONSE_TYPE => $type,
1181              self::RESPONSE_DATA => $data
1182          );
1183      }
1184  
1185      /**
1186       * Sends Binary Packets
1187       *
1188       * Returns true on success, false on failure.
1189       *
1190       * @see self::_get_binary_packet()
1191       * @param string $data
1192       * @return bool
1193       * @access private
1194       */
1195      function _send_binary_packet($data)
1196      {
1197          if (feof($this->fsock)) {
1198              //user_error('connection closed prematurely');
1199              return false;
1200          }
1201  
1202          $length = strlen($data) + 4;
1203  
1204          $padding = Random::string(8 - ($length & 7));
1205  
1206          $orig = $data;
1207          $data = $padding . $data;
1208          $data.= pack('N', $this->_crc($data));
1209  
1210          if ($this->crypto !== false) {
1211              $data = $this->crypto->encrypt($data);
1212          }
1213  
1214          $packet = pack('Na*', $length, $data);
1215  
1216          $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1217          $result = strlen($packet) == fputs($this->fsock, $packet);
1218          $stop = strtok(microtime(), ' ') + strtok('');
1219  
1220          if (defined('NET_SSH1_LOGGING')) {
1221              $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
1222              $temp = '-> ' . $temp .
1223                      ' (' . round($stop - $start, 4) . 's)';
1224              $this->_append_log($temp, $orig);
1225          }
1226  
1227          return $result;
1228      }
1229  
1230      /**
1231       * Cyclic Redundancy Check (CRC)
1232       *
1233       * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
1234       * we've reimplemented it. A more detailed discussion of the differences can be found after
1235       * $crc_lookup_table's initialization.
1236       *
1237       * @see self::_get_binary_packet()
1238       * @see self::_send_binary_packet()
1239       * @param string $data
1240       * @return int
1241       * @access private
1242       */
1243      function _crc($data)
1244      {
1245          static $crc_lookup_table = array(
1246              0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
1247              0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
1248              0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
1249              0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
1250              0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
1251              0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
1252              0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
1253              0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
1254              0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
1255              0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
1256              0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
1257              0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
1258              0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
1259              0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
1260              0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
1261              0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
1262              0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
1263              0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
1264              0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
1265              0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
1266              0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
1267              0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
1268              0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
1269              0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
1270              0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
1271              0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
1272              0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
1273              0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
1274              0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
1275              0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
1276              0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
1277              0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
1278              0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
1279              0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
1280              0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
1281              0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
1282              0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
1283              0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
1284              0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
1285              0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
1286              0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
1287              0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
1288              0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
1289              0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
1290              0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
1291              0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
1292              0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
1293              0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
1294              0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
1295              0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
1296              0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
1297              0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
1298              0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
1299              0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
1300              0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
1301              0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
1302              0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
1303              0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
1304              0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
1305              0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
1306              0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
1307              0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
1308              0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
1309              0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
1310          );
1311  
1312          // For this function to yield the same output as PHP's crc32 function, $crc would have to be
1313          // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
1314          $crc = 0x00000000;
1315          $length = strlen($data);
1316  
1317          for ($i=0; $i<$length; $i++) {
1318              // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
1319              // be zero.  PHP, unfortunately, doesn't always do this.  0x80000000 >> 8, as an example,
1320              // yields 0xFF800000 - not 0x00800000.  The following link elaborates:
1321              // http://www.php.net/manual/en/language.operators.bitwise.php#57281
1322              $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
1323          }
1324  
1325          // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
1326          // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
1327          return $crc;
1328      }
1329  
1330      /**
1331       * String Shift
1332       *
1333       * Inspired by array_shift
1334       *
1335       * @param string $string
1336       * @param int $index
1337       * @return string
1338       * @access private
1339       */
1340      function _string_shift(&$string, $index = 1)
1341      {
1342          $substr = substr($string, 0, $index);
1343          $string = substr($string, $index);
1344          return $substr;
1345      }
1346  
1347      /**
1348       * RSA Encrypt
1349       *
1350       * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
1351       * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1.  Could just make anything that
1352       * calls this call modexp, instead, but I think this makes things clearer, maybe...
1353       *
1354       * @see self::__construct()
1355       * @param BigInteger $m
1356       * @param array $key
1357       * @return BigInteger
1358       * @access private
1359       */
1360      function _rsa_crypt($m, $key)
1361      {
1362          /*
1363          $rsa = new RSA();
1364          $rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW);
1365          $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
1366          return $rsa->encrypt($m);
1367          */
1368  
1369          // To quote from protocol-1.5.txt:
1370          // The most significant byte (which is only partial as the value must be
1371          // less than the public modulus, which is never a power of two) is zero.
1372          //
1373          // The next byte contains the value 2 (which stands for public-key
1374          // encrypted data in the PKCS standard [PKCS#1]).  Then, there are non-
1375          // zero random bytes to fill any unused space, a zero byte, and the data
1376          // to be encrypted in the least significant bytes, the last byte of the
1377          // data in the least significant byte.
1378  
1379          // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
1380          // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
1381          // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
1382          $modulus = $key[1]->toBytes();
1383          $length = strlen($modulus) - strlen($m) - 3;
1384          $random = '';
1385          while (strlen($random) != $length) {
1386              $block = Random::string($length - strlen($random));
1387              $block = str_replace("\x00", '', $block);
1388              $random.= $block;
1389          }
1390          $temp = chr(0) . chr(2) . $random . chr(0) . $m;
1391  
1392          $m = new BigInteger($temp, 256);
1393          $m = $m->modPow($key[0], $key[1]);
1394  
1395          return $m->toBytes();
1396      }
1397  
1398      /**
1399       * Define Array
1400       *
1401       * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
1402       * named constants from it, using the value as the name of the constant and the index as the value of the constant.
1403       * If any of the constants that would be defined already exists, none of the constants will be defined.
1404       *
1405       * @access private
1406       */
1407      function _define_array()
1408      {
1409          $args = func_get_args();
1410          foreach ($args as $arg) {
1411              foreach ($arg as $key => $value) {
1412                  if (!defined($value)) {
1413                      define($value, $key);
1414                  } else {
1415                      break 2;
1416                  }
1417              }
1418          }
1419      }
1420  
1421      /**
1422       * Returns a log of the packets that have been sent and received.
1423       *
1424       * Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING')
1425       *
1426       * @access public
1427       * @return array|false|string
1428       */
1429      function getLog()
1430      {
1431          if (!defined('NET_SSH1_LOGGING')) {
1432              return false;
1433          }
1434  
1435          switch (NET_SSH1_LOGGING) {
1436              case self::LOG_SIMPLE:
1437                  return $this->protocol_flags_log;
1438                  break;
1439              case self::LOG_COMPLEX:
1440                  return $this->_format_log($this->message_log, $this->protocol_flags_log);
1441                  break;
1442              default:
1443                  return false;
1444          }
1445      }
1446  
1447      /**
1448       * Formats a log for printing
1449       *
1450       * @param array $message_log
1451       * @param array $message_number_log
1452       * @access private
1453       * @return string
1454       */
1455      function _format_log($message_log, $message_number_log)
1456      {
1457          $output = '';
1458          for ($i = 0; $i < count($message_log); $i++) {
1459              $output.= $message_number_log[$i] . "\r\n";
1460              $current_log = $message_log[$i];
1461              $j = 0;
1462              do {
1463                  if (strlen($current_log)) {
1464                      $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
1465                  }
1466                  $fragment = $this->_string_shift($current_log, $this->log_short_width);
1467                  $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
1468                  // replace non ASCII printable characters with dots
1469                  // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
1470                  // also replace < with a . since < messes up the output on web browsers
1471                  $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
1472                  $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
1473                  $j++;
1474              } while (strlen($current_log));
1475              $output.= "\r\n";
1476          }
1477  
1478          return $output;
1479      }
1480  
1481      /**
1482       * Helper function for _format_log
1483       *
1484       * For use with preg_replace_callback()
1485       *
1486       * @param array $matches
1487       * @access private
1488       * @return string
1489       */
1490      function _format_log_helper($matches)
1491      {
1492          return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
1493      }
1494  
1495      /**
1496       * Return the server key public exponent
1497       *
1498       * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1499       * the raw bytes.  This behavior is similar to PHP's md5() function.
1500       *
1501       * @param bool $raw_output
1502       * @return string
1503       * @access public
1504       */
1505      function getServerKeyPublicExponent($raw_output = false)
1506      {
1507          return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
1508      }
1509  
1510      /**
1511       * Return the server key public modulus
1512       *
1513       * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1514       * the raw bytes.  This behavior is similar to PHP's md5() function.
1515       *
1516       * @param bool $raw_output
1517       * @return string
1518       * @access public
1519       */
1520      function getServerKeyPublicModulus($raw_output = false)
1521      {
1522          return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
1523      }
1524  
1525      /**
1526       * Return the host key public exponent
1527       *
1528       * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1529       * the raw bytes.  This behavior is similar to PHP's md5() function.
1530       *
1531       * @param bool $raw_output
1532       * @return string
1533       * @access public
1534       */
1535      function getHostKeyPublicExponent($raw_output = false)
1536      {
1537          return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
1538      }
1539  
1540      /**
1541       * Return the host key public modulus
1542       *
1543       * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
1544       * the raw bytes.  This behavior is similar to PHP's md5() function.
1545       *
1546       * @param bool $raw_output
1547       * @return string
1548       * @access public
1549       */
1550      function getHostKeyPublicModulus($raw_output = false)
1551      {
1552          return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
1553      }
1554  
1555      /**
1556       * Return a list of ciphers supported by SSH1 server.
1557       *
1558       * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
1559       * is set to true, returns, instead, an array of constants.  ie. instead of array('Triple-DES in CBC mode'), you'll
1560       * get array(self::CIPHER_3DES).
1561       *
1562       * @param bool $raw_output
1563       * @return array
1564       * @access public
1565       */
1566      function getSupportedCiphers($raw_output = false)
1567      {
1568          return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
1569      }
1570  
1571      /**
1572       * Return a list of authentications supported by SSH1 server.
1573       *
1574       * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
1575       * is set to true, returns, instead, an array of constants.  ie. instead of array('password authentication'), you'll
1576       * get array(self::AUTH_PASSWORD).
1577       *
1578       * @param bool $raw_output
1579       * @return array
1580       * @access public
1581       */
1582      function getSupportedAuthentications($raw_output = false)
1583      {
1584          return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
1585      }
1586  
1587      /**
1588       * Return the server identification.
1589       *
1590       * @return string
1591       * @access public
1592       */
1593      function getServerIdentification()
1594      {
1595          return rtrim($this->server_identification);
1596      }
1597  
1598      /**
1599       * Logs data packets
1600       *
1601       * Makes sure that only the last 1MB worth of packets will be logged
1602       *
1603       * @param int $protocol_flags
1604       * @param string $message
1605       * @access private
1606       */
1607      function _append_log($protocol_flags, $message)
1608      {
1609          switch (NET_SSH1_LOGGING) {
1610              // useful for benchmarks
1611              case self::LOG_SIMPLE:
1612                  $this->protocol_flags_log[] = $protocol_flags;
1613                  break;
1614              // the most useful log for SSH1
1615              case self::LOG_COMPLEX:
1616                  $this->protocol_flags_log[] = $protocol_flags;
1617                  $this->_string_shift($message);
1618                  $this->log_size+= strlen($message);
1619                  $this->message_log[] = $message;
1620                  while ($this->log_size > self::LOG_MAX_SIZE) {
1621                      $this->log_size-= strlen(array_shift($this->message_log));
1622                      array_shift($this->protocol_flags_log);
1623                  }
1624                  break;
1625              // dump the output out realtime; packets may be interspersed with non packets,
1626              // passwords won't be filtered out and select other packets may not be correctly
1627              // identified
1628              case self::LOG_REALTIME:
1629                  echo "<pre>\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n</pre>\r\n";
1630                  @flush();
1631                  @ob_flush();
1632                  break;
1633              // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
1634              // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
1635              // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
1636              // at the beginning of the file
1637              case self::LOG_REALTIME_FILE:
1638                  if (!isset($this->realtime_log_file)) {
1639                      // PHP doesn't seem to like using constants in fopen()
1640                      $filename = self::LOG_REALTIME_FILE;
1641                      $fp = fopen($filename, 'w');
1642                      $this->realtime_log_file = $fp;
1643                  }
1644                  if (!is_resource($this->realtime_log_file)) {
1645                      break;
1646                  }
1647                  $entry = $this->_format_log(array($message), array($protocol_flags));
1648                  if ($this->realtime_log_wrap) {
1649                      $temp = "<<< START >>>\r\n";
1650                      $entry.= $temp;
1651                      fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
1652                  }
1653                  $this->realtime_log_size+= strlen($entry);
1654                  if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
1655                      fseek($this->realtime_log_file, 0);
1656                      $this->realtime_log_size = strlen($entry);
1657                      $this->realtime_log_wrap = true;
1658                  }
1659                  fputs($this->realtime_log_file, $entry);
1660          }
1661      }
1662  }