When an integrity violation is detected, any further access to the file system is blocked. This ensures that the user notices the integrity violation.

This commit is contained in:
Sebastian Messmer 2016-06-25 16:07:52 -07:00
parent 2284571127
commit 839a511c4d
5 changed files with 108 additions and 82 deletions

View File

@ -18,6 +18,7 @@
#include <cpp-utils/logging/logging.h> #include <cpp-utils/logging/logging.h>
#include "../../../../vendor/googletest/gtest-1.7.0/googletest/include/gtest/gtest_prod.h" #include "../../../../vendor/googletest/gtest-1.7.0/googletest/include/gtest/gtest_prod.h"
#include "IntegrityViolationError.h" #include "IntegrityViolationError.h"
#include "VersionCountingBlockStore.h"
namespace blockstore { namespace blockstore {
namespace versioncounting { namespace versioncounting {
@ -26,13 +27,13 @@ namespace versioncounting {
class VersionCountingBlock final: public Block { class VersionCountingBlock final: public Block {
public: public:
static boost::optional<cpputils::unique_ref<VersionCountingBlock>> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions); static boost::optional<cpputils::unique_ref<VersionCountingBlock>> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, VersionCountingBlockStore *blockStore);
static cpputils::unique_ref<VersionCountingBlock> Load(cpputils::unique_ref<Block> baseBlock, KnownBlockVersions *knownBlockVersions); static cpputils::unique_ref<VersionCountingBlock> Load(cpputils::unique_ref<Block> baseBlock, VersionCountingBlockStore *blockStore);
static uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize); static uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize);
//TODO Storing key twice (in parent class and in object pointed to). Once would be enough. //TODO Storing key twice (in parent class and in object pointed to). Once would be enough.
VersionCountingBlock(cpputils::unique_ref<Block> baseBlock, cpputils::Data dataWithHeader, KnownBlockVersions *knownBlockVersions); VersionCountingBlock(cpputils::unique_ref<Block> baseBlock, cpputils::Data dataWithHeader, VersionCountingBlockStore *blockStore);
~VersionCountingBlock(); ~VersionCountingBlock();
const void *data() const override; const void *data() const override;
@ -49,7 +50,7 @@ public:
#endif #endif
private: private:
KnownBlockVersions *_knownBlockVersions; VersionCountingBlockStore *_blockStore;
cpputils::unique_ref<Block> _baseBlock; cpputils::unique_ref<Block> _baseBlock;
cpputils::Data _dataWithHeader; cpputils::Data _dataWithHeader;
uint64_t _version; uint64_t _version;
@ -58,9 +59,9 @@ private:
void _storeToBaseBlock(); void _storeToBaseBlock();
static cpputils::Data _prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data); static cpputils::Data _prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data);
static void _checkFormatHeader(const cpputils::Data &data); static void _checkFormatHeader(const cpputils::Data &data);
static uint64_t _readVersion(const cpputils::Data &data); uint64_t _readVersion();
static uint32_t _readClientId(const cpputils::Data &data); uint32_t _readClientId();
static void _checkVersion(const cpputils::Data &data, const blockstore::Key &key, KnownBlockVersions *knownBlockVersions); void _checkVersion();
// This header is prepended to blocks to allow future versions to have compatibility. // This header is prepended to blocks to allow future versions to have compatibility.
static constexpr uint16_t FORMAT_VERSION_HEADER = 0; static constexpr uint16_t FORMAT_VERSION_HEADER = 0;
@ -77,17 +78,17 @@ public:
}; };
inline boost::optional<cpputils::unique_ref<VersionCountingBlock>> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions) { inline boost::optional<cpputils::unique_ref<VersionCountingBlock>> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, VersionCountingBlockStore *blockStore) {
uint64_t version = knownBlockVersions->incrementVersion(key, VERSION_ZERO); 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? auto baseBlock = baseBlockStore->tryCreate(key, dataWithHeader.copy()); // TODO Copy necessary?
if (baseBlock == boost::none) { if (baseBlock == boost::none) {
//TODO Test this code branch //TODO Test this code branch
return boost::none; return boost::none;
} }
return cpputils::make_unique_ref<VersionCountingBlock>(std::move(*baseBlock), std::move(dataWithHeader), knownBlockVersions); return cpputils::make_unique_ref<VersionCountingBlock>(std::move(*baseBlock), std::move(dataWithHeader), blockStore);
} }
inline cpputils::Data VersionCountingBlock::_prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data) { 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; return result;
} }
inline cpputils::unique_ref<VersionCountingBlock> VersionCountingBlock::Load(cpputils::unique_ref<Block> baseBlock, KnownBlockVersions *knownBlockVersions) { inline cpputils::unique_ref<VersionCountingBlock> VersionCountingBlock::Load(cpputils::unique_ref<Block> baseBlock, VersionCountingBlockStore *blockStore) {
cpputils::Data data(baseBlock->size()); cpputils::Data data(baseBlock->size());
std::memcpy(data.data(), baseBlock->data(), data.size()); std::memcpy(data.data(), baseBlock->data(), data.size());
_checkFormatHeader(data); _checkFormatHeader(data);
_checkVersion(data, baseBlock->key(), knownBlockVersions); auto block = cpputils::make_unique_ref<VersionCountingBlock>(std::move(baseBlock), std::move(data), blockStore);
return cpputils::make_unique_ref<VersionCountingBlock>(std::move(baseBlock), std::move(data), knownBlockVersions); block->_checkVersion();
return std::move(block);
} }
inline void VersionCountingBlock::_checkVersion(const cpputils::Data &data, const blockstore::Key &key, KnownBlockVersions *knownBlockVersions) { inline void VersionCountingBlock::_checkVersion() {
uint32_t lastClientId = _readClientId(data); uint32_t lastClientId = _readClientId();
uint64_t version = _readVersion(data); uint64_t version = _readVersion();
if(!knownBlockVersions->checkAndUpdateVersion(lastClientId, key, version)) { if(!_blockStore->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?"); _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; 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; return clientId;
} }
inline uint64_t VersionCountingBlock::_readVersion(const cpputils::Data &data) { inline uint64_t VersionCountingBlock::_readVersion() {
uint64_t version; 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; return version;
} }
inline VersionCountingBlock::VersionCountingBlock(cpputils::unique_ref<Block> baseBlock, cpputils::Data dataWithHeader, KnownBlockVersions *knownBlockVersions) inline VersionCountingBlock::VersionCountingBlock(cpputils::unique_ref<Block> baseBlock, cpputils::Data dataWithHeader, VersionCountingBlockStore *blockStore)
:Block(baseBlock->key()), :Block(baseBlock->key()),
_knownBlockVersions(knownBlockVersions), _blockStore(blockStore),
_baseBlock(std::move(baseBlock)), _baseBlock(std::move(baseBlock)),
_dataWithHeader(std::move(dataWithHeader)), _dataWithHeader(std::move(dataWithHeader)),
_version(_readVersion(_dataWithHeader)), _version(_readVersion()),
_dataChanged(false), _dataChanged(false),
_mutex() { _mutex() {
if (_version == std::numeric_limits<uint64_t>::max()) { if (_version == std::numeric_limits<uint64_t>::max()) {
@ -179,8 +181,8 @@ inline void VersionCountingBlock::resize(size_t newSize) {
inline void VersionCountingBlock::_storeToBaseBlock() { inline void VersionCountingBlock::_storeToBaseBlock() {
if (_dataChanged) { if (_dataChanged) {
_version = _knownBlockVersions->incrementVersion(key(), _version); _version = _blockStore->knownBlockVersions()->incrementVersion(key(), _version);
uint32_t myClientId = _knownBlockVersions->myClientId(); uint32_t myClientId = _blockStore->knownBlockVersions()->myClientId();
std::memcpy(_dataWithHeader.dataOffset(CLIENTID_HEADER_OFFSET), &myClientId, sizeof(myClientId)); std::memcpy(_dataWithHeader.dataOffset(CLIENTID_HEADER_OFFSET), &myClientId, sizeof(myClientId));
std::memcpy(_dataWithHeader.dataOffset(VERSION_HEADER_OFFSET), &_version, sizeof(_version)); std::memcpy(_dataWithHeader.dataOffset(VERSION_HEADER_OFFSET), &_version, sizeof(_version));
if (_baseBlock->size() != _dataWithHeader.size()) { if (_baseBlock->size() != _dataWithHeader.size()) {

View File

@ -1,14 +1,82 @@
#include "VersionCountingBlockStore.h" #include "VersionCountingBlockStore.h"
#include "VersionCountingBlock.h"
using cpputils::unique_ref; using cpputils::unique_ref;
using cpputils::make_unique_ref; using cpputils::make_unique_ref;
using cpputils::Data; using cpputils::Data;
using boost::none; using boost::none;
using boost::optional;
namespace bf = boost::filesystem; namespace bf = boost::filesystem;
namespace blockstore { namespace blockstore {
namespace versioncounting { namespace versioncounting {
VersionCountingBlockStore::VersionCountingBlockStore(unique_ref<BlockStore> baseBlockStore, const bf::path &integrityFilePath, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) {
}
Key VersionCountingBlockStore::createKey() {
return _baseBlockStore->createKey();
}
optional<unique_ref<Block>> 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<Block>(std::move(*result));
}
optional<unique_ref<Block>> 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<unique_ref<Block>>(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> block) {
Key key = block->key();
auto versionCountingBlock = cpputils::dynamic_pointer_move<VersionCountingBlock>(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<void (const Key &)> callback) const {
return _baseBlockStore->forEachBlock(callback);
}
#ifndef CRYFS_NO_COMPATIBILITY #ifndef CRYFS_NO_COMPATIBILITY
void VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const bf::path &integrityFilePath) { void VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const bf::path &integrityFilePath) {
std::cout << "Migrating file system for integrity features. This can take a while..." << std::flush; std::cout << "Migrating file system for integrity features. This can take a while..." << std::flush;

View File

@ -5,7 +5,6 @@
#include "../../interface/BlockStore.h" #include "../../interface/BlockStore.h"
#include <cpp-utils/macros.h> #include <cpp-utils/macros.h>
#include <cpp-utils/pointer/cast.h> #include <cpp-utils/pointer/cast.h>
#include "VersionCountingBlock.h"
#include "KnownBlockVersions.h" #include "KnownBlockVersions.h"
#include <iostream> #include <iostream>
@ -25,6 +24,9 @@ public:
uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize) const override; uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize) const override;
void forEachBlock(std::function<void (const Key &)> callback) const override; void forEachBlock(std::function<void (const Key &)> callback) const override;
void integrityViolationDetected();
KnownBlockVersions *knownBlockVersions();
#ifndef CRYFS_NO_COMPATIBILITY #ifndef CRYFS_NO_COMPATIBILITY
static void migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const boost::filesystem::path &integrityFilePath); static void migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const boost::filesystem::path &integrityFilePath);
#endif #endif
@ -33,62 +35,15 @@ private:
cpputils::unique_ref<BlockStore> _baseBlockStore; cpputils::unique_ref<BlockStore> _baseBlockStore;
KnownBlockVersions _knownBlockVersions; KnownBlockVersions _knownBlockVersions;
const bool _missingBlockIsIntegrityViolation; const bool _missingBlockIsIntegrityViolation;
bool _integrityViolationDetected;
void _checkNoPastIntegrityViolations();
DISALLOW_COPY_AND_ASSIGN(VersionCountingBlockStore); DISALLOW_COPY_AND_ASSIGN(VersionCountingBlockStore);
}; };
inline KnownBlockVersions *VersionCountingBlockStore::knownBlockVersions() {
inline VersionCountingBlockStore::VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath, bool missingBlockIsIntegrityViolation) return &_knownBlockVersions;
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation) {
}
inline Key VersionCountingBlockStore::createKey() {
return _baseBlockStore->createKey();
}
inline boost::optional<cpputils::unique_ref<Block>> 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<Block>(std::move(*result));
}
inline boost::optional<cpputils::unique_ref<Block>> 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<cpputils::unique_ref<Block>>(VersionCountingBlock::Load(std::move(*block), &_knownBlockVersions));
}
inline void VersionCountingBlockStore::remove(cpputils::unique_ref<Block> block) {
Key key = block->key();
auto versionCountingBlock = cpputils::dynamic_pointer_move<VersionCountingBlock>(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<void (const Key &)> callback) const {
return _baseBlockStore->forEachBlock(callback);
} }
} }

View File

@ -11,7 +11,7 @@
#include <blobstore/implementations/onblocks/BlobStoreOnBlocks.h> #include <blobstore/implementations/onblocks/BlobStoreOnBlocks.h>
#include <blobstore/implementations/onblocks/BlobOnBlocks.h> #include <blobstore/implementations/onblocks/BlobOnBlocks.h>
#include <blockstore/implementations/encrypted/EncryptedBlockStore.h> #include <blockstore/implementations/encrypted/EncryptedBlockStore.h>
#include <blockstore/implementations/versioncounting/VersionCountingBlockStore.h> #include <blockstore/implementations/versioncounting/VersionCountingBlock.h>
#include "parallelaccessfsblobstore/ParallelAccessFsBlobStore.h" #include "parallelaccessfsblobstore/ParallelAccessFsBlobStore.h"
#include "cachingfsblobstore/CachingFsBlobStore.h" #include "cachingfsblobstore/CachingFsBlobStore.h"
#include "../config/CryCipher.h" #include "../config/CryCipher.h"

View File

@ -1,5 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "blockstore/implementations/versioncounting/VersionCountingBlockStore.h" #include "blockstore/implementations/versioncounting/VersionCountingBlockStore.h"
#include "blockstore/implementations/versioncounting/VersionCountingBlock.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h" #include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/utils/BlockStoreUtils.h" #include "blockstore/utils/BlockStoreUtils.h"
#include <cpp-utils/data/DataFixture.h> #include <cpp-utils/data/DataFixture.h>