From bb8c66772abaa5890f4caa23bda0eb2ad3279221 Mon Sep 17 00:00:00 2001 From: Or Ozeri Date: Tue, 9 Mar 2021 22:14:49 +0200 Subject: [PATCH] librbd: crypto format api semantics change This commit alters the semantics of the encryption format api to also load the encryption after format completes. Additionally, several other small changes in librbd crypto are included, in preparation of supporting clone formatting. Signed-off-by: Or Ozeri --- src/include/rbd/librbd.h | 1 + src/librbd/CMakeLists.txt | 2 + src/librbd/ImageCtx.h | 3 + src/librbd/api/Image.cc | 5 + src/librbd/crypto/BlockCrypto.h | 8 + src/librbd/crypto/CryptoContextPool.h | 6 + src/librbd/crypto/CryptoInterface.h | 2 + src/librbd/crypto/DataCryptor.h | 3 + src/librbd/crypto/EncryptionFormat.h | 6 +- src/librbd/crypto/FormatRequest.cc | 77 +++++++-- src/librbd/crypto/FormatRequest.h | 5 + src/librbd/crypto/LoadRequest.cc | 20 +-- src/librbd/crypto/LoadRequest.h | 1 - src/librbd/crypto/ShutDownCryptoRequest.cc | 105 ++++++++++++ src/librbd/crypto/ShutDownCryptoRequest.h | 45 ++++++ src/librbd/crypto/Utils.cc | 73 +++++++++ src/librbd/crypto/Utils.h | 29 ++++ src/librbd/crypto/luks/EncryptionFormat.cc | 17 +- src/librbd/crypto/luks/EncryptionFormat.h | 9 +- src/librbd/crypto/luks/FormatRequest.cc | 34 +++- src/librbd/crypto/luks/FormatRequest.h | 8 +- src/librbd/crypto/luks/Header.cc | 9 +- src/librbd/crypto/luks/Header.h | 4 +- src/librbd/crypto/luks/LoadRequest.cc | 44 +----- src/librbd/crypto/openssl/DataCryptor.cc | 11 +- src/librbd/crypto/openssl/DataCryptor.h | 3 + src/librbd/migration/S3Stream.cc | 2 +- src/test/librbd/CMakeLists.txt | 1 + .../crypto/luks/test_mock_FormatRequest.cc | 29 ++-- .../crypto/luks/test_mock_LoadRequest.cc | 4 +- .../librbd/crypto/test_mock_FormatRequest.cc | 149 +++++++++++++----- .../librbd/crypto/test_mock_LoadRequest.cc | 58 +++---- .../crypto/test_mock_ShutDownCryptoRequest.cc | 141 +++++++++++++++++ src/test/librbd/mock/MockImageCtx.h | 2 + .../librbd/mock/crypto/MockCryptoInterface.h | 2 + src/test/librbd/mock/crypto/MockDataCryptor.h | 2 + .../librbd/mock/crypto/MockEncryptionFormat.h | 5 +- src/test/librbd/test_librbd.cc | 26 ++- 38 files changed, 772 insertions(+), 179 deletions(-) create mode 100644 src/librbd/crypto/ShutDownCryptoRequest.cc create mode 100644 src/librbd/crypto/ShutDownCryptoRequest.h create mode 100644 src/librbd/crypto/Utils.cc create mode 100644 src/librbd/crypto/Utils.h create mode 100644 src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 29e89c475e0..47d9e35933e 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -48,6 +48,7 @@ extern "C" { #define LIBRBD_SUPPORTS_WATCH 0 #define LIBRBD_SUPPORTS_WRITESAME 1 #define LIBRBD_SUPPORTS_WRITE_ZEROES 1 +#define LIBRBD_SUPPORTS_ENCRYPTION 1 #if __GNUC__ >= 4 #define CEPH_RBD_API __attribute__ ((visibility ("default"))) diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index 0ba78390c8a..ea75fc418dd 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -66,6 +66,8 @@ set(librbd_internal_srcs crypto/CryptoObjectDispatch.cc crypto/FormatRequest.cc crypto/LoadRequest.cc + crypto/ShutDownCryptoRequest.cc + crypto/Utils.cc crypto/openssl/DataCryptor.cc deep_copy/ImageCopyRequest.cc deep_copy/MetadataCopyRequest.cc diff --git a/src/librbd/ImageCtx.h b/src/librbd/ImageCtx.h index 5623f2e768f..31d9a67547d 100644 --- a/src/librbd/ImageCtx.h +++ b/src/librbd/ImageCtx.h @@ -57,6 +57,7 @@ namespace librbd { template class PluginRegistry; namespace asio { struct ContextWQ; } + namespace crypto { class CryptoInterface; } namespace exclusive_lock { struct Policy; } namespace io { class AioCompletion; @@ -233,6 +234,8 @@ namespace librbd { ZTracer::Endpoint trace_endpoint; + crypto::CryptoInterface* crypto = nullptr; + // unit test mock helpers static ImageCtx* create(const std::string &image_name, const std::string &image_id, diff --git a/src/librbd/api/Image.cc b/src/librbd/api/Image.cc index b234d4f18f7..7df0f4fc69e 100644 --- a/src/librbd/api/Image.cc +++ b/src/librbd/api/Image.cc @@ -938,6 +938,11 @@ template int Image::encryption_format(I* ictx, encryption_format_t format, encryption_options_t opts, size_t opts_size, bool c_api) { + if (ictx->parent != nullptr) { + lderr(ictx->cct) << "cannot format a cloned image" << dendl; + return -ENOTSUP; + } + crypto::EncryptionFormat* result_format; auto r = util::create_encryption_format( ictx->cct, format, opts, opts_size, c_api, &result_format); diff --git a/src/librbd/crypto/BlockCrypto.h b/src/librbd/crypto/BlockCrypto.h index 31b39a2b3a1..0bbdd2524cd 100644 --- a/src/librbd/crypto/BlockCrypto.h +++ b/src/librbd/crypto/BlockCrypto.h @@ -34,6 +34,14 @@ public: return m_data_offset; } + const unsigned char* get_key() const override { + return m_data_cryptor->get_key(); + } + + int get_key_length() const override { + return m_data_cryptor->get_key_length(); + } + private: CephContext* m_cct; DataCryptor* m_data_cryptor; diff --git a/src/librbd/crypto/CryptoContextPool.h b/src/librbd/crypto/CryptoContextPool.h index ce5abfa2ef4..c0ebce0c233 100644 --- a/src/librbd/crypto/CryptoContextPool.h +++ b/src/librbd/crypto/CryptoContextPool.h @@ -28,6 +28,12 @@ public: inline uint32_t get_iv_size() const override { return m_data_cryptor->get_iv_size(); } + inline int get_key_length() const override { + return m_data_cryptor->get_key_length(); + } + inline const unsigned char* get_key() const override { + return m_data_cryptor->get_key(); + } inline int init_context(T* ctx, const unsigned char* iv, uint32_t iv_length) const override { return m_data_cryptor->init_context(ctx, iv, iv_length); diff --git a/src/librbd/crypto/CryptoInterface.h b/src/librbd/crypto/CryptoInterface.h index 8b1f57924b1..170a5bf28e9 100644 --- a/src/librbd/crypto/CryptoInterface.h +++ b/src/librbd/crypto/CryptoInterface.h @@ -19,6 +19,8 @@ public: virtual int decrypt(ceph::bufferlist* data, uint64_t image_offset) = 0; virtual uint64_t get_block_size() const = 0; virtual uint64_t get_data_offset() const = 0; + virtual const unsigned char* get_key() const = 0; + virtual int get_key_length() const = 0; inline std::pair get_pre_and_post_align( uint64_t off, uint64_t len) { diff --git a/src/librbd/crypto/DataCryptor.h b/src/librbd/crypto/DataCryptor.h index cf293858907..ffcc57ce4c9 100644 --- a/src/librbd/crypto/DataCryptor.h +++ b/src/librbd/crypto/DataCryptor.h @@ -19,6 +19,9 @@ public: virtual uint32_t get_block_size() const = 0; virtual uint32_t get_iv_size() const = 0; + virtual const unsigned char* get_key() const = 0; + virtual int get_key_length() const = 0; + virtual T* get_context(CipherMode mode) = 0; virtual void return_context(T* ctx, CipherMode mode) = 0; diff --git a/src/librbd/crypto/EncryptionFormat.h b/src/librbd/crypto/EncryptionFormat.h index 1ac962216c8..ba57a92524f 100644 --- a/src/librbd/crypto/EncryptionFormat.h +++ b/src/librbd/crypto/EncryptionFormat.h @@ -19,9 +19,9 @@ struct EncryptionFormat { } virtual void format(ImageCtxT* ictx, Context* on_finish) = 0; - virtual void load(ImageCtxT* ictx, - ceph::ref_t* result_crypto, - Context* on_finish) = 0; + virtual void load(ImageCtxT* ictx, Context* on_finish) = 0; + + virtual ceph::ref_t get_crypto() = 0; }; } // namespace crypto diff --git a/src/librbd/crypto/FormatRequest.cc b/src/librbd/crypto/FormatRequest.cc index d23f95096bb..53dda58aae0 100644 --- a/src/librbd/crypto/FormatRequest.cc +++ b/src/librbd/crypto/FormatRequest.cc @@ -7,7 +7,12 @@ #include "common/errno.h" #include "librbd/ImageCtx.h" #include "librbd/Utils.h" +#include "librbd/crypto/ShutDownCryptoRequest.h" +#include "librbd/crypto/Utils.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ImageDispatchSpec.h" #include "librbd/io/ObjectDispatcherInterface.h" +#include "librbd/io/Types.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix @@ -29,33 +34,81 @@ FormatRequest::FormatRequest( template void FormatRequest::send() { - if (m_image_ctx->io_object_dispatcher->exists( - io::OBJECT_DISPATCH_LAYER_CRYPTO)) { - lderr(m_image_ctx->cct) << "cannot format with already loaded encryption" - << dendl; - finish(-EEXIST); + if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) { + lderr(m_image_ctx->cct) << "cannot use encryption with journal" << dendl; + finish(-ENOTSUP); return; } - if (m_image_ctx->parent != nullptr) { - lderr(m_image_ctx->cct) << "cannot format a cloned image" << dendl; - finish(-ENOTSUP); + if (m_image_ctx->crypto == nullptr) { + format(); return; } - if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) { - lderr(m_image_ctx->cct) << "cannot use encryption with journal" << dendl; - finish(-ENOTSUP); + auto ctx = create_context_callback< + FormatRequest, &FormatRequest::handle_shutdown_crypto>(this); + auto *req = ShutDownCryptoRequest::create(m_image_ctx, ctx); + req->send(); +} + +template +void FormatRequest::handle_shutdown_crypto(int r) { + if (r != 0) { + lderr(m_image_ctx->cct) << "unable to unload existing crypto: " + << cpp_strerror(r) << dendl; + finish(r); return; } + format(); +} + +template +void FormatRequest::format() { auto ctx = create_context_callback< - FormatRequest, &FormatRequest::finish>(this); + FormatRequest, &FormatRequest::handle_format>(this); m_format->format(m_image_ctx, ctx); } +template +void FormatRequest::handle_format(int r) { + if (r != 0) { + lderr(m_image_ctx->cct) << "unable to format image: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } + + flush(); +} + +template +void FormatRequest::flush() { + auto ctx = create_context_callback< + FormatRequest, &FormatRequest::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 +void FormatRequest::handle_flush(int r) { + if (r != 0) { + lderr(m_image_ctx->cct) << "unable to flush image: " << cpp_strerror(r) + << dendl; + } + + finish(r); +} + template void FormatRequest::finish(int r) { + if (r == 0) { + util::set_crypto(m_image_ctx, m_format->get_crypto()); + } m_on_finish->complete(r); delete this; } diff --git a/src/librbd/crypto/FormatRequest.h b/src/librbd/crypto/FormatRequest.h index fc4263a9ec5..cfd7978d80b 100644 --- a/src/librbd/crypto/FormatRequest.h +++ b/src/librbd/crypto/FormatRequest.h @@ -27,6 +27,11 @@ public: FormatRequest(I* image_ctx, std::unique_ptr> format, Context* on_finish); void send(); + void handle_shutdown_crypto(int r); + void format(); + void handle_format(int r); + void flush(); + void handle_flush(int r); void finish(int r); private: diff --git a/src/librbd/crypto/LoadRequest.cc b/src/librbd/crypto/LoadRequest.cc index 591e08d57a0..c42011f6258 100644 --- a/src/librbd/crypto/LoadRequest.cc +++ b/src/librbd/crypto/LoadRequest.cc @@ -7,10 +7,7 @@ #include "common/errno.h" #include "librbd/Utils.h" #include "librbd/ImageCtx.h" -#include "librbd/crypto/CryptoImageDispatch.h" -#include "librbd/crypto/CryptoObjectDispatch.h" -#include "librbd/io/ImageDispatcherInterface.h" -#include "librbd/io/ObjectDispatcherInterface.h" +#include "librbd/crypto/Utils.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix @@ -32,8 +29,7 @@ LoadRequest::LoadRequest( template void LoadRequest::send() { - if (m_image_ctx->io_object_dispatcher->exists( - io::OBJECT_DISPATCH_LAYER_CRYPTO)) { + if (m_image_ctx->crypto != nullptr) { lderr(m_image_ctx->cct) << "encryption already loaded" << dendl; finish(-EEXIST); return; @@ -52,22 +48,18 @@ void LoadRequest::send() { auto ctx = create_context_callback< LoadRequest, &LoadRequest::finish>(this); - m_format->load(m_image_ctx, &m_crypto, ctx); + m_format->load(m_image_ctx, ctx); } template void LoadRequest::finish(int r) { + if (r == 0) { // load crypto layers to image and its ancestors + auto crypto = m_format->get_crypto(); auto ictx = m_image_ctx; while (ictx != nullptr) { - auto object_dispatch = CryptoObjectDispatch::create( - ictx, m_crypto); - auto image_dispatch = CryptoImageDispatch::create( - m_crypto->get_data_offset()); - ictx->io_object_dispatcher->register_dispatch(object_dispatch); - ictx->io_image_dispatcher->register_dispatch(image_dispatch); - + util::set_crypto(ictx, crypto); ictx = ictx->parent; } } diff --git a/src/librbd/crypto/LoadRequest.h b/src/librbd/crypto/LoadRequest.h index 6bbe980ea79..50d1dad84ec 100644 --- a/src/librbd/crypto/LoadRequest.h +++ b/src/librbd/crypto/LoadRequest.h @@ -34,7 +34,6 @@ private: I* m_image_ctx; std::unique_ptr> m_format; Context* m_on_finish; - ceph::ref_t m_crypto; }; } // namespace crypto diff --git a/src/librbd/crypto/ShutDownCryptoRequest.cc b/src/librbd/crypto/ShutDownCryptoRequest.cc new file mode 100644 index 00000000000..dad6e2fc192 --- /dev/null +++ b/src/librbd/crypto/ShutDownCryptoRequest.cc @@ -0,0 +1,105 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "ShutDownCryptoRequest.h" + +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" +#include "librbd/Utils.h" +#include "librbd/crypto/CryptoImageDispatch.h" +#include "librbd/crypto/CryptoObjectDispatch.h" +#include "librbd/io/ImageDispatcherInterface.h" +#include "librbd/io/ObjectDispatcherInterface.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::ShutDownCryptoRequest: " \ + << this << " " << __func__ << ": " + +namespace librbd { +namespace crypto { + +using librbd::util::create_context_callback; + +template +ShutDownCryptoRequest::ShutDownCryptoRequest( + I* image_ctx, Context* on_finish) : m_image_ctx(image_ctx), + m_on_finish(on_finish) { +} + +template +void ShutDownCryptoRequest::send() { + m_image_ctx->image_lock.lock_shared(); + m_crypto = m_image_ctx->crypto; + m_image_ctx->image_lock.unlock_shared(); + + shut_down_object_dispatch(); +} + +template +void ShutDownCryptoRequest::shut_down_object_dispatch() { + auto ctx = create_context_callback< + ShutDownCryptoRequest, + &ShutDownCryptoRequest::handle_shut_down_object_dispatch>(this); + if (!m_image_ctx->io_object_dispatcher->exists( + io::OBJECT_DISPATCH_LAYER_CRYPTO)) { + finish(0); + return; + } + + m_image_ctx->io_object_dispatcher->shut_down_dispatch( + io::OBJECT_DISPATCH_LAYER_CRYPTO, ctx); +} + +template +void ShutDownCryptoRequest::handle_shut_down_object_dispatch(int r) { + if (r < 0) { + lderr(m_image_ctx->cct) << "failed to shut down object dispatch: " + << cpp_strerror(r) << dendl; + finish(r); + return; + } + + shut_down_image_dispatch(); +} + +template +void ShutDownCryptoRequest::shut_down_image_dispatch() { + if (!m_image_ctx->io_image_dispatcher->exists( + io::IMAGE_DISPATCH_LAYER_CRYPTO)) { + finish(0); + return; + } + + auto ctx = create_context_callback< + ShutDownCryptoRequest, + &ShutDownCryptoRequest::handle_shut_down_image_dispatch>(this); + m_image_ctx->io_image_dispatcher->shut_down_dispatch( + io::IMAGE_DISPATCH_LAYER_CRYPTO, ctx); +} + +template +void ShutDownCryptoRequest::handle_shut_down_image_dispatch(int r) { + if (r < 0) { + lderr(m_image_ctx->cct) << "failed to shut down image dispatch: " + << cpp_strerror(r) << dendl; + } + finish(r); +} + +template +void ShutDownCryptoRequest::finish(int r) { + if (r == 0) { + std::unique_lock image_locker{m_image_ctx->image_lock}; + m_image_ctx->crypto = nullptr; + } + + m_on_finish->complete(r); + delete this; +} + +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::ShutDownCryptoRequest; diff --git a/src/librbd/crypto/ShutDownCryptoRequest.h b/src/librbd/crypto/ShutDownCryptoRequest.h new file mode 100644 index 00000000000..68142800999 --- /dev/null +++ b/src/librbd/crypto/ShutDownCryptoRequest.h @@ -0,0 +1,45 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_SHUT_DOWN_CRYPTO_REQUEST_H +#define CEPH_LIBRBD_CRYPTO_SHUT_DOWN_CRYPTO_REQUEST_H + +#include "librbd/crypto/CryptoInterface.h" + +struct Context; + +namespace librbd { + +class ImageCtx; + +namespace crypto { + +class CryptoInterface; + +template +class ShutDownCryptoRequest { +public: + static ShutDownCryptoRequest* create(I* image_ctx, Context* on_finish) { + return new ShutDownCryptoRequest(image_ctx, on_finish); + } + + ShutDownCryptoRequest(I* image_ctx, Context* on_finish); + void send(); + void shut_down_object_dispatch(); + void handle_shut_down_object_dispatch(int r); + void shut_down_image_dispatch(); + void handle_shut_down_image_dispatch(int r); + void finish(int r); + +private: + I* m_image_ctx; + Context* m_on_finish; + ceph::ref_t m_crypto; +}; + +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::ShutDownCryptoRequest; + +#endif // CEPH_LIBRBD_CRYPTO_SHUT_DOWN_CRYPTO_REQUEST_H diff --git a/src/librbd/crypto/Utils.cc b/src/librbd/crypto/Utils.cc new file mode 100644 index 00000000000..76cc9f1f533 --- /dev/null +++ b/src/librbd/crypto/Utils.cc @@ -0,0 +1,73 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "Utils.h" + +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" +#include "librbd/crypto/BlockCrypto.h" +#include "librbd/crypto/CryptoImageDispatch.h" +#include "librbd/crypto/CryptoObjectDispatch.h" +#include "librbd/crypto/openssl/DataCryptor.h" +#include "librbd/io/ImageDispatcherInterface.h" +#include "librbd/io/ObjectDispatcherInterface.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::util: " << __func__ << ": " + +namespace librbd { +namespace crypto { +namespace util { + +template +void set_crypto(I *image_ctx, ceph::ref_t crypto) { + { + std::unique_lock image_locker{image_ctx->image_lock}; + ceph_assert(image_ctx->crypto == nullptr); + image_ctx->crypto = crypto.get(); + } + auto object_dispatch = CryptoObjectDispatch::create(image_ctx, crypto); + auto image_dispatch = CryptoImageDispatch::create(crypto->get_data_offset()); + image_ctx->io_object_dispatcher->register_dispatch(object_dispatch); + image_ctx->io_image_dispatcher->register_dispatch(image_dispatch); +} + +int build_crypto( + CephContext* cct, const unsigned char* key, uint32_t key_length, + uint64_t block_size, uint64_t data_offset, + ceph::ref_t* result_crypto) { + const char* cipher_suite; + switch (key_length) { + case 32: + cipher_suite = "aes-128-xts"; + break; + case 64: + cipher_suite = "aes-256-xts"; + break; + default: + lderr(cct) << "unsupported key length: " << key_length << dendl; + return -ENOTSUP; + } + + auto data_cryptor = new openssl::DataCryptor(cct); + int r = data_cryptor->init(cipher_suite, key, key_length); + if (r != 0) { + lderr(cct) << "error initializing data cryptor: " << cpp_strerror(r) + << dendl; + delete data_cryptor; + return r; + } + + *result_crypto = BlockCrypto::create( + cct, data_cryptor, block_size, data_offset); + return 0; +} + +} // namespace util +} // namespace crypto +} // namespace librbd + +template void librbd::crypto::util::set_crypto( + librbd::ImageCtx *image_ctx, ceph::ref_t crypto); diff --git a/src/librbd/crypto/Utils.h b/src/librbd/crypto/Utils.h new file mode 100644 index 00000000000..aed3d4767d9 --- /dev/null +++ b/src/librbd/crypto/Utils.h @@ -0,0 +1,29 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_UTILS_H +#define CEPH_LIBRBD_CRYPTO_UTILS_H + +#include "include/Context.h" +#include "librbd/crypto/CryptoInterface.h" + +namespace librbd { + +struct ImageCtx; + +namespace crypto { +namespace util { + +template +void set_crypto(ImageCtxT *image_ctx, ceph::ref_t crypto); + +int build_crypto( + CephContext* cct, const unsigned char* key, uint32_t key_length, + uint64_t block_size, uint64_t data_offset, + ceph::ref_t* result_crypto); + +} // namespace util +} // namespace crypto +} // namespace librbd + +#endif // CEPH_LIBRBD_CRYPTO_UTILS_H diff --git a/src/librbd/crypto/luks/EncryptionFormat.cc b/src/librbd/crypto/luks/EncryptionFormat.cc index d89ef3639d5..8b1b1580c06 100644 --- a/src/librbd/crypto/luks/EncryptionFormat.cc +++ b/src/librbd/crypto/luks/EncryptionFormat.cc @@ -2,6 +2,7 @@ // 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" @@ -16,20 +17,24 @@ EncryptionFormat::EncryptionFormat( 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), on_finish, - false); + image_ctx, get_format(), m_alg, std::move(m_passphrase), &m_crypto, + on_finish, false); req->send(); } template -void EncryptionFormat::load( - I* image_ctx, ceph::ref_t* result_crypto, - Context* on_finish) { +void EncryptionFormat::load(I* image_ctx, Context* on_finish) { auto req = luks::LoadRequest::create( - image_ctx, get_format(), std::move(m_passphrase), result_crypto, + image_ctx, get_format(), std::move(m_passphrase), &m_crypto, on_finish); req->send(); } diff --git a/src/librbd/crypto/luks/EncryptionFormat.h b/src/librbd/crypto/luks/EncryptionFormat.h index a45a3c91201..8c45cf9ca00 100644 --- a/src/librbd/crypto/luks/EncryptionFormat.h +++ b/src/librbd/crypto/luks/EncryptionFormat.h @@ -19,16 +19,21 @@ 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, ceph::ref_t* result_crypto, - 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 diff --git a/src/librbd/crypto/luks/FormatRequest.cc b/src/librbd/crypto/luks/FormatRequest.cc index b383d12ca88..be2452fd924 100644 --- a/src/librbd/crypto/luks/FormatRequest.cc +++ b/src/librbd/crypto/luks/FormatRequest.cc @@ -3,9 +3,13 @@ #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" @@ -24,10 +28,12 @@ using librbd::util::create_context_callback; template FormatRequest::FormatRequest( I* image_ctx, encryption_format_t format, encryption_algorithm_t alg, - std::string&& passphrase, Context* on_finish, + 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) { @@ -71,6 +77,15 @@ void FormatRequest::send() { 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) { @@ -79,7 +94,8 @@ void FormatRequest::send() { } // format (create LUKS header) - r = m_header.format(type, cipher, key_size, "xts-plain64", sector_size, + 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); @@ -104,6 +120,15 @@ void FormatRequest::send() { 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); @@ -116,7 +141,7 @@ void FormatRequest::send() { auto ctx = create_context_callback< FormatRequest, &FormatRequest::handle_write_header>(this); auto aio_comp = io::AioCompletion::create_and_start( - ctx, util::get_image_ctx(m_image_ctx), io::AIO_TYPE_WRITE); + ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_WRITE); ZTracer::Trace trace; auto req = io::ImageDispatchSpec::create_write( @@ -140,7 +165,8 @@ void FormatRequest::handle_write_header(int r) { template void FormatRequest::finish(int r) { - ceph_memzero_s(&m_passphrase[0], m_passphrase.capacity(), m_passphrase.size()); + ceph_memzero_s( + &m_passphrase[0], m_passphrase.capacity(), m_passphrase.size()); m_on_finish->complete(r); delete this; } diff --git a/src/librbd/crypto/luks/FormatRequest.h b/src/librbd/crypto/luks/FormatRequest.h index 1723185422d..a782dc818fc 100644 --- a/src/librbd/crypto/luks/FormatRequest.h +++ b/src/librbd/crypto/luks/FormatRequest.h @@ -6,6 +6,7 @@ #include "include/rbd/librbd.hpp" #include "librbd/ImageCtx.h" +#include "librbd/crypto/CryptoInterface.h" #include "librbd/crypto/luks/Header.h" namespace librbd { @@ -21,13 +22,15 @@ public: static FormatRequest* create( I* image_ctx, encryption_format_t format, encryption_algorithm_t alg, std::string&& passphrase, - Context* on_finish, bool insecure_fast_mode) { + ceph::ref_t* result_crypto, Context* on_finish, + bool insecure_fast_mode) { return new FormatRequest(image_ctx, format, alg, std::move(passphrase), - on_finish, insecure_fast_mode); + 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); @@ -38,6 +41,7 @@ private: 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; diff --git a/src/librbd/crypto/luks/Header.cc b/src/librbd/crypto/luks/Header.cc index 4307844f504..6d00074efcf 100644 --- a/src/librbd/crypto/luks/Header.cc +++ b/src/librbd/crypto/luks/Header.cc @@ -114,9 +114,10 @@ ssize_t Header::read(ceph::bufferlist* bl) { return r; } -int Header::format(const char* type, const char* alg, size_t key_size, - const char* cipher_mode, uint32_t sector_size, - uint32_t data_alignment, bool insecure_fast_mode) { +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: " @@ -168,7 +169,7 @@ int Header::format(const char* type, const char* alg, size_t key_size, } auto r = crypt_format( - m_cd, type, alg, cipher_mode, NULL, NULL, key_size, params); + 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; diff --git a/src/librbd/crypto/luks/Header.h b/src/librbd/crypto/luks/Header.h index 13ef8fd20d3..cee80a8e462 100644 --- a/src/librbd/crypto/luks/Header.h +++ b/src/librbd/crypto/luks/Header.h @@ -21,8 +21,8 @@ public: int write(const ceph::bufferlist& bl); ssize_t read(ceph::bufferlist* bl); - int format(const char* type, const char* alg, size_t key_size, - const char* cipher_mode, uint32_t sector_size, + 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); diff --git a/src/librbd/crypto/luks/LoadRequest.cc b/src/librbd/crypto/luks/LoadRequest.cc index 2b0dbe47d25..c00563d930e 100644 --- a/src/librbd/crypto/luks/LoadRequest.cc +++ b/src/librbd/crypto/luks/LoadRequest.cc @@ -6,8 +6,7 @@ #include "common/dout.h" #include "common/errno.h" #include "librbd/Utils.h" -#include "librbd/crypto/BlockCrypto.h" -#include "librbd/crypto/openssl/DataCryptor.h" +#include "librbd/crypto/Utils.h" #include "librbd/io/AioCompletion.h" #include "librbd/io/ImageDispatchSpec.h" #include "librbd/io/ReadResult.h" @@ -59,7 +58,8 @@ 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, util::get_image_ctx(m_image_ctx), io::AIO_TYPE_READ); + 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, @@ -175,39 +175,11 @@ void LoadRequest::read_volume_key() { return; } - const char* cipher_suite; - switch (volume_key_size) { - case 32: - cipher_suite = "aes-128-xts"; - break; - case 64: - cipher_suite = "aes-256-xts"; - break; - default: - lderr(m_image_ctx->cct) << "unsupported volume key size: " - << volume_key_size << dendl; - finish(-ENOTSUP); - return; - } - - - auto data_cryptor = new openssl::DataCryptor(m_image_ctx->cct); - r = data_cryptor->init( - cipher_suite, reinterpret_cast(volume_key), - volume_key_size); - if (r != 0) { - lderr(m_image_ctx->cct) << "error initializing data cryptor: " << r - << dendl; - delete data_cryptor; - finish(r); - return; - } - - auto sector_size = m_header.get_sector_size(); - auto data_offset = m_header.get_data_offset(); - *m_result_crypto = BlockCrypto::create( - m_image_ctx->cct, data_cryptor, sector_size, data_offset); - finish(0); + 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 diff --git a/src/librbd/crypto/openssl/DataCryptor.cc b/src/librbd/crypto/openssl/DataCryptor.cc index 6d2ce0ff732..0c164b048f9 100644 --- a/src/librbd/crypto/openssl/DataCryptor.cc +++ b/src/librbd/crypto/openssl/DataCryptor.cc @@ -62,6 +62,14 @@ uint32_t DataCryptor::get_iv_size() const { return m_iv_size; } +const unsigned char* DataCryptor::get_key() const { + return m_key; +} + +int DataCryptor::get_key_length() const { + return EVP_CIPHER_key_length(m_cipher); +} + EVP_CIPHER_CTX* DataCryptor::get_context(CipherMode mode) { int enc; switch(mode) { @@ -130,7 +138,8 @@ void DataCryptor::log_errors() const { if (error == 0) { break; } - lderr(m_cct) << "OpenSSL error: " << error << dendl; + lderr(m_cct) << "OpenSSL error: " << ERR_error_string(error, nullptr) + << dendl; } } diff --git a/src/librbd/crypto/openssl/DataCryptor.h b/src/librbd/crypto/openssl/DataCryptor.h index 0c2795ef63e..c9a177ef595 100644 --- a/src/librbd/crypto/openssl/DataCryptor.h +++ b/src/librbd/crypto/openssl/DataCryptor.h @@ -22,6 +22,9 @@ public: uint16_t key_length); uint32_t get_block_size() const override; uint32_t get_iv_size() const override; + const unsigned char* get_key() const override; + int get_key_length() const override; + EVP_CIPHER_CTX* get_context(CipherMode mode) override; void return_context(EVP_CIPHER_CTX* ctx, CipherMode mode) override; int init_context(EVP_CIPHER_CTX* ctx, const unsigned char* iv, diff --git a/src/librbd/migration/S3Stream.cc b/src/librbd/migration/S3Stream.cc index 46165f208f9..3b4db0cefe1 100644 --- a/src/librbd/migration/S3Stream.cc +++ b/src/librbd/migration/S3Stream.cc @@ -171,7 +171,7 @@ void S3Stream::process_request(HttpRequest& http_request) { // create HMAC-SHA1 signature from secret key + string-to-sign sha1_digest_t digest; - crypto::HMACSHA1 hmac( + ceph::crypto::HMACSHA1 hmac( reinterpret_cast(m_secret_key.data()), m_secret_key.size()); hmac.Update(reinterpret_cast(string_to_sign.data()), diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 68b1b75dba5..6954d22da46 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -58,6 +58,7 @@ set(unittest_librbd_srcs crypto/test_mock_CryptoObjectDispatch.cc crypto/test_mock_FormatRequest.cc crypto/test_mock_LoadRequest.cc + crypto/test_mock_ShutDownCryptoRequest.cc crypto/openssl/test_DataCryptor.cc deep_copy/test_mock_ImageCopyRequest.cc deep_copy/test_mock_MetadataCopyRequest.cc diff --git a/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc b/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc index 89871636609..189c666debc 100644 --- a/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc +++ b/src/test/librbd/crypto/luks/test_mock_FormatRequest.cc @@ -38,6 +38,7 @@ struct TestMockCryptoLuksFormatRequest : public TestMockFixture { Context *on_finish = &finished_cond; io::AioCompletion* aio_comp; ceph::bufferlist header_bl; + ceph::ref_t crypto; void SetUp() override { TestMockFixture::SetUp(); @@ -105,14 +106,20 @@ struct TestMockCryptoLuksFormatRequest : public TestMockFixture { ASSERT_EQ(0, header.read_volume_key( passphrase_cstr, strlen(passphrase_cstr), reinterpret_cast(volume_key), &volume_key_size)); + + ASSERT_EQ(expected_key_length, crypto->get_key_length()); + ASSERT_EQ(0, std::memcmp( + volume_key, crypto->get_key(), expected_key_length)); + ASSERT_EQ(expected_sector_size, crypto->get_block_size()); + ASSERT_EQ(header.get_data_offset(), crypto->get_data_offset()); } }; TEST_F(TestMockCryptoLuksFormatRequest, LUKS1) { auto mock_format_request = MockFormatRequest::create( mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, - RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), on_finish, - true); + RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), &crypto, + on_finish, true); expect_get_object_size(); expect_get_image_size(IMAGE_SIZE); expect_image_write(); @@ -126,8 +133,8 @@ TEST_F(TestMockCryptoLuksFormatRequest, LUKS1) { TEST_F(TestMockCryptoLuksFormatRequest, AES128) { auto mock_format_request = MockFormatRequest::create( mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, - RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), on_finish, - true); + RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), &crypto, + on_finish, true); expect_get_object_size(); expect_get_image_size(IMAGE_SIZE); expect_image_write(); @@ -141,8 +148,8 @@ TEST_F(TestMockCryptoLuksFormatRequest, AES128) { TEST_F(TestMockCryptoLuksFormatRequest, AES256) { auto mock_format_request = MockFormatRequest::create( mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, - RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), on_finish, - true); + RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto, + on_finish, true); expect_get_object_size(); expect_get_image_size(IMAGE_SIZE); expect_image_write(); @@ -150,14 +157,14 @@ TEST_F(TestMockCryptoLuksFormatRequest, AES256) { ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); complete_aio(0); ASSERT_EQ(0, finished_cond.wait()); - ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 62, 4096)); + ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 64, 4096)); } TEST_F(TestMockCryptoLuksFormatRequest, ImageTooSmall) { auto mock_format_request = MockFormatRequest::create( mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, - RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), on_finish, - true); + RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto, + on_finish, true); expect_get_object_size(); expect_get_image_size(1024*1024); mock_format_request->send(); @@ -167,8 +174,8 @@ TEST_F(TestMockCryptoLuksFormatRequest, ImageTooSmall) { TEST_F(TestMockCryptoLuksFormatRequest, WriteFail) { auto mock_format_request = MockFormatRequest::create( mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, - RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), on_finish, - true); + RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto, + on_finish, true); expect_get_object_size(); expect_get_image_size(IMAGE_SIZE); expect_image_write(); diff --git a/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc b/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc index 6f79f3c1f58..b67b5862fdd 100644 --- a/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc +++ b/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc @@ -67,8 +67,8 @@ struct TestMockCryptoLuksLoadRequest : public TestMockFixture { Header header(mock_image_ctx->cct); ASSERT_EQ(0, header.init()); - ASSERT_EQ(0, header.format(type, alg, key_size, cipher_mode, sector_size, - OBJECT_SIZE, true)); + ASSERT_EQ(0, header.format(type, alg, nullptr, key_size, cipher_mode, + sector_size, OBJECT_SIZE, true)); ASSERT_EQ(0, header.add_keyslot(passphrase_cstr, strlen(passphrase_cstr))); ASSERT_LE(0, header.read(&header_bl)); diff --git a/src/test/librbd/crypto/test_mock_FormatRequest.cc b/src/test/librbd/crypto/test_mock_FormatRequest.cc index 722f768da54..2008deb68c2 100644 --- a/src/test/librbd/crypto/test_mock_FormatRequest.cc +++ b/src/test/librbd/crypto/test_mock_FormatRequest.cc @@ -4,26 +4,73 @@ #include "test/librbd/test_mock_fixture.h" #include "test/librbd/test_support.h" #include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/crypto/MockCryptoInterface.h" #include "test/librbd/mock/crypto/MockEncryptionFormat.h" +#include "librbd/crypto/Utils.h" + +namespace librbd { +namespace util { + +inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) { + return image_ctx->image_ctx; +} + +} // namespace util +} // namespace librbd #include "librbd/crypto/FormatRequest.cc" namespace librbd { namespace crypto { +namespace util { + +template <> void set_crypto( + MockImageCtx *image_ctx, ceph::ref_t crypto) { + image_ctx->crypto = crypto.get(); +} + +} // namespace util + using ::testing::_; using ::testing::Invoke; using ::testing::Return; using ::testing::WithArg; +template <> +struct ShutDownCryptoRequest { + Context *on_finish = nullptr; + static ShutDownCryptoRequest *s_instance; + static ShutDownCryptoRequest *create( + MockImageCtx* image_ctx, Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + ShutDownCryptoRequest() { + s_instance = this; + } +}; + +ShutDownCryptoRequest *ShutDownCryptoRequest< + MockImageCtx>::s_instance = nullptr; + struct TestMockCryptoFormatRequest : public TestMockFixture { typedef FormatRequest MockFormatRequest; + typedef ShutDownCryptoRequest MockShutDownCryptoRequest; MockImageCtx* mock_image_ctx; C_SaferCond finished_cond; Context *on_finish = &finished_cond; MockEncryptionFormat* mock_encryption_format; Context* format_context; + MockCryptoInterface* crypto; + MockCryptoInterface* old_crypto; + MockFormatRequest* mock_format_request; + std::string key = std::string(64, '0'); void SetUp() override { TestMockFixture::SetUp(); @@ -32,18 +79,22 @@ struct TestMockCryptoFormatRequest : public TestMockFixture { ASSERT_EQ(0, open_image(m_image_name, &ictx)); mock_image_ctx = new MockImageCtx(*ictx); mock_encryption_format = new MockEncryptionFormat(); + crypto = new MockCryptoInterface(); + old_crypto = new MockCryptoInterface(); + mock_image_ctx->crypto = old_crypto; + mock_format_request = MockFormatRequest::create( + mock_image_ctx, + std::unique_ptr(mock_encryption_format), + on_finish); } void TearDown() override { + crypto->put(); + old_crypto->put(); delete mock_image_ctx; TestMockFixture::TearDown(); } - void expect_crypto_layer_exists_check(bool exists) { - EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists( - io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists)); - } - void expect_test_journal_feature(bool has_journal=false) { EXPECT_CALL(*mock_image_ctx, test_features( RBD_FEATURE_JOURNALING)).WillOnce(Return(has_journal)); @@ -56,66 +107,88 @@ struct TestMockCryptoFormatRequest : public TestMockFixture { format_context = ctx; }))); } -}; -TEST_F(TestMockCryptoFormatRequest, CryptoAlreadyLoaded) { - auto mock_format_request = MockFormatRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(true); - mock_format_request->send(); - ASSERT_EQ(-EEXIST, finished_cond.wait()); -} + void expect_image_flush(int r) { + EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_)).WillOnce( + Invoke([r](io::ImageDispatchSpec* spec) { + ASSERT_TRUE(boost::get( + &spec->request) != nullptr); + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + spec->aio_comp->set_request_count(1); + spec->aio_comp->add_request(); + spec->aio_comp->complete_request(r); + })); + } +}; TEST_F(TestMockCryptoFormatRequest, JournalEnabled) { - auto mock_format_request = MockFormatRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); expect_test_journal_feature(true); mock_format_request->send(); ASSERT_EQ(-ENOTSUP, finished_cond.wait()); + ASSERT_EQ(old_crypto, mock_image_ctx->crypto); } -TEST_F(TestMockCryptoFormatRequest, CloneFormat) { - mock_image_ctx->parent = mock_image_ctx; - auto mock_format_request = MockFormatRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); +TEST_F(TestMockCryptoFormatRequest, FailShutDownCrypto) { + expect_test_journal_feature(false); + MockShutDownCryptoRequest mock_shutdown_crypto_request; + EXPECT_CALL(mock_shutdown_crypto_request, send()); mock_format_request->send(); - ASSERT_EQ(-ENOTSUP, finished_cond.wait()); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + mock_shutdown_crypto_request.on_finish->complete(-EIO); + ASSERT_EQ(-EIO, finished_cond.wait()); + ASSERT_EQ(old_crypto, mock_image_ctx->crypto); } TEST_F(TestMockCryptoFormatRequest, FormatFail) { - auto mock_format_request = MockFormatRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); + mock_image_ctx->crypto = nullptr; expect_test_journal_feature(false); expect_encryption_format(); mock_format_request->send(); ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); format_context->complete(-EIO); ASSERT_EQ(-EIO, finished_cond.wait()); + ASSERT_EQ(nullptr, mock_image_ctx->crypto); } TEST_F(TestMockCryptoFormatRequest, Success) { - auto mock_format_request = MockFormatRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); + mock_image_ctx->crypto = nullptr; + expect_test_journal_feature(false); + expect_encryption_format(); + mock_format_request->send(); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_image_flush(0); + EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(Return(crypto)); + format_context->complete(0); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_EQ(crypto, mock_image_ctx->crypto); +} + +TEST_F(TestMockCryptoFormatRequest, FailFlush) { + mock_image_ctx->crypto = nullptr; expect_test_journal_feature(false); expect_encryption_format(); mock_format_request->send(); ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_image_flush(-EIO); + format_context->complete(0); + ASSERT_EQ(-EIO, finished_cond.wait()); + ASSERT_EQ(nullptr, mock_image_ctx->crypto); +} + +TEST_F(TestMockCryptoFormatRequest, CryptoAlreadyLoaded) { + expect_test_journal_feature(false); + MockShutDownCryptoRequest mock_shutdown_crypto_request; + EXPECT_CALL(mock_shutdown_crypto_request, send()); + mock_format_request->send(); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_encryption_format(); + mock_shutdown_crypto_request.on_finish->complete(0); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_image_flush(0); + EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(Return(crypto)); format_context->complete(0); ASSERT_EQ(0, finished_cond.wait()); + ASSERT_EQ(crypto, mock_image_ctx->crypto); } } // namespace crypto diff --git a/src/test/librbd/crypto/test_mock_LoadRequest.cc b/src/test/librbd/crypto/test_mock_LoadRequest.cc index ac36b65dbb8..ddf57d26de7 100644 --- a/src/test/librbd/crypto/test_mock_LoadRequest.cc +++ b/src/test/librbd/crypto/test_mock_LoadRequest.cc @@ -2,6 +2,7 @@ // vim: ts=8 sw=2 smarttab #include "librbd/crypto/CryptoObjectDispatch.h" +#include "librbd/crypto/Utils.h" #include "test/librbd/test_mock_fixture.h" #include "test/librbd/test_support.h" #include "test/librbd/mock/MockImageCtx.h" @@ -44,7 +45,9 @@ struct TestMockCryptoLoadRequest : public TestMockFixture { C_SaferCond finished_cond; Context *on_finish = &finished_cond; MockEncryptionFormat* mock_encryption_format; + MockCryptoInterface* crypto; Context* load_context; + MockLoadRequest* mock_load_request; void SetUp() override { TestMockFixture::SetUp(); @@ -53,63 +56,52 @@ struct TestMockCryptoLoadRequest : public TestMockFixture { ASSERT_EQ(0, open_image(m_image_name, &ictx)); mock_image_ctx = new MockImageCtx(*ictx); mock_encryption_format = new MockEncryptionFormat(); + crypto = new MockCryptoInterface(); + mock_load_request = MockLoadRequest::create( + mock_image_ctx, + std::unique_ptr(mock_encryption_format), + on_finish); } void TearDown() override { + crypto->put(); delete mock_image_ctx; TestMockFixture::TearDown(); } - void expect_crypto_layer_exists_check(bool exists) { - EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists( - io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists)); + void expect_test_journal_feature() { + expect_test_journal_feature(mock_image_ctx, false); } - void expect_test_journal_feature(bool has_journal=false) { - EXPECT_CALL(*mock_image_ctx, test_features( + void expect_test_journal_feature(MockImageCtx* ctx, bool has_journal=false) { + EXPECT_CALL(*ctx, test_features( RBD_FEATURE_JOURNALING)).WillOnce(Return(has_journal)); } void expect_encryption_load() { EXPECT_CALL(*mock_encryption_format, load( - mock_image_ctx, _, _)).WillOnce( - WithArgs<1, 2>(Invoke([this]( - ceph::ref_t* result_crypto, - Context* ctx) { + mock_image_ctx, _)).WillOnce( + WithArgs<1>(Invoke([this](Context* ctx) { load_context = ctx; - *result_crypto = new MockCryptoInterface(); }))); } + }; TEST_F(TestMockCryptoLoadRequest, CryptoAlreadyLoaded) { - auto mock_load_request = MockLoadRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(true); + mock_image_ctx->crypto = new MockCryptoInterface(); mock_load_request->send(); ASSERT_EQ(-EEXIST, finished_cond.wait()); } TEST_F(TestMockCryptoLoadRequest, JournalEnabled) { - auto mock_load_request = MockLoadRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); - expect_test_journal_feature(true); + expect_test_journal_feature(mock_image_ctx, true); mock_load_request->send(); ASSERT_EQ(-ENOTSUP, finished_cond.wait()); } TEST_F(TestMockCryptoLoadRequest, LoadFail) { - auto mock_load_request = MockLoadRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); - expect_test_journal_feature(false); + expect_test_journal_feature(); expect_encryption_load(); mock_load_request->send(); ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); @@ -118,19 +110,15 @@ TEST_F(TestMockCryptoLoadRequest, LoadFail) { } TEST_F(TestMockCryptoLoadRequest, Success) { - auto mock_load_request = MockLoadRequest::create( - mock_image_ctx, - std::unique_ptr(mock_encryption_format), - on_finish); - expect_crypto_layer_exists_check(false); - expect_test_journal_feature(false); + mock_image_ctx->parent = nullptr; + expect_test_journal_feature(mock_image_ctx, false); expect_encryption_load(); mock_load_request->send(); ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); - EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, register_dispatch(_)); - EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, register_dispatch(_)); + EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(Return(crypto)); load_context->complete(0); ASSERT_EQ(0, finished_cond.wait()); + ASSERT_EQ(crypto, mock_image_ctx->crypto); } } // namespace crypto diff --git a/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc b/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc new file mode 100644 index 00000000000..4a98873d0aa --- /dev/null +++ b/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc @@ -0,0 +1,141 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/crypto/Utils.h" +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/crypto/MockCryptoInterface.h" + +#include "librbd/crypto/ShutDownCryptoRequest.cc" + +namespace librbd { + +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(librbd::ImageCtx &image_ctx) + : librbd::MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +namespace crypto { + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::WithArgs; + +struct TestMockShutDownCryptoRequest : public TestMockFixture { + typedef ShutDownCryptoRequest MockShutDownCryptoRequest; + + MockTestImageCtx* mock_image_ctx; + C_SaferCond finished_cond; + Context *on_finish = &finished_cond; + MockShutDownCryptoRequest* mock_shutdown_crypto_request; + Context* shutdown_object_dispatch_context; + Context* shutdown_image_dispatch_context; + + void SetUp() override { + TestMockFixture::SetUp(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + mock_image_ctx = new MockTestImageCtx(*ictx); + mock_image_ctx->crypto = new MockCryptoInterface(); + mock_shutdown_crypto_request = MockShutDownCryptoRequest::create( + mock_image_ctx, on_finish); + } + + void TearDown() override { + delete mock_image_ctx; + TestMockFixture::TearDown(); + } + + void expect_crypto_object_layer_exists_check(bool exists) { + EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists( + io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists)); + } + + void expect_crypto_image_layer_exists_check(bool exists) { + EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, exists( + io::IMAGE_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists)); + } + + void expect_shutdown_crypto_object_dispatch() { + EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, shut_down_dispatch( + io::OBJECT_DISPATCH_LAYER_CRYPTO, _)).WillOnce( + WithArgs<1>(Invoke([this](Context* ctx) { + shutdown_object_dispatch_context = ctx; + }))); + } + + void expect_shutdown_crypto_image_dispatch() { + EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, shut_down_dispatch( + io::IMAGE_DISPATCH_LAYER_CRYPTO, _)).WillOnce( + WithArgs<1>(Invoke([this](Context* ctx) { + shutdown_image_dispatch_context = ctx; + }))); + } +}; + +TEST_F(TestMockShutDownCryptoRequest, NoCryptoObjectDispatch) { + expect_crypto_object_layer_exists_check(false); + mock_shutdown_crypto_request->send(); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_EQ(nullptr, mock_image_ctx->crypto); +} + +TEST_F(TestMockShutDownCryptoRequest, FailShutdownObjectDispatch) { + expect_crypto_object_layer_exists_check(true); + expect_shutdown_crypto_object_dispatch(); + mock_shutdown_crypto_request->send(); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + shutdown_object_dispatch_context->complete(-EIO); + ASSERT_EQ(-EIO, finished_cond.wait()); + ASSERT_NE(nullptr, mock_image_ctx->crypto); +} + +TEST_F(TestMockShutDownCryptoRequest, NoCryptoImageDispatch) { + expect_crypto_object_layer_exists_check(true); + expect_shutdown_crypto_object_dispatch(); + mock_shutdown_crypto_request->send(); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_crypto_image_layer_exists_check(false); + shutdown_object_dispatch_context->complete(0); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_EQ(nullptr, mock_image_ctx->crypto); +} + +TEST_F(TestMockShutDownCryptoRequest, FailShutdownImageDispatch) { + expect_crypto_object_layer_exists_check(true); + expect_shutdown_crypto_object_dispatch(); + mock_shutdown_crypto_request->send(); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_crypto_image_layer_exists_check(true); + expect_shutdown_crypto_image_dispatch(); + shutdown_object_dispatch_context->complete(0); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + shutdown_image_dispatch_context->complete(-EIO); + ASSERT_EQ(-EIO, finished_cond.wait()); + ASSERT_NE(nullptr, mock_image_ctx->crypto); +} + +TEST_F(TestMockShutDownCryptoRequest, Success) { + expect_crypto_object_layer_exists_check(true); + expect_shutdown_crypto_object_dispatch(); + mock_shutdown_crypto_request->send(); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + expect_crypto_image_layer_exists_check(true); + expect_shutdown_crypto_image_dispatch(); + shutdown_object_dispatch_context->complete(0); + ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0)); + shutdown_image_dispatch_context->complete(0); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_EQ(nullptr, mock_image_ctx->crypto); +} + +} // namespace crypto +} // namespace librbd diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index 80307962eb4..521ecff0c8f 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -318,6 +318,8 @@ struct MockImageCtx { ZTracer::Endpoint trace_endpoint; + crypto::CryptoInterface* crypto = nullptr; + uint64_t sparse_read_threshold_bytes; uint32_t discard_granularity_bytes; int mirroring_replay_delay; diff --git a/src/test/librbd/mock/crypto/MockCryptoInterface.h b/src/test/librbd/mock/crypto/MockCryptoInterface.h index 1263fba95b8..027ebde627c 100644 --- a/src/test/librbd/mock/crypto/MockCryptoInterface.h +++ b/src/test/librbd/mock/crypto/MockCryptoInterface.h @@ -15,6 +15,8 @@ struct MockCryptoInterface : CryptoInterface { MOCK_METHOD2(encrypt, int(ceph::bufferlist*, uint64_t)); MOCK_METHOD2(decrypt, int(ceph::bufferlist*, uint64_t)); + MOCK_CONST_METHOD0(get_key, const unsigned char*()); + MOCK_CONST_METHOD0(get_key_length, int()); uint64_t get_block_size() const override { return 4096; diff --git a/src/test/librbd/mock/crypto/MockDataCryptor.h b/src/test/librbd/mock/crypto/MockDataCryptor.h index 2f0198b5a13..51a738862cf 100644 --- a/src/test/librbd/mock/crypto/MockDataCryptor.h +++ b/src/test/librbd/mock/crypto/MockDataCryptor.h @@ -33,6 +33,8 @@ public: MOCK_CONST_METHOD4(update_context, int(MockCryptoContext*, const unsigned char*, unsigned char*, uint32_t)); + MOCK_CONST_METHOD0(get_key, const unsigned char*()); + MOCK_CONST_METHOD0(get_key_length, int()); }; } // namespace crypto diff --git a/src/test/librbd/mock/crypto/MockEncryptionFormat.h b/src/test/librbd/mock/crypto/MockEncryptionFormat.h index 751ee825dab..1caf99eb068 100644 --- a/src/test/librbd/mock/crypto/MockEncryptionFormat.h +++ b/src/test/librbd/mock/crypto/MockEncryptionFormat.h @@ -14,9 +14,8 @@ namespace crypto { struct MockEncryptionFormat : EncryptionFormat { MOCK_METHOD2(format, void(MockImageCtx* ictx, Context* on_finish)); - MOCK_METHOD3(load, void(MockImageCtx* ictx, - ceph::ref_t* result_crypto, - Context* on_finish)); + MOCK_METHOD2(load, void(MockImageCtx* ictx, Context* on_finish)); + MOCK_METHOD0(get_crypto, ceph::ref_t()); }; } // namespace crypto diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index e7233f0f376..c1ff2c61851 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -2141,10 +2141,21 @@ TEST_F(TestLibRBD, TestEncryptionLUKS1) #else ASSERT_EQ(0, rbd_encryption_format( image, RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts))); - ASSERT_EQ(0, rbd_encryption_load( + ASSERT_EQ(-EEXIST, rbd_encryption_load( image, RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts))); test_io(image); + + bool passed; + write_test_data(image, "test", 0, 4, 0, &passed); + ASSERT_TRUE(passed); + ASSERT_EQ(0, rbd_close(image)); + + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + ASSERT_EQ(0, rbd_encryption_load( + image, RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts))); + read_test_data(image, "test", 0, 4, 0, &passed); + ASSERT_TRUE(passed); #endif ASSERT_EQ(0, rbd_close(image)); @@ -2182,10 +2193,21 @@ TEST_F(TestLibRBD, TestEncryptionLUKS2) #else ASSERT_EQ(0, rbd_encryption_format( image, RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts))); - ASSERT_EQ(0, rbd_encryption_load( + ASSERT_EQ(-EEXIST, rbd_encryption_load( image, RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts))); test_io(image); + + bool passed; + write_test_data(image, "test", 0, 4, 0, &passed); + ASSERT_TRUE(passed); + ASSERT_EQ(0, rbd_close(image)); + + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + ASSERT_EQ(0, rbd_encryption_load( + image, RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts))); + read_test_data(image, "test", 0, 4, 0, &passed); + ASSERT_TRUE(passed); #endif ASSERT_EQ(0, rbd_close(image)); -- 2.39.5