mirror of
https://github.com/vgough/encfs.git
synced 2024-11-29 03:15:07 +01:00
b59d806e4b
git-svn-id: http://encfs.googlecode.com/svn/trunk@111 db9cf616-1c43-0410-9cb8-a902689de0d6
907 lines
22 KiB
C++
907 lines
22 KiB
C++
/*****************************************************************************
|
|
* 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.
|
|
*/
|
|
|
|
|
|
#include "fs/encfs.h"
|
|
|
|
#include "base/autosprintf.h"
|
|
#include "base/config.h"
|
|
#include "base/Error.h"
|
|
#include "base/i18n.h"
|
|
|
|
#include "cipher/CipherV1.h"
|
|
#include "cipher/BlockCipher.h"
|
|
#include "cipher/MAC.h"
|
|
#include "cipher/StreamCipher.h"
|
|
|
|
#include "fs/FileUtils.h"
|
|
#include "fs/Context.h"
|
|
#include "fs/FileNode.h"
|
|
#include "fs/DirNode.h"
|
|
|
|
#include <glog/logging.h>
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <cstdio>
|
|
#include <list>
|
|
|
|
#include <getopt.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
using namespace encfs;
|
|
using gnu::autosprintf;
|
|
using std::cerr;
|
|
using std::cin;
|
|
using std::cout;
|
|
using std::endl;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
static int showInfo( int argc, char **argv );
|
|
static int showVersion( int argc, char **argv );
|
|
static int showCiphers( int argc, char **argv );
|
|
static int chpasswd( int argc, char **argv );
|
|
static int chpasswdAutomaticly( int argc, char **argv );
|
|
static int cmd_ls( int argc, char **argv );
|
|
static int cmd_decode( int argc, char **argv );
|
|
static int cmd_encode( int argc, char **argv );
|
|
static int cmd_showcruft( int argc, char **argv );
|
|
static int cmd_cat( int argc, char **argv );
|
|
static int cmd_export( int argc, char **argv );
|
|
static int cmd_showKey( int argc, char **argv );
|
|
|
|
struct CommandOpts
|
|
{
|
|
const char *name;
|
|
int minOptions;
|
|
int maxOptions;
|
|
int (*func)(int argc, char **argv);
|
|
const char *argStr;
|
|
const char *usageStr;
|
|
} commands[] =
|
|
{
|
|
{"info", 1, 1, showInfo, "(root dir)",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- show information (Default command)")},
|
|
{"showKey", 1, 1, cmd_showKey, "(root dir)",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- show key")},
|
|
{"passwd", 1, 1, chpasswd, "(root dir)",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- change password for volume")},
|
|
{"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- change password for volume, taking password"
|
|
" from standard input.\n\tNo prompts are issued.")},
|
|
{"ls", 1, 2, cmd_ls, 0,0},
|
|
{"showcruft", 1, 1, cmd_showcruft, "(root dir)",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- show undecodable filenames in the volume")},
|
|
{"cat", 2, 2, cmd_cat, "(root dir) path",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- decodes the file and cats it to standard out")},
|
|
{"decode", 1, 100, cmd_decode, "[--extpass=prog] (root dir) [encoded-name ...]",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- decodes name and prints plaintext version")},
|
|
{"encode", 1, 100, cmd_encode, "[--extpass=prog] (root dir) [plaintext-name ...]",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- encodes a filename and print result")},
|
|
{"export", 2, 2, cmd_export, "(root dir) path",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- decrypts a volume and writes results to path")},
|
|
{"--ciphers", 0, 0, showCiphers, "",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- show available ciphers")},
|
|
{"--version", 0, 0, showVersion, "",
|
|
// xgroup(usage)
|
|
gettext_noop(" -- print version number and exit")},
|
|
{0,0,0,0,0,0}
|
|
};
|
|
|
|
|
|
static
|
|
void usage(const char *name)
|
|
{
|
|
cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"
|
|
<< _("Usage:\n")
|
|
// displays usage commands, eg "./encfs (root dir) ..."
|
|
// xgroup(usage)
|
|
<< autosprintf(_("%s (root dir)\n"
|
|
" -- displays information about the filesystem, or \n"), name);
|
|
|
|
int offset = 0;
|
|
while(commands[offset].name != 0)
|
|
{
|
|
if( commands[offset].argStr != 0 )
|
|
{
|
|
cerr << "encfsctl " << commands[offset].name << " "
|
|
<< commands[offset].argStr << "\n"
|
|
<< gettext( commands[offset].usageStr ) << "\n";
|
|
}
|
|
++offset;
|
|
}
|
|
|
|
cerr << "\n"
|
|
// xgroup(usage)
|
|
<< autosprintf(_("Example: \n%s info ~/.crypt\n"), name)
|
|
<< "\n";
|
|
}
|
|
|
|
static bool checkDir( string &rootDir )
|
|
{
|
|
if( !isDirectory( rootDir.c_str() ))
|
|
{
|
|
cout << autosprintf(_("directory %s does not exist.\n"),
|
|
rootDir.c_str());
|
|
return false;
|
|
}
|
|
if(rootDir[ rootDir.length()-1 ] != '/')
|
|
rootDir.append("/");
|
|
|
|
return true;
|
|
}
|
|
|
|
static int showVersion( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
// xgroup(usage)
|
|
cout << autosprintf(_("encfsctl version %s"), VERSION) << "\n";
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int showCiphers( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
cout << _("Block modes:\n");
|
|
for (const string& name : BlockCipher::GetRegistry().GetAll()) {
|
|
auto props = BlockCipher::GetRegistry().GetProperties(name.c_str());
|
|
cout << _("Implementation: ") << name << "\n";
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
cout << "\t" << _("Block cipher: ") << props->cipher << " / "
|
|
<< props->mode << "\n";
|
|
cout << "\t" << _("Key Sizes: ") << props->keySize << "\n";
|
|
}
|
|
cout << "\n";
|
|
|
|
cout << _("Stream modes:\n");
|
|
for (const string& name : StreamCipher::GetRegistry().GetAll()) {
|
|
auto props = StreamCipher::GetRegistry().GetProperties(name.c_str());
|
|
cout << _("Implementation: ") << name << "\n";
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
cout << "\t" << _("Stream cipher: ") << props->cipher << " / "
|
|
<< props->mode << "\n";
|
|
cout << "\t" << _("Key Sizes: ") << props->keySize << "\n";
|
|
}
|
|
cout << "\n";
|
|
|
|
cout << _("MAC modes:\n");
|
|
for (const string& name : MAC::GetRegistry().GetAll()) {
|
|
auto props = MAC::GetRegistry().GetProperties(name.c_str());
|
|
cout << _("Implementation: ") << name << "\n";
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
cout << "\t" << _("Hash mode: ") << props->hashFunction << " / "
|
|
<< props->mode << "\n";
|
|
cout << "\t" << _("Block size: ") << props->blockSize << "\n";
|
|
}
|
|
cout << "\n";
|
|
|
|
cout << _("PBKDF modes:\n");
|
|
for (const string& name : PBKDF::GetRegistry().GetAll()) {
|
|
auto props = PBKDF::GetRegistry().GetProperties(name.c_str());
|
|
cout << _("Implementation: ") << name << "\n";
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
cout << "\t" << _("Mode: ") << props->mode << "\n";
|
|
}
|
|
cout << "\n";
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int showInfo( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
string rootDir = argv[1];
|
|
if( !checkDir( rootDir ))
|
|
return EXIT_FAILURE;
|
|
|
|
EncfsConfig config;
|
|
ConfigType type = readConfig( rootDir, config );
|
|
|
|
// show information stored in config..
|
|
switch(type)
|
|
{
|
|
case Config_None:
|
|
// xgroup(diag)
|
|
cout << _("Unable to load or parse config file\n");
|
|
return EXIT_FAILURE;
|
|
case Config_Prehistoric:
|
|
// xgroup(diag)
|
|
cout << _("A really old EncFS filesystem was found. \n"
|
|
"It is not supported in this EncFS build.\n");
|
|
return EXIT_FAILURE;
|
|
case Config_V3:
|
|
// xgroup(diag)
|
|
cout << "\n" << autosprintf(_("Version 3 configuration; "
|
|
"created by %s\n"), config.creator().c_str());
|
|
break;
|
|
case Config_V4:
|
|
// xgroup(diag)
|
|
cout << "\n" << autosprintf(_("Version 4 configuration; "
|
|
"created by %s\n"), config.creator().c_str());
|
|
break;
|
|
case Config_V5:
|
|
case Config_V6:
|
|
case Config_V7:
|
|
// xgroup(diag)
|
|
cout << "\n" << autosprintf(_("Version %i configuration; "
|
|
"created by %s (revision %i)\n"),
|
|
type,
|
|
config.creator().c_str(),
|
|
config.revision());
|
|
break;
|
|
}
|
|
|
|
showFSInfo( config );
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static RootPtr initRootInfo(int &argc, char ** &argv)
|
|
{
|
|
RootPtr result;
|
|
shared_ptr<EncFS_Opts> opts( new EncFS_Opts() );
|
|
opts->createIfNotFound = false;
|
|
opts->checkKey = false;
|
|
|
|
static struct option long_options[] = {
|
|
{"extpass", 1, 0, 'p'},
|
|
{0,0,0,0}
|
|
};
|
|
|
|
for(;;)
|
|
{
|
|
int option_index = 0;
|
|
|
|
int res = getopt_long( argc, argv, "",
|
|
long_options, &option_index);
|
|
if(res == -1)
|
|
break;
|
|
|
|
switch(res)
|
|
{
|
|
case 'p':
|
|
opts->passwordProgram.assign(optarg);
|
|
break;
|
|
default:
|
|
LOG(WARNING) << "getopt error: " << res;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if(argc == 0)
|
|
{
|
|
cerr << _("Incorrect number of arguments") << "\n";
|
|
} else
|
|
{
|
|
opts->rootDir = string( argv[0] );
|
|
|
|
--argc;
|
|
++argv;
|
|
|
|
if(checkDir( opts->rootDir ))
|
|
result = initFS( NULL, opts );
|
|
|
|
if(!result)
|
|
cerr << _("Unable to initialize encrypted filesystem - check path.\n");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static RootPtr initRootInfo(const char* crootDir)
|
|
{
|
|
string rootDir(crootDir);
|
|
RootPtr result;
|
|
|
|
if(checkDir( rootDir ))
|
|
{
|
|
shared_ptr<EncFS_Opts> opts( new EncFS_Opts() );
|
|
opts->rootDir = rootDir;
|
|
opts->createIfNotFound = false;
|
|
opts->checkKey = false;
|
|
result = initFS( NULL, opts );
|
|
}
|
|
|
|
if(!result)
|
|
cerr << _("Unable to initialize encrypted filesystem - check path.\n");
|
|
|
|
return result;
|
|
}
|
|
|
|
static int cmd_showKey( int argc, char **argv )
|
|
{
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
else
|
|
{
|
|
// encode with itself
|
|
string b64Key = rootInfo->cipher->encodeAsString( rootInfo->volumeKey );
|
|
|
|
cout << b64Key << "\n";
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
static int cmd_decode( int argc, char **argv )
|
|
{
|
|
RootPtr rootInfo = initRootInfo(argc, argv);
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
|
|
if(argc > 0)
|
|
{
|
|
for(int i=0; i<argc; ++i)
|
|
{
|
|
string name = rootInfo->root->plainPath( argv[i] );
|
|
cout << name << "\n";
|
|
}
|
|
} else
|
|
{
|
|
char buf[PATH_MAX+1];
|
|
while(cin.getline(buf,PATH_MAX))
|
|
{
|
|
cout << rootInfo->root->plainPath( buf ) << "\n";
|
|
}
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int cmd_encode( int argc, char **argv )
|
|
{
|
|
RootPtr rootInfo = initRootInfo(argc, argv);
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
|
|
if(argc > 0)
|
|
{
|
|
for(int i=0; i<argc; ++i)
|
|
{
|
|
string name = rootInfo->root->cipherPathWithoutRoot(argv[i]);
|
|
cout << name << "\n";
|
|
}
|
|
} else
|
|
{
|
|
char buf[PATH_MAX+1];
|
|
while(cin.getline(buf,PATH_MAX))
|
|
{
|
|
cout << rootInfo->root->cipherPathWithoutRoot( buf ) << "\n";
|
|
}
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int cmd_ls( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
|
|
// show files in directory
|
|
{
|
|
DirTraverse dt = rootInfo->root->openDir("/");
|
|
if(dt.valid())
|
|
{
|
|
for(string name = dt.nextPlaintextName(); !name.empty();
|
|
name = dt.nextPlaintextName())
|
|
{
|
|
shared_ptr<FileNode> fnode =
|
|
rootInfo->root->lookupNode( name.c_str(), "encfsctl-ls" );
|
|
struct stat stbuf;
|
|
fnode->getAttr( &stbuf );
|
|
|
|
struct tm stm;
|
|
localtime_r( &stbuf.st_mtime, &stm );
|
|
stm.tm_year += 1900;
|
|
// TODO: when I add "%s" to the end and name.c_str(), I get a
|
|
// seg fault from within strlen. Why ???
|
|
printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n",
|
|
int(stbuf.st_size),
|
|
int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday),
|
|
int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec),
|
|
name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
// apply an operation to every block in the file
|
|
template<typename T>
|
|
int processContents( const shared_ptr<EncFS_Root> &rootInfo,
|
|
const char *path, T &op )
|
|
{
|
|
int errCode = 0;
|
|
shared_ptr<FileNode> node = rootInfo->root->openNode( path, "encfsctl",
|
|
O_RDONLY, &errCode );
|
|
|
|
if(!node)
|
|
{
|
|
// try treating filename as an enciphered path
|
|
string plainName = rootInfo->root->plainPath( path );
|
|
node = rootInfo->root->lookupNode( plainName.c_str(), "encfsctl" );
|
|
if(node)
|
|
{
|
|
errCode = node->open( O_RDONLY );
|
|
if(errCode < 0)
|
|
node.reset();
|
|
}
|
|
}
|
|
|
|
if(!node)
|
|
{
|
|
cerr << "unable to open " << path << "\n";
|
|
return errCode;
|
|
} else
|
|
{
|
|
unsigned char buf[512];
|
|
int blocks = (node->getSize() + sizeof(buf)-1) / sizeof(buf);
|
|
// read all the data in blocks
|
|
for(int i=0; i<blocks; ++i)
|
|
{
|
|
int bytes = node->read(i*sizeof(buf), buf, sizeof(buf));
|
|
int res = op(buf, bytes);
|
|
if(res < 0)
|
|
return res;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
class WriteOutput
|
|
{
|
|
int _fd;
|
|
public:
|
|
WriteOutput(int fd) { _fd = fd; }
|
|
~WriteOutput() { close(_fd); }
|
|
|
|
int operator()(const void *buf, int count)
|
|
{
|
|
return (int)write(_fd, buf, count);
|
|
}
|
|
};
|
|
|
|
static int cmd_cat( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
|
|
const char *path = argv[2];
|
|
WriteOutput output(STDOUT_FILENO);
|
|
int errCode = processContents( rootInfo, path, output );
|
|
|
|
return errCode;
|
|
}
|
|
|
|
static int copyLink(const struct stat &stBuf,
|
|
const shared_ptr<EncFS_Root> &rootInfo,
|
|
const string &cpath, const string &destName )
|
|
{
|
|
vector<char> buf(stBuf.st_size+1, 0);
|
|
int res = ::readlink( cpath.c_str(), &buf[0], stBuf.st_size );
|
|
if(res == -1)
|
|
{
|
|
cerr << "unable to readlink of " << cpath << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
buf[res] = '\0';
|
|
string decodedLink = rootInfo->root->plainPath(&buf[0]);
|
|
|
|
res = ::symlink( decodedLink.c_str(), destName.c_str() );
|
|
if(res == -1)
|
|
{
|
|
cerr << "unable to create symlink for " << cpath
|
|
<< " to " << decodedLink << "\n";
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int copyContents(const shared_ptr<EncFS_Root> &rootInfo,
|
|
const char* encfsName, const char* targetName)
|
|
{
|
|
shared_ptr<FileNode> node =
|
|
rootInfo->root->lookupNode( encfsName, "encfsctl" );
|
|
|
|
if(!node)
|
|
{
|
|
cerr << "unable to open " << encfsName << "\n";
|
|
return EXIT_FAILURE;
|
|
} else
|
|
{
|
|
struct stat st;
|
|
|
|
if(node->getAttr(&st) != 0)
|
|
return EXIT_FAILURE;
|
|
|
|
if((st.st_mode & S_IFLNK) == S_IFLNK)
|
|
{
|
|
string d = rootInfo->root->cipherPath(encfsName);
|
|
char linkContents[PATH_MAX+2];
|
|
|
|
if(readlink (d.c_str(), linkContents, PATH_MAX + 1) <= 0)
|
|
{
|
|
cerr << "unable to read link " << encfsName << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
symlink(rootInfo->root->plainPath(linkContents).c_str(),
|
|
targetName);
|
|
} else
|
|
{
|
|
int outfd = creat(targetName, st.st_mode);
|
|
|
|
WriteOutput output(outfd);
|
|
processContents( rootInfo, encfsName, output );
|
|
}
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static bool endsWith(const string &str, char ch)
|
|
{
|
|
if(str.empty())
|
|
return false;
|
|
else
|
|
return str[str.length()-1] == ch;
|
|
}
|
|
|
|
static int traverseDirs(const shared_ptr<EncFS_Root> &rootInfo,
|
|
string volumeDir, string destDir)
|
|
{
|
|
if(!endsWith(volumeDir, '/'))
|
|
volumeDir.append("/");
|
|
if(!endsWith(destDir, '/'))
|
|
destDir.append("/");
|
|
|
|
// Lookup directory node so we can create a destination directory
|
|
// with the same permissions
|
|
{
|
|
struct stat st;
|
|
shared_ptr<FileNode> dirNode =
|
|
rootInfo->root->lookupNode( volumeDir.c_str(), "encfsctl" );
|
|
if(dirNode->getAttr(&st))
|
|
return EXIT_FAILURE;
|
|
|
|
mkdir(destDir.c_str(), st.st_mode);
|
|
}
|
|
|
|
// show files in directory
|
|
DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str());
|
|
if(dt.valid())
|
|
{
|
|
for(string name = dt.nextPlaintextName(); !name.empty();
|
|
name = dt.nextPlaintextName())
|
|
{
|
|
// Recurse to subdirectories
|
|
if(name != "." && name != "..")
|
|
{
|
|
string plainPath = volumeDir + name;
|
|
string cpath = rootInfo->root->cipherPath(plainPath.c_str());
|
|
string destName = destDir + name;
|
|
|
|
int r = EXIT_SUCCESS;
|
|
struct stat stBuf;
|
|
if( !lstat( cpath.c_str(), &stBuf ))
|
|
{
|
|
if( S_ISDIR( stBuf.st_mode ) )
|
|
{
|
|
traverseDirs(rootInfo, (plainPath + '/').c_str(),
|
|
destName + '/');
|
|
} else if( S_ISLNK( stBuf.st_mode ))
|
|
{
|
|
r = copyLink( stBuf, rootInfo, cpath, destName );
|
|
} else
|
|
{
|
|
r = copyContents(rootInfo, plainPath.c_str(),
|
|
destName.c_str());
|
|
}
|
|
} else
|
|
{
|
|
r = EXIT_FAILURE;
|
|
}
|
|
|
|
if(r != EXIT_SUCCESS)
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int cmd_export( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
|
|
string destDir = argv[2];
|
|
// if the dir doesn't exist, then create it (with user permission)
|
|
if(!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700))
|
|
return EXIT_FAILURE;
|
|
|
|
return traverseDirs(rootInfo, "/", destDir);
|
|
}
|
|
|
|
int showcruft( const shared_ptr<EncFS_Root> &rootInfo, const char *dirName )
|
|
{
|
|
int found = 0;
|
|
DirTraverse dt = rootInfo->root->openDir( dirName );
|
|
if(dt.valid())
|
|
{
|
|
bool showedDir = false;
|
|
for(string name = dt.nextInvalid(); !name.empty();
|
|
name = dt.nextInvalid())
|
|
{
|
|
string cpath = rootInfo->root->cipherPath( dirName );
|
|
cpath += '/';
|
|
cpath += name;
|
|
|
|
if(!showedDir)
|
|
{
|
|
// just before showing a list of files in a directory
|
|
cout << autosprintf(_("In directory %s: \n"), dirName);
|
|
showedDir = true;
|
|
}
|
|
++found;
|
|
cout << cpath << "\n";
|
|
}
|
|
|
|
// now go back and look for directories to recurse into..
|
|
dt = rootInfo->root->openDir( dirName );
|
|
if(dt.valid())
|
|
{
|
|
for(string name = dt.nextPlaintextName(); !name.empty();
|
|
name = dt.nextPlaintextName())
|
|
{
|
|
if( name == "." || name == "..")
|
|
continue;
|
|
|
|
string plainPath = dirName;
|
|
plainPath += '/';
|
|
plainPath += name;
|
|
|
|
string cpath = rootInfo->root->cipherPath( plainPath.c_str() );
|
|
|
|
if(isDirectory( cpath.c_str() ))
|
|
found += showcruft( rootInfo, plainPath.c_str() );
|
|
}
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
/*
|
|
iterate recursively through the filesystem and print out names of files
|
|
which have filenames which cannot be decoded with the given key..
|
|
*/
|
|
static int cmd_showcruft( int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
if(!rootInfo)
|
|
return EXIT_FAILURE;
|
|
|
|
int filesFound = showcruft( rootInfo, "/" );
|
|
|
|
cout << autosprintf("Found %i invalid file(s).", filesFound) << "\n";
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv )
|
|
{
|
|
(void)argc;
|
|
string rootDir = argv[1];
|
|
if( !checkDir( rootDir ))
|
|
return EXIT_FAILURE;
|
|
|
|
EncfsConfig config;
|
|
ConfigType cfgType = readConfig( rootDir, config );
|
|
|
|
if(cfgType == Config_None)
|
|
{
|
|
cout << _("Unable to load or parse config file\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// ask for existing password
|
|
cout << _("Enter current Encfs password\n");
|
|
if (annotate)
|
|
cerr << "$PROMPT$ passwd" << endl;
|
|
CipherKey userKey = getUserKey( config, useStdin );
|
|
if(!userKey.valid())
|
|
return EXIT_FAILURE;
|
|
|
|
// instanciate proper cipher
|
|
shared_ptr<CipherV1> cipher = getCipher(config);
|
|
if(!cipher)
|
|
{
|
|
cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"),
|
|
config.cipher().name().c_str());
|
|
return EXIT_FAILURE;
|
|
}
|
|
cipher->setKey(userKey);
|
|
|
|
// decode volume key using user key -- at this point we detect an incorrect
|
|
// password if the key checksum does not match (causing readKey to fail).
|
|
CipherKey volumeKey = cipher->readKey(
|
|
(const unsigned char *)config.key().ciphertext().data(), true );
|
|
|
|
if(!volumeKey.valid())
|
|
{
|
|
cout << _("Invalid password\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
cipher->setKey(volumeKey);
|
|
|
|
// Now, get New user key..
|
|
userKey.reset();
|
|
cout << _("Enter new Encfs password\n");
|
|
|
|
// create new key
|
|
if( useStdin )
|
|
{
|
|
if (annotate)
|
|
cerr << "$PROMPT$ new_passwd" << endl;
|
|
}
|
|
|
|
userKey = getNewUserKey( config, useStdin, string(), string() );
|
|
|
|
// re-encode the volume key using the new user key and write it out..
|
|
int result = EXIT_FAILURE;
|
|
if(userKey.valid())
|
|
{
|
|
int encodedKeySize = cipher->encodedKeySize();
|
|
unsigned char *keyBuf = new unsigned char[ encodedKeySize ];
|
|
|
|
// encode volume key with new user key
|
|
cipher->setKey(userKey);
|
|
cipher->writeKey( volumeKey, keyBuf );
|
|
userKey.reset();
|
|
cipher->setKey(volumeKey);
|
|
|
|
EncryptedKey *key = config.mutable_key();
|
|
key->set_ciphertext( keyBuf, encodedKeySize );
|
|
delete[] keyBuf;
|
|
|
|
if(saveConfig( rootDir, config ))
|
|
{
|
|
// password modified -- changes volume key of filesystem..
|
|
cout << _("Volume Key successfully updated.\n");
|
|
result = EXIT_SUCCESS;
|
|
} else
|
|
{
|
|
cout << _("Error saving modified config file.\n");
|
|
}
|
|
} else
|
|
{
|
|
cout << _("Error creating key\n");
|
|
}
|
|
|
|
volumeKey.reset();
|
|
|
|
return result;
|
|
}
|
|
|
|
static int chpasswd( int argc, char **argv )
|
|
{
|
|
return do_chpasswd( false, false, argc, argv );
|
|
}
|
|
|
|
static int chpasswdAutomaticly( int argc, char **argv )
|
|
{
|
|
return do_chpasswd( true, false, argc, argv );
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FLAGS_logtostderr = 1;
|
|
FLAGS_minloglevel = 1;
|
|
|
|
google::InitGoogleLogging(argv[0]);
|
|
google::InstallFailureSignalHandler();
|
|
|
|
#ifdef LOCALEDIR
|
|
setlocale( LC_ALL, "" );
|
|
bindtextdomain( PACKAGE, LOCALEDIR );
|
|
textdomain( PACKAGE );
|
|
#endif
|
|
|
|
bool isThreaded = false;
|
|
CipherV1::init(isThreaded);
|
|
|
|
if(argc < 2)
|
|
{
|
|
usage( argv[0] );
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if(argc == 2 && !(*argv[1] == '-' && *(argv[1]+1) == '-'))
|
|
{
|
|
// default command when only 1 argument given -- treat the argument as
|
|
// a directory..
|
|
return showInfo( argc, argv );
|
|
} else
|
|
{
|
|
// find the specified command
|
|
int offset = 0;
|
|
while(commands[offset].name != 0)
|
|
{
|
|
if(!strcmp( argv[1], commands[offset].name ))
|
|
break;
|
|
++offset;
|
|
}
|
|
|
|
if(commands[offset].name == 0)
|
|
{
|
|
cerr << autosprintf(_("invalid command: \"%s\""), argv[1]) << "\n";
|
|
} else
|
|
{
|
|
if((argc-2 < commands[offset].minOptions) ||
|
|
(argc-2 > commands[offset].maxOptions))
|
|
{
|
|
cerr << autosprintf(
|
|
_("Incorrect number of arguments for command \"%s\""),
|
|
argv[1]) << "\n";
|
|
} else
|
|
return (*commands[offset].func)( argc-1, argv+1 );
|
|
}
|
|
}
|
|
|
|
CipherV1::shutdown(isThreaded);
|
|
|
|
return EXIT_FAILURE;
|
|
}
|