From 67c72dfe99fd0767d9608583a79a251cf9a7052b Mon Sep 17 00:00:00 2001 From: Valient Gough Date: Thu, 26 Apr 2012 02:34:15 +0000 Subject: [PATCH] Replace boost::serialization. Boost::serialization and the corresponding internal configuration structure are replace with a protocol buffer message. Dropped support for writing old configuration files. Only the new format can be written. A password change will write a newly formatted configuration file, but does not delete the old file. Version bumped to 1.8 git-svn-id: http://encfs.googlecode.com/svn/trunk@80 db9cf616-1c43-0410-9cb8-a902689de0d6 --- ChangeLog | 28 ++ configure.ac | 18 +- encfs/BlockFileIO.cpp | 3 +- encfs/BlockNameIO.cpp | 9 +- encfs/BlockNameIO.h | 6 +- encfs/Cipher.cpp | 3 +- encfs/Cipher.h | 12 +- encfs/CipherFileIO.cpp | 11 +- encfs/CipherFileIO.h | 2 +- encfs/DirNode.cpp | 4 +- encfs/FSConfig.h | 87 +--- encfs/FileIO.h | 2 +- encfs/FileNode.cpp | 6 +- encfs/FileUtils.cpp | 886 +++++++++++++++++++---------------------- encfs/FileUtils.h | 28 +- encfs/Interface.cpp | 232 +++-------- encfs/Interface.h | 71 +--- encfs/MACFileIO.cpp | 22 +- encfs/MACFileIO.h | 2 +- encfs/Makefile.am | 19 +- encfs/MemoryPool.cpp | 33 ++ encfs/MemoryPool.h | 9 + encfs/NameIO.cpp | 3 +- encfs/NameIO.h | 10 +- encfs/NullCipher.cpp | 3 +- encfs/NullCipher.h | 6 +- encfs/NullNameIO.cpp | 3 +- encfs/NullNameIO.h | 4 +- encfs/RawFileIO.cpp | 6 +- encfs/RawFileIO.h | 2 +- encfs/SSL_Cipher.cpp | 33 +- encfs/SSL_Cipher.h | 8 +- encfs/StreamNameIO.cpp | 7 +- encfs/StreamNameIO.h | 6 +- encfs/XmlReader.cpp | 191 +++++++++ encfs/XmlReader.h | 78 ++++ encfs/config.proto | 36 ++ encfs/encfsctl.cpp | 50 +-- encfs/main.cpp | 1 - encfs/test.cpp | 48 +-- 40 files changed, 1028 insertions(+), 960 deletions(-) create mode 100644 encfs/XmlReader.cpp create mode 100644 encfs/XmlReader.h create mode 100644 encfs/config.proto diff --git a/ChangeLog b/ChangeLog index 963bcf6..af35923 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +Thu Mar 22 2012 Valient Gough + * skip name collision test + * bump version to 1.8.0 + * drop boost serialization from requirements + +Sun Mar 4 2012 Valient Gough + * bump ld version + * replace EncFSConfig with protocol buffer + +Fri Mar 2 2012 Valient Gough + * replace boost serialization with protocol buffer support + +Wed Feb 29 2012 Valient Gough + * switch to OpenSSL B64 decode to be comptible with boost serialization + * implement v6 config reader using XML reader based on tinyxml + +Mon Jan 2 2012 Valient Gough + * test for name case collisions in b64 coding + +Thu Dec 29 2011 Valient Gough + * add base32 support for name encoding + +Thu Mar 3 2011 Valient Gough + * update copyright on BlockNameIO + +Thu Dec 29 2011 Valient Gough + * revert fuse_unmount change, wasn't working + Wed Dec 28 2011 Valient Gough * remove m4-local * bump version to 1.7.5 diff --git a/configure.ac b/configure.ac index 32fcf89..0a1d3e5 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(encfs/encfs.h) dnl a source file from your sub dir AC_CONFIG_AUX_DIR([build-aux]) -AM_INIT_AUTOMAKE(encfs, 1.7.5) dnl searches for some needed programs +AM_INIT_AUTOMAKE(encfs, 1.8.0) dnl searches for some needed programs AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST @@ -48,7 +48,6 @@ AX_PTHREAD AX_BOOST_BASE([1.34]) AX_BOOST_SYSTEM -AX_BOOST_SERIALIZATION AX_BOOST_FILESYSTEM dnl Need to include any user specified flags in the tests below, as they might @@ -203,6 +202,21 @@ if test "$with_rlog" = "test" && test "x$RLOG_LIBS" = "x"; then [AC_MSG_ERROR([EncFS depends on librlog])]) fi +# find Protocol Buffers +PKG_CHECK_MODULES(PROTOBUF, protobuf >= 2.0) +AC_PATH_PROG(PROTOC, protoc, [no]) +if test "$PROTOC" == "no"; then + AC_MSG_FAILURE([Protocol Buffers compiler 'protoc' is required to build.]) +fi + +# find TinyXML +AC_LANG_PUSH([C++]) +CPPFLAGS="$CPPFLAGS -DTIXML_USE_STL" +AC_CHECK_HEADER([tinyxml.h],,[AC_MSG_ERROR([tinyxml.h not found])]) +AC_CHECK_LIB([tinyxml],[main],, + [AC_MSG_ERROR([you must install libtinyxml dev])]) +AC_LANG_POP([C++]) + # look for pod2man program for building man pages AC_PATH_PROG(POD2MAN, pod2man, [no]) AC_PATH_PROG(POD2HTML, pod2html, [no]) diff --git a/encfs/BlockFileIO.cpp b/encfs/BlockFileIO.cpp index e6b9df5..9d65313 100644 --- a/encfs/BlockFileIO.cpp +++ b/encfs/BlockFileIO.cpp @@ -18,6 +18,7 @@ #include "BlockFileIO.h" #include "MemoryPool.h" +#include "config.pb.h" #include #include @@ -38,7 +39,7 @@ static void clearCache( IORequest &req, int blockSize ) BlockFileIO::BlockFileIO( int blockSize, const FSConfigPtr &cfg ) : _blockSize( blockSize ) - , _allowHoles( cfg->config->allowHoles ) + , _allowHoles( cfg->config->allow_holes() ) { rAssert( _blockSize > 1 ); _cache.data = new unsigned char [ _blockSize ]; diff --git a/encfs/BlockNameIO.cpp b/encfs/BlockNameIO.cpp index c754bab..34366a3 100644 --- a/encfs/BlockNameIO.cpp +++ b/encfs/BlockNameIO.cpp @@ -31,7 +31,6 @@ #include "i18n.h" using namespace rlog; -using namespace rel; using namespace boost; static RLogChannel * Info = DEF_CHANNEL( "info/nameio", Log_Info ); @@ -94,16 +93,16 @@ Interface BlockNameIO::CurrentInterface(bool caseSensitive) { // implement major version 4 plus support for two prior versions if (caseSensitive) - return Interface("nameio/block32", 4, 0, 2); + return makeInterface("nameio/block32", 4, 0, 2); else - return Interface("nameio/block", 4, 0, 2); + return makeInterface("nameio/block", 4, 0, 2); } -BlockNameIO::BlockNameIO( const rel::Interface &iface, +BlockNameIO::BlockNameIO( const Interface &iface, const shared_ptr &cipher, const CipherKey &key, int blockSize, bool caseSensitiveEncoding ) - : _interface( iface.current() ) + : _interface( iface.major() ) , _bs( blockSize ) , _cipher( cipher ) , _key( key ) diff --git a/encfs/BlockNameIO.h b/encfs/BlockNameIO.h index 591d042..a2a0f5f 100644 --- a/encfs/BlockNameIO.h +++ b/encfs/BlockNameIO.h @@ -33,15 +33,15 @@ class Cipher; class BlockNameIO : public NameIO { public: - static rel::Interface CurrentInterface(bool caseSensitive = false); + static Interface CurrentInterface(bool caseSensitive = false); - BlockNameIO( const rel::Interface &iface, + BlockNameIO( const Interface &iface, const boost::shared_ptr &cipher, const CipherKey &key, int blockSize, bool caseSensitiveEncoding = false ); virtual ~BlockNameIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual int maxEncodedNameLen( int plaintextNameLen ) const; virtual int maxDecodedNameLen( int encodedNameLen ) const; diff --git a/encfs/Cipher.cpp b/encfs/Cipher.cpp index fe00b3a..1c1047c 100644 --- a/encfs/Cipher.cpp +++ b/encfs/Cipher.cpp @@ -34,7 +34,6 @@ #include "SSL_Cipher.h" using namespace std; -using namespace rel; using boost::shared_ptr; #define REF_MODULE(TYPE) \ @@ -150,7 +149,7 @@ shared_ptr Cipher::New( const Interface &iface, int keyLen ) for(it = gCipherMap->begin(); it != mapEnd; ++it) { // TODO: we should look for the newest implementation.. - if( it->second.iface.implements( iface ) ) + if( implements(it->second.iface, iface) ) { CipherConstructor fn = it->second.constructor; // pass in requested interface.. diff --git a/encfs/Cipher.h b/encfs/Cipher.h index a5a6a38..83648f3 100644 --- a/encfs/Cipher.h +++ b/encfs/Cipher.h @@ -42,14 +42,14 @@ class Cipher public: // if no key length was indicated when cipher was registered, then keyLen // <= 0 will be used. - typedef boost::shared_ptr (*CipherConstructor)( const rel::Interface &iface, + typedef boost::shared_ptr (*CipherConstructor)( const Interface &iface, int keyLenBits ); struct CipherAlgorithm { std::string name; std::string description; - rel::Interface iface; + Interface iface; Range keyLength; Range blockSize; }; @@ -59,7 +59,7 @@ public: static AlgorithmList GetAlgorithmList( bool includeHidden = false ); - static boost::shared_ptr New( const rel::Interface &iface, + static boost::shared_ptr New( const Interface &iface, int keyLen = -1); static boost::shared_ptr New( const std::string &cipherName, int keyLen = -1 ); @@ -67,12 +67,12 @@ public: static bool Register(const char *cipherName, const char *description, - const rel::Interface &iface, + const Interface &iface, CipherConstructor constructor, bool hidden = false); static bool Register(const char *cipherName, const char *description, - const rel::Interface &iface, + const Interface &iface, const Range &keyLength, const Range &blockSize, CipherConstructor constructor, bool hidden = false); @@ -81,7 +81,7 @@ public: Cipher(); virtual ~Cipher(); - virtual rel::Interface interface() const =0; + virtual Interface interface() const =0; // create a new key based on a password // if iterationCount == 0, then iteration count will be determined diff --git a/encfs/CipherFileIO.cpp b/encfs/CipherFileIO.cpp index 11e97a0..d7af35b 100644 --- a/encfs/CipherFileIO.cpp +++ b/encfs/CipherFileIO.cpp @@ -19,6 +19,7 @@ #include "Cipher.h" #include "MemoryPool.h" +#include "config.pb.h" #include #include @@ -34,7 +35,7 @@ using boost::shared_ptr; filesystem at the filesystem configuration level. When headers are disabled, 2:0 is compatible with version 1:0. */ -static rel::Interface CipherFileIO_iface("FileIO/Cipher", 2, 0, 1); +static Interface CipherFileIO_iface = makeInterface("FileIO/Cipher", 2, 0, 1); const int HEADER_SIZE = 8; // 64 bit initialization vector.. @@ -51,9 +52,9 @@ static bool checkSize( int fsBlockSize, int cipherBlockSize ) CipherFileIO::CipherFileIO( const shared_ptr &_base, const FSConfigPtr &cfg) - : BlockFileIO( cfg->config->blockSize, cfg ) + : BlockFileIO( cfg->config->block_size(), cfg ) , base( _base ) - , haveHeader( cfg->config->uniqueIV ) + , haveHeader( cfg->config->unique_iv() ) , externalIV( 0 ) , fileIV( 0 ) , lastFlags( 0 ) @@ -65,7 +66,7 @@ CipherFileIO::CipherFileIO( const shared_ptr &_base, static bool warnOnce = false; if(!warnOnce) - warnOnce = checkSize( fsConfig->config->blockSize, + warnOnce = checkSize( fsConfig->config->block_size(), fsConfig->cipher->cipherBlockSize() ); } @@ -73,7 +74,7 @@ CipherFileIO::~CipherFileIO() { } -rel::Interface CipherFileIO::interface() const +Interface CipherFileIO::interface() const { return CipherFileIO_iface; } diff --git a/encfs/CipherFileIO.h b/encfs/CipherFileIO.h index 04200d5..d4b4c1b 100644 --- a/encfs/CipherFileIO.h +++ b/encfs/CipherFileIO.h @@ -38,7 +38,7 @@ public: const FSConfigPtr &cfg); virtual ~CipherFileIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual void setFileName( const char *fileName ); virtual const char *getFileName() const; diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp index 18429f1..7d95fcc 100644 --- a/encfs/DirNode.cpp +++ b/encfs/DirNode.cpp @@ -690,7 +690,7 @@ int DirNode::link( const char *from, const char *to ) rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str()); int res = -EPERM; - if( fsConfig->config->externalIVChaining ) + if( fsConfig->config->external_iv() ) { rLog(Info, "hard links not supported with external IV chaining!"); } else @@ -756,7 +756,7 @@ shared_ptr DirNode::findOrCreate( const char *plainName) plainName, (rootDir + cipherName).c_str())); - if(fsConfig->config->externalIVChaining) + if(fsConfig->config->external_iv()) node->setName(0, 0, iv); rLog(Info, "created FileNode for %s", node->cipherName()); diff --git a/encfs/FSConfig.h b/encfs/FSConfig.h index 81ac0eb..32bc329 100644 --- a/encfs/FSConfig.h +++ b/encfs/FSConfig.h @@ -32,88 +32,37 @@ enum ConfigType { Config_None = 0, Config_Prehistoric, - Config_V3, - Config_V4, - Config_V5, - Config_V6 + Config_V3 = 3, + Config_V4 = 4, + Config_V5 = 5, + Config_V6 = 6, + Config_V7 = 7 }; class EncFS_Opts; class Cipher; class NameIO; +class EncfsConfig; -struct EncFSConfig -{ - ConfigType cfgType; +CipherKey getUserKey(const EncfsConfig &config, bool useStdin); +CipherKey getUserKey(const EncfsConfig &config, + const std::string &passwordProgram, + const std::string &rootDir); - std::string creator; - int subVersion; - - // interface of cipher - rel::Interface cipherIface; - // interface used for file name coding - rel::Interface nameIface; - int keySize; // reported in bits - int blockSize; // reported in bytes - - std::vector keyData; - - std::vector salt; - int kdfIterations; - long desiredKDFDuration; - - int blockMACBytes; // MAC headers on blocks.. - int blockMACRandBytes; // number of random bytes in the block header - - bool uniqueIV; // per-file Initialization Vector - bool externalIVChaining; // IV seeding by filename IV chaining - - bool chainedNameIV; // filename IV chaining - bool allowHoles; // allow holes in files (implicit zero blocks) - - EncFSConfig() - : keyData() - , salt() - { - cfgType = Config_None; - subVersion = 0; - blockMACBytes = 0; - blockMACRandBytes = 0; - uniqueIV = false; - externalIVChaining = false; - chainedNameIV = false; - allowHoles = false; - - kdfIterations = 0; - desiredKDFDuration = 500; - } - - CipherKey getUserKey(bool useStdin); - CipherKey getUserKey(const std::string &passwordProgram, - const std::string &rootDir); - CipherKey getNewUserKey(); +CipherKey getNewUserKey(EncfsConfig &config, bool useStdin, + const std::string &program, const std::string &rootDir); - boost::shared_ptr getCipher() const; +boost::shared_ptr getCipher(const EncfsConfig &cfg); +boost::shared_ptr getCipher(const Interface &iface, int keySize); - // deprecated - void assignKeyData(const std::string &in); - void assignKeyData(unsigned char *data, int length); - void assignSaltData(unsigned char *data, int length); - - unsigned char *getKeyData() const; - unsigned char *getSaltData() const; - -private: - CipherKey makeKey(const char *password, int passwdLen); -}; - // helpers for serializing to/from a stream -std::ostream &operator << (std::ostream &os, const EncFSConfig &cfg); -std::istream &operator >> (std::istream &os, EncFSConfig &cfg); +std::ostream &operator << (std::ostream &os, const EncfsConfig &cfg); +std::istream &operator >> (std::istream &os, EncfsConfig &cfg); +// Filesystem state struct FSConfig { - boost::shared_ptr config; + boost::shared_ptr config; boost::shared_ptr opts; boost::shared_ptr cipher; diff --git a/encfs/FileIO.h b/encfs/FileIO.h index 1889a27..5e0df17 100644 --- a/encfs/FileIO.h +++ b/encfs/FileIO.h @@ -48,7 +48,7 @@ public: FileIO(); virtual ~FileIO(); - virtual rel::Interface interface() const =0; + virtual Interface interface() const =0; // default implementation returns 1, meaning this is not block oriented. virtual int blockSize() const; diff --git a/encfs/FileNode.cpp b/encfs/FileNode.cpp index 4d15037..2671a60 100644 --- a/encfs/FileNode.cpp +++ b/encfs/FileNode.cpp @@ -80,7 +80,7 @@ FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, shared_ptr rawIO( new RawFileIO( _cname ) ); io = shared_ptr( new CipherFileIO( rawIO, fsConfig )); - if(cfg->config->blockMACBytes || cfg->config->blockMACRandBytes) + if(cfg->config->block_mac_bytes() || cfg->config->block_mac_rand_bytes()) io = shared_ptr(new MACFileIO(io, fsConfig)); } @@ -127,7 +127,7 @@ bool FileNode::setName( const char *plaintextName_, const char *cipherName_, rDebug("calling setIV on %s", cipherName_); if(setIVFirst) { - if(fsConfig->config->externalIVChaining && !setIV(io, iv)) + if(fsConfig->config->external_iv() && !setIV(io, iv)) return false; // now change the name.. @@ -151,7 +151,7 @@ bool FileNode::setName( const char *plaintextName_, const char *cipherName_, io->setFileName( cipherName_ ); } - if(fsConfig->config->externalIVChaining && !setIV(io, iv)) + if(fsConfig->config->external_iv() && !setIV(io, iv)) { _pname = oldPName; _cname = oldCName; diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp index 9e89d5d..9972731 100644 --- a/encfs/FileUtils.cpp +++ b/encfs/FileUtils.cpp @@ -23,12 +23,14 @@ #include "encfs.h" #include "config.h" +#include "config.pb.h" #include "readpassphrase.h" #include "autosprintf.h" #include "FileUtils.h" #include "ConfigReader.h" +#include "XmlReader.h" #include "FSConfig.h" #include "DirNode.h" @@ -37,6 +39,7 @@ #include "BlockNameIO.h" #include "NullNameIO.h" #include "Context.h" +#include "MemoryPool.h" #include #include @@ -60,21 +63,18 @@ #include #include -#include -#include -#include -#include + +#include +#include // disable rlog section grouping for this file.. seems to cause problems #undef RLOG_SECTION #define RLOG_SECTION -using namespace rel; using namespace rlog; using namespace std; using namespace gnu; namespace fs = boost::filesystem; -namespace serial = boost::serialization; static const int DefaultBlockSize = 1024; // The maximum length of text passwords. If longer are needed, @@ -94,170 +94,34 @@ static const char ENCFS_ENV_STDERR[] = "encfs_stderr"; //static int V5SubVersion = 20040518; //static int V5SubVersion = 20040621; // add external IV chaining static int V5SubVersion = 20040813; // fix MACFileIO block size issues -static int V5SubVersionDefault = 0; // 20080813 was really made on 20080413 -- typo on date.. //const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option //const int V6SubVersion = 20080816; // add salt and iteration count const int V6SubVersion = 20100713; // add version field for boost 1.42+ +const int ProtoSubVersion = 20120302; + +const char ConfigFileName[] = ".encfs.txt"; + struct ConfigInfo { - const char *fileName; ConfigType type; + const char *fileName; const char *environmentOverride; bool (*loadFunc)(const char *fileName, - const boost::shared_ptr &config, - ConfigInfo *cfg); - bool (*saveFunc)(const char *fileName, - const boost::shared_ptr &config); - int currentSubVersion; - int defaultSubVersion; + EncfsConfig &config, ConfigInfo *cfg); } ConfigFileMapping[] = { - {".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config, - V6SubVersion, 0 }, + {Config_V7, ConfigFileName, "ENCFS_CONFIG", readProtoConfig }, + {Config_V6, ".encfs6.xml", "ENCFS6_CONFIG", readV6Config }, // backward compatible support for older versions - {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config, - V5SubVersion, V5SubVersionDefault }, - {".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0 }, - // no longer support earlier versions - {".encfs3", Config_V3, NULL, NULL, NULL, 0, 0 }, - {".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0 }, - {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0 }, - {NULL,Config_None, NULL, NULL, NULL, 0, 0}}; - - -#include "boost-versioning.h" - -// define serialization helpers -// TODO(vgough): eliminate entirely -namespace boost -{ -namespace serialization -{ - -template -void save(Archive &ar, const EncFSConfig &cfg, - unsigned int version) -{ - (void)version; - // version 20 (aka 20100613) - if (cfg.subVersion == 0) - ar << make_nvp("version", V6SubVersion); - else - ar << make_nvp("version", cfg.subVersion); - - ar << make_nvp("creator", cfg.creator); - ar << make_nvp("cipherAlg", cfg.cipherIface); - ar << make_nvp("nameAlg", cfg.nameIface); - ar << make_nvp("keySize", cfg.keySize); - ar << make_nvp("blockSize", cfg.blockSize); - ar << make_nvp("uniqueIV", cfg.uniqueIV); - ar << make_nvp("chainedNameIV", cfg.chainedNameIV); - ar << make_nvp("externalIVChaining", cfg.externalIVChaining); - ar << make_nvp("blockMACBytes", cfg.blockMACBytes); - ar << make_nvp("blockMACRandBytes", cfg.blockMACRandBytes); - ar << make_nvp("allowHoles", cfg.allowHoles); - - int encodedSize = cfg.keyData.size(); - ar << make_nvp("encodedKeySize", encodedSize); - ar << make_nvp("encodedKeyData", - serial::make_binary_object(cfg.getKeyData(), encodedSize)); - - // version 20080816 - int size = cfg.salt.size(); - ar << make_nvp("saltLen", size); - ar << make_nvp("saltData", - serial::make_binary_object(cfg.getSaltData(), size)); - ar << make_nvp("kdfIterations", cfg.kdfIterations); - ar << make_nvp("desiredKDFDuration", cfg.desiredKDFDuration); -} - -template -void load(Archive &ar, EncFSConfig &cfg, unsigned int version) -{ - rInfo("version = %i", version); - // TODO: figure out how to deprecate all but the first case.. - if (version == 20 || version >= 20100713) - { - rInfo("found new serialization format"); - ar >> make_nvp("version", cfg.subVersion); - } else if (version == 26800) - { - rInfo("found 20080816 version"); - cfg.subVersion = 20080816; - } else if (version == 26797) - { - rInfo("found 20080813"); - cfg.subVersion = 20080813; - } else if (version < (unsigned int)V5SubVersion) - { - rError("Invalid version %i - please fix config file", version); - } else - { - rInfo("Boost <= 1.41 compatibility mode"); - cfg.subVersion = version; - } - rInfo("subVersion = %i", cfg.subVersion); - - ar >> make_nvp("creator", cfg.creator); - ar >> make_nvp("cipherAlg", cfg.cipherIface); - ar >> make_nvp("nameAlg", cfg.nameIface); - ar >> make_nvp("keySize", cfg.keySize); - ar >> make_nvp("blockSize", cfg.blockSize); - ar >> make_nvp("uniqueIV", cfg.uniqueIV); - ar >> make_nvp("chainedNameIV", cfg.chainedNameIV); - ar >> make_nvp("externalIVChaining", cfg.externalIVChaining); - ar >> make_nvp("blockMACBytes", cfg.blockMACBytes); - ar >> make_nvp("blockMACRandBytes", cfg.blockMACRandBytes); - ar >> make_nvp("allowHoles", cfg.allowHoles); - - int encodedSize; - ar >> make_nvp("encodedKeySize", encodedSize); - rAssert(encodedSize == cfg.getCipher()->encodedKeySize()); - - unsigned char *key = new unsigned char[encodedSize]; - ar >> make_nvp("encodedKeyData", - serial::make_binary_object(key, encodedSize)); - cfg.assignKeyData(key, encodedSize); - delete[] key; - - if(cfg.subVersion >= 20080816) - { - int saltLen; - ar >> make_nvp("saltLen", saltLen); - unsigned char *salt = new unsigned char[saltLen]; - ar >> make_nvp("saltData", - serial::make_binary_object(salt, saltLen)); - cfg.assignSaltData(salt, saltLen); - delete[] salt; - - ar >> make_nvp("kdfIterations", cfg.kdfIterations); - ar >> make_nvp("desiredKDFDuration", cfg.desiredKDFDuration); - } else - { - cfg.salt.clear(); - cfg.kdfIterations = 16; - cfg.desiredKDFDuration = NormalKDFDuration; - } -} - -template -void serialize(Archive &ar, EncFSConfig &cfg, unsigned int version) -{ - split_free(ar, cfg, version); -} - -template -void serialize(Archive &ar, Interface &i, const unsigned int version) -{ - (void)version; - ar & make_nvp("name", i.name()); - ar & make_nvp("major", i.current()); - ar & make_nvp("minor", i.revision()); -} -} // namespace serialization -} // namespace boost + {Config_V5, ".encfs5", "ENCFS5_CONFIG", readV5Config }, + {Config_V3, ".encfs4", NULL, readV4Config }, + // prehistoric - no longer support + {Config_V3, ".encfs3", NULL, NULL }, + {Config_Prehistoric, ".encfs2", NULL, NULL }, + {Config_Prehistoric, ".encfs", NULL, NULL }, + {Config_None, NULL, NULL, NULL } }; EncFS_Root::EncFS_Root() { @@ -361,17 +225,14 @@ bool userAllowMkdir(int promptno, const char *path, mode_t mode ) } ConfigType readConfig_load( ConfigInfo *nm, const char *path, - const boost::shared_ptr &config ) + EncfsConfig &config ) { if( nm->loadFunc ) { try { if( (*nm->loadFunc)( path, config, nm )) - { - config->cfgType = nm->type; return nm->type; - } } catch( rlog::Error & err ) { err.log( _RLWarningChannel ); @@ -382,13 +243,11 @@ ConfigType readConfig_load( ConfigInfo *nm, const char *path, } else { // No load function - must be an unsupported type.. - config->cfgType = nm->type; - return nm->type; + return Config_None; } } -ConfigType readConfig( const string &rootDir, - const boost::shared_ptr &config ) +ConfigType readConfig( const string &rootDir, EncfsConfig &config ) { ConfigInfo *nm = ConfigFileMapping; while(nm->fileName) @@ -411,36 +270,110 @@ ConfigType readConfig( const string &rootDir, return Config_None; } +// Read a boost::serialization config file using an Xml reader.. bool readV6Config( const char *configFile, - const boost::shared_ptr &config, - ConfigInfo *info) + EncfsConfig &cfg, ConfigInfo *info) { (void)info; - fs::ifstream st( configFile ); - if(st.is_open()) + XmlReader rdr; + if (!rdr.load(configFile)) { - try - { - boost::archive::xml_iarchive ia( st ); - ia >> BOOST_SERIALIZATION_NVP( *config ); - - return true; - } catch(boost::archive::archive_exception &e) - { - rError("Archive exception: %s", e.what()); - return false; - } - } else - { - rInfo("Failed to load config file %s", configFile); + rError("Failed to load config file %s", configFile); return false; } + + XmlValuePtr serialization = rdr["boost_serialization"]; + XmlValuePtr config = (*serialization)["cfg"]; + int version = 0; + (*config)["@version"] >> version; + + // version numbering was complicated by boost::archive + if (version == 20 || version >= 20100713) + { + rInfo("found new serialization format"); + (*config)["version"] >> version; + cfg.set_revision(version); + } else if (version == 26800) + { + rInfo("found 20080816 version"); + cfg.set_revision(20080816); + } else if (version == 26797) + { + rInfo("found 20080813"); + cfg.set_revision(20080813); + } else if (version < V5SubVersion) + { + rError("Invalid version %i - please fix config file", version); + } else + { + rInfo("Boost <= 1.41 compatibility mode"); + cfg.set_revision(version); + } + rInfo("subVersion = %i", cfg.revision()); + + (*config)["creator"] >> (*cfg.mutable_creator()); + (*config)["cipherAlg"] >> (*cfg.mutable_cipher()); + (*config)["nameAlg"] >> (*cfg.mutable_naming()); + + //(*config)["keySize"] >> cfg.keySize; + int blockSize, blockMacBytes, blockMacRandBytes; + bool uniqueIv, chainedNameIv, externalIv, allowHoles; + + (*config)["blockSize"] >> blockSize; + (*config)["uniqueIV"] >> uniqueIv; + (*config)["chainedNameIV"] >> chainedNameIv; + (*config)["externalIVChaining"] >> externalIv; + (*config)["blockMACBytes"] >> blockMacBytes; + (*config)["blockMACRandBytes"] >> blockMacRandBytes; + (*config)["allowHoles"] >> allowHoles; + + cfg.set_block_size(blockSize); + cfg.set_unique_iv(uniqueIv); + cfg.set_chained_iv(chainedNameIv); + cfg.set_external_iv(externalIv); + cfg.set_block_mac_bytes(blockMacBytes); + cfg.set_block_mac_rand_bytes(blockMacRandBytes); + cfg.set_allow_holes(allowHoles); + + int encodedSize; + (*config)["encodedKeySize"] >> encodedSize; + unsigned char *key = new unsigned char[encodedSize]; + (*config)["encodedKeyData"]->readB64Data(key, encodedSize); + cfg.set_key(key, encodedSize); + delete[] key; + + int keySize; + (*config)["keySize"] >> keySize; + cfg.set_key_size(keySize); + + if(cfg.revision() >= 20080816) + { + int saltLen; + (*config)["saltLen"] >> saltLen; + unsigned char *salt = new unsigned char[saltLen]; + (*config)["saltData"]->readB64Data(salt, saltLen); + cfg.set_salt(salt, saltLen); + delete[] salt; + + int kdfIterations, desiredKDFDuration; + (*config)["kdfIterations"] >> kdfIterations; + (*config)["desiredKDFDuration"] >> desiredKDFDuration; + cfg.set_kdf_iterations(kdfIterations); + cfg.set_kdf_duration(desiredKDFDuration); + } else + { + cfg.clear_salt(); + cfg.set_kdf_iterations(16); + cfg.set_kdf_duration(NormalKDFDuration); + } + + return true; } +// Read a v5 archive, which is a proprietary binary format. bool readV5Config( const char *configFile, - const boost::shared_ptr &config, - ConfigInfo *info) + EncfsConfig &config, ConfigInfo *) { bool ok = false; @@ -450,39 +383,41 @@ bool readV5Config( const char *configFile, { try { - config->subVersion = cfgRdr["subVersion"].readInt( - info->defaultSubVersion ); - if(config->subVersion > info->currentSubVersion) + config.set_revision(cfgRdr["subVersion"].readInt(0)); + if(config.revision() > V5SubVersion) { /* config file specifies a version outside our supported range.. */ rWarning(_("Config subversion %i found, but this version of" " encfs only supports up to version %i."), - config->subVersion, info->currentSubVersion); + config.revision(), V5SubVersion); return false; } - if( config->subVersion < 20040813 ) + if( config.revision() < 20040813 ) { rError(_("This version of EncFS doesn't support " "filesystems created before 2004-08-13")); return false; } - cfgRdr["creator"] >> config->creator; - cfgRdr["cipher"] >> config->cipherIface; - cfgRdr["naming"] >> config->nameIface; - cfgRdr["keySize"] >> config->keySize; - cfgRdr["blockSize"] >> config->blockSize; + cfgRdr["creator"] >> (*config.mutable_creator()); + cfgRdr["cipher"] >> (*config.mutable_cipher()); + cfgRdr["naming"] >> (*config.mutable_naming()); - string data; - cfgRdr["keyData"] >> data; - config->assignKeyData(data); - config->uniqueIV = cfgRdr["uniqueIV"].readBool( false ); - config->chainedNameIV = cfgRdr["chainedIV"].readBool( false ); - config->externalIVChaining = cfgRdr["externalIV"].readBool( false ); - config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0); - config->blockMACRandBytes = - cfgRdr["blockMACRandBytes"].readInt(0); + int blockSize; + cfgRdr["blockSize"] >> blockSize; + config.set_block_size(blockSize); + + int keySize; + cfgRdr["keySize"] >> keySize; + config.set_key_size(keySize); + cfgRdr["keyData"] >> (*config.mutable_key()); + + config.set_unique_iv( cfgRdr["uniqueIV"].readBool( false ) ); + config.set_chained_iv( cfgRdr["chainedIV"].readBool( false ) ); + config.set_external_iv( cfgRdr["externalIV"].readBool( false ) ); + config.set_block_mac_bytes( cfgRdr["blockMACBytes"].readInt(0) ); + config.set_block_mac_rand_bytes( cfgRdr["blockMACRandBytes"].readInt(0) ); ok = true; } catch( rlog::Error &err) @@ -497,8 +432,7 @@ bool readV5Config( const char *configFile, } bool readV4Config( const char *configFile, - const boost::shared_ptr &config, - ConfigInfo *info) + EncfsConfig &config, ConfigInfo *) { bool ok = false; @@ -508,22 +442,16 @@ bool readV4Config( const char *configFile, { try { - cfgRdr["cipher"] >> config->cipherIface; - cfgRdr["keySize"] >> config->keySize; - cfgRdr["blockSize"] >> config->blockSize; - string data; - cfgRdr["keyData"] >> data; - config->assignKeyData(data); + cfgRdr["cipher"] >> (*config.mutable_cipher()); + int blockSize; + cfgRdr["blockSize"] >> blockSize; + config.set_block_size(blockSize); + + cfgRdr["keyData"] >> (*config.mutable_key()); // fill in default for V4 - config->nameIface = Interface("nameio/stream", 1, 0, 0); - config->creator = "EncFS 1.0.x"; - config->subVersion = info->defaultSubVersion; - config->blockMACBytes = 0; - config->blockMACRandBytes = 0; - config->uniqueIV = false; - config->externalIVChaining = false; - config->chainedNameIV = false; + config.mutable_naming()->MergeFrom( makeInterface("nameio/stream", 1, 0, 0) ); + config.set_creator( "EncFS 1.0.x" ); ok = true; } catch( rlog::Error &err) @@ -537,106 +465,66 @@ bool readV4Config( const char *configFile, return ok; } -bool saveConfig( ConfigType type, const string &rootDir, - const boost::shared_ptr &config ) +bool writeTextConfig( const char *fileName, const EncfsConfig &cfg ) +{ + int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 ); + if (fd < 0) + { + rError("Unable to open or create file %s", fileName); + return false; + } + + google::protobuf::io::FileOutputStream fos( fd ); + google::protobuf::TextFormat::Print( cfg, &fos ); + + fos.Close(); + return true; +} + +bool saveConfig( const string &rootDir, const EncfsConfig &config ) { bool ok = false; ConfigInfo *nm = ConfigFileMapping; - while(nm->fileName) + + // TODO(vgough): remove old config after saving a new one? + string path = rootDir + ConfigFileName; + if( nm->environmentOverride != NULL ) { - if( nm->type == type && nm->saveFunc ) - { - string path = rootDir + nm->fileName; - if( nm->environmentOverride != NULL ) - { - // use environment file if specified.. - const char *envFile = getenv( nm->environmentOverride ); - if( envFile != NULL ) - path.assign( envFile ); - } + // use environment file if specified.. + const char *envFile = getenv( nm->environmentOverride ); + if( envFile != NULL ) + path.assign( envFile ); + } - try - { - ok = (*nm->saveFunc)( path.c_str(), config ); - } catch( rlog::Error &err ) - { - err.log( _RLWarningChannel ); - ok = false; - } - break; - } - ++nm; + try + { + ok = writeTextConfig( path.c_str(), config ); + } catch( rlog::Error &err ) + { + err.log( _RLWarningChannel ); + ok = false; } return ok; } -bool writeV6Config( const char *configFile, - const boost::shared_ptr &config ) +bool readProtoConfig( const char *fileName, EncfsConfig &config, + struct ConfigInfo *) { - fs::ofstream st( configFile ); - if(!st.is_open()) + int fd = ::open( fileName, O_RDONLY, 0640 ); + if (fd < 0) + { + rError("Unable to open file %s", fileName); return false; + } + + google::protobuf::io::FileInputStream fis( fd ); + google::protobuf::TextFormat::Parse( &fis, &config ); - st << *config; return true; } -std::ostream &operator << (std::ostream &st, const EncFSConfig &cfg) -{ - boost::archive::xml_oarchive oa(st); - oa << BOOST_SERIALIZATION_NVP( cfg ); - - return st; -} - -std::istream &operator >> (std::istream &st, EncFSConfig &cfg) -{ - boost::archive::xml_iarchive ia(st); - ia >> BOOST_SERIALIZATION_NVP( cfg ); - - return st; -} - -bool writeV5Config( const char *configFile, - const boost::shared_ptr &config ) -{ - ConfigReader cfg; - - cfg["creator"] << config->creator; - cfg["subVersion"] << config->subVersion; - cfg["cipher"] << config->cipherIface; - cfg["naming"] << config->nameIface; - cfg["keySize"] << config->keySize; - cfg["blockSize"] << config->blockSize; - string key; - key.assign((char *)config->getKeyData(), config->keyData.size()); - cfg["keyData"] << key; - cfg["blockMACBytes"] << config->blockMACBytes; - cfg["blockMACRandBytes"] << config->blockMACRandBytes; - cfg["uniqueIV"] << config->uniqueIV; - cfg["chainedIV"] << config->chainedNameIV; - cfg["externalIV"] << config->externalIVChaining; - - return cfg.save( configFile ); -} - -bool writeV4Config( const char *configFile, - const boost::shared_ptr &config ) -{ - ConfigReader cfg; - - cfg["cipher"] << config->cipherIface; - cfg["keySize"] << config->keySize; - cfg["blockSize"] << config->blockSize; - string key; - key.assign((char *)config->getKeyData(), config->keyData.size()); - cfg["keyData"] << key; - - return cfg.save( configFile ); -} - static Cipher::CipherAlgorithm findCipherAlgorithm(const char *name, int keySize ) @@ -986,7 +874,7 @@ bool selectZeroBlockPassThrough() "This avoids writing encrypted blocks when file holes are created.")); } -RootPtr createV6Config( EncFS_Context *ctx, +RootPtr createConfig( EncFS_Context *ctx, const shared_ptr &opts ) { const std::string rootDir = opts->rootDir; @@ -1147,25 +1035,24 @@ RootPtr createV6Config( EncFS_Context *ctx, alg.name.c_str(), keySize, blockSize); } - shared_ptr config( new EncFSConfig ); + EncfsConfig config; - config->cfgType = Config_V6; - config->cipherIface = cipher->interface(); - config->keySize = keySize; - config->blockSize = blockSize; - config->nameIface = nameIOIface; - config->creator = "EncFS " VERSION; - config->subVersion = V6SubVersion; - config->blockMACBytes = blockMACBytes; - config->blockMACRandBytes = blockMACRandBytes; - config->uniqueIV = uniqueIV; - config->chainedNameIV = chainedIV; - config->externalIVChaining = externalIV; - config->allowHoles = allowHoles; + config.mutable_cipher()->MergeFrom( cipher->interface() ); + //config.keySize = keySize; + config.set_block_size( blockSize ); + config.mutable_naming()->MergeFrom( nameIOIface ); + config.set_creator( "EncFS " VERSION ); + config.set_revision( V6SubVersion ); + config.set_block_mac_bytes( blockMACBytes ); + config.set_block_mac_rand_bytes( blockMACRandBytes ); + config.set_unique_iv( uniqueIV ); + config.set_chained_iv( chainedIV ); + config.set_external_iv( externalIV ); + config.set_allow_holes( allowHoles ); - config->salt.clear(); - config->kdfIterations = 0; // filled in by keying function - config->desiredKDFDuration = desiredKDFDuration; + config.clear_salt(); + config.clear_kdf_iterations(); // filled in by keying function + config.set_kdf_duration( desiredKDFDuration ); cout << "\n"; // xgroup(setup) @@ -1173,7 +1060,7 @@ RootPtr createV6Config( EncFS_Context *ctx, "the following properties:") << endl; showFSInfo( config ); - if( config->externalIVChaining ) + if( config.external_iv() ) { cout << _("-------------------------- WARNING --------------------------\n") @@ -1206,17 +1093,13 @@ RootPtr createV6Config( EncFS_Context *ctx, { if (annotate) cerr << "$PROMPT$ new_passwd" << endl; - userKey = config->getUserKey( useStdin ); } - else if(!passwordProgram.empty()) - userKey = config->getUserKey( passwordProgram, rootDir ); - else - userKey = config->getNewUserKey(); + userKey = getNewUserKey( config, useStdin, passwordProgram, rootDir ); cipher->writeKey( volumeKey, encodedKey, userKey ); userKey.reset(); - config->assignKeyData(encodedKey, encodedKeySize); + config.set_key(encodedKey, encodedKeySize); delete[] encodedKey; if(!volumeKey) @@ -1226,11 +1109,11 @@ RootPtr createV6Config( EncFS_Context *ctx, return rootInfo; } - if(!saveConfig( Config_V6, rootDir, config )) + if(!saveConfig( rootDir, config )) return rootInfo; // fill in config struct - shared_ptr nameCoder = NameIO::New( config->nameIface, + shared_ptr nameCoder = NameIO::New( config.naming(), cipher, volumeKey ); if(!nameCoder) { @@ -1240,14 +1123,14 @@ RootPtr createV6Config( EncFS_Context *ctx, return rootInfo; } - nameCoder->setChainedNameIV( config->chainedNameIV ); + nameCoder->setChainedNameIV( config.chained_iv() ); nameCoder->setReverseEncryption( reverseEncryption ); FSConfigPtr fsConfig (new FSConfig); fsConfig->cipher = cipher; fsConfig->key = volumeKey; fsConfig->nameCoding = nameCoder; - fsConfig->config = config; + fsConfig->config = boost::shared_ptr(new EncfsConfig(config)); fsConfig->forceDecode = forceDecode; fsConfig->reverseEncryption = reverseEncryption; fsConfig->idleTracking = enableIdleTracking; @@ -1262,27 +1145,27 @@ RootPtr createV6Config( EncFS_Context *ctx, return rootInfo; } -void showFSInfo( const boost::shared_ptr &config ) +void showFSInfo( const EncfsConfig &config ) { - shared_ptr cipher = Cipher::New( config->cipherIface, -1 ); + shared_ptr cipher = Cipher::New( config.cipher(), -1 ); { cout << autosprintf( // xgroup(diag) _("Filesystem cipher: \"%s\", version %i:%i:%i"), - config->cipherIface.name().c_str(), config->cipherIface.current(), - config->cipherIface.revision(), config->cipherIface.age()); + config.cipher().name().c_str(), config.cipher().major(), + config.cipher().minor(), config.cipher().age()); // check if we support this interface.. if(!cipher) cout << _(" (NOT supported)\n"); else { // if we're using a newer interface, show the version number - if( config->cipherIface != cipher->interface() ) + if( config.cipher() != cipher->interface() ) { Interface iface = cipher->interface(); // xgroup(diag) cout << autosprintf(_(" (using %i:%i:%i)\n"), - iface.current(), iface.revision(), iface.age()); + iface.major(), iface.minor(), iface.age()); } else cout << "\n"; } @@ -1290,11 +1173,11 @@ void showFSInfo( const boost::shared_ptr &config ) { // xgroup(diag) cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"), - config->nameIface.name().c_str(), config->nameIface.current(), - config->nameIface.revision(), config->nameIface.age()); + config.naming().name().c_str(), config.naming().major(), + config.naming().minor(), config.naming().age()); // check if we support the filename encoding interface.. - shared_ptr nameCoder = NameIO::New( config->nameIface, + shared_ptr nameCoder = NameIO::New( config.naming(), cipher, CipherKey() ); if(!nameCoder) { @@ -1303,18 +1186,18 @@ void showFSInfo( const boost::shared_ptr &config ) } else { // if we're using a newer interface, show the version number - if( config->nameIface != nameCoder->interface() ) + if( config.naming() != nameCoder->interface() ) { Interface iface = nameCoder->interface(); cout << autosprintf(_(" (using %i:%i:%i)\n"), - iface.current(), iface.revision(), iface.age()); + iface.major(), iface.minor(), iface.age()); } else cout << "\n"; } } { - cout << autosprintf(_("Key Size: %i bits"), config->keySize); - cipher = config->getCipher(); + cout << autosprintf(_("Key Size: %i bits"), config.key_size()); + cipher = getCipher(config); if(!cipher) { // xgroup(diag) @@ -1322,54 +1205,54 @@ void showFSInfo( const boost::shared_ptr &config ) } else cout << "\n"; } - if(config->kdfIterations > 0 && config->salt.size() > 0) + if(config.kdf_iterations() > 0 && config.salt().size() > 0) { cout << autosprintf(_("Using PBKDF2, with %i iterations"), - config->kdfIterations) << "\n"; + config.kdf_iterations()) << "\n"; cout << autosprintf(_("Salt Size: %i bits"), - 8*(int)config->salt.size()) << "\n"; + 8*(int)config.salt().size()) << "\n"; } - if(config->blockMACBytes || config->blockMACRandBytes) + if(config.block_mac_bytes() || config.block_mac_rand_bytes()) { - if(config->subVersion < 20040813) + if(config.revision() < 20040813) { cout << autosprintf( // xgroup(diag) _("Block Size: %i bytes + %i byte MAC header"), - config->blockSize, - config->blockMACBytes + config->blockMACRandBytes) << endl; + config.block_size(), + config.block_mac_bytes() + config.block_mac_rand_bytes()) << endl; } else { // new version stores the header as part of that block size.. cout << autosprintf( // xgroup(diag) _("Block Size: %i bytes, including %i byte MAC header"), - config->blockSize, - config->blockMACBytes + config->blockMACRandBytes) << endl; + config.block_size(), + config.block_mac_bytes() + config.block_mac_rand_bytes()) << endl; } } else { // xgroup(diag) - cout << autosprintf(_("Block Size: %i bytes"), config->blockSize); + cout << autosprintf(_("Block Size: %i bytes"), config.block_size()); cout << "\n"; } - if(config->uniqueIV) + if(config.unique_iv()) { // xgroup(diag) cout << _("Each file contains 8 byte header with unique IV data.\n"); } - if(config->chainedNameIV) + if(config.chained_iv()) { // xgroup(diag) cout << _("Filenames encoded using IV chaining mode.\n"); } - if(config->externalIVChaining) + if(config.external_iv()) { // xgroup(diag) cout << _("File data IV is chained to filename IV.\n"); } - if(config->allowHoles) + if(config.allow_holes()) { // xgroup(diag) cout << _("File holes passed through to ciphertext.\n"); @@ -1377,113 +1260,77 @@ void showFSInfo( const boost::shared_ptr &config ) cout << "\n"; } -shared_ptr EncFSConfig::getCipher() const +shared_ptr getCipher(const EncfsConfig &config) { - return Cipher::New( cipherIface, keySize ); + return getCipher(config.cipher(), config.key_size()); } -void EncFSConfig::assignKeyData(const std::string &in) +shared_ptr getCipher(const Interface &iface, int keySize) { - keyData.assign(in.data(), in.data()+in.length()); + return Cipher::New( iface, keySize ); } -void EncFSConfig::assignKeyData(unsigned char *data, int len) -{ - keyData.assign(data, data+len); -} - -void EncFSConfig::assignSaltData(unsigned char *data, int len) -{ - salt.assign(data, data+len); -} - -unsigned char *EncFSConfig::getKeyData() const -{ - return const_cast(&keyData.front()); -} - -unsigned char *EncFSConfig::getSaltData() const -{ - return const_cast(&salt.front()); -} - -CipherKey EncFSConfig::makeKey(const char *password, int passwdLen) +CipherKey makeNewKey(EncfsConfig &config, const char *password, int passwdLen) { CipherKey userKey; - shared_ptr cipher = getCipher(); + shared_ptr cipher = getCipher(config); - // if no salt is set and we're creating a new password for a new - // FS type, then initialize salt.. - if(salt.size() == 0 && kdfIterations == 0 && cfgType >= Config_V6) + unsigned char salt[20]; + if(!cipher->randomize( salt, sizeof(salt), true)) { - // upgrade to using salt - salt.resize(20); + cout << _("Error creating salt\n"); + return userKey; } + config.set_salt(salt, sizeof(salt)); - if(salt.size() > 0) + int iterations = config.kdf_iterations(); + userKey = cipher->newKey( password, passwdLen, + iterations, config.kdf_duration(), + salt, sizeof(salt)); + config.set_kdf_iterations(iterations); + + return userKey; +} + +CipherKey decryptKey(const EncfsConfig &config, const char *password, int passwdLen) +{ + CipherKey userKey; + shared_ptr cipher = getCipher(config.cipher(), config.key_size()); + + if(!config.salt().empty()) { - // if iterations isn't known, then we're creating a new key, so - // randomize the salt.. - if(kdfIterations == 0 && !cipher->randomize( - getSaltData(), salt.size(), true)) - { - cout << _("Error creating salt\n"); + int iterations = config.kdf_iterations(); + userKey = cipher->newKey( password, passwdLen, + iterations, config.kdf_duration(), + (const unsigned char *)config.salt().data(), config.salt().size()); + + if (iterations != config.kdf_iterations()) { + rError("Error in KDF, iteration mismatch"); return userKey; } - - userKey = cipher->newKey( password, passwdLen, - kdfIterations, desiredKDFDuration, - getSaltData(), salt.size()); } else { + // old KDF, no salt.. userKey = cipher->newKey( password, passwdLen ); } return userKey; } -CipherKey EncFSConfig::getUserKey(bool useStdin) -{ - char passBuf[MaxPassBuf]; - char *res; - - if( useStdin ) - { - res = fgets( passBuf, sizeof(passBuf), stdin ); - // Kill the trailing newline. - if(passBuf[ strlen(passBuf)-1 ] == '\n') - passBuf[ strlen(passBuf)-1 ] = '\0'; - } else - { - // xgroup(common) - res = readpassphrase( _("EncFS Password: "), - passBuf, sizeof(passBuf), RPP_ECHO_OFF ); - } - - CipherKey userKey; - if(!res) - cerr << _("Zero length password not allowed\n"); - else - userKey = makeKey(passBuf, strlen(passBuf)); - - memset( passBuf, 0, sizeof(passBuf) ); - - return userKey; -} - +// Doesn't use SecureMem, since we don't know how much will be read. +// Besides, password is being produced by another program. std::string readPassword( int FD ) { - char buffer[1024]; + SecureMem *buf = new SecureMem(1024); string result; while(1) { - ssize_t rdSize = recv(FD, buffer, sizeof(buffer), 0); + ssize_t rdSize = recv(FD, buf->data, buf->size, 0); if(rdSize > 0) { - result.append( buffer, rdSize ); - memset(buffer, 0, sizeof(buffer)); + result.append( buf->data, rdSize ); } else break; } @@ -1494,22 +1341,22 @@ std::string readPassword( int FD ) if(!result.empty() && result[ result.length()-1 ] == '\n' ) result.resize( result.length() -1 ); + delete buf; return result; } -CipherKey EncFSConfig::getUserKey( const std::string &passProg, - const std::string &rootDir ) +SecureMem *passwordFromProgram(const std::string &passProg, + const std::string &rootDir) { // have a child process run the command and get the result back to us. int fds[2], pid; int res; - CipherKey result; res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); if(res == -1) { perror(_("Internal error: socketpair() failed")); - return result; + return NULL; } rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]); @@ -1519,7 +1366,7 @@ CipherKey EncFSConfig::getUserKey( const std::string &passProg, perror(_("Internal error: fork() failed")); close(fds[0]); close(fds[1]); - return result; + return NULL; } if(pid == 0) @@ -1568,58 +1415,143 @@ CipherKey EncFSConfig::getUserKey( const std::string &passProg, waitpid(pid, NULL, 0); - // convert to key.. - result = makeKey(password.c_str(), password.length()); - - // clear buffer.. - password.assign( password.length(), '\0' ); + SecureMem *result = new SecureMem(password.length()+1); + if (result) + strncpy(result->data, password.c_str(), result->size); + password.assign(password.length(), '\0'); return result; } -CipherKey EncFSConfig::getNewUserKey() +SecureMem *passwordFromStdin() { - CipherKey userKey; - char passBuf[MaxPassBuf]; - char passBuf2[MaxPassBuf]; + SecureMem *buf = new SecureMem(MaxPassBuf); + + char *res = fgets( buf->data, buf->size, stdin ); + if (res) + { + // Kill the trailing newline. + int last = strnlen(buf->data, buf->size); + if (last > 0 && buf->data[last-1] == '\n') + buf->data[ last-1 ] = '\0'; + } + + return buf; +} + +SecureMem *passwordFromPrompt() +{ + SecureMem *buf = new SecureMem(MaxPassBuf); + + // xgroup(common) + char *res = readpassphrase( _("EncFS Password: "), + buf->data, buf->size-1, RPP_ECHO_OFF ); + if (!res) + { + delete buf; + buf = NULL; + } + + return buf; +} + +SecureMem *passwordFromPrompts() +{ + SecureMem *buf = new SecureMem(MaxPassBuf); + SecureMem *buf2 = new SecureMem(MaxPassBuf); do { // xgroup(common) - char *res1 = readpassphrase(_("New Encfs Password: "), passBuf, - sizeof(passBuf)-1, RPP_ECHO_OFF); + char *res1 = readpassphrase(_("New Encfs Password: "), + buf->data, buf->size-1, RPP_ECHO_OFF); // xgroup(common) - char *res2 = readpassphrase(_("Verify Encfs Password: "), passBuf2, - sizeof(passBuf2)-1, RPP_ECHO_OFF); + char *res2 = readpassphrase(_("Verify Encfs Password: "), + buf2->data, buf2->size-1, RPP_ECHO_OFF); - if(res1 && res2 && !strcmp(passBuf, passBuf2)) + if(res1 && res2 && !strncmp(buf->data, buf2->data, MaxPassBuf)) { - userKey = makeKey(passBuf, strlen(passBuf)); + break; } else { // xgroup(common) -- probably not common, but group with the others cerr << _("Passwords did not match, please try again\n"); } + } while(1); - memset( passBuf, 0, sizeof(passBuf) ); - memset( passBuf2, 0, sizeof(passBuf2) ); - } while( !userKey ); + delete buf2; + return buf; +} + +CipherKey getUserKey(const EncfsConfig &config, bool useStdin) +{ + CipherKey userKey; + SecureMem *password; + + if (useStdin) + password = passwordFromStdin(); + else + password = passwordFromPrompt(); + + if (password) + { + userKey = decryptKey(config, password->data, strlen(password->data)); + delete password; + } return userKey; } +CipherKey getUserKey( const EncfsConfig &config, const std::string &passProg, + const std::string &rootDir ) +{ + CipherKey result; + SecureMem *password = passwordFromProgram(passProg, rootDir); + + if (password) + { + result = decryptKey(config, password->data, strlen(password->data)); + delete password; + } + + return result; +} + +CipherKey getNewUserKey(EncfsConfig &config, + bool useStdin, const std::string &passProg, + const std::string &rootDir) +{ + CipherKey result; + SecureMem *password; + + if (useStdin) + password = passwordFromStdin(); + else if (!passProg.empty()) + password = passwordFromProgram(passProg, rootDir); + else + password = passwordFromPrompts(); + + if (password) + { + result = makeNewKey(config, password->data, strlen(password->data)); + delete password; + } + + return result; +} + RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) { RootPtr rootInfo; - boost::shared_ptr config(new EncFSConfig); + EncfsConfig config; if(readConfig( opts->rootDir, config ) != Config_None) { if(opts->reverseEncryption) { - if (config->blockMACBytes != 0 || config->blockMACRandBytes != 0 - || config->uniqueIV || config->externalIVChaining - || config->chainedNameIV ) + if (config.block_mac_bytes() != 0 || config.block_mac_rand_bytes() != 0 + || config.unique_iv() || config.external_iv() + || config.chained_iv() ) { cout << _("The configuration loaded is not compatible with --reverse\n"); return rootInfo; @@ -1627,14 +1559,14 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) } // first, instanciate the cipher. - shared_ptr cipher = config->getCipher(); + shared_ptr cipher = getCipher(config); if(!cipher) { rError(_("Unable to find cipher %s, version %i:%i:%i"), - config->cipherIface.name().c_str(), - config->cipherIface.current(), - config->cipherIface.revision(), - config->cipherIface.age()); + config.cipher().name().c_str(), + config.cipher().major(), + config.cipher().minor(), + config.cipher().age()); // xgroup(diag) cout << _("The requested cipher interface is not available\n"); return rootInfo; @@ -1648,9 +1580,9 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) rDebug( "useStdin: %i", opts->useStdin ); if (opts->annotate) cerr << "$PROMPT$ passwd" << endl; - userKey = config->getUserKey( opts->useStdin ); + userKey = getUserKey( config, opts->useStdin ); } else - userKey = config->getUserKey( opts->passwordProgram, opts->rootDir ); + userKey = getUserKey( config, opts->passwordProgram, opts->rootDir ); if(!userKey) return rootInfo; @@ -1658,7 +1590,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) rDebug("cipher key size = %i", cipher->encodedKeySize()); // decode volume key.. CipherKey volumeKey = cipher->readKey( - config->getKeyData(), userKey, opts->checkKey); + (const unsigned char *)config.key().data(), userKey, opts->checkKey); userKey.reset(); if(!volumeKey) @@ -1668,29 +1600,29 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) return rootInfo; } - shared_ptr nameCoder = NameIO::New( config->nameIface, + shared_ptr nameCoder = NameIO::New( config.naming(), cipher, volumeKey ); if(!nameCoder) { rError(_("Unable to find nameio interface %s, version %i:%i:%i"), - config->nameIface.name().c_str(), - config->nameIface.current(), - config->nameIface.revision(), - config->nameIface.age()); + config.naming().name().c_str(), + config.naming().major(), + config.naming().minor(), + config.naming().age()); // xgroup(diag) cout << _("The requested filename coding interface is " "not available\n"); return rootInfo; } - nameCoder->setChainedNameIV( config->chainedNameIV ); + nameCoder->setChainedNameIV( config.chained_iv() ); nameCoder->setReverseEncryption( opts->reverseEncryption ); FSConfigPtr fsConfig( new FSConfig ); fsConfig->cipher = cipher; fsConfig->key = volumeKey; fsConfig->nameCoding = nameCoder; - fsConfig->config = config; + fsConfig->config = boost::shared_ptr(new EncfsConfig(config)); fsConfig->forceDecode = opts->forceDecode; fsConfig->reverseEncryption = opts->reverseEncryption; fsConfig->opts = opts; @@ -1705,7 +1637,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) if(opts->createIfNotFound) { // creating a new encrypted filesystem - rootInfo = createV6Config( ctx, opts ); + rootInfo = createConfig( ctx, opts ); } } diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index febe328..c20fbab 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -99,41 +99,33 @@ struct EncFS_Opts /* Read existing config file. Looks for any supported configuration version. */ -ConfigType readConfig( const std::string &rootDir, - const boost::shared_ptr &config ); +ConfigType readConfig( const std::string &rootDir, EncfsConfig &config ); /* Save the configuration. Saves back as the same configuration type as was read from. */ -bool saveConfig( ConfigType type, const std::string &rootdir, - const boost::shared_ptr &config ); +bool saveConfig( const std::string &rootdir, const EncfsConfig &config ); class EncFS_Context; RootPtr initFS( EncFS_Context *ctx, const boost::shared_ptr &opts ); -RootPtr createV6Config( EncFS_Context *ctx, +RootPtr createConfig( EncFS_Context *ctx, const boost::shared_ptr &opts ); -void showFSInfo( const boost::shared_ptr &config ); +void showFSInfo( const EncfsConfig &config ); -bool readV4Config( const char *configFile, - const boost::shared_ptr &config, +bool readV4Config( const char *configFile, EncfsConfig &config, struct ConfigInfo *); -bool writeV4Config( const char *configFile, - const boost::shared_ptr &config); -bool readV5Config( const char *configFile, - const boost::shared_ptr &config, +bool readV5Config( const char *configFile, EncfsConfig &config, struct ConfigInfo *); -bool writeV5Config( const char *configFile, - const boost::shared_ptr &config); -bool readV6Config( const char *configFile, - const boost::shared_ptr &config, +bool readV6Config( const char *configFile, EncfsConfig &config, + struct ConfigInfo *); + +bool readProtoConfig( const char *configFile, EncfsConfig &config, struct ConfigInfo *); -bool writeV6Config( const char *configFile, - const boost::shared_ptr &config); #endif diff --git a/encfs/Interface.cpp b/encfs/Interface.cpp index 01ecb1c..ff32005 100644 --- a/encfs/Interface.cpp +++ b/encfs/Interface.cpp @@ -22,205 +22,69 @@ #include #include -using namespace rel; using namespace rlog; static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info ); -Interface::Interface(const char *name_, int Current, int Revision, int Age) - : _name( name_ ) - , _current( Current ) - , _revision( Revision ) - , _age( Age ) +bool implements(const Interface &A, const Interface &B) { + rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", + A.name().c_str(), A.major(), A.minor(), A.age(), + B.name().c_str(), B.major(), B.minor(), B.age()); + + if( A.name() != B.name() ) + return false; + + int currentDiff = A.major() - B.major(); + return ( currentDiff >= 0 && currentDiff <= (int)A.age() ); } -Interface::Interface(const std::string &name_, int Current, - int Revision, int Age) - : _name( name_ ) - , _current( Current ) - , _revision( Revision ) - , _age( Age ) +Interface makeInterface(const char *name, int major, int minor, int age) { + Interface iface; + iface.set_name(name); + iface.set_major(major); + iface.set_minor(minor); + iface.set_age(age); + return iface; } -Interface::Interface(const Interface &src) - : _name( src._name ) - , _current( src._current ) - , _revision( src._revision ) - , _age( src._age ) +ConfigVar & operator << (ConfigVar &dst, const Interface &iface) { -} - -Interface::Interface() - : _current( 0 ) - , _revision( 0 ) - , _age( 0 ) -{ -} - -Interface &Interface::operator = (const Interface &src) -{ - _name = src._name; - _current = src._current; - _revision = src._revision; - _age = src._age; - return *this; -} - -const std::string & Interface::name() const -{ - return _name; -} - -std::string & Interface::name() -{ - return _name; -} - -int Interface::current() const -{ - return _current; -} - -int &Interface::current() -{ - return _current; -} - -int Interface::revision() const -{ - return _revision; -} - -int &Interface::revision() -{ - return _revision; -} - -int Interface::age() const -{ - return _age; -} - -int &Interface::age() -{ - return _age; -} - -bool operator == (const Interface &A, const Interface &B) -{ - return ( A.name() == B.name() - && A.current() == B.current() - && A.revision() == B.revision() - && A.age() == B.age() ); -} - -bool operator != (const Interface &A, const Interface &B) -{ - return ( A.name() != B.name() - || A.current() != B.current() - || A.revision() != B.revision() - || A.age() != B.age() ); -} - -// zero branch method of getting comparison sign.. -// tricky.. makes assumptions -#if 0 -static int sign( int a, int b ) -{ - unsigned int ab = ((unsigned int)(a - b)) >> 31; - unsigned int ba = ((unsigned int)(b - a)) >> 31; - - return 1 + ba - ab; -} -#else -// simple, easy to check, unlikely to break due to unforseen events.. -static int sign( int a, int b ) -{ - if(a < b) - return 0; - else if(a == b) - return 1; - else - return 2; -} -#endif - -static int diffSum( const Interface &A, const Interface &B ) -{ - int cS = sign( A.current() , B.current() ); - int aS = sign( A.age(), B.age() ); - int rS = sign( A.revision(), B.revision() ); - - return (cS * 3 + aS) * 3 + rS; -} - -const int EqualVersion = (1 * 3 + 1) * 3 + 1; - -bool Interface::implements(const Interface &B) const -{ - rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", - name().c_str(), current(), revision(), age(), - B.name().c_str(), B.current(), B.revision(), B.age()); - - if( name() != B.name() ) - return false; - - int currentDiff = current() - B.current(); - return ( currentDiff >= 0 && currentDiff <= age() ); -} - - -bool operator < (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) < EqualVersion ); - } else - return A.name() < B.name(); -} - -bool operator > (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) > EqualVersion ); - } else - return A.name() < B.name(); -} - -bool operator <= (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) <= EqualVersion ); - } else - return A.name() < B.name(); -} - -bool operator >= (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) >= EqualVersion ); - } else - return A.name() < B.name(); -} - - -ConfigVar & operator << (ConfigVar &dst, const rel::Interface &iface) -{ - dst << iface.name() << iface.current() << iface.revision() << iface.age(); - return dst; + dst << iface.name() << (int)iface.major() << (int)iface.minor() << (int)iface.age(); + return dst; } const ConfigVar & operator >> (const ConfigVar &src, Interface &iface) { - src >> iface.name(); - src >> iface.current(); - src >> iface.revision(); - src >> iface.age(); + src >> *iface.mutable_name(); + int major, minor, age; + src >> major >> minor >> age; + iface.set_major(major); + iface.set_minor(minor); + iface.set_age(age); + return src; +} + +const XmlValuePtr & operator >> (const XmlValuePtr &src, Interface &iface) +{ + (*src)["name"] >> *iface.mutable_name(); + int major, minor; + (*src)["major"] >> major; + (*src)["minor"] >> minor; + iface.set_major(major); + iface.set_minor(minor); return src; } +bool operator != (const Interface &a, const Interface &b) +{ + if (a.major() != b.major()) + return true; + + if (a.minor() != b.minor()) + return true; + + return false; +} + diff --git a/encfs/Interface.h b/encfs/Interface.h index b74e3f7..6ddca40 100644 --- a/encfs/Interface.h +++ b/encfs/Interface.h @@ -19,66 +19,23 @@ #define _Interface_incl_ #include +#include "XmlReader.h" +#include "config.pb.h" +// check if A implements the interface described by B. +// Note that implements(A, B) is not the same as implements(B, A) +// This checks the current() version and age() against B.current() for +// compatibility. Even if A.implements(B) is true, B > A may also be +// true, meaning B is a newer revision of the interface then A. +bool implements( const Interface &a, const Interface &b ); +Interface makeInterface( const char *name, int major, int minor, int age ); + +// Reae operation class ConfigVar; +const ConfigVar & operator >> (const ConfigVar &, Interface &); +const XmlValuePtr & operator >> (const XmlValuePtr &, Interface &); -// part of REL library.. -namespace rel -{ - - class Interface - { - public: - - /*! - Version numbers as described by libtool: info://libtool/versioning - Current - the most recent interface api that is implemented. - Revision - the implementation number of the current interface. - Age - the difference between the newest and oldest interfaces that - are implemented. - */ - Interface( const char *name, int Current, int Revision, int Age ); - Interface( const std::string &name, int Current, int Revision, int Age); - Interface(const Interface &src); - Interface(); - - // check if we implement the interface described by B. - // Note that A.implements(B) is not the same as B.implements(A) - // This checks the current() version and age() against B.current() for - // compatibility. Even if A.implements(B) is true, B > A may also be - // true, meaning B is a newer revision of the interface then A. - bool implements( const Interface &dst ) const; - - const std::string &name() const; - int current() const; - int revision() const; - int age() const; - - std::string &name(); - int ¤t(); - int &revision(); - int &age(); - - Interface &operator = ( const Interface &src ); - - private: - std::string _name; - int _current; - int _revision; - int _age; - }; - -} - -ConfigVar & operator << (ConfigVar &, const rel::Interface &); -const ConfigVar & operator >> (const ConfigVar &, rel::Interface &); - -bool operator < (const rel::Interface &A, const rel::Interface &B); -bool operator > (const rel::Interface &A, const rel::Interface &B); -bool operator <= (const rel::Interface &A, const rel::Interface &B); -bool operator >= (const rel::Interface &A, const rel::Interface &B); -bool operator == (const rel::Interface &A, const rel::Interface &B); -bool operator != (const rel::Interface &A, const rel::Interface &B); +bool operator != (const Interface &a, const Interface &b); #endif diff --git a/encfs/MACFileIO.cpp b/encfs/MACFileIO.cpp index 816e64b..ac69ccc 100644 --- a/encfs/MACFileIO.cpp +++ b/encfs/MACFileIO.cpp @@ -19,6 +19,7 @@ #include "MemoryPool.h" #include "FileUtils.h" +#include "config.pb.h" #include #include @@ -29,7 +30,6 @@ #include "i18n.h" using namespace rlog; -using namespace rel; using namespace std; using boost::shared_ptr; using boost::dynamic_pointer_cast; @@ -48,13 +48,13 @@ static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); // compatible, except at a high level by checking a revision number for the // filesystem... // -static rel::Interface MACFileIO_iface("FileIO/MAC", 2, 1, 0); +static Interface MACFileIO_iface = makeInterface("FileIO/MAC", 2, 1, 0); int dataBlockSize(const FSConfigPtr &cfg) { - return cfg->config->blockSize - - cfg->config->blockMACBytes - - cfg->config->blockMACRandBytes; + return cfg->config->block_size() + - cfg->config->block_mac_bytes() + - cfg->config->block_mac_rand_bytes(); } MACFileIO::MACFileIO( const shared_ptr &_base, @@ -63,23 +63,23 @@ MACFileIO::MACFileIO( const shared_ptr &_base, , base( _base ) , cipher( cfg->cipher ) , key( cfg->key ) - , macBytes( cfg->config->blockMACBytes ) - , randBytes( cfg->config->blockMACRandBytes ) + , macBytes( cfg->config->block_mac_bytes() ) + , randBytes( cfg->config->block_mac_rand_bytes() ) , warnOnly( cfg->opts->forceDecode ) { rAssert( macBytes >= 0 && macBytes <= 8 ); rAssert( randBytes >= 0 ); rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i", - cfg->config->blockSize, - cfg->config->blockMACBytes, - cfg->config->blockMACRandBytes); + cfg->config->block_size(), + cfg->config->block_mac_bytes(), + cfg->config->block_mac_rand_bytes()); } MACFileIO::~MACFileIO() { } -rel::Interface MACFileIO::interface() const +Interface MACFileIO::interface() const { return MACFileIO_iface; } diff --git a/encfs/MACFileIO.h b/encfs/MACFileIO.h index 46573a6..4b6aac5 100644 --- a/encfs/MACFileIO.h +++ b/encfs/MACFileIO.h @@ -36,7 +36,7 @@ public: MACFileIO(); virtual ~MACFileIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual void setFileName( const char *fileName ); virtual const char *getFileName() const; diff --git a/encfs/Makefile.am b/encfs/Makefile.am index ce6665d..ba7c6c8 100644 --- a/encfs/Makefile.am +++ b/encfs/Makefile.am @@ -2,8 +2,10 @@ include $(top_srcdir)/Makefile.common ALL_INCLUDES = @RLOG_CFLAGS@ @OPENSSL_CFLAGS@ @BOOST_CPPFLAGS@ +ALL_INCLUDES += @PROTOBUF_CFLAGS@ ALL_LDFLAGS = @RLOG_LIBS@ @OPENSSL_LIBS@ @BOOST_LDFLAGS@ -ALL_LDFLAGS += @BOOST_SERIALIZATION_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_SYSTEM_LIB@ +ALL_LDFLAGS += @BOOST_FILESYSTEM_LIB@ @BOOST_SYSTEM_LIB@ +ALL_LDFLAGS += @PROTOBUF_LIBS@ INCLUDES = $(all_includes) -I../intl @@ -47,10 +49,10 @@ endif # : : 0 => no new interfaces, but breaks old apps # : +1 : => internal changes, nothing breaks # -libencfs_la_LDFLAGS = -version-info 6:2:0 +libencfs_la_LDFLAGS = -version-info 7:0:0 libencfs_la_LIBADD = @RLOG_LIBS@ \ @OPENSSL_LIBS@ \ - @BOOST_SERIALIZATION_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_SYSTEM_LIB@ + @BOOST_FILESYSTEM_LIB@ @BOOST_SYSTEM_LIB@ EXTRASRC = ../intl/autosprintf.cpp if BUILD_OPENSSL @@ -62,6 +64,7 @@ endif libencfs_la_SOURCES = \ readpassphrase.cpp \ base64.cpp \ + config.pb.cc \ ConfigReader.cpp \ ConfigVar.cpp \ Context.cpp \ @@ -83,6 +86,7 @@ libencfs_la_SOURCES = \ FileNode.cpp \ FileUtils.cpp \ openssl.cpp \ + XmlReader.cpp \ ${EXTRASRC} @@ -109,6 +113,7 @@ noinst_HEADERS = \ CipherKey.h \ ConfigReader.h \ ConfigVar.h \ + config.pb.h \ Context.h \ DirNode.h \ encfs.h \ @@ -134,8 +139,14 @@ noinst_HEADERS = \ man_MANS=encfs.1 encfsctl.1 EXTRA_DIST = encfs.pod encfsctl.pod encfs.1 encfsctl.1 encfs-man.html +SUFFIXES = .1 .pod .proto + +config.pb.cc: config.pb.h + +config.pb.h: config.proto + @PROTOC@ --cpp_out=. config.proto + if BUILD_MAN -SUFFIXES = .1 .pod # since we have POD2MAN, we can specify how to rebuild encfs.1 if necessary .pod.1: @POD2MAN@ --section=1 --release=@VERSION@ --center="Encrypted Filesystem" $< $@ diff --git a/encfs/MemoryPool.cpp b/encfs/MemoryPool.cpp index 1fe79be..d78c946 100644 --- a/encfs/MemoryPool.cpp +++ b/encfs/MemoryPool.cpp @@ -25,6 +25,8 @@ #include "config.h" #include +#include + #ifdef HAVE_VALGRIND_MEMCHECK_H #include #else @@ -34,6 +36,7 @@ using namespace rlog; +# include # include #define BLOCKDATA( BLOCK ) (unsigned char*)BLOCK->data->data @@ -140,4 +143,34 @@ void MemoryPool::destroyAll() } } +SecureMem::SecureMem(int len) +{ + data = (char *)OPENSSL_malloc(len); + if (data) + { + size = len; + mlock(data, size); + memset(data, '\0', size); + VALGRIND_MAKE_MEM_UNDEFINED( data, size ); + } else + { + size = 0; + } +} + +SecureMem::~SecureMem() +{ + if (size) + { + memset(data, '\0', size); + munlock(data, size); + + OPENSSL_free(data); + VALGRIND_MAKE_MEM_NOACCESS( data, size ); + + data = NULL; + size = 0; + } +} + diff --git a/encfs/MemoryPool.h b/encfs/MemoryPool.h index b1edcda..62349bd 100644 --- a/encfs/MemoryPool.h +++ b/encfs/MemoryPool.h @@ -50,5 +50,14 @@ namespace MemoryPool void destroyAll(); } +struct SecureMem +{ + int size; + char *data; + + SecureMem(int len); + ~SecureMem(); +}; + #endif diff --git a/encfs/NameIO.cpp b/encfs/NameIO.cpp index a13694f..3e2b81e 100644 --- a/encfs/NameIO.cpp +++ b/encfs/NameIO.cpp @@ -32,7 +32,6 @@ #include "NullNameIO.h" using namespace std; -using namespace rel; using namespace rlog; #define REF_MODULE(TYPE) \ @@ -130,7 +129,7 @@ shared_ptr NameIO::New( const Interface &iface, NameIOMap_t::const_iterator end = gNameIOMap->end(); for(it = gNameIOMap->begin(); it != end; ++it) { - if( it->second.iface.implements( iface )) + if( implements(it->second.iface, iface )) { Constructor fn = it->second.constructor; result = (*fn)( iface, cipher, key ); diff --git a/encfs/NameIO.h b/encfs/NameIO.h index 0017bd7..93f48a1 100644 --- a/encfs/NameIO.h +++ b/encfs/NameIO.h @@ -32,33 +32,33 @@ class Cipher; class NameIO { public: - typedef shared_ptr (*Constructor)( const rel::Interface &iface, + typedef shared_ptr (*Constructor)( const Interface &iface, const shared_ptr &cipher, const CipherKey &key); struct Algorithm { std::string name; std::string description; - rel::Interface iface; + Interface iface; }; typedef std::list AlgorithmList; static AlgorithmList GetAlgorithmList( bool includeHidden = false ); - static shared_ptr New( const rel::Interface &iface, + static shared_ptr New( const Interface &iface, const shared_ptr &cipher, const CipherKey &key); static shared_ptr New( const std::string &name, const shared_ptr &cipher, const CipherKey &key); static bool Register( const char *name, const char *description, - const rel::Interface &iface, Constructor constructor, + const Interface &iface, Constructor constructor, bool hidden = false); NameIO(); virtual ~NameIO(); - virtual rel::Interface interface() const =0; + virtual Interface interface() const =0; void setChainedNameIV( bool enable ); bool getChainedNameIV() const; diff --git a/encfs/NullCipher.cpp b/encfs/NullCipher.cpp index fa6ca72..f70e077 100644 --- a/encfs/NullCipher.cpp +++ b/encfs/NullCipher.cpp @@ -26,13 +26,12 @@ #include using namespace std; -using namespace rel; using namespace rlog; using boost::shared_ptr; using boost::dynamic_pointer_cast; -static Interface NullInterface( "nullCipher", 1, 0, 0 ); +static Interface NullInterface = makeInterface( "nullCipher", 1, 0, 0 ); static Range NullKeyRange(0); static Range NullBlockRange(1,4096,1); diff --git a/encfs/NullCipher.h b/encfs/NullCipher.h index 3d7ff67..14ed804 100644 --- a/encfs/NullCipher.h +++ b/encfs/NullCipher.h @@ -28,13 +28,13 @@ */ class NullCipher : public Cipher { - rel::Interface iface; + Interface iface; public: - NullCipher(const rel::Interface &iface); + NullCipher(const Interface &iface); virtual ~NullCipher(); - virtual rel::Interface interface() const; + virtual Interface interface() const; // create a new key based on a password virtual CipherKey newKey(const char *password, int passwdLength, diff --git a/encfs/NullNameIO.cpp b/encfs/NullNameIO.cpp index 90485ef..8792e37 100644 --- a/encfs/NullNameIO.cpp +++ b/encfs/NullNameIO.cpp @@ -22,7 +22,6 @@ #include -using namespace rel; using boost::shared_ptr; static shared_ptr NewNNIO( const Interface &, @@ -31,7 +30,7 @@ static shared_ptr NewNNIO( const Interface &, return shared_ptr( new NullNameIO() ); } -static Interface NNIOIface("nameio/null", 1, 0, 0); +static Interface NNIOIface = makeInterface("nameio/null", 1, 0, 0); static bool NullNameIO_registered = NameIO::Register("Null", "No encryption of filenames", NNIOIface, NewNNIO); diff --git a/encfs/NullNameIO.h b/encfs/NullNameIO.h index e86368b..ee5d9d9 100644 --- a/encfs/NullNameIO.h +++ b/encfs/NullNameIO.h @@ -23,13 +23,13 @@ class NullNameIO : public NameIO { public: - static rel::Interface CurrentInterface(); + static Interface CurrentInterface(); NullNameIO( ); virtual ~NullNameIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual int maxEncodedNameLen( int plaintextNameLen ) const; virtual int maxDecodedNameLen( int encodedNameLen ) const; diff --git a/encfs/RawFileIO.cpp b/encfs/RawFileIO.cpp index 9b01dfc..44bbad9 100644 --- a/encfs/RawFileIO.cpp +++ b/encfs/RawFileIO.cpp @@ -33,9 +33,9 @@ using namespace std; -static rel::Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0); +static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0); -FileIO *NewRawFileIO( const rel::Interface &iface ) +FileIO *NewRawFileIO( const Interface &iface ) { (void)iface; return new RawFileIO(); @@ -82,7 +82,7 @@ RawFileIO::~RawFileIO() close( _fd ); } -rel::Interface RawFileIO::interface() const +Interface RawFileIO::interface() const { return RawFileIO_iface; } diff --git a/encfs/RawFileIO.h b/encfs/RawFileIO.h index 6e481ee..0156c4c 100644 --- a/encfs/RawFileIO.h +++ b/encfs/RawFileIO.h @@ -29,7 +29,7 @@ public: RawFileIO( const std::string &fileName ); virtual ~RawFileIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual void setFileName( const char *fileName ); virtual const char *getFileName() const; diff --git a/encfs/SSL_Cipher.cpp b/encfs/SSL_Cipher.cpp index a6e9290..98d5b27 100644 --- a/encfs/SSL_Cipher.cpp +++ b/encfs/SSL_Cipher.cpp @@ -66,10 +66,7 @@ inline int MIN(int a, int b) the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit keys for the EVP Blowfish interface, which is not what we want. - Eliminated the salt code, since we don't use it.. Reason is that we're - using the derived key to encode random data. Since there is no known - plaintext, there is no ability for an attacker to pre-compute known - password->data mappings, which is what the salt is meant to frustrate. + DEPRECATED: this is here for backward compatibilty only. Use PBKDF */ int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, const unsigned char *data, int dataLen, @@ -103,7 +100,7 @@ int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, } int offset = 0; - int toCopy = MIN( nkey, mds - offset ); + int toCopy = MIN( nkey, (int)mds - offset ); if( toCopy ) { memcpy( key, mdBuf+offset, toCopy ); @@ -111,7 +108,7 @@ int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, nkey -= toCopy; offset += toCopy; } - toCopy = MIN( niv, mds - offset ); + toCopy = MIN( niv, (int)mds - offset ); if( toCopy ) { memcpy( iv, mdBuf+offset, toCopy ); @@ -174,8 +171,8 @@ int TimedPBKDF2(const char *pass, int passlen, // - Version 2:1 adds support for Message Digest function interface // - Version 2:2 adds PBKDF2 for password derivation // - Version 3:0 adds a new IV mechanism -static Interface BlowfishInterface( "ssl/blowfish", 3, 0, 2 ); -static Interface AESInterface( "ssl/aes", 3, 0, 2 ); +static Interface BlowfishInterface = makeInterface( "ssl/blowfish", 3, 0, 2 ); +static Interface AESInterface = makeInterface( "ssl/aes", 3, 0, 2 ); #if defined(HAVE_EVP_BF) @@ -275,19 +272,20 @@ SSLKey::SSLKey(int keySize_, int ivLength_) this->ivLength = ivLength_; pthread_mutex_init( &mutex, 0 ); buffer = (unsigned char *)OPENSSL_malloc( keySize + ivLength ); - memset( buffer, 0, keySize + ivLength ); // most likely fails unless we're running as root, or a user-page-lock // kernel patch is applied.. mlock( buffer, keySize + ivLength ); + + memset( buffer, 0, keySize + ivLength ); } SSLKey::~SSLKey() { memset( buffer, 0, keySize + ivLength ); - OPENSSL_free( buffer ); munlock( buffer, keySize + ivLength ); + OPENSSL_free( buffer ); keySize = 0; ivLength = 0; @@ -370,7 +368,7 @@ SSL_Cipher::SSL_Cipher(const Interface &iface_, iface.name().c_str(), _keySize, _ivLength); if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize) - && iface.current() == 1) + && iface.major() == 1) { rWarning("Running in backward compatibilty mode for 1.0 - \n" "key is really %i bits, not %i.\n" @@ -438,7 +436,7 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) shared_ptr key( new SSLKey( _keySize, _ivLength) ); int bytes = 0; - if( iface.current() > 1 ) + if( iface.major() > 1 ) { // now we use BytesToKey, which can deal with Blowfish keys larger then // 128 bits. @@ -680,7 +678,7 @@ int SSL_Cipher::cipherBlockSize() const void SSL_Cipher::setIVec( unsigned char *ivec, uint64_t seed, const shared_ptr &key) const { - if (iface.current() >= 3) + if (iface.major() >= 3) { memcpy( ivec, IVData(key), _ivLength ); @@ -707,7 +705,7 @@ void SSL_Cipher::setIVec( unsigned char *ivec, uint64_t seed, } } -/** For backward compatibility. +/** Deprecated: For backward compatibility only. A watermark attack was discovered against this IV setup. If an attacker could get a victim to store a carefully crafted file, they could later determine if the victim had the file in encrypted storage (without @@ -717,13 +715,6 @@ void SSL_Cipher::setIVec_old(unsigned char *ivec, unsigned int seed, const shared_ptr &key) const { - /* These multiplication constants chosen as they represent (non optimal) - Golumb rulers, the idea being to spread around the information in the - seed. - - 0x060a4011 : ruler length 26, 7 marks, 21 measurable lengths - 0x0221040d : ruler length 25, 7 marks, 21 measurable lengths - */ unsigned int var1 = 0x060a4011 * seed; unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); diff --git a/encfs/SSL_Cipher.h b/encfs/SSL_Cipher.h index cb5ab4a..5ea7f72 100644 --- a/encfs/SSL_Cipher.h +++ b/encfs/SSL_Cipher.h @@ -70,21 +70,21 @@ using boost::shared_ptr; */ class SSL_Cipher : public Cipher { - rel::Interface iface; - rel::Interface realIface; + Interface iface; + Interface realIface; const EVP_CIPHER *_blockCipher; const EVP_CIPHER *_streamCipher; unsigned int _keySize; // in bytes unsigned int _ivLength; public: - SSL_Cipher(const rel::Interface &iface, const rel::Interface &realIface, + SSL_Cipher(const Interface &iface, const Interface &realIface, const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, int keyLength); virtual ~SSL_Cipher(); // returns the real interface, not the one we're emulating (if any).. - virtual rel::Interface interface() const; + virtual Interface interface() const; // create a new key based on a password virtual CipherKey newKey(const char *password, int passwdLength, diff --git a/encfs/StreamNameIO.cpp b/encfs/StreamNameIO.cpp index cd7f4a4..615f31b 100644 --- a/encfs/StreamNameIO.cpp +++ b/encfs/StreamNameIO.cpp @@ -26,7 +26,6 @@ #include "i18n.h" #include -using namespace rel; using namespace std; static shared_ptr NewStreamNameIO( const Interface &iface, @@ -65,13 +64,13 @@ static bool StreamIO_registered = NameIO::Register("Stream", Interface StreamNameIO::CurrentInterface() { // implement major version 2, 1, and 0 - return Interface("nameio/stream", 2, 1, 2); + return makeInterface("nameio/stream", 2, 1, 2); } -StreamNameIO::StreamNameIO( const rel::Interface &iface, +StreamNameIO::StreamNameIO( const Interface &iface, const shared_ptr &cipher, const CipherKey &key ) - : _interface( iface.current() ) + : _interface( iface.major() ) , _cipher( cipher ) , _key( key ) { diff --git a/encfs/StreamNameIO.h b/encfs/StreamNameIO.h index 38e4be8..2940ff3 100644 --- a/encfs/StreamNameIO.h +++ b/encfs/StreamNameIO.h @@ -27,14 +27,14 @@ using boost::shared_ptr; class StreamNameIO : public NameIO { public: - static rel::Interface CurrentInterface(); + static Interface CurrentInterface(); - StreamNameIO( const rel::Interface &iface, + StreamNameIO( const Interface &iface, const shared_ptr &cipher, const CipherKey &key ); virtual ~StreamNameIO(); - virtual rel::Interface interface() const; + virtual Interface interface() const; virtual int maxEncodedNameLen( int plaintextNameLen ) const; virtual int maxDecodedNameLen( int encodedNameLen ) const; diff --git a/encfs/XmlReader.cpp b/encfs/XmlReader.cpp new file mode 100644 index 0000000..f5b54a9 --- /dev/null +++ b/encfs/XmlReader.cpp @@ -0,0 +1,191 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "XmlReader.h" +#include "base64.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace rlog; + +XmlValue::~XmlValue() +{ +} + +XmlValuePtr XmlValue::operator[] (const char *path) const +{ + return this->find(path); +} + +XmlValuePtr XmlValue::find(const char *name) const +{ + rError("in XmlValue::operator[%s]", name); + return XmlValuePtr(new XmlValue()); +} + +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, std::string &out) +{ + out = ptr->text(); + return ptr; +} + +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, int &out) +{ + out = atoi(ptr->text().c_str()); + return ptr; +} + +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, long &out) +{ + out = atol(ptr->text().c_str()); + return ptr; +} + +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, double &out) +{ + out = atof(ptr->text().c_str()); + return ptr; +} + +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, bool &out) +{ + out = atoi(ptr->text().c_str()); + return ptr; +} + +bool XmlValue::readB64Data(unsigned char *data, int length) const +{ + std::string s = value; + s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + BIO *bmem = BIO_new_mem_buf((void *)s.c_str(), s.size()); + bmem = BIO_push(b64, bmem); + + int decodedSize = BIO_read(bmem, data, length); + BIO_free_all(b64); + + if (decodedSize != length) + { + rError("decoding bytes len %i, expecting output len %i, got %i", + (int)s.size(), length, decodedSize); + return false; + } + + return true; +} + +std::string safeValueForNode(TiXmlElement *element) +{ + std::string value; + const TiXmlNode *child = element->FirstChild(); + if (child) + { + const TiXmlText *childText = child->ToText(); + if (childText) + value = childText->Value(); + } + + return value; +} + +class XmlNode : virtual public XmlValue +{ + TiXmlElement *element; +public: + XmlNode(TiXmlElement *element_) + : XmlValue(safeValueForNode(element_)) + , element(element_) + { + } + + virtual ~XmlNode() + { + } + + virtual XmlValuePtr find(const char *name) const + { + if (name[0] == '@') + { + return XmlValuePtr(new XmlValue(element->Attribute(name+1))); + } else + { + return XmlValuePtr(new XmlNode(element->FirstChild(name)->ToElement())); + } + } +}; + +struct XmlReader::XmlReaderData +{ + boost::shared_ptr doc; +}; + +XmlReader::XmlReader() + : pd(new XmlReaderData()) +{ +} + +XmlReader::~XmlReader() +{ +} + +bool XmlReader::load(const char *fileName) +{ + pd->doc.reset(new TiXmlDocument(fileName)); + + return pd->doc->LoadFile(); +} + +XmlValuePtr XmlReader::operator[] ( const char *name ) const +{ + TiXmlNode *node = pd->doc->FirstChild(name); + if (node == NULL) + { + rError("Xml node %s not found", name); + return XmlValuePtr(new XmlValue()); + } + + TiXmlElement *element = node->ToElement(); + if (element == NULL) + { + rError("Xml node %s not element, type = %i", name, node->Type()); + return XmlValuePtr(new XmlValue()); + } + + return XmlValuePtr(new XmlNode(element)); +} + diff --git a/encfs/XmlReader.h b/encfs/XmlReader.h new file mode 100644 index 0000000..e538a66 --- /dev/null +++ b/encfs/XmlReader.h @@ -0,0 +1,78 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _XmlReader_incl_ +#define _XmlReader_incl_ + +#include +#include + +class XmlValue; +typedef boost::shared_ptr XmlValuePtr; + +class XmlValue +{ + std::string value; +public: + XmlValue() + { + } + + XmlValue(const std::string &value) + { + this->value = value; + } + virtual ~XmlValue(); + + XmlValuePtr operator[] (const char *path) const; + + bool readB64Data(unsigned char *data, int length) const; + + const std::string &text() const + { + return value; + } + +protected: + virtual XmlValuePtr find(const char *name) const; +}; + +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, std::string &outStr); +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, int &out); +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, long &out); +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, double &out); +const XmlValuePtr & operator >> (const XmlValuePtr &ptr, bool &out); + +class XmlReader +{ +public: + XmlReader(); + ~XmlReader(); + + bool load(const char *fileName); + + XmlValuePtr operator[](const char *name) const; + +private: + struct XmlReaderData; + boost::shared_ptr pd; +}; + +#endif diff --git a/encfs/config.proto b/encfs/config.proto new file mode 100644 index 0000000..3f975fa --- /dev/null +++ b/encfs/config.proto @@ -0,0 +1,36 @@ + +message EncfsConfig +{ + optional string creator = 1; + optional int32 revision = 2 [default=0]; + + required Interface cipher = 3; + required bytes key = 4; + optional bytes salt = 41; + required int32 key_size = 42; // in bits + optional int32 kdf_iterations = 43 [default=0]; + optional int32 kdf_duration = 44 [default=500]; + + optional Interface naming = 5; + optional bool unique_iv = 51 [default=false]; + optional bool chained_iv = 52 [default=false]; + optional bool external_iv = 53 [default=false]; + + required int32 block_size = 6; + optional int32 block_mac_bytes = 61 [default=0]; + optional int32 block_mac_rand_bytes = 611 [default=0]; + optional bool allow_holes = 62 [default = false]; +} + +message Interface +{ + required string name = 1; + required uint32 major = 2; // major version number + required uint32 minor = 3; // minor version number + + // Age indicates number of major versions supported. 0 means no backward + // compatibility. See libtool "updating version information" for more + // details on how major/minor/age are used for versioning libraries. + optional uint32 age = 4; +} + diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp index 369c37a..4cc2a37 100644 --- a/encfs/encfsctl.cpp +++ b/encfs/encfsctl.cpp @@ -176,7 +176,7 @@ static int showInfo( int argc, char **argv ) if( !checkDir( rootDir )) return EXIT_FAILURE; - boost::shared_ptr config(new EncFSConfig); + EncfsConfig config; ConfigType type = readConfig( rootDir, config ); // show information stored in config.. @@ -194,24 +194,22 @@ static int showInfo( int argc, char **argv ) case Config_V3: // xgroup(diag) cout << "\n" << autosprintf(_("Version 3 configuration; " - "created by %s\n"), config->creator.c_str()); + "created by %s\n"), config.creator().c_str()); break; case Config_V4: // xgroup(diag) cout << "\n" << autosprintf(_("Version 4 configuration; " - "created by %s\n"), config->creator.c_str()); + "created by %s\n"), config.creator().c_str()); break; case Config_V5: - // xgroup(diag) - cout << "\n" << autosprintf(_("Version 5 configuration; " - "created by %s (revision %i)\n"), config->creator.c_str(), - config->subVersion); - break; case Config_V6: + case Config_V7: // xgroup(diag) - cout << "\n" << autosprintf(_("Version 6 configuration; " - "created by %s (revision %i)\n"), config->creator.c_str(), - config->subVersion); + cout << "\n" << autosprintf(_("Version %i configuration; " + "created by %s (revision %i)\n"), + type, + config.creator().c_str(), + config.revision()); break; } @@ -687,9 +685,7 @@ static int cmd_showcruft( int argc, char **argv ) int filesFound = showcruft( rootInfo, "/" ); - cerr << autosprintf( - ngettext("Found %i invalid file.", "Found %i invalid files.", - filesFound), filesFound) << "\n"; + cerr << autosprintf("Found %i invalid file(s).", filesFound) << "\n"; return EXIT_SUCCESS; } @@ -701,7 +697,7 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) if( !checkDir( rootDir )) return EXIT_FAILURE; - boost::shared_ptr config(new EncFSConfig); + EncfsConfig config; ConfigType cfgType = readConfig( rootDir, config ); if(cfgType == Config_None) @@ -711,12 +707,11 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) } // instanciate proper cipher - shared_ptr cipher = Cipher::New( - config->cipherIface, config->keySize ); + shared_ptr cipher = getCipher(config); if(!cipher) { cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), - config->cipherIface.name().c_str()); + config.cipher().name().c_str()); return EXIT_FAILURE; } @@ -724,13 +719,14 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) cout << _("Enter current Encfs password\n"); if (annotate) cerr << "$PROMPT$ passwd" << endl; - CipherKey userKey = config->getUserKey( useStdin ); + CipherKey userKey = getUserKey( config, useStdin ); if(!userKey) return EXIT_FAILURE; // decode volume key using user key -- at this point we detect an incorrect // password if the key checksum does not match (causing readKey to fail). - CipherKey volumeKey = cipher->readKey( config->getKeyData(), userKey ); + CipherKey volumeKey = cipher->readKey( + (const unsigned char *)config.key().data(), userKey ); if(!volumeKey) { @@ -741,17 +737,15 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) // Now, get New user key.. userKey.reset(); cout << _("Enter new Encfs password\n"); - // reinitialize salt and iteration count - config->kdfIterations = 0; // generate new + // create new key if( useStdin ) { if (annotate) cerr << "$PROMPT$ new_passwd" << endl; - userKey = config->getUserKey( true ); } - else - userKey = config->getNewUserKey(); + + userKey = getNewUserKey( config, useStdin, string(), string() ); // re-encode the volume key using the new user key and write it out.. int result = EXIT_FAILURE; @@ -764,10 +758,10 @@ static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) cipher->writeKey( volumeKey, keyBuf, userKey ); userKey.reset(); - config->assignKeyData( keyBuf, encodedKeySize ); + config.set_key( keyBuf, encodedKeySize ); delete[] keyBuf; - if(saveConfig( cfgType, rootDir, config )) + if(saveConfig( rootDir, config )) { // password modified -- changes volume key of filesystem.. cout << _("Volume Key successfully updated.\n"); @@ -816,7 +810,7 @@ int main(int argc, char **argv) slog->subscribeTo( GetGlobalChannel("error") ); slog->subscribeTo( GetGlobalChannel("warning") ); #ifndef NO_DEBUG - //slog->subscribeTo( GetGlobalChannel("debug") ); + slog->subscribeTo( GetGlobalChannel("debug") ); #endif if(argc < 2) diff --git a/encfs/main.cpp b/encfs/main.cpp index 393ebb5..c086704 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -69,7 +69,6 @@ inline static int MAX(int a, int b) using namespace std; using namespace rlog; -using namespace rel; using namespace gnu; using boost::shared_ptr; using boost::scoped_ptr; diff --git a/encfs/test.cpp b/encfs/test.cpp index 4bb2faa..3d0e6dc 100644 --- a/encfs/test.cpp +++ b/encfs/test.cpp @@ -47,11 +47,12 @@ #endif #endif +#include + #include using namespace std; -using namespace rel; using namespace rlog; using boost::shared_ptr; @@ -235,35 +236,28 @@ bool runTests(const shared_ptr &cipher, bool verbose) cipher->writeKey( key, keyBuf, encodingKey ); // store in config struct.. - EncFSConfig cfg; - cfg.cipherIface = cipher->interface(); - cfg.keySize = 8 * cipher->keySize(); - cfg.blockSize = FSBlockSize; - cfg.assignKeyData( keyBuf, encodedKeySize ); + EncfsConfig cfg; + cfg.mutable_cipher()->MergeFrom(cipher->interface()); + cfg.set_key_size(8 * cipher->keySize()); + cfg.set_block_size(FSBlockSize); + cfg.set_key( keyBuf, encodedKeySize ); // save config string data; - { - ostringstream st; - st << cfg; - data = st.str(); - } + google::protobuf::TextFormat::PrintToString(cfg, &data); // read back in and check everything.. - EncFSConfig cfg2; - { - istringstream st(data); - st >> cfg2; - } + EncfsConfig cfg2; + google::protobuf::TextFormat::ParseFromString(data, &cfg2); // check.. - rAssert( cfg.cipherIface.implements(cfg2.cipherIface) ); - rAssert( cfg.keySize == cfg2.keySize ); - rAssert( cfg.blockSize == cfg2.blockSize ); + rAssert( implements(cfg.cipher(),cfg2.cipher()) ); + rAssert( cfg.key_size() == cfg2.key_size() ); + rAssert( cfg.block_size() == cfg2.block_size() ); // try decoding key.. - CipherKey key2 = cipher->readKey( cfg2.getKeyData(), encodingKey ); + CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().data(), encodingKey ); if(!key2) { if(verbose) @@ -286,15 +280,15 @@ bool runTests(const shared_ptr &cipher, bool verbose) FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); fsCfg->cipher = cipher; fsCfg->key = key; - fsCfg->config.reset(new EncFSConfig); - fsCfg->config->blockSize = FSBlockSize; + fsCfg->config.reset(new EncfsConfig); + fsCfg->config->set_block_size(FSBlockSize); if(verbose) cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; { fsCfg->opts.reset(new EncFS_Opts); fsCfg->opts->idleTracking = false; - fsCfg->config->uniqueIV = false; + fsCfg->config->set_unique_iv(false); fsCfg->nameCoding.reset( new StreamNameIO( StreamNameIO::CurrentInterface(), cipher, key ) ); @@ -310,7 +304,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; { fsCfg->opts->idleTracking = false; - fsCfg->config->uniqueIV = false; + fsCfg->config->set_unique_iv(false); fsCfg->nameCoding.reset( new BlockNameIO( BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize() ) ); @@ -326,7 +320,7 @@ bool runTests(const shared_ptr &cipher, bool verbose) cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; { fsCfg->opts->idleTracking = false; - fsCfg->config->uniqueIV = false; + fsCfg->config->set_unique_iv(false); fsCfg->nameCoding.reset( new BlockNameIO( BlockNameIO::CurrentInterface(), cipher, key, cipher->cipherBlockSize(), true ) ); @@ -503,8 +497,8 @@ int main(int argc, char *argv[]) { cerr << it->name << " ( " << it->iface.name() << " " - << it->iface.current() << ":" - << it->iface.revision() << ":" + << it->iface.major() << ":" + << it->iface.minor() << ":" << it->iface.age() << " ) : " << it->description << "\n"; cerr << " - key length " << it->keyLength.min() << " to " << it->keyLength.max() << " , block size " << it->blockSize.min()