Skip to content

Commit

Permalink
Fileset encryption overhaul.
Browse files Browse the repository at this point in the history
This patch adds the following changes:
   - Add new fileset option that forces encryption.
     This new fileset option will fail a backup when encryption is not
     enabled at the client side for a backup.
   - Implement the encryption=<cipher> fileset option which was as it
     seems available but never implemented in the fileset exchange protocol
     between the director and the filed.

In the new setup things work like this:
   - When in compat mode we always fall back to CRYPTO_CIPHER_AES_128_CBC
   - When the fileset has a crypto cipher set we use that one.
     Only one cipher can be set for a backup so multiple ciphers in one
     fileset will give a fatal error.
   - When the fileset has no encryption cipher override the global configured
     encryption cipher is used.
   - When the encryption cipher is determined the force encryption fileset flag
     is checked to see if encryption is enabled for the backup.
  • Loading branch information
Marco van Wieringen committed Apr 28, 2015
1 parent 0481820 commit 4b2b7d7
Show file tree
Hide file tree
Showing 13 changed files with 342 additions and 68 deletions.
1 change: 1 addition & 0 deletions src/dird/inc_conf.c
Expand Up @@ -100,6 +100,7 @@ RES_ITEM options_items[] = {
{ "Size", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
{ "Shadowing", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
{ "AutoExclude", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
{ "ForceEncryption", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
{ "Meta", CFG_TYPE_META, { 0 }, 0, 0, 0, NULL, NULL },
{ NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
};
Expand Down
20 changes: 16 additions & 4 deletions src/dird/inc_conf.h
@@ -1,7 +1,7 @@
/*
BAREOS® - Backup Archiving REcovery Open Sourced
Copyright (C) 2013-2013 Bareos GmbH & Co. KG
Copyright (C) 2013-2015 Bareos GmbH & Co. KG
This program is Free Software; you can redistribute it and/or
modify it under the terms of version three of the GNU Affero General Public
Expand Down Expand Up @@ -58,7 +58,8 @@ enum {
INC_KW_XATTR,
INC_KW_SIZE,
INC_KW_SHADOWING,
INC_KW_AUTO_EXCLUDE
INC_KW_AUTO_EXCLUDE,
INC_KW_FORCE_ENCRYPTION
};

/*
Expand Down Expand Up @@ -96,6 +97,7 @@ static struct s_kw FS_option_kw[] = {
{ "size", INC_KW_SIZE },
{ "shadowing", INC_KW_SHADOWING },
{ "autoexclude", INC_KW_AUTO_EXCLUDE },
{ "forceencryption", INC_KW_FORCE_ENCRYPTION },
{ NULL, 0 }
};

Expand Down Expand Up @@ -133,8 +135,16 @@ static struct s_fs_opt FS_options[] = {
{ "lzfast", INC_KW_COMPRESSION, "Zff" },
{ "lz4", INC_KW_COMPRESSION, "Zf4" },
{ "lz4hc", INC_KW_COMPRESSION, "Zfh" },
{ "blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
{ "3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
{ "blowfish", INC_KW_ENCRYPTION, "Eb" },
{ "3des", INC_KW_ENCRYPTION, "E3" },
{ "aes128", INC_KW_ENCRYPTION, "Ea1" },
{ "aes192", INC_KW_ENCRYPTION, "Ea2" },
{ "aes256", INC_KW_ENCRYPTION, "Ea3" },
{ "camellia128", INC_KW_ENCRYPTION, "Ec1" },
{ "camellia192", INC_KW_ENCRYPTION, "Ec2" },
{ "camellia256", INC_KW_ENCRYPTION, "Ec3" },
{ "aes128hmacsha1", INC_KW_ENCRYPTION, "Eh1" },
{ "aes256hmacsha1", INC_KW_ENCRYPTION, "Eh2" },
{ "yes", INC_KW_ONEFS, "0" },
{ "no", INC_KW_ONEFS, "f" },
{ "yes", INC_KW_RECURSE, "0" },
Expand Down Expand Up @@ -179,6 +189,8 @@ static struct s_fs_opt FS_options[] = {
{ "none", INC_KW_SHADOWING, "0" },
{ "yes", INC_KW_AUTO_EXCLUDE, "0" },
{ "no", INC_KW_AUTO_EXCLUDE, "x" },
{ "yes", INC_KW_FORCE_ENCRYPTION, "Ef" },
{ "no", INC_KW_FORCE_ENCRYPTION, "0" },
{ NULL, 0, 0 }
};

Expand Down
14 changes: 8 additions & 6 deletions src/filed/backup.c
Expand Up @@ -69,13 +69,11 @@ static void close_vss_backup_session(JCR *jcr);
* also run a "heartbeat" monitor which reads the socket and
* reacts accordingly (at the moment it has nothing to do
* except echo the heartbeat to the Director).
*
*/
bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
bool blast_data_to_storage_daemon(JCR *jcr, char *addr, crypto_cipher_t cipher)
{
BSOCK *sd;
bool ok = true;
// TODO landonf: Allow user to specify encryption algorithm

sd = jcr->store_bsock;

Expand Down Expand Up @@ -104,13 +102,15 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
return false;
}

if (!crypto_session_start(jcr)) {
if (!crypto_session_start(jcr, cipher)) {
return false;
}

set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime);

/** in accurate mode, we overload the find_one check function */
/**
* In accurate mode, we overload the find_one check function
*/
if (jcr->accurate) {
set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file);
}
Expand All @@ -133,7 +133,9 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
jcr->xattr_data->u.build->content = get_pool_memory(PM_MESSAGE);
}

/** Subroutine save_file() is called for each file */
/**
* Subroutine save_file() is called for each file
*/
if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) {
ok = false; /* error */
jcr->setJobStatus(JS_ErrorTerminated);
Expand Down
37 changes: 19 additions & 18 deletions src/filed/crypto.c
Expand Up @@ -47,19 +47,8 @@ static void unser_crypto_packet_len(RESTORE_CIPHER_CTX *ctx)
}
}

bool crypto_session_start(JCR *jcr)
bool crypto_session_start(JCR *jcr, crypto_cipher_t cipher)
{
crypto_cipher_t cipher;

/*
* See if we are in compatible mode then we are hardcoded to CRYPTO_CIPHER_AES_128_CBC.
*/
if (me->compatible) {
cipher = CRYPTO_CIPHER_AES_128_CBC;
} else {
cipher = me->pki_cipher;
}

/**
* Create encryption session data and a cached, DER-encoded session data
* structure. We use a single session key for each backup, so we'll encode
Expand All @@ -68,32 +57,44 @@ bool crypto_session_start(JCR *jcr)
if (jcr->crypto.pki_encrypt) {
uint32_t size = 0;

/** Create per-job session encryption context */
/**
* Create per-job session encryption context
*/
jcr->crypto.pki_session = crypto_session_new(cipher, jcr->crypto.pki_recipients);
if (!jcr->crypto.pki_session) {
Jmsg(jcr, M_FATAL, 0, _("Cannot create a new crypto session probably unsupported cipher configured.\n"));
return false;
}

/** Get the session data size */
/**
* Get the session data size
*/
if (!crypto_session_encode(jcr->crypto.pki_session, (uint8_t *)0, &size)) {
Jmsg(jcr, M_FATAL, 0, _("An error occurred while encrypting the stream.\n"));
return false;
}

/** Allocate buffer */
/**
* Allocate buffer
*/
jcr->crypto.pki_session_encoded = get_memory(size);

/** Encode session data */
/**
* Encode session data
*/
if (!crypto_session_encode(jcr->crypto.pki_session, (uint8_t *)jcr->crypto.pki_session_encoded, &size)) {
Jmsg(jcr, M_FATAL, 0, _("An error occurred while encrypting the stream.\n"));
return false;
}

/** ... and store the encoded size */
/**
* ... and store the encoded size
*/
jcr->crypto.pki_session_encoded_size = size;

/** Allocate the encryption/decryption buffer */
/**
* Allocate the encryption/decryption buffer
*/
jcr->crypto.crypto_buf = get_memory(CRYPTO_CIPHER_MAX_BLOCK_SIZE);
}

Expand Down
138 changes: 129 additions & 9 deletions src/filed/dir_cmd.c
Expand Up @@ -1487,16 +1487,131 @@ static bool storage_cmd(JCR *jcr)
return false;
}

