SSH2.php 96 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP implementation of SSHv2.
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * Here are some examples of how to use this library:
  9. * <code>
  10. * <?php
  11. * include('Net/SSH2.php');
  12. *
  13. * $ssh = new Net_SSH2('www.domain.tld');
  14. * if (!$ssh->login('username', 'password')) {
  15. * exit('Login Failed');
  16. * }
  17. *
  18. * echo $ssh->exec('pwd');
  19. * echo $ssh->exec('ls -la');
  20. * ?>
  21. * </code>
  22. *
  23. * <code>
  24. * <?php
  25. * include('Crypt/RSA.php');
  26. * include('Net/SSH2.php');
  27. *
  28. * $key = new Crypt_RSA();
  29. * //$key->setPassword('whatever');
  30. * $key->loadKey(file_get_contents('privatekey'));
  31. *
  32. * $ssh = new Net_SSH2('www.domain.tld');
  33. * if (!$ssh->login('username', $key)) {
  34. * exit('Login Failed');
  35. * }
  36. *
  37. * echo $ssh->read('username@username:~$');
  38. * $ssh->write("ls -la\n");
  39. * echo $ssh->read('username@username:~$');
  40. * ?>
  41. * </code>
  42. *
  43. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  44. * of this software and associated documentation files (the "Software"), to deal
  45. * in the Software without restriction, including without limitation the rights
  46. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  47. * copies of the Software, and to permit persons to whom the Software is
  48. * furnished to do so, subject to the following conditions:
  49. *
  50. * The above copyright notice and this permission notice shall be included in
  51. * all copies or substantial portions of the Software.
  52. *
  53. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  54. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  55. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  56. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  57. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  58. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  59. * THE SOFTWARE.
  60. *
  61. * @category Net
  62. * @package Net_SSH2
  63. * @author Jim Wigginton <terrafrost@php.net>
  64. * @copyright MMVII Jim Wigginton
  65. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  66. * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $
  67. * @link http://phpseclib.sourceforge.net
  68. */
  69. /**
  70. * Include Math_BigInteger
  71. *
  72. * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  73. */
  74. require_once(INITPHP_PATH.'/library/ssh/Math/BigInteger.php');
  75. /**
  76. * Include Crypt_Random
  77. */
  78. require_once(INITPHP_PATH.'/library/ssh/Crypt/Random.php');
  79. /**
  80. * Include Crypt_Hash
  81. */
  82. require_once(INITPHP_PATH.'/library/ssh/Crypt/Hash.php');
  83. /**
  84. * Include Crypt_TripleDES
  85. */
  86. require_once(INITPHP_PATH.'/library/ssh/Crypt/TripleDES.php');
  87. /**
  88. * Include Crypt_RC4
  89. */
  90. require_once(INITPHP_PATH.'/library/ssh/Crypt/RC4.php');
  91. /**
  92. * Include Crypt_AES
  93. */
  94. require_once(INITPHP_PATH.'/library/ssh/Crypt/AES.php');
  95. /**#@+
  96. * Execution Bitmap Masks
  97. *
  98. * @see Net_SSH2::bitmap
  99. * @access private
  100. */
  101. define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
  102. define('NET_SSH2_MASK_LOGIN', 0x00000002);
  103. define('NET_SSH2_MASK_SHELL', 0x00000004);
  104. /**#@-*/
  105. /**#@+
  106. * Channel constants
  107. *
  108. * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
  109. * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  110. * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  111. * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  112. * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  113. * The 'recipient channel' is the channel number given in the original
  114. * open request, and 'sender channel' is the channel number allocated by
  115. * the other side.
  116. *
  117. * @see Net_SSH2::_send_channel_packet()
  118. * @see Net_SSH2::_get_channel_packet()
  119. * @access private
  120. */
  121. define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
  122. define('NET_SSH2_CHANNEL_SHELL',1);
  123. /**#@-*/
  124. /**#@+
  125. * @access public
  126. * @see Net_SSH2::getLog()
  127. */
  128. /**
  129. * Returns the message numbers
  130. */
  131. define('NET_SSH2_LOG_SIMPLE', 1);
  132. /**
  133. * Returns the message content
  134. */
  135. define('NET_SSH2_LOG_COMPLEX', 2);
  136. /**#@-*/
  137. /**#@+
  138. * @access public
  139. * @see Net_SSH2::read()
  140. */
  141. /**
  142. * Returns when a string matching $expect exactly is found
  143. */
  144. define('NET_SSH2_READ_SIMPLE', 1);
  145. /**
  146. * Returns when a string matching the regular expression $expect is found
  147. */
  148. define('NET_SSH2_READ_REGEX', 2);
  149. /**#@-*/
  150. /**
  151. * Pure-PHP implementation of SSHv2.
  152. *
  153. * @author Jim Wigginton <terrafrost@php.net>
  154. * @version 0.1.0
  155. * @access public
  156. * @package Net_SSH2
  157. */
  158. class Net_SSH2 {
  159. /**
  160. * The SSH identifier
  161. *
  162. * @var String
  163. * @access private
  164. */
  165. var $identifier = 'SSH-2.0-phpseclib_0.2';
  166. /**
  167. * The Socket Object
  168. *
  169. * @var Object
  170. * @access private
  171. */
  172. var $fsock;
  173. /**
  174. * Execution Bitmap
  175. *
  176. * The bits that are set reprsent functions that have been called already. This is used to determine
  177. * if a requisite function has been successfully executed. If not, an error should be thrown.
  178. *
  179. * @var Integer
  180. * @access private
  181. */
  182. var $bitmap = 0;
  183. /**
  184. * Error information
  185. *
  186. * @see Net_SSH2::getErrors()
  187. * @see Net_SSH2::getLastError()
  188. * @var String
  189. * @access private
  190. */
  191. var $errors = array();
  192. /**
  193. * Server Identifier
  194. *
  195. * @see Net_SSH2::getServerIdentification()
  196. * @var String
  197. * @access private
  198. */
  199. var $server_identifier = '';
  200. /**
  201. * Key Exchange Algorithms
  202. *
  203. * @see Net_SSH2::getKexAlgorithims()
  204. * @var Array
  205. * @access private
  206. */
  207. var $kex_algorithms;
  208. /**
  209. * Server Host Key Algorithms
  210. *
  211. * @see Net_SSH2::getServerHostKeyAlgorithms()
  212. * @var Array
  213. * @access private
  214. */
  215. var $server_host_key_algorithms;
  216. /**
  217. * Encryption Algorithms: Client to Server
  218. *
  219. * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
  220. * @var Array
  221. * @access private
  222. */
  223. var $encryption_algorithms_client_to_server;
  224. /**
  225. * Encryption Algorithms: Server to Client
  226. *
  227. * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
  228. * @var Array
  229. * @access private
  230. */
  231. var $encryption_algorithms_server_to_client;
  232. /**
  233. * MAC Algorithms: Client to Server
  234. *
  235. * @see Net_SSH2::getMACAlgorithmsClient2Server()
  236. * @var Array
  237. * @access private
  238. */
  239. var $mac_algorithms_client_to_server;
  240. /**
  241. * MAC Algorithms: Server to Client
  242. *
  243. * @see Net_SSH2::getMACAlgorithmsServer2Client()
  244. * @var Array
  245. * @access private
  246. */
  247. var $mac_algorithms_server_to_client;
  248. /**
  249. * Compression Algorithms: Client to Server
  250. *
  251. * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
  252. * @var Array
  253. * @access private
  254. */
  255. var $compression_algorithms_client_to_server;
  256. /**
  257. * Compression Algorithms: Server to Client
  258. *
  259. * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
  260. * @var Array
  261. * @access private
  262. */
  263. var $compression_algorithms_server_to_client;
  264. /**
  265. * Languages: Server to Client
  266. *
  267. * @see Net_SSH2::getLanguagesServer2Client()
  268. * @var Array
  269. * @access private
  270. */
  271. var $languages_server_to_client;
  272. /**
  273. * Languages: Client to Server
  274. *
  275. * @see Net_SSH2::getLanguagesClient2Server()
  276. * @var Array
  277. * @access private
  278. */
  279. var $languages_client_to_server;
  280. /**
  281. * Block Size for Server to Client Encryption
  282. *
  283. * "Note that the length of the concatenation of 'packet_length',
  284. * 'padding_length', 'payload', and 'random padding' MUST be a multiple
  285. * of the cipher block size or 8, whichever is larger. This constraint
  286. * MUST be enforced, even when using stream ciphers."
  287. *
  288. * -- http://tools.ietf.org/html/rfc4253#section-6
  289. *
  290. * @see Net_SSH2::Net_SSH2()
  291. * @see Net_SSH2::_send_binary_packet()
  292. * @var Integer
  293. * @access private
  294. */
  295. var $encrypt_block_size = 8;
  296. /**
  297. * Block Size for Client to Server Encryption
  298. *
  299. * @see Net_SSH2::Net_SSH2()
  300. * @see Net_SSH2::_get_binary_packet()
  301. * @var Integer
  302. * @access private
  303. */
  304. var $decrypt_block_size = 8;
  305. /**
  306. * Server to Client Encryption Object
  307. *
  308. * @see Net_SSH2::_get_binary_packet()
  309. * @var Object
  310. * @access private
  311. */
  312. var $decrypt = false;
  313. /**
  314. * Client to Server Encryption Object
  315. *
  316. * @see Net_SSH2::_send_binary_packet()
  317. * @var Object
  318. * @access private
  319. */
  320. var $encrypt = false;
  321. /**
  322. * Client to Server HMAC Object
  323. *
  324. * @see Net_SSH2::_send_binary_packet()
  325. * @var Object
  326. * @access private
  327. */
  328. var $hmac_create = false;
  329. /**
  330. * Server to Client HMAC Object
  331. *
  332. * @see Net_SSH2::_get_binary_packet()
  333. * @var Object
  334. * @access private
  335. */
  336. var $hmac_check = false;
  337. /**
  338. * Size of server to client HMAC
  339. *
  340. * 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.
  341. * 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
  342. * append it.
  343. *
  344. * @see Net_SSH2::_get_binary_packet()
  345. * @var Integer
  346. * @access private
  347. */
  348. var $hmac_size = false;
  349. /**
  350. * Server Public Host Key
  351. *
  352. * @see Net_SSH2::getServerPublicHostKey()
  353. * @var String
  354. * @access private
  355. */
  356. var $server_public_host_key;
  357. /**
  358. * Session identifer
  359. *
  360. * "The exchange hash H from the first key exchange is additionally
  361. * used as the session identifier, which is a unique identifier for
  362. * this connection."
  363. *
  364. * -- http://tools.ietf.org/html/rfc4253#section-7.2
  365. *
  366. * @see Net_SSH2::_key_exchange()
  367. * @var String
  368. * @access private
  369. */
  370. var $session_id = false;
  371. /**
  372. * Exchange hash
  373. *
  374. * The current exchange hash
  375. *
  376. * @see Net_SSH2::_key_exchange()
  377. * @var String
  378. * @access private
  379. */
  380. var $exchange_hash = false;
  381. /**
  382. * Message Numbers
  383. *
  384. * @see Net_SSH2::Net_SSH2()
  385. * @var Array
  386. * @access private
  387. */
  388. var $message_numbers = array();
  389. /**
  390. * Disconnection Message 'reason codes' defined in RFC4253
  391. *
  392. * @see Net_SSH2::Net_SSH2()
  393. * @var Array
  394. * @access private
  395. */
  396. var $disconnect_reasons = array();
  397. /**
  398. * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  399. *
  400. * @see Net_SSH2::Net_SSH2()
  401. * @var Array
  402. * @access private
  403. */
  404. var $channel_open_failure_reasons = array();
  405. /**
  406. * Terminal Modes
  407. *
  408. * @link http://tools.ietf.org/html/rfc4254#section-8
  409. * @see Net_SSH2::Net_SSH2()
  410. * @var Array
  411. * @access private
  412. */
  413. var $terminal_modes = array();
  414. /**
  415. * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  416. *
  417. * @link http://tools.ietf.org/html/rfc4254#section-5.2
  418. * @see Net_SSH2::Net_SSH2()
  419. * @var Array
  420. * @access private
  421. */
  422. var $channel_extended_data_type_codes = array();
  423. /**
  424. * Send Sequence Number
  425. *
  426. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  427. *
  428. * @see Net_SSH2::_send_binary_packet()
  429. * @var Integer
  430. * @access private
  431. */
  432. var $send_seq_no = 0;
  433. /**
  434. * Get Sequence Number
  435. *
  436. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  437. *
  438. * @see Net_SSH2::_get_binary_packet()
  439. * @var Integer
  440. * @access private
  441. */
  442. var $get_seq_no = 0;
  443. /**
  444. * Server Channels
  445. *
  446. * Maps client channels to server channels
  447. *
  448. * @see Net_SSH2::_get_channel_packet()
  449. * @see Net_SSH2::exec()
  450. * @var Array
  451. * @access private
  452. */
  453. var $server_channels = array();
  454. /**
  455. * Channel Buffers
  456. *
  457. * If a client requests a packet from one channel but receives two packets from another those packets should
  458. * be placed in a buffer
  459. *
  460. * @see Net_SSH2::_get_channel_packet()
  461. * @see Net_SSH2::exec()
  462. * @var Array
  463. * @access private
  464. */
  465. var $channel_buffers = array();
  466. /**
  467. * Channel Status
  468. *
  469. * Contains the type of the last sent message
  470. *
  471. * @see Net_SSH2::_get_channel_packet()
  472. * @var Array
  473. * @access private
  474. */
  475. var $channel_status = array();
  476. /**
  477. * Packet Size
  478. *
  479. * Maximum packet size indexed by channel
  480. *
  481. * @see Net_SSH2::_send_channel_packet()
  482. * @var Array
  483. * @access private
  484. */
  485. var $packet_size_client_to_server = array();
  486. /**
  487. * Message Number Log
  488. *
  489. * @see Net_SSH2::getLog()
  490. * @var Array
  491. * @access private
  492. */
  493. var $message_number_log = array();
  494. /**
  495. * Message Log
  496. *
  497. * @see Net_SSH2::getLog()
  498. * @var Array
  499. * @access private
  500. */
  501. var $message_log = array();
  502. /**
  503. * The Window Size
  504. *
  505. * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB)
  506. *
  507. * @var Integer
  508. * @see Net_SSH2::_send_channel_packet()
  509. * @see Net_SSH2::exec()
  510. * @access private
  511. */
  512. var $window_size = 0x7FFFFFFF;
  513. /**
  514. * Window size
  515. *
  516. * Window size indexed by channel
  517. *
  518. * @see Net_SSH2::_send_channel_packet()
  519. * @var Array
  520. * @access private
  521. */
  522. var $window_size_client_to_server = array();
  523. /**
  524. * Server signature
  525. *
  526. * Verified against $this->session_id
  527. *
  528. * @see Net_SSH2::getServerPublicHostKey()
  529. * @var String
  530. * @access private
  531. */
  532. var $signature = '';
  533. /**
  534. * Server signature format
  535. *
  536. * ssh-rsa or ssh-dss.
  537. *
  538. * @see Net_SSH2::getServerPublicHostKey()
  539. * @var String
  540. * @access private
  541. */
  542. var $signature_format = '';
  543. /**
  544. * Interactive Buffer
  545. *
  546. * @see Net_SSH2::read()
  547. * @var Array
  548. * @access private
  549. */
  550. var $interactiveBuffer = '';
  551. /**
  552. * Default Constructor.
  553. *
  554. * Connects to an SSHv2 server
  555. *
  556. * @param String $host
  557. * @param optional Integer $port
  558. * @param optional Integer $timeout
  559. * @return Net_SSH2
  560. * @access public
  561. */
  562. function Net_SSH2($host, $port = 22, $timeout = 10)
  563. {
  564. $this->message_numbers = array(
  565. 1 => 'NET_SSH2_MSG_DISCONNECT',
  566. 2 => 'NET_SSH2_MSG_IGNORE',
  567. 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
  568. 4 => 'NET_SSH2_MSG_DEBUG',
  569. 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
  570. 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  571. 20 => 'NET_SSH2_MSG_KEXINIT',
  572. 21 => 'NET_SSH2_MSG_NEWKEYS',
  573. 30 => 'NET_SSH2_MSG_KEXDH_INIT',
  574. 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  575. 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  576. 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  577. 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  578. 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  579. 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  580. 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  581. 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  582. 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  583. 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  584. 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  585. 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  586. 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  587. 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  588. 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  589. 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  590. 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  591. 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  592. 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  593. );
  594. $this->disconnect_reasons = array(
  595. 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  596. 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  597. 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  598. 4 => 'NET_SSH2_DISCONNECT_RESERVED',
  599. 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  600. 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  601. 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  602. 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  603. 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  604. 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  605. 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  606. 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  607. 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  608. 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  609. 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  610. );
  611. $this->channel_open_failure_reasons = array(
  612. 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  613. );
  614. $this->terminal_modes = array(
  615. 0 => 'NET_SSH2_TTY_OP_END'
  616. );
  617. $this->channel_extended_data_type_codes = array(
  618. 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
  619. );
  620. $this->_define_array(
  621. $this->message_numbers,
  622. $this->disconnect_reasons,
  623. $this->channel_open_failure_reasons,
  624. $this->terminal_modes,
  625. $this->channel_extended_data_type_codes,
  626. array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
  627. array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
  628. array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  629. 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE')
  630. );
  631. $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
  632. if (!$this->fsock) {
  633. user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
  634. return;
  635. }
  636. /* According to the SSH2 specs,
  637. "The server MAY send other lines of data before sending the version
  638. string. Each line SHOULD be terminated by a Carriage Return and Line
  639. Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  640. in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
  641. MUST be able to process such lines." */
  642. $temp = '';
  643. $extra = '';
  644. while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
  645. if (substr($temp, -2) == "\r\n") {
  646. $extra.= $temp;
  647. $temp = '';
  648. }
  649. $temp.= fgets($this->fsock, 255);
  650. }
  651. if (feof($this->fsock)) {
  652. user_error('Connection closed by server', E_USER_NOTICE);
  653. return false;
  654. }
  655. $ext = array();
  656. if (extension_loaded('mcrypt')) {
  657. $ext[] = 'mcrypt';
  658. }
  659. if (extension_loaded('gmp')) {
  660. $ext[] = 'gmp';
  661. } else if (extension_loaded('bcmath')) {
  662. $ext[] = 'bcmath';
  663. }
  664. if (!empty($ext)) {
  665. $this->identifier.= ' (' . implode(', ', $ext) . ')';
  666. }
  667. if (defined('NET_SSH2_LOGGING')) {
  668. $this->message_number_log[] = '<-';
  669. $this->message_number_log[] = '->';
  670. if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  671. $this->message_log[] = $temp;
  672. $this->message_log[] = $this->identifier . "\r\n";
  673. }
  674. }
  675. $this->server_identifier = trim($temp, "\r\n");
  676. if (!empty($extra)) {
  677. $this->errors[] = utf8_decode($extra);
  678. }
  679. if ($matches[1] != '1.99' && $matches[1] != '2.0') {
  680. user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
  681. return;
  682. }
  683. fputs($this->fsock, $this->identifier . "\r\n");
  684. $response = $this->_get_binary_packet();
  685. if ($response === false) {
  686. user_error('Connection closed by server', E_USER_NOTICE);
  687. return;
  688. }
  689. if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  690. user_error('Expected SSH_MSG_KEXINIT', E_USER_NOTICE);
  691. return;
  692. }
  693. if (!$this->_key_exchange($response)) {
  694. return;
  695. }
  696. $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
  697. }
  698. /**
  699. * Key Exchange
  700. *
  701. * @param String $kexinit_payload_server
  702. * @access private
  703. */
  704. function _key_exchange($kexinit_payload_server)
  705. {
  706. static $kex_algorithms = array(
  707. 'diffie-hellman-group1-sha1', // REQUIRED
  708. 'diffie-hellman-group14-sha1' // REQUIRED
  709. );
  710. static $server_host_key_algorithms = array(
  711. 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
  712. 'ssh-dss' // REQUIRED sign Raw DSS Key
  713. );
  714. static $encryption_algorithms = array(
  715. // from <http://tools.ietf.org/html/rfc4345#section-4>:
  716. 'arcfour256',
  717. 'arcfour128',
  718. 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
  719. 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
  720. 'aes192-cbc', // OPTIONAL AES with a 192-bit key
  721. 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
  722. // from <http://tools.ietf.org/html/rfc4344#section-4>:
  723. 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
  724. 'aes192-ctr', // RECOMMENDED AES with 192-bit key
  725. 'aes256-ctr', // RECOMMENDED AES with 256-bit key
  726. '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
  727. '3des-cbc', // REQUIRED three-key 3DES in CBC mode
  728. 'none' // OPTIONAL no encryption; NOT RECOMMENDED
  729. );
  730. static $mac_algorithms = array(
  731. 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
  732. 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
  733. 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
  734. 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
  735. 'none' // OPTIONAL no MAC; NOT RECOMMENDED
  736. );
  737. static $compression_algorithms = array(
  738. 'none' // REQUIRED no compression
  739. //'zlib' // OPTIONAL ZLIB (LZ77) compression
  740. );
  741. static $str_kex_algorithms, $str_server_host_key_algorithms,
  742. $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
  743. $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
  744. if (empty($str_kex_algorithms)) {
  745. $str_kex_algorithms = implode(',', $kex_algorithms);
  746. $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
  747. $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
  748. $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
  749. $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
  750. }
  751. $client_cookie = '';
  752. for ($i = 0; $i < 16; $i++) {
  753. $client_cookie.= chr(crypt_random(0, 255));
  754. }
  755. $response = $kexinit_payload_server;
  756. $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  757. $server_cookie = $this->_string_shift($response, 16);
  758. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  759. $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  760. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  761. $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  762. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  763. $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  764. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  765. $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  766. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  767. $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  768. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  769. $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  770. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  771. $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  772. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  773. $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  774. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  775. $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  776. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  777. $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  778. extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
  779. $first_kex_packet_follows = $first_kex_packet_follows != 0;
  780. // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
  781. $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
  782. NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
  783. strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
  784. $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
  785. strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
  786. $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
  787. strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
  788. 0, 0
  789. );
  790. if (!$this->_send_binary_packet($kexinit_payload_client)) {
  791. return false;
  792. }
  793. // here ends the second place.
  794. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  795. for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
  796. if ($i == count($encryption_algorithms)) {
  797. user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
  798. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  799. }
  800. // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  801. // diffie-hellman key exchange as fast as possible
  802. $decrypt = $encryption_algorithms[$i];
  803. switch ($decrypt) {
  804. case '3des-cbc':
  805. case '3des-ctr':
  806. $decryptKeyLength = 24; // eg. 192 / 8
  807. break;
  808. case 'aes256-cbc':
  809. case 'aes256-ctr':
  810. $decryptKeyLength = 32; // eg. 256 / 8
  811. break;
  812. case 'aes192-cbc':
  813. case 'aes192-ctr':
  814. $decryptKeyLength = 24; // eg. 192 / 8
  815. break;
  816. case 'aes128-cbc':
  817. case 'aes128-ctr':
  818. $decryptKeyLength = 16; // eg. 128 / 8
  819. break;
  820. case 'arcfour':
  821. case 'arcfour128':
  822. $decryptKeyLength = 16; // eg. 128 / 8
  823. break;
  824. case 'arcfour256':
  825. $decryptKeyLength = 32; // eg. 128 / 8
  826. break;
  827. case 'none';
  828. $decryptKeyLength = 0;
  829. }
  830. for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
  831. if ($i == count($encryption_algorithms)) {
  832. user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
  833. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  834. }
  835. $encrypt = $encryption_algorithms[$i];
  836. switch ($encrypt) {
  837. case '3des-cbc':
  838. case '3des-ctr':
  839. $encryptKeyLength = 24;
  840. break;
  841. case 'aes256-cbc':
  842. case 'aes256-ctr':
  843. $encryptKeyLength = 32;
  844. break;
  845. case 'aes192-cbc':
  846. case 'aes192-ctr':
  847. $encryptKeyLength = 24;
  848. break;
  849. case 'aes128-cbc':
  850. case 'aes128-ctr':
  851. $encryptKeyLength = 16;
  852. break;
  853. case 'arcfour':
  854. case 'arcfour128':
  855. $encryptKeyLength = 16;
  856. break;
  857. case 'arcfour256':
  858. $encryptKeyLength = 32;
  859. break;
  860. case 'none';
  861. $encryptKeyLength = 0;
  862. }
  863. $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
  864. // through diffie-hellman key exchange a symmetric key is obtained
  865. for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
  866. if ($i == count($kex_algorithms)) {
  867. user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
  868. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  869. }
  870. switch ($kex_algorithms[$i]) {
  871. // see http://tools.ietf.org/html/rfc2409#section-6.2 and
  872. // http://tools.ietf.org/html/rfc2412, appendex E
  873. case 'diffie-hellman-group1-sha1':
  874. $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  875. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  876. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  877. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
  878. $keyLength = $keyLength < 160 ? $keyLength : 160;
  879. $hash = 'sha1';
  880. break;
  881. // see http://tools.ietf.org/html/rfc3526#section-3
  882. case 'diffie-hellman-group14-sha1':
  883. $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  884. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  885. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  886. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
  887. '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
  888. '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
  889. 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
  890. '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
  891. $keyLength = $keyLength < 160 ? $keyLength : 160;
  892. $hash = 'sha1';
  893. }
  894. $p = new Math_BigInteger($p, 256);
  895. //$q = $p->bitwise_rightShift(1);
  896. /* To increase the speed of the key exchange, both client and server may
  897. reduce the size of their private exponents. It should be at least
  898. twice as long as the key material that is generated from the shared
  899. secret. For more details, see the paper by van Oorschot and Wiener
  900. [VAN-OORSCHOT].
  901. -- http://tools.ietf.org/html/rfc4419#section-6.2 */
  902. $q = new Math_BigInteger(1);
  903. $q = $q->bitwise_leftShift(2 * $keyLength);
  904. $q = $q->subtract(new Math_BigInteger(1));
  905. $g = new Math_BigInteger(2);
  906. $x = new Math_BigInteger();
  907. $x->setRandomGenerator('crypt_random');
  908. $x = $x->random(new Math_BigInteger(1), $q);
  909. $e = $g->modPow($x, $p);
  910. $eBytes = $e->toBytes(true);
  911. $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
  912. if (!$this->_send_binary_packet($data)) {
  913. user_error('Connection closed by server', E_USER_NOTICE);
  914. return false;
  915. }
  916. $response = $this->_get_binary_packet();
  917. if ($response === false) {
  918. user_error('Connection closed by server', E_USER_NOTICE);
  919. return false;
  920. }
  921. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  922. if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
  923. user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
  924. return false;
  925. }
  926. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  927. $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
  928. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  929. $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
  930. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  931. $fBytes = $this->_string_shift($response, $temp['length']);
  932. $f = new Math_BigInteger($fBytes, -256);
  933. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  934. $this->signature = $this->_string_shift($response, $temp['length']);
  935. $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
  936. $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
  937. $key = $f->modPow($x, $p);
  938. $keyBytes = $key->toBytes(true);
  939. $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
  940. strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
  941. strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
  942. $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
  943. $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
  944. );
  945. $this->exchange_hash = pack('H*', $hash($this->exchange_hash));
  946. if ($this->session_id === false) {
  947. $this->session_id = $this->exchange_hash;
  948. }
  949. for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
  950. if ($i == count($server_host_key_algorithms)) {
  951. user_error('No compatible server host key algorithms found', E_USER_NOTICE);
  952. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  953. }
  954. if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
  955. user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
  956. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  957. }
  958. $packet = pack('C',
  959. NET_SSH2_MSG_NEWKEYS
  960. );
  961. if (!$this->_send_binary_packet($packet)) {
  962. return false;
  963. }
  964. $response = $this->_get_binary_packet();
  965. if ($response === false) {
  966. user_error('Connection closed by server', E_USER_NOTICE);
  967. return false;
  968. }
  969. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  970. if ($type != NET_SSH2_MSG_NEWKEYS) {
  971. user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
  972. return false;
  973. }
  974. switch ($encrypt) {
  975. case '3des-cbc':
  976. $this->encrypt = new Crypt_TripleDES();
  977. // $this->encrypt_block_size = 64 / 8 == the default
  978. break;
  979. case '3des-ctr':
  980. $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  981. // $this->encrypt_block_size = 64 / 8 == the default
  982. break;
  983. case 'aes256-cbc':
  984. case 'aes192-cbc':
  985. case 'aes128-cbc':
  986. $this->encrypt = new Crypt_AES();
  987. $this->encrypt_block_size = 16; // eg. 128 / 8
  988. break;
  989. case 'aes256-ctr':
  990. case 'aes192-ctr':
  991. case 'aes128-ctr':
  992. $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
  993. $this->encrypt_block_size = 16; // eg. 128 / 8
  994. break;
  995. case 'arcfour':
  996. case 'arcfour128':
  997. case 'arcfour256':
  998. $this->encrypt = new Crypt_RC4();
  999. break;
  1000. case 'none';
  1001. //$this->encrypt = new Crypt_Null();
  1002. }
  1003. switch ($decrypt) {
  1004. case '3des-cbc':
  1005. $this->decrypt = new Crypt_TripleDES();
  1006. break;
  1007. case '3des-ctr':
  1008. $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  1009. break;
  1010. case 'aes256-cbc':
  1011. case 'aes192-cbc':
  1012. case 'aes128-cbc':
  1013. $this->decrypt = new Crypt_AES();
  1014. $this->decrypt_block_size = 16;
  1015. break;
  1016. case 'aes256-ctr':
  1017. case 'aes192-ctr':
  1018. case 'aes128-ctr':
  1019. $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
  1020. $this->decrypt_block_size = 16;
  1021. break;
  1022. case 'arcfour':
  1023. case 'arcfour128':
  1024. case 'arcfour256':
  1025. $this->decrypt = new Crypt_RC4();
  1026. break;
  1027. case 'none';
  1028. //$this->decrypt = new Crypt_Null();
  1029. }
  1030. $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
  1031. if ($this->encrypt) {
  1032. $this->encrypt->enableContinuousBuffer();
  1033. $this->encrypt->disablePadding();
  1034. $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id));
  1035. while ($this->encrypt_block_size > strlen($iv)) {
  1036. $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
  1037. }
  1038. $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
  1039. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id));
  1040. while ($encryptKeyLength > strlen($key)) {
  1041. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1042. }
  1043. $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
  1044. }
  1045. if ($this->decrypt) {
  1046. $this->decrypt->enableContinuousBuffer();
  1047. $this->decrypt->disablePadding();
  1048. $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id));
  1049. while ($this->decrypt_block_size > strlen($iv)) {
  1050. $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
  1051. }
  1052. $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
  1053. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id));
  1054. while ($decryptKeyLength > strlen($key)) {
  1055. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1056. }
  1057. $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
  1058. }
  1059. /* The "arcfour128" algorithm is the RC4 cipher, as described in
  1060. [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
  1061. generated by the cipher MUST be discarded, and the first byte of the
  1062. first encrypted packet MUST be encrypted using the 1537th byte of
  1063. keystream.
  1064. -- http://tools.ietf.org/html/rfc4345#section-4 */
  1065. if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
  1066. $this->encrypt->encrypt(str_repeat("\0", 1536));
  1067. }
  1068. if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
  1069. $this->decrypt->decrypt(str_repeat("\0", 1536));
  1070. }
  1071. for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
  1072. if ($i == count($mac_algorithms)) {
  1073. user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
  1074. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1075. }
  1076. $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
  1077. switch ($mac_algorithms[$i]) {
  1078. case 'hmac-sha1':
  1079. $this->hmac_create = new Crypt_Hash('sha1');
  1080. $createKeyLength = 20;
  1081. break;
  1082. case 'hmac-sha1-96':
  1083. $this->hmac_create = new Crypt_Hash('sha1-96');
  1084. $createKeyLength = 20;
  1085. break;
  1086. case 'hmac-md5':
  1087. $this->hmac_create = new Crypt_Hash('md5');
  1088. $createKeyLength = 16;
  1089. break;
  1090. case 'hmac-md5-96':
  1091. $this->hmac_create = new Crypt_Hash('md5-96');
  1092. $createKeyLength = 16;
  1093. }
  1094. for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
  1095. if ($i == count($mac_algorithms)) {
  1096. user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
  1097. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1098. }
  1099. $checkKeyLength = 0;
  1100. $this->hmac_size = 0;
  1101. switch ($mac_algorithms[$i]) {
  1102. case 'hmac-sha1':
  1103. $this->hmac_check = new Crypt_Hash('sha1');
  1104. $checkKeyLength = 20;
  1105. $this->hmac_size = 20;
  1106. break;
  1107. case 'hmac-sha1-96':
  1108. $this->hmac_check = new Crypt_Hash('sha1-96');
  1109. $checkKeyLength = 20;
  1110. $this->hmac_size = 12;
  1111. break;
  1112. case 'hmac-md5':
  1113. $this->hmac_check = new Crypt_Hash('md5');
  1114. $checkKeyLength = 16;
  1115. $this->hmac_size = 16;
  1116. break;
  1117. case 'hmac-md5-96':
  1118. $this->hmac_check = new Crypt_Hash('md5-96');
  1119. $checkKeyLength = 16;
  1120. $this->hmac_size = 12;
  1121. }
  1122. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id));
  1123. while ($createKeyLength > strlen($key)) {
  1124. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1125. }
  1126. $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
  1127. $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id));
  1128. while ($checkKeyLength > strlen($key)) {
  1129. $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
  1130. }
  1131. $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
  1132. for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
  1133. if ($i == count($compression_algorithms)) {
  1134. user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
  1135. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1136. }
  1137. $this->decompress = $compression_algorithms[$i] == 'zlib';
  1138. for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
  1139. if ($i == count($compression_algorithms)) {
  1140. user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
  1141. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1142. }
  1143. $this->compress = $compression_algorithms[$i] == 'zlib';
  1144. return true;
  1145. }
  1146. /**
  1147. * Login
  1148. *
  1149. * The $password parameter can be a plaintext password or a Crypt_RSA object.
  1150. *
  1151. * @param String $username
  1152. * @param optional String $password
  1153. * @return Boolean
  1154. * @access public
  1155. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1156. * by sending dummy SSH_MSG_IGNORE messages.
  1157. */
  1158. function login($username, $password = '')
  1159. {
  1160. if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
  1161. return false;
  1162. }
  1163. $packet = pack('CNa*',
  1164. NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
  1165. );
  1166. if (!$this->_send_binary_packet($packet)) {
  1167. return false;
  1168. }
  1169. $response = $this->_get_binary_packet();
  1170. if ($response === false) {
  1171. user_error('Connection closed by server', E_USER_NOTICE);
  1172. return false;
  1173. }
  1174. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1175. if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
  1176. user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE);
  1177. return false;
  1178. }
  1179. // although PHP5's get_class() preserves the case, PHP4's does not
  1180. if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
  1181. return $this->_privatekey_login($username, $password);
  1182. }
  1183. $utf8_password = utf8_encode($password);
  1184. $packet = pack('CNa*Na*Na*CNa*',
  1185. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1186. strlen('password'), 'password', 0, strlen($utf8_password), $utf8_password
  1187. );
  1188. if (!$this->_send_binary_packet($packet)) {
  1189. return false;
  1190. }
  1191. // remove the username and password from the last logged packet
  1192. if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  1193. $packet = pack('CNa*Na*Na*CNa*',
  1194. NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
  1195. strlen('password'), 'password', 0, strlen('password'), 'password'
  1196. );
  1197. $this->message_log[count($this->message_log) - 1] = $packet;
  1198. }
  1199. $response = $this->_get_binary_packet();
  1200. if ($response === false) {
  1201. user_error('Connection closed by server', E_USER_NOTICE);
  1202. return false;
  1203. }
  1204. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1205. switch ($type) {
  1206. case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
  1207. if (defined('NET_SSH2_LOGGING')) {
  1208. $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
  1209. }
  1210. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1211. $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
  1212. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  1213. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1214. // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
  1215. // multi-factor authentication
  1216. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1217. $auth_methods = explode(',', $this->_string_shift($response, $length));
  1218. if (in_array('keyboard-interactive', $auth_methods)) {
  1219. if ($this->_keyboard_interactive_login($username, $password)) {
  1220. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1221. return true;
  1222. }
  1223. return false;
  1224. }
  1225. return false;
  1226. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1227. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1228. return true;
  1229. }
  1230. return false;
  1231. }
  1232. /**
  1233. * Login via keyboard-interactive authentication
  1234. *
  1235. * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
  1236. *
  1237. * @param String $username
  1238. * @param String $password
  1239. * @return Boolean
  1240. * @access private
  1241. */
  1242. function _keyboard_interactive_login($username, $password)
  1243. {
  1244. $packet = pack('CNa*Na*Na*Na*Na*',
  1245. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1246. strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, ''
  1247. );
  1248. if (!$this->_send_binary_packet($packet)) {
  1249. return false;
  1250. }
  1251. return $this->_keyboard_interactive_process($password);
  1252. }
  1253. /**
  1254. * Handle the keyboard-interactive requests / responses.
  1255. *
  1256. * @param String $responses...
  1257. * @return Boolean
  1258. * @access private
  1259. */
  1260. function _keyboard_interactive_process()
  1261. {
  1262. $responses = func_get_args();
  1263. $response = $this->_get_binary_packet();
  1264. if ($response === false) {
  1265. user_error('Connection closed by server', E_USER_NOTICE);
  1266. return false;
  1267. }
  1268. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1269. switch ($type) {
  1270. case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
  1271. // see http://tools.ietf.org/html/rfc4256#section-3.2
  1272. if (defined('NET_SSH2_LOGGING')) {
  1273. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  1274. 'UNKNOWN',
  1275. 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  1276. $this->message_number_log[count($this->message_number_log) - 1]
  1277. );
  1278. }
  1279. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1280. $this->_string_shift($response, $length); // name; may be empty
  1281. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1282. $this->_string_shift($response, $length); // instruction; may be empty
  1283. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1284. $this->_string_shift($response, $length); // language tag; may be empty
  1285. extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
  1286. /*
  1287. for ($i = 0; $i < $num_prompts; $i++) {
  1288. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1289. // prompt - ie. "Password: "; must not be empty
  1290. $this->_string_shift($response, $length);
  1291. $echo = $this->_string_shift($response) != chr(0);
  1292. }
  1293. */
  1294. /*
  1295. After obtaining the requested information from the user, the client
  1296. MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
  1297. */
  1298. // see http://tools.ietf.org/html/rfc4256#section-3.4
  1299. $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
  1300. for ($i = 0; $i < count($responses); $i++) {
  1301. $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
  1302. $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
  1303. }
  1304. if (!$this->_send_binary_packet($packet)) {
  1305. return false;
  1306. }
  1307. if (defined('NET_SSH2_LOGGING')) {
  1308. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  1309. 'UNKNOWN',
  1310. 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
  1311. $this->message_number_log[count($this->message_number_log) - 1]
  1312. );
  1313. $this->message_log[count($this->message_log) - 1] = $logged;
  1314. }
  1315. /*
  1316. After receiving the response, the server MUST send either an
  1317. SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
  1318. SSH_MSG_USERAUTH_INFO_REQUEST message.
  1319. */
  1320. // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
  1321. // there could be an infinite loop of request / responses.
  1322. return $this->_keyboard_interactive_process();
  1323. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1324. return true;
  1325. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1326. return false;
  1327. }
  1328. return false;
  1329. }
  1330. /**
  1331. * Login with an RSA private key
  1332. *
  1333. * @param String $username
  1334. * @param Crypt_RSA $password
  1335. * @return Boolean
  1336. * @access private
  1337. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1338. * by sending dummy SSH_MSG_IGNORE messages.
  1339. */
  1340. function _privatekey_login($username, $privatekey)
  1341. {
  1342. // see http://tools.ietf.org/html/rfc4253#page-15
  1343. $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  1344. if ($publickey === false) {
  1345. return false;
  1346. }
  1347. $publickey = array(
  1348. 'e' => $publickey['e']->toBytes(true),
  1349. 'n' => $publickey['n']->toBytes(true)
  1350. );
  1351. $publickey = pack('Na*Na*Na*',
  1352. strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
  1353. );
  1354. $part1 = pack('CNa*Na*Na*',
  1355. NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
  1356. strlen('publickey'), 'publickey'
  1357. );
  1358. $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
  1359. $packet = $part1 . chr(0) . $part2;
  1360. if (!$this->_send_binary_packet($packet)) {
  1361. return false;
  1362. }
  1363. $response = $this->_get_binary_packet();
  1364. if ($response === false) {
  1365. user_error('Connection closed by server', E_USER_NOTICE);
  1366. return false;
  1367. }
  1368. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1369. switch ($type) {
  1370. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1371. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1372. $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
  1373. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  1374. case NET_SSH2_MSG_USERAUTH_PK_OK:
  1375. // we'll just take it on faith that the public key blob and the public key algorithm name are as
  1376. // they should be
  1377. if (defined('NET_SSH2_LOGGING')) {
  1378. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  1379. 'UNKNOWN',
  1380. 'NET_SSH2_MSG_USERAUTH_PK_OK',
  1381. $this->message_number_log[count($this->message_number_log) - 1]
  1382. );
  1383. }
  1384. }
  1385. $packet = $part1 . chr(1) . $part2;
  1386. $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  1387. $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
  1388. $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
  1389. $packet.= pack('Na*', strlen($signature), $signature);
  1390. if (!$this->_send_binary_packet($packet)) {
  1391. return false;
  1392. }
  1393. $response = $this->_get_binary_packet();
  1394. if ($response === false) {
  1395. user_error('Connection closed by server', E_USER_NOTICE);
  1396. return false;
  1397. }
  1398. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1399. switch ($type) {
  1400. case NET_SSH2_MSG_USERAUTH_FAILURE:
  1401. // either the login is bad or the server employees multi-factor authentication
  1402. return false;
  1403. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  1404. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  1405. return true;
  1406. }
  1407. return false;
  1408. }
  1409. /**
  1410. * Execute Command
  1411. *
  1412. * If $block is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
  1413. * In all likelihood, this is not a feature you want to be taking advantage of.
  1414. *
  1415. * @param String $command
  1416. * @param optional Boolean $block
  1417. * @return String
  1418. * @access public
  1419. */
  1420. function exec($command, $block = true)
  1421. {
  1422. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1423. return false;
  1424. }
  1425. // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
  1426. // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
  1427. // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
  1428. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
  1429. $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF;
  1430. // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
  1431. // uses 0x4000, that's what will be used here, as well.
  1432. $packet_size = 0x4000;
  1433. $packet = pack('CNa*N3',
  1434. NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size);
  1435. if (!$this->_send_binary_packet($packet)) {
  1436. return false;
  1437. }
  1438. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
  1439. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  1440. if ($response === false) {
  1441. return false;
  1442. }
  1443. // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
  1444. // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
  1445. // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
  1446. // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
  1447. // neither will your script.
  1448. // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
  1449. // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
  1450. // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
  1451. $packet = pack('CNNa*CNa*',
  1452. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
  1453. if (!$this->_send_binary_packet($packet)) {
  1454. return false;
  1455. }
  1456. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
  1457. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  1458. if ($response === false) {
  1459. return false;
  1460. }
  1461. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
  1462. if (!$block) {
  1463. return true;
  1464. }
  1465. $output = '';
  1466. while (true) {
  1467. $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  1468. switch (true) {
  1469. case $temp === true:
  1470. return $output;
  1471. case $temp === false:
  1472. return false;
  1473. default:
  1474. $output.= $temp;
  1475. }
  1476. }
  1477. }
  1478. /**
  1479. * Creates an interactive shell
  1480. *
  1481. * @see Net_SSH2::read()
  1482. * @see Net_SSH2::write()
  1483. * @return Boolean
  1484. * @access private
  1485. */
  1486. function _initShell()
  1487. {
  1488. $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF;
  1489. $packet_size = 0x4000;
  1490. $packet = pack('CNa*N3',
  1491. NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size);
  1492. if (!$this->_send_binary_packet($packet)) {
  1493. return false;
  1494. }
  1495. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
  1496. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  1497. if ($response === false) {
  1498. return false;
  1499. }
  1500. $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  1501. $packet = pack('CNNa*CNa*N5a*',
  1502. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
  1503. 80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
  1504. if (!$this->_send_binary_packet($packet)) {
  1505. return false;
  1506. }
  1507. $response = $this->_get_binary_packet();
  1508. if ($response === false) {
  1509. user_error('Connection closed by server', E_USER_NOTICE);
  1510. return false;
  1511. }
  1512. list(, $type) = unpack('C', $this->_string_shift($response, 1));
  1513. switch ($type) {
  1514. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  1515. break;
  1516. case NET_SSH2_MSG_CHANNEL_FAILURE:
  1517. default:
  1518. user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
  1519. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1520. }
  1521. $packet = pack('CNNa*C',
  1522. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
  1523. if (!$this->_send_binary_packet($packet)) {
  1524. return false;
  1525. }
  1526. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  1527. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  1528. if ($response === false) {
  1529. return false;
  1530. }
  1531. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
  1532. $this->bitmap |= NET_SSH2_MASK_SHELL;
  1533. return true;
  1534. }
  1535. /**
  1536. * Returns the output of an interactive shell
  1537. *
  1538. * Returns when there's a match for $expect, which can take the form of a string literal or,
  1539. * if $mode == NET_SSH2_READ_REGEX, a regular expression.
  1540. *
  1541. * @see Net_SSH2::read()
  1542. * @param String $expect
  1543. * @param Integer $mode
  1544. * @return String
  1545. * @access public
  1546. */
  1547. function read($expect, $mode = NET_SSH2_READ_SIMPLE)
  1548. {
  1549. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1550. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  1551. return false;
  1552. }
  1553. if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  1554. user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
  1555. return false;
  1556. }
  1557. $match = $expect;
  1558. while (true) {
  1559. if ($mode == NET_SSH2_READ_REGEX) {
  1560. preg_match($expect, $this->interactiveBuffer, $matches);
  1561. $match = $matches[0];
  1562. }
  1563. $pos = strpos($this->interactiveBuffer, $match);
  1564. if ($pos !== false) {
  1565. return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
  1566. }
  1567. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  1568. $this->interactiveBuffer.= $response;
  1569. }
  1570. }
  1571. /**
  1572. * Inputs a command into an interactive shell.
  1573. *
  1574. * @see Net_SSH1::interactiveWrite()
  1575. * @param String $cmd
  1576. * @return Boolean
  1577. * @access public
  1578. */
  1579. function write($cmd)
  1580. {
  1581. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1582. user_error('Operation disallowed prior to login()', E_USER_NOTICE);
  1583. return false;
  1584. }
  1585. if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  1586. user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
  1587. return false;
  1588. }
  1589. return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd);
  1590. }
  1591. /**
  1592. * Disconnect
  1593. *
  1594. * @access public
  1595. */
  1596. function disconnect()
  1597. {
  1598. $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1599. }
  1600. /**
  1601. * Destructor.
  1602. *
  1603. * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
  1604. * disconnect().
  1605. *
  1606. * @access public
  1607. */
  1608. function __destruct()
  1609. {
  1610. $this->disconnect();
  1611. }
  1612. /**
  1613. * Gets Binary Packets
  1614. *
  1615. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  1616. *
  1617. * @see Net_SSH2::_send_binary_packet()
  1618. * @return String
  1619. * @access private
  1620. */
  1621. function _get_binary_packet()
  1622. {
  1623. if (feof($this->fsock)) {
  1624. user_error('Connection closed prematurely', E_USER_NOTICE);
  1625. return false;
  1626. }
  1627. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1628. $raw = fread($this->fsock, $this->decrypt_block_size);
  1629. $stop = strtok(microtime(), ' ') + strtok('');
  1630. if (empty($raw)) {
  1631. return '';
  1632. }
  1633. if ($this->decrypt !== false) {
  1634. $raw = $this->decrypt->decrypt($raw);
  1635. }
  1636. extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
  1637. $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
  1638. $buffer = '';
  1639. while ($remaining_length > 0) {
  1640. $temp = fread($this->fsock, $remaining_length);
  1641. $buffer.= $temp;
  1642. $remaining_length-= strlen($temp);
  1643. }
  1644. if (!empty($buffer)) {
  1645. $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
  1646. $buffer = $temp = '';
  1647. }
  1648. $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
  1649. $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
  1650. if ($this->hmac_check !== false) {
  1651. $hmac = fread($this->fsock, $this->hmac_size);
  1652. if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
  1653. user_error('Invalid HMAC', E_USER_NOTICE);
  1654. return false;
  1655. }
  1656. }
  1657. //if ($this->decompress) {
  1658. // $payload = gzinflate(substr($payload, 2));
  1659. //}
  1660. $this->get_seq_no++;
  1661. if (defined('NET_SSH2_LOGGING')) {
  1662. $temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
  1663. $this->message_number_log[] = '<- ' . $temp .
  1664. ' (' . round($stop - $start, 4) . 's)';
  1665. if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  1666. $this->message_log[] = substr($payload, 1);
  1667. }
  1668. }
  1669. return $this->_filter($payload);
  1670. }
  1671. /**
  1672. * Filter Binary Packets
  1673. *
  1674. * Because some binary packets need to be ignored...
  1675. *
  1676. * @see Net_SSH2::_get_binary_packet()
  1677. * @return String
  1678. * @access private
  1679. */
  1680. function _filter($payload)
  1681. {
  1682. switch (ord($payload[0])) {
  1683. case NET_SSH2_MSG_DISCONNECT:
  1684. $this->_string_shift($payload, 1);
  1685. extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
  1686. $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
  1687. $this->bitmask = 0;
  1688. return false;
  1689. case NET_SSH2_MSG_IGNORE:
  1690. $payload = $this->_get_binary_packet();
  1691. break;
  1692. case NET_SSH2_MSG_DEBUG:
  1693. $this->_string_shift($payload, 2);
  1694. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  1695. $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
  1696. $payload = $this->_get_binary_packet();
  1697. break;
  1698. case NET_SSH2_MSG_UNIMPLEMENTED:
  1699. return false;
  1700. case NET_SSH2_MSG_KEXINIT:
  1701. if ($this->session_id !== false) {
  1702. if (!$this->_key_exchange($payload)) {
  1703. $this->bitmask = 0;
  1704. return false;
  1705. }
  1706. $payload = $this->_get_binary_packet();
  1707. }
  1708. }
  1709. // 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
  1710. if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
  1711. $this->_string_shift($payload, 1);
  1712. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  1713. $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
  1714. $payload = $this->_get_binary_packet();
  1715. }
  1716. // only called when we've already logged in
  1717. if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1718. switch (ord($payload[0])) {
  1719. case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
  1720. $this->_string_shift($payload, 1);
  1721. extract(unpack('Nlength', $this->_string_shift($payload)));
  1722. $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
  1723. if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
  1724. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1725. }
  1726. $payload = $this->_get_binary_packet();
  1727. break;
  1728. case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
  1729. $this->_string_shift($payload, 1);
  1730. extract(unpack('N', $this->_string_shift($payload, 4)));
  1731. $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
  1732. $this->_string_shift($payload, 4); // skip over client channel
  1733. extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
  1734. $packet = pack('CN3a*Na*',
  1735. NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
  1736. if (!$this->_send_binary_packet($packet)) {
  1737. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1738. }
  1739. $payload = $this->_get_binary_packet();
  1740. break;
  1741. case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
  1742. $payload = $this->_get_binary_packet();
  1743. }
  1744. }
  1745. return $payload;
  1746. }
  1747. /**
  1748. * Gets channel data
  1749. *
  1750. * Returns the data as a string if it's available and false if not.
  1751. *
  1752. * @param $client_channel
  1753. * @return Mixed
  1754. * @access private
  1755. */
  1756. function _get_channel_packet($client_channel, $skip_extended = false)
  1757. {
  1758. if (!empty($this->channel_buffers[$client_channel])) {
  1759. return array_shift($this->channel_buffers[$client_channel]);
  1760. }
  1761. while (true) {
  1762. $response = $this->_get_binary_packet();
  1763. if ($response === false) {
  1764. user_error('Connection closed by server', E_USER_NOTICE);
  1765. return false;
  1766. }
  1767. if (empty($response)) {
  1768. return '';
  1769. }
  1770. extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
  1771. switch ($this->channel_status[$channel]) {
  1772. case NET_SSH2_MSG_CHANNEL_OPEN:
  1773. switch ($type) {
  1774. case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
  1775. extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
  1776. $this->server_channels[$client_channel] = $server_channel;
  1777. $this->_string_shift($response, 4); // skip over (server) window size
  1778. $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
  1779. $this->packet_size_client_to_server[$client_channel] = $temp['packet_size_client_to_server'];
  1780. return true;
  1781. //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
  1782. default:
  1783. user_error('Unable to open channel', E_USER_NOTICE);
  1784. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1785. }
  1786. break;
  1787. case NET_SSH2_MSG_CHANNEL_REQUEST:
  1788. switch ($type) {
  1789. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  1790. return true;
  1791. //case NET_SSH2_MSG_CHANNEL_FAILURE:
  1792. default:
  1793. user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
  1794. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1795. }
  1796. }
  1797. switch ($type) {
  1798. case NET_SSH2_MSG_CHANNEL_DATA:
  1799. /*
  1800. if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
  1801. // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
  1802. // this actually seems to make things twice as fast. more to the point, the message right after
  1803. // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
  1804. // in OpenSSH it slows things down but only by a couple thousandths of a second.
  1805. $this->_send_channel_packet($client_channel, chr(0));
  1806. }
  1807. */
  1808. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1809. $data = $this->_string_shift($response, $length);
  1810. if ($client_channel == $channel) {
  1811. return $data;
  1812. }
  1813. if (!isset($this->channel_buffers[$client_channel])) {
  1814. $this->channel_buffers[$client_channel] = array();
  1815. }
  1816. $this->channel_buffers[$client_channel][] = $data;
  1817. break;
  1818. case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  1819. if ($skip_extended) {
  1820. break;
  1821. }
  1822. /*
  1823. if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
  1824. $this->_send_channel_packet($client_channel, chr(0));
  1825. }
  1826. */
  1827. // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
  1828. extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
  1829. $data = $this->_string_shift($response, $length);
  1830. if ($client_channel == $channel) {
  1831. return $data;
  1832. }
  1833. if (!isset($this->channel_buffers[$client_channel])) {
  1834. $this->channel_buffers[$client_channel] = array();
  1835. }
  1836. $this->channel_buffers[$client_channel][] = $data;
  1837. break;
  1838. case NET_SSH2_MSG_CHANNEL_REQUEST:
  1839. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1840. $value = $this->_string_shift($response, $length);
  1841. switch ($value) {
  1842. case 'exit-signal':
  1843. $this->_string_shift($response, 1);
  1844. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1845. $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
  1846. $this->_string_shift($response, 1);
  1847. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1848. if ($length) {
  1849. $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
  1850. }
  1851. //case 'exit-status':
  1852. default:
  1853. // "Some systems may not implement signals, in which case they SHOULD ignore this message."
  1854. // -- http://tools.ietf.org/html/rfc4254#section-6.9
  1855. break;
  1856. }
  1857. break;
  1858. case NET_SSH2_MSG_CHANNEL_CLOSE:
  1859. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  1860. return true;
  1861. case NET_SSH2_MSG_CHANNEL_EOF:
  1862. break;
  1863. default:
  1864. user_error('Error reading channel data', E_USER_NOTICE);
  1865. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  1866. }
  1867. }
  1868. }
  1869. /**
  1870. * Sends Binary Packets
  1871. *
  1872. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  1873. *
  1874. * @param String $data
  1875. * @see Net_SSH2::_get_binary_packet()
  1876. * @return Boolean
  1877. * @access private
  1878. */
  1879. function _send_binary_packet($data)
  1880. {
  1881. if (feof($this->fsock)) {
  1882. user_error('Connection closed prematurely', E_USER_NOTICE);
  1883. return false;
  1884. }
  1885. //if ($this->compress) {
  1886. // // the -4 removes the checksum:
  1887. // // http://php.net/function.gzcompress#57710
  1888. // $data = substr(gzcompress($data), 0, -4);
  1889. //}
  1890. // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
  1891. $packet_length = strlen($data) + 9;
  1892. // round up to the nearest $this->encrypt_block_size
  1893. $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
  1894. // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
  1895. $padding_length = $packet_length - strlen($data) - 5;
  1896. $padding = '';
  1897. for ($i = 0; $i < $padding_length; $i++) {
  1898. $padding.= chr(crypt_random(0, 255));
  1899. }
  1900. // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
  1901. $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
  1902. $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
  1903. $this->send_seq_no++;
  1904. if ($this->encrypt !== false) {
  1905. $packet = $this->encrypt->encrypt($packet);
  1906. }
  1907. $packet.= $hmac;
  1908. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1909. $result = strlen($packet) == fputs($this->fsock, $packet);
  1910. $stop = strtok(microtime(), ' ') + strtok('');
  1911. if (defined('NET_SSH2_LOGGING')) {
  1912. $temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
  1913. $this->message_number_log[] = '-> ' . $temp .
  1914. ' (' . round($stop - $start, 4) . 's)';
  1915. if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  1916. $this->message_log[] = substr($data, 1);
  1917. }
  1918. }
  1919. return $result;
  1920. }
  1921. /**
  1922. * Sends channel data
  1923. *
  1924. * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
  1925. *
  1926. * @param Integer $client_channel
  1927. * @param String $data
  1928. * @return Boolean
  1929. * @access private
  1930. */
  1931. function _send_channel_packet($client_channel, $data)
  1932. {
  1933. while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
  1934. // resize the window, if appropriate
  1935. $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel];
  1936. if ($this->window_size_client_to_server[$client_channel] < 0) {
  1937. $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
  1938. if (!$this->_send_binary_packet($packet)) {
  1939. return false;
  1940. }
  1941. $this->window_size_client_to_server[$client_channel]+= $this->window_size;
  1942. }
  1943. $packet = pack('CN2a*',
  1944. NET_SSH2_MSG_CHANNEL_DATA,
  1945. $this->server_channels[$client_channel],
  1946. $this->packet_size_client_to_server[$client_channel],
  1947. $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
  1948. );
  1949. if (!$this->_send_binary_packet($packet)) {
  1950. return false;
  1951. }
  1952. }
  1953. // resize the window, if appropriate
  1954. $this->window_size_client_to_server[$client_channel]-= strlen($data);
  1955. if ($this->window_size_client_to_server[$client_channel] < 0) {
  1956. $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
  1957. if (!$this->_send_binary_packet($packet)) {
  1958. return false;
  1959. }
  1960. $this->window_size_client_to_server[$client_channel]+= $this->window_size;
  1961. }
  1962. return $this->_send_binary_packet(pack('CN2a*',
  1963. NET_SSH2_MSG_CHANNEL_DATA,
  1964. $this->server_channels[$client_channel],
  1965. strlen($data),
  1966. $data));
  1967. }
  1968. /**
  1969. * Closes and flushes a channel
  1970. *
  1971. * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
  1972. * and for SFTP channels are presumably closed when the client disconnects. This functions is intended
  1973. * for SCP more than anything.
  1974. *
  1975. * @param Integer $client_channel
  1976. * @return Boolean
  1977. * @access private
  1978. */
  1979. function _close_channel($client_channel)
  1980. {
  1981. // see http://tools.ietf.org/html/rfc4254#section-5.3
  1982. $packet = pack('CN',
  1983. NET_SSH2_MSG_CHANNEL_EOF,
  1984. $this->server_channels[$client_channel]);
  1985. if (!$this->_send_binary_packet($packet)) {
  1986. return false;
  1987. }
  1988. while ($this->_get_channel_packet($client_channel) !== true);
  1989. }
  1990. /**
  1991. * Disconnect
  1992. *
  1993. * @param Integer $reason
  1994. * @return Boolean
  1995. * @access private
  1996. */
  1997. function _disconnect($reason)
  1998. {
  1999. if ($this->bitmap) {
  2000. $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
  2001. $this->_send_binary_packet($data);
  2002. $this->bitmap = 0;
  2003. fclose($this->fsock);
  2004. return false;
  2005. }
  2006. }
  2007. /**
  2008. * String Shift
  2009. *
  2010. * Inspired by array_shift
  2011. *
  2012. * @param String $string
  2013. * @param optional Integer $index
  2014. * @return String
  2015. * @access private
  2016. */
  2017. function _string_shift(&$string, $index = 1)
  2018. {
  2019. $substr = substr($string, 0, $index);
  2020. $string = substr($string, $index);
  2021. return $substr;
  2022. }
  2023. /**
  2024. * Define Array
  2025. *
  2026. * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
  2027. * named constants from it, using the value as the name of the constant and the index as the value of the constant.
  2028. * If any of the constants that would be defined already exists, none of the constants will be defined.
  2029. *
  2030. * @param Array $array
  2031. * @access private
  2032. */
  2033. function _define_array()
  2034. {
  2035. $args = func_get_args();
  2036. foreach ($args as $arg) {
  2037. foreach ($arg as $key=>$value) {
  2038. if (!defined($value)) {
  2039. define($value, $key);
  2040. } else {
  2041. break 2;
  2042. }
  2043. }
  2044. }
  2045. }
  2046. /**
  2047. * Returns a log of the packets that have been sent and received.
  2048. *
  2049. * 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')
  2050. *
  2051. * @access public
  2052. * @return String or Array
  2053. */
  2054. function getLog()
  2055. {
  2056. if (!defined('NET_SSH2_LOGGING')) {
  2057. return false;
  2058. }
  2059. switch (NET_SSH2_LOGGING) {
  2060. case NET_SSH2_LOG_SIMPLE:
  2061. return $this->message_number_log;
  2062. break;
  2063. case NET_SSH2_LOG_COMPLEX:
  2064. return $this->_format_log($this->message_log, $this->message_number_log);
  2065. break;
  2066. default:
  2067. return false;
  2068. }
  2069. }
  2070. /**
  2071. * Formats a log for printing
  2072. *
  2073. * @param Array $message_log
  2074. * @param Array $message_number_log
  2075. * @access private
  2076. * @return String
  2077. */
  2078. function _format_log($message_log, $message_number_log)
  2079. {
  2080. static $boundary = ':', $long_width = 65, $short_width = 16;
  2081. $output = '';
  2082. for ($i = 0; $i < count($message_log); $i++) {
  2083. $output.= $message_number_log[$i] . "\r\n";
  2084. $current_log = $message_log[$i];
  2085. $j = 0;
  2086. do {
  2087. if (!empty($current_log)) {
  2088. $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
  2089. }
  2090. $fragment = $this->_string_shift($current_log, $short_width);
  2091. $hex = substr(
  2092. preg_replace(
  2093. '#(.)#es',
  2094. '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
  2095. $fragment),
  2096. strlen($boundary)
  2097. );
  2098. // replace non ASCII printable characters with dots
  2099. // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
  2100. // also replace < with a . since < messes up the output on web browsers
  2101. $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
  2102. $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
  2103. $j++;
  2104. } while (!empty($current_log));
  2105. $output.= "\r\n";
  2106. }
  2107. return $output;
  2108. }
  2109. /**
  2110. * Returns all errors
  2111. *
  2112. * @return String
  2113. * @access public
  2114. */
  2115. function getErrors()
  2116. {
  2117. return $this->errors;
  2118. }
  2119. /**
  2120. * Returns the last error
  2121. *
  2122. * @return String
  2123. * @access public
  2124. */
  2125. function getLastError()
  2126. {
  2127. return $this->errors[count($this->errors) - 1];
  2128. }
  2129. /**
  2130. * Return the server identification.
  2131. *
  2132. * @return String
  2133. * @access public
  2134. */
  2135. function getServerIdentification()
  2136. {
  2137. return $this->server_identifier;
  2138. }
  2139. /**
  2140. * Return a list of the key exchange algorithms the server supports.
  2141. *
  2142. * @return Array
  2143. * @access public
  2144. */
  2145. function getKexAlgorithms()
  2146. {
  2147. return $this->kex_algorithms;
  2148. }
  2149. /**
  2150. * Return a list of the host key (public key) algorithms the server supports.
  2151. *
  2152. * @return Array
  2153. * @access public
  2154. */
  2155. function getServerHostKeyAlgorithms()
  2156. {
  2157. return $this->server_host_key_algorithms;
  2158. }
  2159. /**
  2160. * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
  2161. *
  2162. * @return Array
  2163. * @access public
  2164. */
  2165. function getEncryptionAlgorithmsClient2Server()
  2166. {
  2167. return $this->encryption_algorithms_client_to_server;
  2168. }
  2169. /**
  2170. * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
  2171. *
  2172. * @return Array
  2173. * @access public
  2174. */
  2175. function getEncryptionAlgorithmsServer2Client()
  2176. {
  2177. return $this->encryption_algorithms_server_to_client;
  2178. }
  2179. /**
  2180. * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
  2181. *
  2182. * @return Array
  2183. * @access public
  2184. */
  2185. function getMACAlgorithmsClient2Server()
  2186. {
  2187. return $this->mac_algorithms_client_to_server;
  2188. }
  2189. /**
  2190. * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
  2191. *
  2192. * @return Array
  2193. * @access public
  2194. */
  2195. function getMACAlgorithmsServer2Client()
  2196. {
  2197. return $this->mac_algorithms_server_to_client;
  2198. }
  2199. /**
  2200. * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
  2201. *
  2202. * @return Array
  2203. * @access public
  2204. */
  2205. function getCompressionAlgorithmsClient2Server()
  2206. {
  2207. return $this->compression_algorithms_client_to_server;
  2208. }
  2209. /**
  2210. * Return a list of the compression algorithms the server supports, when sending stuff to the client.
  2211. *
  2212. * @return Array
  2213. * @access public
  2214. */
  2215. function getCompressionAlgorithmsServer2Client()
  2216. {
  2217. return $this->compression_algorithms_server_to_client;
  2218. }
  2219. /**
  2220. * Return a list of the languages the server supports, when sending stuff to the client.
  2221. *
  2222. * @return Array
  2223. * @access public
  2224. */
  2225. function getLanguagesServer2Client()
  2226. {
  2227. return $this->languages_server_to_client;
  2228. }
  2229. /**
  2230. * Return a list of the languages the server supports, when receiving stuff from the client.
  2231. *
  2232. * @return Array
  2233. * @access public
  2234. */
  2235. function getLanguagesClient2Server()
  2236. {
  2237. return $this->languages_client_to_server;
  2238. }
  2239. /**
  2240. * Returns the server public host key.
  2241. *
  2242. * Caching this the first time you connect to a server and checking the result on subsequent connections
  2243. * is recommended. Returns false if the server signature is not signed correctly with the public host key.
  2244. *
  2245. * @return Mixed
  2246. * @access public
  2247. */
  2248. function getServerPublicHostKey()
  2249. {
  2250. $signature = $this->signature;
  2251. $server_public_host_key = $this->server_public_host_key;
  2252. extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
  2253. $this->_string_shift($server_public_host_key, $length);
  2254. switch ($this->signature_format) {
  2255. case 'ssh-dss':
  2256. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2257. $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2258. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2259. $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2260. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2261. $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2262. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2263. $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2264. /* The value for 'dss_signature_blob' is encoded as a string containing
  2265. r, followed by s (which are 160-bit integers, without lengths or
  2266. padding, unsigned, and in network byte order). */
  2267. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  2268. if ($temp['length'] != 40) {
  2269. user_error('Invalid signature', E_USER_NOTICE);
  2270. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2271. }
  2272. $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
  2273. $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
  2274. if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
  2275. user_error('Invalid signature', E_USER_NOTICE);
  2276. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2277. }
  2278. $w = $s->modInverse($q);
  2279. $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
  2280. list(, $u1) = $u1->divide($q);
  2281. $u2 = $w->multiply($r);
  2282. list(, $u2) = $u2->divide($q);
  2283. $g = $g->modPow($u1, $p);
  2284. $y = $y->modPow($u2, $p);
  2285. $v = $g->multiply($y);
  2286. list(, $v) = $v->divide($p);
  2287. list(, $v) = $v->divide($q);
  2288. if (!$v->equals($r)) {
  2289. user_error('Bad server signature', E_USER_NOTICE);
  2290. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2291. }
  2292. break;
  2293. case 'ssh-rsa':
  2294. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2295. $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2296. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  2297. $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  2298. $nLength = $temp['length'];
  2299. /*
  2300. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  2301. $signature = $this->_string_shift($signature, $temp['length']);
  2302. if (!class_exists('Crypt_RSA')) {
  2303. require_once('Crypt/RSA.php');
  2304. }
  2305. $rsa = new Crypt_RSA();
  2306. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  2307. $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
  2308. if (!$rsa->verify($this->exchange_hash, $signature)) {
  2309. user_error('Bad server signature', E_USER_NOTICE);
  2310. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2311. }
  2312. */
  2313. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  2314. $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
  2315. // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
  2316. // following URL:
  2317. // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
  2318. // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
  2319. if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
  2320. user_error('Invalid signature', E_USER_NOTICE);
  2321. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  2322. }
  2323. $s = $s->modPow($e, $n);
  2324. $s = $s->toBytes();
  2325. $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
  2326. $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
  2327. if ($s != $h) {
  2328. user_error('Bad server signature', E_USER_NOTICE);
  2329. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  2330. }
  2331. }
  2332. return $this->server_public_host_key;
  2333. }
  2334. }