Added command to change repo password

Also:
- Slight refactoring (zcollector renamed to backup_collector)
- Fixed typo (s/genarate/generate/g)
master
Vladimir Stackov 2014-12-15 14:13:34 +03:00
parent bb576a1adb
commit a331eb094f
12 changed files with 78 additions and 31 deletions

View File

@ -79,7 +79,7 @@ If you have a 32-bit system and a lot of cores, consider lowering the number of
* While you can pipe any data into the program, the data should be uncompressed and unencrypted -- otherwise no deduplication could be performed on it. `zbackup` would compress and encrypt the data itself, so there's no need to do that yourself. So just run `tar c` and pipe it into `zbackup` directly. If backing up disk images employing encryption, pipe the unencrypted version (the one you normally mount). If you create `.zip` or `.rar` files, use no compression (`-0` or `-m0`) and no encryption.
* Parallel LZMA compression uses a lot of RAM (several hundreds of megabytes, depending on the number of threads used), and ten times more virtual address space. The latter is only relevant on 32-bit architectures where it's limited to 2 or 3 GB. If you hit the ceiling, lower the number of threads with `--threads`.
* Since the data is deduplicated, there's naturally no redundancy in it. A loss of a single file can lead to a loss of virtually all data. Make sure you store it on a redundant storage (RAID1, a cloud provider etc).
* The encryption key, if used, is stored in the `info` file in the root of the repo. It is encrypted with your password. Technically thus you can change your password without re-encrypting any data, and as long as no one possesses the old `info` file and knows your old password, you would be safe (even though the actual option to change password is not implemented yet -- someone who needs this is welcome to create a pull request -- the possibility is all there). Also note that it is crucial you don't lose your `info` file, as otherwise the whole backup would be lost.
* The encryption key, if used, is stored in the `info` file in the root of the repo. It is encrypted with your password. Technically thus you can change your password without re-encrypting any data, and as long as no one possesses the old `info` file and knows your old password, you would be safe (note that ability to change repo type between encrypted and non-encrypted is not implemented yet -- someone who needs this is welcome to create a pull request -- the possibility is all there). Also note that it is crucial you don't lose your `info` file, as otherwise the whole backup would be lost.
# Limitations
@ -166,7 +166,7 @@ won't be able to read those bundles.
There's a lot to be improved in the program. It was released with the minimum amount of functionality to be useful. It is also stable. This should hopefully stimulate people to join the development and add all those other fancy features. Here's a list of ideas:
* Additional options, such as configurable chunk and bundle sizes etc.
* A command to change password.
* Ability to change bundle type (between encrypted and non-encrypted).
* Improved garbage collection. The program should support ability to specify maximum index file size / maximum index file count (for better compatibility with cloud storages as well) or something like retention policy.
* A command to fsck the repo by doing something close to what garbage collection does, but also checking all hashes and so on.
* Parallel decompression. Right now decompression is single-threaded, but it is possible to look ahead in the stream and perform prefetching.

View File

@ -1,7 +1,7 @@
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
#include "zcollector.hh"
#include "backup_collector.hh"
#include <string>
#include <vector>

View File

@ -1,8 +1,8 @@
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
#ifndef Z_COLLECTOR_HH_INCLUDED__
#define Z_COLLECTOR_HH_INCLUDED__
#ifndef BACKUP_COLLECTOR_HH_INCLUDED__
#define BACKUP_COLLECTOR_HH_INCLUDED__
#include "zbackup_base.hh"
#include "chunk_storage.hh"

View File

@ -81,7 +81,7 @@ void Writer::commit()
// Generate a random filename
unsigned char buf[ 24 ]; // Same comments as for Bundle::IdSize
Random::genaratePseudo( buf, sizeof( buf ) );
Random::generatePseudo( buf, sizeof( buf ) );
indexTempFile->moveOverTo( Dir::addPath( indexDir,
toHex( buf, sizeof( buf ) ) ) );
@ -152,7 +152,7 @@ Bundle::Id const & Writer::getCurrentBundleId()
if ( !hasCurrentBundleId )
{
// Generate a new one
Random::genaratePseudo( &currentBundleId, sizeof( currentBundleId ) );
Random::generatePseudo( &currentBundleId, sizeof( currentBundleId ) );
hasCurrentBundleId = true;
}

View File