/**
* Clear a flag in the find options.
*
* We walk the list of include blocks and for each option block
* check if a certain flag is set and clear that.
*/
static inline void clear_flag_in_fileset(JCR *jcr, int flag, const char *warning)
{
findFILESET *fileset;
bool cleared_flag = false;

fileset = jcr->ff->fileset;
if (fileset) {
for (int i = 0; i < fileset->include_list.size(); i++) {
findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);

for (int j = 0; j < incexe->opts_list.size(); j++) {
findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);

if (bit_is_set(flag, fo->flags)) {
clear_bit(flag, fo->flags);
cleared_flag = true;
}
}
}
}

if (cleared_flag) {
Jmsg(jcr, M_WARNING, 0, warning);
}
}

/**
* Find out what encryption cipher to use.
*/
static inline bool get_wanted_crypto_cipher(JCR *jcr, crypto_cipher_t *cipher)
{
findFILESET *fileset;
bool force_encrypt = false;
crypto_cipher_t wanted_cipher = CRYPTO_CIPHER_NONE;

/*
* Walk the fileset and check for the FO_FORCE_ENCRYPT flag and any forced crypto cipher.
*/
fileset = jcr->ff->fileset;
if (fileset) {
for (int i = 0; i < fileset->include_list.size(); i++) {
findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);

for (int j = 0; j < incexe->opts_list.size(); j++) {
findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);

if (bit_is_set(FO_FORCE_ENCRYPT, fo->flags)) {
force_encrypt = true;
}

if (fo->Encryption_cipher != CRYPTO_CIPHER_NONE) {
/*
* Make sure we have not found a cipher definition before.
*/
if (wanted_cipher != CRYPTO_CIPHER_NONE) {
Jmsg(jcr, M_FATAL, 0, _("Fileset contains multiple cipher settings\n"));
return false;
}

/*
* See if pki_encrypt is already set for this Job.
*/
if (!jcr->crypto.pki_encrypt) {
if (!me->pki_keypair_file) {
Jmsg(jcr, M_FATAL, 0, _("Fileset contains cipher settings but PKI Key Pair is not configured\n"));
return false;
}

/*
* Enable encryption and signing for this Job.
*/
jcr->crypto.pki_sign = true;
jcr->crypto.pki_encrypt = true;
}

wanted_cipher = (crypto_cipher_t)fo->Encryption_cipher;
}
}
}
}

