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