From 8312892d1e595d0889e576d3b4742e94f417c4fc Mon Sep 17 00:00:00 2001 From: "fabien@gouban.fr" Date: Mon, 23 Mar 2015 17:25:20 +0100 Subject: [PATCH 1/2] Fix bug #39 PWD on windows gives bad path --- lib/ftpd.js | 25 +++++++++++++------------ package.json | 6 +++++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/ftpd.js b/lib/ftpd.js index 4b6dd17..52da6c5 100644 --- a/lib/ftpd.js +++ b/lib/ftpd.js @@ -36,7 +36,7 @@ function withCwd(cwd, path) { if (firstChar === '/' || firstChar === pathModule.sep) { cwd = pathModule.sep; } - path = pathModule.join(pathModule.sep, cwd, path); + path = pathModule.join(cwd, path); return path; } @@ -509,7 +509,7 @@ FtpConnection.prototype._command_CDUP = function () { */ FtpConnection.prototype._command_CWD = function (pathRequest) { var pathServer = withCwd(this.cwd, pathRequest); - var pathFs = pathModule.join(this.root, pathServer); + var pathFs = pathServer; var pathEscaped = pathEscape(pathServer); this.fs.stat(pathFs, function (err, stats) { if (err) { @@ -530,7 +530,7 @@ FtpConnection.prototype._command_DELE = function(commandArg) { var self = this; var filename = withCwd(self.cwd, commandArg); - self.fs.unlink(pathModule.join(self.root, filename), function(err) { + self.fs.unlink(filename, function(err) { if (err) { self._logIf(0, "Error deleting file: " + filename + ", " + err); // write error to socket @@ -563,7 +563,7 @@ FtpConnection.prototype._command_FEAT = function(commandArg) { */ FtpConnection.prototype._command_MDTM = function (file) { file = withCwd(this.cwd, file); - file = pathModule.join(this.root, file); + this.fs.stat(file, function (err, stats) { if (err) { this.respond("550 File unavailable"); @@ -610,7 +610,8 @@ FtpConnection.prototype._LIST = function(commandArg, detailed, cmd) { var dir = withCwd(self.cwd, dirname); glob.setMaxStatsAtOnce(self.server.options.maxStatsAtOnce); - glob.glob(pathModule.join(self.root, dir), self.fs, function(err, files) { + + glob.glob(dir, self.fs, function(err, files) { if (err) { self._logIf(0, "While sending file list, reading directory: " + err); self.respond("550 Not a directory"); @@ -775,7 +776,7 @@ FtpConnection.prototype._listFiles = function(fileInfos, detailed, cmd) { FtpConnection.prototype._command_MKD = function (pathRequest) { var pathServer = withCwd(this.cwd, pathRequest); var pathEscaped = pathEscape(pathServer); - var pathFs = pathModule.join(this.root, pathServer); + var pathFs = pathServer; this.fs.mkdir(pathFs, 0755, function (err) { if (err) { this._logIf(0, 'ERROR: MKD ' + pathRequest + ': ' + err); @@ -1041,7 +1042,7 @@ FtpConnection.prototype._command_QUIT = function(commandArg) { }; FtpConnection.prototype._command_RETR = function(commandArg) { - var filename = pathModule.join(this.root, withCwd(this.cwd, commandArg)); + var filename = withCwd(this.cwd, commandArg); if (this.server.options.useReadFile) this._RETR_usingReadFile(commandArg, filename); @@ -1177,7 +1178,7 @@ FtpConnection.prototype._RETR_usingReadFile = function(commandArg, filename) { */ FtpConnection.prototype._command_RMD = function (pathRequest) { var pathServer = withCwd(this.cwd, pathRequest); - var pathFs = pathModule.join(this.root, pathServer); + var pathFs = pathServer; this.fs.rmdir(pathFs, function (err) { if (err) { this._logIf(0, 'ERROR: RMD ' + pathRequest + ': ' + err); @@ -1201,7 +1202,7 @@ FtpConnection.prototype._command_RNTO = function(commandArg) { var self = this; var fileto = withCwd(self.cwd, commandArg); - self.fs.rename(pathModule.join(self.root, self.filefrom), pathModule.join(self.root, fileto), function(err) { + self.fs.rename(self.filefrom, fileto, function(err) { if (err) { self._logIf(3, "Error renaming file from " + self.filefrom + " to " + fileto); self.respond("550 Rename failed" + ( err.code == 'ENOENT' ? "; file does not exist" : "" )); @@ -1215,7 +1216,7 @@ FtpConnection.prototype._command_SIZE = function(commandArg) { var self = this; var filename = withCwd(self.cwd, commandArg); - self.fs.stat(pathModule.join(self.root, filename), function(err, s) { + self.fs.stat(filename, function(err, s) { if (err) { self._traceIf(0, "Error getting size of file '" + filename + "' ", self.socket); self.respond("450 Failed to get size of file"); @@ -1250,7 +1251,7 @@ FtpConnection.prototype._STOR_usingCreateWriteStream = function(filename, initia var self = this; var wStreamFlags = {flags: "w", mode: 0644}; - var storeStream = self.fs.createWriteStream(pathModule.join(self.root, filename), wStreamFlags); + var storeStream = self.fs.createWriteStream(filename, wStreamFlags); var notErr = true; // Adding for event metadata for file upload (STOR) var startTime = new Date(); @@ -1408,7 +1409,7 @@ FtpConnection.prototype._STOR_usingWriteFile = function(filename) { var contents = {filename: filename, data: slurpBuf.slice(0, totalBytes)}; self.emit("file:stor:contents", contents); - self.fs.writeFile(pathModule.join(self.root, filename), contents.data, function(err) { + self.fs.writeFile(filename, contents.data, function(err) { self.emit("file:stor", "close", { user: self.username, file: filename, diff --git a/package.json b/package.json index 6aaf3b9..2dc2ea7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ftpd", - "version": "0.2.11", + "version": "0.2.12", "description": "Node FTP Server", "main": "./lib/ftpd.js", "scripts": { @@ -48,6 +48,10 @@ { "name": "asylumfunk", "url": "https://github.com/asylumfunk" + }, + { + "name": "fabiengb", + "url": "https://github.com/fabiengb" } ], "license": "BSD-2-Clause", From 8a7ff07a20f2bbc5f8659e844e2976d00d5b7544 Mon Sep 17 00:00:00 2001 From: "fabien@gouban.fr" Date: Tue, 24 Mar 2015 12:10:05 +0100 Subject: [PATCH 2/2] Add virtual root for user --- lib/ftpd.js | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/lib/ftpd.js b/lib/ftpd.js index 52da6c5..7e35e8b 100644 --- a/lib/ftpd.js +++ b/lib/ftpd.js @@ -24,8 +24,16 @@ var starttls = require('./starttls'); - maybe just for milesplit's use? */ -function pathEscape(text) { - text = text.replace(/\"/g, '""'); +function pathEscape(text) { + text = text.replace(/\"/g, '""').replace('\\', '/'); + var slashIndex = text.indexOf("/"); + + // Format path output for client + if(slashIndex != -1) + text = text.substring(slashIndex, text.length); + else + text = "/"; + return text; } @@ -494,7 +502,7 @@ FtpConnection.prototype._command_AUTH = function(commandArg) { * Change working directory to parent directory * @return {FtpConnection} this */ -FtpConnection.prototype._command_CDUP = function () { +FtpConnection.prototype._command_CDUP = function () { var pathServer = pathModule.dirname(this.cwd); var pathEscaped = pathEscape(pathServer); this.cwd = pathServer; @@ -509,7 +517,13 @@ FtpConnection.prototype._command_CDUP = function () { */ FtpConnection.prototype._command_CWD = function (pathRequest) { var pathServer = withCwd(this.cwd, pathRequest); - var pathFs = pathServer; + + // Prevent user from getting upper to his virtual root + if((pathRequest === '..') && (pathServer === '.')) + pathServer = this.cwd; + + var pathFs = pathModule.join(this.root, pathServer); + var pathEscaped = pathEscape(pathServer); this.fs.stat(pathFs, function (err, stats) { if (err) { @@ -530,7 +544,8 @@ FtpConnection.prototype._command_DELE = function(commandArg) { var self = this; var filename = withCwd(self.cwd, commandArg); - self.fs.unlink(filename, function(err) { + + self.fs.unlink(pathModule.join(self.root, filename), function(err) { if (err) { self._logIf(0, "Error deleting file: " + filename + ", " + err); // write error to socket @@ -563,6 +578,7 @@ FtpConnection.prototype._command_FEAT = function(commandArg) { */ FtpConnection.prototype._command_MDTM = function (file) { file = withCwd(this.cwd, file); + file = pathModule.join(this.root, file); this.fs.stat(file, function (err, stats) { if (err) { @@ -611,7 +627,7 @@ FtpConnection.prototype._LIST = function(commandArg, detailed, cmd) { glob.setMaxStatsAtOnce(self.server.options.maxStatsAtOnce); - glob.glob(dir, self.fs, function(err, files) { + glob.glob(pathModule.join(self.root, dir), self.fs, function(err, files) { if (err) { self._logIf(0, "While sending file list, reading directory: " + err); self.respond("550 Not a directory"); @@ -776,7 +792,8 @@ FtpConnection.prototype._listFiles = function(fileInfos, detailed, cmd) { FtpConnection.prototype._command_MKD = function (pathRequest) { var pathServer = withCwd(this.cwd, pathRequest); var pathEscaped = pathEscape(pathServer); - var pathFs = pathServer; + + var pathFs = pathModule.join(this.root, pathServer); this.fs.mkdir(pathFs, 0755, function (err) { if (err) { this._logIf(0, 'ERROR: MKD ' + pathRequest + ': ' + err); @@ -1042,7 +1059,7 @@ FtpConnection.prototype._command_QUIT = function(commandArg) { }; FtpConnection.prototype._command_RETR = function(commandArg) { - var filename = withCwd(this.cwd, commandArg); + var filename = pathModule.join(this.root, withCwd(this.cwd, commandArg)); if (this.server.options.useReadFile) this._RETR_usingReadFile(commandArg, filename); @@ -1178,7 +1195,7 @@ FtpConnection.prototype._RETR_usingReadFile = function(commandArg, filename) { */ FtpConnection.prototype._command_RMD = function (pathRequest) { var pathServer = withCwd(this.cwd, pathRequest); - var pathFs = pathServer; + var pathFs = pathModule.join(this.root, pathServer); this.fs.rmdir(pathFs, function (err) { if (err) { this._logIf(0, 'ERROR: RMD ' + pathRequest + ': ' + err); @@ -1202,7 +1219,7 @@ FtpConnection.prototype._command_RNTO = function(commandArg) { var self = this; var fileto = withCwd(self.cwd, commandArg); - self.fs.rename(self.filefrom, fileto, function(err) { + self.fs.rename(pathModule.join(self.root, self.filefrom), pathModule.join(self.root, fileto), function(err) { if (err) { self._logIf(3, "Error renaming file from " + self.filefrom + " to " + fileto); self.respond("550 Rename failed" + ( err.code == 'ENOENT' ? "; file does not exist" : "" )); @@ -1216,7 +1233,7 @@ FtpConnection.prototype._command_SIZE = function(commandArg) { var self = this; var filename = withCwd(self.cwd, commandArg); - self.fs.stat(filename, function(err, s) { + self.fs.stat(pathModule.join(self.root, filename), function(err, s) { if (err) { self._traceIf(0, "Error getting size of file '" + filename + "' ", self.socket); self.respond("450 Failed to get size of file"); @@ -1251,7 +1268,7 @@ FtpConnection.prototype._STOR_usingCreateWriteStream = function(filename, initia var self = this; var wStreamFlags = {flags: "w", mode: 0644}; - var storeStream = self.fs.createWriteStream(filename, wStreamFlags); + var storeStream = self.fs.createWriteStream(pathModule.join(self.root, filename), wStreamFlags); var notErr = true; // Adding for event metadata for file upload (STOR) var startTime = new Date(); @@ -1409,7 +1426,7 @@ FtpConnection.prototype._STOR_usingWriteFile = function(filename) { var contents = {filename: filename, data: slurpBuf.slice(0, totalBytes)}; self.emit("file:stor:contents", contents); - self.fs.writeFile(filename, contents.data, function(err) { + self.fs.writeFile(pathModule.join(self.root, filename), contents.data, function(err) { self.emit("file:stor", "close", { user: self.username, file: filename, @@ -1486,13 +1503,13 @@ FtpConnection.prototype._command_PASS = function (password) { } function setCwd(cwd) { function setRoot(root) { - self.root = root; + self.root = root; self.fs = userFsModule || fsModule; self.respond('230 User logged in, proceed.'); } self.cwd = cwd; - if (self.server.getRoot.length <= 1) { + if (self.server.getRoot.length <= 1) { setRoot(self.server.getRoot(self)); } else { @@ -1500,7 +1517,7 @@ FtpConnection.prototype._command_PASS = function (password) { if (err) { panic(err, 'getRoot'); } - else { + else { setRoot(root); } }); @@ -1515,7 +1532,7 @@ FtpConnection.prototype._command_PASS = function (password) { if (err) { panic(err, 'getInitialCwd'); } - else { + else { setCwd(withCwd(cwd)); } });