bdy 1.23.6-dev → 1.23.7-dev

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.
Files changed (205) hide show
  1. package/distTs/package.json +10 -2
  2. package/distTs/src/agent/linux.js +53 -3
  3. package/distTs/src/agent/manager.js +3 -6
  4. package/distTs/src/agent/osx.js +51 -3
  5. package/distTs/src/agent/system.js +400 -21
  6. package/distTs/src/agent/windows.js +4 -7
  7. package/distTs/src/command/agent/debug.js +2 -2
  8. package/distTs/src/command/agent/install.js +37 -10
  9. package/distTs/src/command/agent/restart.js +2 -2
  10. package/distTs/src/command/agent/run.js +3 -0
  11. package/distTs/src/command/agent/start.js +2 -2
  12. package/distTs/src/command/agent/status.js +19 -0
  13. package/distTs/src/command/agent/stop.js +2 -2
  14. package/distTs/src/command/agent/uninstall.js +5 -8
  15. package/distTs/src/command/agent/update.js +2 -2
  16. package/distTs/src/command/artifact/publish.js +3 -1
  17. package/distTs/src/command/version.js +13 -5
  18. package/distTs/src/logger.js +40 -4
  19. package/distTs/src/texts.js +35 -19
  20. package/distTs/src/utils.js +98 -3
  21. package/node_modules/asn1/Jenkinsfile +65 -0
  22. package/node_modules/asn1/LICENSE +19 -0
  23. package/node_modules/asn1/README.md +50 -0
  24. package/node_modules/asn1/lib/ber/errors.js +13 -0
  25. package/node_modules/asn1/lib/ber/index.js +27 -0
  26. package/node_modules/asn1/lib/ber/reader.js +262 -0
  27. package/node_modules/asn1/lib/ber/types.js +36 -0
  28. package/node_modules/asn1/lib/ber/writer.js +317 -0
  29. package/node_modules/asn1/lib/index.js +20 -0
  30. package/node_modules/asn1/package.json +31 -0
  31. package/node_modules/bcrypt-pbkdf/CONTRIBUTING.md +13 -0
  32. package/node_modules/bcrypt-pbkdf/LICENSE +66 -0
  33. package/node_modules/bcrypt-pbkdf/README.md +45 -0
  34. package/node_modules/bcrypt-pbkdf/index.js +556 -0
  35. package/node_modules/bcrypt-pbkdf/package.json +15 -0
  36. package/node_modules/safer-buffer/LICENSE +21 -0
  37. package/node_modules/safer-buffer/Porting-Buffer.md +268 -0
  38. package/node_modules/safer-buffer/Readme.md +156 -0
  39. package/node_modules/safer-buffer/dangerous.js +58 -0
  40. package/node_modules/safer-buffer/package.json +34 -0
  41. package/node_modules/safer-buffer/safer.js +77 -0
  42. package/node_modules/safer-buffer/tests.js +406 -0
  43. package/node_modules/ssh2/.eslintignore +4 -0
  44. package/node_modules/ssh2/.eslintrc.js +5 -0
  45. package/node_modules/ssh2/.github/workflows/ci.yml +110 -0
  46. package/node_modules/ssh2/.github/workflows/lint.yml +27 -0
  47. package/node_modules/ssh2/LICENSE +19 -0
  48. package/node_modules/ssh2/README.md +1529 -0
  49. package/node_modules/ssh2/SFTP.md +413 -0
  50. package/node_modules/ssh2/examples/server-chat.js +238 -0
  51. package/node_modules/ssh2/examples/sftp-server-download-only.js +134 -0
  52. package/node_modules/ssh2/install.js +27 -0
  53. package/node_modules/ssh2/lib/Channel.js +295 -0
  54. package/node_modules/ssh2/lib/agent.js +1123 -0
  55. package/node_modules/ssh2/lib/client.js +2176 -0
  56. package/node_modules/ssh2/lib/http-agents.js +84 -0
  57. package/node_modules/ssh2/lib/index.js +44 -0
  58. package/node_modules/ssh2/lib/keygen.js +582 -0
  59. package/node_modules/ssh2/lib/protocol/Protocol.js +2136 -0
  60. package/node_modules/ssh2/lib/protocol/SFTP.js +4052 -0
  61. package/node_modules/ssh2/lib/protocol/constants.js +356 -0
  62. package/node_modules/ssh2/lib/protocol/crypto/binding.gyp +23 -0
  63. package/node_modules/ssh2/lib/protocol/crypto/poly1305.js +43 -0
  64. package/node_modules/ssh2/lib/protocol/crypto/src/binding.cc +2300 -0
  65. package/node_modules/ssh2/lib/protocol/crypto.js +1602 -0
  66. package/node_modules/ssh2/lib/protocol/handlers.js +16 -0
  67. package/node_modules/ssh2/lib/protocol/handlers.misc.js +1285 -0
  68. package/node_modules/ssh2/lib/protocol/kex.js +1908 -0
  69. package/node_modules/ssh2/lib/protocol/keyParser.js +1484 -0
  70. package/node_modules/ssh2/lib/protocol/node-fs-compat.js +115 -0
  71. package/node_modules/ssh2/lib/protocol/utils.js +356 -0
  72. package/node_modules/ssh2/lib/protocol/zlib.js +255 -0
  73. package/node_modules/ssh2/lib/server.js +1380 -0
  74. package/node_modules/ssh2/lib/utils.js +336 -0
  75. package/node_modules/ssh2/package.json +49 -0
  76. package/node_modules/ssh2/test/common.js +316 -0
  77. package/node_modules/ssh2/test/fixtures/bad_rsa_private_key +26 -0
  78. package/node_modules/ssh2/test/fixtures/https_cert.pem +33 -0
  79. package/node_modules/ssh2/test/fixtures/https_key.pem +52 -0
  80. package/node_modules/ssh2/test/fixtures/id_dsa +12 -0
  81. package/node_modules/ssh2/test/fixtures/id_ecdsa +5 -0
  82. package/node_modules/ssh2/test/fixtures/id_rsa +15 -0
  83. package/node_modules/ssh2/test/fixtures/id_rsa.ppk +26 -0
  84. package/node_modules/ssh2/test/fixtures/id_rsa_enc +30 -0
  85. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa +21 -0
  86. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.pub +1 -0
  87. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.pub.result +7 -0
  88. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.result +7 -0
  89. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc +22 -0
  90. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.pub +1 -0
  91. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.pub.result +7 -0
  92. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.result +7 -0
  93. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm +23 -0
  94. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.pub +1 -0
  95. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.pub.result +7 -0
  96. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.result +7 -0
  97. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa +9 -0
  98. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.pub +1 -0
  99. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.pub.result +7 -0
  100. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.result +7 -0
  101. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc +10 -0
  102. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.pub +1 -0
  103. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.pub.result +8 -0
  104. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.result +7 -0
  105. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm +10 -0
  106. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.pub +1 -0
  107. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.pub.result +8 -0
  108. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.result +7 -0
  109. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519 +7 -0
  110. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.pub +1 -0
  111. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.pub.result +7 -0
  112. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.result +7 -0
  113. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa +27 -0
  114. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.pub +1 -0
  115. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.pub.result +7 -0
  116. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.result +7 -0
  117. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc +28 -0
  118. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.pub +1 -0
  119. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.pub.result +8 -0
  120. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.result +7 -0
  121. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm +29 -0
  122. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.pub +1 -0
  123. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.pub.result +8 -0
  124. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.result +7 -0
  125. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa +12 -0
  126. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.pub +1 -0
  127. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.pub.result +7 -0
  128. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.result +7 -0
  129. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc +15 -0
  130. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.pub +1 -0
  131. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.pub.result +8 -0
  132. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.result +7 -0
  133. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa +5 -0
  134. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.pub +1 -0
  135. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.pub.result +7 -0
  136. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.result +7 -0
  137. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc +8 -0
  138. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.pub +1 -0
  139. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.pub.result +8 -0
  140. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.result +7 -0
  141. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa +27 -0
  142. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.pub +1 -0
  143. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.pub.result +7 -0
  144. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.result +7 -0
  145. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc +30 -0
  146. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.pub +1 -0
  147. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.pub.result +8 -0
  148. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.result +7 -0
  149. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256 +54 -0
  150. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.pub +1 -0
  151. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.pub.result +6 -0
  152. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.result +6 -0
  153. package/node_modules/ssh2/test/fixtures/keyParser/ppk_dsa_enc +17 -0
  154. package/node_modules/ssh2/test/fixtures/keyParser/ppk_dsa_enc.result +7 -0
  155. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa +26 -0
  156. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa.result +7 -0
  157. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa_enc +18 -0
  158. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa_enc.result +7 -0
  159. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa.pub +9 -0
  160. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa.pub.result +7 -0
  161. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa2.pub +10 -0
  162. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa2.pub.result +7 -0
  163. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa3.pub +11 -0
  164. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa3.pub.result +7 -0
  165. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa4.pub +11 -0
  166. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa4.pub.result +7 -0
  167. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa5.pub +8 -0
  168. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa5.pub.result +7 -0
  169. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa6.pub +13 -0
  170. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa6.pub.result +7 -0
  171. package/node_modules/ssh2/test/fixtures/openssh_new_rsa +27 -0
  172. package/node_modules/ssh2/test/fixtures/ssh_host_dsa_key +12 -0
  173. package/node_modules/ssh2/test/fixtures/ssh_host_ecdsa_key +5 -0
  174. package/node_modules/ssh2/test/fixtures/ssh_host_rsa_key +15 -0
  175. package/node_modules/ssh2/test/test-exec.js +578 -0
  176. package/node_modules/ssh2/test/test-integration-openssh.js +486 -0
  177. package/node_modules/ssh2/test/test-keygen.js +60 -0
  178. package/node_modules/ssh2/test/test-misc-client-server.js +1460 -0
  179. package/node_modules/ssh2/test/test-openssh.js +261 -0
  180. package/node_modules/ssh2/test/test-protocol-crypto.js +631 -0
  181. package/node_modules/ssh2/test/test-protocol-keyparser.js +177 -0
  182. package/node_modules/ssh2/test/test-server-hostkeys.js +138 -0
  183. package/node_modules/ssh2/test/test-sftp.js +842 -0
  184. package/node_modules/ssh2/test/test-shell.js +109 -0
  185. package/node_modules/ssh2/test/test-userauth-agent-openssh.js +110 -0
  186. package/node_modules/ssh2/test/test-userauth-agent.js +171 -0
  187. package/node_modules/ssh2/test/test-userauth.js +611 -0
  188. package/node_modules/ssh2/test/test-worker-imports.js +25 -0
  189. package/node_modules/ssh2/test/test.js +20 -0
  190. package/node_modules/ssh2/util/build_pagent.bat +2 -0
  191. package/node_modules/ssh2/util/pagent.c +88 -0
  192. package/node_modules/ssh2/util/pagent.exe +0 -0
  193. package/node_modules/tweetnacl/.npmignore +4 -0
  194. package/node_modules/tweetnacl/AUTHORS.md +28 -0
  195. package/node_modules/tweetnacl/CHANGELOG.md +221 -0
  196. package/node_modules/tweetnacl/LICENSE +24 -0
  197. package/node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md +20 -0
  198. package/node_modules/tweetnacl/README.md +459 -0
  199. package/node_modules/tweetnacl/nacl-fast.js +2388 -0
  200. package/node_modules/tweetnacl/nacl-fast.min.js +2 -0
  201. package/node_modules/tweetnacl/nacl.d.ts +98 -0
  202. package/node_modules/tweetnacl/nacl.js +1175 -0
  203. package/node_modules/tweetnacl/nacl.min.js +1 -0
  204. package/node_modules/tweetnacl/package.json +58 -0
  205. package/package.json +10 -2
