diff --git a/src/blockstore/implementations/versioncounting/VersionCountingBlock.h b/src/blockstore/implementations/versioncounting/VersionCountingBlock.h index e1b4622a..251d94de 100644 --- a/src/blockstore/implementations/versioncounting/VersionCountingBlock.h +++ b/src/blockstore/implementations/versioncounting/VersionCountingBlock.h @@ -18,6 +18,7 @@ #include #include "../../../../vendor/googletest/gtest-1.7.0/googletest/include/gtest/gtest_prod.h" #include "IntegrityViolationError.h" +#include "VersionCountingBlockStore.h" namespace blockstore { namespace versioncounting { @@ -26,13 +27,13 @@ namespace versioncounting { class VersionCountingBlock final: public Block { public: - static boost::optional> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions); - static cpputils::unique_ref Load(cpputils::unique_ref baseBlock, KnownBlockVersions *knownBlockVersions); + static boost::optional> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, VersionCountingBlockStore *blockStore); + static cpputils::unique_ref Load(cpputils::unique_ref baseBlock, VersionCountingBlockStore *blockStore); static uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize); //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. - VersionCountingBlock(cpputils::unique_ref baseBlock, cpputils::Data dataWithHeader, KnownBlockVersions *knownBlockVersions); + VersionCountingBlock(cpputils::unique_ref baseBlock, cpputils::Data dataWithHeader, VersionCountingBlockStore *blockStore); ~VersionCountingBlock(); const void *data() const override; @@ -49,7 +50,7 @@ public: #endif private: - KnownBlockVersions *_knownBlockVersions; + VersionCountingBlockStore *_blockStore; cpputils::unique_ref _baseBlock; cpputils::Data _dataWithHeader; uint64_t _version; @@ -58,9 +59,9 @@ private: void _storeToBaseBlock(); static cpputils::Data _prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data); static void _checkFormatHeader(const cpputils::Data &data); - static uint64_t _readVersion(const cpputils::Data &data); - static uint32_t _readClientId(const cpputils::Data &data); - static void _checkVersion(const cpputils::Data &data, const blockstore::Key &key, KnownBlockVersions *knownBlockVersions); + uint64_t _readVersion(); + uint32_t _readClientId(); + void _checkVersion(); // This header is prepended to blocks to allow future versions to have compatibility. static constexpr uint16_t FORMAT_VERSION_HEADER = 0; @@ -77,17 +78,17 @@ public: }; -inline boost::optional> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions) { - uint64_t version = knownBlockVersions->incrementVersion(key, VERSION_ZERO); +inline boost::optional> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, VersionCountingBlockStore *blockStore) { + uint64_t version = blockStore->knownBlockVersions()->incrementVersion(key, VERSION_ZERO); - cpputils::Data dataWithHeader = _prependHeaderToData(knownBlockVersions->myClientId(), version, std::move(data)); + cpputils::Data dataWithHeader = _prependHeaderToData(blockStore->knownBlockVersions()->myClientId(), version, std::move(data)); auto baseBlock = baseBlockStore->tryCreate(key, dataWithHeader.copy()); // TODO Copy necessary? if (baseBlock == boost::none) { //TODO Test this code branch return boost::none; } - return cpputils::make_unique_ref(std::move(*baseBlock), std::move(dataWithHeader), knownBlockVersions); + return cpputils::make_unique_ref(std::move(*baseBlock), std::move(dataWithHeader), blockStore); } inline cpputils::Data VersionCountingBlock::_prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data) { @@ -100,19 +101,20 @@ inline cpputils::Data VersionCountingBlock::_prependHeaderToData(uint32_t myClie return result; } -inline cpputils::unique_ref VersionCountingBlock::Load(cpputils::unique_ref baseBlock, KnownBlockVersions *knownBlockVersions) { +inline cpputils::unique_ref VersionCountingBlock::Load(cpputils::unique_ref baseBlock, VersionCountingBlockStore *blockStore) { cpputils::Data data(baseBlock->size()); std::memcpy(data.data(), baseBlock->data(), data.size()); _checkFormatHeader(data); - _checkVersion(data, baseBlock->key(), knownBlockVersions); - return cpputils::make_unique_ref(std::move(baseBlock), std::move(data), knownBlockVersions); + auto block = cpputils::make_unique_ref(std::move(baseBlock), std::move(data), blockStore); + block->_checkVersion(); + return std::move(block); } -inline void VersionCountingBlock::_checkVersion(const cpputils::Data &data, const blockstore::Key &key, KnownBlockVersions *knownBlockVersions) { - uint32_t lastClientId = _readClientId(data); - uint64_t version = _readVersion(data); - if(!knownBlockVersions->checkAndUpdateVersion(lastClientId, key, version)) { - throw IntegrityViolationError("The block version number is too low. Did an attacker try to roll back the block or to re-introduce a deleted block?"); +inline void VersionCountingBlock::_checkVersion() { + uint32_t lastClientId = _readClientId(); + uint64_t version = _readVersion(); + if(!_blockStore->knownBlockVersions()->checkAndUpdateVersion(lastClientId, key(), version)) { + _blockStore->integrityViolationDetected(); } } @@ -122,24 +124,24 @@ inline void VersionCountingBlock::_checkFormatHeader(const cpputils::Data &data) } } -inline uint32_t VersionCountingBlock::_readClientId(const cpputils::Data &data) { +inline uint32_t VersionCountingBlock::_readClientId() { uint32_t clientId; - std::memcpy(&clientId, data.dataOffset(CLIENTID_HEADER_OFFSET), sizeof(clientId)); + std::memcpy(&clientId, _dataWithHeader.dataOffset(CLIENTID_HEADER_OFFSET), sizeof(clientId)); return clientId; } -inline uint64_t VersionCountingBlock::_readVersion(const cpputils::Data &data) { +inline uint64_t VersionCountingBlock::_readVersion() { uint64_t version; - std::memcpy(&version, data.dataOffset(VERSION_HEADER_OFFSET), sizeof(version)); + std::memcpy(&version, _dataWithHeader.dataOffset(VERSION_HEADER_OFFSET), sizeof(version)); return version; } -inline VersionCountingBlock::VersionCountingBlock(cpputils::unique_ref baseBlock, cpputils::Data dataWithHeader, KnownBlockVersions *knownBlockVersions) +inline VersionCountingBlock::VersionCountingBlock(cpputils::unique_ref baseBlock, cpputils::Data dataWithHeader, VersionCountingBlockStore *blockStore) :Block(baseBlock->key()), - _knownBlockVersions(knownBlockVersions), + _blockStore(blockStore), _baseBlock(std::move(baseBlock)), _dataWithHeader(std::move(dataWithHeader)), - _version(_readVersion(_dataWithHeader)), + _version(_readVersion()), _dataChanged(false), _mutex() { if (_version == std::numeric_limits::max()) { @@ -179,8 +181,8 @@ inline void VersionCountingBlock::resize(size_t newSize) { inline void VersionCountingBlock::_storeToBaseBlock() { if (_dataChanged) { - _version = _knownBlockVersions->incrementVersion(key(), _version); - uint32_t myClientId = _knownBlockVersions->myClientId(); + _version = _blockStore->knownBlockVersions()->incrementVersion(key(), _version); + uint32_t myClientId = _blockStore->knownBlockVersions()->myClientId(); std::memcpy(_dataWithHeader.dataOffset(CLIENTID_HEADER_OFFSET), &myClientId, sizeof(myClientId)); std::memcpy(_dataWithHeader.dataOffset(VERSION_HEADER_OFFSET), &_version, sizeof(_version)); if (_baseBlock->size() != _dataWithHeader.size()) { diff --git a/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.cpp b/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.cpp index 3262e2fc..f73889f9 100644 --- a/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.cpp +++ b/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.cpp @@ -1,14 +1,82 @@ #include "VersionCountingBlockStore.h" +#include "VersionCountingBlock.h" using cpputils::unique_ref; using cpputils::make_unique_ref; using cpputils::Data; using boost::none; +using boost::optional; namespace bf = boost::filesystem; namespace blockstore { namespace versioncounting { + VersionCountingBlockStore::VersionCountingBlockStore(unique_ref baseBlockStore, const bf::path &integrityFilePath, bool missingBlockIsIntegrityViolation) + : _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) { + } + + Key VersionCountingBlockStore::createKey() { + return _baseBlockStore->createKey(); + } + + optional> VersionCountingBlockStore::tryCreate(const Key &key, cpputils::Data data) { + _checkNoPastIntegrityViolations(); + //TODO Easier implementation? This is only so complicated because of the cast VersionCountingBlock -> Block + auto result = VersionCountingBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), this); + if (result == boost::none) { + return boost::none; + } + return unique_ref(std::move(*result)); + } + + optional> VersionCountingBlockStore::load(const Key &key) { + _checkNoPastIntegrityViolations(); + auto block = _baseBlockStore->load(key); + if (block == boost::none) { + if (_missingBlockIsIntegrityViolation && _knownBlockVersions.blockShouldExist(key)) { + integrityViolationDetected(); + } + return boost::none; + } + return optional>(VersionCountingBlock::Load(std::move(*block), this)); + } + + void VersionCountingBlockStore::_checkNoPastIntegrityViolations() { + if (_integrityViolationDetected) { + throw std::runtime_error("There was an integrity violation detected. Preventing any further access to the file system."); + } + } + + void VersionCountingBlockStore::integrityViolationDetected() { + _integrityViolationDetected = true; + throw IntegrityViolationError("A block that should exist wasn't found. Did an attacker delete it?"); + } + + void VersionCountingBlockStore::remove(unique_ref block) { + Key key = block->key(); + auto versionCountingBlock = cpputils::dynamic_pointer_move(block); + ASSERT(versionCountingBlock != boost::none, "Block is not an VersionCountingBlock"); + _knownBlockVersions.markBlockAsDeleted(key); + auto baseBlock = (*versionCountingBlock)->releaseBlock(); + _baseBlockStore->remove(std::move(baseBlock)); + } + + uint64_t VersionCountingBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks(); + } + + uint64_t VersionCountingBlockStore::estimateNumFreeBytes() const { + return _baseBlockStore->estimateNumFreeBytes(); + } + + uint64_t VersionCountingBlockStore::blockSizeFromPhysicalBlockSize(uint64_t blockSize) const { + return VersionCountingBlock::blockSizeFromPhysicalBlockSize(_baseBlockStore->blockSizeFromPhysicalBlockSize(blockSize)); + } + + void VersionCountingBlockStore::forEachBlock(std::function callback) const { + return _baseBlockStore->forEachBlock(callback); + } + #ifndef CRYFS_NO_COMPATIBILITY void VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const bf::path &integrityFilePath) { std::cout << "Migrating file system for integrity features. This can take a while..." << std::flush; diff --git a/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.h b/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.h index 17e9392e..4f679107 100644 --- a/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.h +++ b/src/blockstore/implementations/versioncounting/VersionCountingBlockStore.h @@ -5,7 +5,6 @@ #include "../../interface/BlockStore.h" #include #include -#include "VersionCountingBlock.h" #include "KnownBlockVersions.h" #include @@ -25,6 +24,9 @@ public: uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize) const override; void forEachBlock(std::function callback) const override; + void integrityViolationDetected(); + KnownBlockVersions *knownBlockVersions(); + #ifndef CRYFS_NO_COMPATIBILITY static void migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const boost::filesystem::path &integrityFilePath); #endif @@ -33,62 +35,15 @@ private: cpputils::unique_ref _baseBlockStore; KnownBlockVersions _knownBlockVersions; const bool _missingBlockIsIntegrityViolation; + bool _integrityViolationDetected; + + void _checkNoPastIntegrityViolations(); DISALLOW_COPY_AND_ASSIGN(VersionCountingBlockStore); }; - -inline VersionCountingBlockStore::VersionCountingBlockStore(cpputils::unique_ref baseBlockStore, const boost::filesystem::path &integrityFilePath, bool missingBlockIsIntegrityViolation) - : _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation) { -} - -inline Key VersionCountingBlockStore::createKey() { - return _baseBlockStore->createKey(); -} - -inline boost::optional> VersionCountingBlockStore::tryCreate(const Key &key, cpputils::Data data) { - //TODO Easier implementation? This is only so complicated because of the cast VersionCountingBlock -> Block - auto result = VersionCountingBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), &_knownBlockVersions); - if (result == boost::none) { - return boost::none; - } - return cpputils::unique_ref(std::move(*result)); -} - -inline boost::optional> VersionCountingBlockStore::load(const Key &key) { - auto block = _baseBlockStore->load(key); - if (block == boost::none) { - if (_missingBlockIsIntegrityViolation && _knownBlockVersions.blockShouldExist(key)) { - throw IntegrityViolationError("A block that should exist wasn't found. Did an attacker delete it?"); - } - return boost::none; - } - return boost::optional>(VersionCountingBlock::Load(std::move(*block), &_knownBlockVersions)); -} - -inline void VersionCountingBlockStore::remove(cpputils::unique_ref block) { - Key key = block->key(); - auto versionCountingBlock = cpputils::dynamic_pointer_move(block); - ASSERT(versionCountingBlock != boost::none, "Block is not an VersionCountingBlock"); - _knownBlockVersions.markBlockAsDeleted(key); - auto baseBlock = (*versionCountingBlock)->releaseBlock(); - _baseBlockStore->remove(std::move(baseBlock)); -} - -inline uint64_t VersionCountingBlockStore::numBlocks() const { - return _baseBlockStore->numBlocks(); -} - -inline uint64_t VersionCountingBlockStore::estimateNumFreeBytes() const { - return _baseBlockStore->estimateNumFreeBytes(); -} - -inline uint64_t VersionCountingBlockStore::blockSizeFromPhysicalBlockSize(uint64_t blockSize) const { - return VersionCountingBlock::blockSizeFromPhysicalBlockSize(_baseBlockStore->blockSizeFromPhysicalBlockSize(blockSize)); -} - -inline void VersionCountingBlockStore::forEachBlock(std::function callback) const { - return _baseBlockStore->forEachBlock(callback); +inline KnownBlockVersions *VersionCountingBlockStore::knownBlockVersions() { + return &_knownBlockVersions; } } diff --git a/src/cryfs/filesystem/CryDevice.cpp b/src/cryfs/filesystem/CryDevice.cpp index 41aae374..fe9dbbd3 100644 --- a/src/cryfs/filesystem/CryDevice.cpp +++ b/src/cryfs/filesystem/CryDevice.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include "parallelaccessfsblobstore/ParallelAccessFsBlobStore.h" #include "cachingfsblobstore/CachingFsBlobStore.h" #include "../config/CryCipher.h" diff --git a/test/blockstore/implementations/versioncounting/VersionCountingBlockStoreTest_Specific.cpp b/test/blockstore/implementations/versioncounting/VersionCountingBlockStoreTest_Specific.cpp index 612e7661..207d1bc8 100644 --- a/test/blockstore/implementations/versioncounting/VersionCountingBlockStoreTest_Specific.cpp +++ b/test/blockstore/implementations/versioncounting/VersionCountingBlockStoreTest_Specific.cpp @@ -1,5 +1,6 @@ #include #include "blockstore/implementations/versioncounting/VersionCountingBlockStore.h" +#include "blockstore/implementations/versioncounting/VersionCountingBlock.h" #include "blockstore/implementations/testfake/FakeBlockStore.h" #include "blockstore/utils/BlockStoreUtils.h" #include