12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214 |
- 'use strict';
- const {
- bufferSlice,
- bufferParser,
- doFatalError,
- sigSSHToASN1,
- writeUInt32BE,
- } = require('./utils.js');
- const {
- CHANNEL_OPEN_FAILURE,
- COMPAT,
- MESSAGE,
- TERMINAL_MODE,
- } = require('./constants.js');
- const {
- parseKey,
- } = require('./keyParser.js');
- const TERMINAL_MODE_BY_VALUE =
- Array.from(Object.entries(TERMINAL_MODE))
- .reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {});
- module.exports = {
- // Transport layer protocol ==================================================
- [MESSAGE.DISCONNECT]: (self, payload) => {
- /*
- byte SSH_MSG_DISCONNECT
- uint32 reason code
- string description in ISO-10646 UTF-8 encoding
- string language tag
- */
- bufferParser.init(payload, 1);
- const reason = bufferParser.readUInt32BE();
- const desc = bufferParser.readString(true);
- const lang = bufferParser.readString();
- bufferParser.clear();
- if (lang === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed DISCONNECT packet'
- );
- }
- self._debug && self._debug(
- `Inbound: Received DISCONNECT (${reason}, "${desc}")`
- );
- const handler = self._handlers.DISCONNECT;
- handler && handler(self, reason, desc);
- },
- [MESSAGE.IGNORE]: (self, payload) => {
- /*
- byte SSH_MSG_IGNORE
- string data
- */
- self._debug && self._debug('Inbound: Received IGNORE');
- },
- [MESSAGE.UNIMPLEMENTED]: (self, payload) => {
- /*
- byte SSH_MSG_UNIMPLEMENTED
- uint32 packet sequence number of rejected message
- */
- bufferParser.init(payload, 1);
- const seqno = bufferParser.readUInt32BE();
- bufferParser.clear();
- if (seqno === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed UNIMPLEMENTED packet'
- );
- }
- self._debug
- && self._debug(`Inbound: Received UNIMPLEMENTED (seqno ${seqno})`);
- },
- [MESSAGE.DEBUG]: (self, payload) => {
- /*
- byte SSH_MSG_DEBUG
- boolean always_display
- string message in ISO-10646 UTF-8 encoding [RFC3629]
- string language tag [RFC3066]
- */
- bufferParser.init(payload, 1);
- const display = bufferParser.readBool();
- const msg = bufferParser.readString(true);
- const lang = bufferParser.readString();
- bufferParser.clear();
- if (lang === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed DEBUG packet'
- );
- }
- self._debug && self._debug('Inbound: Received DEBUG');
- const handler = self._handlers.DEBUG;
- handler && handler(self, display, msg);
- },
- [MESSAGE.SERVICE_REQUEST]: (self, payload) => {
- /*
- byte SSH_MSG_SERVICE_REQUEST
- string service name
- */
- bufferParser.init(payload, 1);
- const name = bufferParser.readString(true);
- bufferParser.clear();
- if (name === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed SERVICE_REQUEST packet'
- );
- }
- self._debug && self._debug(`Inbound: Received SERVICE_REQUEST (${name})`);
- const handler = self._handlers.SERVICE_REQUEST;
- handler && handler(self, name);
- },
- [MESSAGE.SERVICE_ACCEPT]: (self, payload) => {
- // S->C
- /*
- byte SSH_MSG_SERVICE_ACCEPT
- string service name
- */
- bufferParser.init(payload, 1);
- const name = bufferParser.readString(true);
- bufferParser.clear();
- if (name === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed SERVICE_ACCEPT packet'
- );
- }
- self._debug && self._debug(`Inbound: Received SERVICE_ACCEPT (${name})`);
- const handler = self._handlers.SERVICE_ACCEPT;
- handler && handler(self, name);
- },
- // User auth protocol -- generic =============================================
- [MESSAGE.USERAUTH_REQUEST]: (self, payload) => {
- /*
- byte SSH_MSG_USERAUTH_REQUEST
- string user name in ISO-10646 UTF-8 encoding [RFC3629]
- string service name in US-ASCII
- string method name in US-ASCII
- .... method specific fields
- */
- bufferParser.init(payload, 1);
- const user = bufferParser.readString(true);
- const service = bufferParser.readString(true);
- const method = bufferParser.readString(true);
- let methodData;
- let methodDesc;
- switch (method) {
- case 'none':
- methodData = null;
- break;
- case 'password': {
- /*
- boolean <new password follows (old) plaintext password?>
- string plaintext password in ISO-10646 UTF-8 encoding [RFC3629]
- [string new password]
- */
- const isChange = bufferParser.readBool();
- if (isChange !== undefined) {
- methodData = bufferParser.readString(true);
- if (methodData !== undefined && isChange) {
- const newPassword = bufferParser.readString(true);
- if (newPassword !== undefined)
- methodData = { oldPassword: methodData, newPassword };
- else
- methodData = undefined;
- }
- }
- break;
- }
- case 'publickey': {
- /*
- boolean <signature follows public key blob?>
- string public key algorithm name
- string public key blob
- [string signature]
- */
- const hasSig = bufferParser.readBool();
- if (hasSig !== undefined) {
- const keyAlgo = bufferParser.readString(true);
- const key = bufferParser.readString();
- if (hasSig) {
- const blobEnd = bufferParser.pos();
- let signature = bufferParser.readString();
- if (signature !== undefined) {
- if (signature.length > (4 + keyAlgo.length + 4)
- && signature.utf8Slice(4, 4 + keyAlgo.length) === keyAlgo) {
- // Skip algoLen + algo + sigLen
- signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
- }
- signature = sigSSHToASN1(signature, keyAlgo);
- if (signature) {
- const sessionID = self._kex.sessionID;
- const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
- writeUInt32BE(blob, sessionID.length, 0);
- blob.set(sessionID, 4);
- blob.set(
- new Uint8Array(payload.buffer, payload.byteOffset, blobEnd),
- 4 + sessionID.length
- );
- methodData = {
- keyAlgo,
- key,
- signature,
- blob,
- };
- }
- }
- } else {
- methodData = { keyAlgo, key };
- methodDesc = 'publickey -- check';
- }
- }
- break;
- }
- case 'hostbased': {
- /*
- string public key algorithm for host key
- string public host key and certificates for client host
- string client host name expressed as the FQDN in US-ASCII
- string user name on the client host in ISO-10646 UTF-8 encoding
- [RFC3629]
- string signature
- */
- const keyAlgo = bufferParser.readString(true);
- const key = bufferParser.readString();
- const localHostname = bufferParser.readString(true);
- const localUsername = bufferParser.readString(true);
- const blobEnd = bufferParser.pos();
- let signature = bufferParser.readString();
- if (signature !== undefined) {
- if (signature.length > (4 + keyAlgo.length + 4)
- && signature.utf8Slice(4, 4 + keyAlgo.length) === keyAlgo) {
- // Skip algoLen + algo + sigLen
- signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
- }
- signature = sigSSHToASN1(signature, keyAlgo);
- if (signature !== undefined) {
- const sessionID = self._kex.sessionID;
- const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
- writeUInt32BE(blob, sessionID.length, 0);
- blob.set(sessionID, 4);
- blob.set(
- new Uint8Array(payload.buffer, payload.byteOffset, blobEnd),
- 4 + sessionID.length
- );
- methodData = {
- keyAlgo,
- key,
- signature,
- blob,
- localHostname,
- localUsername,
- };
- }
- }
- break;
- }
- case 'keyboard-interactive':
- /*
- string language tag (as defined in [RFC-3066])
- string submethods (ISO-10646 UTF-8)
- */
- // Skip/ignore language field -- it's deprecated in RFC 4256
- bufferParser.skipString();
- methodData = bufferParser.readList();
- break;
- default:
- if (method !== undefined)
- methodData = bufferParser.readRaw();
- }
- bufferParser.clear();
- if (methodData === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_REQUEST packet'
- );
- }
- if (methodDesc === undefined)
- methodDesc = method;
- self._authsQueue.push(method);
- self._debug
- && self._debug(`Inbound: Received USERAUTH_REQUEST (${methodDesc})`);
- const handler = self._handlers.USERAUTH_REQUEST;
- handler && handler(self, user, service, method, methodData);
- },
- [MESSAGE.USERAUTH_FAILURE]: (self, payload) => {
- // S->C
- /*
- byte SSH_MSG_USERAUTH_FAILURE
- name-list authentications that can continue
- boolean partial success
- */
- bufferParser.init(payload, 1);
- const authMethods = bufferParser.readList();
- const partialSuccess = bufferParser.readBool();
- bufferParser.clear();
- if (partialSuccess === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_FAILURE packet'
- );
- }
- self._debug
- && self._debug(`Inbound: Received USERAUTH_FAILURE (${authMethods})`);
- self._authsQueue.shift();
- const handler = self._handlers.USERAUTH_FAILURE;
- handler && handler(self, authMethods, partialSuccess);
- },
- [MESSAGE.USERAUTH_SUCCESS]: (self, payload) => {
- // S->C
- /*
- byte SSH_MSG_USERAUTH_SUCCESS
- */
- self._debug && self._debug('Inbound: Received USERAUTH_SUCCESS');
- self._authsQueue.shift();
- const handler = self._handlers.USERAUTH_SUCCESS;
- handler && handler(self);
- },
- [MESSAGE.USERAUTH_BANNER]: (self, payload) => {
- // S->C
- /*
- byte SSH_MSG_USERAUTH_BANNER
- string message in ISO-10646 UTF-8 encoding [RFC3629]
- string language tag [RFC3066]
- */
- bufferParser.init(payload, 1);
- const msg = bufferParser.readString(true);
- const lang = bufferParser.readString();
- bufferParser.clear();
- if (lang === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_BANNER packet'
- );
- }
- self._debug && self._debug('Inbound: Received USERAUTH_BANNER');
- const handler = self._handlers.USERAUTH_BANNER;
- handler && handler(self, msg);
- },
- // User auth protocol -- method-specific =====================================
- 60: (self, payload) => {
- if (!self._authsQueue.length) {
- self._debug
- && self._debug('Inbound: Received payload type 60 without auth');
- return;
- }
- switch (self._authsQueue[0]) {
- case 'password': {
- // S->C
- /*
- byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
- string prompt in ISO-10646 UTF-8 encoding [RFC3629]
- string language tag [RFC3066]
- */
- bufferParser.init(payload, 1);
- const prompt = bufferParser.readString(true);
- const lang = bufferParser.readString();
- bufferParser.clear();
- if (lang === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_PASSWD_CHANGEREQ packet'
- );
- }
- self._debug
- && self._debug('Inbound: Received USERAUTH_PASSWD_CHANGEREQ');
- const handler = self._handlers.USERAUTH_PASSWD_CHANGEREQ;
- handler && handler(self, prompt);
- break;
- }
- case 'publickey': {
- // S->C
- /*
- byte SSH_MSG_USERAUTH_PK_OK
- string public key algorithm name from the request
- string public key blob from the request
- */
- bufferParser.init(payload, 1);
- const keyAlgo = bufferParser.readString(true);
- const key = bufferParser.readString();
- bufferParser.clear();
- if (key === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_PK_OK packet'
- );
- }
- self._debug && self._debug('Inbound: Received USERAUTH_PK_OK');
- self._authsQueue.shift();
- const handler = self._handlers.USERAUTH_PK_OK;
- handler && handler(self, keyAlgo, key);
- break;
- }
- case 'keyboard-interactive': {
- // S->C
- /*
- byte SSH_MSG_USERAUTH_INFO_REQUEST
- string name (ISO-10646 UTF-8)
- string instruction (ISO-10646 UTF-8)
- string language tag (as defined in [RFC-3066])
- int num-prompts
- string prompt[1] (ISO-10646 UTF-8)
- boolean echo[1]
- ...
- string prompt[num-prompts] (ISO-10646 UTF-8)
- boolean echo[num-prompts]
- */
- bufferParser.init(payload, 1);
- const name = bufferParser.readString(true);
- const instructions = bufferParser.readString(true);
- bufferParser.readString(); // skip lang
- const numPrompts = bufferParser.readUInt32BE();
- let prompts;
- if (numPrompts !== undefined) {
- prompts = new Array(numPrompts);
- let i;
- for (i = 0; i < numPrompts; ++i) {
- const prompt = bufferParser.readString(true);
- const echo = bufferParser.readBool();
- if (echo === undefined)
- break;
- prompts[i] = { prompt, echo };
- }
- if (i !== numPrompts)
- prompts = undefined;
- }
- bufferParser.clear();
- if (prompts === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_INFO_REQUEST packet'
- );
- }
- self._debug && self._debug('Inbound: Received USERAUTH_INFO_REQUEST');
- const handler = self._handlers.USERAUTH_INFO_REQUEST;
- handler && handler(self, name, instructions, prompts);
- break;
- }
- default:
- self._debug
- && self._debug('Inbound: Received unexpected payload type 60');
- }
- },
- 61: (self, payload) => {
- if (!self._authsQueue.length) {
- self._debug
- && self._debug('Inbound: Received payload type 61 without auth');
- return;
- }
- /*
- byte SSH_MSG_USERAUTH_INFO_RESPONSE
- int num-responses
- string response[1] (ISO-10646 UTF-8)
- ...
- string response[num-responses] (ISO-10646 UTF-8)
- */
- if (self._authsQueue[0] !== 'keyboard-interactive') {
- return doFatalError(
- self,
- 'Inbound: Received unexpected payload type 61'
- );
- }
- bufferParser.init(payload, 1);
- const numResponses = bufferParser.readUInt32BE();
- let responses;
- if (numResponses !== undefined) {
- responses = new Array(numResponses);
- let i;
- for (i = 0; i < numResponses; ++i) {
- const response = bufferParser.readString(true);
- if (response === undefined)
- break;
- responses[i] = response;
- }
- if (i !== numResponses)
- responses = undefined;
- }
- bufferParser.clear();
- if (responses === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed USERAUTH_INFO_RESPONSE packet'
- );
- }
- self._debug && self._debug('Inbound: Received USERAUTH_INFO_RESPONSE');
- const handler = self._handlers.USERAUTH_INFO_RESPONSE;
- handler && handler(self, responses);
- },
- // Connection protocol -- generic ============================================
- [MESSAGE.GLOBAL_REQUEST]: (self, payload) => {
- /*
- byte SSH_MSG_GLOBAL_REQUEST
- string request name in US-ASCII only
- boolean want reply
- .... request-specific data follows
- */
- bufferParser.init(payload, 1);
- const name = bufferParser.readString(true);
- const wantReply = bufferParser.readBool();
- let data;
- if (wantReply !== undefined) {
- switch (name) {
- case 'tcpip-forward':
- case 'cancel-tcpip-forward': {
- /*
- string address to bind (e.g., "0.0.0.0")
- uint32 port number to bind
- */
- const bindAddr = bufferParser.readString(true);
- const bindPort = bufferParser.readUInt32BE();
- if (bindPort !== undefined)
- data = { bindAddr, bindPort };
- break;
- }
- case 'streamlocal-forward@openssh.com':
- case 'cancel-streamlocal-forward@openssh.com': {
- /*
- string socket path
- */
- const socketPath = bufferParser.readString(true);
- if (socketPath !== undefined)
- data = { socketPath };
- break;
- }
- case 'no-more-sessions@openssh.com':
- data = null;
- break;
- case 'hostkeys-00@openssh.com': {
- data = [];
- while (bufferParser.avail() > 0) {
- const keyRaw = bufferParser.readString();
- if (keyRaw === undefined) {
- data = undefined;
- break;
- }
- const key = parseKey(keyRaw);
- if (!(key instanceof Error))
- data.push(key);
- }
- break;
- }
- default:
- data = bufferParser.readRaw();
- }
- }
- bufferParser.clear();
- if (data === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed GLOBAL_REQUEST packet'
- );
- }
- self._debug && self._debug(`Inbound: GLOBAL_REQUEST (${name})`);
- const handler = self._handlers.GLOBAL_REQUEST;
- if (handler)
- handler(self, name, wantReply, data);
- else
- self.requestFailure(); // Auto reject
- },
- [MESSAGE.REQUEST_SUCCESS]: (self, payload) => {
- /*
- byte SSH_MSG_REQUEST_SUCCESS
- .... response specific data
- */
- const data = (payload.length > 1 ? bufferSlice(payload, 1) : null);
- self._debug && self._debug('Inbound: REQUEST_SUCCESS');
- const handler = self._handlers.REQUEST_SUCCESS;
- handler && handler(self, data);
- },
- [MESSAGE.REQUEST_FAILURE]: (self, payload) => {
- /*
- byte SSH_MSG_REQUEST_FAILURE
- */
- self._debug && self._debug('Inbound: Received REQUEST_FAILURE');
- const handler = self._handlers.REQUEST_FAILURE;
- handler && handler(self);
- },
- // Connection protocol -- channel-related ====================================
- [MESSAGE.CHANNEL_OPEN]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_OPEN
- string channel type in US-ASCII only
- uint32 sender channel
- uint32 initial window size
- uint32 maximum packet size
- .... channel type specific data follows
- */
- bufferParser.init(payload, 1);
- const type = bufferParser.readString(true);
- const sender = bufferParser.readUInt32BE();
- const window = bufferParser.readUInt32BE();
- const packetSize = bufferParser.readUInt32BE();
- let channelInfo;
- switch (type) {
- case 'forwarded-tcpip': // S->C
- case 'direct-tcpip': { // C->S
- /*
- string address that was connected / host to connect
- uint32 port that was connected / port to connect
- string originator IP address
- uint32 originator port
- */
- const destIP = bufferParser.readString(true);
- const destPort = bufferParser.readUInt32BE();
- const srcIP = bufferParser.readString(true);
- const srcPort = bufferParser.readUInt32BE();
- if (srcPort !== undefined) {
- channelInfo = {
- type,
- sender,
- window,
- packetSize,
- data: { destIP, destPort, srcIP, srcPort }
- };
- }
- break;
- }
- case 'forwarded-streamlocal@openssh.com': // S->C
- case 'direct-streamlocal@openssh.com': { // C->S
- /*
- string socket path
- string reserved for future use
- (direct-streamlocal@openssh.com additionally has:)
- uint32 reserved
- */
- const socketPath = bufferParser.readString(true);
- if (socketPath !== undefined) {
- channelInfo = {
- type,
- sender,
- window,
- packetSize,
- data: { socketPath }
- };
- }
- break;
- }
- case 'x11': { // S->C
- /*
- string originator address (e.g., "192.168.7.38")
- uint32 originator port
- */
- const srcIP = bufferParser.readString(true);
- const srcPort = bufferParser.readUInt32BE();
- if (srcPort !== undefined) {
- channelInfo = {
- type,
- sender,
- window,
- packetSize,
- data: { srcIP, srcPort }
- };
- }
- break;
- }
- default:
- // Includes:
- // 'session' (C->S)
- // 'auth-agent@openssh.com' (S->C)
- channelInfo = {
- type,
- sender,
- window,
- packetSize,
- data: {}
- };
- }
- bufferParser.clear();
- if (channelInfo === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_OPEN packet'
- );
- }
- self._debug && self._debug(`Inbound: CHANNEL_OPEN (s:${sender}, ${type})`);
- const handler = self._handlers.CHANNEL_OPEN;
- if (handler) {
- handler(self, channelInfo);
- } else {
- self.channelOpenFail(
- channelInfo.sender,
- CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED,
- '',
- ''
- );
- }
- },
- [MESSAGE.CHANNEL_OPEN_CONFIRMATION]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
- uint32 recipient channel
- uint32 sender channel
- uint32 initial window size
- uint32 maximum packet size
- .... channel type specific data follows
- */
- // "The 'recipient channel' is the channel number given in the
- // original open request, and 'sender channel' is the channel number
- // allocated by the other side."
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- const sender = bufferParser.readUInt32BE();
- const window = bufferParser.readUInt32BE();
- const packetSize = bufferParser.readUInt32BE();
- const data = (bufferParser.avail() ? bufferParser.readRaw() : undefined);
- bufferParser.clear();
- if (packetSize === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_OPEN_CONFIRMATION packet'
- );
- }
- self._debug && self._debug(
- `Inbound: CHANNEL_OPEN_CONFIRMATION (r:${recipient}, s:${sender})`
- );
- const handler = self._handlers.CHANNEL_OPEN_CONFIRMATION;
- if (handler)
- handler(self, { recipient, sender, window, packetSize, data });
- },
- [MESSAGE.CHANNEL_OPEN_FAILURE]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_OPEN_FAILURE
- uint32 recipient channel
- uint32 reason code
- string description in ISO-10646 UTF-8 encoding [RFC3629]
- string language tag [RFC3066]
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- const reason = bufferParser.readUInt32BE();
- const description = bufferParser.readString(true);
- const lang = bufferParser.readString();
- bufferParser.clear();
- if (lang === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_OPEN_FAILURE packet'
- );
- }
- self._debug
- && self._debug(`Inbound: CHANNEL_OPEN_FAILURE (r:${recipient})`);
- const handler = self._handlers.CHANNEL_OPEN_FAILURE;
- handler && handler(self, recipient, reason, description);
- },
- [MESSAGE.CHANNEL_WINDOW_ADJUST]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_WINDOW_ADJUST
- uint32 recipient channel
- uint32 bytes to add
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- const bytesToAdd = bufferParser.readUInt32BE();
- bufferParser.clear();
- if (bytesToAdd === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_WINDOW_ADJUST packet'
- );
- }
- self._debug && self._debug(
- `Inbound: CHANNEL_WINDOW_ADJUST (r:${recipient}, ${bytesToAdd})`
- );
- const handler = self._handlers.CHANNEL_WINDOW_ADJUST;
- handler && handler(self, recipient, bytesToAdd);
- },
- [MESSAGE.CHANNEL_DATA]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_DATA
- uint32 recipient channel
- string data
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- const data = bufferParser.readString();
- bufferParser.clear();
- if (data === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_DATA packet'
- );
- }
- self._debug
- && self._debug(`Inbound: CHANNEL_DATA (r:${recipient}, ${data.length})`);
- const handler = self._handlers.CHANNEL_DATA;
- handler && handler(self, recipient, data);
- },
- [MESSAGE.CHANNEL_EXTENDED_DATA]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_EXTENDED_DATA
- uint32 recipient channel
- uint32 data_type_code
- string data
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- const type = bufferParser.readUInt32BE();
- const data = bufferParser.readString();
- bufferParser.clear();
- if (data === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_EXTENDED_DATA packet'
- );
- }
- self._debug && self._debug(
- `Inbound: CHANNEL_EXTENDED_DATA (r:${recipient}, ${data.length})`
- );
- const handler = self._handlers.CHANNEL_EXTENDED_DATA;
- handler && handler(self, recipient, data, type);
- },
- [MESSAGE.CHANNEL_EOF]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_EOF
- uint32 recipient channel
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- bufferParser.clear();
- if (recipient === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_EOF packet'
- );
- }
- self._debug && self._debug(`Inbound: CHANNEL_EOF (r:${recipient})`);
- const handler = self._handlers.CHANNEL_EOF;
- handler && handler(self, recipient);
- },
- [MESSAGE.CHANNEL_CLOSE]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_CLOSE
- uint32 recipient channel
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- bufferParser.clear();
- if (recipient === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_CLOSE packet'
- );
- }
- self._debug && self._debug(`Inbound: CHANNEL_CLOSE (r:${recipient})`);
- const handler = self._handlers.CHANNEL_CLOSE;
- handler && handler(self, recipient);
- },
- [MESSAGE.CHANNEL_REQUEST]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_REQUEST
- uint32 recipient channel
- string request type in US-ASCII characters only
- boolean want reply
- .... type-specific data follows
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- const type = bufferParser.readString(true);
- const wantReply = bufferParser.readBool();
- let data;
- if (wantReply !== undefined) {
- switch (type) {
- case 'exit-status': // S->C
- /*
- uint32 exit_status
- */
- data = bufferParser.readUInt32BE();
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
- );
- break;
- case 'exit-signal': { // S->C
- /*
- string signal name (without the "SIG" prefix)
- boolean core dumped
- string error message in ISO-10646 UTF-8 encoding
- string language tag
- */
- let signal;
- let coreDumped;
- if (self._compatFlags & COMPAT.OLD_EXIT) {
- /*
- Instead of `signal name` and `core dumped`, we have just:
- uint32 signal number
- */
- const num = bufferParser.readUInt32BE();
- switch (num) {
- case 1:
- signal = 'HUP';
- break;
- case 2:
- signal = 'INT';
- break;
- case 3:
- signal = 'QUIT';
- break;
- case 6:
- signal = 'ABRT';
- break;
- case 9:
- signal = 'KILL';
- break;
- case 14:
- signal = 'ALRM';
- break;
- case 15:
- signal = 'TERM';
- break;
- default:
- if (num !== undefined) {
- // Unknown or OS-specific
- signal = `UNKNOWN (${num})`;
- }
- }
- coreDumped = false;
- } else {
- signal = bufferParser.readString(true);
- coreDumped = bufferParser.readBool();
- if (coreDumped === undefined)
- signal = undefined;
- }
- const errorMessage = bufferParser.readString(true);
- if (bufferParser.skipString() !== undefined)
- data = { signal, coreDumped, errorMessage };
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${signal})`
- );
- break;
- }
- case 'pty-req': { // C->S
- /*
- string TERM environment variable value (e.g., vt100)
- uint32 terminal width, characters (e.g., 80)
- uint32 terminal height, rows (e.g., 24)
- uint32 terminal width, pixels (e.g., 640)
- uint32 terminal height, pixels (e.g., 480)
- string encoded terminal modes
- */
- const term = bufferParser.readString(true);
- const cols = bufferParser.readUInt32BE();
- const rows = bufferParser.readUInt32BE();
- const width = bufferParser.readUInt32BE();
- const height = bufferParser.readUInt32BE();
- const modesBinary = bufferParser.readString();
- if (modesBinary !== undefined) {
- bufferParser.init(modesBinary, 1);
- let modes = {};
- while (bufferParser.avail()) {
- const opcode = bufferParser.readByte();
- if (opcode === TERMINAL_MODE.TTY_OP_END)
- break;
- const name = TERMINAL_MODE_BY_VALUE[opcode];
- const value = bufferParser.readUInt32BE();
- if (opcode === undefined
- || name === undefined
- || value === undefined) {
- modes = undefined;
- break;
- }
- modes[name] = value;
- }
- if (modes !== undefined)
- data = { term, cols, rows, width, height, modes };
- }
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
- );
- break;
- }
- case 'window-change': { // C->S
- /*
- uint32 terminal width, columns
- uint32 terminal height, rows
- uint32 terminal width, pixels
- uint32 terminal height, pixels
- */
- const cols = bufferParser.readUInt32BE();
- const rows = bufferParser.readUInt32BE();
- const width = bufferParser.readUInt32BE();
- const height = bufferParser.readUInt32BE();
- if (height !== undefined)
- data = { cols, rows, width, height };
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
- );
- break;
- }
- case 'x11-req': { // C->S
- /*
- boolean single connection
- string x11 authentication protocol
- string x11 authentication cookie
- uint32 x11 screen number
- */
- const single = bufferParser.readBool();
- const protocol = bufferParser.readString(true);
- const cookie = bufferParser.readString();
- const screen = bufferParser.readUInt32BE();
- if (screen !== undefined)
- data = { single, protocol, cookie, screen };
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
- );
- break;
- }
- case 'env': { // C->S
- /*
- string variable name
- string variable value
- */
- const name = bufferParser.readString(true);
- const value = bufferParser.readString(true);
- if (value !== undefined)
- data = { name, value };
- if (self._debug) {
- self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: `
- + `${name}=${value})`
- );
- }
- break;
- }
- case 'shell': // C->S
- data = null; // No extra data
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
- );
- break;
- case 'exec': // C->S
- /*
- string command
- */
- data = bufferParser.readString(true);
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
- );
- break;
- case 'subsystem': // C->S
- /*
- string subsystem name
- */
- data = bufferParser.readString(true);
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
- );
- break;
- case 'signal': // C->S
- /*
- string signal name (without the "SIG" prefix)
- */
- data = bufferParser.readString(true);
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
- );
- break;
- case 'xon-xoff': // C->S
- /*
- boolean client can do
- */
- data = bufferParser.readBool();
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
- );
- break;
- case 'auth-agent-req@openssh.com': // C-S
- data = null; // No extra data
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
- );
- break;
- default:
- data = (bufferParser.avail() ? bufferParser.readRaw() : null);
- self._debug && self._debug(
- `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
- );
- }
- }
- bufferParser.clear();
- if (data === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_REQUEST packet'
- );
- }
- const handler = self._handlers.CHANNEL_REQUEST;
- handler && handler(self, recipient, type, wantReply, data);
- },
- [MESSAGE.CHANNEL_SUCCESS]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_SUCCESS
- uint32 recipient channel
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- bufferParser.clear();
- if (recipient === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_SUCCESS packet'
- );
- }
- self._debug && self._debug(`Inbound: CHANNEL_SUCCESS (r:${recipient})`);
- const handler = self._handlers.CHANNEL_SUCCESS;
- handler && handler(self, recipient);
- },
- [MESSAGE.CHANNEL_FAILURE]: (self, payload) => {
- /*
- byte SSH_MSG_CHANNEL_FAILURE
- uint32 recipient channel
- */
- bufferParser.init(payload, 1);
- const recipient = bufferParser.readUInt32BE();
- bufferParser.clear();
- if (recipient === undefined) {
- return doFatalError(
- self,
- 'Inbound: Malformed CHANNEL_FAILURE packet'
- );
- }
- self._debug && self._debug(`Inbound: CHANNEL_FAILURE (r:${recipient})`);
- const handler = self._handlers.CHANNEL_FAILURE;
- handler && handler(self, recipient);
- },
- };
|