sftp-server-download-only.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. var crypto = require('crypto');
  2. var constants = require('constants');
  3. var fs = require('fs');
  4. var ssh2 = require('ssh2');
  5. var OPEN_MODE = ssh2.SFTP_OPEN_MODE;
  6. var STATUS_CODE = ssh2.SFTP_STATUS_CODE;
  7. var allowedUser = Buffer.from('foo');
  8. var allowedPassword = Buffer.from('bar');
  9. new ssh2.Server({
  10. hostKeys: [fs.readFileSync('host.key')]
  11. }, function(client) {
  12. console.log('Client connected!');
  13. client.on('authentication', function(ctx) {
  14. var user = Buffer.from(ctx.username);
  15. if (user.length !== allowedUser.length
  16. || !crypto.timingSafeEqual(user, allowedUser)) {
  17. return ctx.reject(['password']);
  18. }
  19. switch (ctx.method) {
  20. case 'password':
  21. var password = Buffer.from(ctx.password);
  22. if (password.length !== allowedPassword.length
  23. || !crypto.timingSafeEqual(password, allowedPassword)) {
  24. return ctx.reject(['password']);
  25. }
  26. break;
  27. default:
  28. return ctx.reject(['password']);
  29. }
  30. ctx.accept();
  31. }).on('ready', function() {
  32. console.log('Client authenticated!');
  33. client.on('session', function(accept, reject) {
  34. var session = accept();
  35. session.on('sftp', function(accept, reject) {
  36. console.log('Client SFTP session');
  37. var openFiles = {};
  38. var handleCount = 0;
  39. // `sftpStream` is an `SFTPStream` instance in server mode
  40. // see: https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md
  41. var sftpStream = accept();
  42. sftpStream.on('OPEN', function(reqid, filename, flags, attrs) {
  43. console.log('OPEN', filename);
  44. // only allow opening /tmp/foo.txt for writing
  45. if (filename !== '/tmp/foo.txt' || !(flags & OPEN_MODE.READ))
  46. return sftpStream.status(reqid, STATUS_CODE.FAILURE);
  47. // create a fake handle to return to the client, this could easily
  48. // be a real file descriptor number for example if actually opening
  49. // the file on the disk
  50. var handle = new Buffer(4);
  51. openFiles[handleCount] = { read: false };
  52. handle.writeUInt32BE(handleCount++, 0, true);
  53. sftpStream.handle(reqid, handle);
  54. console.log('Opening file for read')
  55. }).on('READ', function(reqid, handle, offset, length) {
  56. if (handle.length !== 4 || !openFiles[handle.readUInt32BE(0, true)])
  57. return sftpStream.status(reqid, STATUS_CODE.FAILURE);
  58. // fake the read
  59. var state = openFiles[handle.readUInt32BE(0, true)];
  60. if (state.read)
  61. sftpStream.status(reqid, STATUS_CODE.EOF);
  62. else {
  63. state.read = true;
  64. sftpStream.data(reqid, 'bar');
  65. console.log('Read from file at offset %d, length %d', offset, length);
  66. }
  67. }).on('CLOSE', function(reqid, handle) {
  68. var fnum;
  69. if (handle.length !== 4 || !openFiles[(fnum = handle.readUInt32BE(0, true))])
  70. return sftpStream.status(reqid, STATUS_CODE.FAILURE);
  71. delete openFiles[fnum];
  72. sftpStream.status(reqid, STATUS_CODE.OK);
  73. console.log('Closing file');
  74. }).on('REALPATH', function(reqid, path) {
  75. var name = [{
  76. filename: '/tmp/foo.txt',
  77. longname: '-rwxrwxrwx 1 foo foo 3 Dec 8 2009 foo.txt',
  78. attrs: {}
  79. }];
  80. sftpStream.name(reqid, name);
  81. }).on('STAT', onSTAT)
  82. .on('LSTAT', onSTAT);
  83. function onSTAT(reqid, path) {
  84. if (path !== '/tmp/foo.txt')
  85. return sftpStream.status(reqid, STATUS_CODE.FAILURE);
  86. var mode = constants.S_IFREG; // Regular file
  87. mode |= constants.S_IRWXU; // read, write, execute for user
  88. mode |= constants.S_IRWXG; // read, write, execute for group
  89. mode |= constants.S_IRWXO; // read, write, execute for other
  90. sftpStream.attrs(reqid, {
  91. mode: mode,
  92. uid: 0,
  93. gid: 0,
  94. size: 3,
  95. atime: Date.now(),
  96. mtime: Date.now()
  97. });
  98. }
  99. });
  100. });
  101. }).on('end', function() {
  102. console.log('Client disconnected');
  103. });
  104. }).listen(0, '127.0.0.1', function() {
  105. console.log('Listening on port ' + this.address().port);
  106. });