@@ -0,0 +1,1380 @@
1
+ // TODO:
2
+ // * convert listenerCount() usage to emit() return value checking?
3
+ // * emit error when connection severed early (e.g. before handshake)
4
+ // * add '.connected' or similar property to connection objects to allow
5
+ // immediate connection status checking
6
+ 'use strict';
7
+
8
+ const { Server: netServer } = require('net');
9
+ const EventEmitter = require('events');
10
+ const { listenerCount } = EventEmitter;
11
+
12
+ const {
13
+ CHANNEL_OPEN_FAILURE,
14
+ DEFAULT_CIPHER,
15
+ DEFAULT_COMPRESSION,
16
+ DEFAULT_KEX,
17
+ DEFAULT_MAC,
18
+ DEFAULT_SERVER_HOST_KEY,
19
+ DISCONNECT_REASON,
20
+ DISCONNECT_REASON_BY_VALUE,
21
+ SUPPORTED_CIPHER,
22
+ SUPPORTED_COMPRESSION,
23
+ SUPPORTED_KEX,
24
+ SUPPORTED_MAC,
25
+ SUPPORTED_SERVER_HOST_KEY,
26
+ } = require('./protocol/constants.js');
27
+ const { init: cryptoInit } = require('./protocol/crypto.js');
28
+ const { KexInit } = require('./protocol/kex.js');
29
+ const { parseKey } = require('./protocol/keyParser.js');
30
+ const Protocol = require('./protocol/Protocol.js');
31
+ const { SFTP } = require('./protocol/SFTP.js');
32
+ const { writeUInt32BE } = require('./protocol/utils.js');
33
+
34
+ const {
35
+ Channel,
36
+ MAX_WINDOW,
37
+ PACKET_SIZE,
38
+ windowAdjust,
39
+ WINDOW_THRESHOLD,
40
+ } = require('./Channel.js');
41
+
42
+ const {
43
+ ChannelManager,
44
+ generateAlgorithmList,
45
+ isWritable,
46
+ onChannelOpenFailure,
47
+ onCHANNEL_CLOSE,
48
+ } = require('./utils.js');
49
+
50
+ const MAX_PENDING_AUTHS = 10;
51
+
52
+ class AuthContext extends EventEmitter {
53
+ constructor(protocol, username, service, method, cb) {
54
+ super();
55
+
56
+ this.username = this.user = username;
57
+ this.service = service;
58
+ this.method = method;
59
+ this._initialResponse = false;
60
+ this._finalResponse = false;
61
+ this._multistep = false;
62
+ this._cbfinal = (allowed, methodsLeft, isPartial) => {
63
+ if (!this._finalResponse) {
64
+ this._finalResponse = true;
65
+ cb(this, allowed, methodsLeft, isPartial);
66
+ }
67
+ };
68
+ this._protocol = protocol;
69
+ }
70
+
71
+ accept() {
72
+ this._cleanup && this._cleanup();
73
+ this._initialResponse = true;
74
+ this._cbfinal(true);
75
+ }
76
+ reject(methodsLeft, isPartial) {
77
+ this._cleanup && this._cleanup();
78
+ this._initialResponse = true;
79
+ this._cbfinal(false, methodsLeft, isPartial);
80
+ }
81
+ }
82
+
83
+
84
+ class KeyboardAuthContext extends AuthContext {
85
+ constructor(protocol, username, service, method, submethods, cb) {
86
+ super(protocol, username, service, method, cb);
87
+
88
+ this._multistep = true;
89
+
90
+ this._cb = undefined;
91
+ this._onInfoResponse = (responses) => {
92
+ const callback = this._cb;
93
+ if (callback) {
94
+ this._cb = undefined;
95
+ callback(responses);
96
+ }
97
+ };
98
+ this.submethods = submethods;
99
+ this.on('abort', () => {
100
+ this._cb && this._cb(new Error('Authentication request aborted'));
101
+ });
102
+ }
103
+
104
+ prompt(prompts, title, instructions, cb) {
105
+ if (!Array.isArray(prompts))
106
+ prompts = [ prompts ];
107
+
108
+ if (typeof title === 'function') {
109
+ cb = title;
110
+ title = instructions = undefined;
111
+ } else if (typeof instructions === 'function') {
112
+ cb = instructions;
113
+ instructions = undefined;
114
+ } else if (typeof cb !== 'function') {
115
+ cb = undefined;
116
+ }
117
+
118
+ for (let i = 0; i < prompts.length; ++i) {
119
+ if (typeof prompts[i] === 'string') {
120
+ prompts[i] = {
121
+ prompt: prompts[i],
122
+ echo: true
123
+ };
124
+ }
125
+ }
126
+
127
+ this._cb = cb;
128
+ this._initialResponse = true;
129
+
130
+ this._protocol.authInfoReq(title, instructions, prompts);
131
+ }
132
+ }
133
+
134
+ class PKAuthContext extends AuthContext {
135
+ constructor(protocol, username, service, method, pkInfo, cb) {
136
+ super(protocol, username, service, method, cb);
137
+
138
+ this.key = { algo: pkInfo.keyAlgo, data: pkInfo.key };
139
+ this.hashAlgo = pkInfo.hashAlgo;
140
+ this.signature = pkInfo.signature;
141
+ this.blob = pkInfo.blob;
142
+ }
143
+
144
+ accept() {
145
+ if (!this.signature) {
146
+ this._initialResponse = true;
147
+ this._protocol.authPKOK(this.key.algo, this.key.data);
148
+ } else {
149
+ AuthContext.prototype.accept.call(this);
150
+ }
151
+ }
152
+ }
153
+
154
+ class HostbasedAuthContext extends AuthContext {
155
+ constructor(protocol, username, service, method, pkInfo, cb) {
156
+ super(protocol, username, service, method, cb);
157
+
158
+ this.key = { algo: pkInfo.keyAlgo, data: pkInfo.key };
159
+ this.hashAlgo = pkInfo.hashAlgo;
160
+ this.signature = pkInfo.signature;
161
+ this.blob = pkInfo.blob;
162
+ this.localHostname = pkInfo.localHostname;
163
+ this.localUsername = pkInfo.localUsername;
164
+ }
165
+ }
166
+
167
+ class PwdAuthContext extends AuthContext {
168
+ constructor(protocol, username, service, method, password, cb) {
169
+ super(protocol, username, service, method, cb);
170
+
171
+ this.password = password;
172
+ this._changeCb = undefined;
173
+ }
174
+
175
+ requestChange(prompt, cb) {
176
+ if (this._changeCb)
177
+ throw new Error('Change request already in progress');
178
+ if (typeof prompt !== 'string')
179
+ throw new Error('prompt argument must be a string');
180
+ if (typeof cb !== 'function')
181
+ throw new Error('Callback argument must be a function');
182
+ this._changeCb = cb;
183
+ this._protocol.authPasswdChg(prompt);
184
+ }
185
+ }
186
+
187
+
188
+ class Session extends EventEmitter {
189
+ constructor(client, info, localChan) {
190
+ super();
191
+
192
+ this.type = 'session';
193
+ this.subtype = undefined;
194
+ this.server = true;
195
+ this._ending = false;
196
+ this._channel = undefined;
197
+ this._chanInfo = {
198
+ type: 'session',
199
+ incoming: {
200
+ id: localChan,
201
+ window: MAX_WINDOW,
202
+ packetSize: PACKET_SIZE,
203
+ state: 'open'
204
+ },
205
+ outgoing: {
206
+ id: info.sender,
207
+ window: info.window,
208
+ packetSize: info.packetSize,
209
+ state: 'open'
210
+ }
211
+ };
212
+ }
213
+ }
214
+
215
+
216
+ class Server extends EventEmitter {
217
+ constructor(cfg, listener) {
218
+ super();
219
+
220
+ if (typeof cfg !== 'object' || cfg === null)
221
+ throw new Error('Missing configuration object');
222
+
223
+ const hostKeys = Object.create(null);
224
+ const hostKeyAlgoOrder = [];
225
+
226
+ const hostKeys_ = cfg.hostKeys;
227
+ if (!Array.isArray(hostKeys_))
228
+ throw new Error('hostKeys must be an array');
229
+
230
+ const cfgAlgos = (
231
+ typeof cfg.algorithms === 'object' && cfg.algorithms !== null
232
+ ? cfg.algorithms
233
+ : {}
234
+ );
235
+
236
+ const hostKeyAlgos = generateAlgorithmList(
237
+ cfgAlgos.serverHostKey,
238
+ DEFAULT_SERVER_HOST_KEY,
239
+ SUPPORTED_SERVER_HOST_KEY
240
+ );
241
+ for (let i = 0; i < hostKeys_.length; ++i) {
242
+ let privateKey;
243
+ if (Buffer.isBuffer(hostKeys_[i]) || typeof hostKeys_[i] === 'string')
244
+ privateKey = parseKey(hostKeys_[i]);
245
+ else
246
+ privateKey = parseKey(hostKeys_[i].key, hostKeys_[i].passphrase);
247
+
248
+ if (privateKey instanceof Error)
249
+ throw new Error(`Cannot parse privateKey: ${privateKey.message}`);
250
+
251
+ if (Array.isArray(privateKey)) {
252
+ // OpenSSH's newer format only stores 1 key for now
253
+ privateKey = privateKey[0];
254
+ }
255
+
256
+ if (privateKey.getPrivatePEM() === null)
257
+ throw new Error('privateKey value contains an invalid private key');
258
+
259
+ // Discard key if we already found a key of the same type
260
+ if (hostKeyAlgoOrder.includes(privateKey.type))
261
+ continue;
262
+
263
+ if (privateKey.type === 'ssh-rsa') {
264
+ // SSH supports multiple signature hashing algorithms for RSA, so we add
265
+ // the algorithms in the desired order
266
+ let sha1Pos = hostKeyAlgos.indexOf('ssh-rsa');
267
+ const sha256Pos = hostKeyAlgos.indexOf('rsa-sha2-256');
268
+ const sha512Pos = hostKeyAlgos.indexOf('rsa-sha2-512');
269
+ if (sha1Pos === -1) {
270
+ // Fall back to giving SHA1 the lowest priority
271
+ sha1Pos = Infinity;
272
+ }
273
+ [sha1Pos, sha256Pos, sha512Pos].sort(compareNumbers).forEach((pos) => {
274
+ if (pos === -1)
275
+ return;
276
+
277
+ let type;
278
+ switch (pos) {
279
+ case sha1Pos: type = 'ssh-rsa'; break;
280
+ case sha256Pos: type = 'rsa-sha2-256'; break;
281
+ case sha512Pos: type = 'rsa-sha2-512'; break;
282
+ default: return;
283
+ }
284
+
285
+ // Store same RSA key under each hash algorithm name for convenience
286
+ hostKeys[type] = privateKey;
287
+
288
+ hostKeyAlgoOrder.push(type);
289
+ });
290
+ } else {
291
+ hostKeys[privateKey.type] = privateKey;
292
+ hostKeyAlgoOrder.push(privateKey.type);
293
+ }
294
+ }
295
+
296
+ const algorithms = {
297
+ kex: generateAlgorithmList(
298
+ cfgAlgos.kex,
299
+ DEFAULT_KEX,
300
+ SUPPORTED_KEX
301
+ ).concat(['kex-strict-s-v00@openssh.com']),
302
+ serverHostKey: hostKeyAlgoOrder,
303
+ cs: {
304
+ cipher: generateAlgorithmList(
305
+ cfgAlgos.cipher,
306
+ DEFAULT_CIPHER,
307
+ SUPPORTED_CIPHER
308
+ ),
309
+ mac: generateAlgorithmList(cfgAlgos.hmac, DEFAULT_MAC, SUPPORTED_MAC),
310
+ compress: generateAlgorithmList(
311
+ cfgAlgos.compress,
312
+ DEFAULT_COMPRESSION,
313
+ SUPPORTED_COMPRESSION
314
+ ),
315
+ lang: [],
316
+ },
317
+ sc: undefined,
318
+ };
319
+ algorithms.sc = algorithms.cs;
320
+
321
+ if (typeof listener === 'function')
322
+ this.on('connection', listener);
323
+
324
+ const origDebug = (typeof cfg.debug === 'function' ? cfg.debug : undefined);
325
+ const ident = (cfg.ident ? Buffer.from(cfg.ident) : undefined);
326
+ const offer = new KexInit(algorithms);
327
+
328
+ this._srv = new netServer((socket) => {
329
+ if (this._connections >= this.maxConnections) {
330
+ socket.destroy();
331
+ return;
332
+ }
333
+ ++this._connections;
334
+ socket.once('close', () => {
335
+ --this._connections;
336
+ });
337
+
338
+ let debug;
339
+ if (origDebug) {
340
+ // Prepend debug output with a unique identifier in case there are
341
+ // multiple clients connected at the same time
342
+ const debugPrefix = `[${process.hrtime().join('.')}] `;
343
+ debug = (msg) => {
344
+ origDebug(`${debugPrefix}${msg}`);
345
+ };
346
+ }
347
+
348
+ // eslint-disable-next-line no-use-before-define
349
+ new Client(socket, hostKeys, ident, offer, debug, this, cfg);
350
+ }).on('error', (err) => {
351
+ this.emit('error', err);
352
+ }).on('listening', () => {
353
+ this.emit('listening');
354
+ }).on('close', () => {
355
+ this.emit('close');
356
+ });
357
+ this._connections = 0;
358
+ this.maxConnections = Infinity;
359
+ }
360
+
361
+ injectSocket(socket) {
362
+ this._srv.emit('connection', socket);
363
+ }
364
+
365
+ listen(...args) {
366
+ this._srv.listen(...args);
367
+ return this;
368
+ }
369
+
370
+ address() {
371
+ return this._srv.address();
372
+ }
373
+
374
+ getConnections(cb) {
375
+ this._srv.getConnections(cb);
376
+ return this;
377
+ }
378
+
379
+ close(cb) {
380
+ this._srv.close(cb);
381
+ return this;
382
+ }
383
+
384
+ ref() {
385
+ this._srv.ref();
386
+ return this;
387
+ }
388
+
389
+ unref() {
390
+ this._srv.unref();
391
+ return this;
392
+ }
393
+ }
394
+ Server.KEEPALIVE_CLIENT_INTERVAL = 15000;
395
+ Server.KEEPALIVE_CLIENT_COUNT_MAX = 3;
396
+
397
+
398
+ class Client extends EventEmitter {
399
+ constructor(socket, hostKeys, ident, offer, debug, server, srvCfg) {
400
+ super();
401
+
402
+ let exchanges = 0;
403
+ let acceptedAuthSvc = false;
404
+ let pendingAuths = [];
405
+ let authCtx;
406
+ let kaTimer;
407
+ let onPacket;
408
+ const unsentGlobalRequestsReplies = [];
409
+ this._sock = socket;
410
+ this._chanMgr = new ChannelManager(this);
411
+ this._debug = debug;
412
+ this.noMoreSessions = false;
413
+ this.authenticated = false;
414
+
415
+ // Silence pre-header errors
416
+ function onClientPreHeaderError(err) {}
417
+ this.on('error', onClientPreHeaderError);
418
+
419
+ const DEBUG_HANDLER = (!debug ? undefined : (p, display, msg) => {
420
+ debug(`Debug output from client: ${JSON.stringify(msg)}`);
421
+ });
422
+
423
+ const kaIntvl = (
424
+ typeof srvCfg.keepaliveInterval === 'number'
425
+ && isFinite(srvCfg.keepaliveInterval)
426
+ && srvCfg.keepaliveInterval > 0
427
+ ? srvCfg.keepaliveInterval
428
+ : (
429
+ typeof Server.KEEPALIVE_CLIENT_INTERVAL === 'number'
430
+ && isFinite(Server.KEEPALIVE_CLIENT_INTERVAL)
431
+ && Server.KEEPALIVE_CLIENT_INTERVAL > 0
432
+ ? Server.KEEPALIVE_CLIENT_INTERVAL
433
+ : -1
434
+ )
435
+ );
436
+ const kaCountMax = (
437
+ typeof srvCfg.keepaliveCountMax === 'number'
438
+ && isFinite(srvCfg.keepaliveCountMax)
439
+ && srvCfg.keepaliveCountMax >= 0
440
+ ? srvCfg.keepaliveCountMax
441
+ : (
442
+ typeof Server.KEEPALIVE_CLIENT_COUNT_MAX === 'number'
443
+ && isFinite(Server.KEEPALIVE_CLIENT_COUNT_MAX)
444
+ && Server.KEEPALIVE_CLIENT_COUNT_MAX >= 0
445
+ ? Server.KEEPALIVE_CLIENT_COUNT_MAX
446
+ : -1
447
+ )
448
+ );
449
+ let kaCurCount = 0;
450
+ if (kaIntvl !== -1 && kaCountMax !== -1) {
451
+ this.once('ready', () => {
452
+ const onClose = () => {
453
+ clearInterval(kaTimer);
454
+ };
455
+ this.on('close', onClose).on('end', onClose);
456
+ kaTimer = setInterval(() => {
457
+ if (++kaCurCount > kaCountMax) {
458
+ clearInterval(kaTimer);
459
+ const err = new Error('Keepalive timeout');
460
+ err.level = 'client-timeout';
461
+ this.emit('error', err);
462
+ this.end();
463
+ } else {
464
+ // XXX: if the server ever starts sending real global requests to
465
+ // the client, we will need to add a dummy callback here to
466
+ // keep the correct reply order
467
+ proto.ping();
468
+ }
469
+ }, kaIntvl);
470
+ });
471
+ // TODO: re-verify keepalive behavior with OpenSSH
472
+ onPacket = () => {
473
+ kaTimer && kaTimer.refresh();
474
+ kaCurCount = 0;
475
+ };
476
+ }
477
+
478
+ const proto = this._protocol = new Protocol({
479
+ server: true,
480
+ hostKeys,
481
+ ident,
482
+ offer,
483
+ onPacket,
484
+ greeting: srvCfg.greeting,
485
+ banner: srvCfg.banner,
486
+ onWrite: (data) => {
487
+ if (isWritable(socket))
488
+ socket.write(data);
489
+ },
490
+ onError: (err) => {
491
+ if (!proto._destruct)
492
+ socket.removeAllListeners('data');
493
+ this.emit('error', err);
494
+ try {
495
+ socket.end();
496
+ } catch {}
497
+ },
498
+ onHeader: (header) => {
499
+ this.removeListener('error', onClientPreHeaderError);
500
+
501
+ const info = {
502
+ ip: socket.remoteAddress,
503
+ family: socket.remoteFamily,
504
+ port: socket.remotePort,
505
+ header,
506
+ };
507
+ if (!server.emit('connection', this, info)) {
508
+ // auto reject
509
+ proto.disconnect(DISCONNECT_REASON.BY_APPLICATION);
510
+ socket.end();
511
+ return;
512
+ }
513
+
514
+ if (header.greeting)
515
+ this.emit('greeting', header.greeting);
516
+ },
517
+ onHandshakeComplete: (negotiated) => {
518
+ if (++exchanges > 1)
519
+ this.emit('rekey');
520
+ this.emit('handshake', negotiated);
521
+ },
522
+ debug,
523
+ messageHandlers: {
524
+ DEBUG: DEBUG_HANDLER,
525
+ DISCONNECT: (p, reason, desc) => {
526
+ if (reason !== DISCONNECT_REASON.BY_APPLICATION) {
527
+ if (!desc) {
528
+ desc = DISCONNECT_REASON_BY_VALUE[reason];
529
+ if (desc === undefined)
530
+ desc = `Unexpected disconnection reason: ${reason}`;
531
+ }
532
+ const err = new Error(desc);
533
+ err.code = reason;
534
+ this.emit('error', err);
535
+ }
536
+ socket.end();
537
+ },
538
+ CHANNEL_OPEN: (p, info) => {
539
+ // Handle incoming requests from client
540
+
541
+ // Do early reject in some cases to prevent wasteful channel
542
+ // allocation
543
+ if ((info.type === 'session' && this.noMoreSessions)
544
+ || !this.authenticated) {
545
+ const reasonCode = CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED;
546
+ return proto.channelOpenFail(info.sender, reasonCode);
547
+ }
548
+
549
+ let localChan = -1;
550
+ let reason;
551
+ let replied = false;
552
+
553
+ let accept;
554
+ const reject = () => {
555
+ if (replied)
556
+ return;
557
+ replied = true;
558
+
559
+ if (reason === undefined) {
560
+ if (localChan === -1)
561
+ reason = CHANNEL_OPEN_FAILURE.RESOURCE_SHORTAGE;
562
+ else
563
+ reason = CHANNEL_OPEN_FAILURE.CONNECT_FAILED;
564
+ }
565
+
566
+ if (localChan !== -1)
567
+ this._chanMgr.remove(localChan);
568
+ proto.channelOpenFail(info.sender, reason, '');
569
+ };
570
+ const reserveChannel = () => {
571
+ localChan = this._chanMgr.add();
572
+
573
+ if (localChan === -1) {
574
+ reason = CHANNEL_OPEN_FAILURE.RESOURCE_SHORTAGE;
575
+ if (debug) {
576
+ debug('Automatic rejection of incoming channel open: '
577
+ + 'no channels available');
578
+ }
579
+ }
580
+
581
+ return (localChan !== -1);
582
+ };
583
+
584
+ const data = info.data;
585
+ switch (info.type) {
586
+ case 'session':
587
+ if (listenerCount(this, 'session') && reserveChannel()) {
588
+ accept = () => {
589
+ if (replied)
590
+ return;
591
+ replied = true;
592
+
593
+ const instance = new Session(this, info, localChan);
594
+ this._chanMgr.update(localChan, instance);
595
+
596
+ proto.channelOpenConfirm(info.sender,
597
+ localChan,
598
+ MAX_WINDOW,
599
+ PACKET_SIZE);
600
+
601
+ return instance;
602
+ };
603
+
604
+ this.emit('session', accept, reject);
605
+ return;
606
+ }
607
+ break;
608
+ case 'direct-tcpip':
609
+ if (listenerCount(this, 'tcpip') && reserveChannel()) {
610
+ accept = () => {
611
+ if (replied)
612
+ return;
613
+ replied = true;
614
+
615
+ const chanInfo = {
616
+ type: undefined,
617
+ incoming: {
618
+ id: localChan,
619
+ window: MAX_WINDOW,
620
+ packetSize: PACKET_SIZE,
621
+ state: 'open'
622
+ },
623
+ outgoing: {
624
+ id: info.sender,
625
+ window: info.window,
626
+ packetSize: info.packetSize,
627
+ state: 'open'
628
+ }
629
+ };
630
+
631
+ const stream = new Channel(this, chanInfo, { server: true });
632
+ this._chanMgr.update(localChan, stream);
633
+
634
+ proto.channelOpenConfirm(info.sender,
635
+ localChan,
636
+ MAX_WINDOW,
637
+ PACKET_SIZE);
638
+
639
+ return stream;
640
+ };
641
+
642
+ this.emit('tcpip', accept, reject, data);
643
+ return;
644
+ }
645
+ break;
646
+ case 'direct-streamlocal@openssh.com':
647
+ if (listenerCount(this, 'openssh.streamlocal')
648
+ && reserveChannel()) {
649
+ accept = () => {
650
+ if (replied)
651
+ return;
652
+ replied = true;
653
+
654
+ const chanInfo = {
655
+ type: undefined,
656
+ incoming: {
657
+ id: localChan,
658
+ window: MAX_WINDOW,
659
+ packetSize: PACKET_SIZE,
660
+ state: 'open'
661
+ },
662
+ outgoing: {
663
+ id: info.sender,
664
+ window: info.window,
665
+ packetSize: info.packetSize,
666
+ state: 'open'
667
+ }
668
+ };
669
+
670
+ const stream = new Channel(this, chanInfo, { server: true });
671
+ this._chanMgr.update(localChan, stream);
672
+
673
+ proto.channelOpenConfirm(info.sender,
674
+ localChan,
675
+ MAX_WINDOW,
676
+ PACKET_SIZE);
677
+
678
+ return stream;
679
+ };
680
+
681
+ this.emit('openssh.streamlocal', accept, reject, data);
682
+ return;
683
+ }
684
+ break;
685
+ default:
686
+ // Automatically reject any unsupported channel open requests
687
+ reason = CHANNEL_OPEN_FAILURE.UNKNOWN_CHANNEL_TYPE;
688
+ if (debug) {
689
+ debug('Automatic rejection of unsupported incoming channel open'
690
+ + ` type: ${info.type}`);
691
+ }
692
+ }
693
+
694
+ if (reason === undefined) {
695
+ reason = CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED;
696
+ if (debug) {
697
+ debug('Automatic rejection of unexpected incoming channel open'
698
+ + ` for: ${info.type}`);
699
+ }
700
+ }
701
+
702
+ reject();
703
+ },
704
+ CHANNEL_OPEN_CONFIRMATION: (p, info) => {
705
+ const channel = this._chanMgr.get(info.recipient);
706
+ if (typeof channel !== 'function')
707
+ return;
708
+
709
+ const chanInfo = {
710
+ type: channel.type,
711
+ incoming: {
712
+ id: info.recipient,
713
+ window: MAX_WINDOW,
714
+ packetSize: PACKET_SIZE,
715
+ state: 'open'
716
+ },
717
+ outgoing: {
718
+ id: info.sender,
719
+ window: info.window,
720
+ packetSize: info.packetSize,
721
+ state: 'open'
722
+ }
723
+ };
724
+
725
+ const instance = new Channel(this, chanInfo, { server: true });
726
+ this._chanMgr.update(info.recipient, instance);
727
+ channel(undefined, instance);
728
+ },
729
+ CHANNEL_OPEN_FAILURE: (p, recipient, reason, description) => {
730
+ const channel = this._chanMgr.get(recipient);
731
+ if (typeof channel !== 'function')
732
+ return;
733
+
734
+ const info = { reason, description };
735
+ onChannelOpenFailure(this, recipient, info, channel);
736
+ },
737
+ CHANNEL_DATA: (p, recipient, data) => {
738
+ let channel = this._chanMgr.get(recipient);
739
+ if (typeof channel !== 'object' || channel === null)
740
+ return;
741
+
742
+ if (channel.constructor === Session) {
743
+ channel = channel._channel;
744
+ if (!channel)
745
+ return;
746
+ }
747
+
748
+ // The remote party should not be sending us data if there is no
749
+ // window space available ...
750
+ // TODO: raise error on data with not enough window?
751
+ if (channel.incoming.window === 0)
752
+ return;
753
+
754
+ channel.incoming.window -= data.length;
755
+
756
+ if (channel.push(data) === false) {
757
+ channel._waitChanDrain = true;
758
+ return;
759
+ }
760
+
761
+ if (channel.incoming.window <= WINDOW_THRESHOLD)
762
+ windowAdjust(channel);
763
+ },
764
+ CHANNEL_EXTENDED_DATA: (p, recipient, data, type) => {
765
+ // NOOP -- should not be sent by client
766
+ },
767
+ CHANNEL_WINDOW_ADJUST: (p, recipient, amount) => {
768
+ let channel = this._chanMgr.get(recipient);
769
+ if (typeof channel !== 'object' || channel === null)
770
+ return;
771
+
772
+ if (channel.constructor === Session) {
773
+ channel = channel._channel;
774
+ if (!channel)
775
+ return;
776
+ }
777
+
778
+ // The other side is allowing us to send `amount` more bytes of data
779
+ channel.outgoing.window += amount;
780
+
781
+ if (channel._waitWindow) {
782
+ channel._waitWindow = false;
783
+
784
+ if (channel._chunk) {
785
+ channel._write(channel._chunk, null, channel._chunkcb);
786
+ } else if (channel._chunkcb) {
787
+ channel._chunkcb();
788
+ } else if (channel._chunkErr) {
789
+ channel.stderr._write(channel._chunkErr,
790
+ null,
791
+ channel._chunkcbErr);
792
+ } else if (channel._chunkcbErr) {
793
+ channel._chunkcbErr();
794
+ }
795
+ }
796
+ },
797
+ CHANNEL_SUCCESS: (p, recipient) => {
798
+ let channel = this._chanMgr.get(recipient);
799
+ if (typeof channel !== 'object' || channel === null)
800
+ return;
801
+
802
+ if (channel.constructor === Session) {
803
+ channel = channel._channel;
804
+ if (!channel)
805
+ return;
806
+ }
807
+
808
+ if (channel._callbacks.length)
809
+ channel._callbacks.shift()(false);
810
+ },
811
+ CHANNEL_FAILURE: (p, recipient) => {
812
+ let channel = this._chanMgr.get(recipient);
813
+ if (typeof channel !== 'object' || channel === null)
814
+ return;
815
+
816
+ if (channel.constructor === Session) {
817
+ channel = channel._channel;
818
+ if (!channel)
819
+ return;
820
+ }
821
+
822
+ if (channel._callbacks.length)
823
+ channel._callbacks.shift()(true);
824
+ },
825
+ CHANNEL_REQUEST: (p, recipient, type, wantReply, data) => {
826
+ const session = this._chanMgr.get(recipient);
827
+ if (typeof session !== 'object' || session === null)
828
+ return;
829
+
830
+ let replied = false;
831
+ let accept;
832
+ let reject;
833
+
834
+ if (session.constructor !== Session) {
835
+ // normal Channel instance
836
+ if (wantReply)
837
+ proto.channelFailure(session.outgoing.id);
838
+ return;
839
+ }
840
+
841
+ if (wantReply) {
842
+ // "real session" requests will have custom accept behaviors
843
+ if (type !== 'shell'
844
+ && type !== 'exec'
845
+ && type !== 'subsystem') {
846
+ accept = () => {
847
+ if (replied || session._ending || session._channel)
848
+ return;
849
+ replied = true;
850
+
851
+ proto.channelSuccess(session._chanInfo.outgoing.id);
852
+ };
853
+ }
854
+
855
+ reject = () => {
856
+ if (replied || session._ending || session._channel)
857
+ return;
858
+ replied = true;
859
+
860
+ proto.channelFailure(session._chanInfo.outgoing.id);
861
+ };
862
+ }
863
+
864
+ if (session._ending) {
865
+ reject && reject();
866
+ return;
867
+ }
868
+
869
+ switch (type) {
870
+ // "pre-real session start" requests
871
+ case 'env':
872
+ if (listenerCount(session, 'env')) {
873
+ session.emit('env', accept, reject, {
874
+ key: data.name,
875
+ val: data.value
876
+ });
877
+ return;
878
+ }
879
+ break;
880
+ case 'pty-req':
881
+ if (listenerCount(session, 'pty')) {
882
+ session.emit('pty', accept, reject, data);
883
+ return;
884
+ }
885
+ break;
886
+ case 'window-change':
887
+ if (listenerCount(session, 'window-change'))
888
+ session.emit('window-change', accept, reject, data);
889
+ else
890
+ reject && reject();
891
+ break;
892
+ case 'x11-req':
893
+ if (listenerCount(session, 'x11')) {
894
+ session.emit('x11', accept, reject, data);
895
+ return;
896
+ }
897
+ break;
898
+ // "post-real session start" requests
899
+ case 'signal':
900
+ if (listenerCount(session, 'signal')) {
901
+ session.emit('signal', accept, reject, {
902
+ name: data
903
+ });
904
+ return;
905
+ }
906
+ break;
907
+ // XXX: is `auth-agent-req@openssh.com` really "post-real session
908
+ // start"?
909
+ case 'auth-agent-req@openssh.com':
910
+ if (listenerCount(session, 'auth-agent')) {
911
+ session.emit('auth-agent', accept, reject);
912
+ return;
913
+ }
914
+ break;
915
+ // "real session start" requests
916
+ case 'shell':
917
+ if (listenerCount(session, 'shell')) {
918
+ accept = () => {
919
+ if (replied || session._ending || session._channel)
920
+ return;
921
+ replied = true;
922
+
923
+ if (wantReply)
924
+ proto.channelSuccess(session._chanInfo.outgoing.id);
925
+
926
+ const channel = new Channel(
927
+ this, session._chanInfo, { server: true }
928
+ );
929
+
930
+ channel.subtype = session.subtype = type;
931
+ session._channel = channel;
932
+
933
+ return channel;
934
+ };
935
+
936
+ session.emit('shell', accept, reject);
937
+ return;
938
+ }
939
+ break;
940
+ case 'exec':
941
+ if (listenerCount(session, 'exec')) {
942
+ accept = () => {
943
+ if (replied || session._ending || session._channel)
944
+ return;
945
+ replied = true;
946
+
947
+ if (wantReply)
948
+ proto.channelSuccess(session._chanInfo.outgoing.id);
949
+
950
+ const channel = new Channel(
951
+ this, session._chanInfo, { server: true }
952
+ );
953
+
954
+ channel.subtype = session.subtype = type;
955
+ session._channel = channel;
956
+
957
+ return channel;
958
+ };
959
+
960
+ session.emit('exec', accept, reject, {
961
+ command: data
962
+ });
963
+ return;
964
+ }
965
+ break;
966
+ case 'subsystem': {
967
+ let useSFTP = (data === 'sftp');
968
+ accept = () => {
969
+ if (replied || session._ending || session._channel)
970
+ return;
971
+ replied = true;
972
+
973
+ if (wantReply)
974
+ proto.channelSuccess(session._chanInfo.outgoing.id);
975
+
976
+ let instance;
977
+ if (useSFTP) {
978
+ instance = new SFTP(this, session._chanInfo, {
979
+ server: true,
980
+ debug,
981
+ });
982
+ } else {
983
+ instance = new Channel(
984
+ this, session._chanInfo, { server: true }
985
+ );
986
+ instance.subtype =
987
+ session.subtype = `${type}:${data}`;
988
+ }
989
+ session._channel = instance;
990
+
991
+ return instance;
992
+ };
993
+
994
+ if (data === 'sftp') {
995
+ if (listenerCount(session, 'sftp')) {
996
+ session.emit('sftp', accept, reject);
997
+ return;
998
+ }
999
+ useSFTP = false;
1000
+ }
1001
+ if (listenerCount(session, 'subsystem')) {
1002
+ session.emit('subsystem', accept, reject, {
1003
+ name: data
1004
+ });
1005
+ return;
1006
+ }
1007
+ break;
1008
+ }
1009
+ }
1010
+ debug && debug(
1011
+ `Automatic rejection of incoming channel request: ${type}`
1012
+ );
1013
+ reject && reject();
1014
+ },
1015
+ CHANNEL_EOF: (p, recipient) => {
1016
+ let channel = this._chanMgr.get(recipient);
1017
+ if (typeof channel !== 'object' || channel === null)
1018
+ return;
1019
+
1020
+ if (channel.constructor === Session) {
1021
+ if (!channel._ending) {
1022
+ channel._ending = true;
1023
+ channel.emit('eof');
1024
+ channel.emit('end');
1025
+ }
1026
+ channel = channel._channel;
1027
+ if (!channel)
1028
+ return;
1029
+ }
1030
+
1031
+ if (channel.incoming.state !== 'open')
1032
+ return;
1033
+ channel.incoming.state = 'eof';
1034
+
1035
+ if (channel.readable)
1036
+ channel.push(null);
1037
+ },
1038
+ CHANNEL_CLOSE: (p, recipient) => {
1039
+ let channel = this._chanMgr.get(recipient);
1040
+ if (typeof channel !== 'object' || channel === null)
1041
+ return;
1042
+
1043
+ if (channel.constructor === Session) {
1044
+ channel._ending = true;
1045
+ channel.emit('close');
1046
+ channel = channel._channel;
1047
+ if (!channel)
1048
+ return;
1049
+ }
1050
+
1051
+ onCHANNEL_CLOSE(this, recipient, channel);
1052
+ },
1053
+ // Begin service/auth-related ==========================================
1054
+ SERVICE_REQUEST: (p, service) => {
1055
+ if (exchanges === 0
1056
+ || acceptedAuthSvc
1057
+ || this.authenticated
1058
+ || service !== 'ssh-userauth') {
1059
+ proto.disconnect(DISCONNECT_REASON.SERVICE_NOT_AVAILABLE);
1060
+ socket.end();
1061
+ return;
1062
+ }
1063
+
1064
+ acceptedAuthSvc = true;
1065
+ proto.serviceAccept(service);
1066
+ },
1067
+ USERAUTH_REQUEST: (p, username, service, method, methodData) => {
1068
+ if (exchanges === 0
1069
+ || this.authenticated
1070
+ || (authCtx
1071
+ && (authCtx.username !== username
1072
+ || authCtx.service !== service))
1073
+ // TODO: support hostbased auth
1074
+ || (method !== 'password'
1075
+ && method !== 'publickey'
1076
+ && method !== 'hostbased'
1077
+ && method !== 'keyboard-interactive'
1078
+ && method !== 'none')
1079
+ || pendingAuths.length === MAX_PENDING_AUTHS) {
1080
+ proto.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR);
1081
+ socket.end();
1082
+ return;
1083
+ } else if (service !== 'ssh-connection') {
1084
+ proto.disconnect(DISCONNECT_REASON.SERVICE_NOT_AVAILABLE);
1085
+ socket.end();
1086
+ return;
1087
+ }
1088
+
1089
+ let ctx;
1090
+ switch (method) {
1091
+ case 'keyboard-interactive':
1092
+ ctx = new KeyboardAuthContext(proto, username, service, method,
1093
+ methodData, onAuthDecide);
1094
+ break;
1095
+ case 'publickey':
1096
+ ctx = new PKAuthContext(proto, username, service, method,
1097
+ methodData, onAuthDecide);
1098
+ break;
1099
+ case 'hostbased':
1100
+ ctx = new HostbasedAuthContext(proto, username, service, method,
1101
+ methodData, onAuthDecide);
1102
+ break;
1103
+ case 'password':
1104
+ if (authCtx
1105
+ && authCtx instanceof PwdAuthContext
1106
+ && authCtx._changeCb) {
1107
+ const cb = authCtx._changeCb;
1108
+ authCtx._changeCb = undefined;
1109
+ cb(methodData.newPassword);
1110
+ return;
1111
+ }
1112
+ ctx = new PwdAuthContext(proto, username, service, method,
1113
+ methodData, onAuthDecide);
1114
+ break;
1115
+ case 'none':
1116
+ ctx = new AuthContext(proto, username, service, method,
1117
+ onAuthDecide);
1118
+ break;
1119
+ }
1120
+
1121
+ if (authCtx) {
1122
+ if (!authCtx._initialResponse) {
1123
+ return pendingAuths.push(ctx);
1124
+ } else if (authCtx._multistep && !authCtx._finalResponse) {
1125
+ // RFC 4252 says to silently abort the current auth request if a
1126
+ // new auth request comes in before the final response from an
1127
+ // auth method that requires additional request/response exchanges
1128
+ // -- this means keyboard-interactive for now ...
1129
+ authCtx._cleanup && authCtx._cleanup();
1130
+ authCtx.emit('abort');
1131
+ }
1132
+ }
1133
+
1134
+ authCtx = ctx;
1135
+
1136
+ if (listenerCount(this, 'authentication'))
1137
+ this.emit('authentication', authCtx);
1138
+ else
1139
+ authCtx.reject();
1140
+ },
1141
+ USERAUTH_INFO_RESPONSE: (p, responses) => {
1142
+ if (authCtx && authCtx instanceof KeyboardAuthContext)
1143
+ authCtx._onInfoResponse(responses);
1144
+ },
1145
+ // End service/auth-related ============================================
1146
+ GLOBAL_REQUEST: (p, name, wantReply, data) => {
1147
+ const reply = {
1148
+ type: null,
1149
+ buf: null
1150
+ };
1151
+
1152
+ function setReply(type, buf) {
1153
+ reply.type = type;
1154
+ reply.buf = buf;
1155
+ sendReplies();
1156
+ }
1157
+
1158
+ if (wantReply)
1159
+ unsentGlobalRequestsReplies.push(reply);
1160
+
1161
+ if ((name === 'tcpip-forward'
1162
+ || name === 'cancel-tcpip-forward'
1163
+ || name === 'no-more-sessions@openssh.com'
1164
+ || name === 'streamlocal-forward@openssh.com'
1165
+ || name === 'cancel-streamlocal-forward@openssh.com')
1166
+ && listenerCount(this, 'request')
1167
+ && this.authenticated) {
1168
+ let accept;
1169
+ let reject;
1170
+
1171
+ if (wantReply) {
1172
+ let replied = false;
1173
+ accept = (chosenPort) => {
1174
+ if (replied)
1175
+ return;
1176
+ replied = true;
1177
+ let bufPort;
1178
+ if (name === 'tcpip-forward'
1179
+ && data.bindPort === 0
1180
+ && typeof chosenPort === 'number') {
1181
+ bufPort = Buffer.allocUnsafe(4);
1182
+ writeUInt32BE(bufPort, chosenPort, 0);
1183
+ }
1184
+ setReply('SUCCESS', bufPort);
1185
+ };
1186
+ reject = () => {
1187
+ if (replied)
1188
+ return;
1189
+ replied = true;
1190
+ setReply('FAILURE');
1191
+ };
1192
+ }
1193
+
1194
+ if (name === 'no-more-sessions@openssh.com') {
1195
+ this.noMoreSessions = true;
1196
+ accept && accept();
1197
+ return;
1198
+ }
1199
+
1200
+ this.emit('request', accept, reject, name, data);
1201
+ } else if (wantReply) {
1202
+ setReply('FAILURE');
1203
+ }
1204
+ },
1205
+ },
1206
+ });
1207
+
1208
+ socket.pause();
1209
+ cryptoInit.then(() => {
1210
+ proto.start();
1211
+ socket.on('data', (data) => {
1212
+ try {
1213
+ proto.parse(data, 0, data.length);
1214
+ } catch (ex) {
1215
+ this.emit('error', ex);
1216
+ try {
1217
+ if (isWritable(socket))
1218
+ socket.end();
1219
+ } catch {}
1220
+ }
1221
+ });
1222
+ socket.resume();
1223
+ }).catch((err) => {
1224
+ this.emit('error', err);
1225
+ try {
1226
+ if (isWritable(socket))
1227
+ socket.end();
1228
+ } catch {}
1229
+ });
1230
+ socket.on('error', (err) => {
1231
+ err.level = 'socket';
1232
+ this.emit('error', err);
1233
+ }).once('end', () => {
1234
+ debug && debug('Socket ended');
1235
+ proto.cleanup();
1236
+ this.emit('end');
1237
+ }).once('close', () => {
1238
+ debug && debug('Socket closed');
1239
+ proto.cleanup();
1240
+ this.emit('close');
1241
+
1242
+ const err = new Error('No response from server');
1243
+
1244
+ // Simulate error for pending channels and close any open channels
1245
+ this._chanMgr.cleanup(err);
1246
+ });
1247
+
1248
+ const onAuthDecide = (ctx, allowed, methodsLeft, isPartial) => {
1249
+ if (authCtx === ctx && !this.authenticated) {
1250
+ if (allowed) {
1251
+ authCtx = undefined;
1252
+ this.authenticated = true;
1253
+ proto.authSuccess();
1254
+ pendingAuths = [];
1255
+ this.emit('ready');
1256
+ } else {
1257
+ proto.authFailure(methodsLeft, isPartial);
1258
+ if (pendingAuths.length) {
1259
+ authCtx = pendingAuths.pop();
1260
+ if (listenerCount(this, 'authentication'))
1261
+ this.emit('authentication', authCtx);
1262
+ else
1263
+ authCtx.reject();
1264
+ }
1265
+ }
1266
+ }
1267
+ };
1268
+
1269
+ function sendReplies() {
1270
+ while (unsentGlobalRequestsReplies.length > 0
1271
+ && unsentGlobalRequestsReplies[0].type) {
1272
+ const reply = unsentGlobalRequestsReplies.shift();
1273
+ if (reply.type === 'SUCCESS')
1274
+ proto.requestSuccess(reply.buf);
1275
+ if (reply.type === 'FAILURE')
1276
+ proto.requestFailure();
1277
+ }
1278
+ }
1279
+ }
1280
+
1281
+ end() {
1282
+ if (this._sock && isWritable(this._sock)) {
1283
+ this._protocol.disconnect(DISCONNECT_REASON.BY_APPLICATION);
1284
+ this._sock.end();
1285
+ }
1286
+ return this;
1287
+ }
1288
+
1289
+ x11(originAddr, originPort, cb) {
1290
+ const opts = { originAddr, originPort };
1291
+ openChannel(this, 'x11', opts, cb);
1292
+ return this;
1293
+ }
1294
+
1295
+ forwardOut(boundAddr, boundPort, remoteAddr, remotePort, cb) {
1296
+ const opts = { boundAddr, boundPort, remoteAddr, remotePort };
1297
+ openChannel(this, 'forwarded-tcpip', opts, cb);
1298
+ return this;
1299
+ }
1300
+
1301
+ openssh_forwardOutStreamLocal(socketPath, cb) {
1302
+ const opts = { socketPath };
1303
+ openChannel(this, 'forwarded-streamlocal@openssh.com', opts, cb);
1304
+ return this;
1305
+ }
1306
+
1307
+ rekey(cb) {
1308
+ let error;
1309
+
1310
+ try {
1311
+ this._protocol.rekey();
1312
+ } catch (ex) {
1313
+ error = ex;
1314
+ }
1315
+
1316
+ // TODO: re-throw error if no callback?
1317
+
1318
+ if (typeof cb === 'function') {
1319
+ if (error)
1320
+ process.nextTick(cb, error);
1321
+ else
1322
+ this.once('rekey', cb);
1323
+ }
1324
+ }
1325
+
1326
+ setNoDelay(noDelay) {
1327
+ if (this._sock && typeof this._sock.setNoDelay === 'function')
1328
+ this._sock.setNoDelay(noDelay);
1329
+
1330
+ return this;
1331
+ }
1332
+ }
1333
+
1334
+
1335
+ function openChannel(self, type, opts, cb) {
1336
+ // Ask the client to open a channel for some purpose (e.g. a forwarded TCP
1337
+ // connection)
1338
+ const initWindow = MAX_WINDOW;
1339
+ const maxPacket = PACKET_SIZE;
1340
+
1341
+ if (typeof opts === 'function') {
1342
+ cb = opts;
1343
+ opts = {};
1344
+ }
1345
+
1346
+ const wrapper = (err, stream) => {
1347
+ cb(err, stream);
1348
+ };
1349
+ wrapper.type = type;
1350
+
1351
+ const localChan = self._chanMgr.add(wrapper);
1352
+
1353
+ if (localChan === -1) {
1354
+ cb(new Error('No free channels available'));
1355
+ return;
1356
+ }
1357
+
1358
+ switch (type) {
1359
+ case 'forwarded-tcpip':
1360
+ self._protocol.forwardedTcpip(localChan, initWindow, maxPacket, opts);
1361
+ break;
1362
+ case 'x11':
1363
+ self._protocol.x11(localChan, initWindow, maxPacket, opts);
1364
+ break;
1365
+ case 'forwarded-streamlocal@openssh.com':
1366
+ self._protocol.openssh_forwardedStreamLocal(
1367
+ localChan, initWindow, maxPacket, opts
1368
+ );
1369
+ break;
1370
+ default:
1371
+ throw new Error(`Unsupported channel type: ${type}`);
1372
+ }
1373
+ }
1374
+
1375
+ function compareNumbers(a, b) {
1376
+ return a - b;
1377
+ }
1378
+
1379
+ module.exports = Server;
1380
+ module.exports.IncomingClient = Client;