Check that filesystem id didn't change since we loaded the basedir the last time
This commit is contained in:
parent
26b3b366c9
commit
aace4c2f13
@ -21,6 +21,8 @@
|
|||||||
#include "VersionChecker.h"
|
#include "VersionChecker.h"
|
||||||
#include <gitversion/VersionCompare.h>
|
#include <gitversion/VersionCompare.h>
|
||||||
#include <cpp-utils/io/NoninteractiveConsole.h>
|
#include <cpp-utils/io/NoninteractiveConsole.h>
|
||||||
|
#include <cryfs/localstate/LocalStateDir.h>
|
||||||
|
#include <cryfs/localstate/BasedirMetadata.h>
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
|
|
||||||
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
|
//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;
|
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) {
|
CryConfigLoader::ConfigLoadResult Cli::_loadOrCreateConfig(const ProgramOptions &options) {
|
||||||
try {
|
try {
|
||||||
auto configFile = _determineConfigFile(options);
|
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;
|
std::cerr << "Could not load config file. Did you enter the correct password?" << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
_checkConfigIntegrity(options.baseDir(), config->configFile);
|
||||||
return std::move(*config);
|
return std::move(*config);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
@ -23,6 +23,7 @@ namespace cryfs {
|
|||||||
void _checkForUpdates();
|
void _checkForUpdates();
|
||||||
void _runFilesystem(const program_options::ProgramOptions &options);
|
void _runFilesystem(const program_options::ProgramOptions &options);
|
||||||
CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(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::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);
|
boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options);
|
||||||
static std::string _askPasswordForExistingFilesystem();
|
static std::string _askPasswordForExistingFilesystem();
|
||||||
|
@ -42,6 +42,7 @@ set(LIB_SOURCES
|
|||||||
filesystem/CryDevice.cpp
|
filesystem/CryDevice.cpp
|
||||||
localstate/LocalStateDir.cpp
|
localstate/LocalStateDir.cpp
|
||||||
localstate/LocalStateMetadata.cpp
|
localstate/LocalStateMetadata.cpp
|
||||||
|
localstate/BasedirMetadata.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})
|
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})
|
||||||
|
82
src/cryfs/localstate/BasedirMetadata.cpp
Normal file
82
src/cryfs/localstate/BasedirMetadata.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
src/cryfs/localstate/BasedirMetadata.h
Normal file
18
src/cryfs/localstate/BasedirMetadata.h
Normal 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
|
@ -5,19 +5,36 @@
|
|||||||
namespace bf = boost::filesystem;
|
namespace bf = boost::filesystem;
|
||||||
|
|
||||||
namespace cryfs {
|
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 LocalStateDir::forFilesystemId(const CryConfig::FilesystemID &filesystemId) {
|
||||||
bf::path app_dir = cpputils::system::HomeDirectory::get() / ".cryfs";
|
_createDirIfNotExists(appDir());
|
||||||
_createDirIfNotExists(app_dir);
|
bf::path filesystems_dir = appDir() / "filesystems";
|
||||||
bf::path filesystems_dir = app_dir / "filesystems";
|
|
||||||
_createDirIfNotExists(filesystems_dir);
|
_createDirIfNotExists(filesystems_dir);
|
||||||
bf::path this_filesystem_dir = filesystems_dir / filesystemId.ToString();
|
bf::path this_filesystem_dir = filesystems_dir / filesystemId.ToString();
|
||||||
_createDirIfNotExists(this_filesystem_dir);
|
_createDirIfNotExists(this_filesystem_dir);
|
||||||
return 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) {
|
void LocalStateDir::_createDirIfNotExists(const bf::path &path) {
|
||||||
if (!bf::exists(path)) {
|
if (!bf::exists(path)) {
|
||||||
bf::create_directory(path);
|
bf::create_directories(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalStateDir::setAppDir(boost::filesystem::path path) {
|
||||||
|
appDir() = std::move(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,11 @@ namespace cryfs {
|
|||||||
class LocalStateDir final {
|
class LocalStateDir final {
|
||||||
public:
|
public:
|
||||||
static boost::filesystem::path forFilesystemId(const CryConfig::FilesystemID &filesystemId);
|
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:
|
private:
|
||||||
LocalStateDir(); // static functions only
|
LocalStateDir(); // static functions only
|
||||||
|
@ -67,7 +67,7 @@ optional<uint32_t> _tryLoadClientIdFromLegacyFile(const bf::path &metadataFilePa
|
|||||||
uint32_t value;
|
uint32_t value;
|
||||||
file >> value;
|
file >> value;
|
||||||
file.close();
|
file.close();
|
||||||
//bf::remove(myClientIdFile);
|
bf::remove(myClientIdFile);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,7 +17,8 @@ set(SOURCES
|
|||||||
filesystem/CryFsTest.cpp
|
filesystem/CryFsTest.cpp
|
||||||
filesystem/CryNodeTest.cpp
|
filesystem/CryNodeTest.cpp
|
||||||
filesystem/FileSystemTest.cpp
|
filesystem/FileSystemTest.cpp
|
||||||
localstate/LocalStateMetadataTest.cpp
|
localstate/LocalStateMetadataTest.cpp
|
||||||
|
localstate/BasedirMetadataTest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||||
|
67
test/cryfs/localstate/BasedirMetadataTest.cpp
Normal file
67
test/cryfs/localstate/BasedirMetadataTest.cpp
Normal 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));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user