test-packet60.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. var SSH2Stream = require('../lib/ssh');
  2. var parseKey = require('../lib/utils').parseKey;
  3. var assert = require('assert');
  4. var fs = require('fs');
  5. var t = -1;
  6. var SERVER_PRV_KEY = fs.readFileSync(__dirname + '/fixtures/openssh_new_rsa');
  7. var PARSED_SERVER_KEY = parseKey(SERVER_PRV_KEY);
  8. var CLIENT_PRV_KEY = fs.readFileSync(__dirname + '/fixtures/openssh_old_rsa');
  9. var PARSED_CLIENT_KEY = parseKey(CLIENT_PRV_KEY);
  10. function makePair(cb) {
  11. var server = new SSH2Stream({
  12. server: true,
  13. hostKeys: {
  14. 'ssh-rsa': PARSED_SERVER_KEY
  15. },
  16. algorithms: {
  17. serverHostKey: ['ssh-rsa']
  18. }
  19. });
  20. var client = new SSH2Stream();
  21. var done = [];
  22. function tryDone(who) {
  23. done.push(who);
  24. if (done.length !== 2)
  25. return;
  26. cb(server, client);
  27. }
  28. server.on('NEWKEYS', function () { tryDone('server'); });
  29. client.on('NEWKEYS', function () { tryDone('client'); });
  30. server.pipe(client).pipe(server);
  31. }
  32. function signWithClientKey(blob, syncCb) {
  33. syncCb(PARSED_CLIENT_KEY.sign(blob));
  34. }
  35. function bufferEqual(a, b) {
  36. if (a.length !== b.length)
  37. return false;
  38. for (var i = 0; i < a.length; ++i) {
  39. if (a[i] !== b[i])
  40. return false;
  41. }
  42. return true;
  43. }
  44. function publickey(server, client) {
  45. server.on('USERAUTH_REQUEST', function(user, service, method, data) {
  46. assert.equal(user, 'bob');
  47. assert.equal(service, 'ssh-connection');
  48. assert.equal(method, 'publickey');
  49. assert.equal(data.keyAlgo, PARSED_CLIENT_KEY.type);
  50. assert.equal(true, bufferEqual(data.key, PARSED_CLIENT_KEY.getPublicSSH()));
  51. assert.equal(data.signature, undefined);
  52. assert.equal(data.blob, undefined);
  53. return server.authPKOK(data.keyAlgo, data.key);
  54. });
  55. client.on('USERAUTH_PK_OK', function() {
  56. next();
  57. }).authPK('bob', PARSED_CLIENT_KEY);
  58. }
  59. function keyboardInteractive(server, client) {
  60. var infoReqsRxed = 0;
  61. server.on('USERAUTH_REQUEST', function(user, service, method, data) {
  62. assert.equal(user, 'bob');
  63. assert.equal(service, 'ssh-connection');
  64. assert.equal(method, 'keyboard-interactive');
  65. assert.equal(data, '');
  66. process.nextTick(function() {
  67. server.authInfoReq('req 0', 'instructions', [
  68. { prompt: 'Say something to req 0', echo: true }
  69. ]);
  70. });
  71. }).on('USERAUTH_INFO_RESPONSE', function(responses) {
  72. if (infoReqsRxed === 1) {
  73. assert.equal(responses.length, 1);
  74. assert.equal(responses[0], 'hello to req 0');
  75. process.nextTick(function() {
  76. server.authInfoReq('req 1', 'new instructions', [
  77. { prompt: 'Say something to req 1', echo: true },
  78. { prompt: 'Say something else', echo: false }
  79. ]);
  80. });
  81. } else if (infoReqsRxed === 2) {
  82. assert.equal(responses.length, 2);
  83. assert.equal(responses[0], 'hello to req 1');
  84. assert.equal(responses[1], 'something else');
  85. next();
  86. } else {
  87. throw new Error('Received too many info reqs: ' + infoReqsRxed);
  88. }
  89. });
  90. client.on('USERAUTH_INFO_REQUEST', function (name, inst, lang, prompts) {
  91. infoReqsRxed++;
  92. if (infoReqsRxed === 1) {
  93. assert.equal(name, 'req 0');
  94. assert.equal(inst, 'instructions');
  95. assert.equal(lang, '');
  96. assert.deepEqual(prompts, [
  97. { prompt: 'Say something to req 0', echo: true }
  98. ]);
  99. process.nextTick(function() {
  100. client.authInfoRes([ 'hello to req 0' ]);
  101. });
  102. } else if (infoReqsRxed === 2) {
  103. assert.equal(name, 'req 1');
  104. assert.equal(inst, 'new instructions');
  105. assert.equal(lang, '');
  106. assert.deepEqual(prompts, [
  107. { prompt: 'Say something to req 1', echo: true },
  108. { prompt: 'Say something else', echo: false }
  109. ]);
  110. process.nextTick(function() {
  111. client.authInfoRes([ 'hello to req 1', 'something else' ]);
  112. });
  113. } else {
  114. throw new Error('Received too many info reqs: ' + infoReqsRxed);
  115. }
  116. }).authKeyboard('bob');
  117. }
  118. function mixedMethods(server, client) {
  119. var expectedStages = [
  120. 'SERVER_SEES_PK_CHECK',
  121. 'SERVER_SEES_PK_REQUEST',
  122. 'SERVER_SEES_PASSWORD',
  123. 'SERVER_SEES_KEYBOARD_INTERACTIVE',
  124. 'CLIENT_SEES_PK_OK',
  125. 'CLIENT_SEES_USERAUTH_FAILURE_PK',
  126. 'CLIENT_SEES_USERAUTH_FAILURE_PASSWORD',
  127. 'CLIENT_SEES_KEYBOARD_REQ',
  128. 'SERVER_SEES_KEYBOARD_RES',
  129. 'CLIENT_SEES_USERAUTH_SUCCESS',
  130. ];
  131. server.on('USERAUTH_REQUEST', function(name, service, method, data) {
  132. assert.equal(name, 'bob');
  133. assert.equal(service, 'ssh-connection');
  134. var expectedStage = expectedStages.shift();
  135. switch (expectedStage) {
  136. case 'SERVER_SEES_PK_CHECK':
  137. assert.equal(method, 'publickey');
  138. assert.equal(data.signature, undefined);
  139. return process.nextTick(function() {
  140. server.authPKOK(data.keyAlgo, data.key);
  141. });
  142. case 'SERVER_SEES_PK_REQUEST':
  143. assert.equal(method, 'publickey');
  144. assert.notEqual(data.signature, undefined);
  145. return process.nextTick(function() {
  146. server.authFailure(
  147. ['publickey', 'password', 'keyboard-interactive'],
  148. false
  149. );
  150. });
  151. case 'SERVER_SEES_PASSWORD':
  152. assert.equal(method, 'password');
  153. assert.equal(data, 'seekrit');
  154. return process.nextTick(function() {
  155. server.authFailure(
  156. ['publickey', 'password', 'keyboard-interactive'],
  157. false
  158. );
  159. });
  160. case 'SERVER_SEES_KEYBOARD_INTERACTIVE':
  161. assert.equal(method, 'keyboard-interactive');
  162. assert.equal(data, '');
  163. return process.nextTick(function() {
  164. server.authInfoReq('Password required', 'Password prompt', [
  165. { prompt: 'Password:', echo: false }
  166. ]);
  167. });
  168. default:
  169. throw new Error('Server saw USERAUTH_REQUEST ' + method +
  170. ' but expected ' + expectedStage);
  171. }
  172. }).on('USERAUTH_INFO_RESPONSE', function(responses) {
  173. assert.equal(expectedStages.shift(), 'SERVER_SEES_KEYBOARD_RES');
  174. assert.deepEqual(responses, [ 'seekrit' ]);
  175. process.nextTick(function() {
  176. server.authSuccess();
  177. });
  178. });
  179. client.on('USERAUTH_PK_OK', function() {
  180. assert.equal(expectedStages.shift(), 'CLIENT_SEES_PK_OK');
  181. }).on('USERAUTH_FAILURE', function() {
  182. var expectedStage = expectedStages.shift();
  183. if (expectedStage !== 'CLIENT_SEES_USERAUTH_FAILURE_PK' &&
  184. expectedStage !== 'CLIENT_SEES_USERAUTH_FAILURE_PASSWORD') {
  185. throw new Error('Client saw USERAUTH_FAILURE but expected ' +
  186. expectedStage);
  187. }
  188. }).on('USERAUTH_INFO_REQUEST', function(name, inst, lang, prompts) {
  189. assert.equal(expectedStages.shift(), 'CLIENT_SEES_KEYBOARD_REQ');
  190. assert.equal(name, 'Password required');
  191. assert.equal(inst, 'Password prompt');
  192. assert.equal(lang, '');
  193. assert.deepEqual(prompts, [ { prompt: 'Password:', echo: false } ]);
  194. process.nextTick(function() {
  195. client.authInfoRes([ 'seekrit' ]);
  196. });
  197. }).on('USERAUTH_SUCCESS', function() {
  198. assert.equal(expectedStages.shift(), 'CLIENT_SEES_USERAUTH_SUCCESS');
  199. assert.equal(expectedStages.shift(), undefined);
  200. next();
  201. });
  202. // Silly to submit all these auths at once, but allowed by RFC4252
  203. client.authPK('bob', PARSED_CLIENT_KEY);
  204. client.authPK('bob', PARSED_CLIENT_KEY, signWithClientKey);
  205. client.authPassword('bob', 'seekrit');
  206. client.authKeyboard('bob');
  207. }
  208. var tests = [
  209. publickey,
  210. keyboardInteractive,
  211. // password // ssh2-streams can't generate a password change request
  212. mixedMethods
  213. ];
  214. function next() {
  215. if (Array.isArray(process._events.exit))
  216. process._events.exit = process._events.exit[1];
  217. if (++t === tests.length)
  218. return;
  219. var v = tests[t];
  220. makePair(v);
  221. }
  222. process.once('exit', function() {
  223. assert(t === tests.length,
  224. 'Only finished ' + t + '/' + tests.length + ' tests');
  225. });
  226. next();