Check that filesystem id didn't change since we loaded the basedir the last time

This commit is contained in:
Sebastian Messmer 2017-09-28 07:41:08 +01:00
parent 26b3b366c9
commit aace4c2f13
10 changed files with 212 additions and 6 deletions

View File

@ -21,6 +21,8 @@
#include "VersionChecker.h"
#include <gitversion/VersionCompare.h>
#include <cpp-utils/io/NoninteractiveConsole.h>
#include <cryfs/localstate/LocalStateDir.h>
#include <cryfs/localstate/BasedirMetadata.h>
#include "Environment.h"
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
@ -195,6 +197,17 @@ namespace cryfs {
return *configFile;
}
void Cli::_checkConfigIntegrity(const bf::path& basedir, const CryConfigFile& config) {
if (!BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir, config.config()->FilesystemId())) {
if (!_console->askYesNo("The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir. This can be genuine if you replaced the filesystem with a different one. If you didn't do that, it is possible that an attacker did. Do you want to continue loading the file system?", false)) {
throw std::runtime_error(
"The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir.");
}
}
// Update local state (or create it if it didn't exist yet)
BasedirMetadata::updateFilesystemIdForBasedir(basedir, config.config()->FilesystemId());
}
CryConfigLoader::ConfigLoadResult Cli::_loadOrCreateConfig(const ProgramOptions &options) {
try {
auto configFile = _determineConfigFile(options);
@ -203,6 +216,7 @@ namespace cryfs {
std::cerr << "Could not load config file. Did you enter the correct password?" << std::endl;
exit(1);
}
_checkConfigIntegrity(options.baseDir(), config->configFile);
return std::move(*config);
} catch (const std::exception &e) {
std::cerr << "Error: " << e.what() << std::endl;

View File

@ -23,6 +23,7 @@ namespace cryfs {
void _checkForUpdates();
void _runFilesystem(const program_options::ProgramOptions &options);
CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options);
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const CryConfigFile& config);
boost::optional<CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(const boost::filesystem::path &configFilePath, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, const boost::optional<bool> &missingBlockIsIntegrityViolation);
boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options);
static std::string _askPasswordForExistingFilesystem();

View File

@ -42,6 +42,7 @@ set(LIB_SOURCES
filesystem/CryDevice.cpp
localstate/LocalStateDir.cpp
localstate/LocalStateMetadata.cpp
localstate/BasedirMetadata.cpp
)
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})

View File

