Protocol.js 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076
  1. /*
  2. TODO:
  3. * Replace `buffer._pos` usage in keyParser.js and elsewhere
  4. * Utilize optional "writev" support when writing packets from
  5. cipher.encrypt()
  6. * Built-in support for automatic re-keying, on by default
  7. * Revisit receiving unexpected/unknown packets
  8. * Error (fatal or otherwise) or ignore or pass on to user (in some or all
  9. cases)?
  10. * Including server/client check for single directional packet types?
  11. * Check packets for validity or bail as early as possible?
  12. * Automatic re-key every 2**31 packets after the last key exchange (sent or
  13. received), as suggested by RFC4344. OpenSSH currently does this.
  14. * Automatic re-key every so many blocks depending on cipher. RFC4344:
  15. Because of a birthday property of block ciphers and some modes of
  16. operation, implementations must be careful not to encrypt too many
  17. blocks with the same encryption key.
  18. Let L be the block length (in bits) of an SSH encryption method's
  19. block cipher (e.g., 128 for AES). If L is at least 128, then, after
  20. rekeying, an SSH implementation SHOULD NOT encrypt more than 2**(L/4)
  21. blocks before rekeying again. If L is at least 128, then SSH
  22. implementations should also attempt to force a rekey before receiving
  23. more than 2**(L/4) blocks. If L is less than 128 (which is the case
  24. for older ciphers such as 3DES, Blowfish, CAST-128, and IDEA), then,
  25. although it may be too expensive to rekey every 2**(L/4) blocks, it
  26. is still advisable for SSH implementations to follow the original
  27. recommendation in [RFC4253]: rekey at least once for every gigabyte
  28. of transmitted data.
  29. Note that if L is less than or equal to 128, then the recommendation
  30. in this subsection supersedes the recommendation in Section 3.1. If
  31. an SSH implementation uses a block cipher with a larger block size
  32. (e.g., Rijndael with 256-bit blocks), then the recommendations in
  33. Section 3.1 may supersede the recommendations in this subsection
  34. (depending on the lengths of the packets).
  35. */
  36. 'use strict';
  37. const { inspect } = require('util');
  38. const { bindingAvailable, NullCipher, NullDecipher } = require('./crypto.js');
  39. const {
  40. COMPAT_CHECKS,
  41. DISCONNECT_REASON,
  42. MESSAGE,
  43. SIGNALS,
  44. TERMINAL_MODE,
  45. } = require('./constants.js');
  46. const {
  47. DEFAULT_KEXINIT,
  48. KexInit,
  49. kexinit,
  50. onKEXPayload,
  51. } = require('./kex.js');
  52. const {
  53. parseKey,
  54. } = require('./keyParser.js');
  55. const MESSAGE_HANDLERS = require('./handlers.js');
  56. const {
  57. bufferCopy,
  58. bufferFill,
  59. bufferSlice,
  60. convertSignature,
  61. sendPacket,
  62. writeUInt32BE,
  63. } = require('./utils.js');
  64. const {
  65. PacketReader,
  66. PacketWriter,
  67. ZlibPacketReader,
  68. ZlibPacketWriter,
  69. } = require('./zlib.js');
  70. const MODULE_VER = require('../../package.json').version;
  71. const VALID_DISCONNECT_REASONS = new Map(
  72. Object.values(DISCONNECT_REASON).map((n) => [n, 1])
  73. );
  74. const IDENT_RAW = Buffer.from(`SSH-2.0-ssh2js${MODULE_VER}`);
  75. const IDENT = Buffer.from(`${IDENT_RAW}\r\n`);
  76. const MAX_LINE_LEN = 8192;
  77. const MAX_LINES = 1024;
  78. const PING_PAYLOAD = Buffer.from([
  79. MESSAGE.GLOBAL_REQUEST,
  80. // "keepalive@openssh.com"
  81. 0, 0, 0, 21,
  82. 107, 101, 101, 112, 97, 108, 105, 118, 101, 64, 111, 112, 101, 110, 115,
  83. 115, 104, 46, 99, 111, 109,
  84. // Request a reply
  85. 1,
  86. ]);
  87. const NO_TERMINAL_MODES_BUFFER = Buffer.from([ TERMINAL_MODE.TTY_OP_END ]);
  88. function noop() {}
  89. /*
  90. Inbound:
  91. * kexinit payload (needed only until exchange hash is generated)
  92. * raw ident
  93. * rekey packet queue
  94. * expected packet (implemented as separate _parse() function?)
  95. Outbound:
  96. * kexinit payload (needed only until exchange hash is generated)
  97. * rekey packet queue
  98. * kex secret (needed only until NEWKEYS)
  99. * exchange hash (needed only until NEWKEYS)
  100. * session ID (set to exchange hash from initial handshake)
  101. */
  102. class Protocol {
  103. constructor(config) {
  104. const onWrite = config.onWrite;
  105. if (typeof onWrite !== 'function')
  106. throw new Error('Missing onWrite function');
  107. this._onWrite = (data) => { onWrite(data); };
  108. const onError = config.onError;
  109. if (typeof onError !== 'function')
  110. throw new Error('Missing onError function');
  111. this._onError = (err) => { onError(err); };
  112. const debug = config.debug;
  113. this._debug = (typeof debug === 'function'
  114. ? (msg) => { debug(msg); }
  115. : undefined);
  116. const onHeader = config.onHeader;
  117. this._onHeader = (typeof onHeader === 'function'
  118. ? (...args) => { onHeader(...args); }
  119. : noop);
  120. const onPacket = config.onPacket;
  121. this._onPacket = (typeof onPacket === 'function'
  122. ? () => { onPacket(); }
  123. : noop);
  124. let onHandshakeComplete = config.onHandshakeComplete;
  125. if (typeof onHandshakeComplete !== 'function')
  126. onHandshakeComplete = noop;
  127. this._onHandshakeComplete = (...args) => {
  128. this._debug && this._debug('Handshake completed');
  129. // Process packets queued during a rekey where necessary
  130. const oldQueue = this._queue;
  131. if (oldQueue) {
  132. this._queue = undefined;
  133. this._debug && this._debug(
  134. `Draining outbound queue (${oldQueue.length}) ...`
  135. );
  136. for (let i = 0; i < oldQueue.length; ++i) {
  137. const data = oldQueue[i];
  138. // data === payload only
  139. // XXX: hacky
  140. let finalized = this._packetRW.write.finalize(data);
  141. if (finalized === data) {
  142. const packet = this._cipher.allocPacket(data.length);
  143. packet.set(data, 5);
  144. finalized = packet;
  145. }
  146. sendPacket(this, finalized);
  147. }
  148. this._debug && this._debug('... finished draining outbound queue');
  149. }
  150. onHandshakeComplete(...args);
  151. };
  152. this._queue = undefined;
  153. const messageHandlers = config.messageHandlers;
  154. if (typeof messageHandlers === 'object' && messageHandlers !== null)
  155. this._handlers = messageHandlers;
  156. else
  157. this._handlers = {};
  158. this._onPayload = onPayload.bind(this);
  159. this._server = !!config.server;
  160. this._banner = undefined;
  161. let greeting;
  162. if (this._server) {
  163. if (typeof config.hostKeys !== 'object' || config.hostKeys === null)
  164. throw new Error('Missing server host key(s)');
  165. this._hostKeys = config.hostKeys;
  166. // Greeting displayed before the ssh identification string is sent, this
  167. // is usually ignored by most clients
  168. if (typeof config.greeting === 'string' && config.greeting.length) {
  169. greeting = (config.greeting.slice(-2) === '\r\n'
  170. ? config.greeting
  171. : `${config.greeting}\r\n`);
  172. }
  173. // Banner shown after the handshake completes, but before user
  174. // authentication begins
  175. if (typeof config.banner === 'string' && config.banner.length) {
  176. this._banner = (config.banner.slice(-2) === '\r\n'
  177. ? config.banner
  178. : `${config.banner}\r\n`);
  179. }
  180. } else {
  181. this._hostKeys = undefined;
  182. }
  183. let offer = config.offer;
  184. if (typeof offer !== 'object' || offer === null)
  185. offer = DEFAULT_KEXINIT;
  186. else if (offer.constructor !== KexInit)
  187. offer = new KexInit(offer);
  188. this._kex = undefined;
  189. this._kexinit = undefined;
  190. this._offer = offer;
  191. this._cipher = new NullCipher(0, this._onWrite);
  192. this._decipher = undefined;
  193. this._skipNextInboundPacket = false;
  194. this._packetRW = {
  195. read: new PacketReader(),
  196. write: new PacketWriter(this),
  197. };
  198. this._hostVerifier = (!this._server
  199. && typeof config.hostVerifier === 'function'
  200. ? config.hostVerifier
  201. : undefined);
  202. this._parse = parseHeader;
  203. this._buffer = undefined;
  204. this._authsQueue = [];
  205. this._authenticated = false;
  206. this._remoteIdentRaw = undefined;
  207. let sentIdent;
  208. if (typeof config.ident === 'string') {
  209. this._identRaw = Buffer.from(`SSH-2.0-${config.ident}`);
  210. sentIdent = Buffer.allocUnsafe(this._identRaw.length + 2);
  211. sentIdent.set(this._identRaw, 0);
  212. sentIdent[sentIdent.length - 2] = 13; // '\r'
  213. sentIdent[sentIdent.length - 1] = 10; // '\n'
  214. } else if (Buffer.isBuffer(config.ident)) {
  215. const fullIdent = Buffer.allocUnsafe(8 + config.ident.length);
  216. fullIdent.latin1Write('SSH-2.0-', 0, 8);
  217. fullIdent.set(config.ident, 8);
  218. this._identRaw = fullIdent;
  219. sentIdent = Buffer.allocUnsafe(fullIdent.length + 2);
  220. sentIdent.set(fullIdent, 0);
  221. sentIdent[sentIdent.length - 2] = 13; // '\r'
  222. sentIdent[sentIdent.length - 1] = 10; // '\n'
  223. } else {
  224. this._identRaw = IDENT_RAW;
  225. sentIdent = IDENT;
  226. }
  227. this._compatFlags = 0;
  228. if (this._debug) {
  229. if (bindingAvailable)
  230. this._debug('Custom crypto binding available');
  231. else
  232. this._debug('Custom crypto binding not available');
  233. }
  234. process.nextTick(() => {
  235. this._debug && this._debug(
  236. `Local ident: ${inspect(this._identRaw.toString())}`
  237. );
  238. if (greeting)
  239. this._onWrite(greeting);
  240. this._onWrite(sentIdent);
  241. });
  242. }
  243. _destruct(reason) {
  244. this._packetRW.read.cleanup();
  245. this._packetRW.write.cleanup();
  246. this._cipher && this._cipher.free();
  247. this._decipher && this._decipher.free();
  248. if (typeof reason !== 'string' || reason.length === 0)
  249. reason = 'fatal error';
  250. this.parse = () => {
  251. throw new Error(`Instance unusable after ${reason}`);
  252. };
  253. this._onWrite = () => {
  254. throw new Error(`Instance unusable after ${reason}`);
  255. };
  256. this._destruct = undefined;
  257. }
  258. cleanup() {
  259. this._destruct && this._destruct();
  260. }
  261. parse(chunk, i, len) {
  262. while (i < len)
  263. i = this._parse(chunk, i, len);
  264. }
  265. // Protocol message API
  266. // ===========================================================================
  267. // Common/Shared =============================================================
  268. // ===========================================================================
  269. // Global
  270. // ------
  271. disconnect(reason) {
  272. const pktLen = 1 + 4 + 4 + 4;
  273. // We don't use _packetRW.write.* here because we need to make sure that
  274. // we always get a full packet allocated because this message can be sent
  275. // at any time -- even during a key exchange
  276. let p = this._packetRW.write.allocStartKEX;
  277. const packet = this._packetRW.write.alloc(pktLen, true);
  278. const end = p + pktLen;
  279. if (!VALID_DISCONNECT_REASONS.has(reason))
  280. reason = DISCONNECT_REASON.PROTOCOL_ERROR;
  281. packet[p] = MESSAGE.DISCONNECT;
  282. writeUInt32BE(packet, reason, ++p);
  283. packet.fill(0, p += 4, end);
  284. this._debug && this._debug(`Outbound: Sending DISCONNECT (${reason})`);
  285. sendPacket(this, this._packetRW.write.finalize(packet, true), true);
  286. }
  287. ping() {
  288. const p = this._packetRW.write.allocStart;
  289. const packet = this._packetRW.write.alloc(PING_PAYLOAD.length);
  290. packet.set(PING_PAYLOAD, p);
  291. this._debug && this._debug(
  292. 'Outbound: Sending ping (GLOBAL_REQUEST: keepalive@openssh.com)'
  293. );
  294. sendPacket(this, this._packetRW.write.finalize(packet));
  295. }
  296. rekey() {
  297. if (this._kexinit === undefined) {
  298. this._debug && this._debug('Outbound: Initiated explicit rekey');
  299. this._queue = [];
  300. kexinit(this);
  301. } else {
  302. this._debug && this._debug('Outbound: Ignoring rekey during handshake');
  303. }
  304. }
  305. // 'ssh-connection' service-specific
  306. // ---------------------------------
  307. requestSuccess(data) {
  308. let p = this._packetRW.write.allocStart;
  309. let packet;
  310. if (Buffer.isBuffer(data)) {
  311. packet = this._packetRW.write.alloc(1 + data.length);
  312. packet[p] = MESSAGE.REQUEST_SUCCESS;
  313. packet.set(data, ++p);
  314. } else {
  315. packet = this._packetRW.write.alloc(1);
  316. packet[p] = MESSAGE.REQUEST_SUCCESS;
  317. }
  318. this._debug && this._debug('Outbound: Sending REQUEST_SUCCESS');
  319. sendPacket(this, this._packetRW.write.finalize(packet));
  320. }
  321. requestFailure() {
  322. const p = this._packetRW.write.allocStart;
  323. const packet = this._packetRW.write.alloc(1);
  324. packet[p] = MESSAGE.REQUEST_FAILURE;
  325. this._debug && this._debug('Outbound: Sending REQUEST_FAILURE');
  326. sendPacket(this, this._packetRW.write.finalize(packet));
  327. }
  328. channelSuccess(chan) {
  329. // Does not consume window space
  330. let p = this._packetRW.write.allocStart;
  331. const packet = this._packetRW.write.alloc(1 + 4);
  332. packet[p] = MESSAGE.CHANNEL_SUCCESS;
  333. writeUInt32BE(packet, chan, ++p);
  334. this._debug && this._debug(`Outbound: Sending CHANNEL_SUCCESS (r:${chan})`);
  335. sendPacket(this, this._packetRW.write.finalize(packet));
  336. }
  337. channelFailure(chan) {
  338. // Does not consume window space
  339. let p = this._packetRW.write.allocStart;
  340. const packet = this._packetRW.write.alloc(1 + 4);
  341. packet[p] = MESSAGE.CHANNEL_FAILURE;
  342. writeUInt32BE(packet, chan, ++p);
  343. this._debug && this._debug(`Outbound: Sending CHANNEL_FAILURE (r:${chan})`);
  344. sendPacket(this, this._packetRW.write.finalize(packet));
  345. }
  346. channelEOF(chan) {
  347. // Does not consume window space
  348. let p = this._packetRW.write.allocStart;
  349. const packet = this._packetRW.write.alloc(1 + 4);
  350. packet[p] = MESSAGE.CHANNEL_EOF;
  351. writeUInt32BE(packet, chan, ++p);
  352. this._debug && this._debug(`Outbound: Sending CHANNEL_EOF (r:${chan})`);
  353. sendPacket(this, this._packetRW.write.finalize(packet));
  354. }
  355. channelClose(chan) {
  356. // Does not consume window space
  357. let p = this._packetRW.write.allocStart;
  358. const packet = this._packetRW.write.alloc(1 + 4);
  359. packet[p] = MESSAGE.CHANNEL_CLOSE;
  360. writeUInt32BE(packet, chan, ++p);
  361. this._debug && this._debug(`Outbound: Sending CHANNEL_CLOSE (r:${chan})`);
  362. sendPacket(this, this._packetRW.write.finalize(packet));
  363. }
  364. channelWindowAdjust(chan, amount) {
  365. // Does not consume window space
  366. let p = this._packetRW.write.allocStart;
  367. const packet = this._packetRW.write.alloc(1 + 4 + 4);
  368. packet[p] = MESSAGE.CHANNEL_WINDOW_ADJUST;
  369. writeUInt32BE(packet, chan, ++p);
  370. writeUInt32BE(packet, amount, p += 4);
  371. this._debug && this._debug(
  372. `Outbound: Sending CHANNEL_WINDOW_ADJUST (r:${chan}, ${amount})`
  373. );
  374. sendPacket(this, this._packetRW.write.finalize(packet));
  375. }
  376. channelData(chan, data) {
  377. const isBuffer = Buffer.isBuffer(data);
  378. const dataLen = (isBuffer ? data.length : Buffer.byteLength(data));
  379. let p = this._packetRW.write.allocStart;
  380. const packet = this._packetRW.write.alloc(1 + 4 + 4 + dataLen);
  381. packet[p] = MESSAGE.CHANNEL_DATA;
  382. writeUInt32BE(packet, chan, ++p);
  383. writeUInt32BE(packet, dataLen, p += 4);
  384. if (isBuffer)
  385. packet.set(data, p += 4);
  386. else
  387. packet.utf8Write(data, p += 4, dataLen);
  388. this._debug && this._debug(
  389. `Outbound: Sending CHANNEL_DATA (r:${chan}, ${dataLen})`
  390. );
  391. sendPacket(this, this._packetRW.write.finalize(packet));
  392. }
  393. channelExtData(chan, data, type) {
  394. const isBuffer = Buffer.isBuffer(data);
  395. const dataLen = (isBuffer ? data.length : Buffer.byteLength(data));
  396. let p = this._packetRW.write.allocStart;
  397. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + dataLen);
  398. packet[p] = MESSAGE.CHANNEL_EXTENDED_DATA;
  399. writeUInt32BE(packet, chan, ++p);
  400. writeUInt32BE(packet, type, p += 4);
  401. writeUInt32BE(packet, dataLen, p += 4);
  402. if (isBuffer)
  403. packet.set(data, p += 4);
  404. else
  405. packet.utf8Write(data, p += 4, dataLen);
  406. this._debug
  407. && this._debug(`Outbound: Sending CHANNEL_EXTENDED_DATA (r:${chan})`);
  408. sendPacket(this, this._packetRW.write.finalize(packet));
  409. }
  410. channelOpenConfirm(remote, local, initWindow, maxPacket) {
  411. let p = this._packetRW.write.allocStart;
  412. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + 4);
  413. packet[p] = MESSAGE.CHANNEL_OPEN_CONFIRMATION;
  414. writeUInt32BE(packet, remote, ++p);
  415. writeUInt32BE(packet, local, p += 4);
  416. writeUInt32BE(packet, initWindow, p += 4);
  417. writeUInt32BE(packet, maxPacket, p += 4);
  418. this._debug && this._debug(
  419. `Outbound: Sending CHANNEL_OPEN_CONFIRMATION (r:${remote}, l:${local})`
  420. );
  421. sendPacket(this, this._packetRW.write.finalize(packet));
  422. }
  423. channelOpenFail(remote, reason, desc) {
  424. if (typeof desc !== 'string')
  425. desc = '';
  426. const descLen = Buffer.byteLength(desc);
  427. let p = this._packetRW.write.allocStart;
  428. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + descLen + 4);
  429. packet[p] = MESSAGE.CHANNEL_OPEN_FAILURE;
  430. writeUInt32BE(packet, remote, ++p);
  431. writeUInt32BE(packet, reason, p += 4);
  432. writeUInt32BE(packet, descLen, p += 4);
  433. p += 4;
  434. if (descLen) {
  435. packet.utf8Write(desc, p, descLen);
  436. p += descLen;
  437. }
  438. writeUInt32BE(packet, 0, p); // Empty language tag
  439. this._debug
  440. && this._debug(`Outbound: Sending CHANNEL_OPEN_FAILURE (r:${remote})`);
  441. sendPacket(this, this._packetRW.write.finalize(packet));
  442. }
  443. // ===========================================================================
  444. // Client-specific ===========================================================
  445. // ===========================================================================
  446. // Global
  447. // ------
  448. service(name) {
  449. if (this._server)
  450. throw new Error('Client-only method called in server mode');
  451. const nameLen = Buffer.byteLength(name);
  452. let p = this._packetRW.write.allocStart;
  453. const packet = this._packetRW.write.alloc(1 + 4 + nameLen);
  454. packet[p] = MESSAGE.SERVICE_REQUEST;
  455. writeUInt32BE(packet, nameLen, ++p);
  456. packet.utf8Write(name, p += 4, nameLen);
  457. this._debug && this._debug(`Outbound: Sending SERVICE_REQUEST (${name})`);
  458. sendPacket(this, this._packetRW.write.finalize(packet));
  459. }
  460. // 'ssh-userauth' service-specific
  461. // -------------------------------
  462. authPassword(username, password, newPassword) {
  463. if (this._server)
  464. throw new Error('Client-only method called in server mode');
  465. const userLen = Buffer.byteLength(username);
  466. const passLen = Buffer.byteLength(password);
  467. const newPassLen = (newPassword ? Buffer.byteLength(newPassword) : 0);
  468. let p = this._packetRW.write.allocStart;
  469. const packet = this._packetRW.write.alloc(
  470. 1 + 4 + userLen + 4 + 14 + 4 + 8 + 1 + 4 + passLen
  471. + (newPassword ? 4 + newPassLen : 0)
  472. );
  473. packet[p] = MESSAGE.USERAUTH_REQUEST;
  474. writeUInt32BE(packet, userLen, ++p);
  475. packet.utf8Write(username, p += 4, userLen);
  476. writeUInt32BE(packet, 14, p += userLen);
  477. packet.utf8Write('ssh-connection', p += 4, 14);
  478. writeUInt32BE(packet, 8, p += 14);
  479. packet.utf8Write('password', p += 4, 8);
  480. packet[p += 8] = (newPassword ? 1 : 0);
  481. writeUInt32BE(packet, passLen, ++p);
  482. if (Buffer.isBuffer(password))
  483. bufferCopy(password, packet, 0, passLen, p += 4);
  484. else
  485. packet.utf8Write(password, p += 4, passLen);
  486. if (newPassword) {
  487. writeUInt32BE(packet, newPassLen, p += passLen);
  488. if (Buffer.isBuffer(newPassword))
  489. bufferCopy(newPassword, packet, 0, newPassLen, p += 4);
  490. else
  491. packet.utf8Write(newPassword, p += 4, newPassLen);
  492. this._debug && this._debug(
  493. 'Outbound: Sending USERAUTH_REQUEST (changed password)'
  494. );
  495. } else {
  496. this._debug && this._debug(
  497. 'Outbound: Sending USERAUTH_REQUEST (password)'
  498. );
  499. }
  500. this._authsQueue.push('password');
  501. sendPacket(this, this._packetRW.write.finalize(packet));
  502. }
  503. authPK(username, pubKey, cbSign) {
  504. if (this._server)
  505. throw new Error('Client-only method called in server mode');
  506. pubKey = parseKey(pubKey);
  507. if (pubKey instanceof Error)
  508. throw new Error('Invalid key');
  509. const keyType = pubKey.type;
  510. pubKey = pubKey.getPublicSSH();
  511. const userLen = Buffer.byteLength(username);
  512. const algoLen = Buffer.byteLength(keyType);
  513. const pubKeyLen = pubKey.length;
  514. const sessionID = this._kex.sessionID;
  515. const sesLen = sessionID.length;
  516. const payloadLen =
  517. (cbSign ? 4 + sesLen : 0)
  518. + 1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen;
  519. let packet;
  520. let p;
  521. if (cbSign) {
  522. packet = Buffer.allocUnsafe(payloadLen);
  523. p = 0;
  524. writeUInt32BE(packet, sesLen, p);
  525. packet.set(sessionID, p += 4);
  526. p += sesLen;
  527. } else {
  528. packet = this._packetRW.write.alloc(payloadLen);
  529. p = this._packetRW.write.allocStart;
  530. }
  531. packet[p] = MESSAGE.USERAUTH_REQUEST;
  532. writeUInt32BE(packet, userLen, ++p);
  533. packet.utf8Write(username, p += 4, userLen);
  534. writeUInt32BE(packet, 14, p += userLen);
  535. packet.utf8Write('ssh-connection', p += 4, 14);
  536. writeUInt32BE(packet, 9, p += 14);
  537. packet.utf8Write('publickey', p += 4, 9);
  538. packet[p += 9] = (cbSign ? 1 : 0);
  539. writeUInt32BE(packet, algoLen, ++p);
  540. packet.utf8Write(keyType, p += 4, algoLen);
  541. writeUInt32BE(packet, pubKeyLen, p += algoLen);
  542. packet.set(pubKey, p += 4);
  543. if (!cbSign) {
  544. this._authsQueue.push('publickey');
  545. this._debug && this._debug(
  546. 'Outbound: Sending USERAUTH_REQUEST (publickey -- check)'
  547. );
  548. sendPacket(this, this._packetRW.write.finalize(packet));
  549. return;
  550. }
  551. cbSign(packet, (signature) => {
  552. signature = convertSignature(signature, keyType);
  553. if (signature === false)
  554. throw new Error('Error while converting handshake signature');
  555. const sigLen = signature.length;
  556. p = this._packetRW.write.allocStart;
  557. packet = this._packetRW.write.alloc(
  558. 1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen + 4
  559. + 4 + algoLen + 4 + sigLen
  560. );
  561. // TODO: simply copy from original "packet" to new `packet` to avoid
  562. // having to write each individual field a second time?
  563. packet[p] = MESSAGE.USERAUTH_REQUEST;
  564. writeUInt32BE(packet, userLen, ++p);
  565. packet.utf8Write(username, p += 4, userLen);
  566. writeUInt32BE(packet, 14, p += userLen);
  567. packet.utf8Write('ssh-connection', p += 4, 14);
  568. writeUInt32BE(packet, 9, p += 14);
  569. packet.utf8Write('publickey', p += 4, 9);
  570. packet[p += 9] = 1;
  571. writeUInt32BE(packet, algoLen, ++p);
  572. packet.utf8Write(keyType, p += 4, algoLen);
  573. writeUInt32BE(packet, pubKeyLen, p += algoLen);
  574. packet.set(pubKey, p += 4);
  575. writeUInt32BE(packet, 4 + algoLen + 4 + sigLen, p += pubKeyLen);
  576. writeUInt32BE(packet, algoLen, p += 4);
  577. packet.utf8Write(keyType, p += 4, algoLen);
  578. writeUInt32BE(packet, sigLen, p += algoLen);
  579. packet.set(signature, p += 4);
  580. // Servers shouldn't send packet type 60 in response to signed publickey
  581. // attempts, but if they do, interpret as type 60.
  582. this._authsQueue.push('publickey');
  583. this._debug && this._debug(
  584. 'Outbound: Sending USERAUTH_REQUEST (publickey)'
  585. );
  586. sendPacket(this, this._packetRW.write.finalize(packet));
  587. });
  588. }
  589. authHostbased(username, pubKey, hostname, userlocal, cbSign) {
  590. // TODO: Make DRY by sharing similar code with authPK()
  591. if (this._server)
  592. throw new Error('Client-only method called in server mode');
  593. pubKey = parseKey(pubKey);
  594. if (pubKey instanceof Error)
  595. throw new Error('Invalid key');
  596. const keyType = pubKey.type;
  597. pubKey = pubKey.getPublicSSH();
  598. const userLen = Buffer.byteLength(username);
  599. const algoLen = Buffer.byteLength(keyType);
  600. const pubKeyLen = pubKey.length;
  601. const sessionID = this._kex.sessionID;
  602. const sesLen = sessionID.length;
  603. const hostnameLen = Buffer.byteLength(hostname);
  604. const userlocalLen = Buffer.byteLength(userlocal);
  605. const data = Buffer.allocUnsafe(
  606. 4 + sesLen + 1 + 4 + userLen + 4 + 14 + 4 + 9 + 4 + algoLen
  607. + 4 + pubKeyLen + 4 + hostnameLen + 4 + userlocalLen
  608. );
  609. let p = 0;
  610. writeUInt32BE(data, sesLen, p);
  611. data.set(sessionID, p += 4);
  612. data[p += sesLen] = MESSAGE.USERAUTH_REQUEST;
  613. writeUInt32BE(data, userLen, ++p);
  614. data.utf8Write(username, p += 4, userLen);
  615. writeUInt32BE(data, 14, p += userLen);
  616. data.utf8Write('ssh-connection', p += 4, 14);
  617. writeUInt32BE(data, 9, p += 14);
  618. data.utf8Write('hostbased', p += 4, 9);
  619. writeUInt32BE(data, algoLen, p += 9);
  620. data.utf8Write(keyType, p += 4, algoLen);
  621. writeUInt32BE(data, pubKeyLen, p += algoLen);
  622. data.set(pubKey, p += 4);
  623. writeUInt32BE(data, hostnameLen, p += pubKeyLen);
  624. data.utf8Write(hostname, p += 4, hostnameLen);
  625. writeUInt32BE(data, userlocalLen, p += hostnameLen);
  626. data.utf8Write(userlocal, p += 4, userlocalLen);
  627. cbSign(data, (signature) => {
  628. signature = convertSignature(signature, keyType);
  629. if (!signature)
  630. throw new Error('Error while converting handshake signature');
  631. const sigLen = signature.length;
  632. const reqDataLen = (data.length - sesLen - 4);
  633. p = this._packetRW.write.allocStart;
  634. const packet = this._packetRW.write.alloc(
  635. reqDataLen + 4 + 4 + algoLen + 4 + sigLen
  636. );
  637. bufferCopy(data, packet, 4 + sesLen, data.length, p);
  638. writeUInt32BE(packet, 4 + algoLen + 4 + sigLen, p += reqDataLen);
  639. writeUInt32BE(packet, algoLen, p += 4);
  640. packet.utf8Write(keyType, p += 4, algoLen);
  641. writeUInt32BE(packet, sigLen, p += algoLen);
  642. packet.set(signature, p += 4);
  643. this._authsQueue.push('hostbased');
  644. this._debug && this._debug(
  645. 'Outbound: Sending USERAUTH_REQUEST (hostbased)'
  646. );
  647. sendPacket(this, this._packetRW.write.finalize(packet));
  648. });
  649. }
  650. authKeyboard(username) {
  651. if (this._server)
  652. throw new Error('Client-only method called in server mode');
  653. const userLen = Buffer.byteLength(username);
  654. let p = this._packetRW.write.allocStart;
  655. const packet = this._packetRW.write.alloc(
  656. 1 + 4 + userLen + 4 + 14 + 4 + 20 + 4 + 4
  657. );
  658. packet[p] = MESSAGE.USERAUTH_REQUEST;
  659. writeUInt32BE(packet, userLen, ++p);
  660. packet.utf8Write(username, p += 4, userLen);
  661. writeUInt32BE(packet, 14, p += userLen);
  662. packet.utf8Write('ssh-connection', p += 4, 14);
  663. writeUInt32BE(packet, 20, p += 14);
  664. packet.utf8Write('keyboard-interactive', p += 4, 20);
  665. writeUInt32BE(packet, 0, p += 20);
  666. writeUInt32BE(packet, 0, p += 4);
  667. this._authsQueue.push('keyboard-interactive');
  668. this._debug && this._debug(
  669. 'Outbound: Sending USERAUTH_REQUEST (keyboard-interactive)'
  670. );
  671. sendPacket(this, this._packetRW.write.finalize(packet));
  672. }
  673. authNone(username) {
  674. if (this._server)
  675. throw new Error('Client-only method called in server mode');
  676. const userLen = Buffer.byteLength(username);
  677. let p = this._packetRW.write.allocStart;
  678. const packet = this._packetRW.write.alloc(1 + 4 + userLen + 4 + 14 + 4 + 4);
  679. packet[p] = MESSAGE.USERAUTH_REQUEST;
  680. writeUInt32BE(packet, userLen, ++p);
  681. packet.utf8Write(username, p += 4, userLen);
  682. writeUInt32BE(packet, 14, p += userLen);
  683. packet.utf8Write('ssh-connection', p += 4, 14);
  684. writeUInt32BE(packet, 4, p += 14);
  685. packet.utf8Write('none', p += 4, 4);
  686. this._authsQueue.push('none');
  687. this._debug && this._debug('Outbound: Sending USERAUTH_REQUEST (none)');
  688. sendPacket(this, this._packetRW.write.finalize(packet));
  689. }
  690. authInfoRes(responses) {
  691. if (this._server)
  692. throw new Error('Client-only method called in server mode');
  693. let responsesTotalLen = 0;
  694. let responseLens;
  695. if (responses) {
  696. responseLens = new Array(responses.length);
  697. for (let i = 0; i < responses.length; ++i) {
  698. const len = Buffer.byteLength(responses[i]);
  699. responseLens[i] = len;
  700. responsesTotalLen += 4 + len;
  701. }
  702. }
  703. let p = this._packetRW.write.allocStart;
  704. const packet = this._packetRW.write.alloc(1 + 4 + responsesTotalLen);
  705. packet[p] = MESSAGE.USERAUTH_INFO_RESPONSE;
  706. if (responses) {
  707. writeUInt32BE(packet, responses.length, ++p);
  708. p += 4;
  709. for (let i = 0; i < responses.length; ++i) {
  710. const len = responseLens[i];
  711. writeUInt32BE(packet, len, p);
  712. p += 4;
  713. if (len) {
  714. packet.utf8Write(responses[i], p, len);
  715. p += len;
  716. }
  717. }
  718. } else {
  719. writeUInt32BE(packet, 0, ++p);
  720. }
  721. this._debug && this._debug('Outbound: Sending USERAUTH_INFO_RESPONSE');
  722. sendPacket(this, this._packetRW.write.finalize(packet));
  723. }
  724. // 'ssh-connection' service-specific
  725. // ---------------------------------
  726. tcpipForward(bindAddr, bindPort, wantReply) {
  727. if (this._server)
  728. throw new Error('Client-only method called in server mode');
  729. const addrLen = Buffer.byteLength(bindAddr);
  730. let p = this._packetRW.write.allocStart;
  731. const packet = this._packetRW.write.alloc(1 + 4 + 13 + 1 + 4 + addrLen + 4);
  732. packet[p] = MESSAGE.GLOBAL_REQUEST;
  733. writeUInt32BE(packet, 13, ++p);
  734. packet.utf8Write('tcpip-forward', p += 4, 13);
  735. packet[p += 13] = (wantReply === undefined || wantReply === true ? 1 : 0);
  736. writeUInt32BE(packet, addrLen, ++p);
  737. packet.utf8Write(bindAddr, p += 4, addrLen);
  738. writeUInt32BE(packet, bindPort, p += addrLen);
  739. this._debug
  740. && this._debug('Outbound: Sending GLOBAL_REQUEST (tcpip-forward)');
  741. sendPacket(this, this._packetRW.write.finalize(packet));
  742. }
  743. cancelTcpipForward(bindAddr, bindPort, wantReply) {
  744. if (this._server)
  745. throw new Error('Client-only method called in server mode');
  746. const addrLen = Buffer.byteLength(bindAddr);
  747. let p = this._packetRW.write.allocStart;
  748. const packet = this._packetRW.write.alloc(1 + 4 + 20 + 1 + 4 + addrLen + 4);
  749. packet[p] = MESSAGE.GLOBAL_REQUEST;
  750. writeUInt32BE(packet, 20, ++p);
  751. packet.utf8Write('cancel-tcpip-forward', p += 4, 20);
  752. packet[p += 20] = (wantReply === undefined || wantReply === true ? 1 : 0);
  753. writeUInt32BE(packet, addrLen, ++p);
  754. packet.utf8Write(bindAddr, p += 4, addrLen);
  755. writeUInt32BE(packet, bindPort, p += addrLen);
  756. this._debug
  757. && this._debug('Outbound: Sending GLOBAL_REQUEST (cancel-tcpip-forward)');
  758. sendPacket(this, this._packetRW.write.finalize(packet));
  759. }
  760. openssh_streamLocalForward(socketPath, wantReply) {
  761. if (this._server)
  762. throw new Error('Client-only method called in server mode');
  763. const socketPathLen = Buffer.byteLength(socketPath);
  764. let p = this._packetRW.write.allocStart;
  765. const packet = this._packetRW.write.alloc(
  766. 1 + 4 + 31 + 1 + 4 + socketPathLen
  767. );
  768. packet[p] = MESSAGE.GLOBAL_REQUEST;
  769. writeUInt32BE(packet, 31, ++p);
  770. packet.utf8Write('streamlocal-forward@openssh.com', p += 4, 31);
  771. packet[p += 31] = (wantReply === undefined || wantReply === true ? 1 : 0);
  772. writeUInt32BE(packet, socketPathLen, ++p);
  773. packet.utf8Write(socketPath, p += 4, socketPathLen);
  774. this._debug && this._debug(
  775. 'Outbound: Sending GLOBAL_REQUEST (streamlocal-forward@openssh.com)'
  776. );
  777. sendPacket(this, this._packetRW.write.finalize(packet));
  778. }
  779. openssh_cancelStreamLocalForward(socketPath, wantReply) {
  780. if (this._server)
  781. throw new Error('Client-only method called in server mode');
  782. const socketPathLen = Buffer.byteLength(socketPath);
  783. let p = this._packetRW.write.allocStart;
  784. const packet = this._packetRW.write.alloc(
  785. 1 + 4 + 38 + 1 + 4 + socketPathLen
  786. );
  787. packet[p] = MESSAGE.GLOBAL_REQUEST;
  788. writeUInt32BE(packet, 38, ++p);
  789. packet.utf8Write('cancel-streamlocal-forward@openssh.com', p += 4, 38);
  790. packet[p += 38] = (wantReply === undefined || wantReply === true ? 1 : 0);
  791. writeUInt32BE(packet, socketPathLen, ++p);
  792. packet.utf8Write(socketPath, p += 4, socketPathLen);
  793. if (this._debug) {
  794. this._debug(
  795. 'Outbound: Sending GLOBAL_REQUEST '
  796. + '(cancel-streamlocal-forward@openssh.com)'
  797. );
  798. }
  799. sendPacket(this, this._packetRW.write.finalize(packet));
  800. }
  801. directTcpip(chan, initWindow, maxPacket, cfg) {
  802. if (this._server)
  803. throw new Error('Client-only method called in server mode');
  804. const srcLen = Buffer.byteLength(cfg.srcIP);
  805. const dstLen = Buffer.byteLength(cfg.dstIP);
  806. let p = this._packetRW.write.allocStart;
  807. const packet = this._packetRW.write.alloc(
  808. 1 + 4 + 12 + 4 + 4 + 4 + 4 + srcLen + 4 + 4 + dstLen + 4
  809. );
  810. packet[p] = MESSAGE.CHANNEL_OPEN;
  811. writeUInt32BE(packet, 12, ++p);
  812. packet.utf8Write('direct-tcpip', p += 4, 12);
  813. writeUInt32BE(packet, chan, p += 12);
  814. writeUInt32BE(packet, initWindow, p += 4);
  815. writeUInt32BE(packet, maxPacket, p += 4);
  816. writeUInt32BE(packet, dstLen, p += 4);
  817. packet.utf8Write(cfg.dstIP, p += 4, dstLen);
  818. writeUInt32BE(packet, cfg.dstPort, p += dstLen);
  819. writeUInt32BE(packet, srcLen, p += 4);
  820. packet.utf8Write(cfg.srcIP, p += 4, srcLen);
  821. writeUInt32BE(packet, cfg.srcPort, p += srcLen);
  822. this._debug && this._debug(
  823. `Outbound: Sending CHANNEL_OPEN (r:${chan}, direct-tcpip)`
  824. );
  825. sendPacket(this, this._packetRW.write.finalize(packet));
  826. }
  827. openssh_directStreamLocal(chan, initWindow, maxPacket, cfg) {
  828. if (this._server)
  829. throw new Error('Client-only method called in server mode');
  830. const pathLen = Buffer.byteLength(cfg.socketPath);
  831. let p = this._packetRW.write.allocStart;
  832. const packet = this._packetRW.write.alloc(
  833. 1 + 4 + 30 + 4 + 4 + 4 + 4 + pathLen + 4 + 4
  834. );
  835. packet[p] = MESSAGE.CHANNEL_OPEN;
  836. writeUInt32BE(packet, 30, ++p);
  837. packet.utf8Write('direct-streamlocal@openssh.com', p += 4, 30);
  838. writeUInt32BE(packet, chan, p += 30);
  839. writeUInt32BE(packet, initWindow, p += 4);
  840. writeUInt32BE(packet, maxPacket, p += 4);
  841. writeUInt32BE(packet, pathLen, p += 4);
  842. packet.utf8Write(cfg.socketPath, p += 4, pathLen);
  843. // zero-fill reserved fields (string and uint32)
  844. bufferFill(packet, 0, p += pathLen, p + 8);
  845. if (this._debug) {
  846. this._debug(
  847. 'Outbound: Sending CHANNEL_OPEN '
  848. + `(r:${chan}, direct-streamlocal@openssh.com)`
  849. );
  850. }
  851. sendPacket(this, this._packetRW.write.finalize(packet));
  852. }
  853. openssh_noMoreSessions(wantReply) {
  854. if (this._server)
  855. throw new Error('Client-only method called in server mode');
  856. let p = this._packetRW.write.allocStart;
  857. const packet = this._packetRW.write.alloc(1 + 4 + 28 + 1);
  858. packet[p] = MESSAGE.GLOBAL_REQUEST;
  859. writeUInt32BE(packet, 28, ++p);
  860. packet.utf8Write('no-more-sessions@openssh.com', p += 4, 28);
  861. packet[p += 28] = (wantReply === undefined || wantReply === true ? 1 : 0);
  862. this._debug && this._debug(
  863. 'Outbound: Sending GLOBAL_REQUEST (no-more-sessions@openssh.com)'
  864. );
  865. sendPacket(this, this._packetRW.write.finalize(packet));
  866. }
  867. session(chan, initWindow, maxPacket) {
  868. if (this._server)
  869. throw new Error('Client-only method called in server mode');
  870. // Does not consume window space
  871. let p = this._packetRW.write.allocStart;
  872. const packet = this._packetRW.write.alloc(1 + 4 + 7 + 4 + 4 + 4);
  873. packet[p] = MESSAGE.CHANNEL_OPEN;
  874. writeUInt32BE(packet, 7, ++p);
  875. packet.utf8Write('session', p += 4, 7);
  876. writeUInt32BE(packet, chan, p += 7);
  877. writeUInt32BE(packet, initWindow, p += 4);
  878. writeUInt32BE(packet, maxPacket, p += 4);
  879. this._debug
  880. && this._debug(`Outbound: Sending CHANNEL_OPEN (r:${chan}, session)`);
  881. sendPacket(this, this._packetRW.write.finalize(packet));
  882. }
  883. windowChange(chan, rows, cols, height, width) {
  884. if (this._server)
  885. throw new Error('Client-only method called in server mode');
  886. // Does not consume window space
  887. let p = this._packetRW.write.allocStart;
  888. const packet = this._packetRW.write.alloc(
  889. 1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4
  890. );
  891. packet[p] = MESSAGE.CHANNEL_REQUEST;
  892. writeUInt32BE(packet, chan, ++p);
  893. writeUInt32BE(packet, 13, p += 4);
  894. packet.utf8Write('window-change', p += 4, 13);
  895. packet[p += 13] = 0;
  896. writeUInt32BE(packet, cols, ++p);
  897. writeUInt32BE(packet, rows, p += 4);
  898. writeUInt32BE(packet, width, p += 4);
  899. writeUInt32BE(packet, height, p += 4);
  900. this._debug && this._debug(
  901. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, window-change)`
  902. );
  903. sendPacket(this, this._packetRW.write.finalize(packet));
  904. }
  905. pty(chan, rows, cols, height, width, term, modes, wantReply) {
  906. if (this._server)
  907. throw new Error('Client-only method called in server mode');
  908. // Does not consume window space
  909. if (!term || !term.length)
  910. term = 'vt100';
  911. if (modes
  912. && !Buffer.isBuffer(modes)
  913. && !Array.isArray(modes)
  914. && typeof modes === 'object'
  915. && modes !== null) {
  916. modes = modesToBytes(modes);
  917. }
  918. if (!modes || !modes.length)
  919. modes = NO_TERMINAL_MODES_BUFFER;
  920. const termLen = term.length;
  921. const modesLen = modes.length;
  922. let p = this._packetRW.write.allocStart;
  923. const packet = this._packetRW.write.alloc(
  924. 1 + 4 + 4 + 7 + 1 + 4 + termLen + 4 + 4 + 4 + 4 + 4 + modesLen
  925. );
  926. packet[p] = MESSAGE.CHANNEL_REQUEST;
  927. writeUInt32BE(packet, chan, ++p);
  928. writeUInt32BE(packet, 7, p += 4);
  929. packet.utf8Write('pty-req', p += 4, 7);
  930. packet[p += 7] = (wantReply === undefined || wantReply === true ? 1 : 0);
  931. writeUInt32BE(packet, termLen, ++p);
  932. packet.utf8Write(term, p += 4, termLen);
  933. writeUInt32BE(packet, cols, p += termLen);
  934. writeUInt32BE(packet, rows, p += 4);
  935. writeUInt32BE(packet, width, p += 4);
  936. writeUInt32BE(packet, height, p += 4);
  937. writeUInt32BE(packet, modesLen, p += 4);
  938. p += 4;
  939. if (Array.isArray(modes)) {
  940. for (let i = 0; i < modesLen; ++i)
  941. packet[p++] = modes[i];
  942. } else if (Buffer.isBuffer(modes)) {
  943. packet.set(modes, p);
  944. }
  945. this._debug
  946. && this._debug(`Outbound: Sending CHANNEL_REQUEST (r:${chan}, pty-req)`);
  947. sendPacket(this, this._packetRW.write.finalize(packet));
  948. }
  949. shell(chan, wantReply) {
  950. if (this._server)
  951. throw new Error('Client-only method called in server mode');
  952. // Does not consume window space
  953. let p = this._packetRW.write.allocStart;
  954. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 5 + 1);
  955. packet[p] = MESSAGE.CHANNEL_REQUEST;
  956. writeUInt32BE(packet, chan, ++p);
  957. writeUInt32BE(packet, 5, p += 4);
  958. packet.utf8Write('shell', p += 4, 5);
  959. packet[p += 5] = (wantReply === undefined || wantReply === true ? 1 : 0);
  960. this._debug
  961. && this._debug(`Outbound: Sending CHANNEL_REQUEST (r:${chan}, shell)`);
  962. sendPacket(this, this._packetRW.write.finalize(packet));
  963. }
  964. exec(chan, cmd, wantReply) {
  965. if (this._server)
  966. throw new Error('Client-only method called in server mode');
  967. // Does not consume window space
  968. const isBuf = Buffer.isBuffer(cmd);
  969. const cmdLen = (isBuf ? cmd.length : Buffer.byteLength(cmd));
  970. let p = this._packetRW.write.allocStart;
  971. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + 1 + 4 + cmdLen);
  972. packet[p] = MESSAGE.CHANNEL_REQUEST;
  973. writeUInt32BE(packet, chan, ++p);
  974. writeUInt32BE(packet, 4, p += 4);
  975. packet.utf8Write('exec', p += 4, 4);
  976. packet[p += 4] = (wantReply === undefined || wantReply === true ? 1 : 0);
  977. writeUInt32BE(packet, cmdLen, ++p);
  978. if (isBuf)
  979. packet.set(cmd, p += 4);
  980. else
  981. packet.utf8Write(cmd, p += 4, cmdLen);
  982. this._debug && this._debug(
  983. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, exec: ${cmd})`
  984. );
  985. sendPacket(this, this._packetRW.write.finalize(packet));
  986. }
  987. signal(chan, signal) {
  988. if (this._server)
  989. throw new Error('Client-only method called in server mode');
  990. // Does not consume window space
  991. const origSignal = signal;
  992. signal = signal.toUpperCase();
  993. if (signal.slice(0, 3) === 'SIG')
  994. signal = signal.slice(3);
  995. if (SIGNALS[signal] !== 1)
  996. throw new Error(`Invalid signal: ${origSignal}`);
  997. const signalLen = signal.length;
  998. let p = this._packetRW.write.allocStart;
  999. const packet = this._packetRW.write.alloc(
  1000. 1 + 4 + 4 + 6 + 1 + 4 + signalLen
  1001. );
  1002. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1003. writeUInt32BE(packet, chan, ++p);
  1004. writeUInt32BE(packet, 6, p += 4);
  1005. packet.utf8Write('signal', p += 4, 6);
  1006. packet[p += 6] = 0;
  1007. writeUInt32BE(packet, signalLen, ++p);
  1008. packet.utf8Write(signal, p += 4, signalLen);
  1009. this._debug && this._debug(
  1010. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, signal: ${signal})`
  1011. );
  1012. sendPacket(this, this._packetRW.write.finalize(packet));
  1013. }
  1014. env(chan, key, val, wantReply) {
  1015. if (this._server)
  1016. throw new Error('Client-only method called in server mode');
  1017. // Does not consume window space
  1018. const keyLen = Buffer.byteLength(key);
  1019. const isBuf = Buffer.isBuffer(val);
  1020. const valLen = (isBuf ? val.length : Buffer.byteLength(val));
  1021. let p = this._packetRW.write.allocStart;
  1022. const packet = this._packetRW.write.alloc(
  1023. 1 + 4 + 4 + 3 + 1 + 4 + keyLen + 4 + valLen
  1024. );
  1025. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1026. writeUInt32BE(packet, chan, ++p);
  1027. writeUInt32BE(packet, 3, p += 4);
  1028. packet.utf8Write('env', p += 4, 3);
  1029. packet[p += 3] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1030. writeUInt32BE(packet, keyLen, ++p);
  1031. packet.utf8Write(key, p += 4, keyLen);
  1032. writeUInt32BE(packet, valLen, p += keyLen);
  1033. if (isBuf)
  1034. packet.set(val, p += 4);
  1035. else
  1036. packet.utf8Write(val, p += 4, valLen);
  1037. this._debug && this._debug(
  1038. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, env: ${key}=${val})`
  1039. );
  1040. sendPacket(this, this._packetRW.write.finalize(packet));
  1041. }
  1042. x11Forward(chan, cfg, wantReply) {
  1043. if (this._server)
  1044. throw new Error('Client-only method called in server mode');
  1045. // Does not consume window space
  1046. const protocol = cfg.protocol;
  1047. const cookie = cfg.cookie;
  1048. const isBufProto = Buffer.isBuffer(protocol);
  1049. const protoLen = (isBufProto
  1050. ? protocol.length
  1051. : Buffer.byteLength(protocol));
  1052. const isBufCookie = Buffer.isBuffer(cookie);
  1053. const cookieLen = (isBufCookie
  1054. ? cookie.length
  1055. : Buffer.byteLength(cookie));
  1056. let p = this._packetRW.write.allocStart;
  1057. const packet = this._packetRW.write.alloc(
  1058. 1 + 4 + 4 + 7 + 1 + 1 + 4 + protoLen + 4 + cookieLen + 4
  1059. );
  1060. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1061. writeUInt32BE(packet, chan, ++p);
  1062. writeUInt32BE(packet, 7, p += 4);
  1063. packet.utf8Write('x11-req', p += 4, 7);
  1064. packet[p += 7] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1065. packet[++p] = (cfg.single ? 1 : 0);
  1066. writeUInt32BE(packet, protoLen, ++p);
  1067. if (isBufProto)
  1068. packet.set(protocol, p += 4);
  1069. else
  1070. packet.utf8Write(protocol, p += 4, protoLen);
  1071. writeUInt32BE(packet, cookieLen, p += protoLen);
  1072. if (isBufCookie)
  1073. packet.set(cookie, p += 4);
  1074. else
  1075. packet.latin1Write(cookie, p += 4, cookieLen);
  1076. writeUInt32BE(packet, (cfg.screen || 0), p += cookieLen);
  1077. this._debug
  1078. && this._debug(`Outbound: Sending CHANNEL_REQUEST (r:${chan}, x11-req)`);
  1079. sendPacket(this, this._packetRW.write.finalize(packet));
  1080. }
  1081. subsystem(chan, name, wantReply) {
  1082. if (this._server)
  1083. throw new Error('Client-only method called in server mode');
  1084. // Does not consume window space
  1085. const nameLen = Buffer.byteLength(name);
  1086. let p = this._packetRW.write.allocStart;
  1087. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 9 + 1 + 4 + nameLen);
  1088. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1089. writeUInt32BE(packet, chan, ++p);
  1090. writeUInt32BE(packet, 9, p += 4);
  1091. packet.utf8Write('subsystem', p += 4, 9);
  1092. packet[p += 9] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1093. writeUInt32BE(packet, nameLen, ++p);
  1094. packet.utf8Write(name, p += 4, nameLen);
  1095. this._debug && this._debug(
  1096. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, subsystem: ${name})`
  1097. );
  1098. sendPacket(this, this._packetRW.write.finalize(packet));
  1099. }
  1100. openssh_agentForward(chan, wantReply) {
  1101. if (this._server)
  1102. throw new Error('Client-only method called in server mode');
  1103. // Does not consume window space
  1104. let p = this._packetRW.write.allocStart;
  1105. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 26 + 1);
  1106. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1107. writeUInt32BE(packet, chan, ++p);
  1108. writeUInt32BE(packet, 26, p += 4);
  1109. packet.utf8Write('auth-agent-req@openssh.com', p += 4, 26);
  1110. packet[p += 26] = (wantReply === undefined || wantReply === true ? 1 : 0);
  1111. if (this._debug) {
  1112. this._debug(
  1113. 'Outbound: Sending CHANNEL_REQUEST '
  1114. + `(r:${chan}, auth-agent-req@openssh.com)`
  1115. );
  1116. }
  1117. sendPacket(this, this._packetRW.write.finalize(packet));
  1118. }
  1119. openssh_hostKeysProve(keys) {
  1120. if (this._server)
  1121. throw new Error('Client-only method called in server mode');
  1122. let keysTotal = 0;
  1123. const publicKeys = [];
  1124. for (const key of keys) {
  1125. const publicKey = key.getPublicSSH();
  1126. keysTotal += 4 + publicKey.length;
  1127. publicKeys.push(publicKey);
  1128. }
  1129. let p = this._packetRW.write.allocStart;
  1130. const packet = this._packetRW.write.alloc(1 + 4 + 29 + 1 + keysTotal);
  1131. packet[p] = MESSAGE.GLOBAL_REQUEST;
  1132. writeUInt32BE(packet, 29, ++p);
  1133. packet.utf8Write('hostkeys-prove-00@openssh.com', p += 4, 29);
  1134. packet[p += 29] = 1; // want reply
  1135. ++p;
  1136. for (const buf of publicKeys) {
  1137. writeUInt32BE(packet, buf.length, p);
  1138. bufferCopy(buf, packet, 0, buf.length, p += 4);
  1139. p += buf.length;
  1140. }
  1141. if (this._debug) {
  1142. this._debug(
  1143. 'Outbound: Sending GLOBAL_REQUEST (hostkeys-prove-00@openssh.com)'
  1144. );
  1145. }
  1146. sendPacket(this, this._packetRW.write.finalize(packet));
  1147. }
  1148. // ===========================================================================
  1149. // Server-specific ===========================================================
  1150. // ===========================================================================
  1151. // Global
  1152. // ------
  1153. serviceAccept(svcName) {
  1154. if (!this._server)
  1155. throw new Error('Server-only method called in client mode');
  1156. const svcNameLen = Buffer.byteLength(svcName);
  1157. let p = this._packetRW.write.allocStart;
  1158. const packet = this._packetRW.write.alloc(1 + 4 + svcNameLen);
  1159. packet[p] = MESSAGE.SERVICE_ACCEPT;
  1160. writeUInt32BE(packet, svcNameLen, ++p);
  1161. packet.utf8Write(svcName, p += 4, svcNameLen);
  1162. this._debug && this._debug(`Outbound: Sending SERVICE_ACCEPT (${svcName})`);
  1163. sendPacket(this, this._packetRW.write.finalize(packet));
  1164. if (this._server && this._banner && svcName === 'ssh-userauth') {
  1165. const banner = this._banner;
  1166. this._banner = undefined; // Prevent banner from being displayed again
  1167. const bannerLen = Buffer.byteLength(banner);
  1168. p = this._packetRW.write.allocStart;
  1169. const packet = this._packetRW.write.alloc(1 + 4 + bannerLen + 4);
  1170. packet[p] = MESSAGE.USERAUTH_BANNER;
  1171. writeUInt32BE(packet, bannerLen, ++p);
  1172. packet.utf8Write(banner, p += 4, bannerLen);
  1173. writeUInt32BE(packet, 0, p += bannerLen); // Empty language tag
  1174. this._debug && this._debug('Outbound: Sending USERAUTH_BANNER');
  1175. sendPacket(this, this._packetRW.write.finalize(packet));
  1176. }
  1177. }
  1178. // 'ssh-connection' service-specific
  1179. forwardedTcpip(chan, initWindow, maxPacket, cfg) {
  1180. if (!this._server)
  1181. throw new Error('Server-only method called in client mode');
  1182. const boundAddrLen = Buffer.byteLength(cfg.boundAddr);
  1183. const remoteAddrLen = Buffer.byteLength(cfg.remoteAddr);
  1184. let p = this._packetRW.write.allocStart;
  1185. const packet = this._packetRW.write.alloc(
  1186. 1 + 4 + 15 + 4 + 4 + 4 + 4 + boundAddrLen + 4 + 4 + remoteAddrLen + 4
  1187. );
  1188. packet[p] = MESSAGE.CHANNEL_OPEN;
  1189. writeUInt32BE(packet, 15, ++p);
  1190. packet.utf8Write('forwarded-tcpip', p += 4, 15);
  1191. writeUInt32BE(packet, chan, p += 15);
  1192. writeUInt32BE(packet, initWindow, p += 4);
  1193. writeUInt32BE(packet, maxPacket, p += 4);
  1194. writeUInt32BE(packet, boundAddrLen, p += 4);
  1195. packet.utf8Write(cfg.boundAddr, p += 4, boundAddrLen);
  1196. writeUInt32BE(packet, cfg.boundPort, p += boundAddrLen);
  1197. writeUInt32BE(packet, remoteAddrLen, p += 4);
  1198. packet.utf8Write(cfg.remoteAddr, p += 4, remoteAddrLen);
  1199. writeUInt32BE(packet, cfg.remotePort, p += remoteAddrLen);
  1200. this._debug && this._debug(
  1201. `Outbound: Sending CHANNEL_OPEN (r:${chan}, forwarded-tcpip)`
  1202. );
  1203. sendPacket(this, this._packetRW.write.finalize(packet));
  1204. }
  1205. x11(chan, initWindow, maxPacket, cfg) {
  1206. if (!this._server)
  1207. throw new Error('Server-only method called in client mode');
  1208. const addrLen = Buffer.byteLength(cfg.originAddr);
  1209. let p = this._packetRW.write.allocStart;
  1210. const packet = this._packetRW.write.alloc(
  1211. 1 + 4 + 3 + 4 + 4 + 4 + 4 + addrLen + 4
  1212. );
  1213. packet[p] = MESSAGE.CHANNEL_OPEN;
  1214. writeUInt32BE(packet, 3, ++p);
  1215. packet.utf8Write('x11', p += 4, 3);
  1216. writeUInt32BE(packet, chan, p += 3);
  1217. writeUInt32BE(packet, initWindow, p += 4);
  1218. writeUInt32BE(packet, maxPacket, p += 4);
  1219. writeUInt32BE(packet, addrLen, p += 4);
  1220. packet.utf8Write(cfg.originAddr, p += 4, addrLen);
  1221. writeUInt32BE(packet, cfg.originPort, p += addrLen);
  1222. this._debug && this._debug(
  1223. `Outbound: Sending CHANNEL_OPEN (r:${chan}, x11)`
  1224. );
  1225. sendPacket(this, this._packetRW.write.finalize(packet));
  1226. }
  1227. openssh_authAgent(chan, initWindow, maxPacket) {
  1228. if (!this._server)
  1229. throw new Error('Server-only method called in client mode');
  1230. let p = this._packetRW.write.allocStart;
  1231. const packet = this._packetRW.write.alloc(1 + 4 + 22 + 4 + 4 + 4);
  1232. packet[p] = MESSAGE.CHANNEL_OPEN;
  1233. writeUInt32BE(packet, 22, ++p);
  1234. packet.utf8Write('auth-agent@openssh.com', p += 4, 22);
  1235. writeUInt32BE(packet, chan, p += 22);
  1236. writeUInt32BE(packet, initWindow, p += 4);
  1237. writeUInt32BE(packet, maxPacket, p += 4);
  1238. this._debug && this._debug(
  1239. `Outbound: Sending CHANNEL_OPEN (r:${chan}, auth-agent@openssh.com)`
  1240. );
  1241. sendPacket(this, this._packetRW.write.finalize(packet));
  1242. }
  1243. openssh_forwardedStreamLocal(chan, initWindow, maxPacket, cfg) {
  1244. if (!this._server)
  1245. throw new Error('Server-only method called in client mode');
  1246. const pathLen = Buffer.byteLength(cfg.socketPath);
  1247. let p = this._packetRW.write.allocStart;
  1248. const packet = this._packetRW.write.alloc(
  1249. 1 + 4 + 33 + 4 + 4 + 4 + 4 + pathLen + 4
  1250. );
  1251. packet[p] = MESSAGE.CHANNEL_OPEN;
  1252. writeUInt32BE(packet, 33, ++p);
  1253. packet.utf8Write('forwarded-streamlocal@openssh.com', p += 4, 33);
  1254. writeUInt32BE(packet, chan, p += 33);
  1255. writeUInt32BE(packet, initWindow, p += 4);
  1256. writeUInt32BE(packet, maxPacket, p += 4);
  1257. writeUInt32BE(packet, pathLen, p += 4);
  1258. packet.utf8Write(cfg.socketPath, p += 4, pathLen);
  1259. writeUInt32BE(packet, 0, p += pathLen);
  1260. if (this._debug) {
  1261. this._debug(
  1262. 'Outbound: Sending CHANNEL_OPEN '
  1263. + `(r:${chan}, forwarded-streamlocal@openssh.com)`
  1264. );
  1265. }
  1266. sendPacket(this, this._packetRW.write.finalize(packet));
  1267. }
  1268. exitStatus(chan, status) {
  1269. if (!this._server)
  1270. throw new Error('Server-only method called in client mode');
  1271. // Does not consume window space
  1272. let p = this._packetRW.write.allocStart;
  1273. const packet = this._packetRW.write.alloc(1 + 4 + 4 + 11 + 1 + 4);
  1274. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1275. writeUInt32BE(packet, chan, ++p);
  1276. writeUInt32BE(packet, 11, p += 4);
  1277. packet.utf8Write('exit-status', p += 4, 11);
  1278. packet[p += 11] = 0;
  1279. writeUInt32BE(packet, status, ++p);
  1280. this._debug && this._debug(
  1281. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, exit-status: ${status})`
  1282. );
  1283. sendPacket(this, this._packetRW.write.finalize(packet));
  1284. }
  1285. exitSignal(chan, name, coreDumped, msg) {
  1286. if (!this._server)
  1287. throw new Error('Server-only method called in client mode');
  1288. // Does not consume window space
  1289. const origSignal = name;
  1290. if (typeof origSignal !== 'string' || !origSignal)
  1291. throw new Error(`Invalid signal: ${origSignal}`);
  1292. let signal = name.toUpperCase();
  1293. if (signal.slice(0, 3) === 'SIG')
  1294. signal = signal.slice(3);
  1295. if (SIGNALS[signal] !== 1)
  1296. throw new Error(`Invalid signal: ${origSignal}`);
  1297. const nameLen = Buffer.byteLength(signal);
  1298. const msgLen = (msg ? Buffer.byteLength(msg) : 0);
  1299. let p = this._packetRW.write.allocStart;
  1300. const packet = this._packetRW.write.alloc(
  1301. 1 + 4 + 4 + 11 + 1 + 4 + nameLen + 1 + 4 + msgLen + 4
  1302. );
  1303. packet[p] = MESSAGE.CHANNEL_REQUEST;
  1304. writeUInt32BE(packet, chan, ++p);
  1305. writeUInt32BE(packet, 11, p += 4);
  1306. packet.utf8Write('exit-signal', p += 4, 11);
  1307. packet[p += 11] = 0;
  1308. writeUInt32BE(packet, nameLen, ++p);
  1309. packet.utf8Write(signal, p += 4, nameLen);
  1310. packet[p += nameLen] = (coreDumped ? 1 : 0);
  1311. writeUInt32BE(packet, msgLen, ++p);
  1312. p += 4;
  1313. if (msgLen) {
  1314. packet.utf8Write(msg, p, msgLen);
  1315. p += msgLen;
  1316. }
  1317. writeUInt32BE(packet, 0, p);
  1318. this._debug && this._debug(
  1319. `Outbound: Sending CHANNEL_REQUEST (r:${chan}, exit-signal: ${name})`
  1320. );
  1321. sendPacket(this, this._packetRW.write.finalize(packet));
  1322. }
  1323. // 'ssh-userauth' service-specific
  1324. authFailure(authMethods, isPartial) {
  1325. if (!this._server)
  1326. throw new Error('Server-only method called in client mode');
  1327. if (this._authsQueue.length === 0)
  1328. throw new Error('No auth in progress');
  1329. let methods;
  1330. if (typeof authMethods === 'boolean') {
  1331. isPartial = authMethods;
  1332. authMethods = undefined;
  1333. }
  1334. if (authMethods) {
  1335. methods = [];
  1336. for (let i = 0; i < authMethods.length; ++i) {
  1337. if (authMethods[i].toLowerCase() === 'none')
  1338. continue;
  1339. methods.push(authMethods[i]);
  1340. }
  1341. methods = methods.join(',');
  1342. } else {
  1343. methods = '';
  1344. }
  1345. const methodsLen = methods.length;
  1346. let p = this._packetRW.write.allocStart;
  1347. const packet = this._packetRW.write.alloc(1 + 4 + methodsLen + 1);
  1348. packet[p] = MESSAGE.USERAUTH_FAILURE;
  1349. writeUInt32BE(packet, methodsLen, ++p);
  1350. packet.utf8Write(methods, p += 4, methodsLen);
  1351. packet[p += methodsLen] = (isPartial === true ? 1 : 0);
  1352. this._authsQueue.shift();
  1353. this._debug && this._debug('Outbound: Sending USERAUTH_FAILURE');
  1354. sendPacket(this, this._packetRW.write.finalize(packet));
  1355. }
  1356. authSuccess() {
  1357. if (!this._server)
  1358. throw new Error('Server-only method called in client mode');
  1359. if (this._authsQueue.length === 0)
  1360. throw new Error('No auth in progress');
  1361. const p = this._packetRW.write.allocStart;
  1362. const packet = this._packetRW.write.alloc(1);
  1363. packet[p] = MESSAGE.USERAUTH_SUCCESS;
  1364. this._authsQueue.shift();
  1365. this._authenticated = true;
  1366. this._debug && this._debug('Outbound: Sending USERAUTH_SUCCESS');
  1367. sendPacket(this, this._packetRW.write.finalize(packet));
  1368. if (this._kex.negotiated.cs.compress === 'zlib@openssh.com')
  1369. this._packetRW.read = new ZlibPacketReader();
  1370. if (this._kex.negotiated.sc.compress === 'zlib@openssh.com')
  1371. this._packetRW.write = new ZlibPacketWriter(this);
  1372. }
  1373. authPKOK(keyAlgo, key) {
  1374. if (!this._server)
  1375. throw new Error('Server-only method called in client mode');
  1376. if (this._authsQueue.length === 0 || this._authsQueue[0] !== 'publickey')
  1377. throw new Error('"publickey" auth not in progress');
  1378. // TODO: support parsed key for `key`
  1379. const keyAlgoLen = Buffer.byteLength(keyAlgo);
  1380. const keyLen = key.length;
  1381. let p = this._packetRW.write.allocStart;
  1382. const packet = this._packetRW.write.alloc(1 + 4 + keyAlgoLen + 4 + keyLen);
  1383. packet[p] = MESSAGE.USERAUTH_PK_OK;
  1384. writeUInt32BE(packet, keyAlgoLen, ++p);
  1385. packet.utf8Write(keyAlgo, p += 4, keyAlgoLen);
  1386. writeUInt32BE(packet, keyLen, p += keyAlgoLen);
  1387. packet.set(key, p += 4);
  1388. this._authsQueue.shift();
  1389. this._debug && this._debug('Outbound: Sending USERAUTH_PK_OK');
  1390. sendPacket(this, this._packetRW.write.finalize(packet));
  1391. }
  1392. authPasswdChg(prompt) {
  1393. if (!this._server)
  1394. throw new Error('Server-only method called in client mode');
  1395. const promptLen = Buffer.byteLength(prompt);
  1396. let p = this._packetRW.write.allocStart;
  1397. const packet = this._packetRW.write.alloc(1 + 4 + promptLen + 4);
  1398. packet[p] = MESSAGE.USERAUTH_PASSWD_CHANGEREQ;
  1399. writeUInt32BE(packet, promptLen, ++p);
  1400. packet.utf8Write(prompt, p += 4, promptLen);
  1401. writeUInt32BE(packet, 0, p += promptLen); // Empty language tag
  1402. this._debug && this._debug('Outbound: Sending USERAUTH_PASSWD_CHANGEREQ');
  1403. sendPacket(this, this._packetRW.write.finalize(packet));
  1404. }
  1405. authInfoReq(name, instructions, prompts) {
  1406. if (!this._server)
  1407. throw new Error('Server-only method called in client mode');
  1408. let promptsLen = 0;
  1409. const nameLen = name ? Buffer.byteLength(name) : 0;
  1410. const instrLen = instructions ? Buffer.byteLength(instructions) : 0;
  1411. for (let i = 0; i < prompts.length; ++i)
  1412. promptsLen += 4 + Buffer.byteLength(prompts[i].prompt) + 1;
  1413. let p = this._packetRW.write.allocStart;
  1414. const packet = this._packetRW.write.alloc(
  1415. 1 + 4 + nameLen + 4 + instrLen + 4 + 4 + promptsLen
  1416. );
  1417. packet[p] = MESSAGE.USERAUTH_INFO_REQUEST;
  1418. writeUInt32BE(packet, nameLen, ++p);
  1419. p += 4;
  1420. if (name) {
  1421. packet.utf8Write(name, p, nameLen);
  1422. p += nameLen;
  1423. }
  1424. writeUInt32BE(packet, instrLen, p);
  1425. p += 4;
  1426. if (instructions) {
  1427. packet.utf8Write(instructions, p, instrLen);
  1428. p += instrLen;
  1429. }
  1430. writeUInt32BE(packet, 0, p);
  1431. writeUInt32BE(packet, prompts.length, p += 4);
  1432. p += 4;
  1433. for (let i = 0; i < prompts.length; ++i) {
  1434. const prompt = prompts[i];
  1435. const promptLen = Buffer.byteLength(prompt.prompt);
  1436. writeUInt32BE(packet, promptLen, p);
  1437. p += 4;
  1438. if (promptLen) {
  1439. packet.utf8Write(prompt.prompt, p, promptLen);
  1440. p += promptLen;
  1441. }
  1442. packet[p++] = (prompt.echo ? 1 : 0);
  1443. }
  1444. this._debug && this._debug('Outbound: Sending USERAUTH_INFO_REQUEST');
  1445. sendPacket(this, this._packetRW.write.finalize(packet));
  1446. }
  1447. }
  1448. // SSH-protoversion-softwareversion (SP comments) CR LF
  1449. const RE_IDENT = /^SSH-(2\.0|1\.99)-([^ ]+)(?: (.*))?$/;
  1450. // TODO: optimize this by starting n bytes from the end of this._buffer instead
  1451. // of the beginning
  1452. function parseHeader(chunk, p, len) {
  1453. let data;
  1454. let chunkOffset;
  1455. if (this._buffer) {
  1456. data = Buffer.allocUnsafe(this._buffer.length + (len - p));
  1457. data.set(this._buffer, 0);
  1458. if (p === 0) {
  1459. data.set(chunk, this._buffer.length);
  1460. } else {
  1461. data.set(new Uint8Array(chunk.buffer,
  1462. chunk.byteOffset + p,
  1463. (len - p)),
  1464. this._buffer.length);
  1465. }
  1466. chunkOffset = this._buffer.length;
  1467. p = 0;
  1468. } else {
  1469. data = chunk;
  1470. chunkOffset = 0;
  1471. }
  1472. const op = p;
  1473. let start = p;
  1474. let end = p;
  1475. let needNL = false;
  1476. let lineLen = 0;
  1477. let lines = 0;
  1478. for (; p < data.length; ++p) {
  1479. const ch = data[p];
  1480. if (ch === 13 /* '\r' */) {
  1481. needNL = true;
  1482. continue;
  1483. }
  1484. if (ch === 10 /* '\n' */) {
  1485. if (end > start
  1486. && end - start > 4
  1487. && data[start] === 83 /* 'S' */
  1488. && data[start + 1] === 83 /* 'S' */
  1489. && data[start + 2] === 72 /* 'H' */
  1490. && data[start + 3] === 45 /* '-' */) {
  1491. const full = data.latin1Slice(op, end + 1);
  1492. const identRaw = (start === op ? full : full.slice(start - op));
  1493. const m = RE_IDENT.exec(identRaw);
  1494. if (!m)
  1495. throw new Error('Invalid identification string');
  1496. const header = {
  1497. greeting: (start === op ? '' : full.slice(0, start - op)),
  1498. identRaw,
  1499. versions: {
  1500. protocol: m[1],
  1501. software: m[2],
  1502. },
  1503. comments: m[3]
  1504. };
  1505. // Needed during handshake
  1506. this._remoteIdentRaw = Buffer.from(identRaw);
  1507. this._debug && this._debug(`Remote ident: ${inspect(identRaw)}`);
  1508. this._compatFlags = getCompatFlags(header);
  1509. this._buffer = undefined;
  1510. this._decipher =
  1511. new NullDecipher(0, onKEXPayload.bind(this, { firstPacket: true }));
  1512. this._parse = parsePacket;
  1513. this._onHeader(header);
  1514. if (!this._destruct) {
  1515. // We disconnected inside _onHeader
  1516. return len;
  1517. }
  1518. kexinit(this);
  1519. return p + 1 - chunkOffset;
  1520. }
  1521. // Only allow pre-ident greetings when we're a client
  1522. if (this._server)
  1523. throw new Error('Greetings from clients not permitted');
  1524. if (++lines > MAX_LINES)
  1525. throw new Error('Max greeting lines exceeded');
  1526. needNL = false;
  1527. start = p + 1;
  1528. lineLen = 0;
  1529. } else if (needNL) {
  1530. throw new Error('Invalid header: expected newline');
  1531. } else if (++lineLen >= MAX_LINE_LEN) {
  1532. throw new Error('Header line too long');
  1533. }
  1534. end = p;
  1535. }
  1536. if (!this._buffer)
  1537. this._buffer = bufferSlice(data, op);
  1538. return p - chunkOffset;
  1539. }
  1540. function parsePacket(chunk, p, len) {
  1541. return this._decipher.decrypt(chunk, p, len);
  1542. }
  1543. function onPayload(payload) {
  1544. // XXX: move this to the Decipher implementations?
  1545. this._onPacket();
  1546. if (payload.length === 0) {
  1547. this._debug && this._debug('Inbound: Skipping empty packet payload');
  1548. return;
  1549. }
  1550. payload = this._packetRW.read.read(payload);
  1551. const type = payload[0];
  1552. if (type === MESSAGE.USERAUTH_SUCCESS
  1553. && !this._server
  1554. && !this._authenticated) {
  1555. this._authenticated = true;
  1556. if (this._kex.negotiated.cs.compress === 'zlib@openssh.com')
  1557. this._packetRW.write = new ZlibPacketWriter(this);
  1558. if (this._kex.negotiated.sc.compress === 'zlib@openssh.com')
  1559. this._packetRW.read = new ZlibPacketReader();
  1560. }
  1561. const handler = MESSAGE_HANDLERS[type];
  1562. if (handler === undefined) {
  1563. this._debug && this._debug(`Inbound: Unsupported message type: ${type}`);
  1564. return;
  1565. }
  1566. return handler(this, payload);
  1567. }
  1568. function getCompatFlags(header) {
  1569. const software = header.versions.software;
  1570. let flags = 0;
  1571. for (const rule of COMPAT_CHECKS) {
  1572. if (typeof rule[0] === 'string') {
  1573. if (software === rule[0])
  1574. flags |= rule[1];
  1575. } else if (rule[0].test(software)) {
  1576. flags |= rule[1];
  1577. }
  1578. }
  1579. return flags;
  1580. }
  1581. function modesToBytes(modes) {
  1582. const keys = Object.keys(modes);
  1583. const bytes = Buffer.allocUnsafe((5 * keys.length) + 1);
  1584. let b = 0;
  1585. for (let i = 0; i < keys.length; ++i) {
  1586. const key = keys[i];
  1587. if (key === 'TTY_OP_END')
  1588. continue;
  1589. const opcode = TERMINAL_MODE[key];
  1590. if (opcode === undefined)
  1591. continue;
  1592. const val = modes[key];
  1593. if (typeof val === 'number' && isFinite(val)) {
  1594. bytes[b++] = opcode;
  1595. bytes[b++] = val >>> 24;
  1596. bytes[b++] = val >>> 16;
  1597. bytes[b++] = val >>> 8;
  1598. bytes[b++] = val;
  1599. }
  1600. }
  1601. bytes[b++] = TERMINAL_MODE.TTY_OP_END;
  1602. if (b < bytes.length)
  1603. return bufferSlice(bytes, 0, b);
  1604. return bytes;
  1605. }
  1606. module.exports = Protocol;