From: Radoslaw Zarzynski Date: Wed, 27 Feb 2019 20:49:39 +0000 (+0100) Subject: msg/async, v2: use frame epilogue for crc32 integrity checking. X-Git-Tag: v14.1.1~70^2~13 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=7ba670a26a7eeecf2beb106f61460f2b0d55f38e;p=ceph.git msg/async, v2: use frame epilogue for crc32 integrity checking. Signed-off-by: Radoslaw Zarzynski --- diff --git a/src/msg/async/ProtocolV2.cc b/src/msg/async/ProtocolV2.cc index 28e3196a75c41..71ed6065e7a4c 100644 --- a/src/msg/async/ProtocolV2.cc +++ b/src/msg/async/ProtocolV2.cc @@ -520,50 +520,12 @@ ssize_t ProtocolV2::write_message(Message *m, bool more) { sizeof(header2) - sizeof(header2.header_crc)); } - auto message = MessageHeaderFrame::Encode(header2, - m->get_payload().length(), - m->get_middle().length(), - m->get_data().length()); - if (auth_meta->is_mode_secure()) { - ceph_assert(session_stream_handlers.tx); - - // let's cipher allocate one huge buffer for entire ciphertext. - // NOTE: ultimately we'll align these sizes to cipher's block size. - // AES-GCM can live without that as it's basically stream cipher. - session_stream_handlers.tx->reset_tx_handler({ - message.get_buffer().length(), - m->get_payload().length(), - m->get_middle().length(), - m->get_data().length() - }); - - ceph_assert(message.get_buffer().length()); - session_stream_handlers.tx->authenticated_encrypt_update( - std::move(message.get_buffer())); - - // receiver uses "front" for "payload" - // TODO: switch TxHandler from `bl&&` to `const bl&`. - if (m->get_payload().length()) { - session_stream_handlers.tx->authenticated_encrypt_update( - m->get_payload()); - } - if (m->get_middle().length()) { - session_stream_handlers.tx->authenticated_encrypt_update( - m->get_middle()); - } - if (m->get_data().length()) { - session_stream_handlers.tx->authenticated_encrypt_update( - m->get_data()); - } - - auto cipherbl = session_stream_handlers.tx->authenticated_encrypt_final(); - connection->outcoming_bl.claim_append(cipherbl); - } else { - connection->outcoming_bl.claim_append(message.get_buffer()); - connection->outcoming_bl.append(m->get_payload()); - connection->outcoming_bl.append(m->get_middle()); - connection->outcoming_bl.append(m->get_data()); - } + auto message = MessageHeaderFrame::Encode(session_stream_handlers, + header2, + m->get_payload(), + m->get_middle(), + m->get_data()); + connection->outcoming_bl.claim_append(message.get_buffer()); ldout(cct, 5) << __func__ << " sending message m=" << m << " seq=" << m->get_seq() << " " << *m << dendl; @@ -1241,11 +1203,7 @@ CtPtr ProtocolV2::handle_read_frame_segment(char *buffer, int r) { if (rx_segments_desc.size() == rx_segments_data.size()) { // OK, all segments planned to read are read. Can go with epilogue. - if (session_stream_handlers.rx) { - return READ(FRAME_EPILOGUE_SIZE, handle_read_frame_epilogue_main); - } else { - return handle_read_frame_dispatch(); - } + return READ(FRAME_EPILOGUE_SIZE, handle_read_frame_epilogue_main); } else { // TODO: for makeshift only. This will be more generic and throttled return read_frame_segment(); @@ -1433,15 +1391,11 @@ CtPtr ProtocolV2::read_message_data() { } state = READ_MESSAGE_COMPLETE; - // TODO: implement epilogue for non-secure frames - if (session_stream_handlers.rx) { - return READ(FRAME_EPILOGUE_SIZE, handle_read_frame_epilogue_main); - } else { - return handle_read_frame_dispatch(); - } + return READ(FRAME_EPILOGUE_SIZE, handle_read_frame_epilogue_main); } -CtPtr ProtocolV2::handle_read_frame_epilogue_main(char *buffer, int r) { +CtPtr ProtocolV2::handle_read_frame_epilogue_main(char *buffer, int r) +{ ldout(cct, 20) << __func__ << " r=" << r << dendl; if (r < 0) { @@ -1449,10 +1403,7 @@ CtPtr ProtocolV2::handle_read_frame_epilogue_main(char *buffer, int r) { return _fault(); } - // I expect that ::temp_buffer is being used here. - ceph::bufferlist epilogue; - epilogue.push_back(buffer::create_static(FRAME_EPILOGUE_SIZE, buffer)); - + auto& epilogue = reinterpret_cast(*buffer); // FIXME: if (auth_meta->is_mode_secure()) { if (session_stream_handlers.rx) { @@ -1464,14 +1415,36 @@ CtPtr ProtocolV2::handle_read_frame_epilogue_main(char *buffer, int r) { ceph_assert(session_stream_handlers.rx); ceph_assert(FRAME_EPILOGUE_SIZE == \ session_stream_handlers.rx->get_extra_size_at_final()); + + // I expect that ::temp_buffer is being used here. + ceph::bufferlist epilogue_bl; + epilogue_bl.push_back(buffer::create_static(FRAME_EPILOGUE_SIZE, + epilogue.auth_tag)); try { session_stream_handlers.rx->authenticated_decrypt_update_final( - std::move(epilogue), segment_t::DEFAULT_ALIGNMENT); + std::move(epilogue_bl), segment_t::DEFAULT_ALIGNMENT); } catch (ceph::crypto::onwire::MsgAuthError &e) { ldout(cct, 5) << __func__ << " message authentication failed: " << e.what() << dendl; return _fault(); } + } else { + for (std::uint8_t idx = 0; idx < rx_segments_data.size(); idx++) { + const __u32 expected_crc = epilogue.crc_values[idx]; + const __u32 calculated_crc = rx_segments_data[idx].crc32c(-1); + if (expected_crc != calculated_crc) { + ldout(cct, 5) << __func__ << " message integrity check failed: " + << " expected_crc=" << expected_crc + << " calculated_crc=" << calculated_crc + << dendl; + return _fault(); + } else { + ldout(cct, 20) << __func__ << " message integrity check success: " + << " expected_crc=" << expected_crc + << " calculated_crc=" << calculated_crc + << dendl; + } + } } return handle_read_frame_dispatch(); diff --git a/src/msg/async/ProtocolV2.h b/src/msg/async/ProtocolV2.h index defc20561b026..1385885c9fdb0 100644 --- a/src/msg/async/ProtocolV2.h +++ b/src/msg/async/ProtocolV2.h @@ -86,30 +86,7 @@ private: public: - struct onwire_segment_t { - // crypto-processed segment can be expanded on-wire because of: - // * padding to achieve CRYPTO_BLOCK_SIZE alignment, - // * authentication tag. It's appended at the end of message. - // See RxHandler::get_extra_size_at_final(). - __le32 onwire_length; - - struct ceph::msgr::v2::segment_t logical; - } __attribute__((packed)); - - struct SegmentIndex { - struct Msg { - static constexpr std::size_t HEADER = 0; - static constexpr std::size_t FRONT = 1; - static constexpr std::size_t MIDDLE = 2; - static constexpr std::size_t DATA = 3; - }; - - struct Frame { - static constexpr std::size_t PAYLOAD = 0; - }; - }; - - boost::container::static_vector rx_segments_desc; boost::container::static_vector rx_segments_data; diff --git a/src/msg/async/frames_v2.h b/src/msg/async/frames_v2.h index 08a1eaa3c8357..f1c60db9a4f44 100644 --- a/src/msg/async/frames_v2.h +++ b/src/msg/async/frames_v2.h @@ -63,6 +63,29 @@ struct segment_t { __le16 alignment; } __attribute__((packed)); +struct onwire_segment_t { + // crypto-processed segment can be expanded on-wire because of: + // * padding to achieve CRYPTO_BLOCK_SIZE alignment, + // * authentication tag. It's appended at the end of message. + // See RxHandler::get_extra_size_at_final(). + __le32 onwire_length; + + struct ceph::msgr::v2::segment_t logical; +} __attribute__((packed)); + +struct SegmentIndex { + struct Msg { + static constexpr std::size_t HEADER = 0; + static constexpr std::size_t FRONT = 1; + static constexpr std::size_t MIDDLE = 2; + static constexpr std::size_t DATA = 3; + }; + + struct Frame { + static constexpr std::size_t PAYLOAD = 0; + }; +}; + static constexpr uint8_t CRYPTO_BLOCK_SIZE { 16 }; static constexpr std::size_t MAX_NUM_SEGMENTS = 4; @@ -96,8 +119,8 @@ static_assert(std::is_standard_layout::value); // * CRC32 for MAX_NUM_SEGMENTS -- in plain mode, // * cipher-specific data (e.g. auth tag for AES-GCM). union epilogue_block_t { - // TODO: add crc32 for plain mode - std::array auth_tag; + char auth_tag[CRYPTO_BLOCK_SIZE]; + std::array<__le32, MAX_NUM_SEGMENTS> crc_values; }; static_assert(sizeof(epilogue_block_t) % CRYPTO_BLOCK_SIZE == 0); static_assert(std::is_standard_layout::value); @@ -153,6 +176,15 @@ public: ceph::bufferlist &get_buffer() { fill_preamble({segment_t{payload.length() - FRAME_PREAMBLE_SIZE, segment_t::DEFAULT_ALIGNMENT}}); + + epilogue_block_t epilogue; + ::memset(&epilogue, 0, sizeof(epilogue)); + + ceph::bufferlist::const_iterator hdriter(&this->payload, FRAME_PREAMBLE_SIZE); + epilogue.crc_values[SegmentIndex::Frame::PAYLOAD] = + hdriter.crc32c(hdriter.get_remaining(), -1); + this->payload.append(reinterpret_cast(&epilogue), sizeof(epilogue)); + return payload; } }; @@ -350,6 +382,14 @@ struct SignedEncryptedFrame : public PayloadFrame { session_stream_handlers.tx->authenticated_encrypt_update( std::move(c.payload)); c.payload = session_stream_handlers.tx->authenticated_encrypt_final(); + } else { + epilogue_block_t epilogue; + ::memset(&epilogue, 0, sizeof(epilogue)); + + ceph::bufferlist::const_iterator hdriter(&c.payload, FRAME_PREAMBLE_SIZE); + epilogue.crc_values[SegmentIndex::Frame::PAYLOAD] = + hdriter.crc32c(hdriter.get_remaining(), -1); + c.payload.append(reinterpret_cast(&epilogue), sizeof(epilogue)); } return c; } @@ -561,20 +601,67 @@ struct MessageHeaderFrame return this->payload; } - static MessageHeaderFrame Encode(const ceph_msg_header2 &msg_header, - const uint32_t front_len, - const uint32_t middle_len, - const uint32_t data_len) { + static MessageHeaderFrame Encode(ceph::crypto::onwire::rxtx_t &session_stream_handlers, + const ceph_msg_header2 &msg_header, + const ceph::bufferlist& front, + const ceph::bufferlist& middle, + const ceph::bufferlist& data) { MessageHeaderFrame f = PayloadFrame::Encode(msg_header); // FIXME: plainsize -> ciphersize; for AES-GCM they are equall apart from auth tag size f.fill_preamble({ segment_t{ f.payload.length() - FRAME_PREAMBLE_SIZE, - segment_t::DEFAULT_ALIGNMENT }, - segment_t{ front_len, segment_t::DEFAULT_ALIGNMENT }, - segment_t{ middle_len, segment_t::DEFAULT_ALIGNMENT }, - segment_t{ data_len, segment_t::DEFERRED_ALLOCATION }, + segment_t::DEFAULT_ALIGNMENT }, + segment_t{ front.length(), segment_t::DEFAULT_ALIGNMENT }, + segment_t{ middle.length(), segment_t::DEFAULT_ALIGNMENT }, + segment_t{ data.length(), segment_t::DEFERRED_ALLOCATION }, }); + + // FIXME: plainsize -> ciphersize; for AES-GCM they are equall apart from auth tag size + if (session_stream_handlers.tx) { + // let's cipher allocate one huge buffer for entire ciphertext. + // NOTE: ultimately we'll align these sizes to cipher's block size. + // AES-GCM can live without that as it's basically stream cipher. + session_stream_handlers.tx->reset_tx_handler({ + f.payload.length(), + front.length(), + middle.length(), + data.length() + }); + + ceph_assert(f.payload.length()); + session_stream_handlers.tx->authenticated_encrypt_update( + std::move(f.payload)); + + // TODO: switch TxHandler from `bl&&` to `const bl&`. + if (front.length()) { + session_stream_handlers.tx->authenticated_encrypt_update(front); + } + if (middle.length()) { + session_stream_handlers.tx->authenticated_encrypt_update(middle); + } + if (data.length()) { + session_stream_handlers.tx->authenticated_encrypt_update(data); + } + + // auth tag will be appended at the end + f.payload = session_stream_handlers.tx->authenticated_encrypt_final(); + } else { + f.payload.append(front); + f.payload.append(middle); + f.payload.append(data); + + epilogue_block_t epilogue; + ceph::bufferlist::const_iterator hdriter(&f.payload, FRAME_PREAMBLE_SIZE); + epilogue.crc_values[SegmentIndex::Msg::HEADER] = + hdriter.crc32c(hdriter.get_remaining(), -1); + epilogue.crc_values[SegmentIndex::Msg::FRONT] = front.crc32c(-1), + epilogue.crc_values[SegmentIndex::Msg::MIDDLE] = middle.crc32c(-1), + epilogue.crc_values[SegmentIndex::Msg::DATA] = data.crc32c(-1), + + f.payload.append(reinterpret_cast(&epilogue), sizeof(epilogue)); + } + return f; }