diff options
Diffstat (limited to 'src/librbd/crypto/luks')
-rw-r--r-- | src/librbd/crypto/luks/FlattenRequest.cc | 154 | ||||
-rw-r--r-- | src/librbd/crypto/luks/FlattenRequest.h | 65 | ||||
-rw-r--r-- | src/librbd/crypto/luks/FormatRequest.cc | 200 | ||||
-rw-r--r-- | src/librbd/crypto/luks/FormatRequest.h | 59 | ||||
-rw-r--r-- | src/librbd/crypto/luks/Header.cc | 261 | ||||
-rw-r--r-- | src/librbd/crypto/luks/Header.h | 52 | ||||
-rw-r--r-- | src/librbd/crypto/luks/LUKSEncryptionFormat.cc | 85 | ||||
-rw-r--r-- | src/librbd/crypto/luks/LUKSEncryptionFormat.h | 100 | ||||
-rw-r--r-- | src/librbd/crypto/luks/LoadRequest.cc | 272 | ||||
-rw-r--r-- | src/librbd/crypto/luks/LoadRequest.h | 71 | ||||
-rw-r--r-- | src/librbd/crypto/luks/Magic.cc | 139 | ||||
-rw-r--r-- | src/librbd/crypto/luks/Magic.h | 32 |
12 files changed, 1490 insertions, 0 deletions
diff --git a/src/librbd/crypto/luks/FlattenRequest.cc b/src/librbd/crypto/luks/FlattenRequest.cc new file mode 100644 index 000000000..fdf6b5dae --- /dev/null +++ b/src/librbd/crypto/luks/FlattenRequest.cc @@ -0,0 +1,154 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "FlattenRequest.h" + +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/Utils.h" +#include "librbd/crypto/EncryptionFormat.h" +#include "librbd/crypto/Utils.h" +#include "librbd/crypto/luks/LoadRequest.h" +#include "librbd/crypto/luks/Magic.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ImageDispatchSpec.h" +#include "librbd/io/ReadResult.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::luks::FlattenRequest: " \ + << this << " " << __func__ << ": " + +namespace librbd { +namespace crypto { +namespace luks { + +using librbd::util::create_context_callback; + +template <typename I> +FlattenRequest<I>::FlattenRequest( + I* image_ctx, Context* on_finish) : m_image_ctx(image_ctx), + m_on_finish(on_finish) { + ceph_assert(m_image_ctx->encryption_format.get() != nullptr); +} + +template <typename I> +void FlattenRequest<I>::send() { + read_header(); +} + +template <typename I> +void FlattenRequest<I>::read_header() { + auto ctx = create_context_callback< + FlattenRequest<I>, &FlattenRequest<I>::handle_read_header>(this); + auto aio_comp = io::AioCompletion::create_and_start( + ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_READ); + + auto crypto = m_image_ctx->encryption_format->get_crypto(); + ZTracer::Trace trace; + auto req = io::ImageDispatchSpec::create_read( + *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp, + {{0, crypto->get_data_offset()}}, io::ImageArea::CRYPTO_HEADER, + io::ReadResult{&m_bl}, m_image_ctx->get_data_io_context(), 0, 0, + trace); + req->send(); +} + +template <typename I> +void FlattenRequest<I>::handle_read_header(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_image_ctx->cct) << "error reading from image: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } + + r = Magic::is_rbd_clone(m_bl); + if (r < 0) { + lderr(m_image_ctx->cct) << "unable to determine encryption header magic: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } else if (r > 0) { + // switch magic + r = Magic::replace_magic(m_image_ctx->cct, m_bl); + if (r < 0) { + lderr(m_image_ctx->cct) << "unable to restore header magic: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } + } + + write_header(); +} + +template <typename I> +void FlattenRequest<I>::write_header() { + // write header to offset 0 of the image + auto ctx = create_context_callback< + FlattenRequest<I>, &FlattenRequest<I>::handle_write_header>(this); + auto aio_comp = io::AioCompletion::create_and_start( + ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_WRITE); + + ZTracer::Trace trace; + auto req = io::ImageDispatchSpec::create_write( + *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp, + {{0, m_bl.length()}}, io::ImageArea::CRYPTO_HEADER, + std::move(m_bl), 0, trace); + req->send(); +} + +template <typename I> +void FlattenRequest<I>::handle_write_header(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_image_ctx->cct) << "error writing header to image: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } + + flush(); +} + +template <typename I> +void FlattenRequest<I>::flush() { + auto ctx = create_context_callback< + FlattenRequest<I>, &FlattenRequest<I>::handle_flush>(this); + auto aio_comp = io::AioCompletion::create_and_start( + ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_FLUSH); + auto req = io::ImageDispatchSpec::create_flush( + *m_image_ctx, io::IMAGE_DISPATCH_LAYER_INTERNAL_START, aio_comp, + io::FLUSH_SOURCE_INTERNAL, {}); + req->send(); +} + +template <typename I> +void FlattenRequest<I>::handle_flush(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_image_ctx->cct) << "unable to flush image: " << cpp_strerror(r) + << dendl; + } + + finish(r); +} + +template <typename I> +void FlattenRequest<I>::finish(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::FlattenRequest<librbd::ImageCtx>; diff --git a/src/librbd/crypto/luks/FlattenRequest.h b/src/librbd/crypto/luks/FlattenRequest.h new file mode 100644 index 000000000..a1432f505 --- /dev/null +++ b/src/librbd/crypto/luks/FlattenRequest.h @@ -0,0 +1,65 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_LUKS_FLATTEN_REQUEST_H +#define CEPH_LIBRBD_CRYPTO_LUKS_FLATTEN_REQUEST_H + +#include "librbd/ImageCtx.h" + +namespace librbd { + +namespace crypto { +namespace luks { + +template <typename I> +class FlattenRequest { +public: + using EncryptionFormat = decltype(I::encryption_format); + + static FlattenRequest* create(I* image_ctx, Context* on_finish) { + return new FlattenRequest(image_ctx, on_finish); + } + + FlattenRequest(I* image_ctx, Context* on_finish); + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * READ_HEADER + * | + * v + * WRITE_HEADER (replacing magic back from RBDL to LUKS if needed) + * | + * v + * FLUSH + * | + * v + * <finish> + * + * @endverbatim + */ + I* m_image_ctx; + Context* m_on_finish; + ceph::bufferlist m_bl; + + void read_header(); + void handle_read_header(int r); + void write_header(); + void handle_write_header(int r); + void flush(); + void handle_flush(int r); + void finish(int r); +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::luks::FlattenRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_FLATTEN_REQUEST_H diff --git a/src/librbd/crypto/luks/FormatRequest.cc b/src/librbd/crypto/luks/FormatRequest.cc new file mode 100644 index 000000000..32673b9cf --- /dev/null +++ b/src/librbd/crypto/luks/FormatRequest.cc @@ -0,0 +1,200 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "FormatRequest.h" + +#include <stdlib.h> +#include <openssl/rand.h> +#include "common/dout.h" +#include "common/errno.h" +#include "include/compat.h" +#include "librbd/Utils.h" +#include "librbd/crypto/Utils.h" +#include "librbd/crypto/luks/Header.h" +#include "librbd/crypto/luks/Magic.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ImageDispatchSpec.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::luks::FormatRequest: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace crypto { +namespace luks { + +using librbd::util::create_context_callback; + +template <typename I> +FormatRequest<I>::FormatRequest( + I* image_ctx, encryption_format_t format, encryption_algorithm_t alg, + std::string_view passphrase, + std::unique_ptr<CryptoInterface>* result_crypto, Context* on_finish, + bool insecure_fast_mode) : m_image_ctx(image_ctx), m_format(format), + m_alg(alg), + m_passphrase(passphrase), + m_result_crypto(result_crypto), + m_on_finish(on_finish), + m_insecure_fast_mode(insecure_fast_mode), + m_header(image_ctx->cct) { +} + +template <typename I> +void FormatRequest<I>::send() { + const char* type; + size_t sector_size; + switch (m_format) { + case RBD_ENCRYPTION_FORMAT_LUKS1: + type = CRYPT_LUKS1; + sector_size = 512; + break; + case RBD_ENCRYPTION_FORMAT_LUKS2: + type = CRYPT_LUKS2; + sector_size = 4096; + break; + default: + lderr(m_image_ctx->cct) << "unsupported format type: " << m_format + << dendl; + finish(-EINVAL); + return; + } + + const char* cipher; + size_t key_size; + switch (m_alg) { + case RBD_ENCRYPTION_ALGORITHM_AES128: + cipher = "aes"; + key_size = 32; + break; + case RBD_ENCRYPTION_ALGORITHM_AES256: + cipher = "aes"; + key_size = 64; + break; + default: + lderr(m_image_ctx->cct) << "unsupported cipher algorithm: " << m_alg + << dendl; + finish(-EINVAL); + return; + } + + // generate encryption key + unsigned char* key = (unsigned char*)alloca(key_size); + if (RAND_bytes((unsigned char *)key, key_size) != 1) { + lderr(m_image_ctx->cct) << "cannot generate random encryption key" + << dendl; + finish(-EAGAIN); + return; + } + + // setup interface with libcryptsetup + auto r = m_header.init(); + if (r < 0) { + finish(r); + return; + } + + // format (create LUKS header) + auto stripe_period = m_image_ctx->get_stripe_period(); + r = m_header.format(type, cipher, reinterpret_cast<char*>(key), key_size, + "xts-plain64", sector_size, stripe_period, + m_insecure_fast_mode); + if (r != 0) { + finish(r); + return; + } + + m_image_ctx->image_lock.lock_shared(); + uint64_t image_size = m_image_ctx->get_image_size(CEPH_NOSNAP); + m_image_ctx->image_lock.unlock_shared(); + + if (m_header.get_data_offset() > image_size) { + lderr(m_image_ctx->cct) << "image is too small, format requires " + << m_header.get_data_offset() << " bytes" << dendl; + finish(-ENOSPC); + return; + } + + // add keyslot (volume key encrypted with passphrase) + r = m_header.add_keyslot(m_passphrase.data(), m_passphrase.size()); + if (r != 0) { + finish(r); + return; + } + + r = util::build_crypto(m_image_ctx->cct, key, key_size, + m_header.get_sector_size(), + m_header.get_data_offset(), m_result_crypto); + ceph_memzero_s(key, key_size, key_size); + if (r != 0) { + finish(r); + return; + } + + // read header from libcryptsetup interface + ceph::bufferlist bl; + r = m_header.read(&bl); + if (r < 0) { + finish(r); + return; + } + + if (m_image_ctx->parent != nullptr) { + // parent is not encrypted with same key + // change LUKS magic to prevent decryption by other LUKS implementations + r = Magic::replace_magic(m_image_ctx->cct, bl); + if (r < 0) { + lderr(m_image_ctx->cct) << "error replacing LUKS magic: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } + } + + // pad header to stripe period alignment to prevent copyup of parent data + // when writing encryption header to the child image + auto alignment = bl.length() % stripe_period; + if (alignment > 0) { + bl.append_zero(stripe_period - alignment); + } + + // write header to offset 0 of the image + auto ctx = create_context_callback< + FormatRequest<I>, &FormatRequest<I>::handle_write_header>(this); + auto aio_comp = io::AioCompletion::create_and_start( + ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_WRITE); + + ZTracer::Trace trace; + auto req = io::ImageDispatchSpec::create_write( + *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp, + {{0, bl.length()}}, io::ImageArea::DATA, std::move(bl), 0, trace); + req->send(); +} + +template <typename I> +void FormatRequest<I>::handle_write_header(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_image_ctx->cct) << "error writing header to image: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } + + finish(0); +} + +template <typename I> +void FormatRequest<I>::finish(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::FormatRequest<librbd::ImageCtx>; diff --git a/src/librbd/crypto/luks/FormatRequest.h b/src/librbd/crypto/luks/FormatRequest.h new file mode 100644 index 000000000..17d0b3af9 --- /dev/null +++ b/src/librbd/crypto/luks/FormatRequest.h @@ -0,0 +1,59 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_LUKS_FORMAT_REQUEST_H +#define CEPH_LIBRBD_CRYPTO_LUKS_FORMAT_REQUEST_H + +#include <string_view> +#include "include/rbd/librbd.hpp" +#include "librbd/ImageCtx.h" +#include "librbd/crypto/CryptoInterface.h" +#include "librbd/crypto/luks/Header.h" + +namespace librbd { + +class ImageCtx; + +namespace crypto { +namespace luks { + +template <typename I> +class FormatRequest { +public: + static FormatRequest* create( + I* image_ctx, encryption_format_t format, + encryption_algorithm_t alg, std::string_view passphrase, + std::unique_ptr<CryptoInterface>* result_crypto, Context* on_finish, + bool insecure_fast_mode) { + return new FormatRequest(image_ctx, format, alg, passphrase, + result_crypto, on_finish, insecure_fast_mode); + } + + FormatRequest(I* image_ctx, encryption_format_t format, + encryption_algorithm_t alg, std::string_view passphrase, + std::unique_ptr<CryptoInterface>* result_crypto, + Context* on_finish, bool insecure_fast_mode); + void send(); + void finish(int r); + +private: + I* m_image_ctx; + + encryption_format_t m_format; + encryption_algorithm_t m_alg; + std::string_view m_passphrase; + std::unique_ptr<CryptoInterface>* m_result_crypto; + Context* m_on_finish; + bool m_insecure_fast_mode; + Header m_header; + + void handle_write_header(int r); +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::luks::FormatRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_FORMAT_REQUEST_H diff --git a/src/librbd/crypto/luks/Header.cc b/src/librbd/crypto/luks/Header.cc new file mode 100644 index 000000000..0866f285f --- /dev/null +++ b/src/librbd/crypto/luks/Header.cc @@ -0,0 +1,261 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "Header.h" + +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include "common/dout.h" +#include "common/errno.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::luks::Header: " << this << " " \ + << __func__ << ": " + +namespace librbd { +namespace crypto { +namespace luks { + +Header::Header(CephContext* cct) : m_cct(cct), m_fd(-1), m_cd(nullptr) { +} + +Header::~Header() { + if (m_fd != -1) { + close(m_fd); + m_fd = -1; + } + if (m_cd != nullptr) { + crypt_free(m_cd); + m_cd = nullptr; + } +} + +void Header::libcryptsetup_log_wrapper(int level, const char* msg, void* header) { + ((Header*)header)->libcryptsetup_log(level, msg); +} + +void Header::libcryptsetup_log(int level, const char* msg) { + switch (level) { + case CRYPT_LOG_NORMAL: + ldout(m_cct, 5) << "[libcryptsetup] " << msg << dendl; + break; + case CRYPT_LOG_ERROR: + lderr(m_cct) << "[libcryptsetup] " << msg << dendl; + break; + case CRYPT_LOG_VERBOSE: + ldout(m_cct, 10) << "[libcryptsetup] " << msg << dendl; + break; + case CRYPT_LOG_DEBUG: + ldout(m_cct, 20) << "[libcryptsetup] " << msg << dendl; + break; + } +} + +int Header::init() { + if (m_fd != -1) { + return 0; + } + + // create anonymous file + m_fd = syscall(SYS_memfd_create, "LibcryptsetupInterface", 0); + if (m_fd == -1) { + lderr(m_cct) << "error creating anonymous file: " << cpp_strerror(-errno) + << dendl; + return -errno; + } + std::string path = + "/proc/" + std::to_string(getpid()) + "/fd/" + std::to_string(m_fd); + + if (m_cct->_conf->subsys.should_gather<dout_subsys, 30>()) { + crypt_set_debug_level(CRYPT_DEBUG_ALL); + } + + // init libcryptsetup handle + auto r = crypt_init(&m_cd, path.c_str()); + if (r != 0) { + lderr(m_cct) << "crypt_init failed: " << cpp_strerror(r) << dendl; + return r; + } + + // redirect logging + crypt_set_log_callback(m_cd, &libcryptsetup_log_wrapper, this); + + return 0; +} + +int Header::write(const ceph::bufferlist& bl) { + ceph_assert(m_fd != -1); + + auto r = bl.write_fd(m_fd); + if (r != 0) { + lderr(m_cct) << "error writing header: " << cpp_strerror(r) << dendl; + } + return r; +} + +ssize_t Header::read(ceph::bufferlist* bl) { + ceph_assert(m_fd != -1); + + // get current header size + struct stat st; + ssize_t r = fstat(m_fd, &st); + if (r < 0) { + r = -errno; + lderr(m_cct) << "failed to stat anonymous file: " << cpp_strerror(r) + << dendl; + return r; + } + + r = bl->read_fd(m_fd, st.st_size); + if (r < 0) { + lderr(m_cct) << "error reading header: " << cpp_strerror(r) << dendl; + } + + ldout(m_cct, 20) << "read size = " << r << dendl; + return r; +} + +int Header::format(const char* type, const char* alg, const char* key, + size_t key_size, const char* cipher_mode, + uint32_t sector_size, uint32_t data_alignment, + bool insecure_fast_mode) { + ceph_assert(m_cd != nullptr); + + ldout(m_cct, 20) << "sector size: " << sector_size << ", data alignment: " + << data_alignment << dendl; + + // required for passing libcryptsetup device size check + if (ftruncate(m_fd, 4096) != 0) { + lderr(m_cct) << "failed to truncate anonymous file: " + << cpp_strerror(-errno) << dendl; + return -errno; + } + + struct crypt_params_luks1 luks1params; + struct crypt_params_luks2 luks2params; + + const size_t converted_data_alignment = data_alignment / 512; + + void* params = nullptr; + if (strcmp(type, CRYPT_LUKS1) == 0) { + memset(&luks1params, 0, sizeof(luks1params)); + luks1params.data_alignment = converted_data_alignment; + params = &luks1params; + } else if (strcmp(type, CRYPT_LUKS2) == 0) { + memset(&luks2params, 0, sizeof(luks2params)); + luks2params.data_alignment = converted_data_alignment; + luks2params.sector_size = sector_size; + params = &luks2params; + } + + // this mode should be used for testing only + if (insecure_fast_mode) { + struct crypt_pbkdf_type pbkdf; + memset(&pbkdf, 0, sizeof(pbkdf)); + pbkdf.type = CRYPT_KDF_PBKDF2; + pbkdf.flags = CRYPT_PBKDF_NO_BENCHMARK; + pbkdf.hash = "sha256"; + pbkdf.iterations = 1000; + pbkdf.time_ms = 1; + auto r = crypt_set_pbkdf_type(m_cd, &pbkdf); + if (r != 0) { + lderr(m_cct) << "crypt_set_pbkdf_type failed: " << cpp_strerror(r) + << dendl; + return r; + } + } + + auto r = crypt_format( + m_cd, type, alg, cipher_mode, NULL, key, key_size, params); + if (r != 0) { + lderr(m_cct) << "crypt_format failed: " << cpp_strerror(r) << dendl; + return r; + } + + return 0; +} + +int Header::add_keyslot(const char* passphrase, size_t passphrase_size) { + ceph_assert(m_cd != nullptr); + + auto r = crypt_keyslot_add_by_volume_key( + m_cd, CRYPT_ANY_SLOT, NULL, 0, passphrase, passphrase_size); + if (r < 0) { + lderr(m_cct) << "crypt_keyslot_add_by_volume_key failed: " + << cpp_strerror(r) << dendl; + return r; + } + + return 0; +} + +int Header::load(const char* type) { + ceph_assert(m_cd != nullptr); + + // libcryptsetup checks if device size matches the header and keyslots size + // in LUKS2, 2 X 4MB header + 128MB keyslots + if (ftruncate(m_fd, 136 * 1024 * 1024) != 0) { + lderr(m_cct) << "failed to truncate anonymous file: " + << cpp_strerror(-errno) << dendl; + return -errno; + } + + auto r = crypt_load(m_cd, type, NULL); + if (r != 0) { + ldout(m_cct, 20) << "crypt_load failed: " << cpp_strerror(r) << dendl; + return r; + } + + ldout(m_cct, 20) << "sector size: " << get_sector_size() << ", data offset: " + << get_data_offset() << dendl; + + return 0; +} + +int Header::read_volume_key(const char* passphrase, size_t passphrase_size, + char* volume_key, size_t* volume_key_size) { + ceph_assert(m_cd != nullptr); + + auto r = crypt_volume_key_get( + m_cd, CRYPT_ANY_SLOT, volume_key, volume_key_size, passphrase, + passphrase_size); + if (r < 0) { + ldout(m_cct, 20) << "crypt_volume_key_get failed: " << cpp_strerror(r) + << dendl; + return r; + } + + return 0; +} + +int Header::get_sector_size() { + ceph_assert(m_cd != nullptr); + return crypt_get_sector_size(m_cd); +} + +uint64_t Header::get_data_offset() { + ceph_assert(m_cd != nullptr); + return crypt_get_data_offset(m_cd) << 9; +} + +const char* Header::get_cipher() { + ceph_assert(m_cd != nullptr); + return crypt_get_cipher(m_cd); +} + +const char* Header::get_cipher_mode() { + ceph_assert(m_cd != nullptr); + return crypt_get_cipher_mode(m_cd); +} + +const char* Header::get_format_name() { + ceph_assert(m_cd != nullptr); + return crypt_get_type(m_cd); +} + +} // namespace luks +} // namespace crypto +} // namespace librbd diff --git a/src/librbd/crypto/luks/Header.h b/src/librbd/crypto/luks/Header.h new file mode 100644 index 000000000..067d96b4a --- /dev/null +++ b/src/librbd/crypto/luks/Header.h @@ -0,0 +1,52 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_LUKS_HEADER_H +#define CEPH_LIBRBD_CRYPTO_LUKS_HEADER_H + +#include <libcryptsetup.h> +#include "common/ceph_context.h" +#include "include/buffer.h" + +namespace librbd { +namespace crypto { +namespace luks { + +class Header { +public: + Header(CephContext* cct); + ~Header(); + int init(); + + int write(const ceph::bufferlist& bl); + ssize_t read(ceph::bufferlist* bl); + + int format(const char* type, const char* alg, const char* key, + size_t key_size, const char* cipher_mode, uint32_t sector_size, + uint32_t data_alignment, bool insecure_fast_mode); + int add_keyslot(const char* passphrase, size_t passphrase_size); + int load(const char* type); + int read_volume_key(const char* passphrase, size_t passphrase_size, + char* volume_key, size_t* volume_key_size); + + int get_sector_size(); + uint64_t get_data_offset(); + const char* get_cipher(); + const char* get_cipher_mode(); + const char* get_format_name(); + +private: + void libcryptsetup_log(int level, const char* msg); + static void libcryptsetup_log_wrapper(int level, const char* msg, + void* header); + + CephContext* m_cct; + int m_fd; + struct crypt_device *m_cd; +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_HEADER_H diff --git a/src/librbd/crypto/luks/LUKSEncryptionFormat.cc b/src/librbd/crypto/luks/LUKSEncryptionFormat.cc new file mode 100644 index 000000000..1f92cf0f7 --- /dev/null +++ b/src/librbd/crypto/luks/LUKSEncryptionFormat.cc @@ -0,0 +1,85 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "LUKSEncryptionFormat.h" +#include "common/dout.h" +#include "common/errno.h" +#include "include/compat.h" +#include "librbd/crypto/luks/FlattenRequest.h" +#include "librbd/crypto/luks/FormatRequest.h" +#include "librbd/crypto/luks/LoadRequest.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::luks::LUKSEncryptionFormat:: " \ + << this << " " << __func__ << ": " + +namespace librbd { +namespace crypto { +namespace luks { + +template <typename I> +void EncryptionFormat<I>::flatten(I* image_ctx, Context* on_finish) { + auto req = luks::FlattenRequest<I>::create(image_ctx, on_finish); + req->send(); +} + +template <typename I> +void LUKSEncryptionFormat<I>::format(I* image_ctx, Context* on_finish) { + lderr(image_ctx->cct) << "explicit LUKS version required for format" << dendl; + on_finish->complete(-EINVAL); +} + +template <typename I> +void LUKSEncryptionFormat<I>::load(I* image_ctx, + std::string* detected_format_name, + Context* on_finish) { + auto req = luks::LoadRequest<I>::create(image_ctx, RBD_ENCRYPTION_FORMAT_LUKS, + m_passphrase, &this->m_crypto, + detected_format_name, on_finish); + req->send(); +} + +template <typename I> +void LUKS1EncryptionFormat<I>::format(I* image_ctx, Context* on_finish) { + auto req = luks::FormatRequest<I>::create( + image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, m_alg, m_passphrase, + &this->m_crypto, on_finish, false); + req->send(); +} + +template <typename I> +void LUKS1EncryptionFormat<I>::load(I* image_ctx, + std::string* detected_format_name, + Context* on_finish) { + auto req = luks::LoadRequest<I>::create( + image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, m_passphrase, &this->m_crypto, + detected_format_name, on_finish); + req->send(); +} + +template <typename I> +void LUKS2EncryptionFormat<I>::format(I* image_ctx, Context* on_finish) { + auto req = luks::FormatRequest<I>::create( + image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, m_alg, m_passphrase, + &this->m_crypto, on_finish, false); + req->send(); +} + +template <typename I> +void LUKS2EncryptionFormat<I>::load(I* image_ctx, + std::string* detected_format_name, + Context* on_finish) { + auto req = luks::LoadRequest<I>::create( + image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, m_passphrase, &this->m_crypto, + detected_format_name, on_finish); + req->send(); +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::LUKSEncryptionFormat<librbd::ImageCtx>; +template class librbd::crypto::luks::LUKS1EncryptionFormat<librbd::ImageCtx>; +template class librbd::crypto::luks::LUKS2EncryptionFormat<librbd::ImageCtx>; diff --git a/src/librbd/crypto/luks/LUKSEncryptionFormat.h b/src/librbd/crypto/luks/LUKSEncryptionFormat.h new file mode 100644 index 000000000..353bd8933 --- /dev/null +++ b/src/librbd/crypto/luks/LUKSEncryptionFormat.h @@ -0,0 +1,100 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_LUKS_ENCRYPTION_FORMAT_H +#define CEPH_LIBRBD_CRYPTO_LUKS_ENCRYPTION_FORMAT_H + +#include <string_view> +#include "include/rbd/librbd.hpp" +#include "librbd/crypto/CryptoInterface.h" +#include "librbd/crypto/EncryptionFormat.h" + +namespace librbd { + +struct ImageCtx; + +namespace crypto { +namespace luks { + +template <typename ImageCtxT> +class EncryptionFormat : public crypto::EncryptionFormat<ImageCtxT> { +public: + void flatten(ImageCtxT* ictx, Context* on_finish) override; + + CryptoInterface* get_crypto() override { + ceph_assert(m_crypto); + return m_crypto.get(); + } + +protected: + std::unique_ptr<CryptoInterface> m_crypto; +}; + +template <typename ImageCtxT> +class LUKSEncryptionFormat : public EncryptionFormat<ImageCtxT> { +public: + LUKSEncryptionFormat(std::string_view passphrase) + : m_passphrase(passphrase) {} + + std::unique_ptr<crypto::EncryptionFormat<ImageCtxT>> clone() const override { + return std::make_unique<LUKSEncryptionFormat>(m_passphrase); + } + + void format(ImageCtxT* ictx, Context* on_finish) override; + void load(ImageCtxT* ictx, std::string* detected_format_name, + Context* on_finish) override; + +private: + std::string_view m_passphrase; +}; + +template <typename ImageCtxT> +class LUKS1EncryptionFormat : public EncryptionFormat<ImageCtxT> { +public: + LUKS1EncryptionFormat(encryption_algorithm_t alg, std::string_view passphrase) + : m_alg(alg), m_passphrase(passphrase) {} + + std::unique_ptr<crypto::EncryptionFormat<ImageCtxT>> clone() const override { + return std::make_unique<LUKS1EncryptionFormat>(m_alg, m_passphrase); + } + + void format(ImageCtxT* ictx, Context* on_finish) override; + void load(ImageCtxT* ictx, std::string* detected_format_name, + Context* on_finish) override; + +private: + encryption_algorithm_t m_alg; + std::string_view m_passphrase; +}; + +template <typename ImageCtxT> +class LUKS2EncryptionFormat : public EncryptionFormat<ImageCtxT> { +public: + LUKS2EncryptionFormat(encryption_algorithm_t alg, std::string_view passphrase) + : m_alg(alg), m_passphrase(passphrase) {} + + std::unique_ptr<crypto::EncryptionFormat<ImageCtxT>> clone() const override { + return std::make_unique<LUKS2EncryptionFormat>(m_alg, m_passphrase); + } + + void format(ImageCtxT* ictx, Context* on_finish) override; + void load(ImageCtxT* ictx, std::string* detected_format_name, + Context* on_finish) override; + +private: + encryption_algorithm_t m_alg; + std::string_view m_passphrase; +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::luks::LUKSEncryptionFormat< + librbd::ImageCtx>; +extern template class librbd::crypto::luks::LUKS1EncryptionFormat< + librbd::ImageCtx>; +extern template class librbd::crypto::luks::LUKS2EncryptionFormat< + librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_ENCRYPTION_FORMAT_H diff --git a/src/librbd/crypto/luks/LoadRequest.cc b/src/librbd/crypto/luks/LoadRequest.cc new file mode 100644 index 000000000..b5e16f100 --- /dev/null +++ b/src/librbd/crypto/luks/LoadRequest.cc @@ -0,0 +1,272 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "LoadRequest.h" + +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/Utils.h" +#include "librbd/crypto/Utils.h" +#include "librbd/crypto/LoadRequest.h" +#include "librbd/crypto/luks/Magic.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ImageDispatchSpec.h" +#include "librbd/io/ReadResult.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::luks::LoadRequest: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace crypto { +namespace luks { + +using librbd::util::create_context_callback; + +template <typename I> +LoadRequest<I>::LoadRequest( + I* image_ctx, encryption_format_t format, std::string_view passphrase, + std::unique_ptr<CryptoInterface>* result_crypto, + std::string* detected_format_name, + Context* on_finish) : m_image_ctx(image_ctx), + m_format(format), + m_passphrase(passphrase), + m_on_finish(on_finish), + m_result_crypto(result_crypto), + m_detected_format_name(detected_format_name), + m_initial_read_size(DEFAULT_INITIAL_READ_SIZE), + m_header(image_ctx->cct), m_offset(0) { +} + +template <typename I> +void LoadRequest<I>::set_initial_read_size(uint64_t read_size) { + m_initial_read_size = read_size; +} + +template <typename I> +void LoadRequest<I>::send() { + auto ctx = create_context_callback< + LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this); + read(m_initial_read_size, ctx); +} + +template <typename I> +void LoadRequest<I>::read(uint64_t end_offset, Context* on_finish) { + auto length = end_offset - m_offset; + auto aio_comp = io::AioCompletion::create_and_start( + on_finish, librbd::util::get_image_ctx(m_image_ctx), + io::AIO_TYPE_READ); + ZTracer::Trace trace; + auto req = io::ImageDispatchSpec::create_read( + *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp, + {{m_offset, length}}, io::ImageArea::DATA, io::ReadResult{&m_bl}, + m_image_ctx->get_data_io_context(), 0, 0, trace); + req->send(); +} + +template <typename I> +bool LoadRequest<I>::handle_read(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_image_ctx->cct) << "error reading from image: " << cpp_strerror(r) + << dendl; + finish(r); + return false; + } + + // first, check LUKS magic at the beginning of the image + // If no magic is detected, caller may assume image is actually plaintext + if (m_offset == 0) { + if (Magic::is_luks(m_bl) > 0 || Magic::is_rbd_clone(m_bl) > 0) { + *m_detected_format_name = "LUKS"; + } else { + *m_detected_format_name = crypto::LoadRequest<I>::UNKNOWN_FORMAT; + finish(-EINVAL); + return false; + } + + if (m_image_ctx->parent != nullptr && Magic::is_rbd_clone(m_bl) > 0) { + r = Magic::replace_magic(m_image_ctx->cct, m_bl); + if (r < 0) { + m_image_ctx->image_lock.lock_shared(); + auto image_size = m_image_ctx->get_image_size(m_image_ctx->snap_id); + m_image_ctx->image_lock.unlock_shared(); + + auto max_header_size = std::min(MAXIMUM_HEADER_SIZE, image_size); + + if (r == -EINVAL && m_bl.length() < max_header_size) { + m_bl.clear(); + auto ctx = create_context_callback< + LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this); + read(max_header_size, ctx); + return false; + } + + lderr(m_image_ctx->cct) << "error replacing rbd clone magic: " + << cpp_strerror(r) << dendl; + finish(r); + return false; + } + } + } + + // setup interface with libcryptsetup + r = m_header.init(); + if (r < 0) { + finish(r); + return false; + } + + m_offset += m_bl.length(); + + // write header to libcryptsetup interface + r = m_header.write(m_bl); + if (r < 0) { + finish(r); + return false; + } + + m_bl.clear(); + + return true; +} + +template <typename I> +void LoadRequest<I>::handle_read_header(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (!handle_read(r)) { + return; + } + + const char* type; + switch (m_format) { + case RBD_ENCRYPTION_FORMAT_LUKS: + type = CRYPT_LUKS; + break; + case RBD_ENCRYPTION_FORMAT_LUKS1: + type = CRYPT_LUKS1; + break; + case RBD_ENCRYPTION_FORMAT_LUKS2: + type = CRYPT_LUKS2; + break; + default: + lderr(m_image_ctx->cct) << "unsupported format type: " << m_format + << dendl; + finish(-EINVAL); + return; + } + + // parse header via libcryptsetup + r = m_header.load(type); + if (r != 0) { + if (m_offset < MAXIMUM_HEADER_SIZE) { + // perhaps we did not feed the entire header to libcryptsetup, retry + auto ctx = create_context_callback< + LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this); + read(MAXIMUM_HEADER_SIZE, ctx); + return; + } + + finish(r); + return; + } + + // gets actual LUKS version (only used for logging) + ceph_assert(*m_detected_format_name == "LUKS"); + *m_detected_format_name = m_header.get_format_name(); + + auto cipher = m_header.get_cipher(); + if (strcmp(cipher, "aes") != 0) { + lderr(m_image_ctx->cct) << "unsupported cipher: " << cipher << dendl; + finish(-ENOTSUP); + return; + } + + auto cipher_mode = m_header.get_cipher_mode(); + if (strcmp(cipher_mode, "xts-plain64") != 0) { + lderr(m_image_ctx->cct) << "unsupported cipher mode: " << cipher_mode + << dendl; + finish(-ENOTSUP); + return; + } + + m_image_ctx->image_lock.lock_shared(); + uint64_t image_size = m_image_ctx->get_image_size(CEPH_NOSNAP); + m_image_ctx->image_lock.unlock_shared(); + + if (m_header.get_data_offset() > image_size) { + lderr(m_image_ctx->cct) << "image is too small, data offset " + << m_header.get_data_offset() << dendl; + finish(-EINVAL); + return; + } + + uint64_t stripe_period = m_image_ctx->get_stripe_period(); + if (m_header.get_data_offset() % stripe_period != 0) { + lderr(m_image_ctx->cct) << "incompatible stripe pattern, data offset " + << m_header.get_data_offset() << dendl; + finish(-EINVAL); + return; + } + + read_volume_key(); + return; +} + +template <typename I> +void LoadRequest<I>::handle_read_keyslots(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + if (!handle_read(r)) { + return; + } + + read_volume_key(); +} + +template <typename I> +void LoadRequest<I>::read_volume_key() { + char volume_key[64]; + size_t volume_key_size = sizeof(volume_key); + + auto r = m_header.read_volume_key( + m_passphrase.data(), m_passphrase.size(), + reinterpret_cast<char*>(volume_key), &volume_key_size); + if (r != 0) { + auto keyslots_end_offset = m_header.get_data_offset(); + if (m_offset < keyslots_end_offset) { + // perhaps we did not feed the the necessary keyslot, retry + auto ctx = create_context_callback< + LoadRequest<I>, &LoadRequest<I>::handle_read_keyslots>(this); + read(keyslots_end_offset, ctx); + return; + } + + finish(r); + return; + } + + r = util::build_crypto( + m_image_ctx->cct, reinterpret_cast<unsigned char*>(volume_key), + volume_key_size, m_header.get_sector_size(), + m_header.get_data_offset(), m_result_crypto); + ceph_memzero_s(volume_key, 64, 64); + finish(r); +} + +template <typename I> +void LoadRequest<I>::finish(int r) { + ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::LoadRequest<librbd::ImageCtx>; diff --git a/src/librbd/crypto/luks/LoadRequest.h b/src/librbd/crypto/luks/LoadRequest.h new file mode 100644 index 000000000..60ed9a4a4 --- /dev/null +++ b/src/librbd/crypto/luks/LoadRequest.h @@ -0,0 +1,71 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_LUKS_LOAD_REQUEST_H +#define CEPH_LIBRBD_CRYPTO_LUKS_LOAD_REQUEST_H + +#include <string_view> +#include "include/rbd/librbd.hpp" +#include "librbd/ImageCtx.h" +#include "librbd/crypto/CryptoInterface.h" +#include "librbd/crypto/luks/Header.h" + +namespace librbd { + +class ImageCtx; + +namespace crypto { +namespace luks { + +// max header size in LUKS1/2 (excl. keyslots) is 4MB +const uint64_t MAXIMUM_HEADER_SIZE = 4 * 1024 * 1024; +// default header size in LUKS2 2 X 16KB + 1 X 256KB keyslot +const uint64_t DEFAULT_INITIAL_READ_SIZE = 288 * 1024; + +template <typename I> +class LoadRequest { +public: + static LoadRequest* create( + I* image_ctx, encryption_format_t format, + std::string_view passphrase, + std::unique_ptr<CryptoInterface>* result_crypto, + std::string* detected_format_name, + Context* on_finish) { + return new LoadRequest(image_ctx, format, passphrase, result_crypto, + detected_format_name, on_finish); + } + + LoadRequest(I* image_ctx, encryption_format_t format, + std::string_view passphrase, + std::unique_ptr<CryptoInterface>* result_crypto, + std::string* detected_format_name, Context* on_finish); + void send(); + void finish(int r); + void set_initial_read_size(uint64_t read_size); + +private: + I* m_image_ctx; + encryption_format_t m_format; + std::string_view m_passphrase; + Context* m_on_finish; + ceph::bufferlist m_bl; + std::unique_ptr<CryptoInterface>* m_result_crypto; + std::string* m_detected_format_name; + uint64_t m_initial_read_size; + Header m_header; + uint64_t m_offset; + + void read(uint64_t end_offset, Context* on_finish); + bool handle_read(int r); + void handle_read_header(int r); + void handle_read_keyslots(int r); + void read_volume_key(); +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::luks::LoadRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_LOAD_REQUEST_H diff --git a/src/librbd/crypto/luks/Magic.cc b/src/librbd/crypto/luks/Magic.cc new file mode 100644 index 000000000..bc5e19704 --- /dev/null +++ b/src/librbd/crypto/luks/Magic.cc @@ -0,0 +1,139 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "Magic.h" + +#include "common/dout.h" +#include "common/errno.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::luks::Magic: " << __func__ \ + << ": " + +namespace librbd { +namespace crypto { +namespace luks { + +namespace { + +constexpr uint64_t MAGIC_LENGTH = 6; +const std::string LUKS_MAGIC = "LUKS\xba\xbe"; +const std::string RBD_CLONE_MAGIC = "RBDL\xba\xbe"; + +} // anonymous namespace + +int Magic::read(ceph::bufferlist &bl, uint32_t bl_off, + uint32_t read_size, char* result) { + if (bl_off + read_size > bl.length()) { + return -EINVAL; + } + + memcpy(result, bl.c_str() + bl_off, read_size); + return 0; +} + +int Magic::cmp(ceph::bufferlist &bl, uint32_t bl_off, + const std::string &cmp_str) { + auto cmp_length = cmp_str.length(); + + if (bl_off + cmp_length > bl.length()) { + return -EINVAL; + } + + if (memcmp(bl.c_str() + bl_off, cmp_str.c_str(), cmp_length)) { + return 0; + } + + return 1; +} + +int Magic::is_luks(ceph::bufferlist& bl) { + return cmp(bl, 0, LUKS_MAGIC); +} + +int Magic::is_rbd_clone(ceph::bufferlist& bl) { + return cmp(bl, 0, RBD_CLONE_MAGIC); +} + +void Magic::transform_secondary_header_magic(char* magic) { + std::swap(magic[0], magic[3]); + std::swap(magic[1], magic[2]); +} + +int Magic::replace_magic(CephContext* cct, ceph::bufferlist& bl) { + const std::string *old_magic, *new_magic; + if (is_luks(bl) > 0) { + old_magic = &LUKS_MAGIC; + new_magic = &RBD_CLONE_MAGIC; + } else if (is_rbd_clone(bl) > 0) { + old_magic = &RBD_CLONE_MAGIC; + new_magic = &LUKS_MAGIC; + } else { + lderr(cct) << "invalid magic: " << dendl; + return -EILSEQ; + } + + // read luks version + uint16_t version; + auto r = read(bl, MAGIC_LENGTH, sizeof(version), (char*)&version); + if (r < 0) { + lderr(cct) << "cannot read header version: " << cpp_strerror(r) << dendl; + return r; + } + boost::endian::big_to_native_inplace(version); + + switch (version) { + case 1: { + // LUKS1, no secondary header + break; + } + case 2: { + // LUKS2, secondary header follows primary header + // read header size + uint64_t hdr_size; + r = read(bl, MAGIC_LENGTH + sizeof(version), sizeof(hdr_size), + (char*)&hdr_size); + if (r < 0) { + lderr(cct) << "cannot read header size: " << cpp_strerror(r) << dendl; + return r; + } + boost::endian::big_to_native_inplace(hdr_size); + + if ((uint32_t)hdr_size + MAGIC_LENGTH > bl.length()) { + ldout(cct, 20) << "cannot replace secondary header magic" << dendl; + return -EINVAL; + } + + // check secondary header magic + auto secondary_header_magic = bl.c_str() + hdr_size; + transform_secondary_header_magic(secondary_header_magic); + auto is_secondary_header_magic_valid = + !memcmp(secondary_header_magic, old_magic->c_str(), MAGIC_LENGTH); + if (!is_secondary_header_magic_valid) { + transform_secondary_header_magic(secondary_header_magic); + lderr(cct) << "invalid secondary header magic" << dendl; + return -EILSEQ; + } + + // replace secondary header magic + memcpy(secondary_header_magic, new_magic->c_str(), MAGIC_LENGTH); + transform_secondary_header_magic(secondary_header_magic); + + break; + } + default: { + lderr(cct) << "bad header version: " << version << dendl; + return -EINVAL; + } + } + + // switch primary header magic + memcpy(bl.c_str(), new_magic->c_str(), MAGIC_LENGTH); + + return 0; +} + +} // namespace luks +} // namespace crypto +} // namespace librbd diff --git a/src/librbd/crypto/luks/Magic.h b/src/librbd/crypto/luks/Magic.h new file mode 100644 index 000000000..ad06e67f5 --- /dev/null +++ b/src/librbd/crypto/luks/Magic.h @@ -0,0 +1,32 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_LUKS_MAGIC_H +#define CEPH_LIBRBD_CRYPTO_LUKS_MAGIC_H + +#include "common/ceph_context.h" +#include "include/buffer.h" + +namespace librbd { +namespace crypto { +namespace luks { + +class Magic { +public: + static int is_luks(ceph::bufferlist& bl); + static int is_rbd_clone(ceph::bufferlist& bl); + + static int replace_magic(CephContext* cct, ceph::bufferlist& bl); +private: + static int read(ceph::bufferlist& bl, uint32_t bl_off, + uint32_t read_size, char* result); + static int cmp(ceph::bufferlist& bl, uint32_t bl_off, + const std::string& cmp_str); + static void transform_secondary_header_magic(char* magic); +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_MAGIC_H |