2008-01-07 09:09:04 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* Author: Valient Gough <vgough@pobox.com>
|
|
|
|
*
|
|
|
|
*****************************************************************************
|
|
|
|
* Copyright (c) 2004, Valient Gough
|
|
|
|
*
|
|
|
|
* This program is free software; you can distribute it and/or modify it under
|
|
|
|
* the terms of the GNU General Public License (GPL), as published by the Free
|
|
|
|
* Software Foundation; either version 2 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// defines needed for RedHat 7.3...
|
|
|
|
#ifdef linux
|
|
|
|
#define _XOPEN_SOURCE 500 // make sure pwrite() is pulled in
|
|
|
|
#endif
|
|
|
|
#define _BSD_SOURCE // pick up setenv on RH7.3
|
|
|
|
|
|
|
|
#include "encfs.h"
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "readpassphrase.h"
|
|
|
|
#include "autosprintf.h"
|
|
|
|
|
|
|
|
#include "FileUtils.h"
|
|
|
|
#include "ConfigReader.h"
|
|
|
|
|
|
|
|
#include "DirNode.h"
|
|
|
|
#include "Cipher.h"
|
|
|
|
#include "StreamNameIO.h"
|
|
|
|
#include "BlockNameIO.h"
|
|
|
|
#include "NullNameIO.h"
|
|
|
|
#include "Context.h"
|
|
|
|
|
|
|
|
#include <rlog/rlog.h>
|
|
|
|
#include <rlog/Error.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2008-05-19 06:08:58 +02:00
|
|
|
#include <cctype>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <cstring>
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "i18n.h"
|
|
|
|
|
2010-06-15 08:20:51 +02:00
|
|
|
#include <boost/version.hpp>
|
2008-04-13 22:35:57 +02:00
|
|
|
#include <boost/filesystem/fstream.hpp>
|
|
|
|
#include <boost/archive/xml_iarchive.hpp>
|
|
|
|
#include <boost/archive/xml_oarchive.hpp>
|
|
|
|
#include <boost/serialization/split_free.hpp>
|
2008-05-14 19:34:51 +02:00
|
|
|
#include <boost/serialization/binary_object.hpp>
|
2008-04-13 22:35:57 +02:00
|
|
|
|
2008-09-10 07:53:44 +02:00
|
|
|
// disable rlog section grouping for this file.. seems to cause problems
|
|
|
|
#undef RLOG_SECTION
|
|
|
|
#define RLOG_SECTION
|
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
using namespace rel;
|
|
|
|
using namespace rlog;
|
|
|
|
using namespace std;
|
|
|
|
using namespace gnu;
|
2008-04-13 22:35:57 +02:00
|
|
|
namespace fs = boost::filesystem;
|
2008-05-11 05:41:24 +02:00
|
|
|
namespace serial = boost::serialization;
|
2008-01-07 09:09:04 +01:00
|
|
|
|
2008-04-11 10:37:36 +02:00
|
|
|
static const int DefaultBlockSize = 1024;
|
2008-01-13 00:13:49 +01:00
|
|
|
// The maximum length of text passwords. If longer are needed,
|
|
|
|
// use the extpass option, as extpass can return arbitrary length binary data.
|
|
|
|
static const int MaxPassBuf = 512;
|
|
|
|
|
2008-08-17 09:26:52 +02:00
|
|
|
static const int NormalKDFDuration = 500; // 1/2 a second
|
|
|
|
static const int ParanoiaKDFDuration = 3000; // 3 seconds
|
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
// environment variable names for values encfs stores in the environment when
|
|
|
|
// calling an external password program.
|
|
|
|
static const char ENCFS_ENV_ROOTDIR[] = "encfs_root";
|
|
|
|
static const char ENCFS_ENV_STDOUT[] = "encfs_stdout";
|
|
|
|
static const char ENCFS_ENV_STDERR[] = "encfs_stderr";
|
|
|
|
|
|
|
|
|
|
|
|
//static int V5SubVersion = 20040518;
|
|
|
|
//static int V5SubVersion = 20040621; // add external IV chaining
|
2008-04-13 22:35:57 +02:00
|
|
|
static int V5SubVersion = 20040813; // fix MACFileIO block size issues
|
2008-01-07 09:09:04 +01:00
|
|
|
static int V5SubVersionDefault = 0;
|
|
|
|
|
2008-08-16 18:39:31 +02:00
|
|
|
// 20080813 was really made on 20080413 -- typo on date..
|
|
|
|
//const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option
|
2010-06-15 08:20:51 +02:00
|
|
|
//const int V6SubVersion = 20080816; // add salt and iteration count
|
|
|
|
const int V6SubVersion = 20100713; // add version field for boost 1.42+
|
2008-04-13 22:35:57 +02:00
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
struct ConfigInfo
|
|
|
|
{
|
|
|
|
const char *fileName;
|
|
|
|
ConfigType type;
|
|
|
|
const char *environmentOverride;
|
|
|
|
bool (*loadFunc)(const char *fileName, EncFSConfig *config,
|
|
|
|
ConfigInfo *cfg);
|
|
|
|
bool (*saveFunc)(const char *fileName, EncFSConfig *config);
|
|
|
|
int currentSubVersion;
|
|
|
|
int defaultSubVersion;
|
|
|
|
} ConfigFileMapping[] = {
|
2008-04-13 22:35:57 +02:00
|
|
|
{".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config,
|
|
|
|
V6SubVersion, 0 },
|
|
|
|
// backward compatible support for older versions
|
2008-01-07 09:09:04 +01:00
|
|
|
{".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config,
|
|
|
|
V5SubVersion, V5SubVersionDefault },
|
|
|
|
{".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0 },
|
2008-04-13 22:35:57 +02:00
|
|
|
// no longer support earlier versions
|
|
|
|
{".encfs3", Config_V3, NULL, NULL, NULL, 0, 0 },
|
2008-01-07 09:09:04 +01:00
|
|
|
{".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0 },
|
|
|
|
{".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0 },
|
|
|
|
{NULL,Config_None, NULL, NULL, NULL, 0, 0}};
|
|
|
|
|
|
|
|
|
2010-06-15 08:20:51 +02:00
|
|
|
#include "boost-versioning.h"
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
// define serialization helpers
|
|
|
|
namespace boost
|
|
|
|
{
|
|
|
|
namespace serialization
|
|
|
|
{
|
|
|
|
template<class Archive>
|
|
|
|
void save(Archive &ar, const EncFSConfig &cfg,
|
|
|
|
unsigned int version)
|
|
|
|
{
|
|
|
|
(void)version;
|
2010-06-15 08:20:51 +02:00
|
|
|
// version 20 (aka 20100613)
|
|
|
|
ar << make_nvp("version", cfg.subVersion);
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
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);
|
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
int encodedSize = cfg.keyData.size();
|
|
|
|
ar << make_nvp("encodedKeySize", encodedSize);
|
|
|
|
ar << make_nvp("encodedKeyData",
|
|
|
|
serial::make_binary_object(cfg.getKeyData(), encodedSize));
|
2008-08-16 18:39:31 +02:00
|
|
|
|
|
|
|
// version 20080816
|
2008-08-23 08:33:24 +02:00
|
|
|
int size = cfg.salt.size();
|
|
|
|
ar << make_nvp("saltLen", size);
|
2008-08-16 18:39:31 +02:00
|
|
|
ar << make_nvp("saltData",
|
2008-08-23 08:33:24 +02:00
|
|
|
serial::make_binary_object(cfg.getSaltData(), size));
|
2008-08-16 18:39:31 +02:00
|
|
|
ar << make_nvp("kdfIterations", cfg.kdfIterations);
|
2008-08-17 09:26:52 +02:00
|
|
|
ar << make_nvp("desiredKDFDuration", cfg.desiredKDFDuration);
|
2008-04-13 22:35:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class Archive>
|
|
|
|
void load(Archive &ar, EncFSConfig &cfg, unsigned int version)
|
|
|
|
{
|
2010-06-15 08:20:51 +02:00
|
|
|
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 - please fix config file");
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
rInfo("Boost <= 1.41 compatibility mode");
|
|
|
|
cfg.subVersion = version;
|
|
|
|
}
|
|
|
|
rInfo("subVersion = %i", cfg.subVersion);
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
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);
|
2008-08-16 18:39:31 +02:00
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
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;
|
2008-08-16 18:39:31 +02:00
|
|
|
|
2010-06-15 08:20:51 +02:00
|
|
|
if(cfg.subVersion >= 20080816)
|
2008-08-16 18:39:31 +02:00
|
|
|
{
|
2008-08-23 08:33:24 +02:00
|
|
|
int saltLen;
|
|
|
|
ar >> make_nvp("saltLen", saltLen);
|
|
|
|
unsigned char *salt = new unsigned char[saltLen];
|
2008-08-16 18:39:31 +02:00
|
|
|
ar >> make_nvp("saltData",
|
2008-08-23 08:33:24 +02:00
|
|
|
serial::make_binary_object(salt, saltLen));
|
|
|
|
cfg.assignSaltData(salt, saltLen);
|
|
|
|
delete[] salt;
|
|
|
|
|
2008-08-16 18:39:31 +02:00
|
|
|
ar >> make_nvp("kdfIterations", cfg.kdfIterations);
|
2008-08-17 09:26:52 +02:00
|
|
|
ar >> make_nvp("desiredKDFDuration", cfg.desiredKDFDuration);
|
2008-08-16 18:39:31 +02:00
|
|
|
} else
|
|
|
|
{
|
2008-08-23 08:33:24 +02:00
|
|
|
cfg.salt.clear();
|
2008-08-17 09:26:52 +02:00
|
|
|
cfg.kdfIterations = 16;
|
|
|
|
cfg.desiredKDFDuration = NormalKDFDuration;
|
2008-08-16 18:39:31 +02:00
|
|
|
}
|
2008-04-13 22:35:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class Archive>
|
|
|
|
void serialize(Archive &ar, EncFSConfig &cfg, unsigned int version)
|
|
|
|
{
|
|
|
|
split_free(ar, cfg, version);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class Archive>
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
EncFS_Root::EncFS_Root()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
EncFS_Root::~EncFS_Root()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool fileExists( const char * fileName )
|
|
|
|
{
|
|
|
|
struct stat buf;
|
|
|
|
if(!lstat( fileName, &buf ))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// XXX show perror?
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDirectory( const char *fileName )
|
|
|
|
{
|
|
|
|
struct stat buf;
|
|
|
|
if( !lstat( fileName, &buf ))
|
|
|
|
{
|
|
|
|
return S_ISDIR( buf.st_mode );
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAbsolutePath( const char *fileName )
|
|
|
|
{
|
|
|
|
if(fileName && fileName[0] != '\0' && fileName[0] == '/')
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *lastPathElement( const char *name )
|
|
|
|
{
|
|
|
|
const char *loc = strrchr( name, '/' );
|
|
|
|
return loc ? loc + 1 : name;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string parentDirectory( const std::string &path )
|
|
|
|
{
|
|
|
|
size_t last = path.find_last_of( '/' );
|
|
|
|
if(last == string::npos)
|
|
|
|
return string("");
|
|
|
|
else
|
|
|
|
return path.substr(0, last);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool userAllowMkdir( const char *path, mode_t mode )
|
|
|
|
{
|
|
|
|
// TODO: can we internationalize the y/n names? Seems strange to prompt in
|
|
|
|
// their own language but then have to respond 'y' or 'n'.
|
|
|
|
// xgroup(setup)
|
|
|
|
cerr << autosprintf( _("The directory \"%s\" does not exist. Should it be created? (y,n) "), path );
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
|
|
|
|
if(toupper(answer[0]) == 'Y')
|
|
|
|
{
|
|
|
|
int result = mkdir( path, mode );
|
|
|
|
if(result < 0)
|
|
|
|
{
|
|
|
|
perror( _("Unable to create directory: ") );
|
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// Directory not created, by user request
|
|
|
|
cerr << _("Directory not created.") << "\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ConfigType readConfig_load( ConfigInfo *nm, const char *path,
|
|
|
|
EncFSConfig *config )
|
|
|
|
{
|
|
|
|
if( nm->loadFunc )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if( (*nm->loadFunc)( path, config, nm ))
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
config->cfgType = nm->type;
|
2008-01-07 09:09:04 +01:00
|
|
|
return nm->type;
|
2008-08-17 09:26:52 +02:00
|
|
|
}
|
2008-01-07 09:09:04 +01:00
|
|
|
} catch( rlog::Error & err )
|
|
|
|
{
|
|
|
|
err.log( _RLWarningChannel );
|
|
|
|
}
|
|
|
|
|
|
|
|
rError( _("Found config file %s, but failed to load"), path);
|
|
|
|
return Config_None;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// No load function - must be an unsupported type..
|
2008-08-17 09:26:52 +02:00
|
|
|
config->cfgType = nm->type;
|
2008-01-07 09:09:04 +01:00
|
|
|
return nm->type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ConfigType readConfig( const string &rootDir, EncFSConfig *config )
|
|
|
|
{
|
|
|
|
ConfigInfo *nm = ConfigFileMapping;
|
|
|
|
while(nm->fileName)
|
|
|
|
{
|
|
|
|
// allow environment variable to override default config path
|
|
|
|
if( nm->environmentOverride != NULL )
|
|
|
|
{
|
|
|
|
char *envFile = getenv( nm->environmentOverride );
|
|
|
|
if( envFile != NULL )
|
|
|
|
return readConfig_load( nm, envFile, config );
|
|
|
|
}
|
|
|
|
// the standard place to look is in the root directory
|
|
|
|
string path = rootDir + nm->fileName;
|
|
|
|
if( fileExists( path.c_str() ) )
|
|
|
|
return readConfig_load( nm, path.c_str(), config);
|
|
|
|
|
|
|
|
++nm;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Config_None;
|
|
|
|
}
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
bool readV6Config( const char *configFile, EncFSConfig *config,
|
|
|
|
ConfigInfo *info)
|
|
|
|
{
|
|
|
|
(void)info;
|
|
|
|
|
2008-05-05 05:50:22 +02:00
|
|
|
fs::ifstream st( configFile );
|
2008-04-13 22:35:57 +02:00
|
|
|
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);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
bool readV5Config( const char *configFile, EncFSConfig *config,
|
|
|
|
ConfigInfo *info)
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
|
|
|
|
// use Config to parse the file and query it..
|
|
|
|
ConfigReader cfgRdr;
|
|
|
|
if(cfgRdr.load( configFile ))
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
config->subVersion = cfgRdr["subVersion"].readInt(
|
|
|
|
info->defaultSubVersion );
|
|
|
|
if(config->subVersion > info->currentSubVersion)
|
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if( config->subVersion < 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;
|
2008-08-23 08:33:24 +02:00
|
|
|
|
|
|
|
string data;
|
|
|
|
cfgRdr["keyData"] >> data;
|
|
|
|
config->assignKeyData(data);
|
2008-01-07 09:09:04 +01:00
|
|
|
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);
|
2008-04-11 10:37:19 +02:00
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
ok = true;
|
|
|
|
} catch( rlog::Error &err)
|
|
|
|
{
|
|
|
|
err.log( _RLWarningChannel );
|
|
|
|
rDebug("Error parsing data in config file %s", configFile);
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool readV4Config( const char *configFile, EncFSConfig *config,
|
|
|
|
ConfigInfo *info)
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
|
|
|
|
// use Config to parse the file and query it..
|
|
|
|
ConfigReader cfgRdr;
|
|
|
|
if(cfgRdr.load( configFile ))
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
cfgRdr["cipher"] >> config->cipherIface;
|
|
|
|
cfgRdr["keySize"] >> config->keySize;
|
|
|
|
cfgRdr["blockSize"] >> config->blockSize;
|
2008-08-23 08:33:24 +02:00
|
|
|
string data;
|
|
|
|
cfgRdr["keyData"] >> data;
|
|
|
|
config->assignKeyData(data);
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
ok = true;
|
|
|
|
} catch( rlog::Error &err)
|
|
|
|
{
|
|
|
|
err.log( _RLWarningChannel );
|
|
|
|
rDebug("Error parsing config file %s", configFile);
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool saveConfig( ConfigType type, const string &rootDir,
|
|
|
|
EncFSConfig *config )
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
|
|
|
|
ConfigInfo *nm = ConfigFileMapping;
|
|
|
|
while(nm->fileName)
|
|
|
|
{
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ok = (*nm->saveFunc)( path.c_str(), config );
|
|
|
|
} catch( rlog::Error &err )
|
|
|
|
{
|
|
|
|
err.log( _RLWarningChannel );
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++nm;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
bool writeV6Config( const char *configFile, EncFSConfig *config )
|
|
|
|
{
|
2008-05-05 05:50:22 +02:00
|
|
|
fs::ofstream st( configFile );
|
2008-04-13 22:35:57 +02:00
|
|
|
if(!st.is_open())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
boost::archive::xml_oarchive oa(st);
|
|
|
|
oa << BOOST_SERIALIZATION_NVP( config );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
bool writeV5Config( const char *configFile, EncFSConfig *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;
|
2008-08-23 08:33:24 +02:00
|
|
|
string key;
|
|
|
|
key.assign((char *)config->getKeyData(), config->keyData.size());
|
|
|
|
cfg["keyData"] << key;
|
2008-01-07 09:09:04 +01:00
|
|
|
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, EncFSConfig *config )
|
|
|
|
{
|
|
|
|
ConfigReader cfg;
|
|
|
|
|
|
|
|
cfg["cipher"] << config->cipherIface;
|
|
|
|
cfg["keySize"] << config->keySize;
|
|
|
|
cfg["blockSize"] << config->blockSize;
|
2008-08-23 08:33:24 +02:00
|
|
|
string key;
|
|
|
|
key.assign((char *)config->getKeyData(), config->keyData.size());
|
|
|
|
cfg["keyData"] << key;
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
return cfg.save( configFile );
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
Cipher::CipherAlgorithm findCipherAlgorithm(const char *name,
|
|
|
|
int keySize )
|
|
|
|
{
|
|
|
|
Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList();
|
|
|
|
Cipher::AlgorithmList::const_iterator it;
|
|
|
|
for(it = algorithms.begin(); it != algorithms.end(); ++it)
|
|
|
|
{
|
|
|
|
if( !strcmp( name, it->name.c_str() )
|
|
|
|
&& it->keyLength.allowed( keySize ))
|
|
|
|
{
|
|
|
|
return *it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Cipher::CipherAlgorithm result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
Cipher::CipherAlgorithm selectCipherAlgorithm()
|
|
|
|
{
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
// figure out what cipher they want to use..
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("The following cipher algorithms are available:") << "\n";
|
|
|
|
Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList();
|
|
|
|
Cipher::AlgorithmList::const_iterator it;
|
|
|
|
int optNum = 1;
|
|
|
|
for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum)
|
|
|
|
{
|
|
|
|
cout << optNum << ". " << it->name
|
|
|
|
<< " : " << gettext(it->description.c_str()) << "\n";
|
|
|
|
if(it->keyLength.min() == it->keyLength.max())
|
|
|
|
{
|
|
|
|
// shown after algorithm name and description.
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << autosprintf(_(" -- key length %i bits")
|
|
|
|
, it->keyLength.min()) << "\n";
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
cout << autosprintf(
|
|
|
|
// shown after algorithm name and description.
|
|
|
|
// xgroup(setup)
|
|
|
|
_(" -- Supports key lengths of %i to %i bits"),
|
|
|
|
it->keyLength.min(), it->keyLength.max()) << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if(it->blockSize.min() == it->blockSize.max())
|
|
|
|
{
|
|
|
|
cout << autosprintf(
|
|
|
|
// shown after algorithm name and description.
|
|
|
|
// xgroup(setup)
|
|
|
|
_(" -- block size %i bytes"), it->blockSize.min())
|
|
|
|
<< "\n";
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
cout << autosprintf(
|
|
|
|
// shown after algorithm name and description.
|
|
|
|
// xgroup(setup)
|
|
|
|
_(" -- Supports block sizes of %i to %i bytes"),
|
|
|
|
it->blockSize.min(), it->blockSize.max()) << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << "\n" << _("Enter the number corresponding to your choice: ");
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
int cipherNum = atoi( answer );
|
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
if( cipherNum < 1 || cipherNum > (int)algorithms.size() )
|
|
|
|
{
|
|
|
|
cerr << _("Invalid selection.") << "\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
it = algorithms.begin();
|
|
|
|
while(--cipherNum) // numbering starts at 1
|
|
|
|
++it;
|
|
|
|
|
|
|
|
Cipher::CipherAlgorithm alg = *it;
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << autosprintf(_("Selected algorithm \"%s\""), alg.name.c_str())
|
|
|
|
<< "\n\n";
|
|
|
|
|
|
|
|
return alg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
Interface selectNameCoding()
|
|
|
|
{
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
// figure out what cipher they want to use..
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("The following filename encoding algorithms are available:")
|
|
|
|
<< "\n";
|
|
|
|
NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList();
|
|
|
|
NameIO::AlgorithmList::const_iterator it;
|
|
|
|
int optNum = 1;
|
|
|
|
for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum)
|
|
|
|
{
|
|
|
|
cout << optNum << ". " << it->name
|
|
|
|
<< " : " << gettext(it->description.c_str()) << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << "\n" << _("Enter the number corresponding to your choice: ");
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
int algNum = atoi( answer );
|
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
if( algNum < 1 || algNum > (int)algorithms.size() )
|
|
|
|
{
|
|
|
|
cerr << _("Invalid selection.") << "\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
it = algorithms.begin();
|
|
|
|
while(--algNum) // numbering starts at 1
|
|
|
|
++it;
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << autosprintf(_("Selected algorithm \"%s\""), it->name.c_str())
|
|
|
|
<< "\"\n\n";
|
|
|
|
|
|
|
|
return it->iface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int selectKeySize( const Cipher::CipherAlgorithm &alg )
|
|
|
|
{
|
|
|
|
if(alg.keyLength.min() == alg.keyLength.max())
|
|
|
|
{
|
|
|
|
cout << autosprintf(_("Using key size of %i bits"),
|
|
|
|
alg.keyLength.min()) << "\n";
|
|
|
|
return alg.keyLength.min();
|
|
|
|
}
|
|
|
|
|
|
|
|
cout << autosprintf(
|
|
|
|
// xgroup(setup)
|
|
|
|
_("Please select a key size in bits. The cipher you have chosen\n"
|
|
|
|
"supports sizes from %i to %i bits in increments of %i bits.\n"
|
|
|
|
"For example: "), alg.keyLength.min(), alg.keyLength.max(),
|
|
|
|
alg.keyLength.inc()) << "\n";
|
|
|
|
|
|
|
|
int numAvail = (alg.keyLength.max() - alg.keyLength.min())
|
|
|
|
/ alg.keyLength.inc();
|
|
|
|
|
|
|
|
if(numAvail < 5)
|
|
|
|
{
|
|
|
|
// show them all
|
|
|
|
for(int i=0; i<=numAvail; ++i)
|
|
|
|
{
|
|
|
|
if(i)
|
|
|
|
cout << ", ";
|
|
|
|
cout << alg.keyLength.min() + i * alg.keyLength.inc();
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// partial
|
|
|
|
for(int i=0; i<3; ++i)
|
|
|
|
{
|
|
|
|
if(i)
|
|
|
|
cout << ", ";
|
|
|
|
cout << alg.keyLength.min() + i * alg.keyLength.inc();
|
|
|
|
}
|
|
|
|
cout << " ... " << alg.keyLength.max() - alg.keyLength.inc();
|
|
|
|
cout << ", " << alg.keyLength.max();
|
|
|
|
}
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << "\n" << _("Selected key size: ");
|
|
|
|
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
int keySize = atoi( answer );
|
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
keySize = alg.keyLength.closest( keySize );
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << autosprintf(_("Using key size of %i bits"), keySize) << "\n\n";
|
|
|
|
|
|
|
|
return keySize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int selectBlockSize( const Cipher::CipherAlgorithm &alg )
|
|
|
|
{
|
|
|
|
if(alg.blockSize.min() == alg.blockSize.max())
|
|
|
|
{
|
|
|
|
cout << autosprintf(
|
|
|
|
// xgroup(setup)
|
|
|
|
_("Using filesystem block size of %i bytes"),
|
|
|
|
alg.blockSize.min()) << "\n";
|
|
|
|
return alg.blockSize.min();
|
|
|
|
}
|
|
|
|
|
|
|
|
cout << autosprintf(
|
|
|
|
// xgroup(setup)
|
|
|
|
_("Select a block size in bytes. The cipher you have chosen\n"
|
|
|
|
"supports sizes from %i to %i bytes in increments of %i.\n"
|
|
|
|
"Or just hit enter for the default (%i bytes)\n"),
|
|
|
|
alg.blockSize.min(), alg.blockSize.max(), alg.blockSize.inc(),
|
|
|
|
DefaultBlockSize);
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << "\n" << _("filesystem block size: ");
|
|
|
|
|
|
|
|
int blockSize = DefaultBlockSize;
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
if( atoi( answer ) >= alg.blockSize.min() )
|
|
|
|
blockSize = atoi( answer );
|
|
|
|
|
|
|
|
blockSize = alg.blockSize.closest( blockSize );
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << autosprintf(_("Using filesystem block size of %i bytes"),
|
|
|
|
blockSize) << "\n\n";
|
|
|
|
|
|
|
|
return blockSize;
|
|
|
|
}
|
|
|
|
|
2008-04-11 10:37:19 +02:00
|
|
|
static
|
|
|
|
bool boolDefaultNo(const char *prompt)
|
|
|
|
{
|
|
|
|
cout << prompt << "\n";
|
|
|
|
cout << _("The default here is No.\n"
|
|
|
|
"Any response that does not begin with 'y' will mean No: ");
|
|
|
|
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
cout << "\n";
|
|
|
|
|
2008-06-03 04:29:45 +02:00
|
|
|
if(tolower(answer[0]) == 'y')
|
2008-04-11 10:37:19 +02:00
|
|
|
return true;
|
2008-06-03 04:29:45 +02:00
|
|
|
else
|
|
|
|
return false;
|
2008-04-11 10:37:19 +02:00
|
|
|
}
|
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
static
|
|
|
|
void selectBlockMAC(int *macBytes, int *macRandBytes)
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
2008-04-11 10:37:19 +02:00
|
|
|
bool addMAC = boolDefaultNo(
|
|
|
|
_("Enable block authentication code headers\n"
|
2008-01-07 09:09:04 +01:00
|
|
|
"on every block in a file? This adds about 12 bytes per block\n"
|
|
|
|
"to the storage requirements for a file, and significantly affects\n"
|
|
|
|
"performance but it also means [almost] any modifications or errors\n"
|
2008-04-11 10:37:19 +02:00
|
|
|
"within a block will be caught and will cause a read error."));
|
2008-01-07 09:09:04 +01:00
|
|
|
|
2008-04-11 10:37:19 +02:00
|
|
|
if(addMAC)
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
*macBytes = 8;
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Add random bytes to each block header?\n"
|
|
|
|
"This adds a performance penalty, but ensures that blocks\n"
|
|
|
|
"have different authentication codes. Note that you can\n"
|
|
|
|
"have the same benefits by enabling per-file initialization\n"
|
|
|
|
"vectors, which does not come with as great of performance\n"
|
|
|
|
"penalty. \n"
|
|
|
|
"Select a number of bytes, from 0 (no random bytes) to 8: ");
|
2008-04-11 10:37:19 +02:00
|
|
|
|
|
|
|
char answer[10];
|
2008-01-07 09:09:04 +01:00
|
|
|
int randSize = 0;
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
randSize = atoi( answer );
|
|
|
|
if(randSize < 0)
|
|
|
|
randSize = 0;
|
|
|
|
if(randSize > 8)
|
|
|
|
randSize = 8;
|
|
|
|
|
|
|
|
*macRandBytes = randSize;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
*macBytes = 0;
|
|
|
|
*macRandBytes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-11 10:37:19 +02:00
|
|
|
static
|
|
|
|
bool boolDefaultYes(const char *prompt)
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
2008-04-11 10:37:19 +02:00
|
|
|
cout << prompt << "\n";
|
|
|
|
cout << _("The default here is Yes.\n"
|
|
|
|
"Any response that does not begin with 'n' will mean Yes: ");
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
char answer[10];
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
cout << "\n";
|
|
|
|
|
2008-06-03 04:29:45 +02:00
|
|
|
if(tolower(answer[0]) == 'n')
|
2008-04-11 10:37:19 +02:00
|
|
|
return false;
|
2008-06-03 04:29:45 +02:00
|
|
|
else
|
|
|
|
return true;
|
2008-04-11 10:37:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
bool selectUniqueIV()
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
|
|
|
return boolDefaultYes(
|
|
|
|
_("Enable per-file initialization vectors?\n"
|
|
|
|
"This adds about 8 bytes per file to the storage requirements.\n"
|
|
|
|
"It should not affect performance except possibly with applications\n"
|
|
|
|
"which rely on block-aligned file io for performance."));
|
2008-01-07 09:09:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
bool selectChainedIV()
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
2008-04-11 10:37:19 +02:00
|
|
|
return boolDefaultYes(
|
|
|
|
_("Enable filename initialization vector chaining?\n"
|
2008-01-07 09:09:04 +01:00
|
|
|
"This makes filename encoding dependent on the complete path, \n"
|
2008-04-11 10:37:19 +02:00
|
|
|
"rather then encoding each path element individually."));
|
2008-01-07 09:09:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
bool selectExternalChainedIV()
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
2008-04-11 10:37:19 +02:00
|
|
|
return boolDefaultNo(
|
|
|
|
_("Enable filename to IV header chaining?\n"
|
2008-01-07 09:09:04 +01:00
|
|
|
"This makes file data encoding dependent on the complete file path.\n"
|
|
|
|
"If a file is renamed, it will not decode sucessfully unless it\n"
|
|
|
|
"was renamed by encfs with the proper key.\n"
|
|
|
|
"If this option is enabled, then hard links will not be supported\n"
|
2008-04-11 10:37:19 +02:00
|
|
|
"in the filesystem."));
|
|
|
|
}
|
2008-01-07 09:09:04 +01:00
|
|
|
|
2008-04-11 10:37:19 +02:00
|
|
|
static
|
|
|
|
bool selectZeroBlockPassThrough()
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
2008-08-17 09:26:52 +02:00
|
|
|
return boolDefaultYes(
|
2008-04-11 10:37:19 +02:00
|
|
|
_("Enable file-hole pass-through?\n"
|
|
|
|
"This avoids writing encrypted blocks when file holes are created."));
|
2008-01-07 09:09:04 +01:00
|
|
|
}
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
RootPtr createV6Config( EncFS_Context *ctx, const std::string &rootDir,
|
2008-01-07 09:09:04 +01:00
|
|
|
bool enableIdleTracking, bool forceDecode,
|
|
|
|
const std::string &passwordProgram,
|
2009-12-07 20:15:23 +01:00
|
|
|
bool useStdin, bool reverseEncryption,
|
|
|
|
ConfigMode configMode)
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
RootPtr rootInfo;
|
|
|
|
|
|
|
|
// creating new volume key.. should check that is what the user is
|
|
|
|
// expecting...
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Creating new encrypted volume.") << endl;
|
|
|
|
|
|
|
|
char answer[10] = {0};
|
2009-12-07 20:15:23 +01:00
|
|
|
if(configMode == Config_Prompt)
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Please choose from one of the following options:\n"
|
|
|
|
" enter \"x\" for expert configuration mode,\n"
|
|
|
|
" enter \"p\" for pre-configured paranoia mode,\n"
|
|
|
|
" anything else, or an empty line will select standard mode.\n"
|
|
|
|
"?> ");
|
|
|
|
|
|
|
|
fgets( answer, sizeof(answer), stdin );
|
|
|
|
cout << "\n";
|
|
|
|
}
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
int keySize = 0;
|
|
|
|
int blockSize = 0;
|
|
|
|
Cipher::CipherAlgorithm alg;
|
|
|
|
Interface nameIOIface;
|
|
|
|
int blockMACBytes = 0;
|
|
|
|
int blockMACRandBytes = 0;
|
|
|
|
bool uniqueIV = false;
|
|
|
|
bool chainedIV = false;
|
|
|
|
bool externalIV = false;
|
2008-08-17 09:26:52 +02:00
|
|
|
bool allowHoles = true;
|
|
|
|
long desiredKDFDuration = NormalKDFDuration;
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
if (reverseEncryption)
|
|
|
|
{
|
|
|
|
uniqueIV = false;
|
|
|
|
chainedIV = false;
|
|
|
|
externalIV = false;
|
|
|
|
blockMACBytes = 0;
|
|
|
|
blockMACRandBytes = 0;
|
|
|
|
}
|
|
|
|
|
2009-12-07 20:15:23 +01:00
|
|
|
if(configMode == Config_Paranoia || answer[0] == 'p')
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
if (reverseEncryption)
|
|
|
|
{
|
|
|
|
rError(_("Paranoia configuration not supported for --reverse"));
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Paranoia configuration selected.") << "\n";
|
|
|
|
// look for AES with 256 bit key..
|
|
|
|
// Use block filename encryption mode.
|
|
|
|
// Enable per-block HMAC headers at substantial performance penalty..
|
|
|
|
// Enable per-file initialization vector headers.
|
|
|
|
// Enable filename initialization vector chaning
|
|
|
|
keySize = 256;
|
|
|
|
blockSize = DefaultBlockSize;
|
|
|
|
alg = findCipherAlgorithm("AES", keySize);
|
|
|
|
nameIOIface = BlockNameIO::CurrentInterface();
|
|
|
|
blockMACBytes = 8;
|
|
|
|
blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary
|
|
|
|
uniqueIV = true;
|
|
|
|
chainedIV = true;
|
|
|
|
externalIV = true;
|
2008-08-17 09:26:52 +02:00
|
|
|
desiredKDFDuration = ParanoiaKDFDuration;
|
2009-12-07 20:15:23 +01:00
|
|
|
} else if(configMode == Config_Standard || answer[0] != 'x')
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Standard configuration selected.") << "\n";
|
2008-04-11 10:37:19 +02:00
|
|
|
// AES w/ 192 bit key, block name encoding, per-file initialization
|
|
|
|
// vectors are all standard.
|
|
|
|
keySize = 192;
|
2008-01-07 09:09:04 +01:00
|
|
|
blockSize = DefaultBlockSize;
|
2008-04-11 10:37:19 +02:00
|
|
|
alg = findCipherAlgorithm("AES", keySize);
|
2008-01-07 09:09:04 +01:00
|
|
|
blockMACBytes = 0;
|
|
|
|
externalIV = false;
|
|
|
|
nameIOIface = BlockNameIO::CurrentInterface();
|
|
|
|
|
|
|
|
if (reverseEncryption)
|
|
|
|
{
|
|
|
|
cout << _("--reverse specified, not using unique/chained IV")
|
|
|
|
<< "\n";
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
uniqueIV = true;
|
|
|
|
chainedIV = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(answer[0] == 'x' || alg.name.empty())
|
|
|
|
{
|
|
|
|
if(answer[0] != 'x')
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Sorry, unable to locate cipher for predefined "
|
|
|
|
"configuration...\n"
|
|
|
|
"Falling through to Manual configuration mode.");
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Manual configuration mode selected.");
|
|
|
|
}
|
|
|
|
cout << endl;
|
|
|
|
|
|
|
|
// query user for settings..
|
|
|
|
alg = selectCipherAlgorithm();
|
|
|
|
keySize = selectKeySize( alg );
|
|
|
|
blockSize = selectBlockSize( alg );
|
|
|
|
nameIOIface = selectNameCoding();
|
|
|
|
if (reverseEncryption)
|
|
|
|
{
|
|
|
|
cout << _("--reverse specified, not using unique/chained IV") << "\n";
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
chainedIV = selectChainedIV();
|
|
|
|
uniqueIV = selectUniqueIV();
|
|
|
|
if(chainedIV && uniqueIV)
|
|
|
|
externalIV = selectExternalChainedIV();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("External chained IV disabled, as both 'IV chaining'\n"
|
|
|
|
"and 'unique IV' features are required for this option.")
|
2008-04-11 10:37:19 +02:00
|
|
|
<< "\n";
|
2008-01-07 09:09:04 +01:00
|
|
|
externalIV = false;
|
|
|
|
}
|
|
|
|
selectBlockMAC(&blockMACBytes, &blockMACRandBytes);
|
2008-04-11 10:37:19 +02:00
|
|
|
allowHoles = selectZeroBlockPassThrough();
|
2008-01-07 09:09:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr<Cipher> cipher = Cipher::New( alg.name, keySize );
|
|
|
|
if(!cipher)
|
|
|
|
{
|
|
|
|
rError(_("Unable to instanciate cipher %s, key size %i, block size %i"),
|
|
|
|
alg.name.c_str(), keySize, blockSize);
|
|
|
|
return rootInfo;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
rDebug("Using cipher %s, key size %i, block size %i",
|
|
|
|
alg.name.c_str(), keySize, blockSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
EncFSConfig config;
|
|
|
|
|
2008-08-17 09:26:52 +02:00
|
|
|
config.cfgType = Config_V6;
|
2008-01-07 09:09:04 +01:00
|
|
|
config.cipherIface = cipher->interface();
|
|
|
|
config.keySize = keySize;
|
|
|
|
config.blockSize = blockSize;
|
|
|
|
config.nameIface = nameIOIface;
|
|
|
|
config.creator = "EncFS " VERSION;
|
2008-04-13 22:35:57 +02:00
|
|
|
config.subVersion = V6SubVersion;
|
2008-01-07 09:09:04 +01:00
|
|
|
config.blockMACBytes = blockMACBytes;
|
|
|
|
config.blockMACRandBytes = blockMACRandBytes;
|
|
|
|
config.uniqueIV = uniqueIV;
|
|
|
|
config.chainedNameIV = chainedIV;
|
|
|
|
config.externalIVChaining = externalIV;
|
2008-04-11 10:37:19 +02:00
|
|
|
config.allowHoles = allowHoles;
|
2008-01-07 09:09:04 +01:00
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
config.salt.clear();
|
2008-08-16 18:39:31 +02:00
|
|
|
config.kdfIterations = 0; // filled in by keying function
|
2008-08-17 09:26:52 +02:00
|
|
|
config.desiredKDFDuration = desiredKDFDuration;
|
2008-08-16 18:39:31 +02:00
|
|
|
|
2008-01-07 09:09:04 +01:00
|
|
|
cout << "\n";
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Configuration finished. The filesystem to be created has\n"
|
|
|
|
"the following properties:") << endl;
|
|
|
|
showFSInfo( config );
|
|
|
|
|
|
|
|
if( config.externalIVChaining )
|
|
|
|
{
|
|
|
|
cout <<
|
|
|
|
_("-------------------------- WARNING --------------------------\n")
|
|
|
|
<<
|
|
|
|
_("The external initialization-vector chaining option has been\n"
|
|
|
|
"enabled. This option disables the use of hard links on the\n"
|
|
|
|
"filesystem. Without hard links, some programs may not work.\n"
|
|
|
|
"The programs 'mutt' and 'procmail' are known to fail. For\n"
|
|
|
|
"more information, please see the encfs mailing list.\n"
|
|
|
|
"If you would like to choose another configuration setting,\n"
|
|
|
|
"please press CTRL-C now to abort and start over.") << endl;
|
|
|
|
cout << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// xgroup(setup)
|
|
|
|
cout << _("Now you will need to enter a password for your filesystem.\n"
|
|
|
|
"You will need to remember this password, as there is absolutely\n"
|
|
|
|
"no recovery mechanism. However, the password can be changed\n"
|
|
|
|
"later using encfsctl.\n\n");
|
|
|
|
|
|
|
|
int encodedKeySize = cipher->encodedKeySize();
|
|
|
|
unsigned char *encodedKey = new unsigned char[ encodedKeySize ];
|
|
|
|
|
|
|
|
CipherKey volumeKey = cipher->newRandomKey();
|
|
|
|
|
|
|
|
// get user key and use it to encode volume key
|
|
|
|
CipherKey userKey;
|
|
|
|
rDebug( "useStdin: %i", useStdin );
|
|
|
|
if(useStdin)
|
2008-08-17 09:26:52 +02:00
|
|
|
userKey = config.getUserKey( useStdin );
|
|
|
|
else if(!passwordProgram.empty())
|
|
|
|
userKey = config.getUserKey( passwordProgram, rootDir );
|
2008-01-07 09:09:04 +01:00
|
|
|
else
|
2008-08-17 09:26:52 +02:00
|
|
|
userKey = config.getNewUserKey();
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
cipher->writeKey( volumeKey, encodedKey, userKey );
|
|
|
|
userKey.reset();
|
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
config.assignKeyData(encodedKey, encodedKeySize);
|
2008-01-07 09:09:04 +01:00
|
|
|
delete[] encodedKey;
|
|
|
|
|
|
|
|
if(!volumeKey)
|
|
|
|
{
|
|
|
|
rWarning(_("Failure generating new volume key! "
|
|
|
|
"Please report this error."));
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
2008-04-13 22:35:57 +02:00
|
|
|
if(!saveConfig( Config_V6, rootDir, &config ))
|
2008-01-07 09:09:04 +01:00
|
|
|
return rootInfo;
|
|
|
|
|
|
|
|
// fill in config struct
|
|
|
|
shared_ptr<NameIO> nameCoder = NameIO::New( config.nameIface,
|
|
|
|
cipher, volumeKey );
|
|
|
|
if(!nameCoder)
|
|
|
|
{
|
|
|
|
rWarning(_("Name coding interface not supported"));
|
|
|
|
cout << _("The filename encoding interface requested is not available")
|
|
|
|
<< endl;
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
nameCoder->setChainedNameIV( config.chainedNameIV );
|
|
|
|
nameCoder->setReverseEncryption( reverseEncryption );
|
|
|
|
|
|
|
|
shared_ptr<DirNode::Config> dirNodeConfig (new DirNode::Config);
|
|
|
|
dirNodeConfig->cipher = cipher;
|
|
|
|
dirNodeConfig->key = volumeKey;
|
|
|
|
dirNodeConfig->nameCoding = nameCoder;
|
|
|
|
dirNodeConfig->fsSubVersion = config.subVersion;
|
|
|
|
dirNodeConfig->blockSize = blockSize;
|
|
|
|
dirNodeConfig->inactivityTimer = enableIdleTracking;
|
|
|
|
dirNodeConfig->blockMACBytes = config.blockMACBytes;
|
|
|
|
dirNodeConfig->blockMACRandBytes = config.blockMACRandBytes;
|
|
|
|
dirNodeConfig->uniqueIV = config.uniqueIV;
|
|
|
|
dirNodeConfig->externalIVChaining = config.externalIVChaining;
|
|
|
|
dirNodeConfig->forceDecode = forceDecode;
|
|
|
|
dirNodeConfig->reverseEncryption = reverseEncryption;
|
2008-04-11 10:37:19 +02:00
|
|
|
dirNodeConfig->allowHoles = config.allowHoles;
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
rootInfo = RootPtr( new EncFS_Root );
|
|
|
|
rootInfo->cipher = cipher;
|
|
|
|
rootInfo->volumeKey = volumeKey;
|
|
|
|
rootInfo->root = shared_ptr<DirNode>(
|
|
|
|
new DirNode( ctx, rootDir, dirNodeConfig ));
|
|
|
|
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void showFSInfo( const EncFSConfig &config )
|
|
|
|
{
|
|
|
|
shared_ptr<Cipher> cipher = Cipher::New( config.cipherIface, -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());
|
|
|
|
// 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() )
|
|
|
|
{
|
|
|
|
Interface iface = cipher->interface();
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << autosprintf(_(" (using %i:%i:%i)\n"),
|
|
|
|
iface.current(), iface.revision(), iface.age());
|
|
|
|
} else
|
|
|
|
cout << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// 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());
|
|
|
|
|
|
|
|
// check if we support the filename encoding interface..
|
|
|
|
shared_ptr<NameIO> nameCoder = NameIO::New( config.nameIface,
|
|
|
|
cipher, CipherKey() );
|
|
|
|
if(!nameCoder)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _(" (NOT supported)\n");
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// if we're using a newer interface, show the version number
|
|
|
|
if( config.nameIface != nameCoder->interface() )
|
|
|
|
{
|
|
|
|
Interface iface = nameCoder->interface();
|
|
|
|
cout << autosprintf(_(" (using %i:%i:%i)\n"),
|
|
|
|
iface.current(), iface.revision(), iface.age());
|
|
|
|
} else
|
|
|
|
cout << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
cout << autosprintf(_("Key Size: %i bits"), config.keySize);
|
2008-08-17 09:26:52 +02:00
|
|
|
cipher = config.getCipher();
|
2008-01-07 09:09:04 +01:00
|
|
|
if(!cipher)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _(" (NOT supported)\n");
|
|
|
|
} else
|
|
|
|
cout << "\n";
|
|
|
|
}
|
2008-08-23 08:33:24 +02:00
|
|
|
if(config.kdfIterations > 0 && config.salt.size() > 0)
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
cout << autosprintf(_("Using PBKDF2, with %i iterations"),
|
|
|
|
config.kdfIterations) << "\n";
|
2008-08-23 08:33:24 +02:00
|
|
|
cout << autosprintf(_("Salt Size: %i bits"),
|
|
|
|
8*(int)config.salt.size()) << "\n";
|
2008-08-17 09:26:52 +02:00
|
|
|
}
|
2008-01-07 09:09:04 +01:00
|
|
|
if(config.blockMACBytes)
|
|
|
|
{
|
|
|
|
if(config.subVersion < 20040813)
|
|
|
|
{
|
|
|
|
cout << autosprintf(
|
|
|
|
// xgroup(diag)
|
|
|
|
_("Block Size: %i bytes + %i byte MAC header"),
|
|
|
|
config.blockSize,
|
|
|
|
config.blockMACBytes + config.blockMACRandBytes) << 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;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << autosprintf(_("Block Size: %i bytes"), config.blockSize);
|
|
|
|
cout << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if(config.uniqueIV)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("Each file contains 8 byte header with unique IV data.\n");
|
|
|
|
}
|
|
|
|
if(config.chainedNameIV)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("Filenames encoded using IV chaining mode.\n");
|
|
|
|
}
|
|
|
|
if(config.externalIVChaining)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("File data IV is chained to filename IV.\n");
|
|
|
|
}
|
2008-04-11 10:37:19 +02:00
|
|
|
if(config.allowHoles)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("File holes passed through to ciphertext.\n");
|
|
|
|
}
|
2008-01-07 09:09:04 +01:00
|
|
|
cout << "\n";
|
|
|
|
}
|
2008-08-17 09:26:52 +02:00
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
shared_ptr<Cipher> EncFSConfig::getCipher() const
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
return Cipher::New( cipherIface, keySize );
|
|
|
|
}
|
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
void EncFSConfig::assignKeyData(const std::string &in)
|
|
|
|
{
|
|
|
|
keyData.resize(in.length());
|
|
|
|
for(unsigned int i=0; i<in.length(); ++i)
|
|
|
|
keyData[i] = in.data()[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void EncFSConfig::assignKeyData(unsigned char *data, int len)
|
|
|
|
{
|
|
|
|
keyData.resize(len);
|
|
|
|
for(int i=0; i<len; ++i)
|
|
|
|
keyData[i] = data[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void EncFSConfig::assignSaltData(unsigned char *data, int len)
|
|
|
|
{
|
|
|
|
salt.resize(len);
|
|
|
|
for(int i=0; i<len; ++i)
|
|
|
|
salt[i] = data[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *EncFSConfig::getKeyData() const
|
|
|
|
{
|
|
|
|
return const_cast<unsigned char *>(&keyData.front());
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *EncFSConfig::getSaltData() const
|
|
|
|
{
|
|
|
|
return const_cast<unsigned char *>(&salt.front());
|
|
|
|
}
|
|
|
|
|
2008-08-17 09:26:52 +02:00
|
|
|
CipherKey EncFSConfig::makeKey(const char *password, int passwdLen)
|
|
|
|
{
|
|
|
|
CipherKey userKey;
|
|
|
|
shared_ptr<Cipher> cipher = getCipher();
|
|
|
|
|
|
|
|
// if no salt is set and we're creating a new password for a new
|
|
|
|
// FS type, then initialize salt..
|
2008-08-23 08:33:24 +02:00
|
|
|
if(salt.size() == 0 && kdfIterations == 0 && cfgType >= Config_V6)
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
// upgrade to using salt
|
2008-08-23 08:33:24 +02:00
|
|
|
salt.resize(20);
|
2008-08-17 09:26:52 +02:00
|
|
|
}
|
|
|
|
|
2008-08-23 08:33:24 +02:00
|
|
|
if(salt.size() > 0)
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
// if iterations isn't known, then we're creating a new key, so
|
|
|
|
// randomize the salt..
|
2008-08-23 08:33:24 +02:00
|
|
|
if(kdfIterations == 0 && !cipher->randomize(
|
|
|
|
getSaltData(), salt.size(), true))
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
cout << _("Error creating salt\n");
|
|
|
|
return userKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
userKey = cipher->newKey( password, passwdLen,
|
|
|
|
kdfIterations, desiredKDFDuration,
|
2008-08-23 08:33:24 +02:00
|
|
|
getSaltData(), salt.size());
|
2008-08-17 09:26:52 +02:00
|
|
|
} else
|
|
|
|
{
|
|
|
|
userKey = cipher->newKey( password, passwdLen );
|
|
|
|
}
|
|
|
|
|
|
|
|
return userKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
CipherKey EncFSConfig::getUserKey(bool useStdin)
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
char passBuf[MaxPassBuf];
|
|
|
|
char *res;
|
|
|
|
|
|
|
|
if( useStdin )
|
|
|
|
{
|
|
|
|
res = fgets( passBuf, sizeof(passBuf), stdin );
|
2008-01-13 00:13:49 +01:00
|
|
|
// Kill the trailing newline.
|
|
|
|
if(passBuf[ strlen(passBuf)-1 ] == '\n')
|
|
|
|
passBuf[ strlen(passBuf)-1 ] = '\0';
|
2008-01-07 09:09:04 +01:00
|
|
|
} 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
|
2008-08-17 09:26:52 +02:00
|
|
|
userKey = makeKey(passBuf, strlen(passBuf));
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
memset( passBuf, 0, sizeof(passBuf) );
|
|
|
|
|
|
|
|
return userKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string readPassword( int FD )
|
|
|
|
{
|
2008-01-13 00:13:49 +01:00
|
|
|
char buffer[1024];
|
2008-01-07 09:09:04 +01:00
|
|
|
string result;
|
|
|
|
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
ssize_t rdSize = recv(FD, buffer, sizeof(buffer), 0);
|
|
|
|
|
|
|
|
if(rdSize > 0)
|
|
|
|
{
|
|
|
|
result.append( buffer, rdSize );
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// chop off trailing "\n" if present..
|
|
|
|
// This is done so that we can use standard programs like ssh-askpass
|
|
|
|
// without modification, as it returns trailing newline..
|
|
|
|
if(!result.empty() && result[ result.length()-1 ] == '\n' )
|
|
|
|
result.resize( result.length() -1 );
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-08-17 09:26:52 +02:00
|
|
|
CipherKey EncFSConfig::getUserKey( const std::string &passProg,
|
|
|
|
const std::string &rootDir )
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]);
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if(pid == -1)
|
|
|
|
{
|
|
|
|
perror(_("Internal error: fork() failed"));
|
|
|
|
close(fds[0]);
|
|
|
|
close(fds[1]);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pid == 0)
|
|
|
|
{
|
|
|
|
const char *argv[4];
|
|
|
|
argv[0] = "/bin/sh";
|
|
|
|
argv[1] = "-c";
|
|
|
|
argv[2] = passProg.c_str();
|
|
|
|
argv[3] = 0;
|
|
|
|
|
|
|
|
// child process.. run the command and send output to fds[0]
|
|
|
|
close(fds[1]); // we don't use the other half..
|
|
|
|
|
|
|
|
// make a copy of stdout and stderr descriptors, and set an environment
|
|
|
|
// variable telling where to find them, in case a child wants it..
|
|
|
|
int stdOutCopy = dup( STDOUT_FILENO );
|
|
|
|
int stdErrCopy = dup( STDERR_FILENO );
|
|
|
|
// replace STDOUT with our socket, which we'll used to receive the
|
|
|
|
// password..
|
|
|
|
dup2( fds[0], STDOUT_FILENO );
|
|
|
|
|
|
|
|
// ensure that STDOUT_FILENO and stdout/stderr are not closed on exec..
|
|
|
|
fcntl(STDOUT_FILENO, F_SETFD, 0); // don't close on exec..
|
|
|
|
fcntl(stdOutCopy, F_SETFD, 0);
|
|
|
|
fcntl(stdErrCopy, F_SETFD, 0);
|
|
|
|
|
|
|
|
char tmpBuf[8];
|
|
|
|
|
|
|
|
setenv(ENCFS_ENV_ROOTDIR, rootDir.c_str(), 1);
|
|
|
|
|
|
|
|
snprintf(tmpBuf, sizeof(tmpBuf)-1, "%i", stdOutCopy);
|
|
|
|
setenv(ENCFS_ENV_STDOUT, tmpBuf, 1);
|
|
|
|
|
|
|
|
snprintf(tmpBuf, sizeof(tmpBuf)-1, "%i", stdErrCopy);
|
|
|
|
setenv(ENCFS_ENV_STDERR, tmpBuf, 1);
|
|
|
|
|
|
|
|
execvp( argv[0], (char * const *)argv ); // returns only on error..
|
|
|
|
|
|
|
|
perror(_("Internal error: failed to exec program"));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fds[0]);
|
|
|
|
string password = readPassword(fds[1]);
|
|
|
|
close(fds[1]);
|
|
|
|
|
|
|
|
waitpid(pid, NULL, 0);
|
|
|
|
|
|
|
|
// convert to key..
|
2008-08-17 09:26:52 +02:00
|
|
|
result = makeKey(password.c_str(), password.length());
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
// clear buffer..
|
|
|
|
password.assign( password.length(), '\0' );
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-08-17 09:26:52 +02:00
|
|
|
CipherKey EncFSConfig::getNewUserKey()
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
CipherKey userKey;
|
|
|
|
char passBuf[MaxPassBuf];
|
|
|
|
char passBuf2[MaxPassBuf];
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// xgroup(common)
|
|
|
|
char *res1 = readpassphrase(_("New Encfs Password: "), passBuf,
|
|
|
|
sizeof(passBuf)-1, RPP_ECHO_OFF);
|
|
|
|
// xgroup(common)
|
|
|
|
char *res2 = readpassphrase(_("Verify Encfs Password: "), passBuf2,
|
|
|
|
sizeof(passBuf2)-1, RPP_ECHO_OFF);
|
|
|
|
|
|
|
|
if(res1 && res2 && !strcmp(passBuf, passBuf2))
|
2008-08-17 09:26:52 +02:00
|
|
|
{
|
|
|
|
userKey = makeKey(passBuf, strlen(passBuf));
|
|
|
|
} else
|
2008-01-07 09:09:04 +01:00
|
|
|
{
|
|
|
|
// xgroup(common) -- probably not common, but group with the others
|
|
|
|
cerr << _("Passwords did not match, please try again\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
memset( passBuf, 0, sizeof(passBuf) );
|
|
|
|
memset( passBuf2, 0, sizeof(passBuf2) );
|
|
|
|
} while( !userKey );
|
|
|
|
|
|
|
|
return userKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
|
|
|
|
{
|
|
|
|
RootPtr rootInfo;
|
|
|
|
EncFSConfig config;
|
|
|
|
|
|
|
|
if(readConfig( opts->rootDir, &config ) != Config_None)
|
|
|
|
{
|
|
|
|
if(opts->reverseEncryption)
|
|
|
|
{
|
|
|
|
if (config.blockMACBytes != 0 || config.blockMACRandBytes != 0
|
|
|
|
|| config.uniqueIV == true || config.externalIVChaining == true
|
|
|
|
|| config.chainedNameIV == true )
|
|
|
|
{
|
|
|
|
cout << _("The configuration loaded is not compatible with --reverse\n");
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-07 20:15:23 +01:00
|
|
|
// first, instanciate the cipher.
|
2008-08-17 09:26:52 +02:00
|
|
|
shared_ptr<Cipher> cipher = config.getCipher();
|
2008-01-07 09:09:04 +01:00
|
|
|
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());
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("The requested cipher interface is not available\n");
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get user key
|
|
|
|
CipherKey userKey;
|
2008-08-17 09:26:52 +02:00
|
|
|
|
|
|
|
if(opts->passwordProgram.empty())
|
|
|
|
{
|
|
|
|
rDebug( "useStdin: %i", opts->useStdin );
|
|
|
|
userKey = config.getUserKey( opts->useStdin );
|
|
|
|
} else
|
|
|
|
userKey = config.getUserKey( opts->passwordProgram, opts->rootDir );
|
2008-01-07 09:09:04 +01:00
|
|
|
|
|
|
|
if(!userKey)
|
|
|
|
return rootInfo;
|
|
|
|
|
|
|
|
rDebug("cipher key size = %i", cipher->encodedKeySize());
|
|
|
|
// decode volume key..
|
2008-08-23 08:33:24 +02:00
|
|
|
CipherKey volumeKey = cipher->readKey(
|
|
|
|
config.getKeyData(), userKey, opts->checkKey);
|
2008-01-07 09:09:04 +01:00
|
|
|
userKey.reset();
|
|
|
|
|
|
|
|
if(!volumeKey)
|
|
|
|
{
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("Error decoding volume key, password incorrect\n");
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr<NameIO> nameCoder = NameIO::New( config.nameIface,
|
|
|
|
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());
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("The requested filename coding interface is "
|
|
|
|
"not available\n");
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
nameCoder->setChainedNameIV( config.chainedNameIV );
|
|
|
|
nameCoder->setReverseEncryption( opts->reverseEncryption );
|
|
|
|
|
|
|
|
shared_ptr<DirNode::Config> dirNodeConfig (new DirNode::Config);
|
|
|
|
dirNodeConfig->cipher = cipher;
|
|
|
|
dirNodeConfig->key = volumeKey;
|
|
|
|
dirNodeConfig->nameCoding = nameCoder;
|
|
|
|
dirNodeConfig->fsSubVersion = config.subVersion;
|
|
|
|
dirNodeConfig->blockSize = config.blockSize;
|
|
|
|
dirNodeConfig->inactivityTimer = opts->idleTracking;
|
|
|
|
dirNodeConfig->blockMACBytes = config.blockMACBytes;
|
|
|
|
dirNodeConfig->blockMACRandBytes = config.blockMACRandBytes;
|
|
|
|
dirNodeConfig->uniqueIV = config.uniqueIV;
|
|
|
|
dirNodeConfig->externalIVChaining = config.externalIVChaining;
|
|
|
|
dirNodeConfig->forceDecode = opts->forceDecode;
|
|
|
|
dirNodeConfig->reverseEncryption = opts->reverseEncryption;
|
|
|
|
|
|
|
|
rootInfo = RootPtr( new EncFS_Root );
|
|
|
|
rootInfo->cipher = cipher;
|
|
|
|
rootInfo->volumeKey = volumeKey;
|
|
|
|
rootInfo->root = shared_ptr<DirNode>(
|
|
|
|
new DirNode( ctx, opts->rootDir, dirNodeConfig ));
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
if(opts->createIfNotFound)
|
|
|
|
{
|
|
|
|
// creating a new encrypted filesystem
|
2008-04-13 22:35:57 +02:00
|
|
|
rootInfo = createV6Config( ctx, opts->rootDir, opts->idleTracking,
|
2008-01-07 09:09:04 +01:00
|
|
|
opts->forceDecode, opts->passwordProgram, opts->useStdin,
|
2009-12-07 20:15:23 +01:00
|
|
|
opts->reverseEncryption, opts->configMode );
|
2008-01-07 09:09:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rootInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
int remountFS(EncFS_Context *ctx)
|
|
|
|
{
|
|
|
|
rDebug("Attempting to reinitialize filesystem");
|
|
|
|
|
|
|
|
RootPtr rootInfo = initFS( ctx, ctx->opts );
|
|
|
|
if(rootInfo)
|
|
|
|
{
|
|
|
|
ctx->setRoot(rootInfo->root);
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
rInfo(_("Remount failed"));
|
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|