@ -341,7 +341,7 @@ void OutputStream::writeRandomIv()
if ( key.hasKey() )
{
char iv[ Encryption::IvSize ];
Random::genaratePseudo( iv, sizeof( iv ) );
Random::generatePseudo( iv, sizeof( iv ) );
write( iv, sizeof( iv ) );
}
}

View File

@ -66,12 +66,13 @@ EncryptionKey::~EncryptionKey()
}
void EncryptionKey::generate( string const & password,
EncryptionKeyInfo & info )
EncryptionKeyInfo & info,
EncryptionKey & encryptionkey )
{
// Use this buf for salts
char buf[ 16 ];
char buf[ KeySize ];
Random::genaratePseudo( buf, sizeof( buf ) );
Random::generatePseudo( buf, sizeof( buf ) );
info.set_salt( buf, sizeof( buf ) );
info.set_rounds( 10000 ); // TODO: make this configurable
@ -79,11 +80,13 @@ void EncryptionKey::generate( string const & password,
deriveKey( password, info, derivedKey, sizeof( derivedKey ) );
char key[ KeySize ];
Random::genarateTrue( key, sizeof( key ) );
if ( encryptionkey.hasKey() )
memcpy( key, encryptionkey.getKey(), KeySize );
else
Random::generateTrue( key, sizeof( key ) );
// Fill in the HMAC verification part
Random::genaratePseudo( buf, sizeof( buf ) );
Random::generatePseudo( buf, sizeof( buf ) );
info.set_key_check_input( buf, sizeof( buf ) );
info.set_key_check_hmac( calculateKeyHmac( key, sizeof( key ),
info.key_check_input() ) );

View File

@ -41,7 +41,8 @@ public:
{ return sizeof( key ); }
/// Generates new key info using the given password
static void generate( string const & password, EncryptionKeyInfo & );
static void generate( string const & password, EncryptionKeyInfo &,
EncryptionKey & encryptionkey );
/// Returns a static instance without any key set
static EncryptionKey const & noKey();

View File

@ -5,13 +5,13 @@
#include <openssl/rand.h>
namespace Random {
void genarateTrue( void * buf, unsigned size )
void generateTrue( void * buf, unsigned size )
{
if ( RAND_bytes( (unsigned char *) buf, size ) != 1 )
throw exCantGenerate();
}
void genaratePseudo( void * buf, unsigned size )
void generatePseudo( void * buf, unsigned size )
{
if ( RAND_pseudo_bytes( (unsigned char *) buf, size ) < 0 )
throw exCantGenerate();

View File

@ -12,10 +12,10 @@ namespace Random {
DEF_EX( exCantGenerate, "Error generating random sequence, try later", std::exception )
/// This one fills the buffer with true randomness, suitable for a key
void genarateTrue( void * buf, unsigned size );
void generateTrue( void * buf, unsigned size );
/// This one fills the buffer with pseudo randomness, suitable for salts but not
/// keys
void genaratePseudo( void * buf, unsigned size );
void generatePseudo( void * buf, unsigned size );
}
#endif

View File

@ -27,7 +27,7 @@
#include "zbackup.hh"
#include "index_file.hh"
#include "bundle.hh"
#include "zcollector.hh"
#include "backup_collector.hh"
using std::vector;
using std::bitset;
@ -321,8 +321,8 @@ void ZExchange::exchange( string const & srcPath, string const & dstPath,
}
}
DEF_EX( exExchangeWithLessThanTwoKeys, "Specify password flag (--non-encrypted or --password-file)"
" for import/export operation twice (first for source and second for destination)", std::exception )
DEF_EX( exSpecifyTwoKeys, "Specify password flag (--non-encrypted or --password-file)"
" for import/export/passwd operation twice (first for source and second for destination)", std::exception )
DEF_EX( exNonEncryptedWithKey, "--non-encrypted and --password-file are incompatible", std::exception )
DEF_EX( exSpecifyEncryptionOptions, "Specify either --password-file or --non-encrypted", std::exception )
DEF_EX_STR( exInvalidThreadsValue, "Invalid threads value specified:", std::exception )
@ -491,7 +491,7 @@ int main( int argc, char *argv[] )
"Usage: %s [flags] <command> [command args]\n"
" Flags: --non-encrypted|--password-file <file>\n"
" password flag should be specified twice if import/export\n"
" password flag should be specified twice if import/export/passwd\n"
" command specified\n"
" --silent (default is verbose)\n"
" --threads <number> (default is %zu on your system)\n"
@ -508,7 +508,8 @@ int main( int argc, char *argv[] )
" performs export from source to destination storage;\n"
" import <source storage path> <destination storage path> -\n"
" performs import from source to destination storage;\n"
" gc <storage path> - performs chunk garbage collection.\n"
" gc <storage path> - performs chunk garbage collection;\n"
" passwd <storage path> - changes repository info file passphrase.\n"
" For export/import storage path must be valid (initialized) storage.\n"
"", *argv,
defaultThreads, defaultCacheSizeMb );
@ -518,12 +519,16 @@ int main( int argc, char *argv[] )
if ( passwords.size() > 1 &&
( ( passwords[ 0 ].empty() && !passwords[ 1 ].empty() ) ||
( !passwords[ 0 ].empty() && passwords[ 1 ].empty() ) ) &&
( strcmp( args[ 0 ], "export" ) != 0 && strcmp( args[ 0 ], "import" ) != 0 ) )
( strcmp( args[ 0 ], "export" ) != 0 &&
strcmp( args[ 0 ], "import" ) != 0 &&
strcmp( args[ 0 ], "passwd" ) ) )
throw exNonEncryptedWithKey();
else
if ( passwords.size() < 2 &&
( strcmp( args[ 0 ], "export" ) == 0 || strcmp( args[ 0 ], "import" ) == 0 ) )
throw exExchangeWithLessThanTwoKeys();
if ( passwords.size() != 2 &&
( strcmp( args[ 0 ], "export" ) == 0 ||
strcmp( args[ 0 ], "import" ) == 0 ||
strcmp( args[ 0 ], "passwd" ) == 0 ) )
throw exSpecifyTwoKeys();
else
if ( passwords.size() < 1 )
throw exSpecifyEncryptionOptions();
@ -614,7 +619,7 @@ int main( int argc, char *argv[] )
// Perform the restore
if ( args.size() != 2 )
{
fprintf( stderr, "Usage: %s gc <backup directory>\n",
fprintf( stderr, "Usage: %s gc <storage path>\n",
*argv );
return EXIT_FAILURE;
}
@ -622,6 +627,28 @@ int main( int argc, char *argv[] )
zr.gc();
}
else
if ( strcmp( args[ 0 ], "passwd" ) == 0 )
{
// Perform the password change
if ( args.size() != 2 )
{
fprintf( stderr, "Usage: %s passwd <storage path>\n",
*argv );
return EXIT_FAILURE;
}
ZBackupBase zbb( args[ 1 ], passwords[ 0 ], true );
if ( passwords[ 0 ].empty() != passwords[ 1 ].empty() )
{
fprintf( stderr,
"Changing repo encryption type (non-encrypted to encrypted and vice versa) "
"is not supported yet.\n"
"Current repo type: %s\n", zbb.encryptionkey.hasKey() ? "encrypted" : "non-encrypted" );
return EXIT_FAILURE;
}
zbb.setPassword( passwords[ 1 ] );
}
else
{
fprintf( stderr, "Error: unknown command line option: %s\n", args[ 0 ] );
return EXIT_FAILURE;

View File

@ -73,12 +73,15 @@ void ZBackupBase::initStorage( string const & storageDir,
// TODO: make the following configurable
storageInfo.set_chunk_max_size( 65536 );
storageInfo.set_bundle_max_payload_size( 0x200000 );
EncryptionKey encryptionkey = EncryptionKey::noKey();
if ( isEncrypted )
EncryptionKey::generate( password,
*storageInfo.mutable_encryption_key() );
*storageInfo.mutable_encryption_key(),
encryptionkey );
storageInfo.set_default_compression_method( Compression::CompressionMethod::defaultCompression->getName() );
storageInfo.set_default_compression_method(
Compression::CompressionMethod::defaultCompression->getName() );
Paths paths( storageDir );
@ -131,3 +134,14 @@ void ZBackupBase::useDefaultCompressionMethod()
Compression::CompressionMethod::defaultCompression = compression;
}
void ZBackupBase::setPassword( string const & password )
{
EncryptionKey::generate( password,
*storageInfo.mutable_encryption_key(), encryptionkey );
StorageInfoFile::save( getStorageInfoPath(), storageInfo );
EncryptionKey encryptionkey( password, storageInfo.has_encryption_key() ?
&storageInfo.encryption_key() : 0 );
}

View File

@ -53,6 +53,8 @@ public:
void useDefaultCompressionMethod();
void setPassword( std::string const & password );
StorageInfo storageInfo;
EncryptionKey encryptionkey;
TmpMgr tmpMgr;