@ -0,0 +1,82 @@
#include "BasedirMetadata.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cryptopp/sha.h>
#include <boost/filesystem/operations.hpp>
#include "LocalStateDir.h"
namespace bf = boost::filesystem;
using boost::property_tree::ptree;
using boost::property_tree::write_json;
using boost::property_tree::read_json;
using boost::optional;
using boost::none;
using std::ostream;
using std::istream;
using std::ifstream;
using std::ofstream;
namespace cryfs {
namespace {
bf::path _localStateConfigFile(const bf::path& basedir) {
std::string basedir_id;
CryptoPP::SHA512 hash;
CryptoPP::StringSource(bf::canonical(basedir).native(), true,
new CryptoPP::HashFilter(hash,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(basedir_id)
)
)
);
return LocalStateDir::forMapFromBasedirToConfigFiles() / basedir_id;
}
void _serialize(ostream& stream, const CryConfig::FilesystemID& filesystemId) {
ptree pt;
pt.put<std::string>("filesystemId", filesystemId.ToString());
write_json(stream, pt);
}
CryConfig::FilesystemID _deserialize(istream& stream) {
ptree pt;
read_json(stream, pt);
std::string filesystemId = pt.get<std::string>("filesystemId");
return CryConfig::FilesystemID::FromString(filesystemId);
}
optional<CryConfig::FilesystemID> _load(const bf::path &metadataFilePath) {
ifstream file(metadataFilePath.native());
if (!file.good()) {
// State file doesn't exist
return none;
}
return _deserialize(file);
}
void _save(const bf::path &metadataFilePath, const CryConfig::FilesystemID& filesystemId) {
ofstream file(metadataFilePath.native(), std::ios::trunc);
_serialize(file, filesystemId);
}
}
bool BasedirMetadata::filesystemIdForBasedirIsCorrect(const bf::path &basedir, const CryConfig::FilesystemID &filesystemId) {
auto metadataFile = _localStateConfigFile(basedir);
auto loaded = _load(metadataFile);
if (loaded == none) {
// Local state not known. Possibly the file system is currently being created.
return true;
}
return loaded == filesystemId;
}
void BasedirMetadata::updateFilesystemIdForBasedir(const bf::path &basedir, const CryConfig::FilesystemID &filesystemId) {
auto metadataFile = _localStateConfigFile(basedir);
_save(metadataFile, filesystemId);
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#ifndef MESSMER_CRYFS_LOCALSTATE_BASEDIRMETADATA_H_
#define MESSMER_CRYFS_LOCALSTATE_BASEDIRMETADATA_H_
#include <boost/filesystem/path.hpp>
#include "../config/CryConfig.h"
namespace cryfs {
class BasedirMetadata final {
public:
static bool filesystemIdForBasedirIsCorrect(const boost::filesystem::path &basedir, const CryConfig::FilesystemID &filesystemId);
static void updateFilesystemIdForBasedir(const boost::filesystem::path &basedir, const CryConfig::FilesystemID &filesystemId);
};
}
#endif

View File

@ -5,19 +5,36 @@
namespace bf = boost::filesystem;
namespace cryfs {
namespace {
// TODO constexpr?
bf::path& appDir() {
static bf::path singleton = cpputils::system::HomeDirectory::get() / ".cryfs";
return singleton;
}
}
bf::path LocalStateDir::forFilesystemId(const CryConfig::FilesystemID &filesystemId) {
bf::path app_dir = cpputils::system::HomeDirectory::get() / ".cryfs";
_createDirIfNotExists(app_dir);
bf::path filesystems_dir = app_dir / "filesystems";
_createDirIfNotExists(appDir());
bf::path filesystems_dir = appDir() / "filesystems";
_createDirIfNotExists(filesystems_dir);
bf::path this_filesystem_dir = filesystems_dir / filesystemId.ToString();
_createDirIfNotExists(this_filesystem_dir);
return this_filesystem_dir;
}
bf::path LocalStateDir::forMapFromBasedirToConfigFiles() {
bf::path result = appDir() / "map_basedir_initialconfigfile";
_createDirIfNotExists(result);
return result;
}
void LocalStateDir::_createDirIfNotExists(const bf::path &path) {
if (!bf::exists(path)) {
bf::create_directory(path);
bf::create_directories(path);
}
}
void LocalStateDir::setAppDir(boost::filesystem::path path) {
appDir() = std::move(path);
}
}

View File

@ -11,6 +11,11 @@ namespace cryfs {
class LocalStateDir final {
public:
static boost::filesystem::path forFilesystemId(const CryConfig::FilesystemID &filesystemId);
static boost::filesystem::path forMapFromBasedirToConfigFiles();
// Use this from test cases to not pollute local config
// TODO Make test cases call this
static void setAppDir(boost::filesystem::path path);
private:
LocalStateDir(); // static functions only

View File

@ -67,7 +67,7 @@ optional<uint32_t> _tryLoadClientIdFromLegacyFile(const bf::path &metadataFilePa
uint32_t value;
file >> value;
file.close();
//bf::remove(myClientIdFile);
bf::remove(myClientIdFile);
return value;
}
#endif

View File

@ -17,7 +17,8 @@ set(SOURCES
filesystem/CryFsTest.cpp
filesystem/CryNodeTest.cpp
filesystem/FileSystemTest.cpp
localstate/LocalStateMetadataTest.cpp
localstate/LocalStateMetadataTest.cpp
localstate/BasedirMetadataTest.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})

View File

@ -0,0 +1,67 @@
#include <gtest/gtest.h>
#include <cryfs/localstate/BasedirMetadata.h>
#include <cryfs/localstate/LocalStateDir.h>
#include <cryfs/config/CryConfig.h>
#include <cpp-utils/tempfile/TempDir.h>
using cpputils::TempDir;
using cryfs::BasedirMetadata;
using std::ofstream;
namespace bf = boost::filesystem;
using FilesystemID = cryfs::CryConfig::FilesystemID ;
class BasedirMetadataTest : public ::testing::Test {
public:
TempDir tempdir;
bf::path basedir1;
bf::path basedir2;
const FilesystemID id1;
const FilesystemID id2;
BasedirMetadataTest()
: tempdir()
, basedir1(tempdir.path() / "my/basedir")
, basedir2(tempdir.path() / "my/other/basedir")
, id1(FilesystemID::FromString("1491BB4932A389EE14BC7090AC772972"))
, id2(FilesystemID::FromString("A1491BB493214BC7090C772972A389EE"))
{
// Use temporary local state dir to not pollute local state
cryfs::LocalStateDir::setAppDir(tempdir.path() / "appdir");
// Create basedirs so bf::canonical() works
bf::create_directories(basedir1);
bf::create_directories(basedir2);
}
};
TEST_F(BasedirMetadataTest, givenEmptyState_whenCalled_thenSucceeds) {
EXPECT_TRUE(BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithBasedir_whenCalledForDifferentBasedir_thenSucceeds) {
BasedirMetadata::updateFilesystemIdForBasedir(basedir2, id1);
EXPECT_TRUE(BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithBasedir_whenCalledWithSameId_thenSucceeds) {
BasedirMetadata::updateFilesystemIdForBasedir(basedir1, id1);
EXPECT_TRUE(BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithBasedir_whenCalledWithDifferentId_thenFails) {
BasedirMetadata::updateFilesystemIdForBasedir(basedir1, id2);
EXPECT_FALSE(BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithUpdatedBasedir_whenCalledWithSameId_thenSucceeds) {
BasedirMetadata::updateFilesystemIdForBasedir(basedir1, id2);
BasedirMetadata::updateFilesystemIdForBasedir(basedir1, id1);
EXPECT_TRUE(BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithUpdatedBasedir_whenCalledWithDifferentId_thenFails) {
BasedirMetadata::updateFilesystemIdForBasedir(basedir1, id2);
BasedirMetadata::updateFilesystemIdForBasedir(basedir1, id1);
EXPECT_FALSE(BasedirMetadata::filesystemIdForBasedirIsCorrect(basedir1, id2));
}