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 "../../../../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<cpputils::unique_ref<VersionCountingBlock>> TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions);
static cpputils::unique_ref<VersionCountingBlock> Load(cpputils::unique_ref<Block> baseBlock, 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, 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<Block> baseBlock, cpputils::Data dataWithHeader, KnownBlockVersions *knownBlockVersions);
VersionCountingBlock(cpputils::unique_ref<Block> 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<Block> _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<cpputils::unique_ref<VersionCountingBlock>> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions) {
uint64_t version = knownBlockVersions->incrementVersion(key, VERSION_ZERO);
inline boost::optional<cpputils::unique_ref<VersionCountingBlock>> 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<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) {
@ -100,19 +101,20 @@ inline cpputils::Data VersionCountingBlock::_prependHeaderToData(uint32_t myClie
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());
std::memcpy(data.data(), baseBlock->data(), data.size());
_checkFormatHeader(data);
_checkVersion(data, baseBlock->key(), knownBlockVersions);
return cpputils::make_unique_ref<VersionCountingBlock>(std::move(baseBlock), std::move(data), knownBlockVersions);
auto block = cpputils::make_unique_ref<VersionCountingBlock>(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<Block> baseBlock, cpputils::Data dataWithHeader, KnownBlockVersions *knownBlockVersions)
inline VersionCountingBlock::VersionCountingBlock(cpputils::unique_ref<Block> 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<uint64_t>::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()) {

View File

@ -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<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
void VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const bf::path &integrityFilePath) {
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 <cpp-utils/macros.h>
#include <cpp-utils/pointer/cast.h>
#include "VersionCountingBlock.h"
#include "KnownBlockVersions.h"
#include <iostream>
@ -25,6 +24,9 @@ public:
uint64_t blockSizeFromPhysicalBlockSize(uint64_t blockSize) const override;
void forEachBlock(std::function<void (const Key &)> 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<BlockStore> _baseBlockStore;
KnownBlockVersions _knownBlockVersions;
const bool _missingBlockIsIntegrityViolation;
bool _integrityViolationDetected;
void _checkNoPastIntegrityViolations();
DISALLOW_COPY_AND_ASSIGN(VersionCountingBlockStore);
};
inline VersionCountingBlockStore::VersionCountingBlockStore(cpputils::unique_ref<BlockStore> 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<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);
inline KnownBlockVersions *VersionCountingBlockStore::knownBlockVersions() {
return &_knownBlockVersions;
}
}

View File

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

View File

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