diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp index 9fec2ae..6ae343c 100644 --- a/encfs/DirNode.cpp +++ b/encfs/DirNode.cpp @@ -254,13 +254,9 @@ DirNode::DirNode(EncFS_Context *_ctx, const string &sourceDir, Lock _lock(mutex); ctx = _ctx; - rootDir = sourceDir; + rootDir = sourceDir; // .. and fsConfig->opts->mountPoint have trailing slash fsConfig = _config; - // make sure rootDir ends in '/', so that we can form a path by appending - // the rest.. - if (rootDir[rootDir.length() - 1] != '/') rootDir.append(1, '/'); - naming = fsConfig->nameCoding; } @@ -277,6 +273,27 @@ string DirNode::rootDirectory() { return string(rootDir, 0, rootDir.length() - 1); } +bool DirNode::touchesMountpoint( const char *realPath ) const { + const string &mountPoint = fsConfig->opts->mountPoint; + // compare mountPoint up to the leading slash. + // examples: + // mountPoint = /home/user/Junk/experiment/ + // realPath = /home/user/Junk/experiment + // realPath = /home/user/Junk/experiment/abc + const ssize_t len = mountPoint.length() - 1; + + if (mountPoint.compare(0, len, realPath, len) == 0) { + // if next character is a NUL or a slash, then we're referencing our + // mount point: + // .../experiment => true + // .../experiment/... => true + // .../experiment2/abc => false + return realPath[len] == '\0' || realPath[len] == '/'; + } + + return false; +} + /** * Encrypt a plain-text file path to the ciphertext path with the * ciphertext root directory name prefixed. diff --git a/encfs/DirNode.h b/encfs/DirNode.h index 9829dcf..5257fc9 100644 --- a/encfs/DirNode.h +++ b/encfs/DirNode.h @@ -86,6 +86,9 @@ class DirNode { // return the path to the root directory std::string rootDirectory(); + // recursive lookup check + bool touchesMountpoint(const char *realPath) const; + // find files shared_ptr lookupNode(const char *plaintextName, const char *requestor); diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index ced272f..4928e47 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -69,6 +69,7 @@ enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia }; */ struct EncFS_Opts { std::string rootDir; + std::string mountPoint; // where to make filesystem visible bool createIfNotFound; // create filesystem if not found bool idleTracking; // turn on idle monitoring of filesystem bool mountOnDemand; // mounting on-demand diff --git a/encfs/encfs.cpp b/encfs/encfs.cpp index f128834..de4eac1 100644 --- a/encfs/encfs.cpp +++ b/encfs/encfs.cpp @@ -139,6 +139,14 @@ static int withFileNode(const char *opName, const char *path, rAssert(fnode.get() != NULL); rLog(Info, "%s %s", opName, fnode->cipherName()); + + // check that we're not recursing into the mount point itself + if (FSRoot->touchesMountpoint(fnode->cipherName())) { + rInfo("%s error: Tried to touch mountpoint: '%s'", + opName, fnode->cipherName()); + return res; // still -EIO + } + res = op(fnode.get()); if (res < 0) rInfo("%s error: %s", opName, strerror(-res)); diff --git a/encfs/main.cpp b/encfs/main.cpp index adcd08b..6b4365a 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -74,7 +74,6 @@ const int MaxFuseArgs = 32; * derived from the arguments */ struct EncFS_Args { - string mountPoint; // where to make filesystem visible bool isDaemon; // true == spawn in background, log to syslog bool isThreaded; // true == threaded bool isVerbose; // false == only enable warning/error messages @@ -373,8 +372,10 @@ static bool processArgs(int argc, char *argv[], // we should have at least 2 arguments left over - the source directory and // the mount point. if (optind + 2 <= argc) { + // both rootDir and mountPoint are assumed to be slash terminated in the + // rest of the code. out->opts->rootDir = slashTerminate(argv[optind++]); - out->mountPoint = argv[optind++]; + out->opts->mountPoint = slashTerminate(argv[optind++]); } else { // no mount point specified rWarning(_("Missing one or more arguments, aborting.")); @@ -420,7 +421,7 @@ static bool processArgs(int argc, char *argv[], } // sanity check - if (out->isDaemon && (!isAbsolutePath(out->mountPoint.c_str()) || + if (out->isDaemon && (!isAbsolutePath(out->opts->mountPoint.c_str()) || !isAbsolutePath(out->opts->rootDir.c_str()))) { cerr << // xgroup(usage) @@ -431,7 +432,7 @@ static bool processArgs(int argc, char *argv[], // the raw directory may not be a subdirectory of the mount point. { - string testMountPoint = slashTerminate(out->mountPoint); + string testMountPoint = out->opts->mountPoint; string testRootDir = out->opts->rootDir.substr(0, testMountPoint.length()); if (testMountPoint == testRootDir) { @@ -464,15 +465,15 @@ static bool processArgs(int argc, char *argv[], rWarning(_("Unable to locate root directory, aborting.")); return false; } - if (!isDirectory(out->mountPoint.c_str()) && - !userAllowMkdir(out->opts->annotate ? 2 : 0, out->mountPoint.c_str(), + if (!isDirectory(out->opts->mountPoint.c_str()) && + !userAllowMkdir(out->opts->annotate ? 2 : 0, out->opts->mountPoint.c_str(), 0700)) { rWarning(_("Unable to locate mount point, aborting.")); return false; } // fill in mount path for fuse - out->fuseArgv[1] = out->mountPoint.c_str(); + out->fuseArgv[1] = out->opts->mountPoint.c_str(); return true; } @@ -767,7 +768,7 @@ static bool unmountFS(EncFS_Context *ctx) { shared_ptr arg = ctx->args; if (arg->opts->mountOnDemand) { rDebug("Detaching filesystem %s due to inactivity", - arg->mountPoint.c_str()); + arg->opts->mountPoint.c_str()); ctx->setRoot(shared_ptr()); return false; @@ -775,8 +776,8 @@ static bool unmountFS(EncFS_Context *ctx) { // Time to unmount! // xgroup(diag) rWarning(_("Unmounting filesystem %s due to inactivity"), - arg->mountPoint.c_str()); - fuse_unmount(arg->mountPoint.c_str()); + arg->opts->mountPoint.c_str()); + fuse_unmount(arg->opts->mountPoint.c_str()); return true; } }