SSH2.php 133 KB

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