mirror of
https://github.com/vgough/encfs.git
synced 2024-11-28 19:03:42 +01:00
add zero-block pass-through option, enabling allow-holes code
git-svn-id: http://encfs.googlecode.com/svn/trunk@14 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
parent
9ce4a03887
commit
30ed7062d3
@ -189,7 +189,7 @@ bool BlockFileIO::write( const IORequest &req )
|
||||
if(lastBlockSize == 0)
|
||||
--lastNonEmptyBlock;
|
||||
|
||||
if( (req.offset > fileSize) && !_allowHoles )
|
||||
if( req.offset > fileSize )
|
||||
{
|
||||
// extend file first to fill hole with 0's..
|
||||
const bool forceWrite = false;
|
||||
@ -340,15 +340,18 @@ void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite )
|
||||
++oldLastBlock;
|
||||
}
|
||||
|
||||
// 2
|
||||
for(; oldLastBlock != newLastBlock; ++oldLastBlock)
|
||||
{
|
||||
rDebug("padding block %" PRIi64, oldLastBlock);
|
||||
req.offset = oldLastBlock * _blockSize;
|
||||
req.dataLen = _blockSize;
|
||||
memset( mb.data, 0, req.dataLen );
|
||||
cacheWriteOneBlock( req );
|
||||
}
|
||||
// 2, pad zero blocks unless holes are allowed
|
||||
if(!_allowHoles)
|
||||
{
|
||||
for(; oldLastBlock != newLastBlock; ++oldLastBlock)
|
||||
{
|
||||
rDebug("padding block %" PRIi64, oldLastBlock);
|
||||
req.offset = oldLastBlock * _blockSize;
|
||||
req.dataLen = _blockSize;
|
||||
memset( mb.data, 0, req.dataLen );
|
||||
cacheWriteOneBlock( req );
|
||||
}
|
||||
}
|
||||
|
||||
// 3. only necessary if write is forced and block is non 0 length
|
||||
if(forceWrite && newBlockSize)
|
||||
|
@ -40,12 +40,12 @@ public:
|
||||
|
||||
virtual int blockSize() const;
|
||||
|
||||
protected:
|
||||
|
||||
// default is false, but setting this to true will allow holes to be stored
|
||||
// in the file. Only works if supported by the underlying FileIO
|
||||
// implementation..
|
||||
void allowHoles( bool allow );
|
||||
virtual void allowHoles( bool allow );
|
||||
|
||||
protected:
|
||||
|
||||
int truncate( off_t size, FileIO *base );
|
||||
void padFile( off_t oldSize, off_t newSize, bool forceWrite );
|
||||
|
@ -372,7 +372,18 @@ bool CipherFileIO::blockRead( unsigned char *buf, int size,
|
||||
if (reverseEncryption)
|
||||
return cipher->blockEncode( buf, size, _iv64, key );
|
||||
else
|
||||
return cipher->blockDecode( buf, size, _iv64, key );
|
||||
{
|
||||
if(_allowHoles)
|
||||
{
|
||||
// special case - leave all 0's alone
|
||||
for(int i=0; i<size; ++i)
|
||||
if(buf[i] != 0)
|
||||
return cipher->blockDecode( buf, size, _iv64, key );
|
||||
|
||||
return true;
|
||||
} else
|
||||
return cipher->blockDecode( buf, size, _iv64, key );
|
||||
}
|
||||
}
|
||||
|
||||
bool CipherFileIO::streamRead( unsigned char *buf, int size,
|
||||
|
@ -726,7 +726,8 @@ shared_ptr<FileNode> DirNode::findOrCreate( const char *plainName)
|
||||
config->uniqueIV,
|
||||
config->externalIVChaining,
|
||||
config->forceDecode,
|
||||
config->reverseEncryption) );
|
||||
config->reverseEncryption,
|
||||
config->allowHoles) );
|
||||
|
||||
if(config->externalIVChaining)
|
||||
node->setName(0, 0, iv);
|
||||
|
@ -101,6 +101,7 @@ public:
|
||||
bool externalIVChaining;
|
||||
bool forceDecode; // force decoding, even if errors are detected
|
||||
bool reverseEncryption;
|
||||
bool allowHoles; // allow holes in files
|
||||
Config()
|
||||
: fsSubVersion(0)
|
||||
, blockSize(1)
|
||||
@ -111,6 +112,7 @@ public:
|
||||
, externalIVChaining( false )
|
||||
, forceDecode( false )
|
||||
, reverseEncryption ( false )
|
||||
, allowHoles( false )
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
using namespace std;
|
||||
using namespace rel;
|
||||
using namespace rlog;
|
||||
using boost::dynamic_pointer_cast;
|
||||
|
||||
/*
|
||||
TODO: locking at the FileNode level is inefficient, since this precludes
|
||||
@ -65,7 +66,8 @@ FileNode::FileNode(DirNode *parent_,
|
||||
const char *plaintextName_, const char *cipherName_,
|
||||
const shared_ptr<Cipher> &dataCipher, const CipherKey &key,
|
||||
int blockSize, int blockMACBytes, int blockMACRandBytes, bool uniqueIV,
|
||||
bool externalIVChaining_, bool forceDecode, bool reverseEncryption_ )
|
||||
bool externalIVChaining_, bool forceDecode, bool reverseEncryption_,
|
||||
bool allowHoles )
|
||||
{
|
||||
pthread_mutex_init( &mutex, 0 );
|
||||
|
||||
@ -88,6 +90,9 @@ FileNode::FileNode(DirNode *parent_,
|
||||
io = shared_ptr<FileIO>(new MACFileIO(io, dataCipher, key,
|
||||
blockSize,blockMACBytes,blockMACRandBytes,forceDecode));
|
||||
}
|
||||
|
||||
if(allowHoles)
|
||||
dynamic_pointer_cast<BlockFileIO>(io)->allowHoles( allowHoles );
|
||||
}
|
||||
|
||||
FileNode::~FileNode()
|
||||
|
@ -43,7 +43,8 @@ public:
|
||||
bool uniqueIV, // enable per-file initialization vectors
|
||||
bool externalIVChaining,
|
||||
bool forceDecode, // decode, even if decoding errors are detected
|
||||
bool reverseEncryption );
|
||||
bool reverseEncryption,
|
||||
bool allowHoles );
|
||||
~FileNode();
|
||||
|
||||
const char *plaintextName() const;
|
||||
|
@ -272,6 +272,8 @@ bool readV5Config( const char *configFile, EncFSConfig *config,
|
||||
config->blockMACRandBytes =
|
||||
cfgRdr["blockMACRandBytes"].readInt(0);
|
||||
|
||||
config->allowHoles = cfgRdr["allowHoles"].readBool( false );
|
||||
|
||||
ok = true;
|
||||
} catch( rlog::Error &err)
|
||||
{
|
||||
@ -411,6 +413,7 @@ bool writeV5Config( const char *configFile, EncFSConfig *config )
|
||||
cfg["uniqueIV"] << config->uniqueIV;
|
||||
cfg["chainedIV"] << config->chainedNameIV;
|
||||
cfg["externalIV"] << config->externalIVChaining;
|
||||
cfg["allowHoles"] << config->allowHoles;
|
||||
|
||||
return cfg.save( configFile );
|
||||
}
|
||||
@ -680,22 +683,34 @@ int selectBlockSize( const Cipher::CipherAlgorithm &alg )
|
||||
}
|
||||
|
||||
static
|
||||
void selectBlockMAC(int *macBytes, int *macRandBytes)
|
||||
bool boolDefaultNo(const char *prompt)
|
||||
{
|
||||
// xgroup(setup)
|
||||
cout << _("Enable block authentication code headers\n"
|
||||
"on every block in a file? This adds about 12 bytes per block\n"
|
||||
"to the storage requirements for a file, and significantly affects\n"
|
||||
"performance but it also means [almost] any modifications or errors\n"
|
||||
"within a block will be caught and will cause a read error.\n"
|
||||
"The default here is No. \n"
|
||||
"Any response that does not begin with 'y' will mean No: ");
|
||||
cout << prompt << "\n";
|
||||
cout << _("The default here is No.\n"
|
||||
"Any response that does not begin with 'y' will mean No: ");
|
||||
|
||||
char answer[10];
|
||||
fgets( answer, sizeof(answer), stdin );
|
||||
cout << "\n";
|
||||
|
||||
if(tolower(answer[0]) == 'y')
|
||||
if(tolower(answer[0]) == 'n')
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
void selectBlockMAC(int *macBytes, int *macRandBytes)
|
||||
{
|
||||
// xgroup(setup)
|
||||
bool addMAC = boolDefaultNo(
|
||||
_("Enable block authentication code headers\n"
|
||||
"on every block in a file? This adds about 12 bytes per block\n"
|
||||
"to the storage requirements for a file, and significantly affects\n"
|
||||
"performance but it also means [almost] any modifications or errors\n"
|
||||
"within a block will be caught and will cause a read error."));
|
||||
|
||||
if(addMAC)
|
||||
{
|
||||
*macBytes = 8;
|
||||
|
||||
@ -708,6 +723,7 @@ void selectBlockMAC(int *macBytes, int *macRandBytes)
|
||||
"penalty. \n"
|
||||
"Select a number of bytes, from 0 (no random bytes) to 8: ");
|
||||
|
||||
char answer[10];
|
||||
int randSize = 0;
|
||||
fgets( answer, sizeof(answer), stdin );
|
||||
cout << "\n";
|
||||
@ -727,58 +743,11 @@ void selectBlockMAC(int *macBytes, int *macRandBytes)
|
||||
}
|
||||
|
||||
static
|
||||
bool selectUniqueIV()
|
||||
bool boolDefaultYes(const char *prompt)
|
||||
{
|
||||
// xgroup(setup)
|
||||
cout << _("Enable per-file initialization vectors?\n"
|
||||
"This adds about 8 bytes per file to the storage requirements.\n"
|
||||
"It should not affect performance except possibly with applications\n"
|
||||
"which rely on block-aligned file io for performance.\n"
|
||||
"The default here is Yes. \n"
|
||||
"Any response that does not begin with 'n' will mean Yes: ");
|
||||
|
||||
char answer[10];
|
||||
fgets( answer, sizeof(answer), stdin );
|
||||
cout << "\n";
|
||||
|
||||
if(tolower(answer[0]) == 'n')
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool selectChainedIV()
|
||||
{
|
||||
// xgroup(setup)
|
||||
cout << _("Enable filename initialization vector chaining?\n"
|
||||
"This makes filename encoding dependent on the complete path, \n"
|
||||
"rather then encoding each path element individually. \n"
|
||||
"This is normally desireable, therefor the default is Yes. \n"
|
||||
"Any response that does not begin with 'n' will mean Yes: ");
|
||||
|
||||
char answer[10];
|
||||
fgets( answer, sizeof(answer), stdin );
|
||||
cout << "\n";
|
||||
|
||||
if(tolower(answer[0]) == 'n')
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool selectExternalChainedIV()
|
||||
{
|
||||
// xgroup(setup)
|
||||
cout << _("Enable filename to IV header chaining?\n"
|
||||
"This makes file data encoding dependent on the complete file path.\n"
|
||||
"If a file is renamed, it will not decode sucessfully unless it\n"
|
||||
"was renamed by encfs with the proper key.\n"
|
||||
"If this option is enabled, then hard links will not be supported\n"
|
||||
"in the filesystem.\n"
|
||||
"The default is No. \n"
|
||||
"Any response that does not begin with 'y' will mean No: ");
|
||||
cout << prompt << "\n";
|
||||
cout << _("The default here is Yes.\n"
|
||||
"Any response that does not begin with 'n' will mean Yes: ");
|
||||
|
||||
char answer[10];
|
||||
fgets( answer, sizeof(answer), stdin );
|
||||
@ -790,6 +759,49 @@ bool selectExternalChainedIV()
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
bool selectUniqueIV()
|
||||
{
|
||||
// xgroup(setup)
|
||||
return boolDefaultYes(
|
||||
_("Enable per-file initialization vectors?\n"
|
||||
"This adds about 8 bytes per file to the storage requirements.\n"
|
||||
"It should not affect performance except possibly with applications\n"
|
||||
"which rely on block-aligned file io for performance."));
|
||||
}
|
||||
|
||||
static
|
||||
bool selectChainedIV()
|
||||
{
|
||||
// xgroup(setup)
|
||||
return boolDefaultYes(
|
||||
_("Enable filename initialization vector chaining?\n"
|
||||
"This makes filename encoding dependent on the complete path, \n"
|
||||
"rather then encoding each path element individually."));
|
||||
}
|
||||
|
||||
static
|
||||
bool selectExternalChainedIV()
|
||||
{
|
||||
// xgroup(setup)
|
||||
return boolDefaultNo(
|
||||
_("Enable filename to IV header chaining?\n"
|
||||
"This makes file data encoding dependent on the complete file path.\n"
|
||||
"If a file is renamed, it will not decode sucessfully unless it\n"
|
||||
"was renamed by encfs with the proper key.\n"
|
||||
"If this option is enabled, then hard links will not be supported\n"
|
||||
"in the filesystem."));
|
||||
}
|
||||
|
||||
static
|
||||
bool selectZeroBlockPassThrough()
|
||||
{
|
||||
// xgroup(setup)
|
||||
return boolDefaultNo(
|
||||
_("Enable file-hole pass-through?\n"
|
||||
"This avoids writing encrypted blocks when file holes are created."));
|
||||
}
|
||||
|
||||
RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
|
||||
bool enableIdleTracking, bool forceDecode,
|
||||
const std::string &passwordProgram,
|
||||
@ -822,6 +834,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
|
||||
bool uniqueIV = false;
|
||||
bool chainedIV = false;
|
||||
bool externalIV = false;
|
||||
bool allowHoles = false;
|
||||
|
||||
if (reverseEncryption)
|
||||
{
|
||||
@ -861,13 +874,11 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
|
||||
{
|
||||
// xgroup(setup)
|
||||
cout << _("Standard configuration selected.") << "\n";
|
||||
// look for blowfish with 160 bit key..
|
||||
// block name encoding is now standard as well..
|
||||
// per-file initialization vectors are now standard, as they shouldn't
|
||||
// have any significant performance penalty.
|
||||
keySize = 160;
|
||||
// AES w/ 192 bit key, block name encoding, per-file initialization
|
||||
// vectors are all standard.
|
||||
keySize = 192;
|
||||
blockSize = DefaultBlockSize;
|
||||
alg = findCipherAlgorithm("Blowfish", keySize);
|
||||
alg = findCipherAlgorithm("AES", keySize);
|
||||
blockMACBytes = 0;
|
||||
externalIV = false;
|
||||
nameIOIface = BlockNameIO::CurrentInterface();
|
||||
@ -917,10 +928,11 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
|
||||
// xgroup(setup)
|
||||
cout << _("External chained IV disabled, as both 'IV chaining'\n"
|
||||
"and 'unique IV' features are required for this option.")
|
||||
<< endl;
|
||||
<< "\n";
|
||||
externalIV = false;
|
||||
}
|
||||
selectBlockMAC(&blockMACBytes, &blockMACRandBytes);
|
||||
allowHoles = selectZeroBlockPassThrough();
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,6 +961,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
|
||||
config.uniqueIV = uniqueIV;
|
||||
config.chainedNameIV = chainedIV;
|
||||
config.externalIVChaining = externalIV;
|
||||
config.allowHoles = allowHoles;
|
||||
|
||||
cout << "\n";
|
||||
// xgroup(setup)
|
||||
@ -1035,6 +1048,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
|
||||
dirNodeConfig->externalIVChaining = config.externalIVChaining;
|
||||
dirNodeConfig->forceDecode = forceDecode;
|
||||
dirNodeConfig->reverseEncryption = reverseEncryption;
|
||||
dirNodeConfig->allowHoles = config.allowHoles;
|
||||
|
||||
rootInfo = RootPtr( new EncFS_Root );
|
||||
rootInfo->cipher = cipher;
|
||||
@ -1145,6 +1159,11 @@ void showFSInfo( const EncFSConfig &config )
|
||||
// xgroup(diag)
|
||||
cout << _("File data IV is chained to filename IV.\n");
|
||||
}
|
||||
if(config.allowHoles)
|
||||
{
|
||||
// xgroup(diag)
|
||||
cout << _("File holes passed through to ciphertext.\n");
|
||||
}
|
||||
cout << "\n";
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ struct EncFSConfig
|
||||
bool externalIVChaining; // IV seeding by filename IV chaining
|
||||
|
||||
bool chainedNameIV; // filename IV chaining
|
||||
bool allowHoles; // allow holes in files (implicit zero blocks)
|
||||
};
|
||||
|
||||
enum ConfigType
|
||||
|
@ -30,6 +30,8 @@
|
||||
using namespace rlog;
|
||||
using namespace rel;
|
||||
using namespace std;
|
||||
using boost::shared_ptr;
|
||||
using boost::dynamic_pointer_cast;
|
||||
|
||||
static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info);
|
||||
//
|
||||
@ -157,6 +159,14 @@ off_t MACFileIO::getSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
void MACFileIO::allowHoles( bool allow )
|
||||
{
|
||||
BlockFileIO::allowHoles( allow );
|
||||
shared_ptr<BlockFileIO> bf = dynamic_pointer_cast<BlockFileIO>( base );
|
||||
if(bf)
|
||||
bf->allowHoles( allow );
|
||||
}
|
||||
|
||||
ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
|
||||
{
|
||||
int headerSize = macBytes + randBytes;
|
||||
@ -173,31 +183,49 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
|
||||
// get the data from the base FileIO layer
|
||||
ssize_t readSize = base->read( tmp );
|
||||
|
||||
// don't store zeros if configured for zero-block pass-through
|
||||
bool skipBlock;
|
||||
if( _allowHoles )
|
||||
{
|
||||
skipBlock = true;
|
||||
for(int i=0; i<readSize; ++i)
|
||||
if(tmp.data[i] != 0)
|
||||
{
|
||||
skipBlock = false;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
skipBlock = false;
|
||||
|
||||
if(readSize > headerSize)
|
||||
{
|
||||
// At this point the data has been decoded. So, compute the MAC of the
|
||||
// block and check against the checksum stored in the header..
|
||||
uint64_t mac = cipher->MAC_64( tmp.data + macBytes,
|
||||
readSize - macBytes, key );
|
||||
if(!skipBlock)
|
||||
{
|
||||
// At this point the data has been decoded. So, compute the MAC of
|
||||
// the block and check against the checksum stored in the header..
|
||||
uint64_t mac = cipher->MAC_64( tmp.data + macBytes,
|
||||
readSize - macBytes, key );
|
||||
|
||||
for(int i=0; i<macBytes; ++i, mac >>= 8)
|
||||
{
|
||||
int test = mac & 0xff;
|
||||
int stored = tmp.data[i];
|
||||
if(test != stored)
|
||||
{
|
||||
// uh oh..
|
||||
long blockNum = req.offset / bs;
|
||||
rWarning(_("MAC comparison failure in block %li"),
|
||||
blockNum);
|
||||
if( !warnOnly )
|
||||
{
|
||||
MemoryPool::release( mb );
|
||||
throw ERROR(_("MAC comparison failure, refusing to read"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int i=0; i<macBytes; ++i, mac >>= 8)
|
||||
{
|
||||
int test = mac & 0xff;
|
||||
int stored = tmp.data[i];
|
||||
if(test != stored)
|
||||
{
|
||||
// uh oh..
|
||||
long blockNum = req.offset / bs;
|
||||
rWarning(_("MAC comparison failure in block %li"),
|
||||
blockNum);
|
||||
if( !warnOnly )
|
||||
{
|
||||
MemoryPool::release( mb );
|
||||
throw ERROR(
|
||||
_("MAC comparison failure, refusing to read"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now copy the data to the output buffer
|
||||
readSize -= headerSize;
|
||||
|
@ -53,6 +53,8 @@ public:
|
||||
|
||||
virtual bool isWritable() const;
|
||||
|
||||
virtual void allowHoles( bool allow );
|
||||
|
||||
private:
|
||||
virtual ssize_t readOneBlock( const IORequest &req ) const;
|
||||
virtual bool writeOneBlock( const IORequest &req );
|
||||
|
Loading…
Reference in New Issue
Block a user