SSH2.php 119 KB

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