From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/librbd/crypto/luks/EncryptionFormat.cc | 48 ++++++ src/librbd/crypto/luks/EncryptionFormat.h | 67 ++++++++ src/librbd/crypto/luks/FormatRequest.cc | 178 ++++++++++++++++++++ src/librbd/crypto/luks/FormatRequest.h | 58 +++++++ src/librbd/crypto/luks/Header.cc | 256 +++++++++++++++++++++++++++++ src/librbd/crypto/luks/Header.h | 51 ++++++ src/librbd/crypto/luks/LoadRequest.cc | 196 ++++++++++++++++++++++ src/librbd/crypto/luks/LoadRequest.h | 66 ++++++++ 8 files changed, 920 insertions(+) create mode 100644 src/librbd/crypto/luks/EncryptionFormat.cc create mode 100644 src/librbd/crypto/luks/EncryptionFormat.h create mode 100644 src/librbd/crypto/luks/FormatRequest.cc create mode 100644 src/librbd/crypto/luks/FormatRequest.h create mode 100644 src/librbd/crypto/luks/Header.cc create mode 100644 src/librbd/crypto/luks/Header.h create mode 100644 src/librbd/crypto/luks/LoadRequest.cc create mode 100644 src/librbd/crypto/luks/LoadRequest.h (limited to 'src/librbd/crypto/luks') diff --git a/src/librbd/crypto/luks/EncryptionFormat.cc b/src/librbd/crypto/luks/EncryptionFormat.cc new file mode 100644 index 000000000..8b1b1580c --- /dev/null +++ b/src/librbd/crypto/luks/EncryptionFormat.cc @@ -0,0 +1,48 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "EncryptionFormat.h" +#include "include/compat.h" +#include "librbd/crypto/luks/FormatRequest.h" +#include "librbd/crypto/luks/LoadRequest.h" + +namespace librbd { +namespace crypto { +namespace luks { + +template +EncryptionFormat::EncryptionFormat( + encryption_algorithm_t alg, + std::string&& passphrase) : m_alg(alg), + m_passphrase(std::move(passphrase)) { +} + +template +EncryptionFormat::~EncryptionFormat() { + ceph_memzero_s( + &m_passphrase[0], m_passphrase.capacity(), m_passphrase.size()); +} + +template +void EncryptionFormat::format(I* image_ctx, Context* on_finish) { + auto req = luks::FormatRequest::create( + image_ctx, get_format(), m_alg, std::move(m_passphrase), &m_crypto, + on_finish, false); + req->send(); +} + +template +void EncryptionFormat::load(I* image_ctx, Context* on_finish) { + auto req = luks::LoadRequest::create( + image_ctx, get_format(), std::move(m_passphrase), &m_crypto, + on_finish); + req->send(); +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::EncryptionFormat; +template class librbd::crypto::luks::LUKS1EncryptionFormat; +template class librbd::crypto::luks::LUKS2EncryptionFormat; diff --git a/src/librbd/crypto/luks/EncryptionFormat.h b/src/librbd/crypto/luks/EncryptionFormat.h new file mode 100644 index 000000000..8c45cf9ca --- /dev/null +++ b/src/librbd/crypto/luks/EncryptionFormat.h @@ -0,0 +1,67 @@ +// -*- 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 "include/rbd/librbd.hpp" +#include "librbd/crypto/EncryptionFormat.h" + +namespace librbd { + +struct ImageCtx; + +namespace crypto { +namespace luks { + +template +class EncryptionFormat : public crypto::EncryptionFormat { + +public: + EncryptionFormat(encryption_algorithm_t alg, std::string&& passphrase); + ~EncryptionFormat(); + + void format(ImageCtxT* ictx, Context* on_finish) override; + void load(ImageCtxT* ictx, Context* on_finish) override; + + ceph::ref_t get_crypto() override { + return m_crypto; + } + +private: + virtual encryption_format_t get_format() = 0; + + encryption_algorithm_t m_alg; + std::string m_passphrase; + ceph::ref_t m_crypto; +}; + +template +class LUKS1EncryptionFormat : public EncryptionFormat { + using EncryptionFormat::EncryptionFormat; + + encryption_format_t get_format() override { + return RBD_ENCRYPTION_FORMAT_LUKS1; + } +}; + +template +class LUKS2EncryptionFormat : public EncryptionFormat { + using EncryptionFormat::EncryptionFormat; + + encryption_format_t get_format() override { + return RBD_ENCRYPTION_FORMAT_LUKS2; + } +}; + +} // namespace luks +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::luks::EncryptionFormat; +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/FormatRequest.cc b/src/librbd/crypto/luks/FormatRequest.cc new file mode 100644 index 000000000..be2452fd9 --- /dev/null +++ b/src/librbd/crypto/luks/FormatRequest.cc @@ -0,0 +1,178 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "FormatRequest.h" + +#include +#include +#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/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 +FormatRequest::FormatRequest( + I* image_ctx, encryption_format_t format, encryption_algorithm_t alg, + std::string&& passphrase, ceph::ref_t* result_crypto, + Context* on_finish, + bool insecure_fast_mode) : m_image_ctx(image_ctx), m_format(format), + m_alg(alg), + m_passphrase(std::move(passphrase)), + m_result_crypto(result_crypto), + m_on_finish(on_finish), + m_insecure_fast_mode(insecure_fast_mode), + m_header(image_ctx->cct) { +} + +template +void FormatRequest::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) + r = m_header.format(type, cipher, reinterpret_cast(key), key_size, + "xts-plain64", sector_size, + m_image_ctx->get_object_size(), 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 more than " + << m_header.get_data_offset() << " bytes" << dendl; + finish(-ENOSPC); + return; + } + + // add keyslot (volume key encrypted with passphrase) + r = m_header.add_keyslot(m_passphrase.c_str(), 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; + } + + // write header to offset 0 of the image + auto ctx = create_context_callback< + FormatRequest, &FormatRequest::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()}}, std::move(bl), + m_image_ctx->get_data_io_context(), 0, trace); + req->send(); +} + +template +void FormatRequest::handle_write_header(int r) { + if (r < 0) { + lderr(m_image_ctx->cct) << "error writing header to image: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } + + finish(0); +} + +template +void FormatRequest::finish(int r) { + ceph_memzero_s( + &m_passphrase[0], m_passphrase.capacity(), m_passphrase.size()); + m_on_finish->complete(r); + delete this; +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::FormatRequest; diff --git a/src/librbd/crypto/luks/FormatRequest.h b/src/librbd/crypto/luks/FormatRequest.h new file mode 100644 index 000000000..a782dc818 --- /dev/null +++ b/src/librbd/crypto/luks/FormatRequest.h @@ -0,0 +1,58 @@ +// -*- 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 "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 +class FormatRequest { +public: + static FormatRequest* create( + I* image_ctx, encryption_format_t format, + encryption_algorithm_t alg, std::string&& passphrase, + ceph::ref_t* result_crypto, Context* on_finish, + bool insecure_fast_mode) { + return new FormatRequest(image_ctx, format, alg, std::move(passphrase), + result_crypto, on_finish, insecure_fast_mode); + } + + FormatRequest(I* image_ctx, encryption_format_t format, + encryption_algorithm_t alg, std::string&& passphrase, + ceph::ref_t* 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 m_passphrase; + ceph::ref_t* 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; + +#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..6d00074ef --- /dev/null +++ b/src/librbd/crypto/luks/Header.cc @@ -0,0 +1,256 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "Header.h" + +#include +#include +#include +#include +#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() { + // 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()) { + 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; + +#ifdef LIBCRYPTSETUP_LEGACY_DATA_ALIGNMENT + size_t converted_data_alignment = data_alignment / sector_size; +#else + size_t converted_data_alignment = data_alignment / 512; +#endif + + 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) { + lderr(m_cct) << "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) { + lderr(m_cct) << "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); +} + +} // 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..cee80a8e4 --- /dev/null +++ b/src/librbd/crypto/luks/Header.h @@ -0,0 +1,51 @@ +// -*- 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 +#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(); + +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/LoadRequest.cc b/src/librbd/crypto/luks/LoadRequest.cc new file mode 100644 index 000000000..10339469d --- /dev/null +++ b/src/librbd/crypto/luks/LoadRequest.cc @@ -0,0 +1,196 @@ +// -*- 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/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 +LoadRequest::LoadRequest( + I* image_ctx, encryption_format_t format, std::string&& passphrase, + ceph::ref_t* result_crypto, + Context* on_finish) : m_image_ctx(image_ctx), + m_format(format), + m_passphrase(std::move(passphrase)), + m_on_finish(on_finish), + m_result_crypto(result_crypto), + m_initial_read_size(DEFAULT_INITIAL_READ_SIZE), + m_header(image_ctx->cct), m_offset(0) { +} + +template +void LoadRequest::set_initial_read_size(uint64_t read_size) { + m_initial_read_size = read_size; +} + +template +void LoadRequest::send() { + // setup interface with libcryptsetup + auto r = m_header.init(); + if (r < 0) { + finish(r); + return; + } + + auto ctx = create_context_callback< + LoadRequest, &LoadRequest::handle_read_header>(this); + read(m_initial_read_size, ctx); +} + +template +void LoadRequest::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::ReadResult{&m_bl}, + m_image_ctx->get_data_io_context(), 0, 0, trace); + req->send(); +} + +template +bool LoadRequest::handle_read(int r) { + if (r < 0) { + lderr(m_image_ctx->cct) << "error reading from image: " << cpp_strerror(r) + << dendl; + finish(r); + return false; + } + + // write header to libcryptsetup interface + r = m_header.write(m_bl); + if (r < 0) { + finish(r); + return false; + } + + m_offset += m_bl.length(); + m_bl.clear(); + return true; +} + +template +void LoadRequest::handle_read_header(int r) { + if (!handle_read(r)) { + return; + } + + const char* type; + switch (m_format) { + 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, &LoadRequest::handle_read_header>(this); + read(MAXIMUM_HEADER_SIZE, ctx); + return; + } + + finish(r); + return; + } + + 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; + } + + read_volume_key(); + return; +} + +template +void LoadRequest::handle_read_keyslots(int r) { + if (!handle_read(r)) { + return; + } + + read_volume_key(); +} + +template +void LoadRequest::read_volume_key() { + char volume_key[64]; + size_t volume_key_size = sizeof(volume_key); + + auto r = m_header.read_volume_key( + m_passphrase.c_str(), m_passphrase.size(), + reinterpret_cast(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, &LoadRequest::handle_read_keyslots>(this); + read(keyslots_end_offset, ctx); + return; + } + + finish(r); + return; + } + + r = util::build_crypto( + m_image_ctx->cct, reinterpret_cast(volume_key), + volume_key_size, m_header.get_sector_size(), + m_header.get_data_offset(), m_result_crypto); + finish(r); +} + +template +void LoadRequest::finish(int r) { + ceph_memzero_s(&m_passphrase[0], m_passphrase.size(), m_passphrase.size()); + m_on_finish->complete(r); + delete this; +} + +} // namespace luks +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::luks::LoadRequest; diff --git a/src/librbd/crypto/luks/LoadRequest.h b/src/librbd/crypto/luks/LoadRequest.h new file mode 100644 index 000000000..340e89503 --- /dev/null +++ b/src/librbd/crypto/luks/LoadRequest.h @@ -0,0 +1,66 @@ +// -*- 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 "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 +class LoadRequest { +public: + static LoadRequest* create( + I* image_ctx, encryption_format_t format, std::string&& passphrase, + ceph::ref_t* result_crypto, Context* on_finish) { + return new LoadRequest(image_ctx, format, std::move(passphrase), + result_crypto, on_finish); + } + + LoadRequest(I* image_ctx, encryption_format_t format, + std::string&& passphrase, + ceph::ref_t* result_crypto, + 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 m_passphrase; + Context* m_on_finish; + ceph::bufferlist m_bl; + ceph::ref_t* m_result_crypto; + 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; + +#endif // CEPH_LIBRBD_CRYPTO_LUKS_LOAD_REQUEST_H -- cgit v1.2.3