summaryrefslogtreecommitdiffstats
path: root/external/meta-virtualization/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch
diff options
context:
space:
mode:
Diffstat (limited to 'external/meta-virtualization/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch')
-rw-r--r--external/meta-virtualization/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch256
1 files changed, 256 insertions, 0 deletions
diff --git a/external/meta-virtualization/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch b/external/meta-virtualization/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch
new file mode 100644
index 00000000..54156698
--- /dev/null
+++ b/external/meta-virtualization/recipes-extended/ceph/ceph/0001-msg-async-ProtocolV2-avoid-AES-GCM-nonce-reuse-vulne.patch
@@ -0,0 +1,256 @@
+From 20b7bb685c5ea74c651ca1ea547ac66b0fee7035 Mon Sep 17 00:00:00 2001
+From: Ilya Dryomov <idryomov@gmail.com>
+Date: Fri, 6 Mar 2020 20:16:45 +0100
+Subject: [PATCH] msg/async/ProtocolV2: avoid AES-GCM nonce reuse
+ vulnerabilities
+
+The secure mode uses AES-128-GCM with 96-bit nonces consisting of a
+32-bit counter followed by a 64-bit salt. The counter is incremented
+after processing each frame, the salt is fixed for the duration of
+the session. Both are initialized from the session key generated
+during session negotiation, so the counter starts with essentially
+a random value. It is allowed to wrap, and, after 2**32 frames, it
+repeats, resulting in nonce reuse (the actual sequence numbers that
+the messenger works with are 64-bit, so the session continues on).
+
+Because of how GCM works, this completely breaks both confidentiality
+and integrity aspects of the secure mode. A single nonce reuse reveals
+the XOR of two plaintexts and almost completely reveals the subkey
+used for producing authentication tags. After a few nonces get used
+twice, all confidentiality and integrity goes out the window and the
+attacker can potentially encrypt-authenticate plaintext of their
+choice.
+
+We can't easily change the nonce format to extend the counter to
+64 bits (and possibly XOR it with a longer salt). Instead, just
+remember the initial nonce and cut the session before it repeats,
+forcing renegotiation.
+
+Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
+Reviewed-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
+Reviewed-by: Sage Weil <sage@redhat.com>
+
+Conflicts:
+ src/msg/async/ProtocolV2.h [ context: commit ed3ec4c01d17
+ ("msg: Build target 'common' without using namespace in
+ headers") not in octopus ]
+
+CVE: CVE-2020-1759
+Upstream Status: Backport [20b7bb685c5ea74c651ca1ea547ac66b0fee7035]
+
+Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com>
+---
+ src/msg/async/ProtocolV2.cc | 62 ++++++++++++++++++++++++----------
+ src/msg/async/ProtocolV2.h | 5 +--
+ src/msg/async/crypto_onwire.cc | 17 ++++++++--
+ src/msg/async/crypto_onwire.h | 5 +++
+ 4 files changed, 67 insertions(+), 22 deletions(-)
+
+diff --git a/src/msg/async/ProtocolV2.cc b/src/msg/async/ProtocolV2.cc
+index 8fc02db6e5..c69f2ccf79 100644
+--- a/src/msg/async/ProtocolV2.cc
++++ b/src/msg/async/ProtocolV2.cc
+@@ -533,7 +533,10 @@ ssize_t ProtocolV2::write_message(Message *m, bool more) {
+ m->get_payload(),
+ m->get_middle(),
+ m->get_data());
+- connection->outgoing_bl.append(message.get_buffer(session_stream_handlers));
++ if (!append_frame(message)) {
++ m->put();
++ return -EILSEQ;
++ }
+
+ ldout(cct, 5) << __func__ << " sending message m=" << m
+ << " seq=" << m->get_seq() << " " << *m << dendl;
+@@ -566,15 +569,17 @@ ssize_t ProtocolV2::write_message(Message *m, bool more) {
+ return rc;
+ }
+
+-void ProtocolV2::append_keepalive() {
+- ldout(cct, 10) << __func__ << dendl;
+- auto keepalive_frame = KeepAliveFrame::Encode();
+- connection->outgoing_bl.append(keepalive_frame.get_buffer(session_stream_handlers));
+-}
+-
+-void ProtocolV2::append_keepalive_ack(utime_t &timestamp) {
+- auto keepalive_ack_frame = KeepAliveFrameAck::Encode(timestamp);
+- connection->outgoing_bl.append(keepalive_ack_frame.get_buffer(session_stream_handlers));
++template <class F>
++bool ProtocolV2::append_frame(F& frame) {
++ ceph::bufferlist bl;
++ try {
++ bl = frame.get_buffer(session_stream_handlers);
++ } catch (ceph::crypto::onwire::TxHandlerError &e) {
++ ldout(cct, 1) << __func__ << " " << e.what() << dendl;
++ return false;
++ }
++ connection->outgoing_bl.append(bl);
++ return true;
+ }
+
+ void ProtocolV2::handle_message_ack(uint64_t seq) {
+@@ -612,7 +617,15 @@ void ProtocolV2::write_event() {
+ connection->write_lock.lock();
+ if (can_write) {
+ if (keepalive) {
+- append_keepalive();
++ ldout(cct, 10) << __func__ << " appending keepalive" << dendl;
++ auto keepalive_frame = KeepAliveFrame::Encode();
++ if (!append_frame(keepalive_frame)) {
++ connection->write_lock.unlock();
++ connection->lock.lock();
++ fault();
++ connection->lock.unlock();
++ return;
++ }
+ keepalive = false;
+ }
+
+@@ -663,13 +676,16 @@ void ProtocolV2::write_event() {
+ if (r == 0) {
+ uint64_t left = ack_left;
+ if (left) {
+- auto ack = AckFrame::Encode(in_seq);
+- connection->outgoing_bl.append(ack.get_buffer(session_stream_handlers));
+ ldout(cct, 10) << __func__ << " try send msg ack, acked " << left
+ << " messages" << dendl;
+- ack_left -= left;
+- left = ack_left;
+- r = connection->_try_send(left);
++ auto ack_frame = AckFrame::Encode(in_seq);
++ if (append_frame(ack_frame)) {
++ ack_left -= left;
++ left = ack_left;
++ r = connection->_try_send(left);
++ } else {
++ r = -EILSEQ;
++ }
+ } else if (is_queued()) {
+ r = connection->_try_send();
+ }
+@@ -769,7 +785,13 @@ template <class F>
+ CtPtr ProtocolV2::write(const std::string &desc,
+ CONTINUATION_TYPE<ProtocolV2> &next,
+ F &frame) {
+- ceph::bufferlist bl = frame.get_buffer(session_stream_handlers);
++ ceph::bufferlist bl;
++ try {
++ bl = frame.get_buffer(session_stream_handlers);
++ } catch (ceph::crypto::onwire::TxHandlerError &e) {
++ ldout(cct, 1) << __func__ << " " << e.what() << dendl;
++ return _fault();
++ }
+ return write(desc, next, bl);
+ }
+
+@@ -1672,7 +1694,11 @@ CtPtr ProtocolV2::handle_keepalive2(ceph::bufferlist &payload)
+ ldout(cct, 30) << __func__ << " got KEEPALIVE2 tag ..." << dendl;
+
+ connection->write_lock.lock();
+- append_keepalive_ack(keepalive_frame.timestamp());
++ auto keepalive_ack_frame = KeepAliveFrameAck::Encode(keepalive_frame.timestamp());
++ if (!append_frame(keepalive_ack_frame)) {
++ connection->write_lock.unlock();
++ return _fault();
++ }
+ connection->write_lock.unlock();
+
+ ldout(cct, 20) << __func__ << " got KEEPALIVE2 "
+diff --git a/src/msg/async/ProtocolV2.h b/src/msg/async/ProtocolV2.h
+index 2dbe647ae5..9897d18cf2 100644
+--- a/src/msg/async/ProtocolV2.h
++++ b/src/msg/async/ProtocolV2.h
+@@ -129,6 +129,9 @@ private:
+ CONTINUATION_TYPE<ProtocolV2> &next,
+ bufferlist &buffer);
+
++ template <class F>
++ bool append_frame(F& frame);
++
+ void requeue_sent();
+ uint64_t discard_requeued_up_to(uint64_t out_seq, uint64_t seq);
+ void reset_recv_state();
+@@ -140,8 +143,6 @@ private:
+ void prepare_send_message(uint64_t features, Message *m);
+ out_queue_entry_t _get_next_outgoing();
+ ssize_t write_message(Message *m, bool more);
+- void append_keepalive();
+- void append_keepalive_ack(utime_t &timestamp);
+ void handle_message_ack(uint64_t seq);
+
+ CONTINUATION_DECL(ProtocolV2, _wait_for_peer_banner);
+diff --git a/src/msg/async/crypto_onwire.cc b/src/msg/async/crypto_onwire.cc
+index acf3f66689..07e7fe6553 100644
+--- a/src/msg/async/crypto_onwire.cc
++++ b/src/msg/async/crypto_onwire.cc
+@@ -22,6 +22,10 @@ static constexpr const std::size_t AESGCM_BLOCK_LEN{16};
+ struct nonce_t {
+ std::uint32_t random_seq;
+ std::uint64_t random_rest;
++
++ bool operator==(const nonce_t& rhs) const {
++ return !memcmp(this, &rhs, sizeof(*this));
++ }
+ } __attribute__((packed));
+ static_assert(sizeof(nonce_t) == AESGCM_IV_LEN);
+
+@@ -35,7 +39,8 @@ class AES128GCM_OnWireTxHandler : public ceph::crypto::onwire::TxHandler {
+ CephContext* const cct;
+ std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ectx;
+ ceph::bufferlist buffer;
+- nonce_t nonce;
++ nonce_t nonce, initial_nonce;
++ bool used_initial_nonce;
+ static_assert(sizeof(nonce) == AESGCM_IV_LEN);
+
+ public:
+@@ -44,7 +49,7 @@ public:
+ const nonce_t& nonce)
+ : cct(cct),
+ ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free),
+- nonce(nonce) {
++ nonce(nonce), initial_nonce(nonce), used_initial_nonce(false) {
+ ceph_assert_always(ectx);
+ ceph_assert_always(key.size() * CHAR_BIT == 128);
+
+@@ -61,6 +66,7 @@ public:
+
+ ~AES128GCM_OnWireTxHandler() override {
+ ::ceph::crypto::zeroize_for_security(&nonce, sizeof(nonce));
++ ::ceph::crypto::zeroize_for_security(&initial_nonce, sizeof(initial_nonce));
+ }
+
+ std::uint32_t calculate_segment_size(std::uint32_t size) override
+@@ -78,6 +84,13 @@ public:
+ void AES128GCM_OnWireTxHandler::reset_tx_handler(
+ std::initializer_list<std::uint32_t> update_size_sequence)
+ {
++ if (nonce == initial_nonce) {
++ if (used_initial_nonce) {
++ throw ceph::crypto::onwire::TxHandlerError("out of nonces");
++ }
++ used_initial_nonce = true;
++ }
++
+ if(1 != EVP_EncryptInit_ex(ectx.get(), nullptr, nullptr, nullptr,
+ reinterpret_cast<const unsigned char*>(&nonce))) {
+ throw std::runtime_error("EVP_EncryptInit_ex failed");
+diff --git a/src/msg/async/crypto_onwire.h b/src/msg/async/crypto_onwire.h
+index bd682e8c71..0c544f205a 100644
+--- a/src/msg/async/crypto_onwire.h
++++ b/src/msg/async/crypto_onwire.h
+@@ -45,6 +45,11 @@ struct MsgAuthError : public std::runtime_error {
+ }
+ };
+
++struct TxHandlerError : public std::runtime_error {
++ TxHandlerError(const char* what)
++ : std::runtime_error(std::string("tx handler error: ") + what) {}
++};
++
+ struct TxHandler {
+ virtual ~TxHandler() = default;
+
+--
+2.20.1
+