Allow disabling integrity checks on command line

This commit is contained in:
Sebastian Messmer 2017-09-16 16:18:04 +01:00
parent ab0a83917f
commit b6ce7c3ae4
11 changed files with 74 additions and 38 deletions

View File

@ -96,12 +96,16 @@ void IntegrityBlockStore2::_checkNoPastIntegrityViolations() const {
}
void IntegrityBlockStore2::integrityViolationDetected(const string &reason) const {
if (_noIntegrityChecks) {
LOG(WARN, "Integrity violation (but integrity checks are disabled): {}", reason);
return;
}
_integrityViolationDetected = true;
throw IntegrityViolationError(reason);
}
IntegrityBlockStore2::IntegrityBlockStore2(unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath, myClientId), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) {
IntegrityBlockStore2::IntegrityBlockStore2(unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool noIntegrityChecks, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath, myClientId), _noIntegrityChecks(noIntegrityChecks), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) {
}
bool IntegrityBlockStore2::tryCreate(const Key &key, const Data &data) {

View File

@ -16,7 +16,7 @@ namespace integrity {
// It depends on being used on top of an encrypted block store that protects integrity of the block contents (i.e. uses an authenticated cipher).
class IntegrityBlockStore2 final: public BlockStore2 {
public:
IntegrityBlockStore2(cpputils::unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool missingBlockIsIntegrityViolation);
IntegrityBlockStore2(cpputils::unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool noIntegrityChecks, bool missingBlockIsIntegrityViolation);
bool tryCreate(const Key &key, const cpputils::Data &data) override;
bool remove(const Key &key) override;
@ -66,6 +66,7 @@ private:
cpputils::unique_ref<BlockStore2> _baseBlockStore;
mutable KnownBlockVersions _knownBlockVersions;
const bool _noIntegrityChecks;
const bool _missingBlockIsIntegrityViolation;
mutable bool _integrityViolationDetected;

View File

@ -228,7 +228,7 @@ namespace cryfs {
try {
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
auto config = _loadOrCreateConfig(options);
CryDevice device(std::move(config.configFile), std::move(blockStore), config.myClientId);
CryDevice device(std::move(config.configFile), std::move(blockStore), config.myClientId, options.noIntegrityChecks());
_sanityCheckFilesystem(&device);
fspp::FilesystemImpl fsimpl(&device);
fspp::fuse::Fuse fuse(&fsimpl, "cryfs", "cryfs@"+options.baseDir().native());

View File

@ -76,6 +76,7 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
if (vm.count("blocksize")) {
blocksizeBytes = vm["blocksize"].as<uint32_t>();
}
bool noIntegrityChecks = vm.count("no-integrity-checks");
optional<bool> missingBlockIsIntegrityViolation = none;
if (vm.count("missing-block-is-integrity-violation")) {
missingBlockIsIntegrityViolation = vm["missing-block-is-integrity-violation"].as<bool>();
@ -91,7 +92,7 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
}
}
return ProgramOptions(baseDir, mountDir, configfile, foreground, unmountAfterIdleMinutes, logfile, cipher, blocksizeBytes, missingBlockIsIntegrityViolation, fuseOptions);
return ProgramOptions(baseDir, mountDir, configfile, foreground, unmountAfterIdleMinutes, logfile, cipher, blocksizeBytes, noIntegrityChecks, missingBlockIsIntegrityViolation, fuseOptions);
}
void Parser::_checkValidCipher(const string &cipher, const vector<string> &supportedCiphers) {
@ -153,6 +154,7 @@ void Parser::_addAllowedOptions(po::options_description *desc) {
("fuse-option,o", po::value<vector<string>>(), "Add a fuse mount option. Example: atime or noatime.")
("cipher", po::value<string>(), cipher_description.c_str())
("blocksize", po::value<uint32_t>(), blocksize_description.c_str())
("no-integrity-checks", "Disable integrity checks. Integrity checks ensure that your file system was not manipulated or rolled back to an earlier version. Disabling them is needed if you want to load an old snapshot of your file system.")
("missing-block-is-integrity-violation", po::value<bool>(), "Whether to treat a missing block as an integrity violation. This makes sure you notice if an attacker deleted some of your files, but only works in single-client mode. You will not be able to use the file system on other devices.")
("show-ciphers", "Show list of supported ciphers.")
("unmount-idle", po::value<double>(), "Automatically unmount after specified number of idle minutes.")

View File

@ -12,9 +12,10 @@ ProgramOptions::ProgramOptions(const bf::path &baseDir, const bf::path &mountDir
bool foreground, const optional<double> &unmountAfterIdleMinutes,
const optional<bf::path> &logFile, const optional<string> &cipher,
const optional<uint32_t> &blocksizeBytes,
bool noIntegrityChecks,
const boost::optional<bool> &missingBlockIsIntegrityViolation,
const vector<string> &fuseOptions)
:_baseDir(baseDir), _mountDir(mountDir), _configFile(configFile), _foreground(foreground),
:_baseDir(baseDir), _mountDir(mountDir), _configFile(configFile), _foreground(foreground), _noIntegrityChecks(noIntegrityChecks),
_cipher(cipher), _blocksizeBytes(blocksizeBytes), _unmountAfterIdleMinutes(unmountAfterIdleMinutes),
_missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _logFile(logFile), _fuseOptions(fuseOptions) {
}
@ -51,6 +52,10 @@ const optional<uint32_t> &ProgramOptions::blocksizeBytes() const {
return _blocksizeBytes;
}
bool ProgramOptions::noIntegrityChecks() const {
return _noIntegrityChecks;
}
const optional<bool> &ProgramOptions::missingBlockIsIntegrityViolation() const {
return _missingBlockIsIntegrityViolation;
}

View File

@ -18,6 +18,7 @@ namespace cryfs {
const boost::optional<boost::filesystem::path> &logFile,
const boost::optional<std::string> &cipher,
const boost::optional<uint32_t> &blocksizeBytes,
bool noIntegrityChecks,
const boost::optional<bool> &missingBlockIsIntegrityViolation,
const std::vector<std::string> &fuseOptions);
ProgramOptions(ProgramOptions &&rhs) = default;
@ -29,6 +30,7 @@ namespace cryfs {
const boost::optional<std::string> &cipher() const;
const boost::optional<uint32_t> &blocksizeBytes() const;
const boost::optional<double> &unmountAfterIdleMinutes() const;
bool noIntegrityChecks() const;
const boost::optional<bool> &missingBlockIsIntegrityViolation() const;
const boost::optional<boost::filesystem::path> &logFile() const;
const std::vector<std::string> &fuseOptions() const;
@ -38,6 +40,7 @@ namespace cryfs {
boost::filesystem::path _mountDir;
boost::optional<boost::filesystem::path> _configFile;
bool _foreground;
bool _noIntegrityChecks;
boost::optional<std::string> _cipher;
boost::optional<uint32_t> _blocksizeBytes;
boost::optional<double> _unmountAfterIdleMinutes;

View File

@ -57,14 +57,14 @@ namespace bf = boost::filesystem;
namespace cryfs {
CryDevice::CryDevice(CryConfigFile configFile, unique_ref<BlockStore2> blockStore, uint32_t myClientId)
: _fsBlobStore(CreateFsBlobStore(std::move(blockStore), &configFile, myClientId)),
CryDevice::CryDevice(CryConfigFile configFile, unique_ref<BlockStore2> blockStore, uint32_t myClientId, bool noIntegrityChecks)
: _fsBlobStore(CreateFsBlobStore(std::move(blockStore), &configFile, myClientId, noIntegrityChecks)),
_rootKey(GetOrCreateRootKey(&configFile)),
_onFsAction() {
}
unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CryDevice::CreateFsBlobStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId) {
auto blobStore = CreateBlobStore(std::move(blockStore), configFile, myClientId);
unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CryDevice::CreateFsBlobStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId, bool noIntegrityChecks) {
auto blobStore = CreateBlobStore(std::move(blockStore), configFile, myClientId, noIntegrityChecks);
#ifndef CRYFS_NO_COMPATIBILITY
auto fsBlobStore = MigrateOrCreateFsBlobStore(std::move(blobStore), configFile);
@ -89,8 +89,8 @@ unique_ref<fsblobstore::FsBlobStore> CryDevice::MigrateOrCreateFsBlobStore(uniqu
}
#endif
unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId) {
auto integrityEncryptedBlockStore = CreateIntegrityEncryptedBlockStore(std::move(blockStore), configFile, myClientId);
unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId, bool noIntegrityChecks) {
auto integrityEncryptedBlockStore = CreateIntegrityEncryptedBlockStore(std::move(blockStore), configFile, myClientId, noIntegrityChecks);
// Create integrityEncryptedBlockStore not in the same line as BlobStoreOnBlocks, because it can modify BlocksizeBytes
// in the configFile and therefore has to be run before the second parameter to the BlobStoreOnBlocks parameter is evaluated.
return make_unique_ref<BlobStoreOnBlocks>(
@ -102,7 +102,7 @@ unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStor
configFile->config()->BlocksizeBytes());
}
unique_ref<BlockStore2> CryDevice::CreateIntegrityEncryptedBlockStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId) {
unique_ref<BlockStore2> CryDevice::CreateIntegrityEncryptedBlockStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId, bool noIntegrityChecks) {
auto encryptedBlockStore = CreateEncryptedBlockStore(*configFile->config(), std::move(blockStore));
auto statePath = LocalStateDir::forFilesystemId(configFile->config()->FilesystemId());
auto integrityFilePath = statePath / "integritydata";
@ -115,8 +115,8 @@ unique_ref<BlockStore2> CryDevice::CreateIntegrityEncryptedBlockStore(unique_ref
configFile->save();
}
#endif
return make_unique_ref<IntegrityBlockStore2>(std::move(encryptedBlockStore), integrityFilePath, myClientId, false);
return make_unique_ref<IntegrityBlockStore2>(std::move(encryptedBlockStore), integrityFilePath, myClientId, noIntegrityChecks, false);
}
Key CryDevice::CreateRootBlobAndReturnKey() {

View File

@ -18,7 +18,7 @@ namespace cryfs {
class CryDevice final: public fspp::Device {
public:
CryDevice(CryConfigFile config, cpputils::unique_ref<blockstore::BlockStore2> blockStore, uint32_t myClientId);
CryDevice(CryConfigFile config, cpputils::unique_ref<blockstore::BlockStore2> blockStore, uint32_t myClientId, bool noIntegrityChecks);
void statfs(const boost::filesystem::path &path, struct ::statvfs *fsstat) override;
@ -53,12 +53,12 @@ private:
blockstore::Key GetOrCreateRootKey(CryConfigFile *config);
blockstore::Key CreateRootBlobAndReturnKey();
static cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CreateFsBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId);
static cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CreateFsBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId, bool noIntegrityChecks);
#ifndef CRYFS_NO_COMPATIBILITY
static cpputils::unique_ref<fsblobstore::FsBlobStore> MigrateOrCreateFsBlobStore(cpputils::unique_ref<blobstore::BlobStore> blobStore, CryConfigFile *configFile);
#endif
static cpputils::unique_ref<blobstore::BlobStore> CreateBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId);
static cpputils::unique_ref<blockstore::BlockStore2> CreateIntegrityEncryptedBlockStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId);
static cpputils::unique_ref<blobstore::BlobStore> CreateBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId, bool noIntegrityChecks);
static cpputils::unique_ref<blockstore::BlockStore2> CreateIntegrityEncryptedBlockStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, uint32_t myClientId, bool noIntegrityChecks);
static cpputils::unique_ref<blockstore::BlockStore2> CreateEncryptedBlockStore(const CryConfig &config, cpputils::unique_ref<blockstore::BlockStore2> baseBlockStore);
struct BlobWithParent {

View File

@ -249,6 +249,7 @@ TEST_F(IntegrityBlockStoreTest, LoadingWithDifferentBlockIdFails) {
// - RollbackPrevention_DoesntAllowReintroducingDeletedBlocks with different client id (i.e. trying to re-introduce the newest block of a different client)
// - RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber with different client id
// - Think about more...
// TODO Test that disabling integrity checks allows all these cases
TEST_F(IntegrityBlockStoreTest, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(0));

View File

@ -156,6 +156,16 @@ TEST_F(ProgramOptionsParserTest, MissingBlockIsIntegrityViolationGiven_False) {
EXPECT_FALSE(options.missingBlockIsIntegrityViolation().value());
}
TEST_F(ProgramOptionsParserTest, NoIntegrityChecks_True) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "--no-integrity-checks", "/home/user/mountDir"});
EXPECT_TRUE(options.noIntegrityChecks());
}
TEST_F(ProgramOptionsParserTest, NoIntegrityChecks_False) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "/home/user/mountDir"});
EXPECT_FALSE(options.noIntegrityChecks());
}
TEST_F(ProgramOptionsParserTest, MissingBlockIsIntegrityViolationNotGiven) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "/home/user/mountDir"});
EXPECT_EQ(none, options.missingBlockIsIntegrityViolation());

