diff --git a/CMakeLists.txt b/CMakeLists.txt index 07fc715..f08772e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,11 +48,8 @@ add_definitions (-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26) find_package (OpenSSL REQUIRED) include_directories (${OPENSSL_INCLUDE_DIR}) -find_package(Boost - 1.34.0 - REQUIRED - COMPONENTS serialization) -include_directories (${Boost_INCLUDE_DIRS}) +find_package (TinyXML REQUIRED) +include_directories (${TINYXML_INCLUDE_DIR}) find_package (RLog REQUIRED) add_definitions (-DRLOG_COMPONENT="encfs") @@ -130,6 +127,7 @@ set(SOURCE_FILES encfs/readpassphrase.cpp encfs/SSL_Cipher.cpp encfs/StreamNameIO.cpp + encfs/XmlReader.cpp ) add_library(encfs SHARED ${SOURCE_FILES}) set_property(TARGET encfs PROPERTY VERSION ${ENCFS_VERSION}) @@ -137,7 +135,7 @@ set_property(TARGET encfs PROPERTY SOVERSION ${ENCFS_SOVERSION}) target_link_libraries(encfs ${FUSE_LIBRARIES} ${OPENSSL_LIBRARIES} - ${Boost_LIBRARIES} + ${TINYXML_LIBRARIES} ${RLOG_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) diff --git a/INSTALL.md b/INSTALL.md index 0d01ac4..e3a7f3c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -41,7 +41,7 @@ Dependencies EncFS depends on a number of libraries: - openssl fuse boost-serialization gettext libintl librlog + openssl fuse tinyxml2 gettext libintl librlog Compiling on Debian and Ubuntu ============================== diff --git a/circle.yml b/circle.yml index c943b28..df24a9e 100644 --- a/circle.yml +++ b/circle.yml @@ -4,8 +4,7 @@ machine: dependencies: pre: - - sudo apt-get update - - sudo apt-get install cmake libfuse-dev librlog-dev libgettextpo-dev + - sudo apt-get install cmake libfuse-dev librlog-dev libgettextpo-dev libtinyxml2-dev - bash ./ci/install-gcc.sh test: diff --git a/cmake/FindTinyXML.cmake b/cmake/FindTinyXML.cmake new file mode 100644 index 0000000..4edf154 --- /dev/null +++ b/cmake/FindTinyXML.cmake @@ -0,0 +1,26 @@ +# - Find TinyXML +# Find the native TinyXML includes and library +# +# TINYXML_FOUND - True if TinyXML found. +# TINYXML_INCLUDE_DIR - where to find tinyxml.h, etc. +# TINYXML_LIBRARIES - List of libraries when using TinyXML. +# + +IF( TINYXML_INCLUDE_DIR ) + # Already in cache, be silent + SET( TinyXML_FIND_QUIETLY TRUE ) +ENDIF( TINYXML_INCLUDE_DIR ) + +FIND_PATH( TINYXML_INCLUDE_DIR "tinyxml2.h" + PATH_SUFFIXES "tinyxml2" ) + +FIND_LIBRARY( TINYXML_LIBRARIES + NAMES "tinyxml2" + PATH_SUFFIXES "tinyxml2" ) + +# handle the QUIETLY and REQUIRED arguments and set TINYXML_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE( "FindPackageHandleStandardArgs" ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( "TinyXML" DEFAULT_MSG TINYXML_INCLUDE_DIR TINYXML_LIBRARIES ) + +MARK_AS_ADVANCED( TINYXML_INCLUDE_DIR TINYXML_LIBRARIES ) diff --git a/devmode b/devmode old mode 100644 new mode 100755 index cb53a37..f9850f5 --- a/devmode +++ b/devmode @@ -1,4 +1,4 @@ # Script which sets up the CMake build for Debug mode. # After running, chdir to the build subdir ane run "make" mkdir build -cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug $@ +cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-fsanitize=address" $@ diff --git a/drone-config/Dockerfile b/drone-config/Dockerfile index 90129e3..f6fc34e 100644 --- a/drone-config/Dockerfile +++ b/drone-config/Dockerfile @@ -4,7 +4,7 @@ FROM ubuntu # Do system updates and install dependencies RUN apt-get update RUN apt-get -y upgrade -RUN sudo apt-get -y install git wget autopoint libfuse-dev libboost-serialization-dev +RUN sudo apt-get -y install git wget autopoint libfuse-dev libtinyxml2-dev RUN apt-get clean # Download Drone.io diff --git a/drone-config/worker/Dockerfile b/drone-config/worker/Dockerfile index 159d5ed..37592f0 100644 --- a/drone-config/worker/Dockerfile +++ b/drone-config/worker/Dockerfile @@ -31,10 +31,10 @@ RUN apt-get -y upgrade \ && apt-get -y install \ git \ libfuse-dev \ - libboost-serialization-dev \ libssl-dev \ librlog-dev \ gettext \ libgettextpo-dev \ + libtinyxml2-dev \ && apt-get clean diff --git a/encfs/FSConfig.h b/encfs/FSConfig.h index 61000dc..922dfc1 100644 --- a/encfs/FSConfig.h +++ b/encfs/FSConfig.h @@ -21,6 +21,7 @@ #ifndef _FSConfig_incl_ #define _FSConfig_incl_ +#include #include #include "encfs.h" @@ -54,12 +55,13 @@ struct EncFSConfig { 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; diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp index 52c2dfc..d23de29 100644 --- a/encfs/FileUtils.cpp +++ b/encfs/FileUtils.cpp @@ -24,12 +24,6 @@ #endif #define _BSD_SOURCE // pick up setenv on RH7.3 -#include -#include -#include -#include -#include -#include #include #include #include @@ -45,6 +39,7 @@ #include #include #include +#include #include #include "BlockNameIO.h" @@ -59,7 +54,9 @@ #include "Interface.h" #include "NameIO.h" #include "Range.h" +#include "XmlReader.h" #include "autosprintf.h" +#include "base64.h" #include "config.h" #include "i18n.h" #include "intl/gettext.h" @@ -73,7 +70,6 @@ using namespace rel; using namespace rlog; using namespace std; using gnu::autosprintf; -namespace serial = boost::serialization; static const int DefaultBlockSize = 1024; // The maximum length of text passwords. If longer are needed, @@ -108,9 +104,8 @@ struct ConfigInfo { const char *fileName; ConfigType type; const char *environmentOverride; - bool (*loadFunc)(const char *fileName, const shared_ptr &config, - ConfigInfo *cfg); - bool (*saveFunc)(const char *fileName, const shared_ptr &config); + bool (*loadFunc)(const char *fileName, EncFSConfig *config, ConfigInfo *cfg); + bool (*saveFunc)(const char *fileName, const EncFSConfig *config); int currentSubVersion; int defaultSubVersion; } ConfigFileMapping[] = { @@ -127,121 +122,6 @@ struct ConfigInfo { {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, {NULL, Config_None, NULL, NULL, NULL, 0, 0}}; -#include "boost-versioning.h" // IWYU pragma: keep - -// define serialization helpers -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()); -} -} -} - EncFS_Root::EncFS_Root() {} EncFS_Root::~EncFS_Root() {} @@ -330,7 +210,7 @@ bool userAllowMkdir(int promptno, const char *path, mode_t mode) { * Load config file by calling the load function on the filename */ ConfigType readConfig_load(ConfigInfo *nm, const char *path, - const shared_ptr &config) { + EncFSConfig *config) { if (nm->loadFunc) { try { if ((*nm->loadFunc)(path, config, nm)) { @@ -354,8 +234,7 @@ ConfigType readConfig_load(ConfigInfo *nm, const char *path, * Try to locate the config file * Tries the most recent format first, then looks for older versions */ -ConfigType readConfig(const string &rootDir, - const shared_ptr &config) { +ConfigType readConfig(const string &rootDir, EncFSConfig *config) { ConfigInfo *nm = ConfigFileMapping; while (nm->fileName) { // allow environment variable to override default config path @@ -386,32 +265,96 @@ ConfigType readConfig(const string &rootDir, * Read config file in current "V6" XML format, normally named ".encfs6.xml" * This format is in use since Apr 13, 2008 (commit 6d081f5c) */ -bool readV6Config(const char *configFile, const shared_ptr &config, + // Read a boost::serialization config file using an Xml reader.. +bool readV6Config(const char *configFile, EncFSConfig *cfg, ConfigInfo *info) { (void)info; - ifstream st(configFile); - if (st.is_open()) { - 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); + XmlReader rdr; + if (!rdr.load(configFile)) { + rError("Failed to load config file %s", configFile); return false; } + + XmlValuePtr serialization = rdr["boost_serialization"]; + XmlValuePtr config = (*serialization)["cfg"]; + if (!config) { + config = (*serialization)["config"]; + } + if (!config) { + rError("Unable to find XML configuration in file %s", configFile); + return false; + } + + int version; + if (!config->read("version", &version) && + !config->read("@version", &version)) { + rError("Unable to find version in config file"); + return false; + } + + // version numbering was complicated by boost::archive + if (version == 20 || version >= 20100713) { + rDebug("found new serialization format"); + cfg->subVersion = version; + } else if (version == 26800) { + rDebug("found 20080816 version"); + cfg->subVersion = 20080816; + } else if (version == 26797) { + rDebug("found 20080813"); + cfg->subVersion = 20080813; + } else if (version < V5SubVersion) { + rError("Invalid version %d - please fix config file", version); + } else { + rInfo("Boost <= 1.41 compatibility mode"); + cfg->subVersion = version; + } + rDebug("subVersion = %d", cfg->subVersion); + + config->read("creator", &cfg->creator); + config->read("cipherAlg", &cfg->cipherIface); + config->read("nameAlg", &cfg->nameIface); + + config->read("keySize", &cfg->keySize); + + config->read("blockSize", &cfg->blockSize); + config->read("uniqueIV", &cfg->uniqueIV); + config->read("chainedNameIV", &cfg->chainedNameIV); + config->read("externalIVChaining", &cfg->externalIVChaining); + config->read("blockMACBytes", &cfg->blockMACBytes); + config->read("blockMACRandBytes", &cfg->blockMACRandBytes); + config->read("allowHoles", &cfg->allowHoles); + + int encodedSize; + config->read("encodedKeySize", &encodedSize); + unsigned char *key = new unsigned char[encodedSize]; + config->readB64("encodedKeyData", key, encodedSize); + cfg->assignKeyData(key, encodedSize); + delete[] key; + + if (cfg->subVersion >= 20080816) { + int saltLen; + config->read("saltLen", &saltLen); + unsigned char *salt = new unsigned char[saltLen]; + config->readB64("saltData", salt, saltLen); + cfg->assignSaltData(salt, saltLen); + delete[] salt; + + config->read("kdfIterations", &cfg->kdfIterations); + config->read("desiredKDFDuration", &cfg->desiredKDFDuration); + } else { + cfg->kdfIterations = 16; + cfg->desiredKDFDuration = NormalKDFDuration; + } + + return true; } /** * Read config file in deprecated "V5" format, normally named ".encfs5" * This format has been used before Apr 13, 2008 */ -bool readV5Config(const char *configFile, const shared_ptr &config, +bool readV5Config(const char *configFile, EncFSConfig *config, ConfigInfo *info) { bool ok = false; @@ -466,7 +409,7 @@ bool readV5Config(const char *configFile, const shared_ptr &config, * Read config file in deprecated "V4" format, normally named ".encfs4" * This format has been used before Jan 7, 2008 */ -bool readV4Config(const char *configFile, const shared_ptr &config, +bool readV4Config(const char *configFile, EncFSConfig *config, ConfigInfo *info) { bool ok = false; @@ -503,7 +446,7 @@ bool readV4Config(const char *configFile, const shared_ptr &config, } bool saveConfig(ConfigType type, const string &rootDir, - const shared_ptr &config) { + const EncFSConfig *config) { bool ok = false; ConfigInfo *nm = ConfigFileMapping; @@ -530,31 +473,93 @@ bool saveConfig(ConfigType type, const string &rootDir, return ok; } -bool writeV6Config(const char *configFile, - const shared_ptr &config) { - ofstream st(configFile); - if (!st.is_open()) return false; - - st << *config; - return true; +template +tinyxml2::XMLElement *addEl(tinyxml2::XMLDocument &doc, + tinyxml2::XMLNode *parent, const char *name, + T value) { + auto el = doc.NewElement(name); + el->SetText(value); + parent->InsertEndChild(el); + return el; } -std::ostream &operator<<(std::ostream &st, const EncFSConfig &cfg) { - boost::archive::xml_oarchive oa(st); - oa << BOOST_SERIALIZATION_NVP(cfg); +template <> +tinyxml2::XMLElement *addEl<>(tinyxml2::XMLDocument &doc, + tinyxml2::XMLNode *parent, const char *name, + Interface iface) { + auto el = doc.NewElement(name); - return st; + auto n = doc.NewElement("name"); + n->SetText(iface.name().c_str()); + el->InsertEndChild(n); + + auto major = doc.NewElement("major"); + major->SetText(iface.current()); + el->InsertEndChild(major); + + auto minor = doc.NewElement("minor"); + minor->SetText(iface.revision()); + el->InsertEndChild(minor); + + parent->InsertEndChild(el); + return el; } -std::istream &operator>>(std::istream &st, EncFSConfig &cfg) { - boost::archive::xml_iarchive ia(st); - ia >> BOOST_SERIALIZATION_NVP(cfg); +template <> +tinyxml2::XMLElement *addEl<>(tinyxml2::XMLDocument &doc, + tinyxml2::XMLNode *parent, const char *name, + std::vector data) { + string v = string("\n") + B64StandardEncode(data) + "\n"; + return addEl(doc, parent, name, v.c_str()); +} - return st; +bool writeV6Config(const char *configFile, const EncFSConfig *cfg) { + tinyxml2::XMLDocument doc; + + // Various static tags are included to make the output compatible with + // older boost-based readers. + doc.InsertEndChild(doc.NewDeclaration(nullptr)); + doc.InsertEndChild(doc.NewUnknown("DOCTYPE boost_serialization")); + + auto header = doc.NewElement("boost_serialization"); + header->SetAttribute("signature", "serialization::archive"); + header->SetAttribute("version", "7"); + doc.InsertEndChild(header); + + auto config = doc.NewElement("cfg"); + config->SetAttribute("class_id", "0"); + config->SetAttribute("tracking_level", "0"); + config->SetAttribute("version", "20"); + header->InsertEndChild(config); + + addEl(doc, config, "version", V6SubVersion); + addEl(doc, config, "creator", cfg->creator.c_str()); + auto cipherAlg = addEl(doc, config, "cipherAlg", cfg->cipherIface); + cipherAlg->SetAttribute("class_id", "1"); + cipherAlg->SetAttribute("tracking_level", "0"); + cipherAlg->SetAttribute("version", "0"); + addEl(doc, config, "nameAlg", cfg->nameIface); + addEl(doc, config, "keySize", cfg->keySize); + addEl(doc, config, "blockSize", cfg->blockSize); + addEl(doc, config, "uniqueIV", cfg->uniqueIV); + addEl(doc, config, "chainedNameIV", cfg->chainedNameIV); + addEl(doc, config, "externalIVChaining", cfg->externalIVChaining); + addEl(doc, config, "blockMACBytes", cfg->blockMACBytes); + addEl(doc, config, "blockMACRandBytes", cfg->blockMACRandBytes); + addEl(doc, config, "allowHoles", cfg->allowHoles); + addEl(doc, config, "encodedKeySize", (int)cfg->keyData.size()); + addEl(doc, config, "encodedKeyData", cfg->keyData); + addEl(doc, config, "saltLen", (int)cfg->salt.size()); + addEl(doc, config, "saltData", cfg->salt); + addEl(doc, config, "kdfIterations", cfg->kdfIterations); + addEl(doc, config, "desiredKDFDuration", (int)cfg->desiredKDFDuration); + + auto err = doc.SaveFile(configFile, false); + return err == tinyxml2::XML_SUCCESS; } bool writeV5Config(const char *configFile, - const shared_ptr &config) { + const EncFSConfig *config) { ConfigReader cfg; cfg["creator"] << config->creator; @@ -576,7 +581,7 @@ bool writeV5Config(const char *configFile, } bool writeV4Config(const char *configFile, - const shared_ptr &config) { + const EncFSConfig *config) { ConfigReader cfg; cfg["cipher"] << config->cipherIface; @@ -1125,7 +1130,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { // xgroup(setup) cout << _("Configuration finished. The filesystem to be created has\n" "the following properties:") << endl; - showFSInfo(config); + showFSInfo(config.get()); if (config->externalIVChaining) { cout << _("-------------------------- WARNING --------------------------\n") @@ -1175,7 +1180,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { return rootInfo; } - if (!saveConfig(Config_V6, rootDir, config)) return rootInfo; + if (!saveConfig(Config_V6, rootDir, config.get())) return rootInfo; // fill in config struct shared_ptr nameCoder = @@ -1208,7 +1213,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { return rootInfo; } -void showFSInfo(const shared_ptr &config) { +void showFSInfo(const EncFSConfig *config) { shared_ptr cipher = Cipher::New(config->cipherIface, -1); { cout << autosprintf( @@ -1517,7 +1522,7 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { RootPtr rootInfo; shared_ptr config(new EncFSConfig); - if (readConfig(opts->rootDir, config) != Config_None) { + if (readConfig(opts->rootDir, config.get()) != Config_None) { if (config->blockMACBytes == 0 && opts->requireMac) { cout << _( "The configuration disabled MAC, but you passed --require-macs\n"); diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index 4928e47..5e09fe9 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -69,11 +69,11 @@ enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia }; */ struct EncFS_Opts { std::string rootDir; - std::string mountPoint; // where to make filesystem visible - bool createIfNotFound; // create filesystem if not found - bool idleTracking; // turn on idle monitoring of filesystem - bool mountOnDemand; // mounting on-demand - bool delayMount; // delay initial mount + std::string mountPoint; // where to make filesystem visible + bool createIfNotFound; // create filesystem if not found + bool idleTracking; // turn on idle monitoring of filesystem + bool mountOnDemand; // mounting on-demand + bool delayMount; // delay initial mount bool checkKey; // check crypto key decoding bool forceDecode; // force decode on MAC block failures @@ -118,15 +118,14 @@ struct EncFS_Opts { /* Read existing config file. Looks for any supported configuration version. */ -ConfigType readConfig(const std::string &rootDir, - const 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 shared_ptr &config); + const EncFSConfig *config); class EncFS_Context; @@ -134,21 +133,18 @@ RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts); RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts); -void showFSInfo(const shared_ptr &config); +void showFSInfo(const EncFSConfig *config); -bool readV4Config(const char *configFile, const shared_ptr &config, +bool readV4Config(const char *configFile, EncFSConfig *config, struct ConfigInfo *); -bool writeV4Config(const char *configFile, - const shared_ptr &config); +bool writeV4Config(const char *configFile, const EncFSConfig *config); -bool readV5Config(const char *configFile, const shared_ptr &config, +bool readV5Config(const char *configFile, EncFSConfig *config, struct ConfigInfo *); -bool writeV5Config(const char *configFile, - const shared_ptr &config); +bool writeV5Config(const char *configFile, const EncFSConfig *config); -bool readV6Config(const char *configFile, const shared_ptr &config, +bool readV6Config(const char *configFile, EncFSConfig *config, struct ConfigInfo *); -bool writeV6Config(const char *configFile, - const shared_ptr &config); +bool writeV6Config(const char *configFile, const EncFSConfig *config); #endif diff --git a/encfs/XmlReader.cpp b/encfs/XmlReader.cpp new file mode 100644 index 0000000..c5338fd --- /dev/null +++ b/encfs/XmlReader.cpp @@ -0,0 +1,192 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012-2013, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "XmlReader.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "base64.h" +#include "Interface.h" +#include "shared_ptr.h" + +XmlValue::~XmlValue() {} + +XmlValuePtr XmlValue::operator[](const char *path) const { return find(path); } + +XmlValuePtr XmlValue::find(const char *path) const { + // Shouldn't get here. + rError("in XmlValue::find(%s)", path); + return XmlValuePtr(); +} + +bool XmlValue::read(const char *path, std::string *out) const { + XmlValuePtr value = find(path); + if (!value) return false; + + *out = value->text(); + return true; +} + +bool XmlValue::read(const char *path, int *out) const { + XmlValuePtr value = find(path); + if (!value) return false; + + *out = atoi(value->text().c_str()); + return true; +} + +bool XmlValue::read(const char *path, long *out) const { + XmlValuePtr value = find(path); + if (!value) return false; + + *out = atol(value->text().c_str()); + return true; +} + +bool XmlValue::read(const char *path, double *out) const { + XmlValuePtr value = find(path); + if (!value) return false; + + *out = atof(value->text().c_str()); + return true; +} + +bool XmlValue::read(const char *path, bool *out) const { + XmlValuePtr value = find(path); + if (!value) return false; + + *out = atoi(value->text().c_str()); + return true; +} + +bool XmlValue::readB64(const char *path, unsigned char *data, int length) const { + XmlValuePtr value = find(path); + if (!value) return false; + + std::string s = value->text(); + s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); + s.erase(s.find_last_not_of("=") + 1); + + int decodedSize = B64ToB256Bytes(s.size()); + if (decodedSize != length) { + rError("decoding bytes len %d, expecting output len %d, got %d", s.size(), + length, decodedSize); + return false; + } + if (!B64StandardDecode(data, (unsigned char *)s.data(), s.size())) { + rError("B64 decode failure on \"%s\"", s.c_str()); + return false; + } + + return true; +} + +bool XmlValue::read(const char *path, rel::Interface *out) const { + XmlValuePtr node = find(path); + if (!node) return false; + + bool ok = node->read("name", &out->name()) && + node->read("major", &out->current()) && + node->read("minor", &out->revision()); + + return ok; +} + +std::string safeValueForNode(const tinyxml2::XMLElement *element) { + std::string value; + if (element == NULL) return value; + + const tinyxml2::XMLNode *child = element->FirstChild(); + if (child) { + const tinyxml2::XMLText *childText = child->ToText(); + if (childText) value = childText->Value(); + } + + return value; +} + +class XmlNode : virtual public XmlValue { + const tinyxml2::XMLElement *element; + + public: + XmlNode(const tinyxml2::XMLElement *element_) + : XmlValue(safeValueForNode(element_)), element(element_) {} + + virtual ~XmlNode() {} + + virtual XmlValuePtr find(const char *name) const { + if (name[0] == '@') { + const char *value = element->Attribute(name + 1); + if (value) + return XmlValuePtr(new XmlValue(value)); + else + return XmlValuePtr(); + } else { + const tinyxml2::XMLElement *el = element->FirstChildElement(name); + if (el) + return XmlValuePtr(new XmlNode(el)); + else + return XmlValuePtr(); + } + } +}; + +struct XmlReader::XmlReaderData { + shared_ptr doc; +}; + +XmlReader::XmlReader() : pd(new XmlReaderData()) {} + +XmlReader::~XmlReader() {} + +bool XmlReader::load(const char *fileName) { + pd->doc.reset(new tinyxml2::XMLDocument()); + + auto err = pd->doc->LoadFile(fileName); + return err == tinyxml2::XML_SUCCESS; +} + +XmlValuePtr XmlReader::operator[](const char *name) const { + tinyxml2::XMLNode *node = pd->doc->FirstChildElement(name); + if (node == NULL) { + rError("Xml node %s not found", name); + return XmlValuePtr(new XmlValue()); + } + + tinyxml2::XMLElement *element = node->ToElement(); + if (element == NULL) { + rError("Xml node %s not element", name); + 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..bae8e58 --- /dev/null +++ b/encfs/XmlReader.h @@ -0,0 +1,72 @@ +/***************************************************************************** + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef _XmlReader_incl_ +#define _XmlReader_incl_ + +#include +#include "Interface.h" +#include "shared_ptr.h" + +class XmlValue; +typedef 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; + + const std::string &text() const { return value; } + + bool read(const char *path, std::string *out) const; + bool readB64(const char *path, unsigned char *out, int length) const; + + bool read(const char *path, int *out) const; + bool read(const char *path, long *out) const; + bool read(const char *path, double *out) const; + bool read(const char *path, bool *out) const; + + bool read(const char *path, rel::Interface *out) const; + + protected: + virtual XmlValuePtr find(const char *name) const; +}; + +class XmlReader { + public: + XmlReader(); + ~XmlReader(); + + bool load(const char *fileName); + + XmlValuePtr operator[](const char *name) const; + + private: + struct XmlReaderData; + shared_ptr pd; +}; + +#endif diff --git a/encfs/base64.cpp b/encfs/base64.cpp index bd2e768..af080f2 100644 --- a/encfs/base64.cpp +++ b/encfs/base64.cpp @@ -20,8 +20,11 @@ #include "base64.h" +#include #include +#include + // change between two powers of two, stored as the low bits of the bytes in the // arrays. // It is the caller's responsibility to make sure the output array is large @@ -177,3 +180,99 @@ void AsciiToB32(unsigned char *out, const unsigned char *in, int length) { *out++ = (unsigned char)lch; } } + +#define WHITESPACE 64 +#define EQUALS 65 +#define INVALID 66 + +static const unsigned char d[] = { + 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 62, 66, 66, 66, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 66, 66, // 50-59 + 66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 66, 66, 66, + 66, 66, 66, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100-109 + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + +bool B64StandardDecode(unsigned char *out, const unsigned char *in, int inLen) { + const unsigned char *end = in + inLen; + size_t buf = 1; + + while (in < end) { + unsigned char v = *in++; + if (v > 'z') { + rError("Invalid character: %d", (unsigned int)v); + return false; + } + unsigned char c = d[v]; + + switch (c) { + case WHITESPACE: + continue; /* skip whitespace */ + case INVALID: + rError("Invalid character: %d", (unsigned int)v); + return false; /* invalid input, return error */ + case EQUALS: /* pad character, end of data */ + in = end; + continue; + default: + buf = buf << 6 | c; + + /* If the buffer is full, split it into bytes */ + if (buf & 0x1000000) { + *out++ = buf >> 16; + *out++ = buf >> 8; + *out++ = buf; + buf = 1; + } + } + } + + if (buf & 0x40000) { + *out++ = buf >> 10; + *out++ = buf >> 2; + } else if (buf & 0x1000) { + *out++ = buf >> 4; + } + + return true; +} + +// Lookup table for encoding +// If you want to use an alternate alphabet, change the characters here +const static char encodeLookup[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +std::string B64StandardEncode(std::vector inputBuffer) { + std::string encodedString; + encodedString.reserve(B256ToB64Bytes(inputBuffer.size())); + long temp; + std::vector::iterator cursor = inputBuffer.begin(); + for (size_t idx = 0; idx < inputBuffer.size() / 3; idx++) { + temp = (*cursor++) << 16; // Convert to big endian + temp += (*cursor++) << 8; + temp += (*cursor++); + encodedString.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]); + encodedString.append(1, encodeLookup[(temp & 0x0003F000) >> 12]); + encodedString.append(1, encodeLookup[(temp & 0x00000FC0) >> 6]); + encodedString.append(1, encodeLookup[(temp & 0x0000003F)]); + } + + switch (inputBuffer.size() % 3) { + case 1: + temp = (*cursor++) << 16; // Convert to big endian + encodedString.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]); + encodedString.append(1, encodeLookup[(temp & 0x0003F000) >> 12]); + encodedString.append(2, '='); + break; + case 2: + temp = (*cursor++) << 16; // Convert to big endian + temp += (*cursor++) << 8; + encodedString.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]); + encodedString.append(1, encodeLookup[(temp & 0x0003F000) >> 12]); + encodedString.append(1, encodeLookup[(temp & 0x00000FC0) >> 6]); + encodedString.append(1, '='); + break; + } + return encodedString; +} diff --git a/encfs/base64.h b/encfs/base64.h index 2b648ed..d88f681 100644 --- a/encfs/base64.h +++ b/encfs/base64.h @@ -21,6 +21,9 @@ #ifndef _base64_incl_ #define _base64_incl_ +#include +#include + inline int B64ToB256Bytes(int numB64Bytes) { return (numB64Bytes * 6) / 8; // round down } @@ -62,4 +65,11 @@ void AsciiToB64(unsigned char *out, const unsigned char *in, int length); void AsciiToB32(unsigned char *buf, int length); void AsciiToB32(unsigned char *out, const unsigned char *in, int length); +// Decode standard B64 into the output array. +// Used only to decode legacy Boost XML serialized config format. +// The output size must be at least B64ToB256Bytes(inputLen). +bool B64StandardDecode(unsigned char *out, const unsigned char *in, int inputLen); + +std::string B64StandardEncode(std::vector input); + #endif diff --git a/encfs/boost-versioning.h b/encfs/boost-versioning.h deleted file mode 100644 index 7f97ab3..0000000 --- a/encfs/boost-versioning.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef BOOST_VERSIONING_INCL -#define BOOST_VERSIONING_INCL - -// This header stores workaround code for dealing with incompatible changes -// made to boost archive/serialization classes. - -#if (BOOST_VERSION <= 104100) -// Easy case, boost archive serial numbers are sizeof(int) -BOOST_CLASS_VERSION(EncFSConfig, V6SubVersion) -#else -// starting with boost 1.42, serial numbers change to 8-bit. However to make -// things tricker, the internal comparison is more like 16bit, which makes -// writing backward compatible code very tricky. - -// We make a partial specialization of the iserializer class to remove the -// version number checking which would otherwise cause boost::serialization to -// throw an exception if it came across a version that was greater then what -// we specify in BOOST_CLASS_VERSION below. Without this, manual editing -// of the file is needed before boost will allow us to read it. - -BOOST_CLASS_VERSION(EncFSConfig, 20) - -namespace boost { -namespace archive { -namespace detail { - -// Specialize iserializer class in order to get rid of version check -template -class iserializer : public basic_iserializer { - private: - virtual void destroy(/*const*/ void *address) const { - boost::serialization::access::destroy(static_cast(address)); - } - - protected: - explicit iserializer() - : basic_iserializer(boost::serialization::singleton< - BOOST_DEDUCED_TYPENAME boost::serialization:: - type_info_implementation< - EncFSConfig>::type>::get_const_instance()) {} - - public: - virtual BOOST_DLLEXPORT void load_object_data( - basic_iarchive &ar, void *x, - const unsigned int file_version) const BOOST_USED; - virtual bool class_info() const { - return boost::serialization::implementation_level::value >= - boost::serialization::object_class_info; - } - virtual bool tracking(const unsigned int /* flags */) const { - return boost::serialization::tracking_level::value == - boost::serialization::track_always || - (boost::serialization::tracking_level::value == - boost::serialization::track_selectively && - serialized_as_pointer()); - } - virtual version_type version() const { - return version_type(::boost::serialization::version::value); - } - virtual bool is_polymorphic() const { - return boost::is_polymorphic::value; - } - virtual ~iserializer(){}; -}; - -template -BOOST_DLLEXPORT void iserializer::load_object_data( - basic_iarchive &ar, void *x, const unsigned int file_version) const { - boost::serialization::serialize_adl( - boost::serialization::smart_cast_reference(ar), - *static_cast(x), file_version); -} -} -} -} - -#endif - -#endif // BOOST_VERSIONING_INCL diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp index 33cd5b5..ce313e5 100644 --- a/encfs/encfsctl.cpp +++ b/encfs/encfsctl.cpp @@ -167,7 +167,7 @@ static int showInfo(int argc, char **argv) { string rootDir = argv[1]; if (!checkDir(rootDir)) return EXIT_FAILURE; - shared_ptr config(new EncFSConfig); + EncFSConfig* config = new EncFSConfig; ConfigType type = readConfig(rootDir, config); // show information stored in config.. @@ -617,7 +617,7 @@ static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, c string rootDir = argv[1]; if (!checkDir(rootDir)) return EXIT_FAILURE; - shared_ptr config(new EncFSConfig); + EncFSConfig* config = new EncFSConfig; ConfigType cfgType = readConfig(rootDir, config); if (cfgType == Config_None) { diff --git a/encfs/test.cpp b/encfs/test.cpp index 6bd8f70..411706d 100644 --- a/encfs/test.cpp +++ b/encfs/test.cpp @@ -16,12 +16,9 @@ * */ -#include -#include -#include -#include #include #include +#include #include #include #include @@ -29,6 +26,11 @@ #include #include +#include +#include +#include +#include + #include "BlockNameIO.h" #include "Cipher.h" #include "CipherKey.h" @@ -132,8 +134,9 @@ static bool testNameCoding(DirNode &dirNode, bool verbose) { bool runTests(const shared_ptr &cipher, bool verbose) { // create a random key - if (verbose) + if (verbose) { cerr << "Generating new key, output will be different on each run\n\n"; + } CipherKey key = cipher->newRandomKey(); if (verbose) cerr << "Testing key save / restore :"; @@ -173,18 +176,17 @@ bool runTests(const shared_ptr &cipher, bool verbose) { cfg.assignKeyData(keyBuf, encodedKeySize); // save config - string data; + auto name = std::tmpnam(nullptr); { - ostringstream st; - st << cfg; - data = st.str(); + auto ok = writeV6Config(name, &cfg); + rAssert(ok == true); } // read back in and check everything.. EncFSConfig cfg2; { - istringstream st(data); - st >> cfg2; + auto ok = readV6Config(name, &cfg2, nullptr); + rAssert(ok == true); } // check..