PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/phpseclib/Net/SSH1.php

https://bitbucket.org/andrewjleavitt/magestudy
PHP | 1159 lines | 479 code | 124 blank | 556 comment | 60 complexity | 19155c50cf00370152905b2a4a6ce657 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP implementation of SSHv1.
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * Here's a short example of how to use this library:
  9. * <code>
  10. * <?php
  11. * include('Net/SSH1.php');
  12. *
  13. * $ssh = new Net_SSH1('www.domain.tld');
  14. * if (!$ssh->login('username', 'password')) {
  15. * exit('Login Failed');
  16. * }
  17. *
  18. * while (true) {
  19. * echo $ssh->interactiveRead();
  20. *
  21. * $read = array(STDIN);
  22. * $write = $except = NULL;
  23. * if (stream_select($read, $write, $except, 0)) {
  24. * $ssh->interactiveWrite(fread(STDIN, 1));
  25. * }
  26. * }
  27. * ?>
  28. * </code>
  29. *
  30. * Here's another short example:
  31. * <code>
  32. * <?php
  33. * include('Net/SSH1.php');
  34. *
  35. * $ssh = new Net_SSH1('www.domain.tld');
  36. * if (!$ssh->login('username', 'password')) {
  37. * exit('Login Failed');
  38. * }
  39. *
  40. * echo $ssh->exec('ls -la');
  41. * ?>
  42. * </code>
  43. *
  44. * More information on the SSHv1 specification can be found by reading
  45. * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
  46. *
  47. * LICENSE: This library is free software; you can redistribute it and/or
  48. * modify it under the terms of the GNU Lesser General Public
  49. * License as published by the Free Software Foundation; either
  50. * version 2.1 of the License, or (at your option) any later version.
  51. *
  52. * This library is distributed in the hope that it will be useful,
  53. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  54. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  55. * Lesser General Public License for more details.
  56. *
  57. * You should have received a copy of the GNU Lesser General Public
  58. * License along with this library; if not, write to the Free Software
  59. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  60. * MA 02111-1307 USA
  61. *
  62. * @category Net
  63. * @package Net_SSH1
  64. * @author Jim Wigginton <terrafrost@php.net>
  65. * @copyright MMVII Jim Wigginton
  66. * @license http://www.gnu.org/licenses/lgpl.txt
  67. * @version $Id: SSH1.php,v 1.15 2010/03/22 22:01:38 terrafrost Exp $
  68. * @link http://phpseclib.sourceforge.net
  69. */
  70. /**
  71. * Include Math_BigInteger
  72. *
  73. * Used to do RSA encryption.
  74. */
  75. require_once('phpseclib/Math/BigInteger.php');
  76. /**
  77. * Include Crypt_Null
  78. */
  79. //require_once('Crypt/Null.php');
  80. /**
  81. * Include Crypt_DES
  82. */
  83. require_once('phpseclib/Crypt/DES.php');
  84. /**
  85. * Include Crypt_TripleDES
  86. */
  87. require_once('phpseclib/Crypt/TripleDES.php');
  88. /**
  89. * Include Crypt_RC4
  90. */
  91. require_once('phpseclib/Crypt/RC4.php');
  92. /**
  93. * Include Crypt_Random
  94. */
  95. require_once('phpseclib/Crypt/Random.php');
  96. /**#@+
  97. * Protocol Flags
  98. *
  99. * @access private
  100. */
  101. define('NET_SSH1_MSG_DISCONNECT', 1);
  102. define('NET_SSH1_SMSG_PUBLIC_KEY', 2);
  103. define('NET_SSH1_CMSG_SESSION_KEY', 3);
  104. define('NET_SSH1_CMSG_USER', 4);
  105. define('NET_SSH1_CMSG_AUTH_PASSWORD', 9);
  106. define('NET_SSH1_CMSG_REQUEST_PTY', 10);
  107. define('NET_SSH1_CMSG_EXEC_SHELL', 12);
  108. define('NET_SSH1_CMSG_EXEC_CMD', 13);
  109. define('NET_SSH1_SMSG_SUCCESS', 14);
  110. define('NET_SSH1_SMSG_FAILURE', 15);
  111. define('NET_SSH1_CMSG_STDIN_DATA', 16);
  112. define('NET_SSH1_SMSG_STDOUT_DATA', 17);
  113. define('NET_SSH1_SMSG_STDERR_DATA', 18);
  114. define('NET_SSH1_SMSG_EXITSTATUS', 20);
  115. define('NET_SSH1_CMSG_EXIT_CONFIRMATION', 33);
  116. /**#@-*/
  117. /**#@+
  118. * Encryption Methods
  119. *
  120. * @see Net_SSH1::getSupportedCiphers()
  121. * @access public
  122. */
  123. /**
  124. * No encryption
  125. *
  126. * Not supported.
  127. */
  128. define('NET_SSH1_CIPHER_NONE', 0);
  129. /**
  130. * IDEA in CFB mode
  131. *
  132. * Not supported.
  133. */
  134. define('NET_SSH1_CIPHER_IDEA', 1);
  135. /**
  136. * DES in CBC mode
  137. */
  138. define('NET_SSH1_CIPHER_DES', 2);
  139. /**
  140. * Triple-DES in CBC mode
  141. *
  142. * All implementations are required to support this
  143. */
  144. define('NET_SSH1_CIPHER_3DES', 3);
  145. /**
  146. * TRI's Simple Stream encryption CBC
  147. *
  148. * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h),
  149. * although it doesn't use it (see cipher.c)
  150. */
  151. define('NET_SSH1_CIPHER_BROKEN_TSS', 4);
  152. /**
  153. * RC4
  154. *
  155. * Not supported.
  156. *
  157. * @internal According to the SSH1 specs:
  158. *
  159. * "The first 16 bytes of the session key are used as the key for
  160. * the server to client direction. The remaining 16 bytes are used
  161. * as the key for the client to server direction. This gives
  162. * independent 128-bit keys for each direction."
  163. *
  164. * This library currently only supports encryption when the same key is being used for both directions. This is
  165. * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps).
  166. */
  167. define('NET_SSH1_CIPHER_RC4', 5);
  168. /**
  169. * Blowfish
  170. *
  171. * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and
  172. * uses it (see cipher.c)
  173. */
  174. define('NET_SSH1_CIPHER_BLOWFISH', 6);
  175. /**#@-*/
  176. /**#@+
  177. * Authentication Methods
  178. *
  179. * @see Net_SSH1::getSupportedAuthentications()
  180. * @access public
  181. */
  182. /**
  183. * .rhosts or /etc/hosts.equiv
  184. */
  185. define('NET_SSH1_AUTH_RHOSTS', 1);
  186. /**
  187. * pure RSA authentication
  188. */
  189. define('NET_SSH1_AUTH_RSA', 2);
  190. /**
  191. * password authentication
  192. *
  193. * This is the only method that is supported by this library.
  194. */
  195. define('NET_SSH1_AUTH_PASSWORD', 3);
  196. /**
  197. * .rhosts with RSA host authentication
  198. */
  199. define('NET_SSH1_AUTH_RHOSTS_RSA', 4);
  200. /**#@-*/
  201. /**#@+
  202. * Terminal Modes
  203. *
  204. * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
  205. * @access private
  206. */
  207. define('NET_SSH1_TTY_OP_END', 0);
  208. /**#@-*/
  209. /**
  210. * The Response Type
  211. *
  212. * @see Net_SSH1::_get_binary_packet()
  213. * @access private
  214. */
  215. define('NET_SSH1_RESPONSE_TYPE', 1);
  216. /**
  217. * The Response Data
  218. *
  219. * @see Net_SSH1::_get_binary_packet()
  220. * @access private
  221. */
  222. define('NET_SSH1_RESPONSE_DATA', 2);
  223. /**#@+
  224. * Execution Bitmap Masks
  225. *
  226. * @see Net_SSH1::bitmap
  227. * @access private
  228. */
  229. define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001);
  230. define('NET_SSH1_MASK_LOGIN', 0x00000002);
  231. define('NET_SSH1_MASK_SHELL', 0x00000004);
  232. /**#@-*/
  233. /**
  234. * Pure-PHP implementation of SSHv1.
  235. *
  236. * @author Jim Wigginton <terrafrost@php.net>
  237. * @version 0.1.0
  238. * @access public
  239. * @package Net_SSH1
  240. */
  241. class Net_SSH1 {
  242. /**
  243. * The SSH identifier
  244. *
  245. * @var String
  246. * @access private
  247. */
  248. var $identifier = 'SSH-1.5-phpseclib';
  249. /**
  250. * The Socket Object
  251. *
  252. * @var Object
  253. * @access private
  254. */
  255. var $fsock;
  256. /**
  257. * The cryptography object
  258. *
  259. * @var Object
  260. * @access private
  261. */
  262. var $crypto = false;
  263. /**
  264. * Execution Bitmap
  265. *
  266. * The bits that are set reprsent functions that have been called already. This is used to determine
  267. * if a requisite function has been successfully executed. If not, an error should be thrown.
  268. *
  269. * @var Integer
  270. * @access private
  271. */
  272. var $bitmap = 0;
  273. /**
  274. * The Server Key Public Exponent
  275. *
  276. * Logged for debug purposes
  277. *
  278. * @see Net_SSH1::getServerKeyPublicExponent()
  279. * @var String
  280. * @access private
  281. */
  282. var $server_key_public_exponent;
  283. /**
  284. * The Server Key Public Modulus
  285. *
  286. * Logged for debug purposes
  287. *
  288. * @see Net_SSH1::getServerKeyPublicModulus()
  289. * @var String
  290. * @access private
  291. */
  292. var $server_key_public_modulus;
  293. /**
  294. * The Host Key Public Exponent
  295. *
  296. * Logged for debug purposes
  297. *
  298. * @see Net_SSH1::getHostKeyPublicExponent()
  299. * @var String
  300. * @access private
  301. */
  302. var $host_key_public_exponent;
  303. /**
  304. * The Host Key Public Modulus
  305. *
  306. * Logged for debug purposes
  307. *
  308. * @see Net_SSH1::getHostKeyPublicModulus()
  309. * @var String
  310. * @access private
  311. */
  312. var $host_key_public_modulus;
  313. /**
  314. * Supported Ciphers
  315. *
  316. * Logged for debug purposes
  317. *
  318. * @see Net_SSH1::getSupportedCiphers()
  319. * @var Array
  320. * @access private
  321. */
  322. var $supported_ciphers = array(
  323. NET_SSH1_CIPHER_NONE => 'No encryption',
  324. NET_SSH1_CIPHER_IDEA => 'IDEA in CFB mode',
  325. NET_SSH1_CIPHER_DES => 'DES in CBC mode',
  326. NET_SSH1_CIPHER_3DES => 'Triple-DES in CBC mode',
  327. NET_SSH1_CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
  328. NET_SSH1_CIPHER_RC4 => 'RC4',
  329. NET_SSH1_CIPHER_BLOWFISH => 'Blowfish'
  330. );
  331. /**
  332. * Supported Authentications
  333. *
  334. * Logged for debug purposes
  335. *
  336. * @see Net_SSH1::getSupportedAuthentications()
  337. * @var Array
  338. * @access private
  339. */
  340. var $supported_authentications = array(
  341. NET_SSH1_AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv',
  342. NET_SSH1_AUTH_RSA => 'pure RSA authentication',
  343. NET_SSH1_AUTH_PASSWORD => 'password authentication',
  344. NET_SSH1_AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
  345. );
  346. /**
  347. * Server Identification
  348. *
  349. * @see Net_SSH1::getServerIdentification()
  350. * @var String
  351. * @access private
  352. */
  353. var $server_identification = '';
  354. /**
  355. * Default Constructor.
  356. *
  357. * Connects to an SSHv1 server
  358. *
  359. * @param String $host
  360. * @param optional Integer $port
  361. * @param optional Integer $timeout
  362. * @param optional Integer $cipher
  363. * @return Net_SSH1
  364. * @access public
  365. */
  366. function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
  367. {
  368. $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
  369. if (!$this->fsock) {
  370. user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
  371. return;
  372. }
  373. $this->server_identification = $init_line = fgets($this->fsock, 255);
  374. if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
  375. user_error('Can only connect to SSH servers', E_USER_NOTICE);
  376. return;
  377. }
  378. if ($parts[1][0] != 1) {
  379. user_error("Cannot connect to SSH $parts[1] servers", E_USER_NOTICE);
  380. return;
  381. }
  382. fputs($this->fsock, $this->identifier."\r\n");
  383. $response = $this->_get_binary_packet();
  384. if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
  385. user_error('Expected SSH_SMSG_PUBLIC_KEY', E_USER_NOTICE);
  386. return;
  387. }
  388. $anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8);
  389. $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
  390. $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
  391. $server_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
  392. $this->server_key_public_exponent = $server_key_public_exponent;
  393. $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
  394. $server_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
  395. $this->server_key_public_modulus = $server_key_public_modulus;
  396. $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
  397. $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
  398. $host_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
  399. $this->host_key_public_exponent = $host_key_public_exponent;
  400. $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
  401. $host_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
  402. $this->host_key_public_modulus = $host_key_public_modulus;
  403. $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
  404. // get a list of the supported ciphers
  405. extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
  406. foreach ($this->supported_ciphers as $mask=>$name) {
  407. if (($supported_ciphers_mask & (1 << $mask)) == 0) {
  408. unset($this->supported_ciphers[$mask]);
  409. }
  410. }
  411. // get a list of the supported authentications
  412. extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
  413. foreach ($this->supported_authentications as $mask=>$name) {
  414. if (($supported_authentications_mask & (1 << $mask)) == 0) {
  415. unset($this->supported_authentications[$mask]);
  416. }
  417. }
  418. $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
  419. $session_key = '';
  420. for ($i = 0; $i < 32; $i++) {
  421. $session_key.= chr(crypt_random(0, 255));
  422. }
  423. $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
  424. if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
  425. $double_encrypted_session_key = $this->_rsa_crypt(
  426. $double_encrypted_session_key,
  427. array(
  428. $server_key_public_exponent,
  429. $server_key_public_modulus
  430. )
  431. );
  432. $double_encrypted_session_key = $this->_rsa_crypt(
  433. $double_encrypted_session_key,
  434. array(
  435. $host_key_public_exponent,
  436. $host_key_public_modulus
  437. )
  438. );
  439. } else {
  440. $double_encrypted_session_key = $this->_rsa_crypt(
  441. $double_encrypted_session_key,
  442. array(
  443. $host_key_public_exponent,
  444. $host_key_public_modulus
  445. )
  446. );
  447. $double_encrypted_session_key = $this->_rsa_crypt(
  448. $double_encrypted_session_key,
  449. array(
  450. $server_key_public_exponent,
  451. $server_key_public_modulus
  452. )
  453. );
  454. }
  455. $cipher = isset($this->supported_ciphers[$cipher]) ? $cipher : NET_SSH1_CIPHER_3DES;
  456. $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
  457. if (!$this->_send_binary_packet($data)) {
  458. user_error('Error sending SSH_CMSG_SESSION_KEY', E_USER_NOTICE);
  459. return;
  460. }
  461. switch ($cipher) {
  462. //case NET_SSH1_CIPHER_NONE:
  463. // $this->crypto = new Crypt_Null();
  464. // break;
  465. case NET_SSH1_CIPHER_DES:
  466. $this->crypto = new Crypt_DES();
  467. $this->crypto->disablePadding();
  468. $this->crypto->enableContinuousBuffer();
  469. $this->crypto->setKey(substr($session_key, 0, 8));
  470. break;
  471. case NET_SSH1_CIPHER_3DES:
  472. $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC);
  473. $this->crypto->disablePadding();
  474. $this->crypto->enableContinuousBuffer();
  475. $this->crypto->setKey(substr($session_key, 0, 24));
  476. break;
  477. //case NET_SSH1_CIPHER_RC4:
  478. // $this->crypto = new Crypt_RC4();
  479. // $this->crypto->enableContinuousBuffer();
  480. // $this->crypto->setKey(substr($session_key, 0, 16));
  481. // break;
  482. }
  483. $response = $this->_get_binary_packet();
  484. if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
  485. user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
  486. return;
  487. }
  488. $this->bitmap = NET_SSH1_MASK_CONSTRUCTOR;
  489. }
  490. /**
  491. * Login
  492. *
  493. * @param String $username
  494. * @param optional String $password
  495. * @return Boolean
  496. * @access public
  497. */
  498. function login($username, $password = '')
  499. {
  500. if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) {
  501. return false;
  502. }
  503. $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
  504. if (!$this->_send_binary_packet($data)) {
  505. user_error('Error sending SSH_CMSG_USER', E_USER_NOTICE);
  506. return false;
  507. }
  508. $response = $this->_get_binary_packet();
  509. if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
  510. $this->bitmap |= NET_SSH1_MASK_LOGIN;
  511. return true;
  512. } else if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
  513. user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE', E_USER_NOTICE);
  514. return false;
  515. }
  516. $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
  517. if (!$this->_send_binary_packet($data)) {
  518. user_error('Error sending SSH_CMSG_AUTH_PASSWORD', E_USER_NOTICE);
  519. return false;
  520. }
  521. $response = $this->_get_binary_packet();
  522. if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
  523. $this->bitmap |= NET_SSH1_MASK_LOGIN;
  524. return true;
  525. } else if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
  526. return false;
  527. } else {
  528. user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE', E_USER_NOTICE);
  529. return false;
  530. }
  531. }
  532. /**
  533. * Executes a command on a non-interactive shell, returns the output, and quits.
  534. *
  535. * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2
  536. * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a
  537. * shell with the -s option, as discussed in the following links:
  538. *
  539. * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
  540. * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
  541. *
  542. * To execute further commands, a new Net_SSH1 object will need to be created.
  543. *
  544. * Returns false on failure and the output, otherwise.
  545. *
  546. * @see Net_SSH1::interactiveRead()
  547. * @see Net_SSH1::interactiveWrite()
  548. * @param String $cmd
  549. * @return mixed
  550. * @access public
  551. */
  552. function exec($cmd)
  553. {
  554. if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
  555. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  556. return false;
  557. }
  558. // connect using the sample parameters in protocol-1.5.txt.
  559. // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
  560. // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell.
  561. $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
  562. if (!$this->_send_binary_packet($data)) {
  563. user_error('Error sending SSH_CMSG_REQUEST_PTY', E_USER_NOTICE);
  564. return false;
  565. }
  566. $response = $this->_get_binary_packet();
  567. if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
  568. user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
  569. return false;
  570. }
  571. $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
  572. if (!$this->_send_binary_packet($data)) {
  573. user_error('Error sending SSH_CMSG_EXEC_CMD', E_USER_NOTICE);
  574. return false;
  575. }
  576. $output = '';
  577. $response = $this->_get_binary_packet();
  578. do {
  579. $output.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
  580. $response = $this->_get_binary_packet();
  581. } while ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
  582. $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
  583. // i don't think it's really all that important if this packet gets sent or not.
  584. $this->_send_binary_packet($data);
  585. fclose($this->fsock);
  586. // reset the execution bitmap - a new Net_SSH1 object needs to be created.
  587. $this->bitmap = 0;
  588. return $output;
  589. }
  590. /**
  591. * Creates an interactive shell
  592. *
  593. * @see Net_SSH1::interactiveRead()
  594. * @see Net_SSH1::interactiveWrite()
  595. * @return Boolean
  596. * @access private
  597. */
  598. function _initShell()
  599. {
  600. $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
  601. if (!$this->_send_binary_packet($data)) {
  602. user_error('Error sending SSH_CMSG_REQUEST_PTY', E_USER_NOTICE);
  603. return false;
  604. }
  605. $response = $this->_get_binary_packet();
  606. if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
  607. user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
  608. return false;
  609. }
  610. $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
  611. if (!$this->_send_binary_packet($data)) {
  612. user_error('Error sending SSH_CMSG_EXEC_SHELL', E_USER_NOTICE);
  613. return false;
  614. }
  615. $this->bitmap |= NET_SSH1_MASK_SHELL;
  616. //stream_set_blocking($this->fsock, 0);
  617. return true;
  618. }
  619. /**
  620. * Inputs a command into an interactive shell.
  621. *
  622. * @see Net_SSH1::interactiveRead()
  623. * @param String $cmd
  624. * @return Boolean
  625. * @access public
  626. */
  627. function interactiveWrite($cmd)
  628. {
  629. if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
  630. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  631. return false;
  632. }
  633. if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
  634. user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
  635. return false;
  636. }
  637. $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
  638. if (!$this->_send_binary_packet($data)) {
  639. user_error('Error sending SSH_CMSG_STDIN', E_USER_NOTICE);
  640. return false;
  641. }
  642. return true;
  643. }
  644. /**
  645. * Reads the output of an interactive shell.
  646. *
  647. * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like
  648. * "", you're seeing ANSI escape codes. According to
  649. * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
  650. * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
  651. * there's not going to be much recourse.
  652. *
  653. * @see Net_SSH1::interactiveRead()
  654. * @return String
  655. * @access public
  656. */
  657. function interactiveRead()
  658. {
  659. if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
  660. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  661. return false;
  662. }
  663. if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
  664. user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
  665. return false;
  666. }
  667. $read = array($this->fsock);
  668. $write = $except = null;
  669. if (stream_select($read, $write, $except, 0)) {
  670. $response = $this->_get_binary_packet();
  671. return substr($response[NET_SSH1_RESPONSE_DATA], 4);
  672. } else {
  673. return '';
  674. }
  675. }
  676. /**
  677. * Disconnect
  678. *
  679. * @access public
  680. */
  681. function disconnect()
  682. {
  683. $this->_disconnect();
  684. }
  685. /**
  686. * Destructor.
  687. *
  688. * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
  689. * disconnect().
  690. *
  691. * @access public
  692. */
  693. function __destruct()
  694. {
  695. $this->_disconnect();
  696. }
  697. /**
  698. * Disconnect
  699. *
  700. * @param String $msg
  701. * @access private
  702. */
  703. function _disconnect($msg = 'Client Quit')
  704. {
  705. if ($this->bitmap) {
  706. $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
  707. $this->_send_binary_packet($data);
  708. fclose($this->fsock);
  709. $this->bitmap = 0;
  710. }
  711. }
  712. /**
  713. * Gets Binary Packets
  714. *
  715. * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
  716. *
  717. * Also, this function could be improved upon by adding detection for the following exploit:
  718. * http://www.securiteam.com/securitynews/5LP042K3FY.html
  719. *
  720. * @see Net_SSH1::_send_binary_packet()
  721. * @return Array
  722. * @access private
  723. */
  724. function _get_binary_packet()
  725. {
  726. if (feof($this->fsock)) {
  727. //user_error('connection closed prematurely', E_USER_NOTICE);
  728. return false;
  729. }
  730. $temp = unpack('Nlength', fread($this->fsock, 4));
  731. $padding_length = 8 - ($temp['length'] & 7);
  732. $length = $temp['length'] + $padding_length;
  733. $raw = fread($this->fsock, $length);
  734. if ($this->crypto !== false) {
  735. $raw = $this->crypto->decrypt($raw);
  736. }
  737. $padding = substr($raw, 0, $padding_length);
  738. $type = $raw[$padding_length];
  739. $data = substr($raw, $padding_length + 1, -4);
  740. $temp = unpack('Ncrc', substr($raw, -4));
  741. //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
  742. // user_error('Bad CRC in packet from server', E_USER_NOTICE);
  743. // return false;
  744. //}
  745. return array(
  746. NET_SSH1_RESPONSE_TYPE => ord($type),
  747. NET_SSH1_RESPONSE_DATA => $data
  748. );
  749. }
  750. /**
  751. * Sends Binary Packets
  752. *
  753. * Returns true on success, false on failure.
  754. *
  755. * @see Net_SSH1::_get_binary_packet()
  756. * @param String $data
  757. * @return Boolean
  758. * @access private
  759. */
  760. function _send_binary_packet($data) {
  761. if (feof($this->fsock)) {
  762. //user_error('connection closed prematurely', E_USER_NOTICE);
  763. return false;
  764. }
  765. $length = strlen($data) + 4;
  766. $padding_length = 8 - ($length & 7);
  767. $padding = '';
  768. for ($i = 0; $i < $padding_length; $i++) {
  769. $padding.= chr(crypt_random(0, 255));
  770. }
  771. $data = $padding . $data;
  772. $data.= pack('N', $this->_crc($data));
  773. if ($this->crypto !== false) {
  774. $data = $this->crypto->encrypt($data);
  775. }
  776. $packet = pack('Na*', $length, $data);
  777. return strlen($packet) == fputs($this->fsock, $packet);
  778. }
  779. /**
  780. * Cyclic Redundancy Check (CRC)
  781. *
  782. * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
  783. * we've reimplemented it. A more detailed discussion of the differences can be found after
  784. * $crc_lookup_table's initialization.
  785. *
  786. * @see Net_SSH1::_get_binary_packet()
  787. * @see Net_SSH1::_send_binary_packet()
  788. * @param String $data
  789. * @return Integer
  790. * @access private
  791. */
  792. function _crc($data)
  793. {
  794. static $crc_lookup_table = array(
  795. 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
  796. 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
  797. 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
  798. 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
  799. 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
  800. 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
  801. 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
  802. 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
  803. 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
  804. 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
  805. 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
  806. 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
  807. 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
  808. 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
  809. 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
  810. 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
  811. 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
  812. 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
  813. 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
  814. 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
  815. 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
  816. 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
  817. 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
  818. 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
  819. 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
  820. 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
  821. 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
  822. 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
  823. 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
  824. 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
  825. 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
  826. 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
  827. 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
  828. 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
  829. 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
  830. 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
  831. 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
  832. 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
  833. 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
  834. 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
  835. 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
  836. 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
  837. 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
  838. 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
  839. 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
  840. 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
  841. 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
  842. 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
  843. 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
  844. 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
  845. 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
  846. 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
  847. 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
  848. 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
  849. 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
  850. 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
  851. 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
  852. 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
  853. 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
  854. 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
  855. 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
  856. 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
  857. 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
  858. 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
  859. );
  860. // For this function to yield the same output as PHP's crc32 function, $crc would have to be
  861. // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
  862. $crc = 0x00000000;
  863. $length = strlen($data);
  864. for ($i=0;$i<$length;$i++) {
  865. // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
  866. // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example,
  867. // yields 0xFF800000 - not 0x00800000. The following link elaborates:
  868. // http://www.php.net/manual/en/language.operators.bitwise.php#57281
  869. $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
  870. }
  871. // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
  872. // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
  873. return $crc;
  874. }
  875. /**
  876. * String Shift
  877. *
  878. * Inspired by array_shift
  879. *
  880. * @param String $string
  881. * @param optional Integer $index
  882. * @return String
  883. * @access private
  884. */
  885. function _string_shift(&$string, $index = 1)
  886. {
  887. $substr = substr($string, 0, $index);
  888. $string = substr($string, $index);
  889. return $substr;
  890. }
  891. /**
  892. * RSA Encrypt
  893. *
  894. * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
  895. * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that
  896. * calls this call modexp, instead, but I think this makes things clearer, maybe...
  897. *
  898. * @see Net_SSH1::Net_SSH1()
  899. * @param Math_BigInteger $m
  900. * @param Array $key
  901. * @return Math_BigInteger
  902. * @access private
  903. */
  904. function _rsa_crypt($m, $key)
  905. {
  906. /*
  907. if (!class_exists('Crypt_RSA')) {
  908. require_once('Crypt/RSA.php');
  909. }
  910. $rsa = new Crypt_RSA();
  911. $rsa->loadKey($key, CRYPT_RSA_PUBLIC_FORMAT_RAW);
  912. $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
  913. return $rsa->encrypt($m);
  914. */
  915. // To quote from protocol-1.5.txt:
  916. // The most significant byte (which is only partial as the value must be
  917. // less than the public modulus, which is never a power of two) is zero.
  918. //
  919. // The next byte contains the value 2 (which stands for public-key
  920. // encrypted data in the PKCS standard [PKCS#1]). Then, there are non-
  921. // zero random bytes to fill any unused space, a zero byte, and the data
  922. // to be encrypted in the least significant bytes, the last byte of the
  923. // data in the least significant byte.
  924. // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
  925. // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
  926. // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
  927. $temp = chr(0) . chr(2);
  928. $modulus = $key[1]->toBytes();
  929. $length = strlen($modulus) - strlen($m) - 3;
  930. for ($i = 0; $i < $length; $i++) {
  931. $temp.= chr(crypt_random(1, 255));
  932. }
  933. $temp.= chr(0) . $m;
  934. $m = new Math_BigInteger($temp, 256);
  935. $m = $m->modPow($key[0], $key[1]);
  936. return $m->toBytes();
  937. }
  938. /**
  939. * Return the server key public exponent
  940. *
  941. * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
  942. * the raw bytes. This behavior is similar to PHP's md5() function.
  943. *
  944. * @param optional Boolean $raw_output
  945. * @return String
  946. * @access public
  947. */
  948. function getServerKeyPublicExponent($raw_output = false)
  949. {
  950. return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
  951. }
  952. /**
  953. * Return the server key public modulus
  954. *
  955. * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
  956. * the raw bytes. This behavior is similar to PHP's md5() function.
  957. *
  958. * @param optional Boolean $raw_output
  959. * @return String
  960. * @access public
  961. */
  962. function getServerKeyPublicModulus($raw_output = false)
  963. {
  964. return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
  965. }
  966. /**
  967. * Return the host key public exponent
  968. *
  969. * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
  970. * the raw bytes. This behavior is similar to PHP's md5() function.
  971. *
  972. * @param optional Boolean $raw_output
  973. * @return String
  974. * @access public
  975. */
  976. function getHostKeyPublicExponent($raw_output = false)
  977. {
  978. return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
  979. }
  980. /**
  981. * Return the host key public modulus
  982. *
  983. * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
  984. * the raw bytes. This behavior is similar to PHP's md5() function.
  985. *
  986. * @param optional Boolean $raw_output
  987. * @return String
  988. * @access public
  989. */
  990. function getHostKeyPublicModulus($raw_output = false)
  991. {
  992. return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
  993. }
  994. /**
  995. * Return a list of ciphers supported by SSH1 server.
  996. *
  997. * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
  998. * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll
  999. * get array(NET_SSH1_CIPHER_3DES).
  1000. *
  1001. * @param optional Boolean $raw_output
  1002. * @return Array
  1003. * @access public
  1004. */
  1005. function getSupportedCiphers($raw_output = false)
  1006. {
  1007. return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
  1008. }
  1009. /**
  1010. * Return a list of authentications supported by SSH1 server.
  1011. *
  1012. * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
  1013. * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll
  1014. * get array(NET_SSH1_AUTH_PASSWORD).
  1015. *
  1016. * @param optional Boolean $raw_output
  1017. * @return Array
  1018. * @access public
  1019. */
  1020. function getSupportedAuthentications($raw_output = false)
  1021. {
  1022. return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
  1023. }
  1024. /**
  1025. * Return the server identification.
  1026. *
  1027. * @return String
  1028. * @access public
  1029. */
  1030. function getServerIdentification()
  1031. {
  1032. return rtrim($this->server_identification);
  1033. }
  1034. }