Heres a version with a working patch ;) -- You received this message because you are subscribed to the Google Groups "bareos-devel" group. To unsubscribe from this group and stop receiving emails from it, send an email to bareos-devel+unsubscr...@googlegroups.com. To post to this group, send email to bareos-devel@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
diff --git a/core/src/lib/crypto.h b/core/src/lib/crypto.h index d4e845c..1eb5aaf 100644 --- a/core/src/lib/crypto.h +++ b/core/src/lib/crypto.h @@ -164,5 +164,6 @@ const char *crypto_digest_name(crypto_digest_t type); const char *crypto_digest_name(DIGEST *digest); crypto_digest_t CryptoDigestStreamType(int stream); const char *crypto_strerror(crypto_error_t error); +int CryptoRandomFill(unsigned char *buf, int size); #endif /* BAREOS_LIB_CRYPTO_H_ */ diff --git a/core/src/lib/crypto_openssl.cc b/core/src/lib/crypto_openssl.cc index 04eb4b8..8ec06bb 100644 --- a/core/src/lib/crypto_openssl.cc +++ b/core/src/lib/crypto_openssl.cc @@ -433,6 +433,13 @@ X509_KEYPAIR *crypto_keypair_dup(X509_KEYPAIR *keypair) return newpair; } +/* + * Fill a buffer with random bytes + */ +int CryptoRandomFill(unsigned char *buf, int size) +{ + return RAND_bytes(buf, size); +} /* * Load a public key from a PEM-encoded x509 certificate. diff --git a/core/src/stored/CMakeLists.txt b/core/src/stored/CMakeLists.txt index 2a3b5e6..aec46ae 100644 --- a/core/src/stored/CMakeLists.txt +++ b/core/src/stored/CMakeLists.txt @@ -78,7 +78,7 @@ set (LIBBAREOSSD_SRCS acquire.cc ansi_label.cc askdir.cc autochanger.cc butil.cc crc32.cc dev.cc device.cc ebcdic.cc label.cc lock.cc mount.cc read_record.cc record.cc reserve.cc scan.cc sd_backends.cc sd_plugins.cc sd_stats.cc spool.cc - stored_globals.cc stored_conf.cc vol_mgr.cc wait.cc + stored_globals.cc stored_conf.cc vol_mgr.cc vol_crypto.cc wait.cc ${AVAILABLE_DEVICE_API_SRCS} ) diff --git a/core/src/stored/block.cc b/core/src/stored/block.cc index b34fc17..9a08592 100644 --- a/core/src/stored/block.cc +++ b/core/src/stored/block.cc @@ -64,6 +64,7 @@ void DumpBlock(DeviceBlock *b, const char *msg) int32_t FileIndex; int32_t Stream; int bhl, rhl; + char Sum[BLKHDR_SUM_LENGTH+1]; char buf1[100], buf2[100]; UnserBegin(b->buf, BLKHDR1_LENGTH); @@ -73,7 +74,15 @@ void DumpBlock(DeviceBlock *b, const char *msg) UnserBytes(Id, BLKHDR_ID_LENGTH); ASSERT(UnserLength(b->buf) == BLKHDR1_LENGTH); Id[BLKHDR_ID_LENGTH] = 0; - if (Id[3] == '2') { + if (Id[3] == '3') { + unser_uint32(VolSessionId); + unser_uint32(VolSessionTime); + Sum[BLKHDR_SUM_LENGTH] = 0; + UnserBytes(Sum, BLKHDR_SUM_LENGTH); + bhl = BLKHDR3_LENGTH; + rhl = RECHDR2_LENGTH; + } + else if (Id[3] == '2') { unser_uint32(VolSessionId); unser_uint32(VolSessionTime); bhl = BLKHDR2_LENGTH; @@ -188,6 +197,7 @@ void EmptyBlock(DeviceBlock *block) block->write_failed = false; block->block_read = false; block->FirstIndex = block->LastIndex = 0; + memset(block->Sum, 0, 32); } /** @@ -202,16 +212,18 @@ static uint32_t SerBlockHeader(DeviceBlock *block, bool DoChecksum) uint32_t block_len = block->binbuf; Dmsg1(1390, "SerBlockHeader: block_len=%d\n", block_len); - SerBegin(block->buf, BLKHDR2_LENGTH); + SerBegin(block->buf, BLKHDR3_LENGTH); ser_uint32(CheckSum); ser_uint32(block_len); ser_uint32(block->BlockNumber); SerBytes(WRITE_BLKHDR_ID, BLKHDR_ID_LENGTH); - if (BLOCK_VER >= 2) { + if (BLOCK_VER == 2) { ser_uint32(block->VolSessionId); ser_uint32(block->VolSessionTime); } - + if (BLOCK_VER == 3) { + SerBytes(block->Sum, BLKHDR_SUM_LENGTH); + } /* * Checksum whole block except for the checksum */ @@ -220,7 +232,7 @@ static uint32_t SerBlockHeader(DeviceBlock *block, bool DoChecksum) block_len-BLKHDR_CS_LENGTH); } Dmsg1(1390, "ser_bloc_header: checksum=%x\n", CheckSum); - SerBegin(block->buf, BLKHDR2_LENGTH); + SerBegin(block->buf, BLKHDR3_LENGTH); ser_uint32(CheckSum); /* now add checksum to block header */ return CheckSum; } @@ -280,6 +292,24 @@ static inline bool unSerBlockHeader(JobControlRecord *jcr, Device *dev, DeviceBl block->read_errors++; return false; } + } else if (Id[3] == '3') { + unser_uint32(block->VolSessionId); + unser_uint32(block->VolSessionTime); + UnserBytes(block->Sum, BLKHDR_SUM_LENGTH); + bhl = BLKHDR3_LENGTH; + block->BlockVer = 3; + block->bufp = block->buf + bhl; + if (!bstrncmp(Id, BLKHDR3_ID, BLKHDR_ID_LENGTH)) { + dev->dev_errno = EIO; + Mmsg4(dev->errmsg, _("Volume data error at %u:%u! Wanted ID: \"%s\", got \"%s\". Buffer discarded.\n"), + dev->file, dev->block_num, BLKHDR3_ID, Id); + if (block->read_errors == 0 || verbose >= 2) { + Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg); + } + block->read_errors++; + return false; + } + } else { dev->dev_errno = EIO; Mmsg4(dev->errmsg, _("Volume data error at %u:%u! Wanted ID: \"%s\", got \"%s\". Buffer discarded.\n"), @@ -322,7 +352,9 @@ static inline bool unSerBlockHeader(JobControlRecord *jcr, Device *dev, DeviceBl block->BlockNumber = BlockNumber; Dmsg3(390, "Read binbuf = %d %d block_len=%d\n", block->binbuf, bhl, block_len); - if (block_len <= block->read_len && dev->DoChecksum()) { + /* The crytpo checksum works to checksum the block and crc32 wont work after + * the raw block is enciphered */ + if (block_len <= block->read_len && dev->DoChecksum() && dev->VolHdr.cipher == 0) { BlockCheckSum = bcrc32((uint8_t *)block->buf+BLKHDR_CS_LENGTH, block_len-BLKHDR_CS_LENGTH); if (BlockCheckSum != CheckSum) { @@ -572,6 +604,7 @@ bool DeviceControlRecord::WriteBlockToDev() int hit_max1, hit_max2; bool ok = true; DeviceControlRecord *dcr = this; + struct DeviceBlock *prev_block; uint32_t checksum; if (no_tape_write_test) { @@ -724,6 +757,17 @@ bool DeviceControlRecord::WriteBlockToDev() int retry = 0; errno = 0; status = 0; + + /* Cryto xfrm the block before write. If somethings goes wrong + * such as end-of-media we revert back to the OLD plaintext + * block and pass it back up to the handlers up the stack. + */ + prev_block = VolCryptoBlockEncrypt(jcr, block); + if (!prev_block) { + Dmsg0(200, "VolCryptoBlockEncrypt() returned FALSE!\n"); + return false; + } + do { if (retry > 0 && status == -1 && errno == EBUSY) { BErrNo be; @@ -752,6 +796,13 @@ bool DeviceControlRecord::WriteBlockToDev() #endif if (status != (ssize_t)wlen) { + /* When using crypto, return to the old, untransformed block before + * we pass back */ + if (prev_block != block) { + FreeBlock(block); + block = prev_block; + } + /* * Some devices simply report EIO when the volume is full. * With a little more thought we may be able to check @@ -850,6 +901,11 @@ bool DeviceControlRecord::WriteBlockToDev() dev->file_addr += wlen; /* update file address */ dev->file_size += wlen; + /* Discard the old unencrypted block */ + if (prev_block != block) { + FreeBlock(prev_block); + } + Dmsg2(1300, "WriteBlock: wrote block %d bytes=%d\n", dev->block_num, wlen); EmptyBlock(block); return true; @@ -1075,6 +1131,10 @@ reread: return false; } + if (!VolCryptoBlockDecrypt(jcr, block)) { + Jmsg0(jcr, M_ERROR, 0, "Volume decryption has failed.\n"); + return false; + } /* * If the block is bigger than the buffer, we Reposition for * re-reading the block, allocate a buffer of the correct size, diff --git a/core/src/stored/block.h b/core/src/stored/block.h index dabc22d..d5f3ebe 100644 --- a/core/src/stored/block.h +++ b/core/src/stored/block.h @@ -41,14 +41,17 @@ class Device; /* for forward reference */ /* Block Header definitions. */ #define BLKHDR1_ID "BB01" #define BLKHDR2_ID "BB02" +#define BLKHDR3_ID "BB03" #define BLKHDR_ID_LENGTH 4 #define BLKHDR_CS_LENGTH 4 /**< checksum length */ +#define BLKHDR_SUM_LENGTH 32 /**< cryptographic checksum len */ #define BLKHDR1_LENGTH 16 /**< Total length */ #define BLKHDR2_LENGTH 24 /**< Total length */ +#define BLKHDR3_LENGTH 56 /**< Total length */ -#define WRITE_BLKHDR_ID BLKHDR2_ID -#define WRITE_BLKHDR_LENGTH BLKHDR2_LENGTH -#define BLOCK_VER 2 +#define WRITE_BLKHDR_ID BLKHDR3_ID +#define WRITE_BLKHDR_LENGTH BLKHDR3_LENGTH +#define BLOCK_VER 3 /* Record header definitions */ #define RECHDR1_LENGTH 20 @@ -59,7 +62,8 @@ class Device; /* for forward reference */ #define BareosId "Bareos 2.0 immortal\n" #define OldBaculaId "Bacula 1.0 immortal\n" #define OlderBaculaId "Bacula 0.9 mortal\n" -#define BareosTapeVersion 20 +#define BareosTapeVersion 21 +#define OldBareosTapeVersion1 20 #define OldCompatibleBareosTapeVersion1 11 #define OldCompatibleBareosTapeVersion2 10 #define OldCompatibleBareosTapeVersion3 9 @@ -102,6 +106,7 @@ struct DeviceBlock { uint32_t read_len; /* bytes read into buffer, if zero, block empty */ uint32_t VolSessionId; /* */ uint32_t VolSessionTime; /* */ + uint8_t Sum[BLKHDR_SUM_LENGTH+1]; /* cryptographic checksum */ uint32_t read_errors; /* block errors (checksum, header, ...) */ int BlockVer; /* block version 1 or 2 */ bool write_failed; /* set if write failed */ diff --git a/core/src/stored/label.cc b/core/src/stored/label.cc index e6315b1..791ceac 100644 --- a/core/src/stored/label.cc +++ b/core/src/stored/label.cc @@ -35,6 +35,7 @@ #include "stored/device.h" #include "stored/label.h" #include "lib/edit.h" +#include "lib/crypto.h" #include "include/jcr.h" namespace storagedaemon { @@ -527,6 +528,8 @@ static void CreateVolumeLabelRecord(DeviceControlRecord *dcr, Device *dev, Devic SerString(dev->VolHdr.LabelProg); SerString(dev->VolHdr.ProgVersion); SerString(dev->VolHdr.ProgDate); + SerBytes(dev->VolHdr.Salt, sizeof(dev->VolHdr.Salt)); + ser_uint16(dev->VolHdr.cipher); SerEnd(rec->data, SER_LENGTH_Volume_Label); bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName)); @@ -577,6 +580,12 @@ void CreateVolumeLabel(Device *dev, const char *VolName, const char *PoolName) bstrncpy(dev->VolHdr.LabelProg, my_name, sizeof(dev->VolHdr.LabelProg)); sprintf(dev->VolHdr.ProgVersion, "Ver. %s %s", VERSION, BDATE); sprintf(dev->VolHdr.ProgDate, "Build %s %s", __DATE__, __TIME__); + memset(dev->VolHdr.Salt, 0, sizeof(dev->VolHdr.Salt)); + if (device->vol_crypto_key) { + /* We dont allow selection, just set something strong */ + dev->VolHdr.cipher = V_CRYPTO_AESGCM256; + CryptoRandomFill((unsigned char *)dev->VolHdr.Salt, 8); + } dev->SetLabeled(); /* set has Bareos label */ if (debug_level >= 90) { DumpVolumeLabel(dev); @@ -736,6 +745,8 @@ bool UnserVolumeLabel(Device *dev, DeviceRecord *rec) dev->VolHdr.LabelType = rec->FileIndex; dev->VolHdr.LabelSize = rec->data_len; + memset(dev->VolHdr.Key, 0, sizeof(dev->VolHdr.Key)); + memset(dev->VolHdr.Iv, 0, sizeof(dev->VolHdr.Iv)); /* UnSerialize the record into the Volume Header */ @@ -765,6 +776,11 @@ bool UnserVolumeLabel(Device *dev, DeviceRecord *rec) UnserString(dev->VolHdr.ProgVersion); UnserString(dev->VolHdr.ProgDate); + if (dev->VolHdr.VerNum > 20) { + UnserBytes(dev->VolHdr.Salt, sizeof(dev->VolHdr.Salt)); + unser_uint16(dev->VolHdr.cipher); + } + SerEnd(rec->data, SER_LENGTH_Volume_Label); Dmsg0(190, "unser_vol_label\n"); if (debug_level >= 190) { @@ -824,7 +840,9 @@ void DumpVolumeLabel(Device *dev) { int dbl = debug_level; uint32_t File; + uint64_t salt; const char *LabelType; + const char *crypto; char buf[30]; struct tm tm; struct date_time dt; @@ -855,7 +873,11 @@ void DumpVolumeLabel(Device *dev) break; } - Pmsg11(-1, _("\nVolume Label:\n" + /* Bareos formatting doens't let me print this directly */ + memcpy(&salt, dev->VolHdr.Salt, 8); + crypto = GetVolCryptoName(dev->VolHdr.cipher); + + Pmsg13(-1, _("\nVolume Label:\n" "Id : %s" "VerNo : %d\n" "VolName : %s\n" @@ -867,12 +889,15 @@ void DumpVolumeLabel(Device *dev) "MediaType : %s\n" "PoolType : %s\n" "HostName : %s\n" +"Cipher : %s\n" +"Key Salt : %llx\n" ""), dev->VolHdr.Id, dev->VolHdr.VerNum, dev->VolHdr.VolumeName, dev->VolHdr.PrevVolumeName, File, LabelType, dev->VolHdr.LabelSize, dev->VolHdr.PoolName, dev->VolHdr.MediaType, - dev->VolHdr.PoolType, dev->VolHdr.HostName); + dev->VolHdr.PoolType, dev->VolHdr.HostName, + crypto, salt); if (dev->VolHdr.VerNum >= 11) { char dt[50]; diff --git a/core/src/stored/record.h b/core/src/stored/record.h index 49ba2a9..74d7051 100644 --- a/core/src/stored/record.h +++ b/core/src/stored/record.h @@ -160,6 +160,8 @@ struct Volume_Label { */ int32_t LabelType; /**< This is written in header only */ uint32_t LabelSize; /**< length of serialized label */ + uint8_t Key[65]; /**< Crypto key */ + uint8_t Iv[65]; /**< Crypto IV */ /* * The items below this line are stored on * the tape @@ -190,6 +192,8 @@ struct Volume_Label { char LabelProg[50]; /**< Label program name */ char ProgVersion[50]; /**< Program version */ char ProgDate[50]; /**< Program build date/time */ + char Salt[8]; /**< Salt used to protect volume */ + uint16_t cipher; /**< Cipher used to encrypt the volume */ }; diff --git a/core/src/stored/stored.h b/core/src/stored/stored.h index d3d909a..1634743 100644 --- a/core/src/stored/stored.h +++ b/core/src/stored/stored.h @@ -62,6 +62,7 @@ const int sd_debuglevel = 300; #include "stored_conf.h" #include "include/jcr.h" #include "vol_mgr.h" +#include "vol_crypto.h" #include "reserve.h" #ifdef BAREOS_LIB_LIB_H_ diff --git a/core/src/stored/stored_conf.cc b/core/src/stored/stored_conf.cc index e9b295c..907f5e4 100644 --- a/core/src/stored/stored_conf.cc +++ b/core/src/stored/stored_conf.cc @@ -262,6 +262,7 @@ static ResourceItem dev_items[] = { NULL}, {"AutoInflate", CFG_TYPE_IODIRECTION, ITEM(res_dev.autoinflate), 0, 0, NULL, "13.4.0-", NULL}, {"CollectStatistics", CFG_TYPE_BOOL, ITEM(res_dev.collectstats), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL}, + {"VolumeCryptoKey", CFG_TYPE_STRNAME, ITEM(res_dev.vol_crypto_key), 0, 0, NULL, NULL, NULL}, {NULL, 0, {0}, 0, 0, NULL, NULL, NULL}}; /** diff --git a/core/src/stored/stored_conf.h b/core/src/stored/stored_conf.h index 69a2137..c8b6d13 100644 --- a/core/src/stored/stored_conf.h +++ b/core/src/stored/stored_conf.h @@ -179,6 +179,7 @@ public: char *unmount_command; /**< Unmount command */ char *write_part_command; /**< Write part command */ char *free_space_command; /**< Free space command */ + char *vol_crypto_key; /**< Encryption/Decrypt key for volume */ /* * The following are set at runtime diff --git a/core/src/stored/vol_crypto.cc b/core/src/stored/vol_crypto.cc new file mode 100644 index 0000000..d094adb --- /dev/null +++ b/core/src/stored/vol_crypto.cc @@ -0,0 +1,310 @@ +#include "include/bareos.h" +#include "stored/stored.h" + +#include "vol_crypto.h" +#include <openssl/evp.h> + +namespace storagedaemon { + +#define MAX_CIPHERS 2 + +/* IMPORTANT. Dont use a strict block cipher, they will pad the data + * * making it larger than what it needs to be. GCM is for all intents + * * and purposes - a stream cipher. So we use that which retains + * * the correct length of the data. + * * We could also use chacha20-poly1305 but its less available + * * in openssl. */ +static const char * vol_crypto_name[] = { + "None", + "AES-GCM-256", + NULL +}; + + +static bool get_crypto_key_material(Device *dev, uint8_t *key, uint8_t *iv) { + + Dmsg0(100, "Entering get_crypto_key_material\n"); + /* We only do this once per volume, given its expensive */ + if (dev->VolHdr.Key[0] == 0 && dev->VolHdr.Iv[0] == 0) { + Dmsg0(10, "Key not derived, attempting to derive key\n"); + if (!EVP_BytesToKey(EVP_aes_128_gcm(), EVP_sha256(), + (unsigned char *)dev->VolHdr.Salt, + (unsigned char *)dev->device->vol_crypto_key, + strlen(dev->device->vol_crypto_key), + VOL_CRYPTO_ROUNDS, + (unsigned char *)dev->VolHdr.Key, + (unsigned char *)dev->VolHdr.Iv)) { + Dmsg0(10, "Key derivation failed\n"); + return false; + } + } + + /* With key material derived, copy the key and derive a new IV */ + memcpy(key, dev->VolHdr.Key, sizeof(dev->VolHdr.Key)); + memcpy(iv, dev->VolHdr.Iv, sizeof(dev->VolHdr.Key)); + + /* For now, the new IV is partially made up of the block number being + * * manipulated */ + memcpy(iv, &dev->block_num, sizeof(dev->block_num)); + + return true; +} + + + +const char * GetVolCryptoName(int v) +{ + if (v >= MAX_CIPHERS) + return NULL; + return vol_crypto_name[v]; +} + + +bool VolCryptoBlockDecrypt(JobControlRecord *jcr, struct DeviceBlock *block) { + struct Device *dev = block->dev; + struct DeviceBlock *old = NULL; + EVP_CIPHER_CTX *evp; + const EVP_CIPHER *cipher = NULL; + uint8_t *op = NULL; + uint8_t *np = NULL; + int32_t olen, nlen, wlen; + + uint8_t key[65]; + uint8_t iv[65]; + uint8_t tmp[64]; + + if (!block) { + return false; + } + + dev = block->dev; + + /* We dont decrypt block zero */ + if (block->BlockNumber == 0) { + Dmsg0(100, "Do not decrypt block zero which is typically the volume label\n"); + return true; + } + + evp = EVP_CIPHER_CTX_new(); + if (!evp) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot allocate memory for cipher context\n")); + return false; + } + + /* No decryption on none block */ + if (dev->VolHdr.cipher == V_CRYPTO_NONE) { + return true; + } + else if (dev->VolHdr.cipher == V_CRYPTO_AESGCM256) { + Dmsg0(100, "VOLUME CRYPTO IS AESGCM256\n"); + cipher = EVP_aes_256_gcm(); + } + else { + Dmsg1(100, "Volume header cipher reported back as %d\n", dev->VolHdr.cipher); + Jmsg1(jcr, M_FATAL, 0, _("Volume header cipher reported back as %d\n"), dev->VolHdr.cipher); + goto fail; + } + + /* Deterine what key and iv we are using for this block */ + if (!get_crypto_key_material(dev, key, iv)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot derive key material for volume crypto\n")); + goto fail; + } + + if (!EVP_DecryptInit(evp, cipher, key, iv)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot initialize encryption routine\n")); + goto fail; + } + + /* Retrieve the auth tag */ + if (dev->VolHdr.cipher == V_CRYPTO_AESGCM256) { + if (!EVP_CIPHER_CTX_ctrl(evp, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) { + Jmsg0(jcr, M_FATAL, 0, _("Was unable to set IV len for volume decryption\n")); + goto fail; + } + } + + old = dup_block(block); + if (!old) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot allocate memory for volume block duplication\n")); + goto fail; + } + + op = (uint8_t *)old->buf + BLKHDR3_LENGTH; + np = (uint8_t *)block->buf + BLKHDR3_LENGTH; + olen = block->block_len - BLKHDR3_LENGTH; + nlen = 0; + if (!EVP_DecryptUpdate(evp, np, &nlen, op, olen)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot perform encryption routine\n")); + goto swapblock; + } + + /* Expect the input to be the same length as the output */ + if (old->block_len != (nlen + BLKHDR3_LENGTH)) { + Jmsg2(jcr, M_FATAL, 0, _("Expected block encryption to be %d bytes but was %d\n"), + old->binbuf, block->binbuf); + goto swapblock; + } + + /* Retrieve the auth tag */ + if (dev->VolHdr.cipher == V_CRYPTO_AESGCM256) { + if (!EVP_CIPHER_CTX_ctrl(evp, EVP_CTRL_GCM_SET_TAG, 16, block->Sum)) { + Jmsg0(jcr, M_FATAL, 0, _("Was unable to set tag for volume decryption\n")); + goto fail; + } + } + + /* Only when doing a true block mode cipher do we get bytes here */ + if (!EVP_DecryptFinal(evp, tmp, &wlen)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot finalize encryption routine, possible corruption.\n")); + goto swapblock; + } + + if (wlen != 0) { + Jmsg2(jcr, M_FATAL, 0, _("Expected block encryption to be %d bytes after finalize but was %d\n"), + block->binbuf, block->binbuf+wlen); + goto swapblock; + } + + if (old) + FreeBlock(old); + if (evp) + EVP_CIPHER_CTX_free(evp); + return true; + +swapblock: + memcpy(block->bufp, old->bufp, old->binbuf); + block->binbuf = old->binbuf; + +fail: + if (old) + FreeBlock(old); + if (evp) + EVP_CIPHER_CTX_free(evp); + return false; + +} + +struct DeviceBlock * VolCryptoBlockEncrypt(JobControlRecord *jcr, struct DeviceBlock *block) { + struct Device *dev = NULL; + struct DeviceBlock *old = NULL; + EVP_CIPHER_CTX *evp; + const EVP_CIPHER *cipher = NULL; + uint8_t *op = NULL; + uint8_t *np = NULL; + int32_t olen, nlen, wlen; + + uint8_t key[65]; + uint8_t iv[65]; + uint8_t tmp[64]; + + if (!block) { + return NULL; + } + + dev = block->dev; + + /* We dont encrypt block zero */ + if (block->BlockNumber == 0) { + Dmsg0(100, "Do not encrypt block zero which is typically the volume label\n"); + return block; + } + + evp = EVP_CIPHER_CTX_new(); + if (!evp) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot allocate memory for cipher context\n")); + return NULL; + } + + /* Do nothing if volume encryption isn't enabled */ + if (dev->VolHdr.cipher == V_CRYPTO_NONE) { + Dmsg0(100, "VOLUME CRYPTO IS NONE\n"); + return block; + } + else if (dev->VolHdr.cipher == V_CRYPTO_AESGCM256) { + Dmsg0(100, "VOLUME CRYPTO IS AESGCM256\n"); + cipher = EVP_aes_256_gcm(); + } + else { + Dmsg1(100, "Volume header cipher reported back as %d\n", dev->VolHdr.cipher); + Jmsg1(jcr, M_FATAL, 0, _("Volume header cipher reported back as %d\n"), dev->VolHdr.cipher); + goto fail; + } + + old = dup_block(block); + if (!old) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot allocate memory for volume block duplication\n")); + goto fail; + } + + /* Deterine what key and iv we are using for this block */ + if (!get_crypto_key_material(dev, key, iv)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot derive key material for volume crypto\n")); + goto fail; + } + + if (!EVP_EncryptInit(evp, cipher, key, iv)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot initialize encryption routine\n")); + goto fail; + } + + op = (uint8_t *)old->buf + BLKHDR3_LENGTH; + np = (uint8_t *)block->buf + BLKHDR3_LENGTH; + olen = block->block_len - BLKHDR3_LENGTH; + nlen = 0; + if (!EVP_EncryptUpdate(evp, np, &nlen, op, olen)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot perform encryption routine\n")); + goto swapblock; + } + + /* Expect the input to be the same length as the output */ + if (old->block_len != (nlen + BLKHDR3_LENGTH)) { + Jmsg2(jcr, M_FATAL, 0, _("Expected block encryption to be %d bytes but was %d\n"), + old->binbuf, block->binbuf); + goto swapblock; + } + + /* Only when doing a true block mode cipher do we get bytes here */ + if (!EVP_EncryptFinal(evp, tmp, &wlen)) { + Jmsg0(jcr, M_FATAL, 0, _("Cannot finalize encryption routine\n")); + goto swapblock; + } + + if (wlen != 0) { + Jmsg2(jcr, M_FATAL, 0, _("Expected block encryption to be %d bytes after finalize but was %d\n"), + block->binbuf, block->binbuf+wlen); + goto swapblock; + } + + /* Retrieve the auth tag */ + if (dev->VolHdr.cipher == V_CRYPTO_AESGCM256) { + if (!EVP_CIPHER_CTX_ctrl(evp, EVP_CTRL_GCM_GET_TAG, 16, block->Sum)) { + Jmsg0(jcr, M_FATAL, 0, _("Was unable to retrieve tag for volume encryption\n")); + goto swapblock; + } + /* We've got to write our bytes directly into the buffer from the checksum */ + memcpy(&block->buf[24], block->Sum, 16); + FILE *f = fopen("/tmp/blockdata.dat", "a"); + fwrite(&block->BlockNumber, sizeof(uint32_t), 1, f); + fwrite(block->Sum, 16, 1, f); + fclose(f); + } + + if (evp) + EVP_CIPHER_CTX_free(evp); + return old; + +swapblock: + memcpy(block->bufp, old->bufp, old->binbuf); + block->binbuf = old->binbuf; + +fail: + if (old) + FreeBlock(old); + if (evp) + EVP_CIPHER_CTX_free(evp); + return block; +} + +} /* namespace storagedaemon */ + diff --git a/core/src/stored/vol_crypto.h b/core/src/stored/vol_crypto.h new file mode 100644 index 0000000..f8fc001 --- /dev/null +++ b/core/src/stored/vol_crypto.h @@ -0,0 +1,24 @@ +#ifndef VOL_CRYPTO_H +#define VOL_CRYPTO_H + +#include <openssl/evp.h> + +namespace storagedaemon { + +#define VOL_CRYPTO_ROUNDS 32768 +#define VOL_CRYPTO_IV_LEN 16 +#define VOL_CRYPTO_KEY_LEN 64 + +enum { + V_CRYPTO_NONE = 0, + V_CRYPTO_AESGCM256 +}; + +struct DeviceBlock * VolCryptoBlockEncrypt(JobControlRecord *jcr, struct DeviceBlock *block); +bool VolCryptoBlockDecrypt(JobControlRecord *jcr, struct DeviceBlock *block); + +const char * GetVolCryptoName(int); + +} /* namespace storagedaemon */ +#endif +