/*
* See if fileset forced a certain cipher.
*/
if (wanted_cipher == CRYPTO_CIPHER_NONE) {
wanted_cipher = me->pki_cipher;
}

/*
* See if we are in compatible mode then we are hardcoded to CRYPTO_CIPHER_AES_128_CBC.
*/
if (me->compatible) {
wanted_cipher = CRYPTO_CIPHER_AES_128_CBC;
}

/*
* See if FO_FORCE_ENCRYPT is set and encryption is not configured for the filed.
*/
if (force_encrypt && !jcr->crypto.pki_encrypt) {
Jmsg(jcr, M_FATAL, 0, _("Fileset forces encryption but encryption is not configured\n"));
return false;
}

*cipher = wanted_cipher;

return true;
}

/**
* Do a backup.
*/
static bool backup_cmd(JCR *jcr)
{
BSOCK *dir = jcr->dir_bsock;
BSOCK *sd = jcr->store_bsock;
int ok = 0;
int SDJobStatus;
int32_t FileIndex;
BSOCK *dir = jcr->dir_bsock;
BSOCK *sd = jcr->store_bsock;
crypto_cipher_t cipher = CRYPTO_CIPHER_NONE;

/*
* See if we are in restore only mode then we don't allow a backup to be initiated.
Expand Down Expand Up @@ -1536,16 +1651,21 @@ static bool backup_cmd(JCR *jcr)
Dmsg1(100, "JobFiles=%ld\n", jcr->JobFiles);
}

if (!get_wanted_crypto_cipher(jcr, &cipher)) {
dir->fsend(BADcmd, "backup");
goto cleanup;
}

/**
* Validate some options given to the backup make sense for the compiled in options of this filed.
*/
if (bit_is_set(FO_ACL, jcr->ff->flags) && !have_acl) {
Jmsg(jcr, M_WARNING, 0, _("ACL support requested in fileset but not available on this platform. Disabling ...\n"));
clear_bit(FO_ACL, jcr->ff->flags);
if (!have_acl) {
clear_flag_in_fileset(jcr, FO_ACL,
_("ACL support requested in fileset but not available on this platform. Disabling ...\n"));
}
if (bit_is_set(FO_XATTR, jcr->ff->flags) && !have_xattr) {
Jmsg(jcr, M_WARNING, 0, _("XATTR support requested in fileset but not available on this platform. Disabling ...\n"));
clear_bit(FO_XATTR, jcr->ff->flags);
if (!have_xattr) {
clear_flag_in_fileset(jcr, FO_XATTR,
_("XATTR support requested in fileset but not available on this platform. Disabling ...\n"));
}

jcr->setJobStatus(JS_Blocked);
Expand Down Expand Up @@ -1676,7 +1796,7 @@ static bool backup_cmd(JCR *jcr)
* Send Files to Storage daemon
*/
Dmsg1(110, "begin blast ff=%p\n", (FF_PKT *)jcr->ff);
if (!blast_data_to_storage_daemon(jcr, NULL)) {
if (!blast_data_to_storage_daemon(jcr, NULL, cipher)) {
jcr->setJobStatus(JS_ErrorTerminated);
bnet_suppress_error_messages(sd, 1);
Dmsg0(110, "Error in blast_data.\n");
Expand Down
3 changes: 2 additions & 1 deletion src/filed/filed_conf.c
Expand Up @@ -496,6 +496,8 @@ void save_resource(int type, RES_ITEM *items, int pass)
}

static struct s_kw CryptoCiphers[] = {
{ "blowfish", CRYPTO_CIPHER_BLOWFISH_CBC },
{ "3des", CRYPTO_CIPHER_3DES_CBC },
{ "aes128", CRYPTO_CIPHER_AES_128_CBC },
{ "aes192", CRYPTO_CIPHER_AES_192_CBC },
{ "aes256", CRYPTO_CIPHER_AES_256_CBC },
Expand All @@ -504,7 +506,6 @@ static struct s_kw CryptoCiphers[] = {
{ "camellia256", CRYPTO_CIPHER_CAMELLIA_256_CBC },
{ "aes128hmacsha1", CRYPTO_CIPHER_AES_128_CBC_HMAC_SHA1 },
{ "aes256hmacsha1", CRYPTO_CIPHER_AES_256_CBC_HMAC_SHA1 },
{ "blowfish", CRYPTO_CIPHER_BLOWFISH_CBC },
{ NULL, 0 }
};

Expand Down

0 comments on commit 4b2b7d7

Please sign in to comment.