SSH2.php 168 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103
  1. <?php
  2. /**
  3. * Pure-PHP implementation of SSHv2.
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * Here are some examples of how to use this library:
  8. * <code>
  9. * <?php
  10. * include 'Net/SSH2.php';
  11. *
  12. * $ssh = new Net_SSH2('www.domain.tld');
  13. * if (!$ssh->login('username', 'password')) {
  14. * exit('Login Failed');
  15. * }
  16. *
  17. * echo $ssh->exec('pwd');
  18. * echo $ssh->exec('ls -la');
  19. * ?>
  20. * </code>
  21. *
  22. * <code>
  23. * <?php
  24. * include 'Crypt/RSA.php';
  25. * include 'Net/SSH2.php';
  26. *
  27. * $key = new Crypt_RSA();
  28. * //$key->setPassword('whatever');
  29. * $key->loadKey(file_get_contents('privatekey'));
  30. *
  31. * $ssh = new Net_SSH2('www.domain.tld');
  32. * if (!$ssh->login('username', $key)) {
  33. * exit('Login Failed');
  34. * }
  35. *
  36. * echo $ssh->read('username@username:~$');
  37. * $ssh->write("ls -la\n");
  38. * echo $ssh->read('username@username:~$');
  39. * ?>
  40. * </code>
  41. *
  42. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  43. * of this software and associated documentation files (the "Software"), to deal
  44. * in the Software without restriction, including without limitation the rights
  45. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  46. * copies of the Software, and to permit persons to whom the Software is
  47. * furnished to do so, subject to the following conditions:
  48. *
  49. * The above copyright notice and this permission notice shall be included in
  50. * all copies or substantial portions of the Software.
  51. *
  52. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  53. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  54. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  55. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  56. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  57. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  58. * THE SOFTWARE.
  59. *
  60. * @category Net
  61. * @package Net_SSH2
  62. * @author Jim Wigginton <terrafrost@php.net>
  63. * @copyright 2007 Jim Wigginton
  64. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  65. * @link http://phpseclib.sourceforge.net
  66. */
  67. /**#@+
  68. * Execution Bitmap Masks
  69. *
  70. * @see self::bitmap
  71. * @access private
  72. */
  73. define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
  74. define('NET_SSH2_MASK_CONNECTED', 0x00000002);
  75. define('NET_SSH2_MASK_LOGIN_REQ', 0x00000004);
  76. define('NET_SSH2_MASK_LOGIN', 0x00000008);
  77. define('NET_SSH2_MASK_SHELL', 0x00000010);
  78. define('NET_SSH2_MASK_WINDOW_ADJUST', 0x00000020);
  79. /**#@-*/
  80. /**#@+
  81. * Channel constants
  82. *
  83. * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
  84. * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
  85. * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
  86. * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
  87. * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
  88. * The 'recipient channel' is the channel number given in the original
  89. * open request, and 'sender channel' is the channel number allocated by
  90. * the other side.
  91. *
  92. * @see self::_send_channel_packet()
  93. * @see self::_get_channel_packet()
  94. * @access private
  95. */
  96. define('NET_SSH2_CHANNEL_EXEC', 1); // PuTTy uses 0x100
  97. define('NET_SSH2_CHANNEL_SHELL', 2);
  98. define('NET_SSH2_CHANNEL_SUBSYSTEM', 3);
  99. define('NET_SSH2_CHANNEL_AGENT_FORWARD', 4);
  100. define('NET_SSH2_CHANNEL_KEEP_ALIVE', 5);
  101. /**#@-*/
  102. /**#@+
  103. * @access public
  104. * @see self::getLog()
  105. */
  106. /**
  107. * Returns the message numbers
  108. */
  109. define('NET_SSH2_LOG_SIMPLE', 1);
  110. /**
  111. * Returns the message content
  112. */
  113. define('NET_SSH2_LOG_COMPLEX', 2);
  114. /**
  115. * Outputs the content real-time
  116. */
  117. define('NET_SSH2_LOG_REALTIME', 3);
  118. /**
  119. * Dumps the content real-time to a file
  120. */
  121. define('NET_SSH2_LOG_REALTIME_FILE', 4);
  122. /**
  123. * Make sure that the log never gets larger than this
  124. */
  125. define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024);
  126. /**#@-*/
  127. /**#@+
  128. * @access public
  129. * @see self::read()
  130. */
  131. /**
  132. * Returns when a string matching $expect exactly is found
  133. */
  134. define('NET_SSH2_READ_SIMPLE', 1);
  135. /**
  136. * Returns when a string matching the regular expression $expect is found
  137. */
  138. define('NET_SSH2_READ_REGEX', 2);
  139. /**
  140. * Returns whenever a data packet is received.
  141. *
  142. * Some data packets may only contain a single character so it may be necessary
  143. * to call read() multiple times when using this option
  144. */
  145. define('NET_SSH2_READ_NEXT', 3);
  146. /**#@-*/
  147. /**
  148. * Pure-PHP implementation of SSHv2.
  149. *
  150. * @package Net_SSH2
  151. * @author Jim Wigginton <terrafrost@php.net>
  152. * @access public
  153. */
  154. class Net_SSH2
  155. {
  156. /**
  157. * The SSH identifier
  158. *
  159. * @var string
  160. * @access private
  161. */
  162. var $identifier;
  163. /**
  164. * The Socket Object
  165. *
  166. * @var object
  167. * @access private
  168. */
  169. var $fsock;
  170. /**
  171. * Execution Bitmap
  172. *
  173. * The bits that are set represent functions that have been called already. This is used to determine
  174. * if a requisite function has been successfully executed. If not, an error should be thrown.
  175. *
  176. * @var int
  177. * @access private
  178. */
  179. var $bitmap = 0;
  180. /**
  181. * Error information
  182. *
  183. * @see self::getErrors()
  184. * @see self::getLastError()
  185. * @var string
  186. * @access private
  187. */
  188. var $errors = array();
  189. /**
  190. * Server Identifier
  191. *
  192. * @see self::getServerIdentification()
  193. * @var array|false
  194. * @access private
  195. */
  196. var $server_identifier = false;
  197. /**
  198. * Key Exchange Algorithms
  199. *
  200. * @see self::getKexAlgorithims()
  201. * @var array|false
  202. * @access private
  203. */
  204. var $kex_algorithms = false;
  205. /**
  206. * Key Exchange Algorithm
  207. *
  208. * @see self::getMethodsNegotiated()
  209. * @var string|false
  210. * @access private
  211. */
  212. var $kex_algorithm = false;
  213. /**
  214. * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  215. *
  216. * @see self::_key_exchange()
  217. * @var int
  218. * @access private
  219. */
  220. var $kex_dh_group_size_min = 1536;
  221. /**
  222. * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  223. *
  224. * @see self::_key_exchange()
  225. * @var int
  226. * @access private
  227. */
  228. var $kex_dh_group_size_preferred = 2048;
  229. /**
  230. * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
  231. *
  232. * @see self::_key_exchange()
  233. * @var int
  234. * @access private
  235. */
  236. var $kex_dh_group_size_max = 4096;
  237. /**
  238. * Server Host Key Algorithms
  239. *
  240. * @see self::getServerHostKeyAlgorithms()
  241. * @var array|false
  242. * @access private
  243. */
  244. var $server_host_key_algorithms = false;
  245. /**
  246. * Encryption Algorithms: Client to Server
  247. *
  248. * @see self::getEncryptionAlgorithmsClient2Server()
  249. * @var array|false
  250. * @access private
  251. */
  252. var $encryption_algorithms_client_to_server = false;
  253. /**
  254. * Encryption Algorithms: Server to Client
  255. *
  256. * @see self::getEncryptionAlgorithmsServer2Client()
  257. * @var array|false
  258. * @access private
  259. */
  260. var $encryption_algorithms_server_to_client = false;
  261. /**
  262. * MAC Algorithms: Client to Server
  263. *
  264. * @see self::getMACAlgorithmsClient2Server()
  265. * @var array|false
  266. * @access private
  267. */
  268. var $mac_algorithms_client_to_server = false;
  269. /**
  270. * MAC Algorithms: Server to Client
  271. *
  272. * @see self::getMACAlgorithmsServer2Client()
  273. * @var array|false
  274. * @access private
  275. */
  276. var $mac_algorithms_server_to_client = false;
  277. /**
  278. * Compression Algorithms: Client to Server
  279. *
  280. * @see self::getCompressionAlgorithmsClient2Server()
  281. * @var array|false
  282. * @access private
  283. */
  284. var $compression_algorithms_client_to_server = false;
  285. /**
  286. * Compression Algorithms: Server to Client
  287. *
  288. * @see self::getCompressionAlgorithmsServer2Client()
  289. * @var array|false
  290. * @access private
  291. */
  292. var $compression_algorithms_server_to_client = false;
  293. /**
  294. * Languages: Server to Client
  295. *
  296. * @see self::getLanguagesServer2Client()
  297. * @var array|false
  298. * @access private
  299. */
  300. var $languages_server_to_client = false;
  301. /**
  302. * Languages: Client to Server
  303. *
  304. * @see self::getLanguagesClient2Server()
  305. * @var array|false
  306. * @access private
  307. */
  308. var $languages_client_to_server = false;
  309. /**
  310. * Preferred Algorithms
  311. *
  312. * @see self::setPreferredAlgorithms()
  313. * @var array
  314. * @access private
  315. */
  316. var $preferred = array();
  317. /**
  318. * Block Size for Server to Client Encryption
  319. *
  320. * "Note that the length of the concatenation of 'packet_length',
  321. * 'padding_length', 'payload', and 'random padding' MUST be a multiple
  322. * of the cipher block size or 8, whichever is larger. This constraint
  323. * MUST be enforced, even when using stream ciphers."
  324. *
  325. * -- http://tools.ietf.org/html/rfc4253#section-6
  326. *
  327. * @see self::Net_SSH2()
  328. * @see self::_send_binary_packet()
  329. * @var int
  330. * @access private
  331. */
  332. var $encrypt_block_size = 8;
  333. /**
  334. * Block Size for Client to Server Encryption
  335. *
  336. * @see self::Net_SSH2()
  337. * @see self::_get_binary_packet()
  338. * @var int
  339. * @access private
  340. */
  341. var $decrypt_block_size = 8;
  342. /**
  343. * Server to Client Encryption Object
  344. *
  345. * @see self::_get_binary_packet()
  346. * @var object
  347. * @access private
  348. */
  349. var $decrypt = false;
  350. /**
  351. * Client to Server Encryption Object
  352. *
  353. * @see self::_send_binary_packet()
  354. * @var object
  355. * @access private
  356. */
  357. var $encrypt = false;
  358. /**
  359. * Client to Server HMAC Object
  360. *
  361. * @see self::_send_binary_packet()
  362. * @var object
  363. * @access private
  364. */
  365. var $hmac_create = false;
  366. /**
  367. * Server to Client HMAC Object
  368. *
  369. * @see self::_get_binary_packet()
  370. * @var object
  371. * @access private
  372. */
  373. var $hmac_check = false;
  374. /**
  375. * Size of server to client HMAC
  376. *
  377. * 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.
  378. * 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
  379. * append it.
  380. *
  381. * @see self::_get_binary_packet()
  382. * @var int
  383. * @access private
  384. */
  385. var $hmac_size = false;
  386. /**
  387. * Server Public Host Key
  388. *
  389. * @see self::getServerPublicHostKey()
  390. * @var string
  391. * @access private
  392. */
  393. var $server_public_host_key;
  394. /**
  395. * Session identifier
  396. *
  397. * "The exchange hash H from the first key exchange is additionally
  398. * used as the session identifier, which is a unique identifier for
  399. * this connection."
  400. *
  401. * -- http://tools.ietf.org/html/rfc4253#section-7.2
  402. *
  403. * @see self::_key_exchange()
  404. * @var string
  405. * @access private
  406. */
  407. var $session_id = false;
  408. /**
  409. * Exchange hash
  410. *
  411. * The current exchange hash
  412. *
  413. * @see self::_key_exchange()
  414. * @var string
  415. * @access private
  416. */
  417. var $exchange_hash = false;
  418. /**
  419. * Message Numbers
  420. *
  421. * @see self::Net_SSH2()
  422. * @var array
  423. * @access private
  424. */
  425. var $message_numbers = array();
  426. /**
  427. * Disconnection Message 'reason codes' defined in RFC4253
  428. *
  429. * @see self::Net_SSH2()
  430. * @var array
  431. * @access private
  432. */
  433. var $disconnect_reasons = array();
  434. /**
  435. * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
  436. *
  437. * @see self::Net_SSH2()
  438. * @var array
  439. * @access private
  440. */
  441. var $channel_open_failure_reasons = array();
  442. /**
  443. * Terminal Modes
  444. *
  445. * @link http://tools.ietf.org/html/rfc4254#section-8
  446. * @see self::Net_SSH2()
  447. * @var array
  448. * @access private
  449. */
  450. var $terminal_modes = array();
  451. /**
  452. * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
  453. *
  454. * @link http://tools.ietf.org/html/rfc4254#section-5.2
  455. * @see self::Net_SSH2()
  456. * @var array
  457. * @access private
  458. */
  459. var $channel_extended_data_type_codes = array();
  460. /**
  461. * Send Sequence Number
  462. *
  463. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  464. *
  465. * @see self::_send_binary_packet()
  466. * @var int
  467. * @access private
  468. */
  469. var $send_seq_no = 0;
  470. /**
  471. * Get Sequence Number
  472. *
  473. * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
  474. *
  475. * @see self::_get_binary_packet()
  476. * @var int
  477. * @access private
  478. */
  479. var $get_seq_no = 0;
  480. /**
  481. * Server Channels
  482. *
  483. * Maps client channels to server channels
  484. *
  485. * @see self::_get_channel_packet()
  486. * @see self::exec()
  487. * @var array
  488. * @access private
  489. */
  490. var $server_channels = array();
  491. /**
  492. * Channel Buffers
  493. *
  494. * If a client requests a packet from one channel but receives two packets from another those packets should
  495. * be placed in a buffer
  496. *
  497. * @see self::_get_channel_packet()
  498. * @see self::exec()
  499. * @var array
  500. * @access private
  501. */
  502. var $channel_buffers = array();
  503. /**
  504. * Channel Status
  505. *
  506. * Contains the type of the last sent message
  507. *
  508. * @see self::_get_channel_packet()
  509. * @var array
  510. * @access private
  511. */
  512. var $channel_status = array();
  513. /**
  514. * Packet Size
  515. *
  516. * Maximum packet size indexed by channel
  517. *
  518. * @see self::_send_channel_packet()
  519. * @var array
  520. * @access private
  521. */
  522. var $packet_size_client_to_server = array();
  523. /**
  524. * Message Number Log
  525. *
  526. * @see self::getLog()
  527. * @var array
  528. * @access private
  529. */
  530. var $message_number_log = array();
  531. /**
  532. * Message Log
  533. *
  534. * @see self::getLog()
  535. * @var array
  536. * @access private
  537. */
  538. var $message_log = array();
  539. /**
  540. * The Window Size
  541. *
  542. * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
  543. *
  544. * @var int
  545. * @see self::_send_channel_packet()
  546. * @see self::exec()
  547. * @access private
  548. */
  549. var $window_size = 0x7FFFFFFF;
  550. /**
  551. * What we resize the window to
  552. *
  553. * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes.
  554. * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so
  555. * we'll just do what PuTTY does
  556. *
  557. * @var int
  558. * @see self::_send_channel_packet()
  559. * @see self::exec()
  560. * @access private
  561. */
  562. var $window_resize = 0x40000000;
  563. /**
  564. * Window size, server to client
  565. *
  566. * Window size indexed by channel
  567. *
  568. * @see self::_send_channel_packet()
  569. * @var array
  570. * @access private
  571. */
  572. var $window_size_server_to_client = array();
  573. /**
  574. * Window size, client to server
  575. *
  576. * Window size indexed by channel
  577. *
  578. * @see self::_get_channel_packet()
  579. * @var array
  580. * @access private
  581. */
  582. var $window_size_client_to_server = array();
  583. /**
  584. * Server signature
  585. *
  586. * Verified against $this->session_id
  587. *
  588. * @see self::getServerPublicHostKey()
  589. * @var string
  590. * @access private
  591. */
  592. var $signature = '';
  593. /**
  594. * Server signature format
  595. *
  596. * ssh-rsa or ssh-dss.
  597. *
  598. * @see self::getServerPublicHostKey()
  599. * @var string
  600. * @access private
  601. */
  602. var $signature_format = '';
  603. /**
  604. * Interactive Buffer
  605. *
  606. * @see self::read()
  607. * @var array
  608. * @access private
  609. */
  610. var $interactiveBuffer = '';
  611. /**
  612. * Current log size
  613. *
  614. * Should never exceed NET_SSH2_LOG_MAX_SIZE
  615. *
  616. * @see self::_send_binary_packet()
  617. * @see self::_get_binary_packet()
  618. * @var int
  619. * @access private
  620. */
  621. var $log_size;
  622. /**
  623. * Timeout
  624. *
  625. * @see self::setTimeout()
  626. * @access private
  627. */
  628. var $timeout;
  629. /**
  630. * Current Timeout
  631. *
  632. * @see self::_get_channel_packet()
  633. * @access private
  634. */
  635. var $curTimeout;
  636. /**
  637. * Real-time log file pointer
  638. *
  639. * @see self::_append_log()
  640. * @var resource
  641. * @access private
  642. */
  643. var $realtime_log_file;
  644. /**
  645. * Real-time log file size
  646. *
  647. * @see self::_append_log()
  648. * @var int
  649. * @access private
  650. */
  651. var $realtime_log_size;
  652. /**
  653. * Has the signature been validated?
  654. *
  655. * @see self::getServerPublicHostKey()
  656. * @var bool
  657. * @access private
  658. */
  659. var $signature_validated = false;
  660. /**
  661. * Real-time log file wrap boolean
  662. *
  663. * @see self::_append_log()
  664. * @access private
  665. */
  666. var $realtime_log_wrap;
  667. /**
  668. * Flag to suppress stderr from output
  669. *
  670. * @see self::enableQuietMode()
  671. * @access private
  672. */
  673. var $quiet_mode = false;
  674. /**
  675. * Time of first network activity
  676. *
  677. * @var int
  678. * @access private
  679. */
  680. var $last_packet;
  681. /**
  682. * Exit status returned from ssh if any
  683. *
  684. * @var int
  685. * @access private
  686. */
  687. var $exit_status;
  688. /**
  689. * Flag to request a PTY when using exec()
  690. *
  691. * @var bool
  692. * @see self::enablePTY()
  693. * @access private
  694. */
  695. var $request_pty = false;
  696. /**
  697. * Flag set while exec() is running when using enablePTY()
  698. *
  699. * @var bool
  700. * @access private
  701. */
  702. var $in_request_pty_exec = false;
  703. /**
  704. * Flag set after startSubsystem() is called
  705. *
  706. * @var bool
  707. * @access private
  708. */
  709. var $in_subsystem;
  710. /**
  711. * Contents of stdError
  712. *
  713. * @var string
  714. * @access private
  715. */
  716. var $stdErrorLog;
  717. /**
  718. * The Last Interactive Response
  719. *
  720. * @see self::_keyboard_interactive_process()
  721. * @var string
  722. * @access private
  723. */
  724. var $last_interactive_response = '';
  725. /**
  726. * Keyboard Interactive Request / Responses
  727. *
  728. * @see self::_keyboard_interactive_process()
  729. * @var array
  730. * @access private
  731. */
  732. var $keyboard_requests_responses = array();
  733. /**
  734. * Banner Message
  735. *
  736. * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  737. * authentication may be relevant for getting legal protection."
  738. *
  739. * @see self::_filter()
  740. * @see self::getBannerMessage()
  741. * @var string
  742. * @access private
  743. */
  744. var $banner_message = '';
  745. /**
  746. * Did read() timeout or return normally?
  747. *
  748. * @see self::isTimeout()
  749. * @var bool
  750. * @access private
  751. */
  752. var $is_timeout = false;
  753. /**
  754. * Log Boundary
  755. *
  756. * @see self::_format_log()
  757. * @var string
  758. * @access private
  759. */
  760. var $log_boundary = ':';
  761. /**
  762. * Log Long Width
  763. *
  764. * @see self::_format_log()
  765. * @var int
  766. * @access private
  767. */
  768. var $log_long_width = 65;
  769. /**
  770. * Log Short Width
  771. *
  772. * @see self::_format_log()
  773. * @var int
  774. * @access private
  775. */
  776. var $log_short_width = 16;
  777. /**
  778. * Hostname
  779. *
  780. * @see self::Net_SSH2()
  781. * @see self::_connect()
  782. * @var string
  783. * @access private
  784. */
  785. var $host;
  786. /**
  787. * Port Number
  788. *
  789. * @see self::Net_SSH2()
  790. * @see self::_connect()
  791. * @var int
  792. * @access private
  793. */
  794. var $port;
  795. /**
  796. * Number of columns for terminal window size
  797. *
  798. * @see self::getWindowColumns()
  799. * @see self::setWindowColumns()
  800. * @see self::setWindowSize()
  801. * @var int
  802. * @access private
  803. */
  804. var $windowColumns = 80;
  805. /**
  806. * Number of columns for terminal window size
  807. *
  808. * @see self::getWindowRows()
  809. * @see self::setWindowRows()
  810. * @see self::setWindowSize()
  811. * @var int
  812. * @access private
  813. */
  814. var $windowRows = 24;
  815. /**
  816. * Crypto Engine
  817. *
  818. * @see self::setCryptoEngine()
  819. * @see self::_key_exchange()
  820. * @var int
  821. * @access private
  822. */
  823. var $crypto_engine = false;
  824. /**
  825. * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
  826. *
  827. * @var System_SSH_Agent
  828. * @access private
  829. */
  830. var $agent;
  831. /**
  832. * Send the identification string first?
  833. *
  834. * @var bool
  835. * @access private
  836. */
  837. var $send_id_string_first = true;
  838. /**
  839. * Send the key exchange initiation packet first?
  840. *
  841. * @var bool
  842. * @access private
  843. */
  844. var $send_kex_first = true;
  845. /**
  846. * Some versions of OpenSSH incorrectly calculate the key size
  847. *
  848. * @var bool
  849. * @access private
  850. */
  851. var $bad_key_size_fix = false;
  852. /**
  853. * Should we try to re-connect to re-establish keys?
  854. *
  855. * @var bool
  856. * @access private
  857. */
  858. var $retry_connect = false;
  859. /**
  860. * Binary Packet Buffer
  861. *
  862. * @var string|false
  863. * @access private
  864. */
  865. var $binary_packet_buffer = false;
  866. /**
  867. * Preferred Signature Format
  868. *
  869. * @var string|false
  870. * @access private
  871. */
  872. var $preferred_signature_format = false;
  873. /**
  874. * Authentication Credentials
  875. *
  876. * @var array
  877. * @access private
  878. */
  879. var $auth = array();
  880. /**
  881. * Default Constructor.
  882. *
  883. * $host can either be a string, representing the host, or a stream resource.
  884. *
  885. * @param mixed $host
  886. * @param int $port
  887. * @param int $timeout
  888. * @see self::login()
  889. * @return Net_SSH2
  890. * @access public
  891. */
  892. function __construct($host, $port = 22, $timeout = 10)
  893. {
  894. // Include Math_BigInteger
  895. // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
  896. if (!class_exists('Math_BigInteger')) {
  897. include_once dirname(__FILE__) . '/../Math/BigInteger.php';
  898. }
  899. if (!function_exists('crypt_random_string')) {
  900. include_once dirname(__FILE__) . '/../Crypt/Random.php';
  901. }
  902. if (!class_exists('Crypt_Hash')) {
  903. include_once dirname(__FILE__) . '/../Crypt/Hash.php';
  904. }
  905. // include Crypt_Base so constants can be defined for setCryptoEngine()
  906. if (!class_exists('Crypt_Base')) {
  907. include_once dirname(__FILE__) . '/../Crypt/Base.php';
  908. }
  909. $this->message_numbers = array(
  910. 1 => 'NET_SSH2_MSG_DISCONNECT',
  911. 2 => 'NET_SSH2_MSG_IGNORE',
  912. 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
  913. 4 => 'NET_SSH2_MSG_DEBUG',
  914. 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
  915. 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
  916. 20 => 'NET_SSH2_MSG_KEXINIT',
  917. 21 => 'NET_SSH2_MSG_NEWKEYS',
  918. 30 => 'NET_SSH2_MSG_KEXDH_INIT',
  919. 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
  920. 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
  921. 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
  922. 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
  923. 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
  924. 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
  925. 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
  926. 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
  927. 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
  928. 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
  929. 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
  930. 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
  931. 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
  932. 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
  933. 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
  934. 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
  935. 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
  936. 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
  937. 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
  938. );
  939. $this->disconnect_reasons = array(
  940. 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
  941. 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
  942. 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
  943. 4 => 'NET_SSH2_DISCONNECT_RESERVED',
  944. 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
  945. 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
  946. 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
  947. 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
  948. 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
  949. 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
  950. 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
  951. 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
  952. 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
  953. 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
  954. 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
  955. );
  956. $this->channel_open_failure_reasons = array(
  957. 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
  958. );
  959. $this->terminal_modes = array(
  960. 0 => 'NET_SSH2_TTY_OP_END'
  961. );
  962. $this->channel_extended_data_type_codes = array(
  963. 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
  964. );
  965. $this->_define_array(
  966. $this->message_numbers,
  967. $this->disconnect_reasons,
  968. $this->channel_open_failure_reasons,
  969. $this->terminal_modes,
  970. $this->channel_extended_data_type_codes,
  971. array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
  972. array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
  973. array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
  974. 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
  975. // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
  976. array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
  977. 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
  978. 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
  979. 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
  980. 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST')
  981. );
  982. if (is_resource($host)) {
  983. $this->fsock = $host;
  984. return;
  985. }
  986. if (is_string($host)) {
  987. $this->host = $host;
  988. $this->port = $port;
  989. $this->timeout = $timeout;
  990. }
  991. }
  992. /**
  993. * PHP4 compatible Default Constructor.
  994. *
  995. * @see self::__construct()
  996. * @param mixed $host
  997. * @param int $port
  998. * @param int $timeout
  999. * @access public
  1000. */
  1001. function Net_SSH2($host, $port = 22, $timeout = 10)
  1002. {
  1003. $this->__construct($host, $port, $timeout);
  1004. }
  1005. /**
  1006. * Set Crypto Engine Mode
  1007. *
  1008. * Possible $engine values:
  1009. * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
  1010. *
  1011. * @param int $engine
  1012. * @access public
  1013. */
  1014. function setCryptoEngine($engine)
  1015. {
  1016. $this->crypto_engine = $engine;
  1017. }
  1018. /**
  1019. * Send Identification String First
  1020. *
  1021. * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1022. * both sides MUST send an identification string". It does not say which side sends it first. In
  1023. * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1024. *
  1025. * @access public
  1026. */
  1027. function sendIdentificationStringFirst()
  1028. {
  1029. $this->send_id_string_first = true;
  1030. }
  1031. /**
  1032. * Send Identification String Last
  1033. *
  1034. * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
  1035. * both sides MUST send an identification string". It does not say which side sends it first. In
  1036. * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1037. *
  1038. * @access public
  1039. */
  1040. function sendIdentificationStringLast()
  1041. {
  1042. $this->send_id_string_first = false;
  1043. }
  1044. /**
  1045. * Send SSH_MSG_KEXINIT First
  1046. *
  1047. * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1048. * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1049. * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1050. *
  1051. * @access public
  1052. */
  1053. function sendKEXINITFirst()
  1054. {
  1055. $this->send_kex_first = true;
  1056. }
  1057. /**
  1058. * Send SSH_MSG_KEXINIT Last
  1059. *
  1060. * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
  1061. * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
  1062. * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
  1063. *
  1064. * @access public
  1065. */
  1066. function sendKEXINITLast()
  1067. {
  1068. $this->send_kex_first = false;
  1069. }
  1070. /**
  1071. * Connect to an SSHv2 server
  1072. *
  1073. * @return bool
  1074. * @access private
  1075. */
  1076. function _connect()
  1077. {
  1078. if ($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) {
  1079. return false;
  1080. }
  1081. $this->bitmap |= NET_SSH2_MASK_CONSTRUCTOR;
  1082. $this->curTimeout = $this->timeout;
  1083. $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5
  1084. if (!is_resource($this->fsock)) {
  1085. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  1086. // with stream_select a timeout of 0 means that no timeout takes place;
  1087. // with fsockopen a timeout of 0 means that you instantly timeout
  1088. // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
  1089. $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
  1090. if (!$this->fsock) {
  1091. $host = $this->host . ':' . $this->port;
  1092. user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
  1093. return false;
  1094. }
  1095. $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
  1096. if ($this->curTimeout) {
  1097. $this->curTimeout-= $elapsed;
  1098. if ($this->curTimeout < 0) {
  1099. $this->is_timeout = true;
  1100. return false;
  1101. }
  1102. }
  1103. }
  1104. $this->identifier = $this->_generate_identifier();
  1105. if ($this->send_id_string_first) {
  1106. fputs($this->fsock, $this->identifier . "\r\n");
  1107. }
  1108. /* According to the SSH2 specs,
  1109. "The server MAY send other lines of data before sending the version
  1110. string. Each line SHOULD be terminated by a Carriage Return and Line
  1111. Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
  1112. in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
  1113. MUST be able to process such lines." */
  1114. $temp = '';
  1115. $extra = '';
  1116. while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
  1117. if (substr($temp, -2) == "\r\n") {
  1118. $extra.= $temp;
  1119. $temp = '';
  1120. }
  1121. if ($this->curTimeout) {
  1122. if ($this->curTimeout < 0) {
  1123. $this->is_timeout = true;
  1124. return false;
  1125. }
  1126. $read = array($this->fsock);
  1127. $write = $except = null;
  1128. $start = strtok(microtime(), ' ') + strtok('');
  1129. $sec = floor($this->curTimeout);
  1130. $usec = 1000000 * ($this->curTimeout - $sec);
  1131. // on windows this returns a "Warning: Invalid CRT parameters detected" error
  1132. // the !count() is done as a workaround for <https://bugs.php.net/42682>
  1133. if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
  1134. $this->is_timeout = true;
  1135. return false;
  1136. }
  1137. $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
  1138. $this->curTimeout-= $elapsed;
  1139. }
  1140. $subtemp = fgets($this->fsock, 255);
  1141. if ($subtemp === '' || $subtemp === false) {
  1142. return false;
  1143. }
  1144. $temp.= $subtemp;
  1145. }
  1146. if (feof($this->fsock)) {
  1147. $this->bitmap = 0;
  1148. user_error('Connection closed by server');
  1149. return false;
  1150. }
  1151. if (defined('NET_SSH2_LOGGING')) {
  1152. $this->_append_log('<-', $extra . $temp);
  1153. $this->_append_log('->', $this->identifier . "\r\n");
  1154. }
  1155. $this->server_identifier = trim($temp, "\r\n");
  1156. if (strlen($extra)) {
  1157. $this->errors[] = $extra;
  1158. }
  1159. if (version_compare($matches[1], '1.99', '<')) {
  1160. user_error("Cannot connect to SSH $matches[1] servers");
  1161. return false;
  1162. }
  1163. if (!$this->send_id_string_first) {
  1164. fputs($this->fsock, $this->identifier . "\r\n");
  1165. }
  1166. if (!$this->send_kex_first) {
  1167. $response = $this->_get_binary_packet();
  1168. if ($response === false) {
  1169. $this->bitmap = 0;
  1170. user_error('Connection closed by server');
  1171. return false;
  1172. }
  1173. if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
  1174. user_error('Expected SSH_MSG_KEXINIT');
  1175. return false;
  1176. }
  1177. if (!$this->_key_exchange($response)) {
  1178. return false;
  1179. }
  1180. }
  1181. if ($this->send_kex_first && !$this->_key_exchange()) {
  1182. return false;
  1183. }
  1184. $this->bitmap|= NET_SSH2_MASK_CONNECTED;
  1185. return true;
  1186. }
  1187. /**
  1188. * Generates the SSH identifier
  1189. *
  1190. * You should overwrite this method in your own class if you want to use another identifier
  1191. *
  1192. * @access protected
  1193. * @return string
  1194. */
  1195. function _generate_identifier()
  1196. {
  1197. $identifier = 'SSH-2.0-phpseclib_1.0';
  1198. $ext = array();
  1199. if (extension_loaded('openssl')) {
  1200. $ext[] = 'openssl';
  1201. } elseif (extension_loaded('mcrypt')) {
  1202. $ext[] = 'mcrypt';
  1203. }
  1204. if (extension_loaded('gmp')) {
  1205. $ext[] = 'gmp';
  1206. } elseif (extension_loaded('bcmath')) {
  1207. $ext[] = 'bcmath';
  1208. }
  1209. if (!empty($ext)) {
  1210. $identifier .= ' (' . implode(', ', $ext) . ')';
  1211. }
  1212. return $identifier;
  1213. }
  1214. /**
  1215. * Key Exchange
  1216. *
  1217. * @param string $kexinit_payload_server optional
  1218. * @access private
  1219. */
  1220. function _key_exchange($kexinit_payload_server = false)
  1221. {
  1222. $preferred = $this->preferred;
  1223. $kex_algorithms = isset($preferred['kex']) ?
  1224. $preferred['kex'] :
  1225. $this->getSupportedKEXAlgorithms();
  1226. $server_host_key_algorithms = isset($preferred['hostkey']) ?
  1227. $preferred['hostkey'] :
  1228. $this->getSupportedHostKeyAlgorithms();
  1229. $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
  1230. $preferred['server_to_client']['crypt'] :
  1231. $this->getSupportedEncryptionAlgorithms();
  1232. $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
  1233. $preferred['client_to_server']['crypt'] :
  1234. $this->getSupportedEncryptionAlgorithms();
  1235. $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
  1236. $preferred['server_to_client']['mac'] :
  1237. $this->getSupportedMACAlgorithms();
  1238. $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
  1239. $preferred['client_to_server']['mac'] :
  1240. $this->getSupportedMACAlgorithms();
  1241. $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
  1242. $preferred['server_to_client']['comp'] :
  1243. $this->getSupportedCompressionAlgorithms();
  1244. $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
  1245. $preferred['client_to_server']['comp'] :
  1246. $this->getSupportedCompressionAlgorithms();
  1247. // some SSH servers have buggy implementations of some of the above algorithms
  1248. switch (true) {
  1249. case $this->server_identifier == 'SSH-2.0-SSHD':
  1250. case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
  1251. if (!isset($preferred['server_to_client']['mac'])) {
  1252. $s2c_mac_algorithms = array_values(array_diff(
  1253. $s2c_mac_algorithms,
  1254. array('hmac-sha1-96', 'hmac-md5-96')
  1255. ));
  1256. }
  1257. if (!isset($preferred['client_to_server']['mac'])) {
  1258. $c2s_mac_algorithms = array_values(array_diff(
  1259. $c2s_mac_algorithms,
  1260. array('hmac-sha1-96', 'hmac-md5-96')
  1261. ));
  1262. }
  1263. }
  1264. $str_kex_algorithms = implode(',', $kex_algorithms);
  1265. $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
  1266. $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
  1267. $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
  1268. $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
  1269. $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
  1270. $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
  1271. $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);
  1272. $client_cookie = crypt_random_string(16);
  1273. $kexinit_payload_client = pack(
  1274. 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
  1275. NET_SSH2_MSG_KEXINIT,
  1276. $client_cookie,
  1277. strlen($str_kex_algorithms),
  1278. $str_kex_algorithms,
  1279. strlen($str_server_host_key_algorithms),
  1280. $str_server_host_key_algorithms,
  1281. strlen($encryption_algorithms_client_to_server),
  1282. $encryption_algorithms_client_to_server,
  1283. strlen($encryption_algorithms_server_to_client),
  1284. $encryption_algorithms_server_to_client,
  1285. strlen($mac_algorithms_client_to_server),
  1286. $mac_algorithms_client_to_server,
  1287. strlen($mac_algorithms_server_to_client),
  1288. $mac_algorithms_server_to_client,
  1289. strlen($compression_algorithms_client_to_server),
  1290. $compression_algorithms_client_to_server,
  1291. strlen($compression_algorithms_server_to_client),
  1292. $compression_algorithms_server_to_client,
  1293. 0,
  1294. '',
  1295. 0,
  1296. '',
  1297. 0,
  1298. 0
  1299. );
  1300. if ($this->send_kex_first) {
  1301. if (!$this->_send_binary_packet($kexinit_payload_client)) {
  1302. return false;
  1303. }
  1304. $kexinit_payload_server = $this->_get_binary_packet();
  1305. if ($kexinit_payload_server === false) {
  1306. $this->bitmap = 0;
  1307. user_error('Connection closed by server');
  1308. return false;
  1309. }
  1310. if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
  1311. user_error('Expected SSH_MSG_KEXINIT');
  1312. return false;
  1313. }
  1314. }
  1315. $response = $kexinit_payload_server;
  1316. $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
  1317. $server_cookie = $this->_string_shift($response, 16);
  1318. if (strlen($response) < 4) {
  1319. return false;
  1320. }
  1321. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1322. $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  1323. if (strlen($response) < 4) {
  1324. return false;
  1325. }
  1326. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1327. $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
  1328. if (strlen($response) < 4) {
  1329. return false;
  1330. }
  1331. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1332. $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  1333. if (strlen($response) < 4) {
  1334. return false;
  1335. }
  1336. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1337. $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  1338. if (strlen($response) < 4) {
  1339. return false;
  1340. }
  1341. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1342. $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  1343. if (strlen($response) < 4) {
  1344. return false;
  1345. }
  1346. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1347. $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  1348. if (strlen($response) < 4) {
  1349. return false;
  1350. }
  1351. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1352. $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  1353. if (strlen($response) < 4) {
  1354. return false;
  1355. }
  1356. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1357. $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  1358. if (strlen($response) < 4) {
  1359. return false;
  1360. }
  1361. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1362. $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
  1363. if (strlen($response) < 4) {
  1364. return false;
  1365. }
  1366. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1367. $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
  1368. if (!strlen($response)) {
  1369. return false;
  1370. }
  1371. extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
  1372. $first_kex_packet_follows = $first_kex_packet_follows != 0;
  1373. if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
  1374. return false;
  1375. }
  1376. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
  1377. // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
  1378. // diffie-hellman key exchange as fast as possible
  1379. $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
  1380. $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
  1381. if ($decryptKeyLength === null) {
  1382. user_error('No compatible server to client encryption algorithms found');
  1383. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1384. }
  1385. $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
  1386. $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
  1387. if ($encryptKeyLength === null) {
  1388. user_error('No compatible client to server encryption algorithms found');
  1389. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1390. }
  1391. $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
  1392. // through diffie-hellman key exchange a symmetric key is obtained
  1393. $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
  1394. if ($kex_algorithm === false) {
  1395. user_error('No compatible key exchange algorithms found');
  1396. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1397. }
  1398. if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
  1399. $dh_group_sizes_packed = pack(
  1400. 'NNN',
  1401. $this->kex_dh_group_size_min,
  1402. $this->kex_dh_group_size_preferred,
  1403. $this->kex_dh_group_size_max
  1404. );
  1405. $packet = pack(
  1406. 'Ca*',
  1407. NET_SSH2_MSG_KEXDH_GEX_REQUEST,
  1408. $dh_group_sizes_packed
  1409. );
  1410. if (!$this->_send_binary_packet($packet)) {
  1411. return false;
  1412. }
  1413. $this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
  1414. $response = $this->_get_binary_packet();
  1415. if ($response === false) {
  1416. $this->bitmap = 0;
  1417. user_error('Connection closed by server');
  1418. return false;
  1419. }
  1420. if (!strlen($response)) {
  1421. return false;
  1422. }
  1423. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1424. if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
  1425. user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
  1426. return false;
  1427. }
  1428. $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
  1429. if (strlen($response) < 4) {
  1430. return false;
  1431. }
  1432. extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
  1433. $primeBytes = $this->_string_shift($response, $primeLength);
  1434. $prime = new Math_BigInteger($primeBytes, -256);
  1435. if (strlen($response) < 4) {
  1436. return false;
  1437. }
  1438. extract(unpack('NgLength', $this->_string_shift($response, 4)));
  1439. $gBytes = $this->_string_shift($response, $gLength);
  1440. $g = new Math_BigInteger($gBytes, -256);
  1441. $exchange_hash_rfc4419 = pack(
  1442. 'a*Na*Na*',
  1443. $dh_group_sizes_packed,
  1444. $primeLength,
  1445. $primeBytes,
  1446. $gLength,
  1447. $gBytes
  1448. );
  1449. $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT;
  1450. $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY;
  1451. } else {
  1452. switch ($kex_algorithm) {
  1453. // see http://tools.ietf.org/html/rfc2409#section-6.2 and
  1454. // http://tools.ietf.org/html/rfc2412, appendex E
  1455. case 'diffie-hellman-group1-sha1':
  1456. $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  1457. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  1458. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  1459. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
  1460. break;
  1461. // see http://tools.ietf.org/html/rfc3526#section-3
  1462. case 'diffie-hellman-group14-sha1':
  1463. $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
  1464. '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
  1465. '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
  1466. 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
  1467. '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
  1468. '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
  1469. 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
  1470. '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
  1471. break;
  1472. }
  1473. // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
  1474. // the generator field element is 2 (decimal) and the hash function is sha1.
  1475. $g = new Math_BigInteger(2);
  1476. $prime = new Math_BigInteger($prime, 16);
  1477. $exchange_hash_rfc4419 = '';
  1478. $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
  1479. $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
  1480. }
  1481. switch ($kex_algorithm) {
  1482. case 'diffie-hellman-group-exchange-sha256':
  1483. $kexHash = new Crypt_Hash('sha256');
  1484. break;
  1485. default:
  1486. $kexHash = new Crypt_Hash('sha1');
  1487. }
  1488. /* To increase the speed of the key exchange, both client and server may
  1489. reduce the size of their private exponents. It should be at least
  1490. twice as long as the key material that is generated from the shared
  1491. secret. For more details, see the paper by van Oorschot and Wiener
  1492. [VAN-OORSCHOT].
  1493. -- http://tools.ietf.org/html/rfc4419#section-6.2 */
  1494. $one = new Math_BigInteger(1);
  1495. $keyLength = min($keyLength, $kexHash->getLength());
  1496. $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
  1497. $max = $max->subtract($one);
  1498. $x = $one->random($one, $max);
  1499. $e = $g->modPow($x, $prime);
  1500. $eBytes = $e->toBytes(true);
  1501. $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
  1502. if (!$this->_send_binary_packet($data)) {
  1503. $this->bitmap = 0;
  1504. user_error('Connection closed by server');
  1505. return false;
  1506. }
  1507. if ($clientKexInitMessage == NET_SSH2_MSG_KEXDH_GEX_INIT) {
  1508. $this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
  1509. }
  1510. $response = $this->_get_binary_packet();
  1511. if ($response === false) {
  1512. $this->bitmap = 0;
  1513. user_error('Connection closed by server');
  1514. return false;
  1515. }
  1516. if (!strlen($response)) {
  1517. return false;
  1518. }
  1519. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1520. if ($type != $serverKexReplyMessage) {
  1521. $expected = $serverKexReplyMessage == NET_SSH2_MSG_KEXDH_GEX_REPLY ?
  1522. 'SSH_MSG_KEXDH_GEX_REPLY' :
  1523. 'SSH_MSG_KEXDH_REPLY';
  1524. user_error("Expected $expected");
  1525. return false;
  1526. }
  1527. if ($serverKexReplyMessage == NET_SSH2_MSG_KEXDH_GEX_REPLY) {
  1528. $this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
  1529. }
  1530. if (strlen($response) < 4) {
  1531. return false;
  1532. }
  1533. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1534. $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
  1535. if (strlen($server_public_host_key) < 4) {
  1536. return false;
  1537. }
  1538. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  1539. $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
  1540. if (strlen($response) < 4) {
  1541. return false;
  1542. }
  1543. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1544. $fBytes = $this->_string_shift($response, $temp['length']);
  1545. $f = new Math_BigInteger($fBytes, -256);
  1546. if (strlen($response) < 4) {
  1547. return false;
  1548. }
  1549. $temp = unpack('Nlength', $this->_string_shift($response, 4));
  1550. $this->signature = $this->_string_shift($response, $temp['length']);
  1551. if (strlen($this->signature) < 4) {
  1552. return false;
  1553. }
  1554. $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
  1555. $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
  1556. $key = $f->modPow($x, $prime);
  1557. $keyBytes = $key->toBytes(true);
  1558. $this->exchange_hash = pack(
  1559. 'Na*Na*Na*Na*Na*a*Na*Na*Na*',
  1560. strlen($this->identifier),
  1561. $this->identifier,
  1562. strlen($this->server_identifier),
  1563. $this->server_identifier,
  1564. strlen($kexinit_payload_client),
  1565. $kexinit_payload_client,
  1566. strlen($kexinit_payload_server),
  1567. $kexinit_payload_server,
  1568. strlen($this->server_public_host_key),
  1569. $this->server_public_host_key,
  1570. $exchange_hash_rfc4419,
  1571. strlen($eBytes),
  1572. $eBytes,
  1573. strlen($fBytes),
  1574. $fBytes,
  1575. strlen($keyBytes),
  1576. $keyBytes
  1577. );
  1578. $this->exchange_hash = $kexHash->hash($this->exchange_hash);
  1579. if ($this->session_id === false) {
  1580. $this->session_id = $this->exchange_hash;
  1581. }
  1582. $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
  1583. if ($server_host_key_algorithm === false) {
  1584. user_error('No compatible server host key algorithms found');
  1585. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1586. }
  1587. switch ($server_host_key_algorithm) {
  1588. case 'ssh-dss':
  1589. $expected_key_format = 'ssh-dss';
  1590. break;
  1591. //case 'rsa-sha2-256':
  1592. //case 'rsa-sha2-512':
  1593. //case 'ssh-rsa':
  1594. default:
  1595. $expected_key_format = 'ssh-rsa';
  1596. }
  1597. if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
  1598. switch (true) {
  1599. case $this->signature_format == $server_host_key_algorithm:
  1600. case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
  1601. case $this->signature_format != 'ssh-rsa':
  1602. user_error('Server Host Key Algorithm Mismatch');
  1603. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1604. }
  1605. }
  1606. $packet = pack(
  1607. 'C',
  1608. NET_SSH2_MSG_NEWKEYS
  1609. );
  1610. if (!$this->_send_binary_packet($packet)) {
  1611. return false;
  1612. }
  1613. $response = $this->_get_binary_packet();
  1614. if ($response === false) {
  1615. $this->bitmap = 0;
  1616. user_error('Connection closed by server');
  1617. return false;
  1618. }
  1619. if (!strlen($response)) {
  1620. return false;
  1621. }
  1622. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1623. if ($type != NET_SSH2_MSG_NEWKEYS) {
  1624. user_error('Expected SSH_MSG_NEWKEYS');
  1625. return false;
  1626. }
  1627. $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
  1628. $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
  1629. $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
  1630. if ($this->encrypt) {
  1631. if ($this->crypto_engine) {
  1632. $this->encrypt->setPreferredEngine($this->crypto_engine);
  1633. }
  1634. $this->encrypt->enableContinuousBuffer();
  1635. $this->encrypt->disablePadding();
  1636. if ($this->encrypt->getBlockLength()) {
  1637. $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3;
  1638. }
  1639. $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
  1640. while ($this->encrypt_block_size > strlen($iv)) {
  1641. $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
  1642. }
  1643. $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
  1644. $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
  1645. while ($encryptKeyLength > strlen($key)) {
  1646. $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  1647. }
  1648. $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
  1649. $this->encrypt->name = $decrypt;
  1650. }
  1651. if ($this->decrypt) {
  1652. if ($this->crypto_engine) {
  1653. $this->decrypt->setPreferredEngine($this->crypto_engine);
  1654. }
  1655. $this->decrypt->enableContinuousBuffer();
  1656. $this->decrypt->disablePadding();
  1657. if ($this->decrypt->getBlockLength()) {
  1658. $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3;
  1659. }
  1660. $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
  1661. while ($this->decrypt_block_size > strlen($iv)) {
  1662. $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
  1663. }
  1664. $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
  1665. $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
  1666. while ($decryptKeyLength > strlen($key)) {
  1667. $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  1668. }
  1669. $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
  1670. $this->decrypt->name = $decrypt;
  1671. }
  1672. /* The "arcfour128" algorithm is the RC4 cipher, as described in
  1673. [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
  1674. generated by the cipher MUST be discarded, and the first byte of the
  1675. first encrypted packet MUST be encrypted using the 1537th byte of
  1676. keystream.
  1677. -- http://tools.ietf.org/html/rfc4345#section-4 */
  1678. if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
  1679. $this->encrypt->encrypt(str_repeat("\0", 1536));
  1680. }
  1681. if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
  1682. $this->decrypt->decrypt(str_repeat("\0", 1536));
  1683. }
  1684. $mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
  1685. if ($mac_algorithm === false) {
  1686. user_error('No compatible client to server message authentication algorithms found');
  1687. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1688. }
  1689. $createKeyLength = 0; // ie. $mac_algorithm == 'none'
  1690. switch ($mac_algorithm) {
  1691. case 'hmac-sha2-256':
  1692. $this->hmac_create = new Crypt_Hash('sha256');
  1693. $createKeyLength = 32;
  1694. break;
  1695. case 'hmac-sha1':
  1696. $this->hmac_create = new Crypt_Hash('sha1');
  1697. $createKeyLength = 20;
  1698. break;
  1699. case 'hmac-sha1-96':
  1700. $this->hmac_create = new Crypt_Hash('sha1-96');
  1701. $createKeyLength = 20;
  1702. break;
  1703. case 'hmac-md5':
  1704. $this->hmac_create = new Crypt_Hash('md5');
  1705. $createKeyLength = 16;
  1706. break;
  1707. case 'hmac-md5-96':
  1708. $this->hmac_create = new Crypt_Hash('md5-96');
  1709. $createKeyLength = 16;
  1710. }
  1711. $this->hmac_create->name = $mac_algorithm;
  1712. $mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
  1713. if ($mac_algorithm === false) {
  1714. user_error('No compatible server to client message authentication algorithms found');
  1715. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1716. }
  1717. $checkKeyLength = 0;
  1718. $this->hmac_size = 0;
  1719. switch ($mac_algorithm) {
  1720. case 'hmac-sha2-256':
  1721. $this->hmac_check = new Crypt_Hash('sha256');
  1722. $checkKeyLength = 32;
  1723. $this->hmac_size = 32;
  1724. break;
  1725. case 'hmac-sha1':
  1726. $this->hmac_check = new Crypt_Hash('sha1');
  1727. $checkKeyLength = 20;
  1728. $this->hmac_size = 20;
  1729. break;
  1730. case 'hmac-sha1-96':
  1731. $this->hmac_check = new Crypt_Hash('sha1-96');
  1732. $checkKeyLength = 20;
  1733. $this->hmac_size = 12;
  1734. break;
  1735. case 'hmac-md5':
  1736. $this->hmac_check = new Crypt_Hash('md5');
  1737. $checkKeyLength = 16;
  1738. $this->hmac_size = 16;
  1739. break;
  1740. case 'hmac-md5-96':
  1741. $this->hmac_check = new Crypt_Hash('md5-96');
  1742. $checkKeyLength = 16;
  1743. $this->hmac_size = 12;
  1744. }
  1745. $this->hmac_check->name = $mac_algorithm;
  1746. $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
  1747. while ($createKeyLength > strlen($key)) {
  1748. $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  1749. }
  1750. $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
  1751. $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
  1752. while ($checkKeyLength > strlen($key)) {
  1753. $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
  1754. }
  1755. $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
  1756. $compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
  1757. if ($compression_algorithm === false) {
  1758. user_error('No compatible client to server compression algorithms found');
  1759. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1760. }
  1761. //$this->decompress = $compression_algorithm == 'zlib';
  1762. $compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
  1763. if ($compression_algorithm === false) {
  1764. user_error('No compatible server to client compression algorithms found');
  1765. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  1766. }
  1767. //$this->compress = $compression_algorithm == 'zlib';
  1768. return true;
  1769. }
  1770. /**
  1771. * Maps an encryption algorithm name to the number of key bytes.
  1772. *
  1773. * @param string $algorithm Name of the encryption algorithm
  1774. * @return int|null Number of bytes as an integer or null for unknown
  1775. * @access private
  1776. */
  1777. function _encryption_algorithm_to_key_size($algorithm)
  1778. {
  1779. if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
  1780. return 16;
  1781. }
  1782. switch ($algorithm) {
  1783. case 'none':
  1784. return 0;
  1785. case 'aes128-cbc':
  1786. case 'aes128-ctr':
  1787. case 'arcfour':
  1788. case 'arcfour128':
  1789. case 'blowfish-cbc':
  1790. case 'blowfish-ctr':
  1791. case 'twofish128-cbc':
  1792. case 'twofish128-ctr':
  1793. return 16;
  1794. case '3des-cbc':
  1795. case '3des-ctr':
  1796. case 'aes192-cbc':
  1797. case 'aes192-ctr':
  1798. case 'twofish192-cbc':
  1799. case 'twofish192-ctr':
  1800. return 24;
  1801. case 'aes256-cbc':
  1802. case 'aes256-ctr':
  1803. case 'arcfour256':
  1804. case 'twofish-cbc':
  1805. case 'twofish256-cbc':
  1806. case 'twofish256-ctr':
  1807. return 32;
  1808. }
  1809. return null;
  1810. }
  1811. /**
  1812. * Maps an encryption algorithm name to an instance of a subclass of
  1813. * \phpseclib\Crypt\Base.
  1814. *
  1815. * @param string $algorithm Name of the encryption algorithm
  1816. * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
  1817. * @access private
  1818. */
  1819. function _encryption_algorithm_to_crypt_instance($algorithm)
  1820. {
  1821. switch ($algorithm) {
  1822. case '3des-cbc':
  1823. if (!class_exists('Crypt_TripleDES')) {
  1824. include_once dirname(__FILE__) . '/../Crypt/TripleDES.php';
  1825. }
  1826. return new Crypt_TripleDES();
  1827. case '3des-ctr':
  1828. if (!class_exists('Crypt_TripleDES')) {
  1829. include_once dirname(__FILE__) . '/../Crypt/TripleDES.php';
  1830. }
  1831. return new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
  1832. case 'aes256-cbc':
  1833. case 'aes192-cbc':
  1834. case 'aes128-cbc':
  1835. if (!class_exists('Crypt_Rijndael')) {
  1836. include_once dirname(__FILE__) . '/../Crypt/Rijndael.php';
  1837. }
  1838. return new Crypt_Rijndael();
  1839. case 'aes256-ctr':
  1840. case 'aes192-ctr':
  1841. case 'aes128-ctr':
  1842. if (!class_exists('Crypt_Rijndael')) {
  1843. include_once dirname(__FILE__) . '/../Crypt/Rijndael.php';
  1844. }
  1845. return new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
  1846. case 'blowfish-cbc':
  1847. if (!class_exists('Crypt_Blowfish')) {
  1848. include_once dirname(__FILE__) . '/../Crypt/Blowfish.php';
  1849. }
  1850. return new Crypt_Blowfish();
  1851. case 'blowfish-ctr':
  1852. if (!class_exists('Crypt_Blowfish')) {
  1853. include_once dirname(__FILE__) . '/../Crypt/Blowfish.php';
  1854. }
  1855. return new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
  1856. case 'twofish128-cbc':
  1857. case 'twofish192-cbc':
  1858. case 'twofish256-cbc':
  1859. case 'twofish-cbc':
  1860. if (!class_exists('Crypt_Twofish')) {
  1861. include_once dirname(__FILE__) . '/../Crypt/Twofish.php';
  1862. }
  1863. return new Crypt_Twofish();
  1864. case 'twofish128-ctr':
  1865. case 'twofish192-ctr':
  1866. case 'twofish256-ctr':
  1867. if (!class_exists('Crypt_Twofish')) {
  1868. include_once dirname(__FILE__) . '/../Crypt/Twofish.php';
  1869. }
  1870. return new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
  1871. case 'arcfour':
  1872. case 'arcfour128':
  1873. case 'arcfour256':
  1874. if (!class_exists('Crypt_RC4')) {
  1875. include_once dirname(__FILE__) . '/../Crypt/RC4.php';
  1876. }
  1877. return new Crypt_RC4();
  1878. case 'none':
  1879. //return new Crypt_Null();
  1880. }
  1881. return null;
  1882. }
  1883. /**
  1884. * Tests whether or not proposed algorithm has a potential for issues
  1885. *
  1886. * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
  1887. * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
  1888. * @param string $algorithm Name of the encryption algorithm
  1889. * @return bool
  1890. * @access private
  1891. */
  1892. function _bad_algorithm_candidate($algorithm)
  1893. {
  1894. switch ($algorithm) {
  1895. case 'arcfour256':
  1896. case 'aes192-ctr':
  1897. case 'aes256-ctr':
  1898. return true;
  1899. }
  1900. return false;
  1901. }
  1902. /**
  1903. * Login
  1904. *
  1905. * The $password parameter can be a plaintext password, a Crypt_RSA object or an array
  1906. *
  1907. * @param string $username
  1908. * @param mixed $password
  1909. * @param mixed $...
  1910. * @return bool
  1911. * @see self::_login()
  1912. * @access public
  1913. */
  1914. function login($username)
  1915. {
  1916. $args = func_get_args();
  1917. $this->auth[] = $args;
  1918. // try logging with 'none' as an authentication method first since that's what
  1919. // PuTTY does
  1920. if ($this->_login($username)) {
  1921. return true;
  1922. }
  1923. if (count($args) == 1) {
  1924. return false;
  1925. }
  1926. return call_user_func_array(array(&$this, '_login'), $args);
  1927. }
  1928. /**
  1929. * Login Helper
  1930. *
  1931. * @param string $username
  1932. * @param mixed $password
  1933. * @param mixed $...
  1934. * @return bool
  1935. * @see self::_login_helper()
  1936. * @access private
  1937. */
  1938. function _login($username)
  1939. {
  1940. if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
  1941. if (!$this->_connect()) {
  1942. return false;
  1943. }
  1944. }
  1945. $args = array_slice(func_get_args(), 1);
  1946. if (empty($args)) {
  1947. return $this->_login_helper($username);
  1948. }
  1949. foreach ($args as $arg) {
  1950. if ($this->_login_helper($username, $arg)) {
  1951. return true;
  1952. }
  1953. }
  1954. return false;
  1955. }
  1956. /**
  1957. * Login Helper
  1958. *
  1959. * @param string $username
  1960. * @param string $password
  1961. * @return bool
  1962. * @access private
  1963. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  1964. * by sending dummy SSH_MSG_IGNORE messages.
  1965. */
  1966. function _login_helper($username, $password = null)
  1967. {
  1968. if (!($this->bitmap & NET_SSH2_MASK_CONNECTED)) {
  1969. return false;
  1970. }
  1971. if (!($this->bitmap & NET_SSH2_MASK_LOGIN_REQ)) {
  1972. $packet = pack(
  1973. 'CNa*',
  1974. NET_SSH2_MSG_SERVICE_REQUEST,
  1975. strlen('ssh-userauth'),
  1976. 'ssh-userauth'
  1977. );
  1978. if (!$this->_send_binary_packet($packet)) {
  1979. return false;
  1980. }
  1981. $response = $this->_get_binary_packet();
  1982. if ($response === false) {
  1983. if ($this->retry_connect) {
  1984. $this->retry_connect = false;
  1985. if (!$this->_connect()) {
  1986. return false;
  1987. }
  1988. return $this->_login_helper($username, $password);
  1989. }
  1990. $this->bitmap = 0;
  1991. user_error('Connection closed by server');
  1992. return false;
  1993. }
  1994. if (strlen($response) < 4) {
  1995. return false;
  1996. }
  1997. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  1998. if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
  1999. user_error('Expected SSH_MSG_SERVICE_ACCEPT');
  2000. return false;
  2001. }
  2002. $this->bitmap |= NET_SSH2_MASK_LOGIN_REQ;
  2003. }
  2004. if (strlen($this->last_interactive_response)) {
  2005. return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
  2006. }
  2007. // although PHP5's get_class() preserves the case, PHP4's does not
  2008. if (is_object($password)) {
  2009. switch (strtolower(get_class($password))) {
  2010. case 'crypt_rsa':
  2011. return $this->_privatekey_login($username, $password);
  2012. case 'system_ssh_agent':
  2013. return $this->_ssh_agent_login($username, $password);
  2014. }
  2015. }
  2016. if (is_array($password)) {
  2017. if ($this->_keyboard_interactive_login($username, $password)) {
  2018. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2019. return true;
  2020. }
  2021. return false;
  2022. }
  2023. if (!isset($password)) {
  2024. $packet = pack(
  2025. 'CNa*Na*Na*',
  2026. NET_SSH2_MSG_USERAUTH_REQUEST,
  2027. strlen($username),
  2028. $username,
  2029. strlen('ssh-connection'),
  2030. 'ssh-connection',
  2031. strlen('none'),
  2032. 'none'
  2033. );
  2034. if (!$this->_send_binary_packet($packet)) {
  2035. return false;
  2036. }
  2037. $response = $this->_get_binary_packet();
  2038. if ($response === false) {
  2039. $this->bitmap = 0;
  2040. user_error('Connection closed by server');
  2041. return false;
  2042. }
  2043. if (!strlen($response)) {
  2044. return false;
  2045. }
  2046. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  2047. switch ($type) {
  2048. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2049. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2050. return true;
  2051. //case NET_SSH2_MSG_USERAUTH_FAILURE:
  2052. default:
  2053. return false;
  2054. }
  2055. }
  2056. $packet = pack(
  2057. 'CNa*Na*Na*CNa*',
  2058. NET_SSH2_MSG_USERAUTH_REQUEST,
  2059. strlen($username),
  2060. $username,
  2061. strlen('ssh-connection'),
  2062. 'ssh-connection',
  2063. strlen('password'),
  2064. 'password',
  2065. 0,
  2066. strlen($password),
  2067. $password
  2068. );
  2069. // remove the username and password from the logged packet
  2070. if (!defined('NET_SSH2_LOGGING')) {
  2071. $logged = null;
  2072. } else {
  2073. $logged = pack(
  2074. 'CNa*Na*Na*CNa*',
  2075. NET_SSH2_MSG_USERAUTH_REQUEST,
  2076. strlen('username'),
  2077. 'username',
  2078. strlen('ssh-connection'),
  2079. 'ssh-connection',
  2080. strlen('password'),
  2081. 'password',
  2082. 0,
  2083. strlen('password'),
  2084. 'password'
  2085. );
  2086. }
  2087. if (!$this->_send_binary_packet($packet, $logged)) {
  2088. return false;
  2089. }
  2090. $response = $this->_get_binary_packet();
  2091. if ($response === false) {
  2092. $this->bitmap = 0;
  2093. user_error('Connection closed by server');
  2094. return false;
  2095. }
  2096. if (!strlen($response)) {
  2097. return false;
  2098. }
  2099. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  2100. switch ($type) {
  2101. case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
  2102. $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
  2103. if (strlen($response) < 4) {
  2104. return false;
  2105. }
  2106. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2107. $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
  2108. return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
  2109. case NET_SSH2_MSG_USERAUTH_FAILURE:
  2110. // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
  2111. // multi-factor authentication
  2112. if (strlen($response) < 4) {
  2113. return false;
  2114. }
  2115. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2116. $auth_methods = explode(',', $this->_string_shift($response, $length));
  2117. if (!strlen($response)) {
  2118. return false;
  2119. }
  2120. extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
  2121. $partial_success = $partial_success != 0;
  2122. if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
  2123. if ($this->_keyboard_interactive_login($username, $password)) {
  2124. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2125. return true;
  2126. }
  2127. return false;
  2128. }
  2129. return false;
  2130. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2131. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2132. return true;
  2133. }
  2134. return false;
  2135. }
  2136. /**
  2137. * Login via keyboard-interactive authentication
  2138. *
  2139. * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
  2140. *
  2141. * @param string $username
  2142. * @param string $password
  2143. * @return bool
  2144. * @access private
  2145. */
  2146. function _keyboard_interactive_login($username, $password)
  2147. {
  2148. $packet = pack(
  2149. 'CNa*Na*Na*Na*Na*',
  2150. NET_SSH2_MSG_USERAUTH_REQUEST,
  2151. strlen($username),
  2152. $username,
  2153. strlen('ssh-connection'),
  2154. 'ssh-connection',
  2155. strlen('keyboard-interactive'),
  2156. 'keyboard-interactive',
  2157. 0,
  2158. '',
  2159. 0,
  2160. ''
  2161. );
  2162. if (!$this->_send_binary_packet($packet)) {
  2163. return false;
  2164. }
  2165. return $this->_keyboard_interactive_process($password);
  2166. }
  2167. /**
  2168. * Handle the keyboard-interactive requests / responses.
  2169. *
  2170. * @param string $responses...
  2171. * @return bool
  2172. * @access private
  2173. */
  2174. function _keyboard_interactive_process()
  2175. {
  2176. $responses = func_get_args();
  2177. if (strlen($this->last_interactive_response)) {
  2178. $response = $this->last_interactive_response;
  2179. } else {
  2180. $orig = $response = $this->_get_binary_packet();
  2181. if ($response === false) {
  2182. $this->bitmap = 0;
  2183. user_error('Connection closed by server');
  2184. return false;
  2185. }
  2186. }
  2187. if (!strlen($response)) {
  2188. return false;
  2189. }
  2190. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  2191. switch ($type) {
  2192. case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
  2193. if (strlen($response) < 4) {
  2194. return false;
  2195. }
  2196. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2197. $this->_string_shift($response, $length); // name; may be empty
  2198. if (strlen($response) < 4) {
  2199. return false;
  2200. }
  2201. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2202. $this->_string_shift($response, $length); // instruction; may be empty
  2203. if (strlen($response) < 4) {
  2204. return false;
  2205. }
  2206. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2207. $this->_string_shift($response, $length); // language tag; may be empty
  2208. if (strlen($response) < 4) {
  2209. return false;
  2210. }
  2211. extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
  2212. for ($i = 0; $i < count($responses); $i++) {
  2213. if (is_array($responses[$i])) {
  2214. foreach ($responses[$i] as $key => $value) {
  2215. $this->keyboard_requests_responses[$key] = $value;
  2216. }
  2217. unset($responses[$i]);
  2218. }
  2219. }
  2220. $responses = array_values($responses);
  2221. if (isset($this->keyboard_requests_responses)) {
  2222. for ($i = 0; $i < $num_prompts; $i++) {
  2223. if (strlen($response) < 4) {
  2224. return false;
  2225. }
  2226. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2227. // prompt - ie. "Password: "; must not be empty
  2228. $prompt = $this->_string_shift($response, $length);
  2229. //$echo = $this->_string_shift($response) != chr(0);
  2230. foreach ($this->keyboard_requests_responses as $key => $value) {
  2231. if (substr($prompt, 0, strlen($key)) == $key) {
  2232. $responses[] = $value;
  2233. break;
  2234. }
  2235. }
  2236. }
  2237. }
  2238. // see http://tools.ietf.org/html/rfc4256#section-3.2
  2239. if (strlen($this->last_interactive_response)) {
  2240. $this->last_interactive_response = '';
  2241. } else {
  2242. $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
  2243. }
  2244. if (!count($responses) && $num_prompts) {
  2245. $this->last_interactive_response = $orig;
  2246. return false;
  2247. }
  2248. /*
  2249. After obtaining the requested information from the user, the client
  2250. MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
  2251. */
  2252. // see http://tools.ietf.org/html/rfc4256#section-3.4
  2253. $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
  2254. for ($i = 0; $i < count($responses); $i++) {
  2255. $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
  2256. $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
  2257. }
  2258. if (!$this->_send_binary_packet($packet, $logged)) {
  2259. return false;
  2260. }
  2261. $this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
  2262. /*
  2263. After receiving the response, the server MUST send either an
  2264. SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
  2265. SSH_MSG_USERAUTH_INFO_REQUEST message.
  2266. */
  2267. // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
  2268. // there could be an infinite loop of request / responses.
  2269. return $this->_keyboard_interactive_process();
  2270. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2271. return true;
  2272. case NET_SSH2_MSG_USERAUTH_FAILURE:
  2273. return false;
  2274. }
  2275. return false;
  2276. }
  2277. /**
  2278. * Login with an ssh-agent provided key
  2279. *
  2280. * @param string $username
  2281. * @param System_SSH_Agent $agent
  2282. * @return bool
  2283. * @access private
  2284. */
  2285. function _ssh_agent_login($username, $agent)
  2286. {
  2287. $this->agent = $agent;
  2288. $keys = $agent->requestIdentities();
  2289. foreach ($keys as $key) {
  2290. if ($this->_privatekey_login($username, $key)) {
  2291. return true;
  2292. }
  2293. }
  2294. return false;
  2295. }
  2296. /**
  2297. * Login with an RSA private key
  2298. *
  2299. * @param string $username
  2300. * @param Crypt_RSA $password
  2301. * @return bool
  2302. * @access private
  2303. * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
  2304. * by sending dummy SSH_MSG_IGNORE messages.
  2305. */
  2306. function _privatekey_login($username, $privatekey)
  2307. {
  2308. // see http://tools.ietf.org/html/rfc4253#page-15
  2309. $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
  2310. if ($publickey === false) {
  2311. return false;
  2312. }
  2313. $publickey = array(
  2314. 'e' => $publickey['e']->toBytes(true),
  2315. 'n' => $publickey['n']->toBytes(true)
  2316. );
  2317. $publickey = pack(
  2318. 'Na*Na*Na*',
  2319. strlen('ssh-rsa'),
  2320. 'ssh-rsa',
  2321. strlen($publickey['e']),
  2322. $publickey['e'],
  2323. strlen($publickey['n']),
  2324. $publickey['n']
  2325. );
  2326. switch ($this->signature_format) {
  2327. case 'rsa-sha2-512':
  2328. $hash = 'sha512';
  2329. $signatureType = 'rsa-sha2-512';
  2330. break;
  2331. case 'rsa-sha2-256':
  2332. $hash = 'sha256';
  2333. $signatureType = 'rsa-sha2-256';
  2334. break;
  2335. //case 'ssh-rsa':
  2336. default:
  2337. $hash = 'sha1';
  2338. $signatureType = 'ssh-rsa';
  2339. }
  2340. $part1 = pack(
  2341. 'CNa*Na*Na*',
  2342. NET_SSH2_MSG_USERAUTH_REQUEST,
  2343. strlen($username),
  2344. $username,
  2345. strlen('ssh-connection'),
  2346. 'ssh-connection',
  2347. strlen('publickey'),
  2348. 'publickey'
  2349. );
  2350. $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);
  2351. $packet = $part1 . chr(0) . $part2;
  2352. if (!$this->_send_binary_packet($packet)) {
  2353. return false;
  2354. }
  2355. $response = $this->_get_binary_packet();
  2356. if ($response === false) {
  2357. $this->bitmap = 0;
  2358. user_error('Connection closed by server');
  2359. return false;
  2360. }
  2361. if (!strlen($response)) {
  2362. return false;
  2363. }
  2364. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  2365. switch ($type) {
  2366. case NET_SSH2_MSG_USERAUTH_FAILURE:
  2367. if (strlen($response) < 4) {
  2368. return false;
  2369. }
  2370. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  2371. $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
  2372. return false;
  2373. case NET_SSH2_MSG_USERAUTH_PK_OK:
  2374. // we'll just take it on faith that the public key blob and the public key algorithm name are as
  2375. // they should be
  2376. $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
  2377. }
  2378. $packet = $part1 . chr(1) . $part2;
  2379. $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  2380. $privatekey->setHash($hash);
  2381. $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
  2382. $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
  2383. $packet.= pack('Na*', strlen($signature), $signature);
  2384. if (!$this->_send_binary_packet($packet)) {
  2385. return false;
  2386. }
  2387. $response = $this->_get_binary_packet();
  2388. if ($response === false) {
  2389. $this->bitmap = 0;
  2390. user_error('Connection closed by server');
  2391. return false;
  2392. }
  2393. if (!strlen($response)) {
  2394. return false;
  2395. }
  2396. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  2397. switch ($type) {
  2398. case NET_SSH2_MSG_USERAUTH_FAILURE:
  2399. // either the login is bad or the server employs multi-factor authentication
  2400. return false;
  2401. case NET_SSH2_MSG_USERAUTH_SUCCESS:
  2402. $this->bitmap |= NET_SSH2_MASK_LOGIN;
  2403. return true;
  2404. }
  2405. return false;
  2406. }
  2407. /**
  2408. * Set Timeout
  2409. *
  2410. * $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.
  2411. * Setting $timeout to false or 0 will mean there is no timeout.
  2412. *
  2413. * @param mixed $timeout
  2414. * @access public
  2415. */
  2416. function setTimeout($timeout)
  2417. {
  2418. $this->timeout = $this->curTimeout = $timeout;
  2419. }
  2420. /**
  2421. * Get the output from stdError
  2422. *
  2423. * @access public
  2424. */
  2425. function getStdError()
  2426. {
  2427. return $this->stdErrorLog;
  2428. }
  2429. /**
  2430. * Execute Command
  2431. *
  2432. * If $callback is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
  2433. * In all likelihood, this is not a feature you want to be taking advantage of.
  2434. *
  2435. * @param string $command
  2436. * @param Callback $callback
  2437. * @return string
  2438. * @access public
  2439. */
  2440. function exec($command, $callback = null)
  2441. {
  2442. $this->curTimeout = $this->timeout;
  2443. $this->is_timeout = false;
  2444. $this->stdErrorLog = '';
  2445. if (!$this->isAuthenticated()) {
  2446. return false;
  2447. }
  2448. if ($this->in_request_pty_exec) {
  2449. user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
  2450. return false;
  2451. }
  2452. // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
  2453. // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
  2454. // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
  2455. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
  2456. $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC] = $this->window_size;
  2457. // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
  2458. // uses 0x4000, that's what will be used here, as well.
  2459. $packet_size = 0x4000;
  2460. $packet = pack(
  2461. 'CNa*N3',
  2462. NET_SSH2_MSG_CHANNEL_OPEN,
  2463. strlen('session'),
  2464. 'session',
  2465. NET_SSH2_CHANNEL_EXEC,
  2466. $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC],
  2467. $packet_size
  2468. );
  2469. if (!$this->_send_binary_packet($packet)) {
  2470. return false;
  2471. }
  2472. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
  2473. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  2474. if ($response === false) {
  2475. return false;
  2476. }
  2477. if ($this->request_pty === true) {
  2478. $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  2479. $packet = pack(
  2480. 'CNNa*CNa*N5a*',
  2481. NET_SSH2_MSG_CHANNEL_REQUEST,
  2482. $this->server_channels[NET_SSH2_CHANNEL_EXEC],
  2483. strlen('pty-req'),
  2484. 'pty-req',
  2485. 1,
  2486. strlen('vt100'),
  2487. 'vt100',
  2488. $this->windowColumns,
  2489. $this->windowRows,
  2490. 0,
  2491. 0,
  2492. strlen($terminal_modes),
  2493. $terminal_modes
  2494. );
  2495. if (!$this->_send_binary_packet($packet)) {
  2496. return false;
  2497. }
  2498. $response = $this->_get_binary_packet();
  2499. if ($response === false) {
  2500. $this->bitmap = 0;
  2501. user_error('Connection closed by server');
  2502. return false;
  2503. }
  2504. if (!strlen($response)) {
  2505. return false;
  2506. }
  2507. list(, $type) = unpack('C', $this->_string_shift($response, 1));
  2508. switch ($type) {
  2509. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  2510. break;
  2511. case NET_SSH2_MSG_CHANNEL_FAILURE:
  2512. default:
  2513. user_error('Unable to request pseudo-terminal');
  2514. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2515. }
  2516. $this->in_request_pty_exec = true;
  2517. }
  2518. // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
  2519. // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
  2520. // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
  2521. // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
  2522. // neither will your script.
  2523. // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
  2524. // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
  2525. // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
  2526. $packet = pack(
  2527. 'CNNa*CNa*',
  2528. NET_SSH2_MSG_CHANNEL_REQUEST,
  2529. $this->server_channels[NET_SSH2_CHANNEL_EXEC],
  2530. strlen('exec'),
  2531. 'exec',
  2532. 1,
  2533. strlen($command),
  2534. $command
  2535. );
  2536. if (!$this->_send_binary_packet($packet)) {
  2537. return false;
  2538. }
  2539. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2540. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  2541. if ($response === false) {
  2542. return false;
  2543. }
  2544. $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
  2545. if ($callback === false || $this->in_request_pty_exec) {
  2546. return true;
  2547. }
  2548. $output = '';
  2549. while (true) {
  2550. $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
  2551. switch (true) {
  2552. case $temp === true:
  2553. return is_callable($callback) ? true : $output;
  2554. case $temp === false:
  2555. return false;
  2556. default:
  2557. if (is_callable($callback)) {
  2558. if (call_user_func($callback, $temp) === true) {
  2559. $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
  2560. return true;
  2561. }
  2562. } else {
  2563. $output.= $temp;
  2564. }
  2565. }
  2566. }
  2567. }
  2568. /**
  2569. * Creates an interactive shell
  2570. *
  2571. * @see self::read()
  2572. * @see self::write()
  2573. * @return bool
  2574. * @access private
  2575. */
  2576. function _initShell()
  2577. {
  2578. if ($this->in_request_pty_exec === true) {
  2579. return true;
  2580. }
  2581. $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL] = $this->window_size;
  2582. $packet_size = 0x4000;
  2583. $packet = pack(
  2584. 'CNa*N3',
  2585. NET_SSH2_MSG_CHANNEL_OPEN,
  2586. strlen('session'),
  2587. 'session',
  2588. NET_SSH2_CHANNEL_SHELL,
  2589. $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL],
  2590. $packet_size
  2591. );
  2592. if (!$this->_send_binary_packet($packet)) {
  2593. return false;
  2594. }
  2595. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
  2596. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  2597. if ($response === false) {
  2598. return false;
  2599. }
  2600. $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
  2601. $packet = pack(
  2602. 'CNNa*CNa*N5a*',
  2603. NET_SSH2_MSG_CHANNEL_REQUEST,
  2604. $this->server_channels[NET_SSH2_CHANNEL_SHELL],
  2605. strlen('pty-req'),
  2606. 'pty-req',
  2607. 1,
  2608. strlen('vt100'),
  2609. 'vt100',
  2610. $this->windowColumns,
  2611. $this->windowRows,
  2612. 0,
  2613. 0,
  2614. strlen($terminal_modes),
  2615. $terminal_modes
  2616. );
  2617. if (!$this->_send_binary_packet($packet)) {
  2618. return false;
  2619. }
  2620. $response = $this->_get_binary_packet();
  2621. if ($response === false) {
  2622. $this->bitmap = 0;
  2623. user_error('Connection closed by server');
  2624. return false;
  2625. }
  2626. if (!strlen($response)) {
  2627. return false;
  2628. }
  2629. list(, $type) = unpack('C', $this->_string_shift($response, 1));
  2630. switch ($type) {
  2631. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  2632. // if a pty can't be opened maybe commands can still be executed
  2633. case NET_SSH2_MSG_CHANNEL_FAILURE:
  2634. break;
  2635. default:
  2636. user_error('Unable to request pseudo-terminal');
  2637. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2638. }
  2639. $packet = pack(
  2640. 'CNNa*C',
  2641. NET_SSH2_MSG_CHANNEL_REQUEST,
  2642. $this->server_channels[NET_SSH2_CHANNEL_SHELL],
  2643. strlen('shell'),
  2644. 'shell',
  2645. 1
  2646. );
  2647. if (!$this->_send_binary_packet($packet)) {
  2648. return false;
  2649. }
  2650. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2651. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
  2652. if ($response === false) {
  2653. return false;
  2654. }
  2655. $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
  2656. $this->bitmap |= NET_SSH2_MASK_SHELL;
  2657. return true;
  2658. }
  2659. /**
  2660. * Return the channel to be used with read() / write()
  2661. *
  2662. * @see self::read()
  2663. * @see self::write()
  2664. * @return int
  2665. * @access public
  2666. */
  2667. function _get_interactive_channel()
  2668. {
  2669. switch (true) {
  2670. case $this->in_subsystem:
  2671. return NET_SSH2_CHANNEL_SUBSYSTEM;
  2672. case $this->in_request_pty_exec:
  2673. return NET_SSH2_CHANNEL_EXEC;
  2674. default:
  2675. return NET_SSH2_CHANNEL_SHELL;
  2676. }
  2677. }
  2678. /**
  2679. * Return an available open channel
  2680. *
  2681. * @return int
  2682. * @access public
  2683. */
  2684. function _get_open_channel()
  2685. {
  2686. $channel = NET_SSH2_CHANNEL_EXEC;
  2687. do {
  2688. if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
  2689. return $channel;
  2690. }
  2691. } while ($channel++ < NET_SSH2_CHANNEL_SUBSYSTEM);
  2692. return false;
  2693. }
  2694. /**
  2695. * Returns the output of an interactive shell
  2696. *
  2697. * Returns when there's a match for $expect, which can take the form of a string literal or,
  2698. * if $mode == NET_SSH2_READ_REGEX, a regular expression.
  2699. *
  2700. * @see self::write()
  2701. * @param string $expect
  2702. * @param int $mode
  2703. * @return string|bool
  2704. * @access public
  2705. */
  2706. function read($expect = '', $mode = NET_SSH2_READ_SIMPLE)
  2707. {
  2708. $this->curTimeout = $this->timeout;
  2709. $this->is_timeout = false;
  2710. if (!$this->isAuthenticated()) {
  2711. user_error('Operation disallowed prior to login()');
  2712. return false;
  2713. }
  2714. if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  2715. user_error('Unable to initiate an interactive shell session');
  2716. return false;
  2717. }
  2718. $channel = $this->_get_interactive_channel();
  2719. if ($mode == NET_SSH2_READ_NEXT) {
  2720. return $this->_get_channel_packet($channel);
  2721. }
  2722. $match = $expect;
  2723. while (true) {
  2724. if ($mode == NET_SSH2_READ_REGEX) {
  2725. preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
  2726. $match = isset($matches[0]) ? $matches[0] : '';
  2727. }
  2728. $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
  2729. if ($pos !== false) {
  2730. return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
  2731. }
  2732. $response = $this->_get_channel_packet($channel);
  2733. if (is_bool($response)) {
  2734. $this->in_request_pty_exec = false;
  2735. return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
  2736. }
  2737. $this->interactiveBuffer.= $response;
  2738. }
  2739. }
  2740. /**
  2741. * Inputs a command into an interactive shell.
  2742. *
  2743. * @see self::read()
  2744. * @param string $cmd
  2745. * @return bool
  2746. * @access public
  2747. */
  2748. function write($cmd)
  2749. {
  2750. if (!$this->isAuthenticated()) {
  2751. user_error('Operation disallowed prior to login()');
  2752. return false;
  2753. }
  2754. if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
  2755. user_error('Unable to initiate an interactive shell session');
  2756. return false;
  2757. }
  2758. return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
  2759. }
  2760. /**
  2761. * Start a subsystem.
  2762. *
  2763. * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
  2764. * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
  2765. * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
  2766. * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
  2767. * if there's sufficient demand for such a feature.
  2768. *
  2769. * @see self::stopSubsystem()
  2770. * @param string $subsystem
  2771. * @return bool
  2772. * @access public
  2773. */
  2774. function startSubsystem($subsystem)
  2775. {
  2776. $this->window_size_server_to_client[NET_SSH2_CHANNEL_SUBSYSTEM] = $this->window_size;
  2777. $packet = pack(
  2778. 'CNa*N3',
  2779. NET_SSH2_MSG_CHANNEL_OPEN,
  2780. strlen('session'),
  2781. 'session',
  2782. NET_SSH2_CHANNEL_SUBSYSTEM,
  2783. $this->window_size,
  2784. 0x4000
  2785. );
  2786. if (!$this->_send_binary_packet($packet)) {
  2787. return false;
  2788. }
  2789. $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
  2790. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
  2791. if ($response === false) {
  2792. return false;
  2793. }
  2794. $packet = pack(
  2795. 'CNNa*CNa*',
  2796. NET_SSH2_MSG_CHANNEL_REQUEST,
  2797. $this->server_channels[NET_SSH2_CHANNEL_SUBSYSTEM],
  2798. strlen('subsystem'),
  2799. 'subsystem',
  2800. 1,
  2801. strlen($subsystem),
  2802. $subsystem
  2803. );
  2804. if (!$this->_send_binary_packet($packet)) {
  2805. return false;
  2806. }
  2807. $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
  2808. $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
  2809. if ($response === false) {
  2810. return false;
  2811. }
  2812. $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
  2813. $this->bitmap |= NET_SSH2_MASK_SHELL;
  2814. $this->in_subsystem = true;
  2815. return true;
  2816. }
  2817. /**
  2818. * Stops a subsystem.
  2819. *
  2820. * @see self::startSubsystem()
  2821. * @return bool
  2822. * @access public
  2823. */
  2824. function stopSubsystem()
  2825. {
  2826. $this->in_subsystem = false;
  2827. $this->_close_channel(NET_SSH2_CHANNEL_SUBSYSTEM);
  2828. return true;
  2829. }
  2830. /**
  2831. * Closes a channel
  2832. *
  2833. * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
  2834. *
  2835. * @access public
  2836. */
  2837. function reset()
  2838. {
  2839. $this->_close_channel($this->_get_interactive_channel());
  2840. }
  2841. /**
  2842. * Is timeout?
  2843. *
  2844. * Did exec() or read() return because they timed out or because they encountered the end?
  2845. *
  2846. * @access public
  2847. */
  2848. function isTimeout()
  2849. {
  2850. return $this->is_timeout;
  2851. }
  2852. /**
  2853. * Disconnect
  2854. *
  2855. * @access public
  2856. */
  2857. function disconnect()
  2858. {
  2859. $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  2860. if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
  2861. fclose($this->realtime_log_file);
  2862. }
  2863. }
  2864. /**
  2865. * Destructor.
  2866. *
  2867. * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
  2868. * disconnect().
  2869. *
  2870. * @access public
  2871. */
  2872. function __destruct()
  2873. {
  2874. $this->disconnect();
  2875. }
  2876. /**
  2877. * Is the connection still active?
  2878. *
  2879. * @return bool
  2880. * @access public
  2881. */
  2882. function isConnected()
  2883. {
  2884. return (bool) ($this->bitmap & NET_SSH2_MASK_CONNECTED);
  2885. }
  2886. /**
  2887. * Have you successfully been logged in?
  2888. *
  2889. * @return bool
  2890. * @access public
  2891. */
  2892. function isAuthenticated()
  2893. {
  2894. return (bool) ($this->bitmap & NET_SSH2_MASK_LOGIN);
  2895. }
  2896. /**
  2897. * Pings a server connection, or tries to reconnect if the connection has gone down
  2898. *
  2899. * Inspired by http://php.net/manual/en/mysqli.ping.php
  2900. *
  2901. * @return bool
  2902. * @access public
  2903. */
  2904. function ping()
  2905. {
  2906. if (!$this->isAuthenticated()) {
  2907. if (!empty($this->auth)) {
  2908. return $this->_reconnect();
  2909. }
  2910. return false;
  2911. }
  2912. $this->window_size_server_to_client[NET_SSH2_CHANNEL_KEEP_ALIVE] = $this->window_size;
  2913. $packet_size = 0x4000;
  2914. $packet = pack(
  2915. 'CNa*N3',
  2916. NET_SSH2_MSG_CHANNEL_OPEN,
  2917. strlen('session'),
  2918. 'session',
  2919. NET_SSH2_CHANNEL_KEEP_ALIVE,
  2920. $this->window_size_server_to_client[NET_SSH2_CHANNEL_KEEP_ALIVE],
  2921. $packet_size
  2922. );
  2923. if (!@$this->_send_binary_packet($packet)) {
  2924. return $this->_reconnect();
  2925. }
  2926. $this->channel_status[NET_SSH2_CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
  2927. $response = @$this->_get_channel_packet(NET_SSH2_CHANNEL_KEEP_ALIVE);
  2928. if ($response !== false) {
  2929. $this->_close_channel(NET_SSH2_CHANNEL_KEEP_ALIVE);
  2930. return true;
  2931. }
  2932. return $this->_reconnect();
  2933. }
  2934. /**
  2935. * In situ reconnect method
  2936. *
  2937. * @return boolean
  2938. * @access private
  2939. */
  2940. function _reconnect()
  2941. {
  2942. $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
  2943. $this->retry_connect = true;
  2944. if (!$this->_connect()) {
  2945. return false;
  2946. }
  2947. foreach ($this->auth as $auth) {
  2948. $result = call_user_func_array(array(&$this, 'login'), $auth);
  2949. }
  2950. return $result;
  2951. }
  2952. /**
  2953. * Resets a connection for re-use
  2954. *
  2955. * @param int $reason
  2956. * @access private
  2957. */
  2958. function _reset_connection($reason)
  2959. {
  2960. $this->_disconnect($reason);
  2961. $this->decrypt = $this->encrypt = false;
  2962. $this->decrypt_block_size = $this->encrypt_block_size = 8;
  2963. $this->hmac_check = $this->hmac_create = false;
  2964. $this->hmac_size = false;
  2965. $this->session_id = false;
  2966. $this->retry_connect = true;
  2967. $this->get_seq_no = $this->send_seq_no = 0;
  2968. }
  2969. /**
  2970. * Gets Binary Packets
  2971. *
  2972. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  2973. *
  2974. * @see self::_send_binary_packet()
  2975. * @return string
  2976. * @access private
  2977. */
  2978. function _get_binary_packet($skip_channel_filter = false)
  2979. {
  2980. if (!is_resource($this->fsock) || feof($this->fsock)) {
  2981. $this->bitmap = 0;
  2982. user_error('Connection closed prematurely');
  2983. return false;
  2984. }
  2985. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  2986. $raw = fread($this->fsock, $this->decrypt_block_size);
  2987. if (!strlen($raw)) {
  2988. return '';
  2989. }
  2990. if ($this->decrypt !== false) {
  2991. $raw = $this->decrypt->decrypt($raw);
  2992. }
  2993. if ($raw === false) {
  2994. user_error('Unable to decrypt content');
  2995. return false;
  2996. }
  2997. if (strlen($raw) < 5) {
  2998. return false;
  2999. }
  3000. extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
  3001. $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
  3002. // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
  3003. // "implementations SHOULD check that the packet length is reasonable"
  3004. // PuTTY uses 0x9000 as the actual max packet size and so to shall we
  3005. if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
  3006. if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  3007. $this->bad_key_size_fix = true;
  3008. $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  3009. return false;
  3010. }
  3011. user_error('Invalid size');
  3012. return false;
  3013. }
  3014. $buffer = '';
  3015. while ($remaining_length > 0) {
  3016. $temp = fread($this->fsock, $remaining_length);
  3017. if ($temp === false || feof($this->fsock)) {
  3018. $this->bitmap = 0;
  3019. user_error('Error reading from socket');
  3020. return false;
  3021. }
  3022. $buffer.= $temp;
  3023. $remaining_length-= strlen($temp);
  3024. }
  3025. $stop = strtok(microtime(), ' ') + strtok('');
  3026. if (strlen($buffer)) {
  3027. $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
  3028. }
  3029. $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
  3030. $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
  3031. if ($this->hmac_check !== false) {
  3032. $hmac = fread($this->fsock, $this->hmac_size);
  3033. if ($hmac === false || strlen($hmac) != $this->hmac_size) {
  3034. $this->bitmap = 0;
  3035. user_error('Error reading socket');
  3036. return false;
  3037. } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
  3038. user_error('Invalid HMAC');
  3039. return false;
  3040. }
  3041. }
  3042. //if ($this->decompress) {
  3043. // $payload = gzinflate(substr($payload, 2));
  3044. //}
  3045. $this->get_seq_no++;
  3046. if (defined('NET_SSH2_LOGGING')) {
  3047. $current = strtok(microtime(), ' ') + strtok('');
  3048. $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
  3049. $message_number = '<- ' . $message_number .
  3050. ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
  3051. $this->_append_log($message_number, $payload);
  3052. $this->last_packet = $current;
  3053. }
  3054. return $this->_filter($payload, $skip_channel_filter);
  3055. }
  3056. /**
  3057. * Filter Binary Packets
  3058. *
  3059. * Because some binary packets need to be ignored...
  3060. *
  3061. * @see self::_get_binary_packet()
  3062. * @return string
  3063. * @access private
  3064. */
  3065. function _filter($payload, $skip_channel_filter)
  3066. {
  3067. switch (ord($payload[0])) {
  3068. case NET_SSH2_MSG_DISCONNECT:
  3069. $this->_string_shift($payload, 1);
  3070. if (strlen($payload) < 8) {
  3071. return false;
  3072. }
  3073. extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
  3074. $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
  3075. $this->bitmap = 0;
  3076. return false;
  3077. case NET_SSH2_MSG_IGNORE:
  3078. $payload = $this->_get_binary_packet($skip_channel_filter);
  3079. break;
  3080. case NET_SSH2_MSG_DEBUG:
  3081. $this->_string_shift($payload, 2);
  3082. if (strlen($payload) < 4) {
  3083. return false;
  3084. }
  3085. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  3086. $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
  3087. $payload = $this->_get_binary_packet($skip_channel_filter);
  3088. break;
  3089. case NET_SSH2_MSG_UNIMPLEMENTED:
  3090. return false;
  3091. case NET_SSH2_MSG_KEXINIT:
  3092. if ($this->session_id !== false) {
  3093. $this->send_kex_first = false;
  3094. if (!$this->_key_exchange($payload)) {
  3095. $this->bitmap = 0;
  3096. return false;
  3097. }
  3098. $payload = $this->_get_binary_packet($skip_channel_filter);
  3099. }
  3100. }
  3101. // 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
  3102. if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
  3103. $this->_string_shift($payload, 1);
  3104. if (strlen($payload) < 4) {
  3105. return false;
  3106. }
  3107. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  3108. $this->banner_message = $this->_string_shift($payload, $length);
  3109. $payload = $this->_get_binary_packet();
  3110. }
  3111. // only called when we've already logged in
  3112. if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) {
  3113. switch (ord($payload[0])) {
  3114. case NET_SSH2_MSG_CHANNEL_DATA:
  3115. case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  3116. case NET_SSH2_MSG_CHANNEL_REQUEST:
  3117. case NET_SSH2_MSG_CHANNEL_CLOSE:
  3118. case NET_SSH2_MSG_CHANNEL_EOF:
  3119. if (!$skip_channel_filter && !empty($this->server_channels)) {
  3120. $this->binary_packet_buffer = $payload;
  3121. $this->_get_channel_packet(true);
  3122. $payload = $this->_get_binary_packet();
  3123. }
  3124. break;
  3125. case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
  3126. if (strlen($payload) < 4) {
  3127. return false;
  3128. }
  3129. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  3130. $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);
  3131. if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
  3132. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3133. }
  3134. $payload = $this->_get_binary_packet($skip_channel_filter);
  3135. break;
  3136. case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
  3137. $this->_string_shift($payload, 1);
  3138. if (strlen($payload) < 4) {
  3139. return false;
  3140. }
  3141. extract(unpack('Nlength', $this->_string_shift($payload, 4)));
  3142. $data = $this->_string_shift($payload, $length);
  3143. if (strlen($payload) < 4) {
  3144. return false;
  3145. }
  3146. extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
  3147. switch ($data) {
  3148. case 'auth-agent':
  3149. case 'auth-agent@openssh.com':
  3150. if (isset($this->agent)) {
  3151. $new_channel = NET_SSH2_CHANNEL_AGENT_FORWARD;
  3152. if (strlen($payload) < 8) {
  3153. return false;
  3154. }
  3155. extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
  3156. extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));
  3157. $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
  3158. $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
  3159. $this->window_size_client_to_server[$new_channel] = $this->window_size;
  3160. $packet_size = 0x4000;
  3161. $packet = pack(
  3162. 'CN4',
  3163. NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
  3164. $server_channel,
  3165. $new_channel,
  3166. $packet_size,
  3167. $packet_size
  3168. );
  3169. $this->server_channels[$new_channel] = $server_channel;
  3170. $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
  3171. if (!$this->_send_binary_packet($packet)) {
  3172. return false;
  3173. }
  3174. }
  3175. break;
  3176. default:
  3177. $packet = pack(
  3178. 'CN3a*Na*',
  3179. NET_SSH2_MSG_REQUEST_FAILURE,
  3180. $server_channel,
  3181. NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
  3182. 0,
  3183. '',
  3184. 0,
  3185. ''
  3186. );
  3187. if (!$this->_send_binary_packet($packet)) {
  3188. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3189. }
  3190. }
  3191. $payload = $this->_get_binary_packet($skip_channel_filter);
  3192. break;
  3193. case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
  3194. $this->_string_shift($payload, 1);
  3195. if (strlen($payload) < 8) {
  3196. return false;
  3197. }
  3198. extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
  3199. extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
  3200. $this->window_size_client_to_server[$channel]+= $window_size;
  3201. $payload = ($this->bitmap & NET_SSH2_MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
  3202. }
  3203. }
  3204. return $payload;
  3205. }
  3206. /**
  3207. * Enable Quiet Mode
  3208. *
  3209. * Suppress stderr from output
  3210. *
  3211. * @access public
  3212. */
  3213. function enableQuietMode()
  3214. {
  3215. $this->quiet_mode = true;
  3216. }
  3217. /**
  3218. * Disable Quiet Mode
  3219. *
  3220. * Show stderr in output
  3221. *
  3222. * @access public
  3223. */
  3224. function disableQuietMode()
  3225. {
  3226. $this->quiet_mode = false;
  3227. }
  3228. /**
  3229. * Returns whether Quiet Mode is enabled or not
  3230. *
  3231. * @see self::enableQuietMode()
  3232. * @see self::disableQuietMode()
  3233. *
  3234. * @access public
  3235. * @return bool
  3236. */
  3237. function isQuietModeEnabled()
  3238. {
  3239. return $this->quiet_mode;
  3240. }
  3241. /**
  3242. * Enable request-pty when using exec()
  3243. *
  3244. * @access public
  3245. */
  3246. function enablePTY()
  3247. {
  3248. $this->request_pty = true;
  3249. }
  3250. /**
  3251. * Disable request-pty when using exec()
  3252. *
  3253. * @access public
  3254. */
  3255. function disablePTY()
  3256. {
  3257. if ($this->in_request_pty_exec) {
  3258. $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
  3259. $this->in_request_pty_exec = false;
  3260. }
  3261. $this->request_pty = false;
  3262. }
  3263. /**
  3264. * Returns whether request-pty is enabled or not
  3265. *
  3266. * @see self::enablePTY()
  3267. * @see self::disablePTY()
  3268. *
  3269. * @access public
  3270. * @return bool
  3271. */
  3272. function isPTYEnabled()
  3273. {
  3274. return $this->request_pty;
  3275. }
  3276. /**
  3277. * Gets channel data
  3278. *
  3279. * Returns the data as a string if it's available and false if not.
  3280. *
  3281. * @param $client_channel
  3282. * @return mixed
  3283. * @access private
  3284. */
  3285. function _get_channel_packet($client_channel, $skip_extended = false)
  3286. {
  3287. if (!empty($this->channel_buffers[$client_channel])) {
  3288. return array_shift($this->channel_buffers[$client_channel]);
  3289. }
  3290. while (true) {
  3291. if ($this->binary_packet_buffer !== false) {
  3292. $response = $this->binary_packet_buffer;
  3293. $this->binary_packet_buffer = false;
  3294. } else {
  3295. $read = array($this->fsock);
  3296. $write = $except = null;
  3297. if (!$this->curTimeout) {
  3298. @stream_select($read, $write, $except, null);
  3299. } else {
  3300. if ($this->curTimeout < 0) {
  3301. $this->is_timeout = true;
  3302. return true;
  3303. }
  3304. $read = array($this->fsock);
  3305. $write = $except = null;
  3306. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  3307. $sec = floor($this->curTimeout);
  3308. $usec = 1000000 * ($this->curTimeout - $sec);
  3309. // on windows this returns a "Warning: Invalid CRT parameters detected" error
  3310. if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
  3311. $this->is_timeout = true;
  3312. if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) {
  3313. $this->_close_channel($client_channel);
  3314. }
  3315. return true;
  3316. }
  3317. $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
  3318. $this->curTimeout-= $elapsed;
  3319. }
  3320. $response = $this->_get_binary_packet(true);
  3321. if ($response === false) {
  3322. $this->bitmap = 0;
  3323. user_error('Connection closed by server');
  3324. return false;
  3325. }
  3326. }
  3327. if ($client_channel == -1 && $response === true) {
  3328. return true;
  3329. }
  3330. if (!strlen($response)) {
  3331. return false;
  3332. }
  3333. extract(unpack('Ctype', $this->_string_shift($response, 1)));
  3334. if (strlen($response) < 4) {
  3335. return false;
  3336. }
  3337. if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
  3338. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  3339. } else {
  3340. extract(unpack('Nchannel', $this->_string_shift($response, 4)));
  3341. }
  3342. // will not be setup yet on incoming channel open request
  3343. if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
  3344. $this->window_size_server_to_client[$channel]-= strlen($response);
  3345. // resize the window, if appropriate
  3346. if ($this->window_size_server_to_client[$channel] < 0) {
  3347. // PuTTY does something more analogous to the following:
  3348. //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
  3349. $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
  3350. if (!$this->_send_binary_packet($packet)) {
  3351. return false;
  3352. }
  3353. $this->window_size_server_to_client[$channel]+= $this->window_resize;
  3354. }
  3355. switch ($type) {
  3356. case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
  3357. /*
  3358. if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
  3359. $this->_send_channel_packet($client_channel, chr(0));
  3360. }
  3361. */
  3362. // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
  3363. if (strlen($response) < 8) {
  3364. return false;
  3365. }
  3366. extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
  3367. $data = $this->_string_shift($response, $length);
  3368. $this->stdErrorLog.= $data;
  3369. if ($skip_extended || $this->quiet_mode) {
  3370. continue 2;
  3371. }
  3372. if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
  3373. return $data;
  3374. }
  3375. if (!isset($this->channel_buffers[$channel])) {
  3376. $this->channel_buffers[$channel] = array();
  3377. }
  3378. $this->channel_buffers[$channel][] = $data;
  3379. continue 2;
  3380. case NET_SSH2_MSG_CHANNEL_REQUEST:
  3381. if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
  3382. continue 2;
  3383. }
  3384. if (strlen($response) < 4) {
  3385. return false;
  3386. }
  3387. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  3388. $value = $this->_string_shift($response, $length);
  3389. switch ($value) {
  3390. case 'exit-signal':
  3391. $this->_string_shift($response, 1);
  3392. if (strlen($response) < 4) {
  3393. return false;
  3394. }
  3395. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  3396. $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
  3397. $this->_string_shift($response, 1);
  3398. if (strlen($response) < 4) {
  3399. return false;
  3400. }
  3401. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  3402. if ($length) {
  3403. $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
  3404. }
  3405. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
  3406. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  3407. $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
  3408. continue 3;
  3409. case 'exit-status':
  3410. if (strlen($response) < 5) {
  3411. return false;
  3412. }
  3413. extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
  3414. $this->exit_status = $exit_status;
  3415. // "The client MAY ignore these messages."
  3416. // -- http://tools.ietf.org/html/rfc4254#section-6.10
  3417. continue 3;
  3418. default:
  3419. // "Some systems may not implement signals, in which case they SHOULD ignore this message."
  3420. // -- http://tools.ietf.org/html/rfc4254#section-6.9
  3421. continue 3;
  3422. }
  3423. }
  3424. switch ($this->channel_status[$channel]) {
  3425. case NET_SSH2_MSG_CHANNEL_OPEN:
  3426. switch ($type) {
  3427. case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
  3428. if (strlen($response) < 4) {
  3429. return false;
  3430. }
  3431. extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
  3432. $this->server_channels[$channel] = $server_channel;
  3433. if (strlen($response) < 4) {
  3434. return false;
  3435. }
  3436. extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
  3437. if ($window_size < 0) {
  3438. $window_size&= 0x7FFFFFFF;
  3439. $window_size+= 0x80000000;
  3440. }
  3441. $this->window_size_client_to_server[$channel] = $window_size;
  3442. if (strlen($response) < 4) {
  3443. return false;
  3444. }
  3445. $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
  3446. $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
  3447. $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
  3448. $this->_on_channel_open();
  3449. return $result;
  3450. //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
  3451. default:
  3452. user_error('Unable to open channel');
  3453. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3454. }
  3455. break;
  3456. case NET_SSH2_MSG_CHANNEL_REQUEST:
  3457. switch ($type) {
  3458. case NET_SSH2_MSG_CHANNEL_SUCCESS:
  3459. return true;
  3460. case NET_SSH2_MSG_CHANNEL_FAILURE:
  3461. return false;
  3462. default:
  3463. user_error('Unable to fulfill channel request');
  3464. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3465. }
  3466. case NET_SSH2_MSG_CHANNEL_CLOSE:
  3467. return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
  3468. }
  3469. }
  3470. // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
  3471. switch ($type) {
  3472. case NET_SSH2_MSG_CHANNEL_DATA:
  3473. /*
  3474. if ($channel == NET_SSH2_CHANNEL_EXEC) {
  3475. // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
  3476. // this actually seems to make things twice as fast. more to the point, the message right after
  3477. // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
  3478. // in OpenSSH it slows things down but only by a couple thousandths of a second.
  3479. $this->_send_channel_packet($channel, chr(0));
  3480. }
  3481. */
  3482. if (strlen($response) < 4) {
  3483. return false;
  3484. }
  3485. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  3486. $data = $this->_string_shift($response, $length);
  3487. if ($channel == NET_SSH2_CHANNEL_AGENT_FORWARD) {
  3488. $agent_response = $this->agent->_forward_data($data);
  3489. if (!is_bool($agent_response)) {
  3490. $this->_send_channel_packet($channel, $agent_response);
  3491. }
  3492. break;
  3493. }
  3494. if ($client_channel == $channel) {
  3495. return $data;
  3496. }
  3497. if (!isset($this->channel_buffers[$channel])) {
  3498. $this->channel_buffers[$channel] = array();
  3499. }
  3500. $this->channel_buffers[$channel][] = $data;
  3501. break;
  3502. case NET_SSH2_MSG_CHANNEL_CLOSE:
  3503. $this->curTimeout = 5;
  3504. if ($this->bitmap & NET_SSH2_MASK_SHELL) {
  3505. $this->bitmap&= ~NET_SSH2_MASK_SHELL;
  3506. }
  3507. if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
  3508. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
  3509. }
  3510. $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  3511. if ($client_channel == $channel) {
  3512. return true;
  3513. }
  3514. case NET_SSH2_MSG_CHANNEL_EOF:
  3515. break;
  3516. default:
  3517. user_error('Error reading channel data');
  3518. return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
  3519. }
  3520. }
  3521. }
  3522. /**
  3523. * Sends Binary Packets
  3524. *
  3525. * See '6. Binary Packet Protocol' of rfc4253 for more info.
  3526. *
  3527. * @param string $data
  3528. * @param string $logged
  3529. * @see self::_get_binary_packet()
  3530. * @return bool
  3531. * @access private
  3532. */
  3533. function _send_binary_packet($data, $logged = null)
  3534. {
  3535. if (!is_resource($this->fsock) || feof($this->fsock)) {
  3536. $this->bitmap = 0;
  3537. user_error('Connection closed prematurely');
  3538. return false;
  3539. }
  3540. //if ($this->compress) {
  3541. // // the -4 removes the checksum:
  3542. // // http://php.net/function.gzcompress#57710
  3543. // $data = substr(gzcompress($data), 0, -4);
  3544. //}
  3545. // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
  3546. $packet_length = strlen($data) + 9;
  3547. // round up to the nearest $this->encrypt_block_size
  3548. $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
  3549. // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
  3550. $padding_length = $packet_length - strlen($data) - 5;
  3551. $padding = crypt_random_string($padding_length);
  3552. // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
  3553. $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
  3554. $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
  3555. $this->send_seq_no++;
  3556. if ($this->encrypt !== false) {
  3557. $packet = $this->encrypt->encrypt($packet);
  3558. }
  3559. $packet.= $hmac;
  3560. $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
  3561. $result = strlen($packet) == fputs($this->fsock, $packet);
  3562. $stop = strtok(microtime(), ' ') + strtok('');
  3563. if (defined('NET_SSH2_LOGGING')) {
  3564. $current = strtok(microtime(), ' ') + strtok('');
  3565. $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
  3566. $message_number = '-> ' . $message_number .
  3567. ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
  3568. $this->_append_log($message_number, isset($logged) ? $logged : $data);
  3569. $this->last_packet = $current;
  3570. }
  3571. return $result;
  3572. }
  3573. /**
  3574. * Logs data packets
  3575. *
  3576. * Makes sure that only the last 1MB worth of packets will be logged
  3577. *
  3578. * @param string $data
  3579. * @access private
  3580. */
  3581. function _append_log($message_number, $message)
  3582. {
  3583. // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
  3584. if (strlen($message_number) > 2) {
  3585. $this->_string_shift($message);
  3586. }
  3587. switch (NET_SSH2_LOGGING) {
  3588. // useful for benchmarks
  3589. case NET_SSH2_LOG_SIMPLE:
  3590. $this->message_number_log[] = $message_number;
  3591. break;
  3592. // the most useful log for SSH2
  3593. case NET_SSH2_LOG_COMPLEX:
  3594. $this->message_number_log[] = $message_number;
  3595. $this->log_size+= strlen($message);
  3596. $this->message_log[] = $message;
  3597. while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
  3598. $this->log_size-= strlen(array_shift($this->message_log));
  3599. array_shift($this->message_number_log);
  3600. }
  3601. break;
  3602. // dump the output out realtime; packets may be interspersed with non packets,
  3603. // passwords won't be filtered out and select other packets may not be correctly
  3604. // identified
  3605. case NET_SSH2_LOG_REALTIME:
  3606. switch (PHP_SAPI) {
  3607. case 'cli':
  3608. $start = $stop = "\r\n";
  3609. break;
  3610. default:
  3611. $start = '<pre>';
  3612. $stop = '</pre>';
  3613. }
  3614. echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
  3615. @flush();
  3616. @ob_flush();
  3617. break;
  3618. // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE
  3619. // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE.
  3620. // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
  3621. // at the beginning of the file
  3622. case NET_SSH2_LOG_REALTIME_FILE:
  3623. if (!isset($this->realtime_log_file)) {
  3624. // PHP doesn't seem to like using constants in fopen()
  3625. $filename = NET_SSH2_LOG_REALTIME_FILENAME;
  3626. $fp = fopen($filename, 'w');
  3627. $this->realtime_log_file = $fp;
  3628. }
  3629. if (!is_resource($this->realtime_log_file)) {
  3630. break;
  3631. }
  3632. $entry = $this->_format_log(array($message), array($message_number));
  3633. if ($this->realtime_log_wrap) {
  3634. $temp = "<<< START >>>\r\n";
  3635. $entry.= $temp;
  3636. fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
  3637. }
  3638. $this->realtime_log_size+= strlen($entry);
  3639. if ($this->realtime_log_size > NET_SSH2_LOG_MAX_SIZE) {
  3640. fseek($this->realtime_log_file, 0);
  3641. $this->realtime_log_size = strlen($entry);
  3642. $this->realtime_log_wrap = true;
  3643. }
  3644. fputs($this->realtime_log_file, $entry);
  3645. }
  3646. }
  3647. /**
  3648. * Sends channel data
  3649. *
  3650. * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
  3651. *
  3652. * @param int $client_channel
  3653. * @param string $data
  3654. * @return bool
  3655. * @access private
  3656. */
  3657. function _send_channel_packet($client_channel, $data)
  3658. {
  3659. while (strlen($data)) {
  3660. if (!$this->window_size_client_to_server[$client_channel]) {
  3661. $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
  3662. // using an invalid channel will let the buffers be built up for the valid channels
  3663. $this->_get_channel_packet(-1);
  3664. $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
  3665. }
  3666. /* The maximum amount of data allowed is determined by the maximum
  3667. packet size for the channel, and the current window size, whichever
  3668. is smaller.
  3669. -- http://tools.ietf.org/html/rfc4254#section-5.2 */
  3670. $max_size = min(
  3671. $this->packet_size_client_to_server[$client_channel],
  3672. $this->window_size_client_to_server[$client_channel]
  3673. );
  3674. $temp = $this->_string_shift($data, $max_size);
  3675. $packet = pack(
  3676. 'CN2a*',
  3677. NET_SSH2_MSG_CHANNEL_DATA,
  3678. $this->server_channels[$client_channel],
  3679. strlen($temp),
  3680. $temp
  3681. );
  3682. $this->window_size_client_to_server[$client_channel]-= strlen($temp);
  3683. if (!$this->_send_binary_packet($packet)) {
  3684. return false;
  3685. }
  3686. }
  3687. return true;
  3688. }
  3689. /**
  3690. * Closes and flushes a channel
  3691. *
  3692. * Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
  3693. * and for SFTP channels are presumably closed when the client disconnects. This functions is intended
  3694. * for SCP more than anything.
  3695. *
  3696. * @param int $client_channel
  3697. * @param bool $want_reply
  3698. * @return bool
  3699. * @access private
  3700. */
  3701. function _close_channel($client_channel, $want_reply = false)
  3702. {
  3703. // see http://tools.ietf.org/html/rfc4254#section-5.3
  3704. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
  3705. if (!$want_reply) {
  3706. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
  3707. }
  3708. $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
  3709. $this->curTimeout = 5;
  3710. while (!is_bool($this->_get_channel_packet($client_channel))) {
  3711. }
  3712. if ($this->is_timeout) {
  3713. $this->disconnect();
  3714. }
  3715. if ($want_reply) {
  3716. $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
  3717. }
  3718. if ($this->bitmap & NET_SSH2_MASK_SHELL) {
  3719. $this->bitmap&= ~NET_SSH2_MASK_SHELL;
  3720. }
  3721. }
  3722. /**
  3723. * Disconnect
  3724. *
  3725. * @param int $reason
  3726. * @return bool
  3727. * @access private
  3728. */
  3729. function _disconnect($reason)
  3730. {
  3731. if ($this->bitmap & NET_SSH2_MASK_CONNECTED) {
  3732. $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
  3733. $this->_send_binary_packet($data);
  3734. }
  3735. $this->bitmap = 0;
  3736. if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
  3737. fclose($this->fsock);
  3738. }
  3739. return false;
  3740. }
  3741. /**
  3742. * String Shift
  3743. *
  3744. * Inspired by array_shift
  3745. *
  3746. * @param string $string
  3747. * @param int $index
  3748. * @return string
  3749. * @access private
  3750. */
  3751. function _string_shift(&$string, $index = 1)
  3752. {
  3753. $substr = substr($string, 0, $index);
  3754. $string = substr($string, $index);
  3755. return $substr;
  3756. }
  3757. /**
  3758. * Define Array
  3759. *
  3760. * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
  3761. * named constants from it, using the value as the name of the constant and the index as the value of the constant.
  3762. * If any of the constants that would be defined already exists, none of the constants will be defined.
  3763. *
  3764. * @param array $array
  3765. * @access private
  3766. */
  3767. function _define_array()
  3768. {
  3769. $args = func_get_args();
  3770. foreach ($args as $arg) {
  3771. foreach ($arg as $key => $value) {
  3772. if (!defined($value)) {
  3773. define($value, $key);
  3774. } else {
  3775. break 2;
  3776. }
  3777. }
  3778. }
  3779. }
  3780. /**
  3781. * Returns a log of the packets that have been sent and received.
  3782. *
  3783. * 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')
  3784. *
  3785. * @access public
  3786. * @return array|false|string
  3787. */
  3788. function getLog()
  3789. {
  3790. if (!defined('NET_SSH2_LOGGING')) {
  3791. return false;
  3792. }
  3793. switch (NET_SSH2_LOGGING) {
  3794. case NET_SSH2_LOG_SIMPLE:
  3795. return $this->message_number_log;
  3796. case NET_SSH2_LOG_COMPLEX:
  3797. $log = $this->_format_log($this->message_log, $this->message_number_log);
  3798. return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
  3799. default:
  3800. return false;
  3801. }
  3802. }
  3803. /**
  3804. * Formats a log for printing
  3805. *
  3806. * @param array $message_log
  3807. * @param array $message_number_log
  3808. * @access private
  3809. * @return string
  3810. */
  3811. function _format_log($message_log, $message_number_log)
  3812. {
  3813. $output = '';
  3814. for ($i = 0; $i < count($message_log); $i++) {
  3815. $output.= $message_number_log[$i] . "\r\n";
  3816. $current_log = $message_log[$i];
  3817. $j = 0;
  3818. do {
  3819. if (strlen($current_log)) {
  3820. $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
  3821. }
  3822. $fragment = $this->_string_shift($current_log, $this->log_short_width);
  3823. $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
  3824. // replace non ASCII printable characters with dots
  3825. // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
  3826. // also replace < with a . since < messes up the output on web browsers
  3827. $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
  3828. $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
  3829. $j++;
  3830. } while (strlen($current_log));
  3831. $output.= "\r\n";
  3832. }
  3833. return $output;
  3834. }
  3835. /**
  3836. * Helper function for _format_log
  3837. *
  3838. * For use with preg_replace_callback()
  3839. *
  3840. * @param array $matches
  3841. * @access private
  3842. * @return string
  3843. */
  3844. function _format_log_helper($matches)
  3845. {
  3846. return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
  3847. }
  3848. /**
  3849. * Helper function for agent->_on_channel_open()
  3850. *
  3851. * Used when channels are created to inform agent
  3852. * of said channel opening. Must be called after
  3853. * channel open confirmation received
  3854. *
  3855. * @access private
  3856. */
  3857. function _on_channel_open()
  3858. {
  3859. if (isset($this->agent)) {
  3860. $this->agent->_on_channel_open($this);
  3861. }
  3862. }
  3863. /**
  3864. * Returns the first value of the intersection of two arrays or false if
  3865. * the intersection is empty. The order is defined by the first parameter.
  3866. *
  3867. * @param array $array1
  3868. * @param array $array2
  3869. * @return mixed False if intersection is empty, else intersected value.
  3870. * @access private
  3871. */
  3872. function _array_intersect_first($array1, $array2)
  3873. {
  3874. foreach ($array1 as $value) {
  3875. if (in_array($value, $array2)) {
  3876. return $value;
  3877. }
  3878. }
  3879. return false;
  3880. }
  3881. /**
  3882. * Returns all errors
  3883. *
  3884. * @return string[]
  3885. * @access public
  3886. */
  3887. function getErrors()
  3888. {
  3889. return $this->errors;
  3890. }
  3891. /**
  3892. * Returns the last error
  3893. *
  3894. * @return string
  3895. * @access public
  3896. */
  3897. function getLastError()
  3898. {
  3899. $count = count($this->errors);
  3900. if ($count > 0) {
  3901. return $this->errors[$count - 1];
  3902. }
  3903. }
  3904. /**
  3905. * Return the server identification.
  3906. *
  3907. * @return string
  3908. * @access public
  3909. */
  3910. function getServerIdentification()
  3911. {
  3912. $this->_connect();
  3913. return $this->server_identifier;
  3914. }
  3915. /**
  3916. * Return a list of the key exchange algorithms the server supports.
  3917. *
  3918. * @return array
  3919. * @access public
  3920. */
  3921. function getKexAlgorithms()
  3922. {
  3923. $this->_connect();
  3924. return $this->kex_algorithms;
  3925. }
  3926. /**
  3927. * Return a list of the host key (public key) algorithms the server supports.
  3928. *
  3929. * @return array
  3930. * @access public
  3931. */
  3932. function getServerHostKeyAlgorithms()
  3933. {
  3934. $this->_connect();
  3935. return $this->server_host_key_algorithms;
  3936. }
  3937. /**
  3938. * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
  3939. *
  3940. * @return array
  3941. * @access public
  3942. */
  3943. function getEncryptionAlgorithmsClient2Server()
  3944. {
  3945. $this->_connect();
  3946. return $this->encryption_algorithms_client_to_server;
  3947. }
  3948. /**
  3949. * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
  3950. *
  3951. * @return array
  3952. * @access public
  3953. */
  3954. function getEncryptionAlgorithmsServer2Client()
  3955. {
  3956. $this->_connect();
  3957. return $this->encryption_algorithms_server_to_client;
  3958. }
  3959. /**
  3960. * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
  3961. *
  3962. * @return array
  3963. * @access public
  3964. */
  3965. function getMACAlgorithmsClient2Server()
  3966. {
  3967. $this->_connect();
  3968. return $this->mac_algorithms_client_to_server;
  3969. }
  3970. /**
  3971. * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
  3972. *
  3973. * @return array
  3974. * @access public
  3975. */
  3976. function getMACAlgorithmsServer2Client()
  3977. {
  3978. $this->_connect();
  3979. return $this->mac_algorithms_server_to_client;
  3980. }
  3981. /**
  3982. * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
  3983. *
  3984. * @return array
  3985. * @access public
  3986. */
  3987. function getCompressionAlgorithmsClient2Server()
  3988. {
  3989. $this->_connect();
  3990. return $this->compression_algorithms_client_to_server;
  3991. }
  3992. /**
  3993. * Return a list of the compression algorithms the server supports, when sending stuff to the client.
  3994. *
  3995. * @return array
  3996. * @access public
  3997. */
  3998. function getCompressionAlgorithmsServer2Client()
  3999. {
  4000. $this->_connect();
  4001. return $this->compression_algorithms_server_to_client;
  4002. }
  4003. /**
  4004. * Return a list of the languages the server supports, when sending stuff to the client.
  4005. *
  4006. * @return array
  4007. * @access public
  4008. */
  4009. function getLanguagesServer2Client()
  4010. {
  4011. $this->_connect();
  4012. return $this->languages_server_to_client;
  4013. }
  4014. /**
  4015. * Return a list of the languages the server supports, when receiving stuff from the client.
  4016. *
  4017. * @return array
  4018. * @access public
  4019. */
  4020. function getLanguagesClient2Server()
  4021. {
  4022. $this->_connect();
  4023. return $this->languages_client_to_server;
  4024. }
  4025. /**
  4026. * Returns a list of algorithms the server supports
  4027. *
  4028. * @return array
  4029. * @access public
  4030. */
  4031. function getServerAlgorithms()
  4032. {
  4033. $this->_connect();
  4034. return array(
  4035. 'kex' => $this->kex_algorithms,
  4036. 'hostkey' => $this->server_host_key_algorithms,
  4037. 'client_to_server' => array(
  4038. 'crypt' => $this->encryption_algorithms_client_to_server,
  4039. 'mac' => $this->mac_algorithms_client_to_server,
  4040. 'comp' => $this->compression_algorithms_client_to_server,
  4041. 'lang' => $this->languages_client_to_server
  4042. ),
  4043. 'server_to_client' => array(
  4044. 'crypt' => $this->encryption_algorithms_server_to_client,
  4045. 'mac' => $this->mac_algorithms_server_to_client,
  4046. 'comp' => $this->compression_algorithms_server_to_client,
  4047. 'lang' => $this->languages_server_to_client
  4048. )
  4049. );
  4050. }
  4051. /**
  4052. * Returns a list of KEX algorithms that phpseclib supports
  4053. *
  4054. * @return array
  4055. * @access public
  4056. */
  4057. function getSupportedKEXAlgorithms()
  4058. {
  4059. $kex_algorithms = array(
  4060. 'diffie-hellman-group-exchange-sha256',// RFC 4419
  4061. 'diffie-hellman-group-exchange-sha1', // RFC 4419
  4062. // Diffie-Hellman Key Agreement (DH) using integer modulo prime
  4063. // groups.
  4064. 'diffie-hellman-group14-sha1', // REQUIRED
  4065. 'diffie-hellman-group1-sha1', // REQUIRED
  4066. );
  4067. return $kex_algorithms;
  4068. }
  4069. /**
  4070. * Returns a list of host key algorithms that phpseclib supports
  4071. *
  4072. * @return array
  4073. * @access public
  4074. */
  4075. function getSupportedHostKeyAlgorithms()
  4076. {
  4077. return array(
  4078. 'rsa-sha2-256', // RFC 8332
  4079. 'rsa-sha2-512', // RFC 8332
  4080. 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
  4081. 'ssh-dss' // REQUIRED sign Raw DSS Key
  4082. );
  4083. }
  4084. /**
  4085. * Returns a list of symmetric key algorithms that phpseclib supports
  4086. *
  4087. * @return array
  4088. * @access public
  4089. */
  4090. function getSupportedEncryptionAlgorithms()
  4091. {
  4092. $algos = array(
  4093. // from <http://tools.ietf.org/html/rfc4345#section-4>:
  4094. 'arcfour256',
  4095. 'arcfour128',
  4096. //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
  4097. // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
  4098. 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
  4099. 'aes192-ctr', // RECOMMENDED AES with 192-bit key
  4100. 'aes256-ctr', // RECOMMENDED AES with 256-bit key
  4101. 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
  4102. 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
  4103. 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
  4104. 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
  4105. 'aes192-cbc', // OPTIONAL AES with a 192-bit key
  4106. 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
  4107. 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key
  4108. 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key
  4109. 'twofish256-cbc',
  4110. 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc"
  4111. // (this is being retained for historical reasons)
  4112. 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode
  4113. 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode
  4114. '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
  4115. '3des-cbc', // REQUIRED three-key 3DES in CBC mode
  4116. //'none' // OPTIONAL no encryption; NOT RECOMMENDED
  4117. );
  4118. $engines = array(
  4119. CRYPT_ENGINE_OPENSSL,
  4120. CRYPT_ENGINE_MCRYPT,
  4121. CRYPT_ENGINE_INTERNAL
  4122. );
  4123. $ciphers = array();
  4124. foreach ($engines as $engine) {
  4125. foreach ($algos as $algo) {
  4126. $obj = $this->_encryption_algorithm_to_crypt_instance($algo);
  4127. if (strtolower(get_class($obj)) == 'crypt_rijndael') {
  4128. $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
  4129. }
  4130. switch ($algo) {
  4131. case 'arcfour128':
  4132. case 'arcfour256':
  4133. if ($engine != CRYPT_ENGINE_INTERNAL) {
  4134. continue 2;
  4135. }
  4136. }
  4137. if ($obj->isValidEngine($engine)) {
  4138. $algos = array_diff($algos, array($algo));
  4139. $ciphers[] = $algo;
  4140. }
  4141. }
  4142. }
  4143. return $ciphers;
  4144. }
  4145. /**
  4146. * Returns a list of MAC algorithms that phpseclib supports
  4147. *
  4148. * @return array
  4149. * @access public
  4150. */
  4151. function getSupportedMACAlgorithms()
  4152. {
  4153. return array(
  4154. // from <http://www.ietf.org/rfc/rfc6668.txt>:
  4155. 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
  4156. 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
  4157. 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
  4158. 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
  4159. 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
  4160. //'none' // OPTIONAL no MAC; NOT RECOMMENDED
  4161. );
  4162. }
  4163. /**
  4164. * Returns a list of compression algorithms that phpseclib supports
  4165. *
  4166. * @return array
  4167. * @access public
  4168. */
  4169. function getSupportedCompressionAlgorithms()
  4170. {
  4171. return array(
  4172. 'none' // REQUIRED no compression
  4173. //'zlib' // OPTIONAL ZLIB (LZ77) compression
  4174. );
  4175. }
  4176. /**
  4177. * Return list of negotiated algorithms
  4178. *
  4179. * Uses the same format as https://www.php.net/ssh2-methods-negotiated
  4180. *
  4181. * @return array
  4182. * @access public
  4183. */
  4184. function getAlgorithmsNegotiated()
  4185. {
  4186. $this->_connect();
  4187. return array(
  4188. 'kex' => $this->kex_algorithm,
  4189. 'hostkey' => $this->signature_format,
  4190. 'client_to_server' => array(
  4191. 'crypt' => $this->encrypt->name,
  4192. 'mac' => $this->hmac_create->name,
  4193. 'comp' => 'none',
  4194. ),
  4195. 'server_to_client' => array(
  4196. 'crypt' => $this->decrypt->name,
  4197. 'mac' => $this->hmac_check->name,
  4198. 'comp' => 'none',
  4199. )
  4200. );
  4201. }
  4202. /**
  4203. * Accepts an associative array with up to four parameters as described at
  4204. * <https://www.php.net/manual/en/function.ssh2-connect.php>
  4205. *
  4206. * @param array $methods
  4207. * @access public
  4208. */
  4209. function setPreferredAlgorithms($methods)
  4210. {
  4211. $preferred = $methods;
  4212. if (isset($preferred['kex'])) {
  4213. $preferred['kex'] = array_intersect(
  4214. $preferred['kex'],
  4215. $this->getSupportedKEXAlgorithms()
  4216. );
  4217. }
  4218. if (isset($preferred['hostkey'])) {
  4219. $preferred['hostkey'] = array_intersect(
  4220. $preferred['hostkey'],
  4221. $this->getSupportedHostKeyAlgorithms()
  4222. );
  4223. }
  4224. $keys = array('client_to_server', 'server_to_client');
  4225. foreach ($keys as $key) {
  4226. if (isset($preferred[$key])) {
  4227. $a = &$preferred[$key];
  4228. if (isset($a['crypt'])) {
  4229. $a['crypt'] = array_intersect(
  4230. $a['crypt'],
  4231. $this->getSupportedEncryptionAlgorithms()
  4232. );
  4233. }
  4234. if (isset($a['comp'])) {
  4235. $a['comp'] = array_intersect(
  4236. $a['comp'],
  4237. $this->getSupportedCompressionAlgorithms()
  4238. );
  4239. }
  4240. if (isset($a['mac'])) {
  4241. $a['mac'] = array_intersect(
  4242. $a['mac'],
  4243. $this->getSupportedMACAlgorithms()
  4244. );
  4245. }
  4246. }
  4247. }
  4248. $keys = array(
  4249. 'kex',
  4250. 'hostkey',
  4251. 'client_to_server/crypt',
  4252. 'client_to_server/comp',
  4253. 'client_to_server/mac',
  4254. 'server_to_client/crypt',
  4255. 'server_to_client/comp',
  4256. 'server_to_client/mac',
  4257. );
  4258. foreach ($keys as $key) {
  4259. $p = $preferred;
  4260. $m = $methods;
  4261. $subkeys = explode('/', $key);
  4262. foreach ($subkeys as $subkey) {
  4263. if (!isset($p[$subkey])) {
  4264. continue 2;
  4265. }
  4266. $p = $p[$subkey];
  4267. $m = $m[$subkey];
  4268. }
  4269. if (count($p) != count($m)) {
  4270. $diff = array_diff($m, $p);
  4271. $msg = count($diff) == 1 ?
  4272. ' is not a supported algorithm' :
  4273. ' are not supported algorithms';
  4274. user_error(implode(', ', $diff) . $msg);
  4275. return false;
  4276. }
  4277. }
  4278. $this->preferred = $preferred;
  4279. }
  4280. /**
  4281. * Returns the banner message.
  4282. *
  4283. * Quoting from the RFC, "in some jurisdictions, sending a warning message before
  4284. * authentication may be relevant for getting legal protection."
  4285. *
  4286. * @return string
  4287. * @access public
  4288. */
  4289. function getBannerMessage()
  4290. {
  4291. return $this->banner_message;
  4292. }
  4293. /**
  4294. * Returns the server public host key.
  4295. *
  4296. * Caching this the first time you connect to a server and checking the result on subsequent connections
  4297. * is recommended. Returns false if the server signature is not signed correctly with the public host key.
  4298. *
  4299. * @return mixed
  4300. * @access public
  4301. */
  4302. function getServerPublicHostKey()
  4303. {
  4304. if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
  4305. if (!$this->_connect()) {
  4306. return false;
  4307. }
  4308. }
  4309. $signature = $this->signature;
  4310. $server_public_host_key = $this->server_public_host_key;
  4311. if (strlen($server_public_host_key) < 4) {
  4312. return false;
  4313. }
  4314. extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
  4315. $this->_string_shift($server_public_host_key, $length);
  4316. if ($this->signature_validated) {
  4317. return $this->bitmap ?
  4318. $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
  4319. false;
  4320. }
  4321. $this->signature_validated = true;
  4322. switch ($this->signature_format) {
  4323. case 'ssh-dss':
  4324. $zero = new Math_BigInteger();
  4325. if (strlen($server_public_host_key) < 4) {
  4326. return false;
  4327. }
  4328. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  4329. $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  4330. if (strlen($server_public_host_key) < 4) {
  4331. return false;
  4332. }
  4333. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  4334. $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  4335. if (strlen($server_public_host_key) < 4) {
  4336. return false;
  4337. }
  4338. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  4339. $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  4340. if (strlen($server_public_host_key) < 4) {
  4341. return false;
  4342. }
  4343. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  4344. $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  4345. /* The value for 'dss_signature_blob' is encoded as a string containing
  4346. r, followed by s (which are 160-bit integers, without lengths or
  4347. padding, unsigned, and in network byte order). */
  4348. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  4349. if ($temp['length'] != 40) {
  4350. user_error('Invalid signature');
  4351. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  4352. }
  4353. $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
  4354. $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
  4355. switch (true) {
  4356. case $r->equals($zero):
  4357. case $r->compare($q) >= 0:
  4358. case $s->equals($zero):
  4359. case $s->compare($q) >= 0:
  4360. user_error('Invalid signature');
  4361. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  4362. }
  4363. $w = $s->modInverse($q);
  4364. $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
  4365. list(, $u1) = $u1->divide($q);
  4366. $u2 = $w->multiply($r);
  4367. list(, $u2) = $u2->divide($q);
  4368. $g = $g->modPow($u1, $p);
  4369. $y = $y->modPow($u2, $p);
  4370. $v = $g->multiply($y);
  4371. list(, $v) = $v->divide($p);
  4372. list(, $v) = $v->divide($q);
  4373. if (!$v->equals($r)) {
  4374. user_error('Bad server signature');
  4375. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4376. }
  4377. break;
  4378. case 'ssh-rsa':
  4379. case 'rsa-sha2-256':
  4380. case 'rsa-sha2-512':
  4381. if (strlen($server_public_host_key) < 4) {
  4382. return false;
  4383. }
  4384. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  4385. $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
  4386. if (strlen($server_public_host_key) < 4) {
  4387. return false;
  4388. }
  4389. $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
  4390. $rawN = $this->_string_shift($server_public_host_key, $temp['length']);
  4391. $n = new Math_BigInteger($rawN, -256);
  4392. $nLength = strlen(ltrim($rawN, "\0"));
  4393. /*
  4394. if (strlen($signature) < 4) {
  4395. return false;
  4396. }
  4397. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  4398. $signature = $this->_string_shift($signature, $temp['length']);
  4399. if (!class_exists('Crypt_RSA')) {
  4400. include_once 'Crypt/RSA.php';
  4401. }
  4402. $rsa = new Crypt_RSA();
  4403. switch ($this->signature_format) {
  4404. case 'rsa-sha2-512':
  4405. $hash = 'sha512';
  4406. break;
  4407. case 'rsa-sha2-256':
  4408. $hash = 'sha256';
  4409. break;
  4410. //case 'ssh-rsa':
  4411. default:
  4412. $hash = 'sha1';
  4413. }
  4414. $rsa->setHash($hash);
  4415. $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  4416. $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
  4417. if (!$rsa->verify($this->exchange_hash, $signature)) {
  4418. user_error('Bad server signature');
  4419. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4420. }
  4421. */
  4422. if (strlen($signature) < 4) {
  4423. return false;
  4424. }
  4425. $temp = unpack('Nlength', $this->_string_shift($signature, 4));
  4426. $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
  4427. // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
  4428. // following URL:
  4429. // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
  4430. // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
  4431. if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
  4432. user_error('Invalid signature');
  4433. return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
  4434. }
  4435. $s = $s->modPow($e, $n);
  4436. $s = $s->toBytes();
  4437. switch ($this->signature_format) {
  4438. case 'rsa-sha2-512':
  4439. $hash = 'sha512';
  4440. break;
  4441. case 'rsa-sha2-256':
  4442. $hash = 'sha256';
  4443. break;
  4444. //case 'ssh-rsa':
  4445. default:
  4446. $hash = 'sha1';
  4447. }
  4448. $hashObj = new Crypt_Hash($hash);
  4449. switch ($this->signature_format) {
  4450. case 'rsa-sha2-512':
  4451. $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash));
  4452. break;
  4453. case 'rsa-sha2-256':
  4454. $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash));
  4455. break;
  4456. //case 'ssh-rsa':
  4457. default:
  4458. $hash = 'sha1';
  4459. $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
  4460. }
  4461. $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;
  4462. if ($s != $h) {
  4463. user_error('Bad server signature');
  4464. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4465. }
  4466. break;
  4467. default:
  4468. user_error('Unsupported signature format');
  4469. return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
  4470. }
  4471. return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
  4472. }
  4473. /**
  4474. * Returns the exit status of an SSH command or false.
  4475. *
  4476. * @return false|int
  4477. * @access public
  4478. */
  4479. function getExitStatus()
  4480. {
  4481. if (is_null($this->exit_status)) {
  4482. return false;
  4483. }
  4484. return $this->exit_status;
  4485. }
  4486. /**
  4487. * Returns the number of columns for the terminal window size.
  4488. *
  4489. * @return int
  4490. * @access public
  4491. */
  4492. function getWindowColumns()
  4493. {
  4494. return $this->windowColumns;
  4495. }
  4496. /**
  4497. * Returns the number of rows for the terminal window size.
  4498. *
  4499. * @return int
  4500. * @access public
  4501. */
  4502. function getWindowRows()
  4503. {
  4504. return $this->windowRows;
  4505. }
  4506. /**
  4507. * Sets the number of columns for the terminal window size.
  4508. *
  4509. * @param int $value
  4510. * @access public
  4511. */
  4512. function setWindowColumns($value)
  4513. {
  4514. $this->windowColumns = $value;
  4515. }
  4516. /**
  4517. * Sets the number of rows for the terminal window size.
  4518. *
  4519. * @param int $value
  4520. * @access public
  4521. */
  4522. function setWindowRows($value)
  4523. {
  4524. $this->windowRows = $value;
  4525. }
  4526. /**
  4527. * Sets the number of columns and rows for the terminal window size.
  4528. *
  4529. * @param int $columns
  4530. * @param int $rows
  4531. * @access public
  4532. */
  4533. function setWindowSize($columns = 80, $rows = 24)
  4534. {
  4535. $this->windowColumns = $columns;
  4536. $this->windowRows = $rows;
  4537. }
  4538. /**
  4539. * Update packet types in log history
  4540. *
  4541. * @param string $old
  4542. * @param string $new
  4543. * @access private
  4544. */
  4545. function _updateLogHistory($old, $new)
  4546. {
  4547. if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
  4548. $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
  4549. $old,
  4550. $new,
  4551. $this->message_number_log[count($this->message_number_log) - 1]
  4552. );
  4553. }
  4554. }
  4555. }