ssh.js 160 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374
  1. // TODO: * Automatic re-key every (configurable) n bytes or length of time
  2. // - RFC suggests every 1GB of transmitted data or 1 hour, whichever
  3. // comes sooner
  4. // * Filter control codes from strings
  5. // (as per http://tools.ietf.org/html/rfc4251#section-9.2)
  6. var crypto = require('crypto');
  7. var zlib = require('zlib');
  8. var TransformStream = require('stream').Transform;
  9. var inherits = require('util').inherits;
  10. var inspect = require('util').inspect;
  11. var StreamSearch = require('streamsearch');
  12. var Ber = require('asn1').Ber;
  13. var readUInt32BE = require('./buffer-helpers').readUInt32BE;
  14. var writeUInt32BE = require('./buffer-helpers').writeUInt32BE;
  15. var consts = require('./constants');
  16. var utils = require('./utils');
  17. var iv_inc = utils.iv_inc;
  18. var readString = utils.readString;
  19. var readInt = utils.readInt;
  20. var DSASigBERToBare = utils.DSASigBERToBare;
  21. var ECDSASigASN1ToSSH = utils.ECDSASigASN1ToSSH;
  22. var sigSSHToASN1 = utils.sigSSHToASN1;
  23. var parseDERKey = require('./keyParser').parseDERKey;
  24. var CIPHER_INFO = consts.CIPHER_INFO;
  25. var HMAC_INFO = consts.HMAC_INFO;
  26. var MESSAGE = consts.MESSAGE;
  27. var DYNAMIC_KEXDH_MESSAGE = consts.DYNAMIC_KEXDH_MESSAGE;
  28. var KEXDH_MESSAGE = consts.KEXDH_MESSAGE;
  29. var ALGORITHMS = consts.ALGORITHMS;
  30. var DISCONNECT_REASON = consts.DISCONNECT_REASON;
  31. var CHANNEL_OPEN_FAILURE = consts.CHANNEL_OPEN_FAILURE;
  32. var SSH_TO_OPENSSL = consts.SSH_TO_OPENSSL;
  33. var TERMINAL_MODE = consts.TERMINAL_MODE;
  34. var SIGNALS = consts.SIGNALS;
  35. var EDDSA_SUPPORTED = consts.EDDSA_SUPPORTED;
  36. var CURVE25519_SUPPORTED = consts.CURVE25519_SUPPORTED;
  37. var BUGS = consts.BUGS;
  38. var BUGGY_IMPLS = consts.BUGGY_IMPLS;
  39. var BUGGY_IMPLS_LEN = BUGGY_IMPLS.length;
  40. var MODULE_VER = require('../package.json').version;
  41. var I = 0;
  42. var IN_INIT = I++;
  43. var IN_GREETING = I++;
  44. var IN_HEADER = I++;
  45. var IN_PACKETBEFORE = I++;
  46. var IN_PACKET = I++;
  47. var IN_PACKETDATA = I++;
  48. var IN_PACKETDATAVERIFY = I++;
  49. var IN_PACKETDATAAFTER = I++;
  50. var OUT_INIT = I++;
  51. var OUT_READY = I++;
  52. var OUT_REKEYING = I++;
  53. var MAX_SEQNO = 4294967295;
  54. var MAX_PACKET_SIZE = 35000;
  55. var MAX_PACKETS_REKEYING = 50;
  56. var EXP_TYPE_HEADER = 0;
  57. var EXP_TYPE_LF = 1;
  58. var EXP_TYPE_BYTES = 2; // Waits until n bytes have been seen
  59. var Z_PARTIAL_FLUSH = zlib.Z_PARTIAL_FLUSH;
  60. var ZLIB_OPTS = { flush: Z_PARTIAL_FLUSH };
  61. var RE_NULL = /\x00/g;
  62. var IDENT_PREFIX_BUFFER = Buffer.from('SSH-');
  63. var EMPTY_BUFFER = Buffer.allocUnsafe(0);
  64. var HMAC_COMPUTE = Buffer.allocUnsafe(9);
  65. var PING_PACKET = Buffer.from([
  66. MESSAGE.GLOBAL_REQUEST,
  67. // "keepalive@openssh.com"
  68. 0, 0, 0, 21,
  69. 107, 101, 101, 112, 97, 108, 105, 118, 101, 64, 111, 112, 101, 110, 115,
  70. 115, 104, 46, 99, 111, 109,
  71. // Request a reply
  72. 1
  73. ]);
  74. var NEWKEYS_PACKET = Buffer.from([MESSAGE.NEWKEYS]);
  75. var USERAUTH_SUCCESS_PACKET = Buffer.from([MESSAGE.USERAUTH_SUCCESS]);
  76. var REQUEST_SUCCESS_PACKET = Buffer.from([MESSAGE.REQUEST_SUCCESS]);
  77. var REQUEST_FAILURE_PACKET = Buffer.from([MESSAGE.REQUEST_FAILURE]);
  78. var NO_TERMINAL_MODES_BUFFER = Buffer.from([TERMINAL_MODE.TTY_OP_END]);
  79. var KEXDH_GEX_REQ_PACKET = Buffer.from([
  80. MESSAGE.KEXDH_GEX_REQUEST,
  81. // Minimal size in bits of an acceptable group
  82. 0, 0, 4, 0, // 1024, modp2
  83. // Preferred size in bits of the group the server will send
  84. 0, 0, 16, 0, // 4096, modp16
  85. // Maximal size in bits of an acceptable group
  86. 0, 0, 32, 0 // 8192, modp18
  87. ]);
  88. function DEBUG_NOOP(msg) {}
  89. function SSH2Stream(cfg) {
  90. if (typeof cfg !== 'object' || cfg === null)
  91. cfg = {};
  92. TransformStream.call(this, {
  93. highWaterMark: (typeof cfg.highWaterMark === 'number'
  94. ? cfg.highWaterMark
  95. : 32 * 1024)
  96. });
  97. this._needContinue = false;
  98. this.bytesSent = this.bytesReceived = 0;
  99. this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP);
  100. this.server = (cfg.server === true);
  101. this.maxPacketSize = (typeof cfg.maxPacketSize === 'number'
  102. ? cfg.maxPacketSize
  103. : MAX_PACKET_SIZE);
  104. // Bitmap that indicates any bugs the remote side has. This is determined
  105. // by the reported software version.
  106. this.remoteBugs = 0;
  107. if (this.server) {
  108. // TODO: Remove when we support group exchange for server implementation
  109. this.remoteBugs = BUGS.BAD_DHGEX;
  110. }
  111. this.readable = true;
  112. var self = this;
  113. var hostKeys = cfg.hostKeys;
  114. if (this.server && (typeof hostKeys !== 'object' || hostKeys === null))
  115. throw new Error('hostKeys must be an object keyed on host key type');
  116. this.config = {
  117. // Server
  118. hostKeys: hostKeys, // All keys supported by server
  119. // Client/Server
  120. ident: 'SSH-2.0-'
  121. + (cfg.ident
  122. || ('ssh2js' + MODULE_VER + (this.server ? 'srv' : ''))),
  123. algorithms: {
  124. kex: ALGORITHMS.KEX,
  125. kexBuf: ALGORITHMS.KEX_BUF,
  126. serverHostKey: ALGORITHMS.SERVER_HOST_KEY,
  127. serverHostKeyBuf: ALGORITHMS.SERVER_HOST_KEY_BUF,
  128. cipher: ALGORITHMS.CIPHER,
  129. cipherBuf: ALGORITHMS.CIPHER_BUF,
  130. hmac: ALGORITHMS.HMAC,
  131. hmacBuf: ALGORITHMS.HMAC_BUF,
  132. compress: ALGORITHMS.COMPRESS,
  133. compressBuf: ALGORITHMS.COMPRESS_BUF
  134. }
  135. };
  136. // RFC 4253 states the identification string must not contain NULL
  137. this.config.ident.replace(RE_NULL, '');
  138. if (this.config.ident.length + 2 /* Account for "\r\n" */ > 255)
  139. throw new Error('ident too long');
  140. if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) {
  141. var algos = cfg.algorithms;
  142. if (Array.isArray(algos.kex) && algos.kex.length > 0) {
  143. this.config.algorithms.kex = algos.kex;
  144. if (!Buffer.isBuffer(algos.kexBuf))
  145. algos.kexBuf = Buffer.from(algos.kex.join(','), 'ascii');
  146. this.config.algorithms.kexBuf = algos.kexBuf;
  147. }
  148. if (Array.isArray(algos.serverHostKey) && algos.serverHostKey.length > 0) {
  149. this.config.algorithms.serverHostKey = algos.serverHostKey;
  150. if (!Buffer.isBuffer(algos.serverHostKeyBuf)) {
  151. algos.serverHostKeyBuf = Buffer.from(algos.serverHostKey.join(','),
  152. 'ascii');
  153. }
  154. this.config.algorithms.serverHostKeyBuf = algos.serverHostKeyBuf;
  155. }
  156. if (Array.isArray(algos.cipher) && algos.cipher.length > 0) {
  157. this.config.algorithms.cipher = algos.cipher;
  158. if (!Buffer.isBuffer(algos.cipherBuf))
  159. algos.cipherBuf = Buffer.from(algos.cipher.join(','), 'ascii');
  160. this.config.algorithms.cipherBuf = algos.cipherBuf;
  161. }
  162. if (Array.isArray(algos.hmac) && algos.hmac.length > 0) {
  163. this.config.algorithms.hmac = algos.hmac;
  164. if (!Buffer.isBuffer(algos.hmacBuf))
  165. algos.hmacBuf = Buffer.from(algos.hmac.join(','), 'ascii');
  166. this.config.algorithms.hmacBuf = algos.hmacBuf;
  167. }
  168. if (Array.isArray(algos.compress) && algos.compress.length > 0) {
  169. this.config.algorithms.compress = algos.compress;
  170. if (!Buffer.isBuffer(algos.compressBuf))
  171. algos.compressBuf = Buffer.from(algos.compress.join(','), 'ascii');
  172. this.config.algorithms.compressBuf = algos.compressBuf;
  173. }
  174. }
  175. this.reset(true);
  176. // Common events
  177. this.on('end', function() {
  178. // Let GC collect any Buffers we were previously storing
  179. self.readable = false;
  180. self._state = undefined;
  181. self.reset();
  182. self._state.outgoing.bufSeqno = undefined;
  183. });
  184. this.on('DISCONNECT', function(reason, code, desc, lang) {
  185. onDISCONNECT(self, reason, code, desc, lang);
  186. });
  187. this.on('KEXINIT', function(init, firstFollows) {
  188. onKEXINIT(self, init, firstFollows);
  189. });
  190. this.on('NEWKEYS', function() { onNEWKEYS(self); });
  191. if (this.server) {
  192. // Server-specific events
  193. this.on('KEXDH_INIT', function(e) { onKEXDH_INIT(self, e); });
  194. } else {
  195. // Client-specific events
  196. this.on('KEXDH_REPLY', function(info) { onKEXDH_REPLY(self, info); })
  197. .on('KEXDH_GEX_GROUP',
  198. function(prime, gen) { onKEXDH_GEX_GROUP(self, prime, gen); });
  199. }
  200. if (this.server) {
  201. // Greeting displayed before the ssh identification string is sent, this is
  202. // usually ignored by most clients
  203. if (typeof cfg.greeting === 'string' && cfg.greeting.length) {
  204. if (cfg.greeting.slice(-2) === '\r\n')
  205. this.push(cfg.greeting);
  206. else
  207. this.push(cfg.greeting + '\r\n');
  208. }
  209. // Banner shown after the handshake completes, but before user
  210. // authentication begins
  211. if (typeof cfg.banner === 'string' && cfg.banner.length) {
  212. if (cfg.banner.slice(-2) === '\r\n')
  213. this.banner = cfg.banner;
  214. else
  215. this.banner = cfg.banner + '\r\n';
  216. }
  217. }
  218. this.debug('DEBUG: Local ident: ' + inspect(this.config.ident));
  219. this.push(this.config.ident + '\r\n');
  220. this._state.incoming.expectedPacket = 'KEXINIT';
  221. }
  222. inherits(SSH2Stream, TransformStream);
  223. SSH2Stream.prototype.__read = TransformStream.prototype._read;
  224. SSH2Stream.prototype._read = function(n) {
  225. if (this._needContinue) {
  226. this._needContinue = false;
  227. this.emit('continue');
  228. }
  229. return this.__read(n);
  230. };
  231. SSH2Stream.prototype.__push = TransformStream.prototype.push;
  232. SSH2Stream.prototype.push = function(chunk, encoding) {
  233. var ret = this.__push(chunk, encoding);
  234. this._needContinue = (ret === false);
  235. return ret;
  236. };
  237. SSH2Stream.prototype._cleanup = function(callback) {
  238. this.reset();
  239. this.debug('DEBUG: Parser: Malformed packet');
  240. callback && callback(new Error('Malformed packet'));
  241. };
  242. SSH2Stream.prototype._transform = function(chunk, encoding, callback, decomp) {
  243. var skipDecrypt = false;
  244. var decryptAuthMode = false;
  245. var state = this._state;
  246. var instate = state.incoming;
  247. var outstate = state.outgoing;
  248. var expect = instate.expect;
  249. var decrypt = instate.decrypt;
  250. var decompress = instate.decompress;
  251. var chlen = chunk.length;
  252. var chleft = 0;
  253. var debug = this.debug;
  254. var self = this;
  255. var i = 0;
  256. var p = i;
  257. var blockLen;
  258. var buffer;
  259. var buf;
  260. var r;
  261. this.bytesReceived += chlen;
  262. while (true) {
  263. if (expect.type !== undefined) {
  264. if (i >= chlen)
  265. break;
  266. if (expect.type === EXP_TYPE_BYTES) {
  267. chleft = (chlen - i);
  268. var pktLeft = (expect.buf.length - expect.ptr);
  269. if (pktLeft <= chleft) {
  270. chunk.copy(expect.buf, expect.ptr, i, i + pktLeft);
  271. i += pktLeft;
  272. buffer = expect.buf;
  273. expect.buf = undefined;
  274. expect.ptr = 0;
  275. expect.type = undefined;
  276. } else {
  277. chunk.copy(expect.buf, expect.ptr, i);
  278. expect.ptr += chleft;
  279. i += chleft;
  280. }
  281. continue;
  282. } else if (expect.type === EXP_TYPE_HEADER) {
  283. i += instate.search.push(chunk);
  284. if (expect.type !== undefined)
  285. continue;
  286. } else if (expect.type === EXP_TYPE_LF) {
  287. if (++expect.ptr + 4 /* Account for "SSH-" */ > 255) {
  288. this.reset();
  289. debug('DEBUG: Parser: Identification string exceeded 255 characters');
  290. return callback(new Error('Max identification string size exceeded'));
  291. }
  292. if (chunk[i] === 0x0A) {
  293. expect.type = undefined;
  294. if (p < i) {
  295. if (expect.buf === undefined)
  296. expect.buf = chunk.toString('ascii', p, i);
  297. else
  298. expect.buf += chunk.toString('ascii', p, i);
  299. }
  300. buffer = expect.buf;
  301. expect.buf = undefined;
  302. ++i;
  303. } else {
  304. if (++i === chlen && p < i) {
  305. if (expect.buf === undefined)
  306. expect.buf = chunk.toString('ascii', p, i);
  307. else
  308. expect.buf += chunk.toString('ascii', p, i);
  309. }
  310. continue;
  311. }
  312. }
  313. }
  314. if (instate.status === IN_INIT) {
  315. if (!this.readable)
  316. return callback();
  317. if (this.server) {
  318. // Retrieve what should be the start of the protocol version exchange
  319. if (!buffer) {
  320. debug('DEBUG: Parser: IN_INIT (waiting for identification begin)');
  321. expectData(this, EXP_TYPE_BYTES, 4);
  322. } else {
  323. if (buffer[0] === 0x53 // S
  324. && buffer[1] === 0x53 // S
  325. && buffer[2] === 0x48 // H
  326. && buffer[3] === 0x2D) { // -
  327. instate.status = IN_GREETING;
  328. debug('DEBUG: Parser: IN_INIT (waiting for rest of identification)');
  329. } else {
  330. this.reset();
  331. debug('DEBUG: Parser: Bad identification start');
  332. return callback(new Error('Bad identification start'));
  333. }
  334. }
  335. } else {
  336. debug('DEBUG: Parser: IN_INIT');
  337. // Retrieve any bytes that may come before the protocol version exchange
  338. var ss = instate.search = new StreamSearch(IDENT_PREFIX_BUFFER);
  339. ss.on('info', function onInfo(matched, data, start, end) {
  340. if (data) {
  341. if (instate.greeting === undefined)
  342. instate.greeting = data.toString('binary', start, end);
  343. else
  344. instate.greeting += data.toString('binary', start, end);
  345. }
  346. if (matched) {
  347. expect.type = undefined;
  348. instate.search.removeListener('info', onInfo);
  349. }
  350. });
  351. ss.maxMatches = 1;
  352. expectData(this, EXP_TYPE_HEADER);
  353. instate.status = IN_GREETING;
  354. }
  355. } else if (instate.status === IN_GREETING) {
  356. debug('DEBUG: Parser: IN_GREETING');
  357. instate.search = undefined;
  358. // Retrieve the identification bytes after the "SSH-" header
  359. p = i;
  360. expectData(this, EXP_TYPE_LF);
  361. instate.status = IN_HEADER;
  362. } else if (instate.status === IN_HEADER) {
  363. debug('DEBUG: Parser: IN_HEADER');
  364. if (buffer.charCodeAt(buffer.length - 1) === 13)
  365. buffer = buffer.slice(0, -1);
  366. var idxDash = buffer.indexOf('-');
  367. var idxSpace = buffer.indexOf(' ');
  368. var header = {
  369. // RFC says greeting SHOULD be utf8
  370. greeting: instate.greeting,
  371. identRaw: 'SSH-' + buffer,
  372. versions: {
  373. protocol: buffer.substr(0, idxDash),
  374. software: (idxSpace === -1
  375. ? buffer.substring(idxDash + 1)
  376. : buffer.substring(idxDash + 1, idxSpace))
  377. },
  378. comments: (idxSpace > -1 ? buffer.substring(idxSpace + 1) : undefined)
  379. };
  380. instate.greeting = undefined;
  381. if (header.versions.protocol !== '1.99'
  382. && header.versions.protocol !== '2.0') {
  383. this.reset();
  384. debug('DEBUG: Parser: protocol version not supported: '
  385. + header.versions.protocol);
  386. return callback(new Error('Protocol version not supported'));
  387. } else
  388. this.emit('header', header);
  389. if (instate.status === IN_INIT) {
  390. // We reset from an event handler, possibly due to an unsupported SSH
  391. // protocol version?
  392. return;
  393. }
  394. var identRaw = header.identRaw;
  395. var software = header.versions.software;
  396. this.debug('DEBUG: Remote ident: ' + inspect(identRaw));
  397. for (var j = 0, rule; j < BUGGY_IMPLS_LEN; ++j) {
  398. rule = BUGGY_IMPLS[j];
  399. if (typeof rule[0] === 'string') {
  400. if (software === rule[0])
  401. this.remoteBugs |= rule[1];
  402. } else if (rule[0].test(software))
  403. this.remoteBugs |= rule[1];
  404. }
  405. instate.identRaw = identRaw;
  406. // Adjust bytesReceived first otherwise it will have an incorrectly larger
  407. // total when we call back into this function after completing KEXINIT
  408. this.bytesReceived -= (chlen - i);
  409. KEXINIT(this, function() {
  410. if (i === chlen)
  411. callback();
  412. else
  413. self._transform(chunk.slice(i), encoding, callback);
  414. });
  415. instate.status = IN_PACKETBEFORE;
  416. return;
  417. } else if (instate.status === IN_PACKETBEFORE) {
  418. blockLen = (decrypt.instance ? decrypt.info.blockLen : 8);
  419. debug('DEBUG: Parser: IN_PACKETBEFORE (expecting ' + blockLen + ')');
  420. // Wait for the right number of bytes so we can determine the incoming
  421. // packet length
  422. expectData(this, EXP_TYPE_BYTES, blockLen, decrypt.buf);
  423. instate.status = IN_PACKET;
  424. } else if (instate.status === IN_PACKET) {
  425. debug('DEBUG: Parser: IN_PACKET');
  426. if (decrypt.instance) {
  427. decryptAuthMode = (decrypt.info.authLen > 0);
  428. if (!decryptAuthMode)
  429. buffer = decryptData(this, buffer);
  430. blockLen = decrypt.info.blockLen;
  431. } else {
  432. decryptAuthMode = false;
  433. blockLen = 8;
  434. }
  435. r = readInt(buffer, 0, this, callback);
  436. if (r === false)
  437. return;
  438. var hmacInfo = instate.hmac.info;
  439. var macSize;
  440. if (hmacInfo)
  441. macSize = hmacInfo.actualLen;
  442. else
  443. macSize = 0;
  444. var fullPacketLen = r + 4 + macSize;
  445. var maxPayloadLen = this.maxPacketSize;
  446. if (decompress.instance) {
  447. // Account for compressed payloads
  448. // This formula is taken from dropbear which derives it from zlib's
  449. // documentation. Explanation from dropbear:
  450. /* For exact details see http://www.zlib.net/zlib_tech.html
  451. * 5 bytes per 16kB block, plus 6 bytes for the stream.
  452. * We might allocate 5 unnecessary bytes here if it's an
  453. * exact multiple. */
  454. maxPayloadLen += (((this.maxPacketSize / 16384) + 1) * 5 + 6);
  455. }
  456. if (r > maxPayloadLen
  457. // TODO: Change 16 to "MAX(16, decrypt.info.blockLen)" when/if SSH2
  458. // adopts 512-bit ciphers
  459. || fullPacketLen < (16 + macSize)
  460. || ((r + (decryptAuthMode ? 0 : 4)) % blockLen) !== 0) {
  461. this.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
  462. debug('DEBUG: Parser: Bad packet length (' + fullPacketLen + ')');
  463. return callback(new Error('Bad packet length'));
  464. }
  465. instate.pktLen = r;
  466. var remainLen = instate.pktLen + 4 - blockLen;
  467. if (decryptAuthMode) {
  468. decrypt.instance.setAAD(buffer.slice(0, 4));
  469. debug('DEBUG: Parser: pktLen:'
  470. + instate.pktLen
  471. + ',remainLen:'
  472. + remainLen);
  473. } else {
  474. instate.padLen = buffer[4];
  475. debug('DEBUG: Parser: pktLen:'
  476. + instate.pktLen
  477. + ',padLen:'
  478. + instate.padLen
  479. + ',remainLen:'
  480. + remainLen);
  481. }
  482. if (remainLen > 0) {
  483. if (decryptAuthMode)
  484. instate.pktExtra = buffer.slice(4);
  485. else
  486. instate.pktExtra = buffer.slice(5);
  487. // Grab the rest of the packet
  488. expectData(this, EXP_TYPE_BYTES, remainLen);
  489. instate.status = IN_PACKETDATA;
  490. } else if (remainLen < 0)
  491. instate.status = IN_PACKETBEFORE;
  492. else {
  493. // Entire message fit into one block
  494. skipDecrypt = true;
  495. instate.status = IN_PACKETDATA;
  496. continue;
  497. }
  498. } else if (instate.status === IN_PACKETDATA) {
  499. debug('DEBUG: Parser: IN_PACKETDATA');
  500. if (decrypt.instance) {
  501. decryptAuthMode = (decrypt.info.authLen > 0);
  502. if (!skipDecrypt) {
  503. if (!decryptAuthMode)
  504. buffer = decryptData(this, buffer);
  505. } else {
  506. skipDecrypt = false;
  507. }
  508. } else {
  509. decryptAuthMode = false;
  510. skipDecrypt = false;
  511. }
  512. var padStart = instate.pktLen - instate.padLen - 1;
  513. // TODO: Allocate a Buffer once that is slightly larger than maxPacketSize
  514. // (to accommodate for packet length field and MAC) and re-use that
  515. // instead
  516. if (instate.pktExtra) {
  517. buf = Buffer.allocUnsafe(instate.pktExtra.length + buffer.length);
  518. instate.pktExtra.copy(buf);
  519. buffer.copy(buf, instate.pktExtra.length);
  520. instate.payload = buf.slice(0, padStart);
  521. } else {
  522. // Entire message fit into one block
  523. if (decryptAuthMode)
  524. buf = buffer.slice(4);
  525. else
  526. buf = buffer.slice(5);
  527. instate.payload = buffer.slice(5, 5 + padStart);
  528. }
  529. if (instate.hmac.info !== undefined) {
  530. // Wait for hmac hash
  531. var inHMACSize = decrypt.info.authLen || instate.hmac.info.actualLen;
  532. debug('DEBUG: Parser: HMAC size:' + inHMACSize);
  533. expectData(this, EXP_TYPE_BYTES, inHMACSize, instate.hmac.buf);
  534. instate.status = IN_PACKETDATAVERIFY;
  535. instate.packet = buf;
  536. } else
  537. instate.status = IN_PACKETDATAAFTER;
  538. instate.pktExtra = undefined;
  539. buf = undefined;
  540. } else if (instate.status === IN_PACKETDATAVERIFY) {
  541. debug('DEBUG: Parser: IN_PACKETDATAVERIFY');
  542. // Verify packet data integrity
  543. if (hmacVerify(this, buffer)) {
  544. debug('DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)');
  545. instate.status = IN_PACKETDATAAFTER;
  546. instate.packet = undefined;
  547. } else {
  548. this.reset();
  549. debug('DEBUG: Parser: IN_PACKETDATAVERIFY (Invalid HMAC)');
  550. return callback(new Error('Invalid HMAC'));
  551. }
  552. } else if (instate.status === IN_PACKETDATAAFTER) {
  553. if (decompress.instance) {
  554. if (!decomp) {
  555. debug('DEBUG: Parser: Decompressing');
  556. decompress.instance.write(instate.payload);
  557. var decompBuf = [];
  558. var decompBufLen = 0;
  559. decompress.instance.on('readable', function() {
  560. var buf;
  561. while (buf = this.read()) {
  562. decompBuf.push(buf);
  563. decompBufLen += buf.length;
  564. }
  565. }).flush(Z_PARTIAL_FLUSH, function() {
  566. decompress.instance.removeAllListeners('readable');
  567. if (decompBuf.length === 1)
  568. instate.payload = decompBuf[0];
  569. else
  570. instate.payload = Buffer.concat(decompBuf, decompBufLen);
  571. decompBuf = null;
  572. var nextSlice;
  573. if (i === chlen)
  574. nextSlice = EMPTY_BUFFER; // Avoid slicing a zero-length buffer
  575. else
  576. nextSlice = chunk.slice(i);
  577. self._transform(nextSlice, encoding, callback, true);
  578. });
  579. return;
  580. } else {
  581. // Make sure we reset this after this first time in the loop,
  582. // otherwise we could end up trying to interpret as-is another
  583. // compressed packet that is within the same chunk
  584. decomp = false;
  585. }
  586. }
  587. this.emit('packet');
  588. var ptype = instate.payload[0];
  589. if (debug !== DEBUG_NOOP) {
  590. var msgPacket = 'DEBUG: Parser: IN_PACKETDATAAFTER, packet: ';
  591. var authMethod = state.authsQueue[0];
  592. var msgPktType = null;
  593. if (outstate.status === OUT_REKEYING
  594. && !(ptype <= 4 || (ptype >= 20 && ptype <= 49)))
  595. msgPacket += '(enqueued) ';
  596. if (ptype === MESSAGE.KEXDH_INIT) {
  597. switch (state.kex.type) {
  598. case 'group':
  599. msgPktType = 'KEXDH_INIT';
  600. break;
  601. case 'groupex':
  602. msgPktType = 'KEXDH_GEX_REQUEST';
  603. break;
  604. default:
  605. msgPktType = 'KEXECDH_INIT';
  606. }
  607. } else if (ptype === MESSAGE.KEXDH_REPLY) {
  608. switch (state.kex.type) {
  609. case 'group':
  610. msgPktType = 'KEXDH_REPLY';
  611. break;
  612. case 'groupex':
  613. msgPktType = 'KEXDH_GEX_GROUP';
  614. break;
  615. default:
  616. msgPktType = 'KEXECDH_REPLY';
  617. }
  618. } else if (ptype === MESSAGE.KEXDH_GEX_GROUP) {
  619. msgPktType = 'KEXDH_GEX_GROUP';
  620. } else if (ptype === MESSAGE.KEXDH_GEX_REPLY) {
  621. msgPktType = 'KEXDH_GEX_REPLY';
  622. } else if (ptype === 60) {
  623. if (authMethod === 'password')
  624. msgPktType = 'USERAUTH_PASSWD_CHANGEREQ';
  625. else if (authMethod === 'keyboard-interactive')
  626. msgPktType = 'USERAUTH_INFO_REQUEST';
  627. else if (authMethod === 'publickey')
  628. msgPktType = 'USERAUTH_PK_OK';
  629. else
  630. msgPktType = 'UNKNOWN PACKET 60';
  631. } else if (ptype === 61) {
  632. if (authMethod === 'keyboard-interactive')
  633. msgPktType = 'USERAUTH_INFO_RESPONSE';
  634. else
  635. msgPktType = 'UNKNOWN PACKET 61';
  636. }
  637. if (msgPktType === null)
  638. msgPktType = MESSAGE[ptype];
  639. // Don't write debug output for messages we custom make in parsePacket()
  640. if (ptype !== MESSAGE.CHANNEL_OPEN
  641. && ptype !== MESSAGE.CHANNEL_REQUEST
  642. && ptype !== MESSAGE.CHANNEL_SUCCESS
  643. && ptype !== MESSAGE.CHANNEL_FAILURE
  644. && ptype !== MESSAGE.CHANNEL_EOF
  645. && ptype !== MESSAGE.CHANNEL_CLOSE
  646. && ptype !== MESSAGE.CHANNEL_DATA
  647. && ptype !== MESSAGE.CHANNEL_EXTENDED_DATA
  648. && ptype !== MESSAGE.CHANNEL_WINDOW_ADJUST
  649. && ptype !== MESSAGE.DISCONNECT
  650. && ptype !== MESSAGE.USERAUTH_REQUEST
  651. && ptype !== MESSAGE.GLOBAL_REQUEST)
  652. debug(msgPacket + msgPktType);
  653. }
  654. // Only parse packet if we are not re-keying or the packet is not a
  655. // transport layer packet needed for re-keying
  656. if (outstate.status === OUT_READY
  657. || ptype <= 4
  658. || (ptype >= 20 && ptype <= 49)) {
  659. if (parsePacket(this, callback) === false)
  660. return;
  661. if (instate.status === IN_INIT) {
  662. // We were reset due to some error/disagreement ?
  663. return;
  664. }
  665. } else if (outstate.status === OUT_REKEYING) {
  666. if (instate.rekeyQueue.length === MAX_PACKETS_REKEYING) {
  667. debug('DEBUG: Parser: Max incoming re-key queue length reached');
  668. this.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
  669. return callback(
  670. new Error('Incoming re-key queue length limit reached')
  671. );
  672. }
  673. // Make sure to record the sequence number in case we need it later on
  674. // when we drain the queue (e.g. unknown packet)
  675. var seqno = instate.seqno;
  676. if (++instate.seqno > MAX_SEQNO)
  677. instate.seqno = 0;
  678. instate.rekeyQueue.push([seqno, instate.payload]);
  679. }
  680. instate.status = IN_PACKETBEFORE;
  681. instate.payload = undefined;
  682. }
  683. if (buffer !== undefined)
  684. buffer = undefined;
  685. }
  686. callback();
  687. };
  688. SSH2Stream.prototype.reset = function(noend) {
  689. if (this._state) {
  690. var state = this._state;
  691. state.incoming.status = IN_INIT;
  692. state.outgoing.status = OUT_INIT;
  693. } else {
  694. this._state = {
  695. authsQueue: [],
  696. hostkeyFormat: undefined,
  697. kex: undefined,
  698. incoming: {
  699. status: IN_INIT,
  700. expectedPacket: undefined,
  701. search: undefined,
  702. greeting: undefined,
  703. seqno: 0,
  704. pktLen: undefined,
  705. padLen: undefined,
  706. pktExtra: undefined,
  707. payload: undefined,
  708. packet: undefined,
  709. kexinit: undefined,
  710. identRaw: undefined,
  711. rekeyQueue: [],
  712. ignoreNext: false,
  713. expect: {
  714. amount: undefined,
  715. type: undefined,
  716. ptr: 0,
  717. buf: undefined
  718. },
  719. decrypt: {
  720. instance: false,
  721. info: undefined,
  722. iv: undefined,
  723. key: undefined,
  724. buf: undefined,
  725. type: undefined
  726. },
  727. hmac: {
  728. info: undefined,
  729. key: undefined,
  730. buf: undefined,
  731. type: false
  732. },
  733. decompress: {
  734. instance: false,
  735. type: false
  736. }
  737. },
  738. outgoing: {
  739. status: OUT_INIT,
  740. seqno: 0,
  741. bufSeqno: Buffer.allocUnsafe(4),
  742. rekeyQueue: [],
  743. kexinit: undefined,
  744. kexsecret: undefined,
  745. pubkey: undefined,
  746. exchangeHash: undefined,
  747. sessionId: undefined,
  748. sentNEWKEYS: false,
  749. encrypt: {
  750. instance: false,
  751. info: undefined,
  752. iv: undefined,
  753. key: undefined,
  754. type: undefined
  755. },
  756. hmac: {
  757. info: undefined,
  758. key: undefined,
  759. buf: undefined,
  760. type: false
  761. },
  762. compress: {
  763. instance: false,
  764. type: false,
  765. queue: null
  766. }
  767. }
  768. };
  769. }
  770. if (!noend) {
  771. if (this.readable)
  772. this.push(null);
  773. }
  774. };
  775. // Common methods
  776. // Global
  777. SSH2Stream.prototype.disconnect = function(reason) {
  778. /*
  779. byte SSH_MSG_DISCONNECT
  780. uint32 reason code
  781. string description in ISO-10646 UTF-8 encoding
  782. string language tag
  783. */
  784. var buf = Buffer.alloc(1 + 4 + 4 + 4);
  785. buf[0] = MESSAGE.DISCONNECT;
  786. if (DISCONNECT_REASON[reason] === undefined)
  787. reason = DISCONNECT_REASON.BY_APPLICATION;
  788. writeUInt32BE(buf, reason, 1);
  789. this.debug('DEBUG: Outgoing: Writing DISCONNECT ('
  790. + DISCONNECT_REASON[reason]
  791. + ')');
  792. send(this, buf);
  793. this.reset();
  794. return false;
  795. };
  796. SSH2Stream.prototype.ping = function() {
  797. this.debug('DEBUG: Outgoing: Writing ping (GLOBAL_REQUEST: keepalive@openssh.com)');
  798. return send(this, PING_PACKET);
  799. };
  800. SSH2Stream.prototype.rekey = function() {
  801. var status = this._state.outgoing.status;
  802. if (status === OUT_REKEYING)
  803. throw new Error('A re-key is already in progress');
  804. else if (status !== OUT_READY)
  805. throw new Error('Cannot re-key yet');
  806. this.debug('DEBUG: Outgoing: Starting re-key');
  807. return KEXINIT(this);
  808. };
  809. // 'ssh-connection' service-specific
  810. SSH2Stream.prototype.requestSuccess = function(data) {
  811. var buf;
  812. if (Buffer.isBuffer(data)) {
  813. buf = Buffer.allocUnsafe(1 + data.length);
  814. buf[0] = MESSAGE.REQUEST_SUCCESS;
  815. data.copy(buf, 1);
  816. } else
  817. buf = REQUEST_SUCCESS_PACKET;
  818. this.debug('DEBUG: Outgoing: Writing REQUEST_SUCCESS');
  819. return send(this, buf);
  820. };
  821. SSH2Stream.prototype.requestFailure = function() {
  822. this.debug('DEBUG: Outgoing: Writing REQUEST_FAILURE');
  823. return send(this, REQUEST_FAILURE_PACKET);
  824. };
  825. SSH2Stream.prototype.channelSuccess = function(chan) {
  826. // Does not consume window space
  827. var buf = Buffer.allocUnsafe(1 + 4);
  828. buf[0] = MESSAGE.CHANNEL_SUCCESS;
  829. writeUInt32BE(buf, chan, 1);
  830. this.debug('DEBUG: Outgoing: Writing CHANNEL_SUCCESS (' + chan + ')');
  831. return send(this, buf);
  832. };
  833. SSH2Stream.prototype.channelFailure = function(chan) {
  834. // Does not consume window space
  835. var buf = Buffer.allocUnsafe(1 + 4);
  836. buf[0] = MESSAGE.CHANNEL_FAILURE;
  837. writeUInt32BE(buf, chan, 1);
  838. this.debug('DEBUG: Outgoing: Writing CHANNEL_FAILURE (' + chan + ')');
  839. return send(this, buf);
  840. };
  841. SSH2Stream.prototype.channelEOF = function(chan) {
  842. // Does not consume window space
  843. var buf = Buffer.allocUnsafe(1 + 4);
  844. buf[0] = MESSAGE.CHANNEL_EOF;
  845. writeUInt32BE(buf, chan, 1);
  846. this.debug('DEBUG: Outgoing: Writing CHANNEL_EOF (' + chan + ')');
  847. return send(this, buf);
  848. };
  849. SSH2Stream.prototype.channelClose = function(chan) {
  850. // Does not consume window space
  851. var buf = Buffer.allocUnsafe(1 + 4);
  852. buf[0] = MESSAGE.CHANNEL_CLOSE;
  853. writeUInt32BE(buf, chan, 1);
  854. this.debug('DEBUG: Outgoing: Writing CHANNEL_CLOSE (' + chan + ')');
  855. return send(this, buf);
  856. };
  857. SSH2Stream.prototype.channelWindowAdjust = function(chan, amount) {
  858. // Does not consume window space
  859. var buf = Buffer.allocUnsafe(1 + 4 + 4);
  860. buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST;
  861. writeUInt32BE(buf, chan, 1);
  862. writeUInt32BE(buf, amount, 5);
  863. this.debug('DEBUG: Outgoing: Writing CHANNEL_WINDOW_ADJUST ('
  864. + chan
  865. + ', '
  866. + amount
  867. + ')');
  868. return send(this, buf);
  869. };
  870. SSH2Stream.prototype.channelData = function(chan, data) {
  871. var dataIsBuffer = Buffer.isBuffer(data);
  872. var dataLen = (dataIsBuffer ? data.length : Buffer.byteLength(data));
  873. var buf = Buffer.allocUnsafe(1 + 4 + 4 + dataLen);
  874. buf[0] = MESSAGE.CHANNEL_DATA;
  875. writeUInt32BE(buf, chan, 1);
  876. writeUInt32BE(buf, dataLen, 5);
  877. if (dataIsBuffer)
  878. data.copy(buf, 9);
  879. else
  880. buf.write(data, 9, dataLen, 'utf8');
  881. this.debug('DEBUG: Outgoing: Writing CHANNEL_DATA (' + chan + ')');
  882. return send(this, buf);
  883. };
  884. SSH2Stream.prototype.channelExtData = function(chan, data, type) {
  885. var dataIsBuffer = Buffer.isBuffer(data);
  886. var dataLen = (dataIsBuffer ? data.length : Buffer.byteLength(data));
  887. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + dataLen);
  888. buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA;
  889. writeUInt32BE(buf, chan, 1);
  890. writeUInt32BE(buf, type, 5);
  891. writeUInt32BE(buf, dataLen, 9);
  892. if (dataIsBuffer)
  893. data.copy(buf, 13);
  894. else
  895. buf.write(data, 13, dataLen, 'utf8');
  896. this.debug('DEBUG: Outgoing: Writing CHANNEL_EXTENDED_DATA (' + chan + ')');
  897. return send(this, buf);
  898. };
  899. SSH2Stream.prototype.channelOpenConfirm = function(remoteChan, localChan,
  900. initWindow, maxPacket) {
  901. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + 4);
  902. buf[0] = MESSAGE.CHANNEL_OPEN_CONFIRMATION;
  903. writeUInt32BE(buf, remoteChan, 1);
  904. writeUInt32BE(buf, localChan, 5);
  905. writeUInt32BE(buf, initWindow, 9);
  906. writeUInt32BE(buf, maxPacket, 13);
  907. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN_CONFIRMATION (r:'
  908. + remoteChan
  909. + ', l:'
  910. + localChan
  911. + ')');
  912. return send(this, buf);
  913. };
  914. SSH2Stream.prototype.channelOpenFail = function(remoteChan, reason, desc,
  915. lang) {
  916. if (typeof desc !== 'string')
  917. desc = '';
  918. if (typeof lang !== 'string')
  919. lang = '';
  920. var descLen = Buffer.byteLength(desc);
  921. var langLen = Buffer.byteLength(lang);
  922. var p = 9;
  923. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + descLen + 4 + langLen);
  924. buf[0] = MESSAGE.CHANNEL_OPEN_FAILURE;
  925. writeUInt32BE(buf, remoteChan, 1);
  926. writeUInt32BE(buf, reason, 5);
  927. writeUInt32BE(buf, descLen, p);
  928. p += 4;
  929. if (descLen) {
  930. buf.write(desc, p, descLen, 'utf8');
  931. p += descLen;
  932. }
  933. writeUInt32BE(buf, langLen, p);
  934. if (langLen)
  935. buf.write(lang, p += 4, langLen, 'ascii');
  936. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN_FAILURE ('
  937. + remoteChan
  938. + ')');
  939. return send(this, buf);
  940. };
  941. // Client-specific methods
  942. // Global
  943. SSH2Stream.prototype.service = function(svcName) {
  944. if (this.server)
  945. throw new Error('Client-only method called in server mode');
  946. var svcNameLen = Buffer.byteLength(svcName);
  947. var buf = Buffer.allocUnsafe(1 + 4 + svcNameLen);
  948. buf[0] = MESSAGE.SERVICE_REQUEST;
  949. writeUInt32BE(buf, svcNameLen, 1);
  950. buf.write(svcName, 5, svcNameLen, 'ascii');
  951. this.debug('DEBUG: Outgoing: Writing SERVICE_REQUEST (' + svcName + ')');
  952. return send(this, buf);
  953. };
  954. // 'ssh-connection' service-specific
  955. SSH2Stream.prototype.tcpipForward = function(bindAddr, bindPort, wantReply) {
  956. if (this.server)
  957. throw new Error('Client-only method called in server mode');
  958. var addrlen = Buffer.byteLength(bindAddr);
  959. var buf = Buffer.allocUnsafe(1 + 4 + 13 + 1 + 4 + addrlen + 4);
  960. buf[0] = MESSAGE.GLOBAL_REQUEST;
  961. writeUInt32BE(buf, 13, 1);
  962. buf.write('tcpip-forward', 5, 13, 'ascii');
  963. buf[18] = (wantReply === undefined || wantReply === true ? 1 : 0);
  964. writeUInt32BE(buf, addrlen, 19);
  965. buf.write(bindAddr, 23, addrlen, 'ascii');
  966. writeUInt32BE(buf, bindPort, 23 + addrlen);
  967. this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (tcpip-forward)');
  968. return send(this, buf);
  969. };
  970. SSH2Stream.prototype.cancelTcpipForward = function(bindAddr, bindPort,
  971. wantReply) {
  972. if (this.server)
  973. throw new Error('Client-only method called in server mode');
  974. var addrlen = Buffer.byteLength(bindAddr);
  975. var buf = Buffer.allocUnsafe(1 + 4 + 20 + 1 + 4 + addrlen + 4);
  976. buf[0] = MESSAGE.GLOBAL_REQUEST;
  977. writeUInt32BE(buf, 20, 1);
  978. buf.write('cancel-tcpip-forward', 5, 20, 'ascii');
  979. buf[25] = (wantReply === undefined || wantReply === true ? 1 : 0);
  980. writeUInt32BE(buf, addrlen, 26);
  981. buf.write(bindAddr, 30, addrlen, 'ascii');
  982. writeUInt32BE(buf, bindPort, 30 + addrlen);
  983. this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (cancel-tcpip-forward)');
  984. return send(this, buf);
  985. };
  986. SSH2Stream.prototype.openssh_streamLocalForward = function(socketPath,
  987. wantReply) {
  988. if (this.server)
  989. throw new Error('Client-only method called in server mode');
  990. var pathlen = Buffer.byteLength(socketPath);
  991. var buf = Buffer.allocUnsafe(1 + 4 + 31 + 1 + 4 + pathlen);
  992. buf[0] = MESSAGE.GLOBAL_REQUEST;
  993. writeUInt32BE(buf, 31, 1);
  994. buf.write('streamlocal-forward@openssh.com', 5, 31, 'ascii');
  995. buf[36] = (wantReply === undefined || wantReply === true ? 1 : 0);
  996. writeUInt32BE(buf, pathlen, 37);
  997. buf.write(socketPath, 41, pathlen, 'utf8');
  998. this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (streamlocal-forward@openssh.com)');
  999. return send(this, buf);
  1000. };
  1001. SSH2Stream.prototype.openssh_cancelStreamLocalForward = function(socketPath,
  1002. wantReply) {
  1003. if (this.server)
  1004. throw new Error('Client-only method called in server mode');
  1005. var pathlen = Buffer.byteLength(socketPath);
  1006. var buf = Buffer.allocUnsafe(1 + 4 + 38 + 1 + 4 + pathlen);
  1007. buf[0] = MESSAGE.GLOBAL_REQUEST;
  1008. writeUInt32BE(buf, 38, 1);
  1009. buf.write('cancel-streamlocal-forward@openssh.com', 5, 38, 'ascii');
  1010. buf[43] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1011. writeUInt32BE(buf, pathlen, 44);
  1012. buf.write(socketPath, 48, pathlen, 'utf8');
  1013. this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (cancel-streamlocal-forward@openssh.com)');
  1014. return send(this, buf);
  1015. };
  1016. SSH2Stream.prototype.directTcpip = function(chan, initWindow, maxPacket, cfg) {
  1017. if (this.server)
  1018. throw new Error('Client-only method called in server mode');
  1019. var srclen = Buffer.byteLength(cfg.srcIP);
  1020. var dstlen = Buffer.byteLength(cfg.dstIP);
  1021. var p = 29;
  1022. var buf = Buffer.allocUnsafe(1 + 4 + 12 + 4 + 4 + 4 + 4 + srclen + 4 + 4
  1023. + dstlen + 4);
  1024. buf[0] = MESSAGE.CHANNEL_OPEN;
  1025. writeUInt32BE(buf, 12, 1);
  1026. buf.write('direct-tcpip', 5, 12, 'ascii');
  1027. writeUInt32BE(buf, chan, 17);
  1028. writeUInt32BE(buf, initWindow, 21);
  1029. writeUInt32BE(buf, maxPacket, 25);
  1030. writeUInt32BE(buf, dstlen, p);
  1031. buf.write(cfg.dstIP, p += 4, dstlen, 'ascii');
  1032. writeUInt32BE(buf, cfg.dstPort, p += dstlen);
  1033. writeUInt32BE(buf, srclen, p += 4);
  1034. buf.write(cfg.srcIP, p += 4, srclen, 'ascii');
  1035. writeUInt32BE(buf, cfg.srcPort, p += srclen);
  1036. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1037. + chan
  1038. + ', direct-tcpip)');
  1039. return send(this, buf);
  1040. };
  1041. SSH2Stream.prototype.openssh_directStreamLocal = function(chan, initWindow,
  1042. maxPacket, cfg) {
  1043. if (this.server)
  1044. throw new Error('Client-only method called in server mode');
  1045. var pathlen = Buffer.byteLength(cfg.socketPath);
  1046. var p = 47;
  1047. var buf = Buffer.allocUnsafe(1 + 4 + 30 + 4 + 4 + 4 + 4 + pathlen + 4 + 4);
  1048. buf[0] = MESSAGE.CHANNEL_OPEN;
  1049. writeUInt32BE(buf, 30, 1);
  1050. buf.write('direct-streamlocal@openssh.com', 5, 30, 'ascii');
  1051. writeUInt32BE(buf, chan, 35);
  1052. writeUInt32BE(buf, initWindow, 39);
  1053. writeUInt32BE(buf, maxPacket, 43);
  1054. writeUInt32BE(buf, pathlen, p);
  1055. buf.write(cfg.socketPath, p += 4, pathlen, 'utf8');
  1056. // reserved fields (string and uint32)
  1057. buf.fill(0, buf.length - 8);
  1058. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1059. + chan
  1060. + ', direct-streamlocal@openssh.com)');
  1061. return send(this, buf);
  1062. };
  1063. SSH2Stream.prototype.openssh_noMoreSessions = function(wantReply) {
  1064. if (this.server)
  1065. throw new Error('Client-only method called in server mode');
  1066. var buf = Buffer.allocUnsafe(1 + 4 + 28 + 1);
  1067. buf[0] = MESSAGE.GLOBAL_REQUEST;
  1068. writeUInt32BE(buf, 28, 1);
  1069. buf.write('no-more-sessions@openssh.com', 5, 28, 'ascii');
  1070. buf[33] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1071. this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (no-more-sessions@openssh.com)');
  1072. return send(this, buf);
  1073. };
  1074. SSH2Stream.prototype.session = function(chan, initWindow, maxPacket) {
  1075. if (this.server)
  1076. throw new Error('Client-only method called in server mode');
  1077. // Does not consume window space
  1078. var buf = Buffer.allocUnsafe(1 + 4 + 7 + 4 + 4 + 4);
  1079. buf[0] = MESSAGE.CHANNEL_OPEN;
  1080. writeUInt32BE(buf, 7, 1);
  1081. buf.write('session', 5, 7, 'ascii');
  1082. writeUInt32BE(buf, chan, 12);
  1083. writeUInt32BE(buf, initWindow, 16);
  1084. writeUInt32BE(buf, maxPacket, 20);
  1085. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1086. + chan
  1087. + ', session)');
  1088. return send(this, buf);
  1089. };
  1090. SSH2Stream.prototype.windowChange = function(chan, rows, cols, height, width) {
  1091. if (this.server)
  1092. throw new Error('Client-only method called in server mode');
  1093. // Does not consume window space
  1094. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4);
  1095. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1096. writeUInt32BE(buf, chan, 1);
  1097. writeUInt32BE(buf, 13, 5);
  1098. buf.write('window-change', 9, 13, 'ascii');
  1099. buf[22] = 0;
  1100. writeUInt32BE(buf, cols, 23);
  1101. writeUInt32BE(buf, rows, 27);
  1102. writeUInt32BE(buf, width, 31);
  1103. writeUInt32BE(buf, height, 35);
  1104. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1105. + chan
  1106. + ', window-change)');
  1107. return send(this, buf);
  1108. };
  1109. SSH2Stream.prototype.pty = function(chan, rows, cols, height,
  1110. width, term, modes, wantReply) {
  1111. if (this.server)
  1112. throw new Error('Client-only method called in server mode');
  1113. // Does not consume window space
  1114. if (!term || !term.length)
  1115. term = 'vt100';
  1116. if (modes
  1117. && !Buffer.isBuffer(modes)
  1118. && !Array.isArray(modes)
  1119. && typeof modes === 'object')
  1120. modes = modesToBytes(modes);
  1121. if (!modes || !modes.length)
  1122. modes = NO_TERMINAL_MODES_BUFFER;
  1123. var termLen = term.length;
  1124. var modesLen = modes.length;
  1125. var p = 21;
  1126. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 7 + 1 + 4 + termLen + 4 + 4 + 4 + 4
  1127. + 4 + modesLen);
  1128. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1129. writeUInt32BE(buf, chan, 1);
  1130. writeUInt32BE(buf, 7, 5);
  1131. buf.write('pty-req', 9, 7, 'ascii');
  1132. buf[16] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1133. writeUInt32BE(buf, termLen, 17);
  1134. buf.write(term, 21, termLen, 'utf8');
  1135. writeUInt32BE(buf, cols, p += termLen);
  1136. writeUInt32BE(buf, rows, p += 4);
  1137. writeUInt32BE(buf, width, p += 4);
  1138. writeUInt32BE(buf, height, p += 4);
  1139. writeUInt32BE(buf, modesLen, p += 4);
  1140. p += 4;
  1141. if (Array.isArray(modes)) {
  1142. for (var i = 0; i < modesLen; ++i)
  1143. buf[p++] = modes[i];
  1144. } else if (Buffer.isBuffer(modes)) {
  1145. modes.copy(buf, p);
  1146. }
  1147. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1148. + chan
  1149. + ', pty-req)');
  1150. return send(this, buf);
  1151. };
  1152. SSH2Stream.prototype.shell = function(chan, wantReply) {
  1153. if (this.server)
  1154. throw new Error('Client-only method called in server mode');
  1155. // Does not consume window space
  1156. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 5 + 1);
  1157. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1158. writeUInt32BE(buf, chan, 1);
  1159. writeUInt32BE(buf, 5, 5);
  1160. buf.write('shell', 9, 5, 'ascii');
  1161. buf[14] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1162. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1163. + chan
  1164. + ', shell)');
  1165. return send(this, buf);
  1166. };
  1167. SSH2Stream.prototype.exec = function(chan, cmd, wantReply) {
  1168. if (this.server)
  1169. throw new Error('Client-only method called in server mode');
  1170. // Does not consume window space
  1171. var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd));
  1172. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + 1 + 4 + cmdlen);
  1173. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1174. writeUInt32BE(buf, chan, 1);
  1175. writeUInt32BE(buf, 4, 5);
  1176. buf.write('exec', 9, 4, 'ascii');
  1177. buf[13] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1178. writeUInt32BE(buf, cmdlen, 14);
  1179. if (Buffer.isBuffer(cmd))
  1180. cmd.copy(buf, 18);
  1181. else
  1182. buf.write(cmd, 18, cmdlen, 'utf8');
  1183. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1184. + chan
  1185. + ', exec)');
  1186. return send(this, buf);
  1187. };
  1188. SSH2Stream.prototype.signal = function(chan, signal) {
  1189. if (this.server)
  1190. throw new Error('Client-only method called in server mode');
  1191. // Does not consume window space
  1192. signal = signal.toUpperCase();
  1193. if (signal.slice(0, 3) === 'SIG')
  1194. signal = signal.substring(3);
  1195. if (SIGNALS.indexOf(signal) === -1)
  1196. throw new Error('Invalid signal: ' + signal);
  1197. var signalLen = signal.length;
  1198. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 6 + 1 + 4 + signalLen);
  1199. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1200. writeUInt32BE(buf, chan, 1);
  1201. writeUInt32BE(buf, 6, 5);
  1202. buf.write('signal', 9, 6, 'ascii');
  1203. buf[15] = 0;
  1204. writeUInt32BE(buf, signalLen, 16);
  1205. buf.write(signal, 20, signalLen, 'ascii');
  1206. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1207. + chan
  1208. + ', signal)');
  1209. return send(this, buf);
  1210. };
  1211. SSH2Stream.prototype.env = function(chan, key, val, wantReply) {
  1212. if (this.server)
  1213. throw new Error('Client-only method called in server mode');
  1214. // Does not consume window space
  1215. var keyLen = Buffer.byteLength(key);
  1216. var valLen = (Buffer.isBuffer(val) ? val.length : Buffer.byteLength(val));
  1217. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 3 + 1 + 4 + keyLen + 4 + valLen);
  1218. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1219. writeUInt32BE(buf, chan, 1);
  1220. writeUInt32BE(buf, 3, 5);
  1221. buf.write('env', 9, 3, 'ascii');
  1222. buf[12] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1223. writeUInt32BE(buf, keyLen, 13);
  1224. buf.write(key, 17, keyLen, 'ascii');
  1225. writeUInt32BE(buf, valLen, 17 + keyLen);
  1226. if (Buffer.isBuffer(val))
  1227. val.copy(buf, 17 + keyLen + 4);
  1228. else
  1229. buf.write(val, 17 + keyLen + 4, valLen, 'utf8');
  1230. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1231. + chan
  1232. + ', env)');
  1233. return send(this, buf);
  1234. };
  1235. SSH2Stream.prototype.x11Forward = function(chan, cfg, wantReply) {
  1236. if (this.server)
  1237. throw new Error('Client-only method called in server mode');
  1238. // Does not consume window space
  1239. var protolen = Buffer.byteLength(cfg.protocol);
  1240. var cookielen = Buffer.byteLength(cfg.cookie);
  1241. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 7 + 1 + 1 + 4 + protolen + 4
  1242. + cookielen + 4);
  1243. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1244. writeUInt32BE(buf, chan, 1);
  1245. writeUInt32BE(buf, 7, 5);
  1246. buf.write('x11-req', 9, 7, 'ascii');
  1247. buf[16] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1248. buf[17] = (cfg.single ? 1 : 0);
  1249. writeUInt32BE(buf, protolen, 18);
  1250. var bp = 22;
  1251. if (Buffer.isBuffer(cfg.protocol))
  1252. cfg.protocol.copy(buf, bp);
  1253. else
  1254. buf.write(cfg.protocol, bp, protolen, 'utf8');
  1255. bp += protolen;
  1256. writeUInt32BE(buf, cookielen, bp);
  1257. bp += 4;
  1258. if (Buffer.isBuffer(cfg.cookie))
  1259. cfg.cookie.copy(buf, bp);
  1260. else
  1261. buf.write(cfg.cookie, bp, cookielen, 'binary');
  1262. bp += cookielen;
  1263. writeUInt32BE(buf, (cfg.screen || 0), bp);
  1264. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1265. + chan
  1266. + ', x11-req)');
  1267. return send(this, buf);
  1268. };
  1269. SSH2Stream.prototype.subsystem = function(chan, name, wantReply) {
  1270. if (this.server)
  1271. throw new Error('Client-only method called in server mode');
  1272. // Does not consume window space
  1273. var nameLen = Buffer.byteLength(name);
  1274. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 9 + 1 + 4 + nameLen);
  1275. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1276. writeUInt32BE(buf, chan, 1);
  1277. writeUInt32BE(buf, 9, 5);
  1278. buf.write('subsystem', 9, 9, 'ascii');
  1279. buf[18] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1280. writeUInt32BE(buf, nameLen, 19);
  1281. buf.write(name, 23, nameLen, 'ascii');
  1282. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1283. + chan
  1284. + ', subsystem: '
  1285. + name
  1286. + ')');
  1287. return send(this, buf);
  1288. };
  1289. SSH2Stream.prototype.openssh_agentForward = function(chan, wantReply) {
  1290. if (this.server)
  1291. throw new Error('Client-only method called in server mode');
  1292. // Does not consume window space
  1293. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 26 + 1);
  1294. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1295. writeUInt32BE(buf, chan, 1);
  1296. writeUInt32BE(buf, 26, 5);
  1297. buf.write('auth-agent-req@openssh.com', 9, 26, 'ascii');
  1298. buf[35] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1299. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1300. + chan
  1301. + ', auth-agent-req@openssh.com)');
  1302. return send(this, buf);
  1303. };
  1304. // 'ssh-userauth' service-specific
  1305. SSH2Stream.prototype.authPassword = function(username, password) {
  1306. if (this.server)
  1307. throw new Error('Client-only method called in server mode');
  1308. var userLen = Buffer.byteLength(username);
  1309. var passLen = Buffer.byteLength(password);
  1310. var p = 0;
  1311. var buf = Buffer.allocUnsafe(1
  1312. + 4 + userLen
  1313. + 4 + 14 // "ssh-connection"
  1314. + 4 + 8 // "password"
  1315. + 1
  1316. + 4 + passLen);
  1317. buf[p] = MESSAGE.USERAUTH_REQUEST;
  1318. writeUInt32BE(buf, userLen, ++p);
  1319. buf.write(username, p += 4, userLen, 'utf8');
  1320. writeUInt32BE(buf, 14, p += userLen);
  1321. buf.write('ssh-connection', p += 4, 14, 'ascii');
  1322. writeUInt32BE(buf, 8, p += 14);
  1323. buf.write('password', p += 4, 8, 'ascii');
  1324. buf[p += 8] = 0;
  1325. writeUInt32BE(buf, passLen, ++p);
  1326. buf.write(password, p += 4, passLen, 'utf8');
  1327. this._state.authsQueue.push('password');
  1328. this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (password)');
  1329. return send(this, buf);
  1330. };
  1331. SSH2Stream.prototype.authPK = function(username, pubKey, cbSign) {
  1332. if (this.server)
  1333. throw new Error('Client-only method called in server mode');
  1334. var self = this;
  1335. var outstate = this._state.outgoing;
  1336. var keyType;
  1337. if (typeof pubKey.getPublicSSH === 'function') {
  1338. keyType = pubKey.type;
  1339. pubKey = pubKey.getPublicSSH();
  1340. } else {
  1341. keyType = pubKey.toString('ascii',
  1342. 4,
  1343. 4 + readUInt32BE(pubKey, 0));
  1344. }
  1345. var userLen = Buffer.byteLength(username);
  1346. var algoLen = Buffer.byteLength(keyType);
  1347. var pubKeyLen = pubKey.length;
  1348. var sesLen = outstate.sessionId.length;
  1349. var p = 0;
  1350. var buf = Buffer.allocUnsafe((cbSign ? 4 + sesLen : 0)
  1351. + 1
  1352. + 4 + userLen
  1353. + 4 + 14 // "ssh-connection"
  1354. + 4 + 9 // "publickey"
  1355. + 1
  1356. + 4 + algoLen
  1357. + 4 + pubKeyLen
  1358. );
  1359. if (cbSign) {
  1360. writeUInt32BE(buf, sesLen, p);
  1361. outstate.sessionId.copy(buf, p += 4);
  1362. buf[p += sesLen] = MESSAGE.USERAUTH_REQUEST;
  1363. } else {
  1364. buf[p] = MESSAGE.USERAUTH_REQUEST;
  1365. }
  1366. writeUInt32BE(buf, userLen, ++p);
  1367. buf.write(username, p += 4, userLen, 'utf8');
  1368. writeUInt32BE(buf, 14, p += userLen);
  1369. buf.write('ssh-connection', p += 4, 14, 'ascii');
  1370. writeUInt32BE(buf, 9, p += 14);
  1371. buf.write('publickey', p += 4, 9, 'ascii');
  1372. buf[p += 9] = (cbSign ? 1 : 0);
  1373. writeUInt32BE(buf, algoLen, ++p);
  1374. buf.write(keyType, p += 4, algoLen, 'ascii');
  1375. writeUInt32BE(buf, pubKeyLen, p += algoLen);
  1376. pubKey.copy(buf, p += 4);
  1377. if (!cbSign) {
  1378. this._state.authsQueue.push('publickey');
  1379. this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (publickey -- check)');
  1380. return send(this, buf);
  1381. }
  1382. cbSign(buf, function(signature) {
  1383. signature = convertSignature(signature, keyType);
  1384. if (signature === false)
  1385. throw new Error('Error while converting handshake signature');
  1386. var sigLen = signature.length;
  1387. var sigbuf = Buffer.allocUnsafe(1
  1388. + 4 + userLen
  1389. + 4 + 14 // "ssh-connection"
  1390. + 4 + 9 // "publickey"
  1391. + 1
  1392. + 4 + algoLen
  1393. + 4 + pubKeyLen
  1394. + 4 // 4 + algoLen + 4 + sigLen
  1395. + 4 + algoLen
  1396. + 4 + sigLen);
  1397. p = 0;
  1398. sigbuf[p] = MESSAGE.USERAUTH_REQUEST;
  1399. writeUInt32BE(sigbuf, userLen, ++p);
  1400. sigbuf.write(username, p += 4, userLen, 'utf8');
  1401. writeUInt32BE(sigbuf, 14, p += userLen);
  1402. sigbuf.write('ssh-connection', p += 4, 14, 'ascii');
  1403. writeUInt32BE(sigbuf, 9, p += 14);
  1404. sigbuf.write('publickey', p += 4, 9, 'ascii');
  1405. sigbuf[p += 9] = 1;
  1406. writeUInt32BE(sigbuf, algoLen, ++p);
  1407. sigbuf.write(keyType, p += 4, algoLen, 'ascii');
  1408. writeUInt32BE(sigbuf, pubKeyLen, p += algoLen);
  1409. pubKey.copy(sigbuf, p += 4);
  1410. writeUInt32BE(sigbuf, 4 + algoLen + 4 + sigLen, p += pubKeyLen);
  1411. writeUInt32BE(sigbuf, algoLen, p += 4);
  1412. sigbuf.write(keyType, p += 4, algoLen, 'ascii');
  1413. writeUInt32BE(sigbuf, sigLen, p += algoLen);
  1414. signature.copy(sigbuf, p += 4);
  1415. // Servers shouldn't send packet type 60 in response to signed publickey
  1416. // attempts, but if they do, interpret as type 60.
  1417. self._state.authsQueue.push('publickey');
  1418. self.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (publickey)');
  1419. return send(self, sigbuf);
  1420. });
  1421. return true;
  1422. };
  1423. SSH2Stream.prototype.authHostbased = function(username, pubKey, hostname,
  1424. userlocal, cbSign) {
  1425. // TODO: Make DRY by sharing similar code with authPK()
  1426. if (this.server)
  1427. throw new Error('Client-only method called in server mode');
  1428. var self = this;
  1429. var outstate = this._state.outgoing;
  1430. var keyType;
  1431. if (typeof pubKey.getPublicSSH === 'function') {
  1432. keyType = pubKey.type;
  1433. pubKey = pubKey.getPublicSSH();
  1434. } else {
  1435. keyType = pubKey.toString('ascii',
  1436. 4,
  1437. 4 + readUInt32BE(pubKey, 0));
  1438. }
  1439. var userLen = Buffer.byteLength(username);
  1440. var algoLen = Buffer.byteLength(keyType);
  1441. var pubKeyLen = pubKey.length;
  1442. var sesLen = outstate.sessionId.length;
  1443. var hostnameLen = Buffer.byteLength(hostname);
  1444. var userlocalLen = Buffer.byteLength(userlocal);
  1445. var p = 0;
  1446. var buf = Buffer.allocUnsafe(4 + sesLen
  1447. + 1
  1448. + 4 + userLen
  1449. + 4 + 14 // "ssh-connection"
  1450. + 4 + 9 // "hostbased"
  1451. + 4 + algoLen
  1452. + 4 + pubKeyLen
  1453. + 4 + hostnameLen
  1454. + 4 + userlocalLen
  1455. );
  1456. writeUInt32BE(buf, sesLen, p);
  1457. outstate.sessionId.copy(buf, p += 4);
  1458. buf[p += sesLen] = MESSAGE.USERAUTH_REQUEST;
  1459. writeUInt32BE(buf, userLen, ++p);
  1460. buf.write(username, p += 4, userLen, 'utf8');
  1461. writeUInt32BE(buf, 14, p += userLen);
  1462. buf.write('ssh-connection', p += 4, 14, 'ascii');
  1463. writeUInt32BE(buf, 9, p += 14);
  1464. buf.write('hostbased', p += 4, 9, 'ascii');
  1465. writeUInt32BE(buf, algoLen, p += 9);
  1466. buf.write(keyType, p += 4, algoLen, 'ascii');
  1467. writeUInt32BE(buf, pubKeyLen, p += algoLen);
  1468. pubKey.copy(buf, p += 4);
  1469. writeUInt32BE(buf, hostnameLen, p += pubKeyLen);
  1470. buf.write(hostname, p += 4, hostnameLen, 'ascii');
  1471. writeUInt32BE(buf, userlocalLen, p += hostnameLen);
  1472. buf.write(userlocal, p += 4, userlocalLen, 'utf8');
  1473. cbSign(buf, function(signature) {
  1474. signature = convertSignature(signature, keyType);
  1475. if (signature === false)
  1476. throw new Error('Error while converting handshake signature');
  1477. var sigLen = signature.length;
  1478. var sigbuf = Buffer.allocUnsafe((buf.length - sesLen) + sigLen);
  1479. buf.copy(sigbuf, 0, 4 + sesLen);
  1480. writeUInt32BE(sigbuf, sigLen, sigbuf.length - sigLen - 4);
  1481. signature.copy(sigbuf, sigbuf.length - sigLen);
  1482. self._state.authsQueue.push('hostbased');
  1483. self.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (hostbased)');
  1484. return send(self, sigbuf);
  1485. });
  1486. return true;
  1487. };
  1488. SSH2Stream.prototype.authKeyboard = function(username) {
  1489. if (this.server)
  1490. throw new Error('Client-only method called in server mode');
  1491. var userLen = Buffer.byteLength(username);
  1492. var p = 0;
  1493. var buf = Buffer.allocUnsafe(1
  1494. + 4 + userLen
  1495. + 4 + 14 // "ssh-connection"
  1496. + 4 + 20 // "keyboard-interactive"
  1497. + 4 // no language set
  1498. + 4 // no submethods
  1499. );
  1500. buf[p] = MESSAGE.USERAUTH_REQUEST;
  1501. writeUInt32BE(buf, userLen, ++p);
  1502. buf.write(username, p += 4, userLen, 'utf8');
  1503. writeUInt32BE(buf, 14, p += userLen);
  1504. buf.write('ssh-connection', p += 4, 14, 'ascii');
  1505. writeUInt32BE(buf, 20, p += 14);
  1506. buf.write('keyboard-interactive', p += 4, 20, 'ascii');
  1507. writeUInt32BE(buf, 0, p += 20);
  1508. writeUInt32BE(buf, 0, p += 4);
  1509. this._state.authsQueue.push('keyboard-interactive');
  1510. this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (keyboard-interactive)');
  1511. return send(this, buf);
  1512. };
  1513. SSH2Stream.prototype.authNone = function(username) {
  1514. if (this.server)
  1515. throw new Error('Client-only method called in server mode');
  1516. var userLen = Buffer.byteLength(username);
  1517. var p = 0;
  1518. var buf = Buffer.allocUnsafe(1
  1519. + 4 + userLen
  1520. + 4 + 14 // "ssh-connection"
  1521. + 4 + 4 // "none"
  1522. );
  1523. buf[p] = MESSAGE.USERAUTH_REQUEST;
  1524. writeUInt32BE(buf, userLen, ++p);
  1525. buf.write(username, p += 4, userLen, 'utf8');
  1526. writeUInt32BE(buf, 14, p += userLen);
  1527. buf.write('ssh-connection', p += 4, 14, 'ascii');
  1528. writeUInt32BE(buf, 4, p += 14);
  1529. buf.write('none', p += 4, 4, 'ascii');
  1530. this._state.authsQueue.push('none');
  1531. this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (none)');
  1532. return send(this, buf);
  1533. };
  1534. SSH2Stream.prototype.authInfoRes = function(responses) {
  1535. if (this.server)
  1536. throw new Error('Client-only method called in server mode');
  1537. var responsesLen = 0;
  1538. var p = 0;
  1539. var resLen;
  1540. var len;
  1541. var i;
  1542. if (responses) {
  1543. for (i = 0, len = responses.length; i < len; ++i)
  1544. responsesLen += 4 + Buffer.byteLength(responses[i]);
  1545. }
  1546. var buf = Buffer.allocUnsafe(1 + 4 + responsesLen);
  1547. buf[p++] = MESSAGE.USERAUTH_INFO_RESPONSE;
  1548. writeUInt32BE(buf, responses ? responses.length : 0, p);
  1549. if (responses) {
  1550. p += 4;
  1551. for (i = 0, len = responses.length; i < len; ++i) {
  1552. resLen = Buffer.byteLength(responses[i]);
  1553. writeUInt32BE(buf, resLen, p);
  1554. p += 4;
  1555. if (resLen) {
  1556. buf.write(responses[i], p, resLen, 'utf8');
  1557. p += resLen;
  1558. }
  1559. }
  1560. }
  1561. this.debug('DEBUG: Outgoing: Writing USERAUTH_INFO_RESPONSE');
  1562. return send(this, buf);
  1563. };
  1564. // Server-specific methods
  1565. // Global
  1566. SSH2Stream.prototype.serviceAccept = function(svcName) {
  1567. if (!this.server)
  1568. throw new Error('Server-only method called in client mode');
  1569. var svcNameLen = svcName.length;
  1570. var buf = Buffer.allocUnsafe(1 + 4 + svcNameLen);
  1571. buf[0] = MESSAGE.SERVICE_ACCEPT;
  1572. writeUInt32BE(buf, svcNameLen, 1);
  1573. buf.write(svcName, 5, svcNameLen, 'ascii');
  1574. this.debug('DEBUG: Outgoing: Writing SERVICE_ACCEPT (' + svcName + ')');
  1575. send(this, buf);
  1576. if (this.server && this.banner && svcName === 'ssh-userauth') {
  1577. /*
  1578. byte SSH_MSG_USERAUTH_BANNER
  1579. string message in ISO-10646 UTF-8 encoding
  1580. string language tag
  1581. */
  1582. var bannerLen = Buffer.byteLength(this.banner);
  1583. var packetLen = 1 + 4 + bannerLen + 4;
  1584. var packet = Buffer.allocUnsafe(packetLen);
  1585. packet[0] = MESSAGE.USERAUTH_BANNER;
  1586. writeUInt32BE(packet, bannerLen, 1);
  1587. packet.write(this.banner, 5, bannerLen, 'utf8');
  1588. packet.fill(0, packetLen - 4); // Empty language tag
  1589. this.debug('DEBUG: Outgoing: Writing USERAUTH_BANNER');
  1590. send(this, packet);
  1591. this.banner = undefined; // Prevent banner from being displayed again
  1592. }
  1593. };
  1594. // 'ssh-connection' service-specific
  1595. SSH2Stream.prototype.forwardedTcpip = function(chan, initWindow, maxPacket,
  1596. cfg) {
  1597. if (!this.server)
  1598. throw new Error('Server-only method called in client mode');
  1599. var boundAddrLen = Buffer.byteLength(cfg.boundAddr);
  1600. var remoteAddrLen = Buffer.byteLength(cfg.remoteAddr);
  1601. var p = 36 + boundAddrLen;
  1602. var buf = Buffer.allocUnsafe(1 + 4 + 15 + 4 + 4 + 4 + 4 + boundAddrLen + 4 + 4
  1603. + remoteAddrLen + 4);
  1604. buf[0] = MESSAGE.CHANNEL_OPEN;
  1605. writeUInt32BE(buf, 15, 1);
  1606. buf.write('forwarded-tcpip', 5, 15, 'ascii');
  1607. writeUInt32BE(buf, chan, 20);
  1608. writeUInt32BE(buf, initWindow, 24);
  1609. writeUInt32BE(buf, maxPacket, 28);
  1610. writeUInt32BE(buf, boundAddrLen, 32);
  1611. buf.write(cfg.boundAddr, 36, boundAddrLen, 'ascii');
  1612. writeUInt32BE(buf, cfg.boundPort, p);
  1613. writeUInt32BE(buf, remoteAddrLen, p += 4);
  1614. buf.write(cfg.remoteAddr, p += 4, remoteAddrLen, 'ascii');
  1615. writeUInt32BE(buf, cfg.remotePort, p += remoteAddrLen);
  1616. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1617. + chan
  1618. + ', forwarded-tcpip)');
  1619. return send(this, buf);
  1620. };
  1621. SSH2Stream.prototype.x11 = function(chan, initWindow, maxPacket, cfg) {
  1622. if (!this.server)
  1623. throw new Error('Server-only method called in client mode');
  1624. var addrLen = Buffer.byteLength(cfg.originAddr);
  1625. var p = 24 + addrLen;
  1626. var buf = Buffer.allocUnsafe(1 + 4 + 3 + 4 + 4 + 4 + 4 + addrLen + 4);
  1627. buf[0] = MESSAGE.CHANNEL_OPEN;
  1628. writeUInt32BE(buf, 3, 1);
  1629. buf.write('x11', 5, 3, 'ascii');
  1630. writeUInt32BE(buf, chan, 8);
  1631. writeUInt32BE(buf, initWindow, 12);
  1632. writeUInt32BE(buf, maxPacket, 16);
  1633. writeUInt32BE(buf, addrLen, 20);
  1634. buf.write(cfg.originAddr, 24, addrLen, 'ascii');
  1635. writeUInt32BE(buf, cfg.originPort, p);
  1636. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1637. + chan
  1638. + ', x11)');
  1639. return send(this, buf);
  1640. };
  1641. SSH2Stream.prototype.openssh_authAgent = function(chan, initWindow, maxPacket) {
  1642. if (!this.server)
  1643. throw new Error('Server-only method called in client mode');
  1644. var buf = Buffer.allocUnsafe(1 + 4 + 22 + 4 + 4 + 4);
  1645. buf[0] = MESSAGE.CHANNEL_OPEN;
  1646. writeUInt32BE(buf, 22, 1);
  1647. buf.write('auth-agent@openssh.com', 5, 22, 'ascii');
  1648. writeUInt32BE(buf, chan, 27);
  1649. writeUInt32BE(buf, initWindow, 31);
  1650. writeUInt32BE(buf, maxPacket, 35);
  1651. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1652. + chan
  1653. + ', auth-agent@openssh.com)');
  1654. return send(this, buf);
  1655. };
  1656. SSH2Stream.prototype.openssh_forwardedStreamLocal = function(chan, initWindow,
  1657. maxPacket, cfg) {
  1658. if (!this.server)
  1659. throw new Error('Server-only method called in client mode');
  1660. var pathlen = Buffer.byteLength(cfg.socketPath);
  1661. var buf = Buffer.allocUnsafe(1 + 4 + 33 + 4 + 4 + 4 + 4 + pathlen + 4);
  1662. buf[0] = MESSAGE.CHANNEL_OPEN;
  1663. writeUInt32BE(buf, 33, 1);
  1664. buf.write('forwarded-streamlocal@openssh.com', 5, 33, 'ascii');
  1665. writeUInt32BE(buf, chan, 38);
  1666. writeUInt32BE(buf, initWindow, 42);
  1667. writeUInt32BE(buf, maxPacket, 46);
  1668. writeUInt32BE(buf, pathlen, 50);
  1669. buf.write(cfg.socketPath, 54, pathlen, 'utf8');
  1670. writeUInt32BE(buf, 0, 54 + pathlen);
  1671. this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN ('
  1672. + chan
  1673. + ', forwarded-streamlocal@openssh.com)');
  1674. return send(this, buf);
  1675. };
  1676. SSH2Stream.prototype.exitStatus = function(chan, status) {
  1677. if (!this.server)
  1678. throw new Error('Server-only method called in client mode');
  1679. // Does not consume window space
  1680. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 11 + 1 + 4);
  1681. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1682. writeUInt32BE(buf, chan, 1);
  1683. writeUInt32BE(buf, 11, 5);
  1684. buf.write('exit-status', 9, 11, 'ascii');
  1685. buf[20] = 0;
  1686. writeUInt32BE(buf, status, 21);
  1687. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1688. + chan
  1689. + ', exit-status)');
  1690. return send(this, buf);
  1691. };
  1692. SSH2Stream.prototype.exitSignal = function(chan, name, coreDumped, msg) {
  1693. if (!this.server)
  1694. throw new Error('Server-only method called in client mode');
  1695. // Does not consume window space
  1696. var nameLen = Buffer.byteLength(name);
  1697. var msgLen = (msg ? Buffer.byteLength(msg) : 0);
  1698. var p = 25 + nameLen;
  1699. var buf = Buffer.allocUnsafe(1 + 4 + 4 + 11 + 1 + 4 + nameLen + 1 + 4 + msgLen
  1700. + 4);
  1701. buf[0] = MESSAGE.CHANNEL_REQUEST;
  1702. writeUInt32BE(buf, chan, 1);
  1703. writeUInt32BE(buf, 11, 5);
  1704. buf.write('exit-signal', 9, 11, 'ascii');
  1705. buf[20] = 0;
  1706. writeUInt32BE(buf, nameLen, 21);
  1707. buf.write(name, 25, nameLen, 'utf8');
  1708. buf[p++] = (coreDumped ? 1 : 0);
  1709. writeUInt32BE(buf, msgLen, p);
  1710. p += 4;
  1711. if (msgLen) {
  1712. buf.write(msg, p, msgLen, 'utf8');
  1713. p += msgLen;
  1714. }
  1715. writeUInt32BE(buf, 0, p);
  1716. this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST ('
  1717. + chan
  1718. + ', exit-signal)');
  1719. return send(this, buf);
  1720. };
  1721. // 'ssh-userauth' service-specific
  1722. SSH2Stream.prototype.authFailure = function(authMethods, isPartial) {
  1723. if (!this.server)
  1724. throw new Error('Server-only method called in client mode');
  1725. var authsQueue = this._state.authsQueue;
  1726. if (!authsQueue.length)
  1727. throw new Error('No auth in progress');
  1728. var methods;
  1729. if (typeof authMethods === 'boolean') {
  1730. isPartial = authMethods;
  1731. authMethods = undefined;
  1732. }
  1733. if (authMethods) {
  1734. methods = [];
  1735. for (var i = 0, len = authMethods.length; i < len; ++i) {
  1736. if (authMethods[i].toLowerCase() === 'none')
  1737. continue;
  1738. methods.push(authMethods[i]);
  1739. }
  1740. methods = methods.join(',');
  1741. } else
  1742. methods = '';
  1743. var methodsLen = methods.length;
  1744. var buf = Buffer.allocUnsafe(1 + 4 + methodsLen + 1);
  1745. buf[0] = MESSAGE.USERAUTH_FAILURE;
  1746. writeUInt32BE(buf, methodsLen, 1);
  1747. buf.write(methods, 5, methodsLen, 'ascii');
  1748. buf[5 + methodsLen] = (isPartial === true ? 1 : 0);
  1749. this._state.authsQueue.shift();
  1750. this.debug('DEBUG: Outgoing: Writing USERAUTH_FAILURE');
  1751. return send(this, buf);
  1752. };
  1753. SSH2Stream.prototype.authSuccess = function() {
  1754. if (!this.server)
  1755. throw new Error('Server-only method called in client mode');
  1756. var authsQueue = this._state.authsQueue;
  1757. if (!authsQueue.length)
  1758. throw new Error('No auth in progress');
  1759. var state = this._state;
  1760. var outstate = state.outgoing;
  1761. var instate = state.incoming;
  1762. state.authsQueue.shift();
  1763. this.debug('DEBUG: Outgoing: Writing USERAUTH_SUCCESS');
  1764. var ret = send(this, USERAUTH_SUCCESS_PACKET);
  1765. if (outstate.compress.type === 'zlib@openssh.com') {
  1766. outstate.compress.instance = zlib.createDeflate(ZLIB_OPTS);
  1767. outstate.compress.queue = [];
  1768. }
  1769. if (instate.decompress.type === 'zlib@openssh.com')
  1770. instate.decompress.instance = zlib.createInflate(ZLIB_OPTS);
  1771. return ret;
  1772. };
  1773. SSH2Stream.prototype.authPKOK = function(keyAlgo, key) {
  1774. if (!this.server)
  1775. throw new Error('Server-only method called in client mode');
  1776. var authsQueue = this._state.authsQueue;
  1777. if (!authsQueue.length || authsQueue[0] !== 'publickey')
  1778. throw new Error('"publickey" auth not in progress');
  1779. var keyAlgoLen = keyAlgo.length;
  1780. var keyLen = key.length;
  1781. var buf = Buffer.allocUnsafe(1 + 4 + keyAlgoLen + 4 + keyLen);
  1782. buf[0] = MESSAGE.USERAUTH_PK_OK;
  1783. writeUInt32BE(buf, keyAlgoLen, 1);
  1784. buf.write(keyAlgo, 5, keyAlgoLen, 'ascii');
  1785. writeUInt32BE(buf, keyLen, 5 + keyAlgoLen);
  1786. key.copy(buf, 5 + keyAlgoLen + 4);
  1787. this._state.authsQueue.shift();
  1788. this.debug('DEBUG: Outgoing: Writing USERAUTH_PK_OK');
  1789. return send(this, buf);
  1790. };
  1791. SSH2Stream.prototype.authPasswdChg = function(prompt, lang) {
  1792. if (!this.server)
  1793. throw new Error('Server-only method called in client mode');
  1794. var promptLen = Buffer.byteLength(prompt);
  1795. var langLen = lang ? lang.length : 0;
  1796. var p = 0;
  1797. var buf = Buffer.allocUnsafe(1 + 4 + promptLen + 4 + langLen);
  1798. buf[p] = MESSAGE.USERAUTH_PASSWD_CHANGEREQ;
  1799. writeUInt32BE(buf, promptLen, ++p);
  1800. buf.write(prompt, p += 4, promptLen, 'utf8');
  1801. writeUInt32BE(buf, langLen, p += promptLen);
  1802. if (langLen)
  1803. buf.write(lang, p += 4, langLen, 'ascii');
  1804. this.debug('DEBUG: Outgoing: Writing USERAUTH_PASSWD_CHANGEREQ');
  1805. return send(this, buf);
  1806. };
  1807. SSH2Stream.prototype.authInfoReq = function(name, instructions, prompts) {
  1808. if (!this.server)
  1809. throw new Error('Server-only method called in client mode');
  1810. var promptsLen = 0;
  1811. var nameLen = name ? Buffer.byteLength(name) : 0;
  1812. var instrLen = instructions ? Buffer.byteLength(instructions) : 0;
  1813. var p = 0;
  1814. var promptLen;
  1815. var prompt;
  1816. var len;
  1817. var i;
  1818. for (i = 0, len = prompts.length; i < len; ++i)
  1819. promptsLen += 4 + Buffer.byteLength(prompts[i].prompt) + 1;
  1820. var buf = Buffer.allocUnsafe(1 + 4 + nameLen + 4 + instrLen + 4 + 4
  1821. + promptsLen);
  1822. buf[p++] = MESSAGE.USERAUTH_INFO_REQUEST;
  1823. writeUInt32BE(buf, nameLen, p);
  1824. p += 4;
  1825. if (name) {
  1826. buf.write(name, p, nameLen, 'utf8');
  1827. p += nameLen;
  1828. }
  1829. writeUInt32BE(buf, instrLen, p);
  1830. p += 4;
  1831. if (instructions) {
  1832. buf.write(instructions, p, instrLen, 'utf8');
  1833. p += instrLen;
  1834. }
  1835. writeUInt32BE(buf, 0, p);
  1836. p += 4;
  1837. writeUInt32BE(buf, prompts.length, p);
  1838. p += 4;
  1839. for (i = 0, len = prompts.length; i < len; ++i) {
  1840. prompt = prompts[i];
  1841. promptLen = Buffer.byteLength(prompt.prompt);
  1842. writeUInt32BE(buf, promptLen, p);
  1843. p += 4;
  1844. if (promptLen) {
  1845. buf.write(prompt.prompt, p, promptLen, 'utf8');
  1846. p += promptLen;
  1847. }
  1848. buf[p++] = (prompt.echo ? 1 : 0);
  1849. }
  1850. this.debug('DEBUG: Outgoing: Writing USERAUTH_INFO_REQUEST');
  1851. return send(this, buf);
  1852. };
  1853. // Shared incoming/parser functions
  1854. function onDISCONNECT(self, reason, code, desc, lang) { // Client/Server
  1855. if (code !== DISCONNECT_REASON.BY_APPLICATION) {
  1856. var err = new Error(desc || reason);
  1857. err.code = code;
  1858. self.emit('error', err);
  1859. }
  1860. self.reset();
  1861. }
  1862. function onKEXINIT(self, init, firstFollows) { // Client/Server
  1863. var state = self._state;
  1864. var outstate = state.outgoing;
  1865. if (outstate.status === OUT_READY) {
  1866. self.debug('DEBUG: Received re-key request');
  1867. outstate.status = OUT_REKEYING;
  1868. outstate.kexinit = undefined;
  1869. KEXINIT(self, check);
  1870. } else {
  1871. check();
  1872. }
  1873. function check() {
  1874. if (check_KEXINIT(self, init, firstFollows) === true) {
  1875. if (!self.server) {
  1876. if (state.kex.type === 'groupex')
  1877. KEXDH_GEX_REQ(self);
  1878. else
  1879. KEXDH_INIT(self);
  1880. } else {
  1881. state.incoming.expectedPacket = state.kex.pktInit;
  1882. }
  1883. }
  1884. }
  1885. }
  1886. function check_KEXINIT(self, init, firstFollows) {
  1887. var state = self._state;
  1888. var instate = state.incoming;
  1889. var outstate = state.outgoing;
  1890. var debug = self.debug;
  1891. var serverList;
  1892. var clientList;
  1893. var val;
  1894. var len;
  1895. var i;
  1896. debug('DEBUG: Comparing KEXINITs ...');
  1897. var algos = self.config.algorithms;
  1898. var kexList = algos.kex;
  1899. if (self.remoteBugs & BUGS.BAD_DHGEX) {
  1900. var copied = false;
  1901. for (var j = kexList.length - 1; j >= 0; --j) {
  1902. if (kexList[j].indexOf('group-exchange') !== -1) {
  1903. if (!copied) {
  1904. kexList = kexList.slice();
  1905. copied = true;
  1906. }
  1907. kexList.splice(j, 1);
  1908. }
  1909. }
  1910. }
  1911. debug('DEBUG: (local) KEX algorithms: ' + kexList);
  1912. debug('DEBUG: (remote) KEX algorithms: ' + init.algorithms.kex);
  1913. if (self.server) {
  1914. serverList = kexList;
  1915. clientList = init.algorithms.kex;
  1916. } else {
  1917. serverList = init.algorithms.kex;
  1918. clientList = kexList;
  1919. }
  1920. // Check for agreeable key exchange algorithm
  1921. for (i = 0, len = clientList.length;
  1922. i < len && serverList.indexOf(clientList[i]) === -1;
  1923. ++i);
  1924. if (i === len) {
  1925. // No suitable match found!
  1926. debug('DEBUG: No matching key exchange algorithm');
  1927. var err = new Error('Handshake failed: no matching key exchange algorithm');
  1928. err.level = 'handshake';
  1929. self.emit('error', err);
  1930. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  1931. return false;
  1932. }
  1933. var kex_algorithm = clientList[i];
  1934. debug('DEBUG: KEX algorithm: ' + kex_algorithm);
  1935. if (firstFollows
  1936. && (!init.algorithms.kex.length
  1937. || kex_algorithm !== init.algorithms.kex[0])) {
  1938. // Ignore next incoming packet, it was a wrong first guess at KEX algorithm
  1939. instate.ignoreNext = true;
  1940. }
  1941. debug('DEBUG: (local) Host key formats: ' + algos.serverHostKey);
  1942. debug('DEBUG: (remote) Host key formats: ' + init.algorithms.srvHostKey);
  1943. if (self.server) {
  1944. serverList = algos.serverHostKey;
  1945. clientList = init.algorithms.srvHostKey;
  1946. } else {
  1947. serverList = init.algorithms.srvHostKey;
  1948. clientList = algos.serverHostKey;
  1949. }
  1950. // Check for agreeable server host key format
  1951. for (i = 0, len = clientList.length;
  1952. i < len && serverList.indexOf(clientList[i]) === -1;
  1953. ++i);
  1954. if (i === len) {
  1955. // No suitable match found!
  1956. debug('DEBUG: No matching host key format');
  1957. var err = new Error('Handshake failed: no matching host key format');
  1958. err.level = 'handshake';
  1959. self.emit('error', err);
  1960. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  1961. return false;
  1962. }
  1963. state.hostkeyFormat = clientList[i];
  1964. debug('DEBUG: Host key format: ' + state.hostkeyFormat);
  1965. debug('DEBUG: (local) Client->Server ciphers: ' + algos.cipher);
  1966. debug('DEBUG: (remote) Client->Server ciphers: '
  1967. + init.algorithms.cs.encrypt);
  1968. if (self.server) {
  1969. serverList = algos.cipher;
  1970. clientList = init.algorithms.cs.encrypt;
  1971. } else {
  1972. serverList = init.algorithms.cs.encrypt;
  1973. clientList = algos.cipher;
  1974. }
  1975. // Check for agreeable client->server cipher
  1976. for (i = 0, len = clientList.length;
  1977. i < len && serverList.indexOf(clientList[i]) === -1;
  1978. ++i);
  1979. if (i === len) {
  1980. // No suitable match found!
  1981. debug('DEBUG: No matching Client->Server cipher');
  1982. var err = new Error('Handshake failed: no matching client->server cipher');
  1983. err.level = 'handshake';
  1984. self.emit('error', err);
  1985. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  1986. return false;
  1987. }
  1988. if (self.server)
  1989. val = instate.decrypt.type = clientList[i];
  1990. else
  1991. val = outstate.encrypt.type = clientList[i];
  1992. debug('DEBUG: Client->Server Cipher: ' + val);
  1993. debug('DEBUG: (local) Server->Client ciphers: ' + algos.cipher);
  1994. debug('DEBUG: (remote) Server->Client ciphers: '
  1995. + (init.algorithms.sc.encrypt));
  1996. if (self.server) {
  1997. serverList = algos.cipher;
  1998. clientList = init.algorithms.sc.encrypt;
  1999. } else {
  2000. serverList = init.algorithms.sc.encrypt;
  2001. clientList = algos.cipher;
  2002. }
  2003. // Check for agreeable server->client cipher
  2004. for (i = 0, len = clientList.length;
  2005. i < len && serverList.indexOf(clientList[i]) === -1;
  2006. ++i);
  2007. if (i === len) {
  2008. // No suitable match found!
  2009. debug('DEBUG: No matching Server->Client cipher');
  2010. var err = new Error('Handshake failed: no matching server->client cipher');
  2011. err.level = 'handshake';
  2012. self.emit('error', err);
  2013. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2014. return false;
  2015. }
  2016. if (self.server)
  2017. val = outstate.encrypt.type = clientList[i];
  2018. else
  2019. val = instate.decrypt.type = clientList[i];
  2020. debug('DEBUG: Server->Client Cipher: ' + val);
  2021. debug('DEBUG: (local) Client->Server HMAC algorithms: ' + algos.hmac);
  2022. debug('DEBUG: (remote) Client->Server HMAC algorithms: '
  2023. + init.algorithms.cs.mac);
  2024. if (self.server) {
  2025. serverList = algos.hmac;
  2026. clientList = init.algorithms.cs.mac;
  2027. } else {
  2028. serverList = init.algorithms.cs.mac;
  2029. clientList = algos.hmac;
  2030. }
  2031. // Check for agreeable client->server hmac algorithm
  2032. for (i = 0, len = clientList.length;
  2033. i < len && serverList.indexOf(clientList[i]) === -1;
  2034. ++i);
  2035. if (i === len) {
  2036. // No suitable match found!
  2037. debug('DEBUG: No matching Client->Server HMAC algorithm');
  2038. var err = new Error('Handshake failed: no matching client->server HMAC');
  2039. err.level = 'handshake';
  2040. self.emit('error', err);
  2041. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2042. return false;
  2043. }
  2044. if (self.server)
  2045. val = instate.hmac.type = clientList[i];
  2046. else
  2047. val = outstate.hmac.type = clientList[i];
  2048. debug('DEBUG: Client->Server HMAC algorithm: ' + val);
  2049. debug('DEBUG: (local) Server->Client HMAC algorithms: ' + algos.hmac);
  2050. debug('DEBUG: (remote) Server->Client HMAC algorithms: '
  2051. + init.algorithms.sc.mac);
  2052. if (self.server) {
  2053. serverList = algos.hmac;
  2054. clientList = init.algorithms.sc.mac;
  2055. } else {
  2056. serverList = init.algorithms.sc.mac;
  2057. clientList = algos.hmac;
  2058. }
  2059. // Check for agreeable server->client hmac algorithm
  2060. for (i = 0, len = clientList.length;
  2061. i < len && serverList.indexOf(clientList[i]) === -1;
  2062. ++i);
  2063. if (i === len) {
  2064. // No suitable match found!
  2065. debug('DEBUG: No matching Server->Client HMAC algorithm');
  2066. var err = new Error('Handshake failed: no matching server->client HMAC');
  2067. err.level = 'handshake';
  2068. self.emit('error', err);
  2069. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2070. return false;
  2071. }
  2072. if (self.server)
  2073. val = outstate.hmac.type = clientList[i];
  2074. else
  2075. val = instate.hmac.type = clientList[i];
  2076. debug('DEBUG: Server->Client HMAC algorithm: ' + val);
  2077. debug('DEBUG: (local) Client->Server compression algorithms: '
  2078. + algos.compress);
  2079. debug('DEBUG: (remote) Client->Server compression algorithms: '
  2080. + init.algorithms.cs.compress);
  2081. if (self.server) {
  2082. serverList = algos.compress;
  2083. clientList = init.algorithms.cs.compress;
  2084. } else {
  2085. serverList = init.algorithms.cs.compress;
  2086. clientList = algos.compress;
  2087. }
  2088. // Check for agreeable client->server compression algorithm
  2089. for (i = 0, len = clientList.length;
  2090. i < len && serverList.indexOf(clientList[i]) === -1;
  2091. ++i);
  2092. if (i === len) {
  2093. // No suitable match found!
  2094. debug('DEBUG: No matching Client->Server compression algorithm');
  2095. var err = new Error('Handshake failed: no matching client->server '
  2096. + 'compression algorithm');
  2097. err.level = 'handshake';
  2098. self.emit('error', err);
  2099. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2100. return false;
  2101. }
  2102. if (self.server)
  2103. val = instate.decompress.type = clientList[i];
  2104. else
  2105. val = outstate.compress.type = clientList[i];
  2106. debug('DEBUG: Client->Server compression algorithm: ' + val);
  2107. debug('DEBUG: (local) Server->Client compression algorithms: '
  2108. + algos.compress);
  2109. debug('DEBUG: (remote) Server->Client compression algorithms: '
  2110. + init.algorithms.sc.compress);
  2111. if (self.server) {
  2112. serverList = algos.compress;
  2113. clientList = init.algorithms.sc.compress;
  2114. } else {
  2115. serverList = init.algorithms.sc.compress;
  2116. clientList = algos.compress;
  2117. }
  2118. // Check for agreeable server->client compression algorithm
  2119. for (i = 0, len = clientList.length;
  2120. i < len && serverList.indexOf(clientList[i]) === -1;
  2121. ++i);
  2122. if (i === len) {
  2123. // No suitable match found!
  2124. debug('DEBUG: No matching Server->Client compression algorithm');
  2125. var err = new Error('Handshake failed: no matching server->client '
  2126. + 'compression algorithm');
  2127. err.level = 'handshake';
  2128. self.emit('error', err);
  2129. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2130. return false;
  2131. }
  2132. if (self.server)
  2133. val = outstate.compress.type = clientList[i];
  2134. else
  2135. val = instate.decompress.type = clientList[i];
  2136. debug('DEBUG: Server->Client compression algorithm: ' + val);
  2137. state.kex = new KeyExchange(kex_algorithm);
  2138. state.kex.generateKeys();
  2139. outstate.pubkey = state.kex.getPublicKey();
  2140. return true;
  2141. }
  2142. function onKEXDH_GEX_GROUP(self, prime, gen) {
  2143. var state = self._state;
  2144. var outstate = state.outgoing;
  2145. state.kex.setDHParams(prime, gen);
  2146. state.kex.generateKeys();
  2147. outstate.pubkey = state.kex.getPublicKey();
  2148. KEXDH_INIT(self);
  2149. }
  2150. function onKEXDH_INIT(self, e) { // Server
  2151. KEXDH_REPLY(self, e);
  2152. }
  2153. function onKEXDH_REPLY(self, info, verifiedHost) { // Client
  2154. var state = self._state;
  2155. var instate = state.incoming;
  2156. var outstate = state.outgoing;
  2157. var debug = self.debug;
  2158. var len;
  2159. var i;
  2160. if (verifiedHost === undefined) {
  2161. instate.expectedPacket = 'NEWKEYS';
  2162. outstate.sentNEWKEYS = false;
  2163. debug('DEBUG: Checking host key format');
  2164. // Ensure all host key formats agree
  2165. var hostkey_format = readString(info.hostkey, 0, 'ascii', self);
  2166. if (hostkey_format === false)
  2167. return false;
  2168. if (info.hostkey_format !== state.hostkeyFormat
  2169. || info.hostkey_format !== hostkey_format) {
  2170. // Expected and actual server host key format do not match!
  2171. debug('DEBUG: Host key format mismatch');
  2172. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2173. self.reset();
  2174. var err = new Error('Handshake failed: host key format mismatch');
  2175. err.level = 'handshake';
  2176. self.emit('error', err);
  2177. return false;
  2178. }
  2179. debug('DEBUG: Checking signature format');
  2180. // Ensure signature formats agree
  2181. var sig_format = readString(info.sig, 0, 'ascii', self);
  2182. if (sig_format === false)
  2183. return false;
  2184. if (info.sig_format !== sig_format) {
  2185. debug('DEBUG: Signature format mismatch');
  2186. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2187. self.reset();
  2188. var err = new Error('Handshake failed: signature format mismatch');
  2189. err.level = 'handshake';
  2190. self.emit('error', err);
  2191. return false;
  2192. }
  2193. }
  2194. // Verify the host fingerprint first if needed
  2195. if (outstate.status === OUT_INIT) {
  2196. if (verifiedHost === undefined) {
  2197. debug('DEBUG: Verifying host fingerprint');
  2198. var sync = true;
  2199. var emitted = self.emit('fingerprint', info.hostkey, function(permitted) {
  2200. // Prevent multiple calls to this callback
  2201. if (verifiedHost !== undefined)
  2202. return;
  2203. verifiedHost = !!permitted;
  2204. if (!sync) {
  2205. // Continue execution by re-entry
  2206. onKEXDH_REPLY(self, info, verifiedHost);
  2207. }
  2208. });
  2209. sync = false;
  2210. // Support async calling of verification callback
  2211. if (emitted && verifiedHost === undefined)
  2212. return;
  2213. }
  2214. if (verifiedHost === undefined)
  2215. debug('DEBUG: Host accepted by default (no verification)');
  2216. else if (verifiedHost === true)
  2217. debug('DEBUG: Host accepted (verified)');
  2218. else {
  2219. debug('DEBUG: Host denied via fingerprint verification');
  2220. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2221. self.reset();
  2222. var err = new Error('Handshake failed: '
  2223. + 'host fingerprint verification failed');
  2224. err.level = 'handshake';
  2225. self.emit('error', err);
  2226. return false;
  2227. }
  2228. }
  2229. info.pubkey = state.kex.convertPublicKey(info.pubkey);
  2230. info.secret = state.kex.computeSecret(info.pubkey);
  2231. if (info.secret instanceof Error) {
  2232. info.secret.message = 'Error while computing DH secret ('
  2233. + state.kex.type + '): '
  2234. + info.secret.message;
  2235. info.secret.level = 'handshake';
  2236. self.emit('error', info.secret);
  2237. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2238. return false;
  2239. }
  2240. var hash = crypto.createHash(state.kex.hash);
  2241. var len_ident = Buffer.byteLength(self.config.ident);
  2242. var len_sident = Buffer.byteLength(instate.identRaw);
  2243. var len_init = outstate.kexinit.length;
  2244. var len_sinit = instate.kexinit.length;
  2245. var len_hostkey = info.hostkey.length;
  2246. var len_pubkey = outstate.pubkey.length;
  2247. var len_spubkey = info.pubkey.length;
  2248. var len_secret = info.secret.length;
  2249. var exchangeBufLen = len_ident
  2250. + len_sident
  2251. + len_init
  2252. + len_sinit
  2253. + len_hostkey
  2254. + len_pubkey
  2255. + len_spubkey
  2256. + len_secret
  2257. + (4 * 8); // Length fields for above values
  2258. // Group exchange-related
  2259. var len_gex_prime;
  2260. var len_gex_gen;
  2261. var gex_prime;
  2262. var gex_gen;
  2263. var dhParams = state.kex.getDHParams();
  2264. if (dhParams) {
  2265. gex_prime = dhParams.prime;
  2266. gex_gen = dhParams.generator;
  2267. len_gex_prime = gex_prime.length;
  2268. len_gex_gen = gex_gen.length;
  2269. exchangeBufLen += (4 * 3); // min, n, max values
  2270. exchangeBufLen += (4 * 2); // prime, generator length fields
  2271. exchangeBufLen += len_gex_prime;
  2272. exchangeBufLen += len_gex_gen;
  2273. }
  2274. var bp = 0;
  2275. var exchangeBuf = Buffer.allocUnsafe(exchangeBufLen);
  2276. writeUInt32BE(exchangeBuf, len_ident, bp);
  2277. bp += 4;
  2278. exchangeBuf.write(self.config.ident, bp, 'utf8'); // V_C
  2279. bp += len_ident;
  2280. writeUInt32BE(exchangeBuf, len_sident, bp);
  2281. bp += 4;
  2282. exchangeBuf.write(instate.identRaw, bp, 'utf8'); // V_S
  2283. bp += len_sident;
  2284. writeUInt32BE(exchangeBuf, len_init, bp);
  2285. bp += 4;
  2286. outstate.kexinit.copy(exchangeBuf, bp); // I_C
  2287. bp += len_init;
  2288. outstate.kexinit = undefined;
  2289. writeUInt32BE(exchangeBuf, len_sinit, bp);
  2290. bp += 4;
  2291. instate.kexinit.copy(exchangeBuf, bp); // I_S
  2292. bp += len_sinit;
  2293. instate.kexinit = undefined;
  2294. writeUInt32BE(exchangeBuf, len_hostkey, bp);
  2295. bp += 4;
  2296. info.hostkey.copy(exchangeBuf, bp); // K_S
  2297. bp += len_hostkey;
  2298. if (dhParams) {
  2299. KEXDH_GEX_REQ_PACKET.slice(1).copy(exchangeBuf, bp); // min, n, max
  2300. bp += (4 * 3); // Skip over bytes just copied
  2301. writeUInt32BE(exchangeBuf, len_gex_prime, bp);
  2302. bp += 4;
  2303. gex_prime.copy(exchangeBuf, bp); // p
  2304. bp += len_gex_prime;
  2305. writeUInt32BE(exchangeBuf, len_gex_gen, bp);
  2306. bp += 4;
  2307. gex_gen.copy(exchangeBuf, bp); // g
  2308. bp += len_gex_gen;
  2309. }
  2310. writeUInt32BE(exchangeBuf, len_pubkey, bp);
  2311. bp += 4;
  2312. outstate.pubkey.copy(exchangeBuf, bp); // e
  2313. bp += len_pubkey;
  2314. writeUInt32BE(exchangeBuf, len_spubkey, bp);
  2315. bp += 4;
  2316. info.pubkey.copy(exchangeBuf, bp); // f
  2317. bp += len_spubkey;
  2318. writeUInt32BE(exchangeBuf, len_secret, bp);
  2319. bp += 4;
  2320. info.secret.copy(exchangeBuf, bp); // K
  2321. outstate.exchangeHash = hash.update(exchangeBuf).digest(); // H
  2322. var rawsig = readString(info.sig, info.sig._pos, self); // s
  2323. if (rawsig === false
  2324. || !(rawsig = sigSSHToASN1(rawsig, info.sig_format, self))) {
  2325. return false;
  2326. }
  2327. var hostPubKey = parseDERKey(info.hostkey, info.sig_format);
  2328. if (hostPubKey instanceof Error)
  2329. return false;
  2330. debug('DEBUG: Verifying signature');
  2331. if (hostPubKey.verify(outstate.exchangeHash, rawsig) !== true) {
  2332. debug('DEBUG: Signature verification failed');
  2333. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  2334. self.reset();
  2335. var err = new Error('Handshake failed: signature verification failed');
  2336. err.level = 'handshake';
  2337. self.emit('error', err);
  2338. return false;
  2339. }
  2340. if (outstate.sessionId === undefined)
  2341. outstate.sessionId = outstate.exchangeHash;
  2342. outstate.kexsecret = info.secret;
  2343. debug('DEBUG: Outgoing: Writing NEWKEYS');
  2344. if (outstate.status === OUT_REKEYING)
  2345. send(self, NEWKEYS_PACKET, undefined, true);
  2346. else
  2347. send(self, NEWKEYS_PACKET);
  2348. outstate.sentNEWKEYS = true;
  2349. if (verifiedHost !== undefined && instate.expectedPacket === undefined) {
  2350. // We received NEWKEYS while we were waiting for the fingerprint
  2351. // verification callback to be called. In this case we have to re-execute
  2352. // onNEWKEYS to finish the handshake.
  2353. onNEWKEYS(self);
  2354. }
  2355. }
  2356. function onNEWKEYS(self) { // Client/Server
  2357. var state = self._state;
  2358. var outstate = state.outgoing;
  2359. var instate = state.incoming;
  2360. instate.expectedPacket = undefined;
  2361. if (!outstate.sentNEWKEYS)
  2362. return;
  2363. var len = outstate.kexsecret.length;
  2364. var outCipherInfo = outstate.encrypt.info = CIPHER_INFO[outstate.encrypt.type];
  2365. var p = 0;
  2366. var dhHashAlgo = state.kex.hash;
  2367. var secret = Buffer.allocUnsafe(4 + len);
  2368. var iv;
  2369. var key;
  2370. // Whenever the client sends a new authentication request, it is enqueued
  2371. // here. Once the request is resolved (success, fail, or PK_OK),
  2372. // dequeue. Whatever is at the front of the queue determines how we
  2373. // interpret packet type 60.
  2374. state.authsQueue = [];
  2375. writeUInt32BE(secret, len, p);
  2376. p += 4;
  2377. outstate.kexsecret.copy(secret, p);
  2378. outstate.kexsecret = undefined;
  2379. if (!outCipherInfo.stream) {
  2380. iv = crypto.createHash(dhHashAlgo)
  2381. .update(secret)
  2382. .update(outstate.exchangeHash)
  2383. .update(!self.server ? 'A' : 'B', 'ascii')
  2384. .update(outstate.sessionId)
  2385. .digest();
  2386. while (iv.length < outCipherInfo.ivLen) {
  2387. iv = Buffer.concat([iv,
  2388. crypto.createHash(dhHashAlgo)
  2389. .update(secret)
  2390. .update(outstate.exchangeHash)
  2391. .update(iv)
  2392. .digest()]);
  2393. }
  2394. if (iv.length > outCipherInfo.ivLen)
  2395. iv = iv.slice(0, outCipherInfo.ivLen);
  2396. } else {
  2397. iv = EMPTY_BUFFER; // Streaming ciphers don't use an IV upfront
  2398. }
  2399. key = crypto.createHash(dhHashAlgo)
  2400. .update(secret)
  2401. .update(outstate.exchangeHash)
  2402. .update(!self.server ? 'C' : 'D', 'ascii')
  2403. .update(outstate.sessionId)
  2404. .digest();
  2405. while (key.length < outCipherInfo.keyLen) {
  2406. key = Buffer.concat([key,
  2407. crypto.createHash(dhHashAlgo)
  2408. .update(secret)
  2409. .update(outstate.exchangeHash)
  2410. .update(key)
  2411. .digest()]);
  2412. }
  2413. if (key.length > outCipherInfo.keyLen)
  2414. key = key.slice(0, outCipherInfo.keyLen);
  2415. if (outCipherInfo.authLen > 0) {
  2416. outstate.encrypt.iv = iv;
  2417. outstate.encrypt.key = key;
  2418. outstate.encrypt.instance = true;
  2419. } else {
  2420. var cipherAlgo = SSH_TO_OPENSSL[outstate.encrypt.type];
  2421. outstate.encrypt.instance = crypto.createCipheriv(cipherAlgo, key, iv);
  2422. outstate.encrypt.instance.setAutoPadding(false);
  2423. }
  2424. // And now for decrypting ...
  2425. var inCipherInfo = instate.decrypt.info = CIPHER_INFO[instate.decrypt.type];
  2426. if (!inCipherInfo.stream) {
  2427. iv = crypto.createHash(dhHashAlgo)
  2428. .update(secret)
  2429. .update(outstate.exchangeHash)
  2430. .update(!self.server ? 'B' : 'A', 'ascii')
  2431. .update(outstate.sessionId)
  2432. .digest();
  2433. while (iv.length < inCipherInfo.ivLen) {
  2434. iv = Buffer.concat([iv,
  2435. crypto.createHash(dhHashAlgo)
  2436. .update(secret)
  2437. .update(outstate.exchangeHash)
  2438. .update(iv)
  2439. .digest()]);
  2440. }
  2441. if (iv.length > inCipherInfo.ivLen)
  2442. iv = iv.slice(0, inCipherInfo.ivLen);
  2443. } else {
  2444. iv = EMPTY_BUFFER; // Streaming ciphers don't use an IV upfront
  2445. }
  2446. // Create a reusable buffer for decryption purposes
  2447. instate.decrypt.buf = Buffer.allocUnsafe(inCipherInfo.blockLen);
  2448. key = crypto.createHash(dhHashAlgo)
  2449. .update(secret)
  2450. .update(outstate.exchangeHash)
  2451. .update(!self.server ? 'D' : 'C', 'ascii')
  2452. .update(outstate.sessionId)
  2453. .digest();
  2454. while (key.length < inCipherInfo.keyLen) {
  2455. key = Buffer.concat([key,
  2456. crypto.createHash(dhHashAlgo)
  2457. .update(secret)
  2458. .update(outstate.exchangeHash)
  2459. .update(key)
  2460. .digest()]);
  2461. }
  2462. if (key.length > inCipherInfo.keyLen)
  2463. key = key.slice(0, inCipherInfo.keyLen);
  2464. var decipherAlgo = SSH_TO_OPENSSL[instate.decrypt.type];
  2465. instate.decrypt.instance = crypto.createDecipheriv(decipherAlgo, key, iv);
  2466. instate.decrypt.instance.setAutoPadding(false);
  2467. instate.decrypt.iv = iv;
  2468. instate.decrypt.key = key;
  2469. var emptyBuf;
  2470. if (outCipherInfo.discardLen > 0) {
  2471. emptyBuf = Buffer.alloc(outCipherInfo.discardLen);
  2472. outstate.encrypt.instance.update(emptyBuf);
  2473. }
  2474. if (inCipherInfo.discardLen > 0) {
  2475. if (!emptyBuf || emptyBuf.length !== inCipherInfo.discardLen)
  2476. emptyBuf = Buffer.alloc(outCipherInfo.discardLen);
  2477. instate.decrypt.instance.update(emptyBuf);
  2478. }
  2479. var outHMACInfo = outstate.hmac.info = HMAC_INFO[outstate.hmac.type];
  2480. var inHMACInfo = instate.hmac.info = HMAC_INFO[instate.hmac.type];
  2481. if (outCipherInfo.authLen === 0) {
  2482. key = crypto.createHash(dhHashAlgo)
  2483. .update(secret)
  2484. .update(outstate.exchangeHash)
  2485. .update(!self.server ? 'E' : 'F', 'ascii')
  2486. .update(outstate.sessionId)
  2487. .digest();
  2488. while (key.length < outHMACInfo.len) {
  2489. key = Buffer.concat([key,
  2490. crypto.createHash(dhHashAlgo)
  2491. .update(secret)
  2492. .update(outstate.exchangeHash)
  2493. .update(key)
  2494. .digest()]);
  2495. }
  2496. if (key.length > outHMACInfo.len)
  2497. key = key.slice(0, outHMACInfo.len);
  2498. outstate.hmac.key = key;
  2499. } else {
  2500. outstate.hmac.key = undefined;
  2501. }
  2502. if (inCipherInfo.authLen === 0) {
  2503. key = crypto.createHash(dhHashAlgo)
  2504. .update(secret)
  2505. .update(outstate.exchangeHash)
  2506. .update(!self.server ? 'F' : 'E', 'ascii')
  2507. .update(outstate.sessionId)
  2508. .digest();
  2509. while (key.length < inHMACInfo.len) {
  2510. key = Buffer.concat([key,
  2511. crypto.createHash(dhHashAlgo)
  2512. .update(secret)
  2513. .update(outstate.exchangeHash)
  2514. .update(key)
  2515. .digest()]);
  2516. }
  2517. if (key.length > inHMACInfo.len)
  2518. key = key.slice(0, inHMACInfo.len);
  2519. instate.hmac.key = key;
  2520. } else {
  2521. instate.hmac.key = undefined;
  2522. }
  2523. // Create a reusable buffer for message verification purposes
  2524. var inHMACSize = inCipherInfo.authLen || instate.hmac.info.actualLen;
  2525. if (!instate.hmac.buf
  2526. || instate.hmac.buf.length !== inHMACSize) {
  2527. instate.hmac.buf = Buffer.allocUnsafe(inHMACSize);
  2528. }
  2529. outstate.exchangeHash = undefined;
  2530. if (outstate.compress.type === 'zlib') {
  2531. outstate.compress.instance = zlib.createDeflate(ZLIB_OPTS);
  2532. outstate.compress.queue = [];
  2533. } else if (outstate.compress.type === 'none') {
  2534. outstate.compress.instance = false;
  2535. outstate.compress.queue = null;
  2536. }
  2537. if (instate.decompress.type === 'zlib')
  2538. instate.decompress.instance = zlib.createInflate(ZLIB_OPTS);
  2539. else if (instate.decompress.type === 'none')
  2540. instate.decompress.instance = false;
  2541. self.bytesSent = self.bytesReceived = 0;
  2542. if (outstate.status === OUT_REKEYING) {
  2543. outstate.status = OUT_READY;
  2544. // Empty our outbound buffer of any data we tried to send during the
  2545. // re-keying process
  2546. var queue = outstate.rekeyQueue;
  2547. var qlen = queue.length;
  2548. var q = 0;
  2549. outstate.rekeyQueue = [];
  2550. for (; q < qlen; ++q) {
  2551. if (Buffer.isBuffer(queue[q]))
  2552. send(self, queue[q]);
  2553. else
  2554. send(self, queue[q][0], queue[q][1]);
  2555. }
  2556. // Now empty our inbound buffer of any non-transport layer packets we
  2557. // received during the re-keying process
  2558. queue = instate.rekeyQueue;
  2559. qlen = queue.length;
  2560. q = 0;
  2561. instate.rekeyQueue = [];
  2562. var curSeqno = instate.seqno;
  2563. for (; q < qlen; ++q) {
  2564. instate.seqno = queue[q][0];
  2565. instate.payload = queue[q][1];
  2566. if (parsePacket(self) === false)
  2567. return;
  2568. if (instate.status === IN_INIT) {
  2569. // We were reset due to some error/disagreement ?
  2570. return;
  2571. }
  2572. }
  2573. instate.seqno = curSeqno;
  2574. } else {
  2575. outstate.status = OUT_READY;
  2576. if (instate.status === IN_PACKET) {
  2577. // Explicitly update incoming packet parser status in order to get the
  2578. // correct decipher, hmac, etc. states.
  2579. // We only get here if the host fingerprint callback was called
  2580. // asynchronously and the incoming packet parser is still expecting an
  2581. // unencrypted packet, etc.
  2582. self.debug('DEBUG: Parser: IN_PACKETBEFORE (update) (expecting '
  2583. + inCipherInfo.blockLen + ')');
  2584. // Wait for the right number of bytes so we can determine the incoming
  2585. // packet length
  2586. expectData(self,
  2587. EXP_TYPE_BYTES,
  2588. inCipherInfo.blockLen,
  2589. instate.decrypt.buf);
  2590. }
  2591. self.emit('ready');
  2592. }
  2593. }
  2594. function getPacketType(self, pktType) {
  2595. var kex = self._state.kex;
  2596. if (kex) {
  2597. // Disambiguate
  2598. switch (pktType) {
  2599. case 30:
  2600. return kex.pktInit;
  2601. case 31:
  2602. switch (kex.type) {
  2603. case 'group':
  2604. return 'KEXDH_REPLY';
  2605. case 'groupex':
  2606. return 'KEXDH_GEX_GROUP';
  2607. default:
  2608. return 'KEXECDH_REPLY';
  2609. }
  2610. break;
  2611. case 33:
  2612. if (kex.type === 'groupex')
  2613. return 'KEXDH_GEX_REPLY';
  2614. }
  2615. }
  2616. return MESSAGE[pktType];
  2617. }
  2618. function parsePacket(self, callback) {
  2619. var instate = self._state.incoming;
  2620. var outstate = self._state.outgoing;
  2621. var payload = instate.payload;
  2622. var seqno = instate.seqno;
  2623. var serviceName;
  2624. var lang;
  2625. var message;
  2626. var info;
  2627. var chan;
  2628. var data;
  2629. var srcIP;
  2630. var srcPort;
  2631. var sender;
  2632. var window;
  2633. var packetSize;
  2634. var recipient;
  2635. var description;
  2636. var socketPath;
  2637. if (++instate.seqno > MAX_SEQNO)
  2638. instate.seqno = 0;
  2639. if (instate.ignoreNext) {
  2640. self.debug('DEBUG: Parser: Packet ignored');
  2641. instate.ignoreNext = false;
  2642. return;
  2643. }
  2644. var type = payload[0];
  2645. if (type === undefined)
  2646. return false;
  2647. // If we receive a packet during handshake that is not the expected packet
  2648. // and it is not one of: DISCONNECT, IGNORE, UNIMPLEMENTED, or DEBUG, then we
  2649. // close the stream
  2650. if (outstate.status !== OUT_READY
  2651. && getPacketType(self, type) !== instate.expectedPacket
  2652. && type < 1
  2653. && type > 4) {
  2654. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, expected: '
  2655. + instate.expectedPacket
  2656. + ' but got: '
  2657. + getPacketType(self, type));
  2658. // XXX: Potential issue where the module user decides to initiate a rekey
  2659. // via KEXINIT() (which sets `expectedPacket`) after receiving a packet
  2660. // and there is still another packet already waiting to be parsed at the
  2661. // time the KEXINIT is written. this will cause an unexpected disconnect...
  2662. self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
  2663. var err = new Error('Received unexpected packet');
  2664. err.level = 'protocol';
  2665. self.emit('error', err);
  2666. return false;
  2667. }
  2668. if (type === MESSAGE.CHANNEL_DATA) {
  2669. /*
  2670. byte SSH_MSG_CHANNEL_DATA
  2671. uint32 recipient channel
  2672. string data
  2673. */
  2674. chan = readInt(payload, 1, self, callback);
  2675. if (chan === false)
  2676. return false;
  2677. // TODO: MAX_CHAN_DATA_LEN here should really be dependent upon the
  2678. // channel's packet size. The ssh2 module uses 32KB, so we'll hard
  2679. // code this for now ...
  2680. data = readString(payload, 5, self, callback, 32768);
  2681. if (data === false)
  2682. return false;
  2683. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_DATA ('
  2684. + chan
  2685. + ')');
  2686. self.emit('CHANNEL_DATA:' + chan, data);
  2687. } else if (type === MESSAGE.CHANNEL_EXTENDED_DATA) {
  2688. /*
  2689. byte SSH_MSG_CHANNEL_EXTENDED_DATA
  2690. uint32 recipient channel
  2691. uint32 data_type_code
  2692. string data
  2693. */
  2694. chan = readInt(payload, 1, self, callback);
  2695. if (chan === false)
  2696. return false;
  2697. var dataType = readInt(payload, 5, self, callback);
  2698. if (dataType === false)
  2699. return false;
  2700. data = readString(payload, 9, self, callback);
  2701. if (data === false)
  2702. return false;
  2703. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: '
  2704. + 'CHANNEL_EXTENDED_DATA ('
  2705. + chan
  2706. + ')');
  2707. self.emit('CHANNEL_EXTENDED_DATA:' + chan, dataType, data);
  2708. } else if (type === MESSAGE.CHANNEL_WINDOW_ADJUST) {
  2709. /*
  2710. byte SSH_MSG_CHANNEL_WINDOW_ADJUST
  2711. uint32 recipient channel
  2712. uint32 bytes to add
  2713. */
  2714. chan = readInt(payload, 1, self, callback);
  2715. if (chan === false)
  2716. return false;
  2717. var bytesToAdd = readInt(payload, 5, self, callback);
  2718. if (bytesToAdd === false)
  2719. return false;
  2720. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: '
  2721. + 'CHANNEL_WINDOW_ADJUST ('
  2722. + chan
  2723. + ', '
  2724. + bytesToAdd
  2725. + ')');
  2726. self.emit('CHANNEL_WINDOW_ADJUST:' + chan, bytesToAdd);
  2727. } else if (type === MESSAGE.CHANNEL_SUCCESS) {
  2728. /*
  2729. byte SSH_MSG_CHANNEL_SUCCESS
  2730. uint32 recipient channel
  2731. */
  2732. chan = readInt(payload, 1, self, callback);
  2733. if (chan === false)
  2734. return false;
  2735. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_SUCCESS ('
  2736. + chan
  2737. + ')');
  2738. self.emit('CHANNEL_SUCCESS:' + chan);
  2739. } else if (type === MESSAGE.CHANNEL_FAILURE) {
  2740. /*
  2741. byte SSH_MSG_CHANNEL_FAILURE
  2742. uint32 recipient channel
  2743. */
  2744. chan = readInt(payload, 1, self, callback);
  2745. if (chan === false)
  2746. return false;
  2747. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_FAILURE ('
  2748. + chan
  2749. + ')');
  2750. self.emit('CHANNEL_FAILURE:' + chan);
  2751. } else if (type === MESSAGE.CHANNEL_EOF) {
  2752. /*
  2753. byte SSH_MSG_CHANNEL_EOF
  2754. uint32 recipient channel
  2755. */
  2756. chan = readInt(payload, 1, self, callback);
  2757. if (chan === false)
  2758. return false;
  2759. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_EOF ('
  2760. + chan
  2761. + ')');
  2762. self.emit('CHANNEL_EOF:' + chan);
  2763. } else if (type === MESSAGE.CHANNEL_OPEN) {
  2764. /*
  2765. byte SSH_MSG_CHANNEL_OPEN
  2766. string channel type in US-ASCII only
  2767. uint32 sender channel
  2768. uint32 initial window size
  2769. uint32 maximum packet size
  2770. .... channel type specific data follows
  2771. */
  2772. var chanType = readString(payload, 1, 'ascii', self, callback);
  2773. if (chanType === false)
  2774. return false;
  2775. sender = readInt(payload, payload._pos, self, callback);
  2776. if (sender === false)
  2777. return false;
  2778. window = readInt(payload, payload._pos += 4, self, callback);
  2779. if (window === false)
  2780. return false;
  2781. packetSize = readInt(payload, payload._pos += 4, self, callback);
  2782. if (packetSize === false)
  2783. return false;
  2784. var channel;
  2785. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_OPEN ('
  2786. + sender
  2787. + ', '
  2788. + chanType
  2789. + ')');
  2790. if (chanType === 'forwarded-tcpip' // Server->Client
  2791. || chanType === 'direct-tcpip') { // Client->Server
  2792. /*
  2793. string address that was connected / host to connect
  2794. uint32 port that was connected / port to connect
  2795. string originator IP address
  2796. uint32 originator port
  2797. */
  2798. var destIP = readString(payload,
  2799. payload._pos += 4,
  2800. 'ascii',
  2801. self,
  2802. callback);
  2803. if (destIP === false)
  2804. return false;
  2805. var destPort = readInt(payload, payload._pos, self, callback);
  2806. if (destPort === false)
  2807. return false;
  2808. srcIP = readString(payload, payload._pos += 4, 'ascii', self, callback);
  2809. if (srcIP === false)
  2810. return false;
  2811. srcPort = readInt(payload, payload._pos, self, callback);
  2812. if (srcPort === false)
  2813. return false;
  2814. channel = {
  2815. type: chanType,
  2816. sender: sender,
  2817. window: window,
  2818. packetSize: packetSize,
  2819. data: {
  2820. destIP: destIP,
  2821. destPort: destPort,
  2822. srcIP: srcIP,
  2823. srcPort: srcPort
  2824. }
  2825. };
  2826. } else if (// Server->Client
  2827. chanType === 'forwarded-streamlocal@openssh.com'
  2828. // Client->Server
  2829. || chanType === 'direct-streamlocal@openssh.com') {
  2830. /*
  2831. string socket path
  2832. string reserved for future use
  2833. */
  2834. socketPath = readString(payload,
  2835. payload._pos += 4,
  2836. 'utf8',
  2837. self,
  2838. callback);
  2839. if (socketPath === false)
  2840. return false;
  2841. channel = {
  2842. type: chanType,
  2843. sender: sender,
  2844. window: window,
  2845. packetSize: packetSize,
  2846. data: {
  2847. socketPath: socketPath,
  2848. }
  2849. };
  2850. } else if (chanType === 'x11') { // Server->Client
  2851. /*
  2852. string originator address (e.g., "192.168.7.38")
  2853. uint32 originator port
  2854. */
  2855. srcIP = readString(payload, payload._pos += 4, 'ascii', self, callback);
  2856. if (srcIP === false)
  2857. return false;
  2858. srcPort = readInt(payload, payload._pos, self, callback);
  2859. if (srcPort === false)
  2860. return false;
  2861. channel = {
  2862. type: chanType,
  2863. sender: sender,
  2864. window: window,
  2865. packetSize: packetSize,
  2866. data: {
  2867. srcIP: srcIP,
  2868. srcPort: srcPort
  2869. }
  2870. };
  2871. } else {
  2872. // 'session' (Client->Server), 'auth-agent@openssh.com' (Server->Client)
  2873. channel = {
  2874. type: chanType,
  2875. sender: sender,
  2876. window: window,
  2877. packetSize: packetSize,
  2878. data: {}
  2879. };
  2880. }
  2881. self.emit('CHANNEL_OPEN', channel);
  2882. } else if (type === MESSAGE.CHANNEL_OPEN_CONFIRMATION) {
  2883. /*
  2884. byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
  2885. uint32 recipient channel
  2886. uint32 sender channel
  2887. uint32 initial window size
  2888. uint32 maximum packet size
  2889. .... channel type specific data follows
  2890. */
  2891. // "The 'recipient channel' is the channel number given in the
  2892. // original open request, and 'sender channel' is the channel number
  2893. // allocated by the other side."
  2894. recipient = readInt(payload, 1, self, callback);
  2895. if (recipient === false)
  2896. return false;
  2897. sender = readInt(payload, 5, self, callback);
  2898. if (sender === false)
  2899. return false;
  2900. window = readInt(payload, 9, self, callback);
  2901. if (window === false)
  2902. return false;
  2903. packetSize = readInt(payload, 13, self, callback);
  2904. if (packetSize === false)
  2905. return false;
  2906. info = {
  2907. recipient: recipient,
  2908. sender: sender,
  2909. window: window,
  2910. packetSize: packetSize
  2911. };
  2912. if (payload.length > 17)
  2913. info.data = payload.slice(17);
  2914. self.emit('CHANNEL_OPEN_CONFIRMATION:' + info.recipient, info);
  2915. } else if (type === MESSAGE.CHANNEL_OPEN_FAILURE) {
  2916. /*
  2917. byte SSH_MSG_CHANNEL_OPEN_FAILURE
  2918. uint32 recipient channel
  2919. uint32 reason code
  2920. string description in ISO-10646 UTF-8 encoding
  2921. string language tag
  2922. */
  2923. recipient = readInt(payload, 1, self, callback);
  2924. if (recipient === false)
  2925. return false;
  2926. var reasonCode = readInt(payload, 5, self, callback);
  2927. if (reasonCode === false)
  2928. return false;
  2929. description = readString(payload, 9, 'utf8', self, callback);
  2930. if (description === false)
  2931. return false;
  2932. lang = readString(payload, payload._pos, 'utf8', self, callback);
  2933. if (lang === false)
  2934. return false;
  2935. payload._pos = 9;
  2936. info = {
  2937. recipient: recipient,
  2938. reasonCode: reasonCode,
  2939. reason: CHANNEL_OPEN_FAILURE[reasonCode],
  2940. description: description,
  2941. lang: lang
  2942. };
  2943. self.emit('CHANNEL_OPEN_FAILURE:' + info.recipient, info);
  2944. } else if (type === MESSAGE.CHANNEL_CLOSE) {
  2945. /*
  2946. byte SSH_MSG_CHANNEL_CLOSE
  2947. uint32 recipient channel
  2948. */
  2949. chan = readInt(payload, 1, self, callback);
  2950. if (chan === false)
  2951. return false;
  2952. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_CLOSE ('
  2953. + chan
  2954. + ')');
  2955. self.emit('CHANNEL_CLOSE:' + chan);
  2956. } else if (type === MESSAGE.IGNORE) {
  2957. /*
  2958. byte SSH_MSG_IGNORE
  2959. string data
  2960. */
  2961. } else if (type === MESSAGE.DISCONNECT) {
  2962. /*
  2963. byte SSH_MSG_DISCONNECT
  2964. uint32 reason code
  2965. string description in ISO-10646 UTF-8 encoding
  2966. string language tag
  2967. */
  2968. var reason = readInt(payload, 1, self, callback);
  2969. if (reason === false)
  2970. return false;
  2971. var reasonText = DISCONNECT_REASON[reason];
  2972. description = readString(payload, 5, 'utf8', self, callback);
  2973. if (description === false)
  2974. return false;
  2975. if (payload._pos < payload.length)
  2976. lang = readString(payload, payload._pos, 'ascii', self, callback);
  2977. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: DISCONNECT ('
  2978. + reasonText
  2979. + ')');
  2980. self.emit('DISCONNECT', reasonText, reason, description, lang);
  2981. } else if (type === MESSAGE.DEBUG) {
  2982. /*
  2983. byte SSH_MSG_DEBUG
  2984. boolean always_display
  2985. string message in ISO-10646 UTF-8 encoding
  2986. string language tag
  2987. */
  2988. message = readString(payload, 2, 'utf8', self, callback);
  2989. if (message === false)
  2990. return false;
  2991. lang = readString(payload, payload._pos, 'ascii', self, callback);
  2992. if (lang === false)
  2993. return false;
  2994. self.emit('DEBUG', message, lang);
  2995. } else if (type === MESSAGE.NEWKEYS) {
  2996. /*
  2997. byte SSH_MSG_NEW_KEYS
  2998. */
  2999. self.emit('NEWKEYS');
  3000. } else if (type === MESSAGE.SERVICE_REQUEST) {
  3001. /*
  3002. byte SSH_MSG_SERVICE_REQUEST
  3003. string service name
  3004. */
  3005. serviceName = readString(payload, 1, 'ascii', self, callback);
  3006. if (serviceName === false)
  3007. return false;
  3008. self.emit('SERVICE_REQUEST', serviceName);
  3009. } else if (type === MESSAGE.SERVICE_ACCEPT) {
  3010. /*
  3011. byte SSH_MSG_SERVICE_ACCEPT
  3012. string service name
  3013. */
  3014. serviceName = readString(payload, 1, 'ascii', self, callback);
  3015. if (serviceName === false)
  3016. return false;
  3017. self.emit('SERVICE_ACCEPT', serviceName);
  3018. } else if (type === MESSAGE.USERAUTH_REQUEST) {
  3019. /*
  3020. byte SSH_MSG_USERAUTH_REQUEST
  3021. string user name in ISO-10646 UTF-8 encoding [RFC3629]
  3022. string service name in US-ASCII
  3023. string method name in US-ASCII
  3024. .... method specific fields
  3025. */
  3026. var username = readString(payload, 1, 'utf8', self, callback);
  3027. if (username === false)
  3028. return false;
  3029. var svcName = readString(payload, payload._pos, 'ascii', self, callback);
  3030. if (svcName === false)
  3031. return false;
  3032. var method = readString(payload, payload._pos, 'ascii', self, callback);
  3033. if (method === false)
  3034. return false;
  3035. var methodData;
  3036. var methodDesc;
  3037. if (method === 'password') {
  3038. methodData = readString(payload,
  3039. payload._pos + 1,
  3040. 'utf8',
  3041. self,
  3042. callback);
  3043. if (methodData === false)
  3044. return false;
  3045. } else if (method === 'publickey' || method === 'hostbased') {
  3046. var pkSigned;
  3047. var keyAlgo;
  3048. var key;
  3049. var signature;
  3050. var blob;
  3051. var hostname;
  3052. var userlocal;
  3053. if (method === 'publickey') {
  3054. pkSigned = payload[payload._pos++];
  3055. if (pkSigned === undefined)
  3056. return false;
  3057. pkSigned = (pkSigned !== 0);
  3058. }
  3059. keyAlgo = readString(payload, payload._pos, 'ascii', self, callback);
  3060. if (keyAlgo === false)
  3061. return false;
  3062. key = readString(payload, payload._pos, self, callback);
  3063. if (key === false)
  3064. return false;
  3065. if (pkSigned || method === 'hostbased') {
  3066. if (method === 'hostbased') {
  3067. hostname = readString(payload, payload._pos, 'ascii', self, callback);
  3068. if (hostname === false)
  3069. return false;
  3070. userlocal = readString(payload, payload._pos, 'utf8', self, callback);
  3071. if (userlocal === false)
  3072. return false;
  3073. }
  3074. var blobEnd = payload._pos;
  3075. signature = readString(payload, blobEnd, self, callback);
  3076. if (signature === false)
  3077. return false;
  3078. if (signature.length > (4 + keyAlgo.length + 4)
  3079. && signature.toString('ascii', 4, 4 + keyAlgo.length) === keyAlgo) {
  3080. // Skip algoLen + algo + sigLen
  3081. signature = signature.slice(4 + keyAlgo.length + 4);
  3082. }
  3083. signature = sigSSHToASN1(signature, keyAlgo, self, callback);
  3084. if (signature === false)
  3085. return false;
  3086. blob = Buffer.allocUnsafe(4 + outstate.sessionId.length + blobEnd);
  3087. writeUInt32BE(blob, outstate.sessionId.length, 0);
  3088. outstate.sessionId.copy(blob, 4);
  3089. payload.copy(blob, 4 + outstate.sessionId.length, 0, blobEnd);
  3090. } else {
  3091. methodDesc = 'publickey -- check';
  3092. }
  3093. methodData = {
  3094. keyAlgo: keyAlgo,
  3095. key: key,
  3096. signature: signature,
  3097. blob: blob,
  3098. localHostname: hostname,
  3099. localUsername: userlocal
  3100. };
  3101. } else if (method === 'keyboard-interactive') {
  3102. // Skip language, it's deprecated
  3103. var skipLen = readInt(payload, payload._pos, self, callback);
  3104. if (skipLen === false)
  3105. return false;
  3106. methodData = readString(payload,
  3107. payload._pos + 4 + skipLen,
  3108. 'utf8',
  3109. self,
  3110. callback);
  3111. if (methodData === false)
  3112. return false;
  3113. } else if (method !== 'none')
  3114. methodData = payload.slice(payload._pos);
  3115. if (methodDesc === undefined)
  3116. methodDesc = method;
  3117. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: USERAUTH_REQUEST ('
  3118. + methodDesc
  3119. + ')');
  3120. self._state.authsQueue.push(method);
  3121. self.emit('USERAUTH_REQUEST', username, svcName, method, methodData);
  3122. } else if (type === MESSAGE.USERAUTH_SUCCESS) {
  3123. /*
  3124. byte SSH_MSG_USERAUTH_SUCCESS
  3125. */
  3126. if (outstate.compress.type === 'zlib@openssh.com') {
  3127. outstate.compress.instance = zlib.createDeflate(ZLIB_OPTS);
  3128. outstate.compress.queue = [];
  3129. }
  3130. if (instate.decompress.type === 'zlib@openssh.com')
  3131. instate.decompress.instance = zlib.createInflate(ZLIB_OPTS);
  3132. self._state.authsQueue.shift();
  3133. self.emit('USERAUTH_SUCCESS');
  3134. } else if (type === MESSAGE.USERAUTH_FAILURE) {
  3135. /*
  3136. byte SSH_MSG_USERAUTH_FAILURE
  3137. name-list authentications that can continue
  3138. boolean partial success
  3139. */
  3140. var auths = readString(payload, 1, 'ascii', self, callback);
  3141. if (auths === false)
  3142. return false;
  3143. var partSuccess = payload[payload._pos];
  3144. if (partSuccess === undefined)
  3145. return false;
  3146. partSuccess = (partSuccess !== 0);
  3147. auths = auths.split(',');
  3148. self._state.authsQueue.shift();
  3149. self.emit('USERAUTH_FAILURE', auths, partSuccess);
  3150. } else if (type === MESSAGE.USERAUTH_BANNER) {
  3151. /*
  3152. byte SSH_MSG_USERAUTH_BANNER
  3153. string message in ISO-10646 UTF-8 encoding
  3154. string language tag
  3155. */
  3156. message = readString(payload, 1, 'utf8', self, callback);
  3157. if (message === false)
  3158. return false;
  3159. lang = readString(payload, payload._pos, 'utf8', self, callback);
  3160. if (lang === false)
  3161. return false;
  3162. self.emit('USERAUTH_BANNER', message, lang);
  3163. } else if (type === MESSAGE.GLOBAL_REQUEST) {
  3164. /*
  3165. byte SSH_MSG_GLOBAL_REQUEST
  3166. string request name in US-ASCII only
  3167. boolean want reply
  3168. .... request-specific data follows
  3169. */
  3170. var request = readString(payload, 1, 'ascii', self, callback);
  3171. if (request === false) {
  3172. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: GLOBAL_REQUEST');
  3173. return false;
  3174. }
  3175. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: GLOBAL_REQUEST ('
  3176. + request
  3177. + ')');
  3178. var wantReply = payload[payload._pos++];
  3179. if (wantReply === undefined)
  3180. return false;
  3181. wantReply = (wantReply !== 0);
  3182. var reqData;
  3183. if (request === 'tcpip-forward' || request === 'cancel-tcpip-forward') {
  3184. var bindAddr = readString(payload, payload._pos, 'ascii', self, callback);
  3185. if (bindAddr === false)
  3186. return false;
  3187. var bindPort = readInt(payload, payload._pos, self, callback);
  3188. if (bindPort === false)
  3189. return false;
  3190. reqData = {
  3191. bindAddr: bindAddr,
  3192. bindPort: bindPort
  3193. };
  3194. } else if (request === 'streamlocal-forward@openssh.com'
  3195. || request === 'cancel-streamlocal-forward@openssh.com') {
  3196. socketPath = readString(payload, payload._pos, 'utf8', self, callback);
  3197. if (socketPath === false)
  3198. return false;
  3199. reqData = {
  3200. socketPath: socketPath
  3201. };
  3202. } else if (request === 'no-more-sessions@openssh.com') {
  3203. // No data
  3204. } else {
  3205. reqData = payload.slice(payload._pos);
  3206. }
  3207. self.emit('GLOBAL_REQUEST', request, wantReply, reqData);
  3208. } else if (type === MESSAGE.REQUEST_SUCCESS) {
  3209. /*
  3210. byte SSH_MSG_REQUEST_SUCCESS
  3211. .... response specific data
  3212. */
  3213. if (payload.length > 1)
  3214. self.emit('REQUEST_SUCCESS', payload.slice(1));
  3215. else
  3216. self.emit('REQUEST_SUCCESS');
  3217. } else if (type === MESSAGE.REQUEST_FAILURE) {
  3218. /*
  3219. byte SSH_MSG_REQUEST_FAILURE
  3220. */
  3221. self.emit('REQUEST_FAILURE');
  3222. } else if (type === MESSAGE.UNIMPLEMENTED) {
  3223. /*
  3224. byte SSH_MSG_UNIMPLEMENTED
  3225. uint32 packet sequence number of rejected message
  3226. */
  3227. // TODO
  3228. } else if (type === MESSAGE.KEXINIT)
  3229. return parse_KEXINIT(self, callback);
  3230. else if (type === MESSAGE.CHANNEL_REQUEST)
  3231. return parse_CHANNEL_REQUEST(self, callback);
  3232. else if (type >= 30 && type <= 49) // Key exchange method-specific messages
  3233. return parse_KEX(self, type, callback);
  3234. else if (type >= 60 && type <= 70) // User auth context-specific messages
  3235. return parse_USERAUTH(self, type, callback);
  3236. else {
  3237. // Unknown packet type
  3238. var unimpl = Buffer.allocUnsafe(1 + 4);
  3239. unimpl[0] = MESSAGE.UNIMPLEMENTED;
  3240. writeUInt32BE(unimpl, seqno, 1);
  3241. send(self, unimpl);
  3242. }
  3243. }
  3244. function parse_KEXINIT(self, callback) {
  3245. var instate = self._state.incoming;
  3246. var payload = instate.payload;
  3247. /*
  3248. byte SSH_MSG_KEXINIT
  3249. byte[16] cookie (random bytes)
  3250. name-list kex_algorithms
  3251. name-list server_host_key_algorithms
  3252. name-list encryption_algorithms_client_to_server
  3253. name-list encryption_algorithms_server_to_client
  3254. name-list mac_algorithms_client_to_server
  3255. name-list mac_algorithms_server_to_client
  3256. name-list compression_algorithms_client_to_server
  3257. name-list compression_algorithms_server_to_client
  3258. name-list languages_client_to_server
  3259. name-list languages_server_to_client
  3260. boolean first_kex_packet_follows
  3261. uint32 0 (reserved for future extension)
  3262. */
  3263. var init = {
  3264. algorithms: {
  3265. kex: undefined,
  3266. srvHostKey: undefined,
  3267. cs: {
  3268. encrypt: undefined,
  3269. mac: undefined,
  3270. compress: undefined
  3271. },
  3272. sc: {
  3273. encrypt: undefined,
  3274. mac: undefined,
  3275. compress: undefined
  3276. }
  3277. },
  3278. languages: {
  3279. cs: undefined,
  3280. sc: undefined
  3281. }
  3282. };
  3283. var val;
  3284. val = readList(payload, 17, self, callback);
  3285. if (val === false)
  3286. return false;
  3287. init.algorithms.kex = val;
  3288. val = readList(payload, payload._pos, self, callback);
  3289. if (val === false)
  3290. return false;
  3291. init.algorithms.srvHostKey = val;
  3292. val = readList(payload, payload._pos, self, callback);
  3293. if (val === false)
  3294. return false;
  3295. init.algorithms.cs.encrypt = val;
  3296. val = readList(payload, payload._pos, self, callback);
  3297. if (val === false)
  3298. return false;
  3299. init.algorithms.sc.encrypt = val;
  3300. val = readList(payload, payload._pos, self, callback);
  3301. if (val === false)
  3302. return false;
  3303. init.algorithms.cs.mac = val;
  3304. val = readList(payload, payload._pos, self, callback);
  3305. if (val === false)
  3306. return false;
  3307. init.algorithms.sc.mac = val;
  3308. val = readList(payload, payload._pos, self, callback);
  3309. if (val === false)
  3310. return false;
  3311. init.algorithms.cs.compress = val;
  3312. val = readList(payload, payload._pos, self, callback);
  3313. if (val === false)
  3314. return false;
  3315. init.algorithms.sc.compress = val;
  3316. val = readList(payload, payload._pos, self, callback);
  3317. if (val === false)
  3318. return false;
  3319. init.languages.cs = val;
  3320. val = readList(payload, payload._pos, self, callback);
  3321. if (val === false)
  3322. return false;
  3323. init.languages.sc = val;
  3324. var firstFollows = (payload._pos < payload.length
  3325. && payload[payload._pos] === 1);
  3326. instate.kexinit = payload;
  3327. self.emit('KEXINIT', init, firstFollows);
  3328. }
  3329. function parse_KEX(self, type, callback) {
  3330. var state = self._state;
  3331. var instate = state.incoming;
  3332. var payload = instate.payload;
  3333. if (state.outgoing.status === OUT_READY
  3334. || getPacketType(self, type) !== instate.expectedPacket) {
  3335. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, expected: '
  3336. + instate.expectedPacket
  3337. + ' but got: '
  3338. + getPacketType(self, type));
  3339. self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
  3340. var err = new Error('Received unexpected packet');
  3341. err.level = 'protocol';
  3342. self.emit('error', err);
  3343. return false;
  3344. }
  3345. if (state.kex.type === 'groupex') {
  3346. // Dynamic group exchange-related
  3347. if (self.server) {
  3348. // TODO: Support group exchange server-side
  3349. self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
  3350. var err = new Error('DH group exchange not supported by server');
  3351. err.level = 'handshake';
  3352. self.emit('error', err);
  3353. return false;
  3354. } else {
  3355. if (type === MESSAGE.KEXDH_GEX_GROUP) {
  3356. /*
  3357. byte SSH_MSG_KEX_DH_GEX_GROUP
  3358. mpint p, safe prime
  3359. mpint g, generator for subgroup in GF(p)
  3360. */
  3361. var prime = readString(payload, 1, self, callback);
  3362. if (prime === false)
  3363. return false;
  3364. var gen = readString(payload, payload._pos, self, callback);
  3365. if (gen === false)
  3366. return false;
  3367. self.emit('KEXDH_GEX_GROUP', prime, gen);
  3368. } else if (type === MESSAGE.KEXDH_GEX_REPLY)
  3369. return parse_KEXDH_REPLY(self, callback);
  3370. }
  3371. } else {
  3372. // Static group or ECDH-related
  3373. if (type === MESSAGE.KEXDH_INIT) {
  3374. /*
  3375. byte SSH_MSG_KEXDH_INIT
  3376. mpint e
  3377. */
  3378. var e = readString(payload, 1, self, callback);
  3379. if (e === false)
  3380. return false;
  3381. self.emit('KEXDH_INIT', e);
  3382. } else if (type === MESSAGE.KEXDH_REPLY)
  3383. return parse_KEXDH_REPLY(self, callback);
  3384. }
  3385. }
  3386. function parse_KEXDH_REPLY(self, callback) {
  3387. var payload = self._state.incoming.payload;
  3388. /*
  3389. byte SSH_MSG_KEXDH_REPLY
  3390. / SSH_MSG_KEX_DH_GEX_REPLY
  3391. / SSH_MSG_KEX_ECDH_REPLY
  3392. string server public host key and certificates (K_S)
  3393. mpint f
  3394. string signature of H
  3395. */
  3396. var hostkey = readString(payload, 1, self, callback);
  3397. if (hostkey === false)
  3398. return false;
  3399. var pubkey = readString(payload, payload._pos, self, callback);
  3400. if (pubkey === false)
  3401. return false;
  3402. var sig = readString(payload, payload._pos, self, callback);
  3403. if (sig === false)
  3404. return false;
  3405. var info = {
  3406. hostkey: hostkey,
  3407. hostkey_format: undefined,
  3408. pubkey: pubkey,
  3409. sig: sig,
  3410. sig_format: undefined
  3411. };
  3412. var hostkey_format = readString(hostkey, 0, 'ascii', self, callback);
  3413. if (hostkey_format === false)
  3414. return false;
  3415. info.hostkey_format = hostkey_format;
  3416. var sig_format = readString(sig, 0, 'ascii', self, callback);
  3417. if (sig_format === false)
  3418. return false;
  3419. info.sig_format = sig_format;
  3420. self.emit('KEXDH_REPLY', info);
  3421. }
  3422. function parse_USERAUTH(self, type, callback) {
  3423. var state = self._state;
  3424. var authMethod = state.authsQueue[0];
  3425. var payload = state.incoming.payload;
  3426. var message;
  3427. var lang;
  3428. var text;
  3429. if (authMethod === 'password') {
  3430. if (type === MESSAGE.USERAUTH_PASSWD_CHANGEREQ) {
  3431. /*
  3432. byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
  3433. string prompt in ISO-10646 UTF-8 encoding
  3434. string language tag
  3435. */
  3436. message = readString(payload, 1, 'utf8', self, callback);
  3437. if (message === false)
  3438. return false;
  3439. lang = readString(payload, payload._pos, 'utf8', self, callback);
  3440. if (lang === false)
  3441. return false;
  3442. self.emit('USERAUTH_PASSWD_CHANGEREQ', message, lang);
  3443. }
  3444. } else if (authMethod === 'keyboard-interactive') {
  3445. if (type === MESSAGE.USERAUTH_INFO_REQUEST) {
  3446. /*
  3447. byte SSH_MSG_USERAUTH_INFO_REQUEST
  3448. string name (ISO-10646 UTF-8)
  3449. string instruction (ISO-10646 UTF-8)
  3450. string language tag -- MAY be empty
  3451. int num-prompts
  3452. string prompt[1] (ISO-10646 UTF-8)
  3453. boolean echo[1]
  3454. ...
  3455. string prompt[num-prompts] (ISO-10646 UTF-8)
  3456. boolean echo[num-prompts]
  3457. */
  3458. var name;
  3459. var instr;
  3460. var nprompts;
  3461. name = readString(payload, 1, 'utf8', self, callback);
  3462. if (name === false)
  3463. return false;
  3464. instr = readString(payload, payload._pos, 'utf8', self, callback);
  3465. if (instr === false)
  3466. return false;
  3467. lang = readString(payload, payload._pos, 'utf8', self, callback);
  3468. if (lang === false)
  3469. return false;
  3470. nprompts = readInt(payload, payload._pos, self, callback);
  3471. if (nprompts === false)
  3472. return false;
  3473. payload._pos += 4;
  3474. var prompts = [];
  3475. for (var prompt = 0; prompt < nprompts; ++prompt) {
  3476. text = readString(payload, payload._pos, 'utf8', self, callback);
  3477. if (text === false)
  3478. return false;
  3479. var echo = payload[payload._pos++];
  3480. if (echo === undefined)
  3481. return false;
  3482. echo = (echo !== 0);
  3483. prompts.push({
  3484. prompt: text,
  3485. echo: echo
  3486. });
  3487. }
  3488. self.emit('USERAUTH_INFO_REQUEST', name, instr, lang, prompts);
  3489. } else if (type === MESSAGE.USERAUTH_INFO_RESPONSE) {
  3490. /*
  3491. byte SSH_MSG_USERAUTH_INFO_RESPONSE
  3492. int num-responses
  3493. string response[1] (ISO-10646 UTF-8)
  3494. ...
  3495. string response[num-responses] (ISO-10646 UTF-8)
  3496. */
  3497. var nresponses = readInt(payload, 1, self, callback);
  3498. if (nresponses === false)
  3499. return false;
  3500. payload._pos = 5;
  3501. var responses = [];
  3502. for (var response = 0; response < nresponses; ++response) {
  3503. text = readString(payload, payload._pos, 'utf8', self, callback);
  3504. if (text === false)
  3505. return false;
  3506. responses.push(text);
  3507. }
  3508. self.emit('USERAUTH_INFO_RESPONSE', responses);
  3509. }
  3510. } else if (authMethod === 'publickey') {
  3511. if (type === MESSAGE.USERAUTH_PK_OK) {
  3512. /*
  3513. byte SSH_MSG_USERAUTH_PK_OK
  3514. string public key algorithm name from the request
  3515. string public key blob from the request
  3516. */
  3517. var authsQueue = self._state.authsQueue;
  3518. if (!authsQueue.length || authsQueue[0] !== 'publickey')
  3519. return;
  3520. authsQueue.shift();
  3521. self.emit('USERAUTH_PK_OK');
  3522. // XXX: Parse public key info? client currently can ignore it because
  3523. // there is only one outstanding auth request at any given time, so it
  3524. // knows which key was OK'd
  3525. }
  3526. } else if (authMethod !== undefined) {
  3527. // Invalid packet for this auth type
  3528. self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
  3529. var err = new Error('Invalid authentication method: ' + authMethod);
  3530. err.level = 'protocol';
  3531. self.emit('error', err);
  3532. }
  3533. }
  3534. function parse_CHANNEL_REQUEST(self, callback) {
  3535. var payload = self._state.incoming.payload;
  3536. var info;
  3537. var cols;
  3538. var rows;
  3539. var width;
  3540. var height;
  3541. var wantReply;
  3542. var signal;
  3543. var recipient = readInt(payload, 1, self, callback);
  3544. if (recipient === false)
  3545. return false;
  3546. var request = readString(payload, 5, 'ascii', self, callback);
  3547. if (request === false)
  3548. return false;
  3549. if (request === 'exit-status') { // Server->Client
  3550. /*
  3551. byte SSH_MSG_CHANNEL_REQUEST
  3552. uint32 recipient channel
  3553. string "exit-status"
  3554. boolean FALSE
  3555. uint32 exit_status
  3556. */
  3557. var code = readInt(payload, ++payload._pos, self, callback);
  3558. if (code === false)
  3559. return false;
  3560. info = {
  3561. recipient: recipient,
  3562. request: request,
  3563. wantReply: false,
  3564. code: code
  3565. };
  3566. } else if (request === 'exit-signal') { // Server->Client
  3567. /*
  3568. byte SSH_MSG_CHANNEL_REQUEST
  3569. uint32 recipient channel
  3570. string "exit-signal"
  3571. boolean FALSE
  3572. string signal name (without the "SIG" prefix)
  3573. boolean core dumped
  3574. string error message in ISO-10646 UTF-8 encoding
  3575. string language tag
  3576. */
  3577. var coredump;
  3578. if (!(self.remoteBugs & BUGS.OLD_EXIT)) {
  3579. signal = readString(payload, ++payload._pos, 'ascii', self, callback);
  3580. if (signal === false)
  3581. return false;
  3582. coredump = payload[payload._pos++];
  3583. if (coredump === undefined)
  3584. return false;
  3585. coredump = (coredump !== 0);
  3586. } else {
  3587. /*
  3588. Instead of `signal name` and `core dumped`, we have just:
  3589. uint32 signal number
  3590. */
  3591. signal = readInt(payload, ++payload._pos, self, callback);
  3592. if (signal === false)
  3593. return false;
  3594. switch (signal) {
  3595. case 1:
  3596. signal = 'HUP';
  3597. break;
  3598. case 2:
  3599. signal = 'INT';
  3600. break;
  3601. case 3:
  3602. signal = 'QUIT';
  3603. break;
  3604. case 6:
  3605. signal = 'ABRT';
  3606. break;
  3607. case 9:
  3608. signal = 'KILL';
  3609. break;
  3610. case 14:
  3611. signal = 'ALRM';
  3612. break;
  3613. case 15:
  3614. signal = 'TERM';
  3615. break;
  3616. default:
  3617. // Unknown or OS-specific
  3618. signal = 'UNKNOWN (' + signal + ')';
  3619. }
  3620. coredump = false;
  3621. }
  3622. var description = readString(payload, payload._pos, 'utf8', self,
  3623. callback);
  3624. if (description === false)
  3625. return false;
  3626. var lang = readString(payload, payload._pos, 'utf8', self, callback);
  3627. if (lang === false)
  3628. return false;
  3629. info = {
  3630. recipient: recipient,
  3631. request: request,
  3632. wantReply: false,
  3633. signal: signal,
  3634. coredump: coredump,
  3635. description: description,
  3636. lang: lang
  3637. };
  3638. } else if (request === 'pty-req') { // Client->Server
  3639. /*
  3640. byte SSH_MSG_CHANNEL_REQUEST
  3641. uint32 recipient channel
  3642. string "pty-req"
  3643. boolean want_reply
  3644. string TERM environment variable value (e.g., vt100)
  3645. uint32 terminal width, characters (e.g., 80)
  3646. uint32 terminal height, rows (e.g., 24)
  3647. uint32 terminal width, pixels (e.g., 640)
  3648. uint32 terminal height, pixels (e.g., 480)
  3649. string encoded terminal modes
  3650. */
  3651. wantReply = payload[payload._pos++];
  3652. if (wantReply === undefined)
  3653. return false;
  3654. wantReply = (wantReply !== 0);
  3655. var term = readString(payload, payload._pos, 'ascii', self, callback);
  3656. if (term === false)
  3657. return false;
  3658. cols = readInt(payload, payload._pos, self, callback);
  3659. if (cols === false)
  3660. return false;
  3661. rows = readInt(payload, payload._pos += 4, self, callback);
  3662. if (rows === false)
  3663. return false;
  3664. width = readInt(payload, payload._pos += 4, self, callback);
  3665. if (width === false)
  3666. return false;
  3667. height = readInt(payload, payload._pos += 4, self, callback);
  3668. if (height === false)
  3669. return false;
  3670. var modes = readString(payload, payload._pos += 4, self, callback);
  3671. if (modes === false)
  3672. return false;
  3673. modes = bytesToModes(modes);
  3674. info = {
  3675. recipient: recipient,
  3676. request: request,
  3677. wantReply: wantReply,
  3678. term: term,
  3679. cols: cols,
  3680. rows: rows,
  3681. width: width,
  3682. height: height,
  3683. modes: modes
  3684. };
  3685. } else if (request === 'window-change') { // Client->Server
  3686. /*
  3687. byte SSH_MSG_CHANNEL_REQUEST
  3688. uint32 recipient channel
  3689. string "window-change"
  3690. boolean FALSE
  3691. uint32 terminal width, columns
  3692. uint32 terminal height, rows
  3693. uint32 terminal width, pixels
  3694. uint32 terminal height, pixels
  3695. */
  3696. cols = readInt(payload, ++payload._pos, self, callback);
  3697. if (cols === false)
  3698. return false;
  3699. rows = readInt(payload, payload._pos += 4, self, callback);
  3700. if (rows === false)
  3701. return false;
  3702. width = readInt(payload, payload._pos += 4, self, callback);
  3703. if (width === false)
  3704. return false;
  3705. height = readInt(payload, payload._pos += 4, self, callback);
  3706. if (height === false)
  3707. return false;
  3708. info = {
  3709. recipient: recipient,
  3710. request: request,
  3711. wantReply: false,
  3712. cols: cols,
  3713. rows: rows,
  3714. width: width,
  3715. height: height
  3716. };
  3717. } else if (request === 'x11-req') { // Client->Server
  3718. /*
  3719. byte SSH_MSG_CHANNEL_REQUEST
  3720. uint32 recipient channel
  3721. string "x11-req"
  3722. boolean want reply
  3723. boolean single connection
  3724. string x11 authentication protocol
  3725. string x11 authentication cookie
  3726. uint32 x11 screen number
  3727. */
  3728. wantReply = payload[payload._pos++];
  3729. if (wantReply === undefined)
  3730. return false;
  3731. wantReply = (wantReply !== 0);
  3732. var single = payload[payload._pos++];
  3733. if (single === undefined)
  3734. return false;
  3735. single = (single !== 0);
  3736. var protocol = readString(payload, payload._pos, 'ascii', self, callback);
  3737. if (protocol === false)
  3738. return false;
  3739. var cookie = readString(payload, payload._pos, 'binary', self, callback);
  3740. if (cookie === false)
  3741. return false;
  3742. var screen = readInt(payload, payload._pos, self, callback);
  3743. if (screen === false)
  3744. return false;
  3745. info = {
  3746. recipient: recipient,
  3747. request: request,
  3748. wantReply: wantReply,
  3749. single: single,
  3750. protocol: protocol,
  3751. cookie: cookie,
  3752. screen: screen
  3753. };
  3754. } else if (request === 'env') { // Client->Server
  3755. /*
  3756. byte SSH_MSG_CHANNEL_REQUEST
  3757. uint32 recipient channel
  3758. string "env"
  3759. boolean want reply
  3760. string variable name
  3761. string variable value
  3762. */
  3763. wantReply = payload[payload._pos++];
  3764. if (wantReply === undefined)
  3765. return false;
  3766. wantReply = (wantReply !== 0);
  3767. var key = readString(payload, payload._pos, 'utf8', self, callback);
  3768. if (key === false)
  3769. return false;
  3770. var val = readString(payload, payload._pos, 'utf8', self, callback);
  3771. if (val === false)
  3772. return false;
  3773. info = {
  3774. recipient: recipient,
  3775. request: request,
  3776. wantReply: wantReply,
  3777. key: key,
  3778. val: val
  3779. };
  3780. } else if (request === 'shell') { // Client->Server
  3781. /*
  3782. byte SSH_MSG_CHANNEL_REQUEST
  3783. uint32 recipient channel
  3784. string "shell"
  3785. boolean want reply
  3786. */
  3787. wantReply = payload[payload._pos];
  3788. if (wantReply === undefined)
  3789. return false;
  3790. wantReply = (wantReply !== 0);
  3791. info = {
  3792. recipient: recipient,
  3793. request: request,
  3794. wantReply: wantReply
  3795. };
  3796. } else if (request === 'exec') { // Client->Server
  3797. /*
  3798. byte SSH_MSG_CHANNEL_REQUEST
  3799. uint32 recipient channel
  3800. string "exec"
  3801. boolean want reply
  3802. string command
  3803. */
  3804. wantReply = payload[payload._pos++];
  3805. if (wantReply === undefined)
  3806. return false;
  3807. wantReply = (wantReply !== 0);
  3808. var command = readString(payload, payload._pos, 'utf8', self, callback);
  3809. if (command === false)
  3810. return false;
  3811. info = {
  3812. recipient: recipient,
  3813. request: request,
  3814. wantReply: wantReply,
  3815. command: command
  3816. };
  3817. } else if (request === 'subsystem') { // Client->Server
  3818. /*
  3819. byte SSH_MSG_CHANNEL_REQUEST
  3820. uint32 recipient channel
  3821. string "subsystem"
  3822. boolean want reply
  3823. string subsystem name
  3824. */
  3825. wantReply = payload[payload._pos++];
  3826. if (wantReply === undefined)
  3827. return false;
  3828. wantReply = (wantReply !== 0);
  3829. var subsystem = readString(payload, payload._pos, 'utf8', self, callback);
  3830. if (subsystem === false)
  3831. return false;
  3832. info = {
  3833. recipient: recipient,
  3834. request: request,
  3835. wantReply: wantReply,
  3836. subsystem: subsystem
  3837. };
  3838. } else if (request === 'signal') { // Client->Server
  3839. /*
  3840. byte SSH_MSG_CHANNEL_REQUEST
  3841. uint32 recipient channel
  3842. string "signal"
  3843. boolean FALSE
  3844. string signal name (without the "SIG" prefix)
  3845. */
  3846. signal = readString(payload, ++payload._pos, 'ascii', self, callback);
  3847. if (signal === false)
  3848. return false;
  3849. info = {
  3850. recipient: recipient,
  3851. request: request,
  3852. wantReply: false,
  3853. signal: 'SIG' + signal
  3854. };
  3855. } else if (request === 'xon-xoff') { // Client->Server
  3856. /*
  3857. byte SSH_MSG_CHANNEL_REQUEST
  3858. uint32 recipient channel
  3859. string "xon-xoff"
  3860. boolean FALSE
  3861. boolean client can do
  3862. */
  3863. var clientControl = payload[++payload._pos];
  3864. if (clientControl === undefined)
  3865. return false;
  3866. clientControl = (clientControl !== 0);
  3867. info = {
  3868. recipient: recipient,
  3869. request: request,
  3870. wantReply: false,
  3871. clientControl: clientControl
  3872. };
  3873. } else if (request === 'auth-agent-req@openssh.com') { // Client->Server
  3874. /*
  3875. byte SSH_MSG_CHANNEL_REQUEST
  3876. uint32 recipient channel
  3877. string "auth-agent-req@openssh.com"
  3878. boolean want reply
  3879. */
  3880. wantReply = payload[payload._pos];
  3881. if (wantReply === undefined)
  3882. return false;
  3883. wantReply = (wantReply !== 0);
  3884. info = {
  3885. recipient: recipient,
  3886. request: request,
  3887. wantReply: wantReply
  3888. };
  3889. } else {
  3890. // Unknown request type
  3891. wantReply = payload[payload._pos];
  3892. if (wantReply === undefined)
  3893. return false;
  3894. wantReply = (wantReply !== 0);
  3895. info = {
  3896. recipient: recipient,
  3897. request: request,
  3898. wantReply: wantReply
  3899. };
  3900. }
  3901. self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_REQUEST ('
  3902. + recipient
  3903. + ', '
  3904. + request
  3905. + ')');
  3906. self.emit('CHANNEL_REQUEST:' + recipient, info);
  3907. }
  3908. function hmacVerify(self, data) {
  3909. var instate = self._state.incoming;
  3910. var hmac = instate.hmac;
  3911. self.debug('DEBUG: Parser: Verifying MAC');
  3912. if (instate.decrypt.info.authLen > 0) {
  3913. var decrypt = instate.decrypt;
  3914. var instance = decrypt.instance;
  3915. instance.setAuthTag(data);
  3916. var payload = instance.update(instate.packet);
  3917. instate.payload = payload.slice(1, instate.packet.length - payload[0]);
  3918. iv_inc(decrypt.iv);
  3919. decrypt.instance = crypto.createDecipheriv(
  3920. SSH_TO_OPENSSL[decrypt.type],
  3921. decrypt.key,
  3922. decrypt.iv
  3923. );
  3924. decrypt.instance.setAutoPadding(false);
  3925. return true;
  3926. } else {
  3927. var calcHmac = crypto.createHmac(SSH_TO_OPENSSL[hmac.type], hmac.key);
  3928. writeUInt32BE(HMAC_COMPUTE, instate.seqno, 0);
  3929. writeUInt32BE(HMAC_COMPUTE, instate.pktLen, 4);
  3930. HMAC_COMPUTE[8] = instate.padLen;
  3931. calcHmac.update(HMAC_COMPUTE);
  3932. calcHmac.update(instate.packet);
  3933. var mac = calcHmac.digest();
  3934. if (mac.length > instate.hmac.info.actualLen)
  3935. mac = mac.slice(0, instate.hmac.info.actualLen);
  3936. return timingSafeEqual(mac, data);
  3937. }
  3938. }
  3939. function decryptData(self, data) {
  3940. var instance = self._state.incoming.decrypt.instance;
  3941. self.debug('DEBUG: Parser: Decrypting');
  3942. return instance.update(data);
  3943. }
  3944. function expectData(self, type, amount, buffer) {
  3945. var expect = self._state.incoming.expect;
  3946. expect.amount = amount;
  3947. expect.type = type;
  3948. expect.ptr = 0;
  3949. if (buffer)
  3950. expect.buf = buffer;
  3951. else if (amount)
  3952. expect.buf = Buffer.allocUnsafe(amount);
  3953. }
  3954. function readList(buffer, start, stream, callback) {
  3955. var list = readString(buffer, start, 'ascii', stream, callback);
  3956. return (list !== false ? (list.length ? list.split(',') : []) : false);
  3957. }
  3958. function bytesToModes(buffer) {
  3959. var modes = {};
  3960. for (var i = 0, len = buffer.length, opcode; i < len; i += 5) {
  3961. opcode = buffer[i];
  3962. if (opcode === TERMINAL_MODE.TTY_OP_END
  3963. || TERMINAL_MODE[opcode] === undefined
  3964. || i + 5 > len)
  3965. break;
  3966. modes[TERMINAL_MODE[opcode]] = readUInt32BE(buffer, i + 1);
  3967. }
  3968. return modes;
  3969. }
  3970. function modesToBytes(modes) {
  3971. var RE_IS_NUM = /^\d+$/;
  3972. var keys = Object.keys(modes);
  3973. var b = 0;
  3974. var bytes = [];
  3975. for (var i = 0, len = keys.length, key, opcode, val; i < len; ++i) {
  3976. key = keys[i];
  3977. opcode = TERMINAL_MODE[key];
  3978. if (opcode
  3979. && !RE_IS_NUM.test(key)
  3980. && typeof modes[key] === 'number'
  3981. && key !== 'TTY_OP_END') {
  3982. val = modes[key];
  3983. bytes[b++] = opcode;
  3984. bytes[b++] = (val >>> 24) & 0xFF;
  3985. bytes[b++] = (val >>> 16) & 0xFF;
  3986. bytes[b++] = (val >>> 8) & 0xFF;
  3987. bytes[b++] = val & 0xFF;
  3988. }
  3989. }
  3990. bytes[b] = TERMINAL_MODE.TTY_OP_END;
  3991. return bytes;
  3992. }
  3993. // Shared outgoing functions
  3994. function KEXINIT(self, cb) { // Client/Server
  3995. randBytes(16, function(myCookie) {
  3996. /*
  3997. byte SSH_MSG_KEXINIT
  3998. byte[16] cookie (random bytes)
  3999. name-list kex_algorithms
  4000. name-list server_host_key_algorithms
  4001. name-list encryption_algorithms_client_to_server
  4002. name-list encryption_algorithms_server_to_client
  4003. name-list mac_algorithms_client_to_server
  4004. name-list mac_algorithms_server_to_client
  4005. name-list compression_algorithms_client_to_server
  4006. name-list compression_algorithms_server_to_client
  4007. name-list languages_client_to_server
  4008. name-list languages_server_to_client
  4009. boolean first_kex_packet_follows
  4010. uint32 0 (reserved for future extension)
  4011. */
  4012. var algos = self.config.algorithms;
  4013. var kexBuf = algos.kexBuf;
  4014. if (self.remoteBugs & BUGS.BAD_DHGEX) {
  4015. var copied = false;
  4016. var kexList = algos.kex;
  4017. for (var j = kexList.length - 1; j >= 0; --j) {
  4018. if (kexList[j].indexOf('group-exchange') !== -1) {
  4019. if (!copied) {
  4020. kexList = kexList.slice();
  4021. copied = true;
  4022. }
  4023. kexList.splice(j, 1);
  4024. }
  4025. }
  4026. if (copied)
  4027. kexBuf = Buffer.from(kexList.join(','));
  4028. }
  4029. var hostKeyBuf = algos.serverHostKeyBuf;
  4030. var kexInitSize = 1 + 16
  4031. + 4 + kexBuf.length
  4032. + 4 + hostKeyBuf.length
  4033. + (2 * (4 + algos.cipherBuf.length))
  4034. + (2 * (4 + algos.hmacBuf.length))
  4035. + (2 * (4 + algos.compressBuf.length))
  4036. + (2 * (4 /* languages skipped */))
  4037. + 1 + 4;
  4038. var buf = Buffer.allocUnsafe(kexInitSize);
  4039. var p = 17;
  4040. buf[0] = MESSAGE.KEXINIT;
  4041. if (myCookie !== false)
  4042. myCookie.copy(buf, 1);
  4043. writeUInt32BE(buf, kexBuf.length, p);
  4044. p += 4;
  4045. kexBuf.copy(buf, p);
  4046. p += kexBuf.length;
  4047. writeUInt32BE(buf, hostKeyBuf.length, p);
  4048. p += 4;
  4049. hostKeyBuf.copy(buf, p);
  4050. p += hostKeyBuf.length;
  4051. writeUInt32BE(buf, algos.cipherBuf.length, p);
  4052. p += 4;
  4053. algos.cipherBuf.copy(buf, p);
  4054. p += algos.cipherBuf.length;
  4055. writeUInt32BE(buf, algos.cipherBuf.length, p);
  4056. p += 4;
  4057. algos.cipherBuf.copy(buf, p);
  4058. p += algos.cipherBuf.length;
  4059. writeUInt32BE(buf, algos.hmacBuf.length, p);
  4060. p += 4;
  4061. algos.hmacBuf.copy(buf, p);
  4062. p += algos.hmacBuf.length;
  4063. writeUInt32BE(buf, algos.hmacBuf.length, p);
  4064. p += 4;
  4065. algos.hmacBuf.copy(buf, p);
  4066. p += algos.hmacBuf.length;
  4067. writeUInt32BE(buf, algos.compressBuf.length, p);
  4068. p += 4;
  4069. algos.compressBuf.copy(buf, p);
  4070. p += algos.compressBuf.length;
  4071. writeUInt32BE(buf, algos.compressBuf.length, p);
  4072. p += 4;
  4073. algos.compressBuf.copy(buf, p);
  4074. p += algos.compressBuf.length;
  4075. // Skip language lists, first_kex_packet_follows, and reserved bytes
  4076. buf.fill(0, buf.length - 13);
  4077. self.debug('DEBUG: Outgoing: Writing KEXINIT');
  4078. self._state.incoming.expectedPacket = 'KEXINIT';
  4079. var outstate = self._state.outgoing;
  4080. outstate.kexinit = buf;
  4081. if (outstate.status === OUT_READY) {
  4082. // We are the one starting the rekeying process ...
  4083. outstate.status = OUT_REKEYING;
  4084. }
  4085. send(self, buf, cb, true);
  4086. });
  4087. return true;
  4088. }
  4089. function KEXDH_INIT(self) { // Client
  4090. var state = self._state;
  4091. var outstate = state.outgoing;
  4092. var buf = Buffer.allocUnsafe(1 + 4 + outstate.pubkey.length);
  4093. state.incoming.expectedPacket = state.kex.pktReply;
  4094. if (state.kex.type === 'groupex') {
  4095. buf[0] = MESSAGE.KEXDH_GEX_INIT;
  4096. self.debug('DEBUG: Outgoing: Writing KEXDH_GEX_INIT');
  4097. } else {
  4098. buf[0] = MESSAGE.KEXDH_INIT;
  4099. if (state.kex.type === 'group')
  4100. self.debug('DEBUG: Outgoing: Writing KEXDH_INIT');
  4101. else
  4102. self.debug('DEBUG: Outgoing: Writing KEXECDH_INIT');
  4103. }
  4104. writeUInt32BE(buf, outstate.pubkey.length, 1);
  4105. outstate.pubkey.copy(buf, 5);
  4106. return send(self, buf, undefined, true);
  4107. }
  4108. function KEXDH_REPLY(self, e) { // Server
  4109. var state = self._state;
  4110. var outstate = state.outgoing;
  4111. var instate = state.incoming;
  4112. var curHostKey = self.config.hostKeys[state.hostkeyFormat];
  4113. if (Array.isArray(curHostKey))
  4114. curHostKey = curHostKey[0];
  4115. var hostkey = curHostKey.getPublicSSH();
  4116. var hostkeyAlgo = curHostKey.type;
  4117. // e === client DH public key
  4118. e = state.kex.convertPublicKey(e);
  4119. var secret = state.kex.computeSecret(e);
  4120. if (secret instanceof Error) {
  4121. secret.message = 'Error while computing DH secret ('
  4122. + state.kex.type + '): '
  4123. + secret.message;
  4124. secret.level = 'handshake';
  4125. self.emit('error', secret);
  4126. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  4127. return false;
  4128. }
  4129. var hash = crypto.createHash(state.kex.hash);
  4130. var len_ident = Buffer.byteLength(instate.identRaw);
  4131. var len_sident = Buffer.byteLength(self.config.ident);
  4132. var len_init = instate.kexinit.length;
  4133. var len_sinit = outstate.kexinit.length;
  4134. var len_hostkey = hostkey.length;
  4135. var len_pubkey = e.length;
  4136. var len_spubkey = outstate.pubkey.length;
  4137. var len_secret = secret.length;
  4138. var exchangeBufLen = len_ident
  4139. + len_sident
  4140. + len_init
  4141. + len_sinit
  4142. + len_hostkey
  4143. + len_pubkey
  4144. + len_spubkey
  4145. + len_secret
  4146. + (4 * 8); // Length fields for above values
  4147. // Group exchange-related
  4148. var len_gex_prime;
  4149. var len_gex_gen;
  4150. var gex_prime;
  4151. var gex_gen;
  4152. var dhParams = state.kex.getDHParams();
  4153. if (dhParams) {
  4154. gex_prime = dhParams.prime;
  4155. gex_gen = dhParams.generator;
  4156. len_gex_prime = gex_prime.length;
  4157. len_gex_gen = gex_gen.length;
  4158. exchangeBufLen += (4 * 3); // min, n, max values
  4159. exchangeBufLen += (4 * 2); // prime, generator length fields
  4160. exchangeBufLen += len_gex_prime;
  4161. exchangeBufLen += len_gex_gen;
  4162. }
  4163. var bp = 0;
  4164. var exchangeBuf = Buffer.allocUnsafe(exchangeBufLen);
  4165. writeUInt32BE(exchangeBuf, len_ident, bp);
  4166. bp += 4;
  4167. exchangeBuf.write(instate.identRaw, bp, 'utf8'); // V_C
  4168. bp += len_ident;
  4169. writeUInt32BE(exchangeBuf, len_sident, bp);
  4170. bp += 4;
  4171. exchangeBuf.write(self.config.ident, bp, 'utf8'); // V_S
  4172. bp += len_sident;
  4173. writeUInt32BE(exchangeBuf, len_init, bp);
  4174. bp += 4;
  4175. instate.kexinit.copy(exchangeBuf, bp); // I_C
  4176. bp += len_init;
  4177. instate.kexinit = undefined;
  4178. writeUInt32BE(exchangeBuf, len_sinit, bp);
  4179. bp += 4;
  4180. outstate.kexinit.copy(exchangeBuf, bp); // I_S
  4181. bp += len_sinit;
  4182. outstate.kexinit = undefined;
  4183. writeUInt32BE(exchangeBuf, len_hostkey, bp);
  4184. bp += 4;
  4185. hostkey.copy(exchangeBuf, bp); // K_S
  4186. bp += len_hostkey;
  4187. if (dhParams) {
  4188. KEXDH_GEX_REQ_PACKET.slice(1).copy(exchangeBuf, bp); // min, n, max
  4189. bp += (4 * 3); // Skip over bytes just copied
  4190. writeUInt32BE(exchangeBuf, len_gex_prime, bp);
  4191. bp += 4;
  4192. gex_prime.copy(exchangeBuf, bp); // p
  4193. bp += len_gex_prime;
  4194. writeUInt32BE(exchangeBuf, len_gex_gen, bp);
  4195. bp += 4;
  4196. gex_gen.copy(exchangeBuf, bp); // g
  4197. bp += len_gex_gen;
  4198. }
  4199. writeUInt32BE(exchangeBuf, len_pubkey, bp);
  4200. bp += 4;
  4201. e.copy(exchangeBuf, bp); // e
  4202. bp += len_pubkey;
  4203. writeUInt32BE(exchangeBuf, len_spubkey, bp);
  4204. bp += 4;
  4205. outstate.pubkey.copy(exchangeBuf, bp); // f
  4206. bp += len_spubkey;
  4207. writeUInt32BE(exchangeBuf, len_secret, bp);
  4208. bp += 4;
  4209. secret.copy(exchangeBuf, bp); // K
  4210. outstate.exchangeHash = hash.update(exchangeBuf).digest(); // H
  4211. if (outstate.sessionId === undefined)
  4212. outstate.sessionId = outstate.exchangeHash;
  4213. outstate.kexsecret = secret;
  4214. var signature = curHostKey.sign(outstate.exchangeHash);
  4215. if (signature instanceof Error) {
  4216. signature.message = 'Error while signing data with host key ('
  4217. + hostkeyAlgo + '): '
  4218. + signature.message;
  4219. signature.level = 'handshake';
  4220. self.emit('error', signature);
  4221. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  4222. return false;
  4223. }
  4224. signature = convertSignature(signature, hostkeyAlgo);
  4225. if (signature === false) {
  4226. signature.message = 'Error while converting handshake signature';
  4227. signature.level = 'handshake';
  4228. self.emit('error', signature);
  4229. self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED);
  4230. return false;
  4231. }
  4232. /*
  4233. byte SSH_MSG_KEXDH_REPLY
  4234. string server public host key and certificates (K_S)
  4235. mpint f
  4236. string signature of H
  4237. */
  4238. var siglen = 4 + hostkeyAlgo.length + 4 + signature.length;
  4239. var buf = Buffer.allocUnsafe(1
  4240. + 4 + len_hostkey
  4241. + 4 + len_spubkey
  4242. + 4 + siglen);
  4243. bp = 0;
  4244. buf[bp] = MESSAGE[state.kex.pktReply];
  4245. ++bp;
  4246. writeUInt32BE(buf, len_hostkey, bp);
  4247. bp += 4;
  4248. hostkey.copy(buf, bp); // K_S
  4249. bp += len_hostkey;
  4250. writeUInt32BE(buf, len_spubkey, bp);
  4251. bp += 4;
  4252. outstate.pubkey.copy(buf, bp); // f
  4253. bp += len_spubkey;
  4254. writeUInt32BE(buf, siglen, bp);
  4255. bp += 4;
  4256. writeUInt32BE(buf, hostkeyAlgo.length, bp);
  4257. bp += 4;
  4258. buf.write(hostkeyAlgo, bp, hostkeyAlgo.length, 'ascii');
  4259. bp += hostkeyAlgo.length;
  4260. writeUInt32BE(buf, signature.length, bp);
  4261. bp += 4;
  4262. signature.copy(buf, bp);
  4263. state.incoming.expectedPacket = 'NEWKEYS';
  4264. self.debug('DEBUG: Outgoing: Writing ' + state.kex.pktReply);
  4265. send(self, buf, undefined, true);
  4266. outstate.sentNEWKEYS = true;
  4267. self.debug('DEBUG: Outgoing: Writing NEWKEYS');
  4268. return send(self, NEWKEYS_PACKET, undefined, true);
  4269. }
  4270. function KEXDH_GEX_REQ(self) { // Client
  4271. self._state.incoming.expectedPacket = 'KEXDH_GEX_GROUP';
  4272. self.debug('DEBUG: Outgoing: Writing KEXDH_GEX_REQUEST');
  4273. return send(self, KEXDH_GEX_REQ_PACKET, undefined, true);
  4274. }
  4275. function compressPayload(self, payload, cb) {
  4276. var compress = self._state.outgoing.compress.instance;
  4277. compress.write(payload);
  4278. compress.flush(Z_PARTIAL_FLUSH, compressFlushCb.bind(self, cb));
  4279. }
  4280. function compressFlushCb(cb) {
  4281. if (this._readableState.ended || this._writableState.ended)
  4282. return;
  4283. send_(this, this._state.outgoing.compress.instance.read(), cb);
  4284. var queue = this._state.outgoing.compress.queue;
  4285. queue.shift();
  4286. if (queue.length > 0)
  4287. compressPayload(this, queue[0][0], queue[0][1]);
  4288. }
  4289. function send(self, payload, cb, bypass) {
  4290. var state = self._state;
  4291. if (!state)
  4292. return false;
  4293. var outstate = state.outgoing;
  4294. if (outstate.status === OUT_REKEYING && !bypass) {
  4295. if (typeof cb === 'function')
  4296. outstate.rekeyQueue.push([payload, cb]);
  4297. else
  4298. outstate.rekeyQueue.push(payload);
  4299. return false;
  4300. } else if (self._readableState.ended || self._writableState.ended) {
  4301. return false;
  4302. }
  4303. if (outstate.compress.instance) {
  4304. // This queue nonsense only exists because of a change made in node v10.12.0
  4305. // that changed flushing behavior, which now coalesces multiple writes to a
  4306. // single flush, which does not work for us.
  4307. var queue = outstate.compress.queue;
  4308. queue.push([payload, cb]);
  4309. if (queue.length === 1)
  4310. compressPayload(self, queue[0][0], queue[0][1]);
  4311. return true;
  4312. } else {
  4313. return send_(self, payload, cb);
  4314. }
  4315. }
  4316. function send_(self, payload, cb) {
  4317. // TODO: Implement length checks
  4318. var state = self._state;
  4319. var outstate = state.outgoing;
  4320. var encrypt = outstate.encrypt;
  4321. var hmac = outstate.hmac;
  4322. var pktLen;
  4323. var padLen;
  4324. var buf;
  4325. var mac;
  4326. var ret;
  4327. pktLen = payload.length + 9;
  4328. if (encrypt.instance !== false) {
  4329. if (encrypt.info.authLen > 0) {
  4330. var ptlen = 1 + payload.length + 4/* Must have at least 4 bytes padding*/;
  4331. while ((ptlen % encrypt.info.blockLen) !== 0)
  4332. ++ptlen;
  4333. padLen = ptlen - 1 - payload.length;
  4334. pktLen = 4 + ptlen;
  4335. } else {
  4336. var blockLen = encrypt.info.blockLen;
  4337. pktLen += ((blockLen - 1) * pktLen) % blockLen;
  4338. padLen = pktLen - payload.length - 5;
  4339. }
  4340. } else {
  4341. pktLen += (7 * pktLen) % 8;
  4342. padLen = pktLen - payload.length - 5;
  4343. }
  4344. buf = Buffer.allocUnsafe(pktLen);
  4345. writeUInt32BE(buf, pktLen - 4, 0);
  4346. buf[4] = padLen;
  4347. payload.copy(buf, 5);
  4348. copyRandPadBytes(buf, 5 + payload.length, padLen);
  4349. if (hmac.type !== false && hmac.key) {
  4350. mac = crypto.createHmac(SSH_TO_OPENSSL[hmac.type], hmac.key);
  4351. writeUInt32BE(outstate.bufSeqno, outstate.seqno, 0);
  4352. mac.update(outstate.bufSeqno);
  4353. mac.update(buf);
  4354. mac = mac.digest();
  4355. if (mac.length > hmac.info.actualLen)
  4356. mac = mac.slice(0, hmac.info.actualLen);
  4357. }
  4358. var nb = 0;
  4359. var encData;
  4360. if (encrypt.instance !== false) {
  4361. if (encrypt.info.authLen > 0) {
  4362. var encrypter = crypto.createCipheriv(SSH_TO_OPENSSL[encrypt.type],
  4363. encrypt.key,
  4364. encrypt.iv);
  4365. encrypter.setAutoPadding(false);
  4366. var lenbuf = buf.slice(0, 4);
  4367. encrypter.setAAD(lenbuf);
  4368. self.push(lenbuf);
  4369. nb += lenbuf;
  4370. encData = encrypter.update(buf.slice(4));
  4371. self.push(encData);
  4372. nb += encData.length;
  4373. var final = encrypter.final();
  4374. if (final.length) {
  4375. self.push(final);
  4376. nb += final.length;
  4377. }
  4378. var authTag = encrypter.getAuthTag();
  4379. ret = self.push(authTag);
  4380. nb += authTag.length;
  4381. iv_inc(encrypt.iv);
  4382. } else {
  4383. encData = encrypt.instance.update(buf);
  4384. self.push(encData);
  4385. nb += encData.length;
  4386. ret = self.push(mac);
  4387. nb += mac.length;
  4388. }
  4389. } else {
  4390. ret = self.push(buf);
  4391. nb = buf.length;
  4392. }
  4393. self.bytesSent += nb;
  4394. if (++outstate.seqno > MAX_SEQNO)
  4395. outstate.seqno = 0;
  4396. cb && cb();
  4397. return ret;
  4398. }
  4399. var copyRandPadBytes = (function() {
  4400. if (typeof crypto.randomFillSync === 'function') {
  4401. return crypto.randomFillSync;
  4402. } else {
  4403. return function copyRandPadBytes(buf, offset, count) {
  4404. var padBytes = crypto.randomBytes(count);
  4405. padBytes.copy(buf, offset);
  4406. };
  4407. }
  4408. })();
  4409. function randBytes(n, cb) {
  4410. crypto.randomBytes(n, function retry(err, buf) {
  4411. if (err)
  4412. return crypto.randomBytes(n, retry);
  4413. cb && cb(buf);
  4414. });
  4415. }
  4416. function convertSignature(signature, keyType) {
  4417. switch (keyType) {
  4418. case 'ssh-dss':
  4419. return DSASigBERToBare(signature);
  4420. case 'ecdsa-sha2-nistp256':
  4421. case 'ecdsa-sha2-nistp384':
  4422. case 'ecdsa-sha2-nistp521':
  4423. return ECDSASigASN1ToSSH(signature);
  4424. }
  4425. return signature;
  4426. }
  4427. var timingSafeEqual = (function() {
  4428. if (typeof crypto.timingSafeEqual === 'function') {
  4429. return function timingSafeEquals(a, b) {
  4430. if (a.length !== b.length) {
  4431. crypto.timingSafeEqual(a, a);
  4432. return false;
  4433. } else {
  4434. return crypto.timingSafeEqual(a, b);
  4435. }
  4436. };
  4437. } else {
  4438. return function timingSafeEquals(a, b) {
  4439. var val;
  4440. if (a.length === b.length) {
  4441. val = 0;
  4442. } else {
  4443. val = 1;
  4444. b = a;
  4445. }
  4446. for (var i = 0, len = a.length; i < len; ++i)
  4447. val |= (a[i] ^ b[i]);
  4448. return (val === 0);
  4449. }
  4450. }
  4451. })();
  4452. function KeyExchange(algo, options) {
  4453. switch (algo) {
  4454. case 'curve25519-sha256':
  4455. case 'curve25519-sha256@libssh.org':
  4456. if (!CURVE25519_SUPPORTED)
  4457. break;
  4458. this.type = '25519';
  4459. this.hash = 'sha256';
  4460. this.pktInit = 'KEXECDH_INIT';
  4461. this.pktReply = 'KEXECDH_REPLY';
  4462. return;
  4463. case 'ecdh-sha2-nistp256':
  4464. this.type = 'ecdh';
  4465. this.name = 'prime256v1';
  4466. this.hash = 'sha256';
  4467. this.pktInit = 'KEXECDH_INIT';
  4468. this.pktReply = 'KEXECDH_REPLY';
  4469. return;
  4470. case 'ecdh-sha2-nistp384':
  4471. this.type = 'ecdh';
  4472. this.name = 'secp384r1';
  4473. this.hash = 'sha384';
  4474. this.pktInit = 'KEXECDH_INIT';
  4475. this.pktReply = 'KEXECDH_REPLY';
  4476. return;
  4477. case 'ecdh-sha2-nistp521':
  4478. this.type = 'ecdh';
  4479. this.name = 'secp521r1';
  4480. this.hash = 'sha512';
  4481. this.pktInit = 'KEXECDH_INIT';
  4482. this.pktReply = 'KEXECDH_REPLY';
  4483. return;
  4484. case 'diffie-hellman-group1-sha1':
  4485. this.type = 'group';
  4486. this.name = 'modp2';
  4487. this.hash = 'sha1';
  4488. this.pktInit = 'KEXDH_INIT';
  4489. this.pktReply = 'KEXDH_REPLY';
  4490. return;
  4491. case 'diffie-hellman-group14-sha1':
  4492. this.type = 'group';
  4493. this.name = 'modp14';
  4494. this.hash = 'sha1';
  4495. this.pktInit = 'KEXDH_INIT';
  4496. this.pktReply = 'KEXDH_REPLY';
  4497. return;
  4498. case 'diffie-hellman-group14-sha256':
  4499. this.type = 'group';
  4500. this.name = 'modp14';
  4501. this.hash = 'sha256';
  4502. this.pktInit = 'KEXDH_INIT';
  4503. this.pktReply = 'KEXDH_REPLY';
  4504. return;
  4505. case 'diffie-hellman-group16-sha512':
  4506. this.type = 'group';
  4507. this.name = 'modp16';
  4508. this.hash = 'sha512';
  4509. this.pktInit = 'KEXDH_INIT';
  4510. this.pktReply = 'KEXDH_REPLY';
  4511. return;
  4512. case 'diffie-hellman-group18-sha512':
  4513. this.type = 'group';
  4514. this.name = 'modp18';
  4515. this.hash = 'sha512';
  4516. this.pktInit = 'KEXDH_INIT';
  4517. this.pktReply = 'KEXDH_REPLY';
  4518. return;
  4519. case 'diffie-hellman-group-exchange-sha1':
  4520. this.type = 'groupex';
  4521. this.hash = 'sha1';
  4522. this.pktInit = 'KEXDH_GEX_REQ';
  4523. this.pktReply = 'KEXDH_GEX_REPLY';
  4524. this._prime = null;
  4525. this._generator = null;
  4526. return;
  4527. case 'diffie-hellman-group-exchange-sha256':
  4528. this.type = 'groupex';
  4529. this.hash = 'sha256';
  4530. this.pktInit = 'KEXDH_GEX_REQ';
  4531. this.pktReply = 'KEXDH_GEX_REPLY';
  4532. this._prime = null;
  4533. this._generator = null;
  4534. return;
  4535. }
  4536. throw new Error('Unsupported key exchange algorithm: ' + algo);
  4537. }
  4538. KeyExchange.prototype.setDHParams = function(prime, generator) {
  4539. if (this.type === 'groupex') {
  4540. if (!Buffer.isBuffer(prime))
  4541. throw new Error('Invalid prime value');
  4542. if (!Buffer.isBuffer(generator))
  4543. throw new Error('Invalid generator value');
  4544. this._prime = prime;
  4545. this._generator = generator;
  4546. }
  4547. };
  4548. KeyExchange.prototype.getDHParams = function() {
  4549. if (this.type === 'groupex' && this._kex) {
  4550. return {
  4551. prime: convertToMpint(this._kex.getPrime()),
  4552. generator: convertToMpint(this._kex.getGenerator()),
  4553. };
  4554. }
  4555. };
  4556. KeyExchange.prototype.generateKeys = function() {
  4557. switch (this.type) {
  4558. case '25519':
  4559. if (!this._keys)
  4560. this._keys = crypto.generateKeyPairSync('x25519');
  4561. break;
  4562. case 'ecdh':
  4563. if (!this._kex) {
  4564. this._kex = crypto.createECDH(this.name);
  4565. this._public = this._kex.generateKeys();
  4566. }
  4567. break;
  4568. case 'group':
  4569. case 'groupex':
  4570. if (!this._kex) {
  4571. if (this.name)
  4572. this._kex = crypto.createDiffieHellmanGroup(this.name);
  4573. else if (this._prime && this._generator)
  4574. this._kex = crypto.createDiffieHellman(this._prime, this._generator);
  4575. if (this._kex)
  4576. this._public = this._kex.generateKeys();
  4577. }
  4578. break;
  4579. }
  4580. };
  4581. KeyExchange.prototype.getPublicKey = function() {
  4582. this.generateKeys();
  4583. var key;
  4584. switch (this.type) {
  4585. case '25519':
  4586. key = this._keys.publicKey.export({ type: 'spki', format: 'der' });
  4587. return key.slice(-32); // HACK: avoids parsing DER/BER header
  4588. case 'ecdh':
  4589. case 'group':
  4590. case 'groupex':
  4591. key = this._public;
  4592. break;
  4593. }
  4594. if (key)
  4595. return this.convertPublicKey(key);
  4596. };
  4597. KeyExchange.prototype.convertPublicKey = function(key) {
  4598. var newKey;
  4599. var idx = 0;
  4600. var len = key.length;
  4601. while (key[idx] === 0x00) {
  4602. ++idx;
  4603. --len;
  4604. }
  4605. switch (this.type) {
  4606. case '25519':
  4607. if (key.length === 32)
  4608. return key;
  4609. break;
  4610. default:
  4611. if (key[idx] & 0x80) {
  4612. newKey = Buffer.allocUnsafe(1 + len);
  4613. newKey[0] = 0;
  4614. key.copy(newKey, 1, idx);
  4615. return newKey;
  4616. }
  4617. }
  4618. if (len !== key.length) {
  4619. newKey = Buffer.allocUnsafe(len);
  4620. key.copy(newKey, 0, idx);
  4621. key = newKey;
  4622. }
  4623. return key;
  4624. };
  4625. KeyExchange.prototype.computeSecret = function(otherPublicKey) {
  4626. this.generateKeys();
  4627. switch (this.type) {
  4628. case '25519':
  4629. try {
  4630. var asnWriter = new Ber.Writer();
  4631. asnWriter.startSequence();
  4632. // algorithm
  4633. asnWriter.startSequence();
  4634. asnWriter.writeOID('1.3.101.110'); // id-X25519
  4635. asnWriter.endSequence();
  4636. // PublicKey
  4637. asnWriter.startSequence(Ber.BitString);
  4638. asnWriter.writeByte(0x00);
  4639. // XXX: hack to write a raw buffer without a tag -- yuck
  4640. asnWriter._ensure(otherPublicKey.length);
  4641. otherPublicKey.copy(asnWriter._buf,
  4642. asnWriter._offset,
  4643. 0,
  4644. otherPublicKey.length);
  4645. asnWriter._offset += otherPublicKey.length;
  4646. asnWriter.endSequence();
  4647. asnWriter.endSequence();
  4648. return convertToMpint(crypto.diffieHellman({
  4649. privateKey: this._keys.privateKey,
  4650. publicKey: crypto.createPublicKey({
  4651. key: asnWriter.buffer,
  4652. type: 'spki',
  4653. format: 'der',
  4654. }),
  4655. }));
  4656. } catch (ex) {
  4657. return ex;
  4658. }
  4659. break;
  4660. case 'ecdh':
  4661. case 'group':
  4662. case 'groupex':
  4663. try {
  4664. return convertToMpint(this._kex.computeSecret(otherPublicKey));
  4665. } catch (ex) {
  4666. return ex;
  4667. }
  4668. }
  4669. };
  4670. function convertToMpint(buf) {
  4671. var idx = 0;
  4672. var length = buf.length;
  4673. while (buf[idx] === 0x00) {
  4674. ++idx;
  4675. --length;
  4676. }
  4677. var newBuf;
  4678. if (buf[idx] & 0x80) {
  4679. newBuf = Buffer.allocUnsafe(1 + length);
  4680. newBuf[0] = 0;
  4681. buf.copy(newBuf, 1, idx);
  4682. buf = newBuf;
  4683. } else if (length !== buf.length) {
  4684. newBuf = Buffer.allocUnsafe(length);
  4685. buf.copy(newBuf, 0, idx);
  4686. buf = newBuf;
  4687. }
  4688. return buf;
  4689. }
  4690. module.exports = SSH2Stream;
  4691. module.exports._send = send;