View File

@ -23,98 +23,108 @@ namespace boost {
class ProgramOptionsTest: public ProgramOptionsTestBase {};
TEST_F(ProgramOptionsTest, BaseDir) {
ProgramOptions testobj("/home/user/mydir", "", none, false, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("/home/user/mydir", "", none, false, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ("/home/user/mydir", testobj.baseDir());
}
TEST_F(ProgramOptionsTest, MountDir) {
ProgramOptions testobj("", "/home/user/mydir", none, false, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "/home/user/mydir", none, false, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ("/home/user/mydir", testobj.mountDir());
}
TEST_F(ProgramOptionsTest, ConfigfileNone) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(none, testobj.configFile());
}
TEST_F(ProgramOptionsTest, ConfigfileSome) {
ProgramOptions testobj("", "", bf::path("/home/user/configfile"), true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", bf::path("/home/user/configfile"), true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ("/home/user/configfile", testobj.configFile().get());
}
TEST_F(ProgramOptionsTest, ForegroundFalse) {
ProgramOptions testobj("", "", none, false, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, false, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_FALSE(testobj.foreground());
}
TEST_F(ProgramOptionsTest, ForegroundTrue) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_TRUE(testobj.foreground());
}
TEST_F(ProgramOptionsTest, LogfileNone) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(none, testobj.logFile());
}
TEST_F(ProgramOptionsTest, LogfileSome) {
ProgramOptions testobj("", "", none, true, none, bf::path("logfile"), none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, bf::path("logfile"), none, none, false, none, {"./myExecutable"});
EXPECT_EQ("logfile", testobj.logFile().get());
}
TEST_F(ProgramOptionsTest, UnmountAfterIdleMinutesNone) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(none, testobj.unmountAfterIdleMinutes());
}
TEST_F(ProgramOptionsTest, UnmountAfterIdleMinutesSome) {
ProgramOptions testobj("", "", none, true, 10, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, 10, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(10, testobj.unmountAfterIdleMinutes().get());
}
TEST_F(ProgramOptionsTest, CipherNone) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(none, testobj.cipher());
}
TEST_F(ProgramOptionsTest, CipherSome) {
ProgramOptions testobj("", "", none, true, none, none, string("aes-256-gcm"), none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, string("aes-256-gcm"), none, false, none, {"./myExecutable"});
EXPECT_EQ("aes-256-gcm", testobj.cipher().get());
}
TEST_F(ProgramOptionsTest, BlocksizeBytesNone) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(none, testobj.blocksizeBytes());
}
TEST_F(ProgramOptionsTest, BlocksizeBytesSome) {
ProgramOptions testobj("", "", none, true, none, none, none, 10*1024, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, 10*1024, false, none, {"./myExecutable"});
EXPECT_EQ(10*1024u, testobj.blocksizeBytes().get());
}
TEST_F(ProgramOptionsTest, MissingBlockIsIntegrityViolationTrue) {
ProgramOptions testobj("", "", none, true, none, none, none, none, true, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, true, {"./myExecutable"});
EXPECT_TRUE(testobj.missingBlockIsIntegrityViolation().value());
}
TEST_F(ProgramOptionsTest, MissingBlockIsIntegrityViolationFalse) {
ProgramOptions testobj("", "", none, true, none, none, none, none, false, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, false, {"./myExecutable"});
EXPECT_FALSE(testobj.missingBlockIsIntegrityViolation().value());
}
TEST_F(ProgramOptionsTest, MissingBlockIsIntegrityViolationNone) {
ProgramOptions testobj("", "", none, true, none, none, none, none, none, {"./myExecutable"});
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_EQ(none, testobj.missingBlockIsIntegrityViolation());
}
TEST_F(ProgramOptionsTest, NoIntegrityChecksFalse) {
ProgramOptions testobj("", "", none, true, none, none, none, none, false, none, {"./myExecutable"});
EXPECT_FALSE(testobj.noIntegrityChecks());
}
TEST_F(ProgramOptionsTest, NoIntegrityChecksTrue) {
ProgramOptions testobj("", "", none, true, none, none, none, none, true, none, {"./myExecutable"});
EXPECT_TRUE(testobj.noIntegrityChecks());
}
TEST_F(ProgramOptionsTest, EmptyFuseOptions) {
ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, none, none, none, none, {});
ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, none, none, none, false, none, {});
//Fuse should have the mount dir as first parameter
EXPECT_VECTOR_EQ({}, testobj.fuseOptions());
}
TEST_F(ProgramOptionsTest, SomeFuseOptions) {
ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, none, none, none, none, {"-f", "--longoption"});
ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, none, none, none, false, none, {"-f", "--longoption"});
//Fuse should have the mount dir as first parameter
EXPECT_VECTOR_EQ({"-f", "--longoption"}, testobj.fuseOptions());
}