@electerm/ssh2 0.8.11 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1214 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ bufferSlice,
5
+ bufferParser,
6
+ doFatalError,
7
+ sigSSHToASN1,
8
+ writeUInt32BE,
9
+ } = require('./utils.js');
10
+
11
+ const {
12
+ CHANNEL_OPEN_FAILURE,
13
+ COMPAT,
14
+ MESSAGE,
15
+ TERMINAL_MODE,
16
+ } = require('./constants.js');
17
+
18
+ const {
19
+ parseKey,
20
+ } = require('./keyParser.js');
21
+
22
+ const TERMINAL_MODE_BY_VALUE =
23
+ Array.from(Object.entries(TERMINAL_MODE))
24
+ .reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {});
25
+
26
+ module.exports = {
27
+ // Transport layer protocol ==================================================
28
+ [MESSAGE.DISCONNECT]: (self, payload) => {
29
+ /*
30
+ byte SSH_MSG_DISCONNECT
31
+ uint32 reason code
32
+ string description in ISO-10646 UTF-8 encoding
33
+ string language tag
34
+ */
35
+ bufferParser.init(payload, 1);
36
+ const reason = bufferParser.readUInt32BE();
37
+ const desc = bufferParser.readString(true);
38
+ const lang = bufferParser.readString();
39
+ bufferParser.clear();
40
+
41
+ if (lang === undefined) {
42
+ return doFatalError(
43
+ self,
44
+ 'Inbound: Malformed DISCONNECT packet'
45
+ );
46
+ }
47
+
48
+ self._debug && self._debug(
49
+ `Inbound: Received DISCONNECT (${reason}, "${desc}")`
50
+ );
51
+
52
+ const handler = self._handlers.DISCONNECT;
53
+ handler && handler(self, reason, desc);
54
+ },
55
+ [MESSAGE.IGNORE]: (self, payload) => {
56
+ /*
57
+ byte SSH_MSG_IGNORE
58
+ string data
59
+ */
60
+ self._debug && self._debug('Inbound: Received IGNORE');
61
+ },
62
+ [MESSAGE.UNIMPLEMENTED]: (self, payload) => {
63
+ /*
64
+ byte SSH_MSG_UNIMPLEMENTED
65
+ uint32 packet sequence number of rejected message
66
+ */
67
+ bufferParser.init(payload, 1);
68
+ const seqno = bufferParser.readUInt32BE();
69
+ bufferParser.clear();
70
+
71
+ if (seqno === undefined) {
72
+ return doFatalError(
73
+ self,
74
+ 'Inbound: Malformed UNIMPLEMENTED packet'
75
+ );
76
+ }
77
+
78
+ self._debug
79
+ && self._debug(`Inbound: Received UNIMPLEMENTED (seqno ${seqno})`);
80
+ },
81
+ [MESSAGE.DEBUG]: (self, payload) => {
82
+ /*
83
+ byte SSH_MSG_DEBUG
84
+ boolean always_display
85
+ string message in ISO-10646 UTF-8 encoding [RFC3629]
86
+ string language tag [RFC3066]
87
+ */
88
+ bufferParser.init(payload, 1);
89
+ const display = bufferParser.readBool();
90
+ const msg = bufferParser.readString(true);
91
+ const lang = bufferParser.readString();
92
+ bufferParser.clear();
93
+
94
+ if (lang === undefined) {
95
+ return doFatalError(
96
+ self,
97
+ 'Inbound: Malformed DEBUG packet'
98
+ );
99
+ }
100
+
101
+ self._debug && self._debug('Inbound: Received DEBUG');
102
+
103
+ const handler = self._handlers.DEBUG;
104
+ handler && handler(self, display, msg);
105
+ },
106
+ [MESSAGE.SERVICE_REQUEST]: (self, payload) => {
107
+ /*
108
+ byte SSH_MSG_SERVICE_REQUEST
109
+ string service name
110
+ */
111
+ bufferParser.init(payload, 1);
112
+ const name = bufferParser.readString(true);
113
+ bufferParser.clear();
114
+
115
+ if (name === undefined) {
116
+ return doFatalError(
117
+ self,
118
+ 'Inbound: Malformed SERVICE_REQUEST packet'
119
+ );
120
+ }
121
+
122
+ self._debug && self._debug(`Inbound: Received SERVICE_REQUEST (${name})`);
123
+
124
+ const handler = self._handlers.SERVICE_REQUEST;
125
+ handler && handler(self, name);
126
+ },
127
+ [MESSAGE.SERVICE_ACCEPT]: (self, payload) => {
128
+ // S->C
129
+ /*
130
+ byte SSH_MSG_SERVICE_ACCEPT
131
+ string service name
132
+ */
133
+ bufferParser.init(payload, 1);
134
+ const name = bufferParser.readString(true);
135
+ bufferParser.clear();
136
+
137
+ if (name === undefined) {
138
+ return doFatalError(
139
+ self,
140
+ 'Inbound: Malformed SERVICE_ACCEPT packet'
141
+ );
142
+ }
143
+
144
+ self._debug && self._debug(`Inbound: Received SERVICE_ACCEPT (${name})`);
145
+
146
+ const handler = self._handlers.SERVICE_ACCEPT;
147
+ handler && handler(self, name);
148
+ },
149
+
150
+ // User auth protocol -- generic =============================================
151
+ [MESSAGE.USERAUTH_REQUEST]: (self, payload) => {
152
+ /*
153
+ byte SSH_MSG_USERAUTH_REQUEST
154
+ string user name in ISO-10646 UTF-8 encoding [RFC3629]
155
+ string service name in US-ASCII
156
+ string method name in US-ASCII
157
+ .... method specific fields
158
+ */
159
+ bufferParser.init(payload, 1);
160
+ const user = bufferParser.readString(true);
161
+ const service = bufferParser.readString(true);
162
+ const method = bufferParser.readString(true);
163
+ let methodData;
164
+ let methodDesc;
165
+ switch (method) {
166
+ case 'none':
167
+ methodData = null;
168
+ break;
169
+ case 'password': {
170
+ /*
171
+ boolean <new password follows (old) plaintext password?>
172
+ string plaintext password in ISO-10646 UTF-8 encoding [RFC3629]
173
+ [string new password]
174
+ */
175
+ const isChange = bufferParser.readBool();
176
+ if (isChange !== undefined) {
177
+ methodData = bufferParser.readString(true);
178
+ if (methodData !== undefined && isChange) {
179
+ const newPassword = bufferParser.readString(true);
180
+ if (newPassword !== undefined)
181
+ methodData = { oldPassword: methodData, newPassword };
182
+ else
183
+ methodData = undefined;
184
+ }
185
+ }
186
+ break;
187
+ }
188
+ case 'publickey': {
189
+ /*
190
+ boolean <signature follows public key blob?>
191
+ string public key algorithm name
192
+ string public key blob
193
+ [string signature]
194
+ */
195
+ const hasSig = bufferParser.readBool();
196
+ if (hasSig !== undefined) {
197
+ const keyAlgo = bufferParser.readString(true);
198
+ const key = bufferParser.readString();
199
+ if (hasSig) {
200
+ const blobEnd = bufferParser.pos();
201
+ let signature = bufferParser.readString();
202
+ if (signature !== undefined) {
203
+ if (signature.length > (4 + keyAlgo.length + 4)
204
+ && signature.utf8Slice(4, 4 + keyAlgo.length) === keyAlgo) {
205
+ // Skip algoLen + algo + sigLen
206
+ signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
207
+ }
208
+
209
+ signature = sigSSHToASN1(signature, keyAlgo);
210
+ if (signature) {
211
+ const sessionID = self._kex.sessionID;
212
+ const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
213
+ writeUInt32BE(blob, sessionID.length, 0);
214
+ blob.set(sessionID, 4);
215
+ blob.set(
216
+ new Uint8Array(payload.buffer, payload.byteOffset, blobEnd),
217
+ 4 + sessionID.length
218
+ );
219
+ methodData = {
220
+ keyAlgo,
221
+ key,
222
+ signature,
223
+ blob,
224
+ };
225
+ }
226
+ }
227
+ } else {
228
+ methodData = { keyAlgo, key };
229
+ methodDesc = 'publickey -- check';
230
+ }
231
+ }
232
+ break;
233
+ }
234
+ case 'hostbased': {
235
+ /*
236
+ string public key algorithm for host key
237
+ string public host key and certificates for client host
238
+ string client host name expressed as the FQDN in US-ASCII
239
+ string user name on the client host in ISO-10646 UTF-8 encoding
240
+ [RFC3629]
241
+ string signature
242
+ */
243
+ const keyAlgo = bufferParser.readString(true);
244
+ const key = bufferParser.readString();
245
+ const localHostname = bufferParser.readString(true);
246
+ const localUsername = bufferParser.readString(true);
247
+
248
+ const blobEnd = bufferParser.pos();
249
+ let signature = bufferParser.readString();
250
+ if (signature !== undefined) {
251
+ if (signature.length > (4 + keyAlgo.length + 4)
252
+ && signature.utf8Slice(4, 4 + keyAlgo.length) === keyAlgo) {
253
+ // Skip algoLen + algo + sigLen
254
+ signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
255
+ }
256
+
257
+ signature = sigSSHToASN1(signature, keyAlgo);
258
+ if (signature !== undefined) {
259
+ const sessionID = self._kex.sessionID;
260
+ const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
261
+ writeUInt32BE(blob, sessionID.length, 0);
262
+ blob.set(sessionID, 4);
263
+ blob.set(
264
+ new Uint8Array(payload.buffer, payload.byteOffset, blobEnd),
265
+ 4 + sessionID.length
266
+ );
267
+ methodData = {
268
+ keyAlgo,
269
+ key,
270
+ signature,
271
+ blob,
272
+ localHostname,
273
+ localUsername,
274
+ };
275
+ }
276
+ }
277
+ break;
278
+ }
279
+ case 'keyboard-interactive':
280
+ /*
281
+ string language tag (as defined in [RFC-3066])
282
+ string submethods (ISO-10646 UTF-8)
283
+ */
284
+ // Skip/ignore language field -- it's deprecated in RFC 4256
285
+ bufferParser.skipString();
286
+
287
+ methodData = bufferParser.readList();
288
+ break;
289
+ default:
290
+ if (method !== undefined)
291
+ methodData = bufferParser.readRaw();
292
+ }
293
+ bufferParser.clear();
294
+
295
+ if (methodData === undefined) {
296
+ return doFatalError(
297
+ self,
298
+ 'Inbound: Malformed USERAUTH_REQUEST packet'
299
+ );
300
+ }
301
+
302
+ if (methodDesc === undefined)
303
+ methodDesc = method;
304
+
305
+ self._authsQueue.push(method);
306
+
307
+ self._debug
308
+ && self._debug(`Inbound: Received USERAUTH_REQUEST (${methodDesc})`);
309
+
310
+ const handler = self._handlers.USERAUTH_REQUEST;
311
+ handler && handler(self, user, service, method, methodData);
312
+ },
313
+ [MESSAGE.USERAUTH_FAILURE]: (self, payload) => {
314
+ // S->C
315
+ /*
316
+ byte SSH_MSG_USERAUTH_FAILURE
317
+ name-list authentications that can continue
318
+ boolean partial success
319
+ */
320
+ bufferParser.init(payload, 1);
321
+ const authMethods = bufferParser.readList();
322
+ const partialSuccess = bufferParser.readBool();
323
+ bufferParser.clear();
324
+
325
+ if (partialSuccess === undefined) {
326
+ return doFatalError(
327
+ self,
328
+ 'Inbound: Malformed USERAUTH_FAILURE packet'
329
+ );
330
+ }
331
+
332
+ self._debug
333
+ && self._debug(`Inbound: Received USERAUTH_FAILURE (${authMethods})`);
334
+
335
+ self._authsQueue.shift();
336
+ const handler = self._handlers.USERAUTH_FAILURE;
337
+ handler && handler(self, authMethods, partialSuccess);
338
+ },
339
+ [MESSAGE.USERAUTH_SUCCESS]: (self, payload) => {
340
+ // S->C
341
+ /*
342
+ byte SSH_MSG_USERAUTH_SUCCESS
343
+ */
344
+ self._debug && self._debug('Inbound: Received USERAUTH_SUCCESS');
345
+
346
+ self._authsQueue.shift();
347
+ const handler = self._handlers.USERAUTH_SUCCESS;
348
+ handler && handler(self);
349
+ },
350
+ [MESSAGE.USERAUTH_BANNER]: (self, payload) => {
351
+ // S->C
352
+ /*
353
+ byte SSH_MSG_USERAUTH_BANNER
354
+ string message in ISO-10646 UTF-8 encoding [RFC3629]
355
+ string language tag [RFC3066]
356
+ */
357
+ bufferParser.init(payload, 1);
358
+ const msg = bufferParser.readString(true);
359
+ const lang = bufferParser.readString();
360
+ bufferParser.clear();
361
+
362
+ if (lang === undefined) {
363
+ return doFatalError(
364
+ self,
365
+ 'Inbound: Malformed USERAUTH_BANNER packet'
366
+ );
367
+ }
368
+
369
+ self._debug && self._debug('Inbound: Received USERAUTH_BANNER');
370
+
371
+ const handler = self._handlers.USERAUTH_BANNER;
372
+ handler && handler(self, msg);
373
+ },
374
+
375
+ // User auth protocol -- method-specific =====================================
376
+ 60: (self, payload) => {
377
+ if (!self._authsQueue.length) {
378
+ self._debug
379
+ && self._debug('Inbound: Received payload type 60 without auth');
380
+ return;
381
+ }
382
+
383
+ switch (self._authsQueue[0]) {
384
+ case 'password': {
385
+ // S->C
386
+ /*
387
+ byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
388
+ string prompt in ISO-10646 UTF-8 encoding [RFC3629]
389
+ string language tag [RFC3066]
390
+ */
391
+ bufferParser.init(payload, 1);
392
+ const prompt = bufferParser.readString(true);
393
+ const lang = bufferParser.readString();
394
+ bufferParser.clear();
395
+
396
+ if (lang === undefined) {
397
+ return doFatalError(
398
+ self,
399
+ 'Inbound: Malformed USERAUTH_PASSWD_CHANGEREQ packet'
400
+ );
401
+ }
402
+
403
+ self._debug
404
+ && self._debug('Inbound: Received USERAUTH_PASSWD_CHANGEREQ');
405
+
406
+ const handler = self._handlers.USERAUTH_PASSWD_CHANGEREQ;
407
+ handler && handler(self, prompt);
408
+ break;
409
+ }
410
+ case 'publickey': {
411
+ // S->C
412
+ /*
413
+ byte SSH_MSG_USERAUTH_PK_OK
414
+ string public key algorithm name from the request
415
+ string public key blob from the request
416
+ */
417
+ bufferParser.init(payload, 1);
418
+ const keyAlgo = bufferParser.readString(true);
419
+ const key = bufferParser.readString();
420
+ bufferParser.clear();
421
+
422
+ if (key === undefined) {
423
+ return doFatalError(
424
+ self,
425
+ 'Inbound: Malformed USERAUTH_PK_OK packet'
426
+ );
427
+ }
428
+
429
+ self._debug && self._debug('Inbound: Received USERAUTH_PK_OK');
430
+
431
+ self._authsQueue.shift();
432
+ const handler = self._handlers.USERAUTH_PK_OK;
433
+ handler && handler(self, keyAlgo, key);
434
+ break;
435
+ }
436
+ case 'keyboard-interactive': {
437
+ // S->C
438
+ /*
439
+ byte SSH_MSG_USERAUTH_INFO_REQUEST
440
+ string name (ISO-10646 UTF-8)
441
+ string instruction (ISO-10646 UTF-8)
442
+ string language tag (as defined in [RFC-3066])
443
+ int num-prompts
444
+ string prompt[1] (ISO-10646 UTF-8)
445
+ boolean echo[1]
446
+ ...
447
+ string prompt[num-prompts] (ISO-10646 UTF-8)
448
+ boolean echo[num-prompts]
449
+ */
450
+ bufferParser.init(payload, 1);
451
+ const name = bufferParser.readString(true);
452
+ const instructions = bufferParser.readString(true);
453
+ bufferParser.readString(); // skip lang
454
+ const numPrompts = bufferParser.readUInt32BE();
455
+ let prompts;
456
+ if (numPrompts !== undefined) {
457
+ prompts = new Array(numPrompts);
458
+ let i;
459
+ for (i = 0; i < numPrompts; ++i) {
460
+ const prompt = bufferParser.readString(true);
461
+ const echo = bufferParser.readBool();
462
+ if (echo === undefined)
463
+ break;
464
+ prompts[i] = { prompt, echo };
465
+ }
466
+ if (i !== numPrompts)
467
+ prompts = undefined;
468
+ }
469
+ bufferParser.clear();
470
+
471
+ if (prompts === undefined) {
472
+ return doFatalError(
473
+ self,
474
+ 'Inbound: Malformed USERAUTH_INFO_REQUEST packet'
475
+ );
476
+ }
477
+
478
+ self._debug && self._debug('Inbound: Received USERAUTH_INFO_REQUEST');
479
+
480
+ const handler = self._handlers.USERAUTH_INFO_REQUEST;
481
+ handler && handler(self, name, instructions, prompts);
482
+ break;
483
+ }
484
+ default:
485
+ self._debug
486
+ && self._debug('Inbound: Received unexpected payload type 60');
487
+ }
488
+ },
489
+ 61: (self, payload) => {
490
+ if (!self._authsQueue.length) {
491
+ self._debug
492
+ && self._debug('Inbound: Received payload type 61 without auth');
493
+ return;
494
+ }
495
+ /*
496
+ byte SSH_MSG_USERAUTH_INFO_RESPONSE
497
+ int num-responses
498
+ string response[1] (ISO-10646 UTF-8)
499
+ ...
500
+ string response[num-responses] (ISO-10646 UTF-8)
501
+ */
502
+ if (self._authsQueue[0] !== 'keyboard-interactive') {
503
+ return doFatalError(
504
+ self,
505
+ 'Inbound: Received unexpected payload type 61'
506
+ );
507
+ }
508
+ bufferParser.init(payload, 1);
509
+ const numResponses = bufferParser.readUInt32BE();
510
+ let responses;
511
+ if (numResponses !== undefined) {
512
+ responses = new Array(numResponses);
513
+ let i;
514
+ for (i = 0; i < numResponses; ++i) {
515
+ const response = bufferParser.readString(true);
516
+ if (response === undefined)
517
+ break;
518
+ responses[i] = response;
519
+ }
520
+ if (i !== numResponses)
521
+ responses = undefined;
522
+ }
523
+ bufferParser.clear();
524
+
525
+ if (responses === undefined) {
526
+ return doFatalError(
527
+ self,
528
+ 'Inbound: Malformed USERAUTH_INFO_RESPONSE packet'
529
+ );
530
+ }
531
+
532
+ self._debug && self._debug('Inbound: Received USERAUTH_INFO_RESPONSE');
533
+
534
+ const handler = self._handlers.USERAUTH_INFO_RESPONSE;
535
+ handler && handler(self, responses);
536
+ },
537
+
538
+ // Connection protocol -- generic ============================================
539
+ [MESSAGE.GLOBAL_REQUEST]: (self, payload) => {
540
+ /*
541
+ byte SSH_MSG_GLOBAL_REQUEST
542
+ string request name in US-ASCII only
543
+ boolean want reply
544
+ .... request-specific data follows
545
+ */
546
+ bufferParser.init(payload, 1);
547
+ const name = bufferParser.readString(true);
548
+ const wantReply = bufferParser.readBool();
549
+ let data;
550
+ if (wantReply !== undefined) {
551
+ switch (name) {
552
+ case 'tcpip-forward':
553
+ case 'cancel-tcpip-forward': {
554
+ /*
555
+ string address to bind (e.g., "0.0.0.0")
556
+ uint32 port number to bind
557
+ */
558
+ const bindAddr = bufferParser.readString(true);
559
+ const bindPort = bufferParser.readUInt32BE();
560
+ if (bindPort !== undefined)
561
+ data = { bindAddr, bindPort };
562
+ break;
563
+ }
564
+ case 'streamlocal-forward@openssh.com':
565
+ case 'cancel-streamlocal-forward@openssh.com': {
566
+ /*
567
+ string socket path
568
+ */
569
+ const socketPath = bufferParser.readString(true);
570
+ if (socketPath !== undefined)
571
+ data = { socketPath };
572
+ break;
573
+ }
574
+ case 'no-more-sessions@openssh.com':
575
+ data = null;
576
+ break;
577
+ case 'hostkeys-00@openssh.com': {
578
+ data = [];
579
+ while (bufferParser.avail() > 0) {
580
+ const keyRaw = bufferParser.readString();
581
+ if (keyRaw === undefined) {
582
+ data = undefined;
583
+ break;
584
+ }
585
+ const key = parseKey(keyRaw);
586
+ if (!(key instanceof Error))
587
+ data.push(key);
588
+ }
589
+ break;
590
+ }
591
+ default:
592
+ data = bufferParser.readRaw();
593
+ }
594
+ }
595
+ bufferParser.clear();
596
+
597
+ if (data === undefined) {
598
+ return doFatalError(
599
+ self,
600
+ 'Inbound: Malformed GLOBAL_REQUEST packet'
601
+ );
602
+ }
603
+
604
+ self._debug && self._debug(`Inbound: GLOBAL_REQUEST (${name})`);
605
+
606
+ const handler = self._handlers.GLOBAL_REQUEST;
607
+ if (handler)
608
+ handler(self, name, wantReply, data);
609
+ else
610
+ self.requestFailure(); // Auto reject
611
+ },
612
+ [MESSAGE.REQUEST_SUCCESS]: (self, payload) => {
613
+ /*
614
+ byte SSH_MSG_REQUEST_SUCCESS
615
+ .... response specific data
616
+ */
617
+ const data = (payload.length > 1 ? bufferSlice(payload, 1) : null);
618
+
619
+ self._debug && self._debug('Inbound: REQUEST_SUCCESS');
620
+
621
+ const handler = self._handlers.REQUEST_SUCCESS;
622
+ handler && handler(self, data);
623
+ },
624
+ [MESSAGE.REQUEST_FAILURE]: (self, payload) => {
625
+ /*
626
+ byte SSH_MSG_REQUEST_FAILURE
627
+ */
628
+ self._debug && self._debug('Inbound: Received REQUEST_FAILURE');
629
+
630
+ const handler = self._handlers.REQUEST_FAILURE;
631
+ handler && handler(self);
632
+ },
633
+
634
+ // Connection protocol -- channel-related ====================================
635
+ [MESSAGE.CHANNEL_OPEN]: (self, payload) => {
636
+ /*
637
+ byte SSH_MSG_CHANNEL_OPEN
638
+ string channel type in US-ASCII only
639
+ uint32 sender channel
640
+ uint32 initial window size
641
+ uint32 maximum packet size
642
+ .... channel type specific data follows
643
+ */
644
+ bufferParser.init(payload, 1);
645
+ const type = bufferParser.readString(true);
646
+ const sender = bufferParser.readUInt32BE();
647
+ const window = bufferParser.readUInt32BE();
648
+ const packetSize = bufferParser.readUInt32BE();
649
+ let channelInfo;
650
+
651
+ switch (type) {
652
+ case 'forwarded-tcpip': // S->C
653
+ case 'direct-tcpip': { // C->S
654
+ /*
655
+ string address that was connected / host to connect
656
+ uint32 port that was connected / port to connect
657
+ string originator IP address
658
+ uint32 originator port
659
+ */
660
+ const destIP = bufferParser.readString(true);
661
+ const destPort = bufferParser.readUInt32BE();
662
+ const srcIP = bufferParser.readString(true);
663
+ const srcPort = bufferParser.readUInt32BE();
664
+ if (srcPort !== undefined) {
665
+ channelInfo = {
666
+ type,
667
+ sender,
668
+ window,
669
+ packetSize,
670
+ data: { destIP, destPort, srcIP, srcPort }
671
+ };
672
+ }
673
+ break;
674
+ }
675
+ case 'forwarded-streamlocal@openssh.com': // S->C
676
+ case 'direct-streamlocal@openssh.com': { // C->S
677
+ /*
678
+ string socket path
679
+ string reserved for future use
680
+
681
+ (direct-streamlocal@openssh.com additionally has:)
682
+ uint32 reserved
683
+ */
684
+ const socketPath = bufferParser.readString(true);
685
+ if (socketPath !== undefined) {
686
+ channelInfo = {
687
+ type,
688
+ sender,
689
+ window,
690
+ packetSize,
691
+ data: { socketPath }
692
+ };
693
+ }
694
+ break;
695
+ }
696
+ case 'x11': { // S->C
697
+ /*
698
+ string originator address (e.g., "192.168.7.38")
699
+ uint32 originator port
700
+ */
701
+ const srcIP = bufferParser.readString(true);
702
+ const srcPort = bufferParser.readUInt32BE();
703
+ if (srcPort !== undefined) {
704
+ channelInfo = {
705
+ type,
706
+ sender,
707
+ window,
708
+ packetSize,
709
+ data: { srcIP, srcPort }
710
+ };
711
+ }
712
+ break;
713
+ }
714
+ default:
715
+ // Includes:
716
+ // 'session' (C->S)
717
+ // 'auth-agent@openssh.com' (S->C)
718
+ channelInfo = {
719
+ type,
720
+ sender,
721
+ window,
722
+ packetSize,
723
+ data: {}
724
+ };
725
+ }
726
+ bufferParser.clear();
727
+
728
+ if (channelInfo === undefined) {
729
+ return doFatalError(
730
+ self,
731
+ 'Inbound: Malformed CHANNEL_OPEN packet'
732
+ );
733
+ }
734
+
735
+ self._debug && self._debug(`Inbound: CHANNEL_OPEN (s:${sender}, ${type})`);
736
+
737
+ const handler = self._handlers.CHANNEL_OPEN;
738
+ if (handler) {
739
+ handler(self, channelInfo);
740
+ } else {
741
+ self.channelOpenFail(
742
+ channelInfo.sender,
743
+ CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED,
744
+ '',
745
+ ''
746
+ );
747
+ }
748
+ },
749
+ [MESSAGE.CHANNEL_OPEN_CONFIRMATION]: (self, payload) => {
750
+ /*
751
+ byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
752
+ uint32 recipient channel
753
+ uint32 sender channel
754
+ uint32 initial window size
755
+ uint32 maximum packet size
756
+ .... channel type specific data follows
757
+ */
758
+ // "The 'recipient channel' is the channel number given in the
759
+ // original open request, and 'sender channel' is the channel number
760
+ // allocated by the other side."
761
+ bufferParser.init(payload, 1);
762
+ const recipient = bufferParser.readUInt32BE();
763
+ const sender = bufferParser.readUInt32BE();
764
+ const window = bufferParser.readUInt32BE();
765
+ const packetSize = bufferParser.readUInt32BE();
766
+ const data = (bufferParser.avail() ? bufferParser.readRaw() : undefined);
767
+ bufferParser.clear();
768
+
769
+ if (packetSize === undefined) {
770
+ return doFatalError(
771
+ self,
772
+ 'Inbound: Malformed CHANNEL_OPEN_CONFIRMATION packet'
773
+ );
774
+ }
775
+
776
+ self._debug && self._debug(
777
+ `Inbound: CHANNEL_OPEN_CONFIRMATION (r:${recipient}, s:${sender})`
778
+ );
779
+
780
+ const handler = self._handlers.CHANNEL_OPEN_CONFIRMATION;
781
+ if (handler)
782
+ handler(self, { recipient, sender, window, packetSize, data });
783
+ },
784
+ [MESSAGE.CHANNEL_OPEN_FAILURE]: (self, payload) => {
785
+ /*
786
+ byte SSH_MSG_CHANNEL_OPEN_FAILURE
787
+ uint32 recipient channel
788
+ uint32 reason code
789
+ string description in ISO-10646 UTF-8 encoding [RFC3629]
790
+ string language tag [RFC3066]
791
+ */
792
+ bufferParser.init(payload, 1);
793
+ const recipient = bufferParser.readUInt32BE();
794
+ const reason = bufferParser.readUInt32BE();
795
+ const description = bufferParser.readString(true);
796
+ const lang = bufferParser.readString();
797
+ bufferParser.clear();
798
+
799
+ if (lang === undefined) {
800
+ return doFatalError(
801
+ self,
802
+ 'Inbound: Malformed CHANNEL_OPEN_FAILURE packet'
803
+ );
804
+ }
805
+
806
+ self._debug
807
+ && self._debug(`Inbound: CHANNEL_OPEN_FAILURE (r:${recipient})`);
808
+
809
+ const handler = self._handlers.CHANNEL_OPEN_FAILURE;
810
+ handler && handler(self, recipient, reason, description);
811
+ },
812
+ [MESSAGE.CHANNEL_WINDOW_ADJUST]: (self, payload) => {
813
+ /*
814
+ byte SSH_MSG_CHANNEL_WINDOW_ADJUST
815
+ uint32 recipient channel
816
+ uint32 bytes to add
817
+ */
818
+ bufferParser.init(payload, 1);
819
+ const recipient = bufferParser.readUInt32BE();
820
+ const bytesToAdd = bufferParser.readUInt32BE();
821
+ bufferParser.clear();
822
+
823
+ if (bytesToAdd === undefined) {
824
+ return doFatalError(
825
+ self,
826
+ 'Inbound: Malformed CHANNEL_WINDOW_ADJUST packet'
827
+ );
828
+ }
829
+
830
+ self._debug && self._debug(
831
+ `Inbound: CHANNEL_WINDOW_ADJUST (r:${recipient}, ${bytesToAdd})`
832
+ );
833
+
834
+ const handler = self._handlers.CHANNEL_WINDOW_ADJUST;
835
+ handler && handler(self, recipient, bytesToAdd);
836
+ },
837
+ [MESSAGE.CHANNEL_DATA]: (self, payload) => {
838
+ /*
839
+ byte SSH_MSG_CHANNEL_DATA
840
+ uint32 recipient channel
841
+ string data
842
+ */
843
+ bufferParser.init(payload, 1);
844
+ const recipient = bufferParser.readUInt32BE();
845
+ const data = bufferParser.readString();
846
+ bufferParser.clear();
847
+
848
+ if (data === undefined) {
849
+ return doFatalError(
850
+ self,
851
+ 'Inbound: Malformed CHANNEL_DATA packet'
852
+ );
853
+ }
854
+
855
+ self._debug
856
+ && self._debug(`Inbound: CHANNEL_DATA (r:${recipient}, ${data.length})`);
857
+
858
+ const handler = self._handlers.CHANNEL_DATA;
859
+ handler && handler(self, recipient, data);
860
+ },
861
+ [MESSAGE.CHANNEL_EXTENDED_DATA]: (self, payload) => {
862
+ /*
863
+ byte SSH_MSG_CHANNEL_EXTENDED_DATA
864
+ uint32 recipient channel
865
+ uint32 data_type_code
866
+ string data
867
+ */
868
+ bufferParser.init(payload, 1);
869
+ const recipient = bufferParser.readUInt32BE();
870
+ const type = bufferParser.readUInt32BE();
871
+ const data = bufferParser.readString();
872
+ bufferParser.clear();
873
+
874
+ if (data === undefined) {
875
+ return doFatalError(
876
+ self,
877
+ 'Inbound: Malformed CHANNEL_EXTENDED_DATA packet'
878
+ );
879
+ }
880
+
881
+ self._debug && self._debug(
882
+ `Inbound: CHANNEL_EXTENDED_DATA (r:${recipient}, ${data.length})`
883
+ );
884
+
885
+ const handler = self._handlers.CHANNEL_EXTENDED_DATA;
886
+ handler && handler(self, recipient, data, type);
887
+ },
888
+ [MESSAGE.CHANNEL_EOF]: (self, payload) => {
889
+ /*
890
+ byte SSH_MSG_CHANNEL_EOF
891
+ uint32 recipient channel
892
+ */
893
+ bufferParser.init(payload, 1);
894
+ const recipient = bufferParser.readUInt32BE();
895
+ bufferParser.clear();
896
+
897
+ if (recipient === undefined) {
898
+ return doFatalError(
899
+ self,
900
+ 'Inbound: Malformed CHANNEL_EOF packet'
901
+ );
902
+ }
903
+
904
+ self._debug && self._debug(`Inbound: CHANNEL_EOF (r:${recipient})`);
905
+
906
+ const handler = self._handlers.CHANNEL_EOF;
907
+ handler && handler(self, recipient);
908
+ },
909
+ [MESSAGE.CHANNEL_CLOSE]: (self, payload) => {
910
+ /*
911
+ byte SSH_MSG_CHANNEL_CLOSE
912
+ uint32 recipient channel
913
+ */
914
+ bufferParser.init(payload, 1);
915
+ const recipient = bufferParser.readUInt32BE();
916
+ bufferParser.clear();
917
+
918
+ if (recipient === undefined) {
919
+ return doFatalError(
920
+ self,
921
+ 'Inbound: Malformed CHANNEL_CLOSE packet'
922
+ );
923
+ }
924
+
925
+ self._debug && self._debug(`Inbound: CHANNEL_CLOSE (r:${recipient})`);
926
+
927
+ const handler = self._handlers.CHANNEL_CLOSE;
928
+ handler && handler(self, recipient);
929
+ },
930
+ [MESSAGE.CHANNEL_REQUEST]: (self, payload) => {
931
+ /*
932
+ byte SSH_MSG_CHANNEL_REQUEST
933
+ uint32 recipient channel
934
+ string request type in US-ASCII characters only
935
+ boolean want reply
936
+ .... type-specific data follows
937
+ */
938
+ bufferParser.init(payload, 1);
939
+ const recipient = bufferParser.readUInt32BE();
940
+ const type = bufferParser.readString(true);
941
+ const wantReply = bufferParser.readBool();
942
+ let data;
943
+ if (wantReply !== undefined) {
944
+ switch (type) {
945
+ case 'exit-status': // S->C
946
+ /*
947
+ uint32 exit_status
948
+ */
949
+ data = bufferParser.readUInt32BE();
950
+ self._debug && self._debug(
951
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
952
+ );
953
+ break;
954
+ case 'exit-signal': { // S->C
955
+ /*
956
+ string signal name (without the "SIG" prefix)
957
+ boolean core dumped
958
+ string error message in ISO-10646 UTF-8 encoding
959
+ string language tag
960
+ */
961
+ let signal;
962
+ let coreDumped;
963
+ if (self._compatFlags & COMPAT.OLD_EXIT) {
964
+ /*
965
+ Instead of `signal name` and `core dumped`, we have just:
966
+ uint32 signal number
967
+ */
968
+ const num = bufferParser.readUInt32BE();
969
+ switch (num) {
970
+ case 1:
971
+ signal = 'HUP';
972
+ break;
973
+ case 2:
974
+ signal = 'INT';
975
+ break;
976
+ case 3:
977
+ signal = 'QUIT';
978
+ break;
979
+ case 6:
980
+ signal = 'ABRT';
981
+ break;
982
+ case 9:
983
+ signal = 'KILL';
984
+ break;
985
+ case 14:
986
+ signal = 'ALRM';
987
+ break;
988
+ case 15:
989
+ signal = 'TERM';
990
+ break;
991
+ default:
992
+ if (num !== undefined) {
993
+ // Unknown or OS-specific
994
+ signal = `UNKNOWN (${num})`;
995
+ }
996
+ }
997
+ coreDumped = false;
998
+ } else {
999
+ signal = bufferParser.readString(true);
1000
+ coreDumped = bufferParser.readBool();
1001
+ if (coreDumped === undefined)
1002
+ signal = undefined;
1003
+ }
1004
+ const errorMessage = bufferParser.readString(true);
1005
+ if (bufferParser.skipString() !== undefined)
1006
+ data = { signal, coreDumped, errorMessage };
1007
+ self._debug && self._debug(
1008
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${signal})`
1009
+ );
1010
+ break;
1011
+ }
1012
+ case 'pty-req': { // C->S
1013
+ /*
1014
+ string TERM environment variable value (e.g., vt100)
1015
+ uint32 terminal width, characters (e.g., 80)
1016
+ uint32 terminal height, rows (e.g., 24)
1017
+ uint32 terminal width, pixels (e.g., 640)
1018
+ uint32 terminal height, pixels (e.g., 480)
1019
+ string encoded terminal modes
1020
+ */
1021
+ const term = bufferParser.readString(true);
1022
+ const cols = bufferParser.readUInt32BE();
1023
+ const rows = bufferParser.readUInt32BE();
1024
+ const width = bufferParser.readUInt32BE();
1025
+ const height = bufferParser.readUInt32BE();
1026
+ const modesBinary = bufferParser.readString();
1027
+ if (modesBinary !== undefined) {
1028
+ bufferParser.init(modesBinary, 1);
1029
+ let modes = {};
1030
+ while (bufferParser.avail()) {
1031
+ const opcode = bufferParser.readByte();
1032
+ if (opcode === TERMINAL_MODE.TTY_OP_END)
1033
+ break;
1034
+ const name = TERMINAL_MODE_BY_VALUE[opcode];
1035
+ const value = bufferParser.readUInt32BE();
1036
+ if (opcode === undefined
1037
+ || name === undefined
1038
+ || value === undefined) {
1039
+ modes = undefined;
1040
+ break;
1041
+ }
1042
+ modes[name] = value;
1043
+ }
1044
+ if (modes !== undefined)
1045
+ data = { term, cols, rows, width, height, modes };
1046
+ }
1047
+ self._debug && self._debug(
1048
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
1049
+ );
1050
+ break;
1051
+ }
1052
+ case 'window-change': { // C->S
1053
+ /*
1054
+ uint32 terminal width, columns
1055
+ uint32 terminal height, rows
1056
+ uint32 terminal width, pixels
1057
+ uint32 terminal height, pixels
1058
+ */
1059
+ const cols = bufferParser.readUInt32BE();
1060
+ const rows = bufferParser.readUInt32BE();
1061
+ const width = bufferParser.readUInt32BE();
1062
+ const height = bufferParser.readUInt32BE();
1063
+ if (height !== undefined)
1064
+ data = { cols, rows, width, height };
1065
+ self._debug && self._debug(
1066
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
1067
+ );
1068
+ break;
1069
+ }
1070
+ case 'x11-req': { // C->S
1071
+ /*
1072
+ boolean single connection
1073
+ string x11 authentication protocol
1074
+ string x11 authentication cookie
1075
+ uint32 x11 screen number
1076
+ */
1077
+ const single = bufferParser.readBool();
1078
+ const protocol = bufferParser.readString(true);
1079
+ const cookie = bufferParser.readString();
1080
+ const screen = bufferParser.readUInt32BE();
1081
+ if (screen !== undefined)
1082
+ data = { single, protocol, cookie, screen };
1083
+ self._debug && self._debug(
1084
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
1085
+ );
1086
+ break;
1087
+ }
1088
+ case 'env': { // C->S
1089
+ /*
1090
+ string variable name
1091
+ string variable value
1092
+ */
1093
+ const name = bufferParser.readString(true);
1094
+ const value = bufferParser.readString(true);
1095
+ if (value !== undefined)
1096
+ data = { name, value };
1097
+ if (self._debug) {
1098
+ self._debug(
1099
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: `
1100
+ + `${name}=${value})`
1101
+ );
1102
+ }
1103
+ break;
1104
+ }
1105
+ case 'shell': // C->S
1106
+ data = null; // No extra data
1107
+ self._debug && self._debug(
1108
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
1109
+ );
1110
+ break;
1111
+ case 'exec': // C->S
1112
+ /*
1113
+ string command
1114
+ */
1115
+ data = bufferParser.readString(true);
1116
+ self._debug && self._debug(
1117
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
1118
+ );
1119
+ break;
1120
+ case 'subsystem': // C->S
1121
+ /*
1122
+ string subsystem name
1123
+ */
1124
+ data = bufferParser.readString(true);
1125
+ self._debug && self._debug(
1126
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
1127
+ );
1128
+ break;
1129
+ case 'signal': // C->S
1130
+ /*
1131
+ string signal name (without the "SIG" prefix)
1132
+ */
1133
+ data = bufferParser.readString(true);
1134
+ self._debug && self._debug(
1135
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
1136
+ );
1137
+ break;
1138
+ case 'xon-xoff': // C->S
1139
+ /*
1140
+ boolean client can do
1141
+ */
1142
+ data = bufferParser.readBool();
1143
+ self._debug && self._debug(
1144
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type}: ${data})`
1145
+ );
1146
+ break;
1147
+ case 'auth-agent-req@openssh.com': // C-S
1148
+ data = null; // No extra data
1149
+ self._debug && self._debug(
1150
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
1151
+ );
1152
+ break;
1153
+ default:
1154
+ data = (bufferParser.avail() ? bufferParser.readRaw() : null);
1155
+ self._debug && self._debug(
1156
+ `Inbound: CHANNEL_REQUEST (r:${recipient}, ${type})`
1157
+ );
1158
+ }
1159
+ }
1160
+ bufferParser.clear();
1161
+
1162
+ if (data === undefined) {
1163
+ return doFatalError(
1164
+ self,
1165
+ 'Inbound: Malformed CHANNEL_REQUEST packet'
1166
+ );
1167
+ }
1168
+
1169
+ const handler = self._handlers.CHANNEL_REQUEST;
1170
+ handler && handler(self, recipient, type, wantReply, data);
1171
+ },
1172
+ [MESSAGE.CHANNEL_SUCCESS]: (self, payload) => {
1173
+ /*
1174
+ byte SSH_MSG_CHANNEL_SUCCESS
1175
+ uint32 recipient channel
1176
+ */
1177
+ bufferParser.init(payload, 1);
1178
+ const recipient = bufferParser.readUInt32BE();
1179
+ bufferParser.clear();
1180
+
1181
+ if (recipient === undefined) {
1182
+ return doFatalError(
1183
+ self,
1184
+ 'Inbound: Malformed CHANNEL_SUCCESS packet'
1185
+ );
1186
+ }
1187
+
1188
+ self._debug && self._debug(`Inbound: CHANNEL_SUCCESS (r:${recipient})`);
1189
+
1190
+ const handler = self._handlers.CHANNEL_SUCCESS;
1191
+ handler && handler(self, recipient);
1192
+ },
1193
+ [MESSAGE.CHANNEL_FAILURE]: (self, payload) => {
1194
+ /*
1195
+ byte SSH_MSG_CHANNEL_FAILURE
1196
+ uint32 recipient channel
1197
+ */
1198
+ bufferParser.init(payload, 1);
1199
+ const recipient = bufferParser.readUInt32BE();
1200
+ bufferParser.clear();
1201
+
1202
+ if (recipient === undefined) {
1203
+ return doFatalError(
1204
+ self,
1205
+ 'Inbound: Malformed CHANNEL_FAILURE packet'
1206
+ );
1207
+ }
1208
+
1209
+ self._debug && self._debug(`Inbound: CHANNEL_FAILURE (r:${recipient})`);
1210
+
1211
+ const handler = self._handlers.CHANNEL_FAILURE;
1212
+ handler && handler(self, recipient);
1213
+ },
1214
+ };