Guest User

Untitled

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