SSH2.php 163 KB

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