SSH2.php 141 KB

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