diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp index 3dae887..60ca86f 100644 --- a/encfs/FileUtils.cpp +++ b/encfs/FileUtils.cpp @@ -855,14 +855,21 @@ static bool boolDefaultYes(const char *prompt) { /** * Ask the user whether to enable block MAC and random header bytes */ -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.")); +static void selectBlockMAC(int *macBytes, int *macRandBytes, bool forceMac) { + bool addMAC = false; + if (!forceMac) { + // xgroup(setup) + 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.")); + } else { + cout << _("\n\nYou specified --require-macs. " + "Enabling block authentication code headers...\n\n"); + addMAC = true; + } if (addMAC) *macBytes = 8; @@ -1021,6 +1028,10 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { blockSize = DefaultBlockSize; alg = findCipherAlgorithm("AES", keySize); nameIOIface = BlockNameIO::CurrentInterface(); + + if (opts->requireMac) { + blockMACBytes = 8; + } } if (answer[0] == 'x' || alg.name.empty()) { @@ -1060,7 +1071,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { << "\n"; externalIV = false; } - selectBlockMAC(&blockMACBytes, &blockMACRandBytes); + selectBlockMAC(&blockMACBytes, &blockMACRandBytes, opts->requireMac); allowHoles = selectZeroBlockPassThrough(); } } @@ -1490,6 +1501,12 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { shared_ptr config(new EncFSConfig); if (readConfig(opts->rootDir, config) != Config_None) { + if (config->blockMACBytes == 0 && opts->requireMac) { + cout + << _("The configuration disabled MAC, but you passed --require-macs\n"); + return rootInfo; + } + if (opts->reverseEncryption) { if (config->blockMACBytes != 0 || config->blockMACRandBytes != 0 || config->externalIVChaining || diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index 0cedb14..2e4f512 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -88,6 +88,8 @@ struct EncFS_Opts { bool readOnly; // Mount read-only + bool requireMac; // Throw an error if MAC is disabled + ConfigMode configMode; EncFS_Opts() { @@ -104,6 +106,7 @@ struct EncFS_Opts { configMode = Config_Prompt; noCache = false; readOnly = false; + requireMac = false; } }; diff --git a/encfs/encfs.pod b/encfs/encfs.pod index d4e9abd..9cfc581 100644 --- a/encfs/encfs.pod +++ b/encfs/encfs.pod @@ -121,6 +121,15 @@ password. If this succeeds, then the filesystem becomes available again. Do not mount the filesystem when encfs starts; instead, delay mounting until first use. This option only makes sense with B<--ondemand>. +=item B<--require-macs> + +If creating a new filesystem, this forces block authentication code headers to +be enabled. When mounting an existing filesystem, this causes encfs to exit +if block authentication code headers are not enabled. + +This can be used to improve security in case the ciphertext is vulnerable to +tampering, by preventing an attacker from disabling MACs in the config file. + =item B<--reverse> Normally B provides a plaintext view of data on demand. Normally it diff --git a/encfs/main.cpp b/encfs/main.cpp index 9281e60..0fdcb0c 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -61,6 +61,7 @@ extern "C" void fuse_unmount_compat22(const char *mountpoint); * not have a short version */ #define LONG_OPT_ANNOTATE 513 #define LONG_OPT_NOCACHE 514 +#define LONG_OPT_REQUIRE_MAC 515 using namespace std; using namespace rlog; @@ -195,6 +196,7 @@ static bool processArgs(int argc, char *argv[], out->opts->useStdin = false; out->opts->annotate = false; out->opts->reverseEncryption = false; + out->opts->requireMac = false; bool useDefaultFlags = true; @@ -229,6 +231,7 @@ static bool processArgs(int argc, char *argv[], {"reverse", 0, 0, 'r'}, // reverse encryption {"standard", 0, 0, '1'}, // standard configuration {"paranoia", 0, 0, '2'}, // standard configuration + {"require-macs", 0, 0, LONG_OPT_REQUIRE_MAC}, // require MACs {0, 0, 0, 0}}; while (1) { @@ -263,6 +266,9 @@ static bool processArgs(int argc, char *argv[], case LONG_OPT_ANNOTATE: out->opts->annotate = true; break; + case LONG_OPT_REQUIRE_MAC: + out->opts->requireMac = true; + break; case 'f': out->isDaemon = false; // this option was added in fuse 2.x