]> git.proxmox.com Git - ceph.git/blame - ceph/src/msg/async/frames_v2.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / msg / async / frames_v2.cc
CommitLineData
f6b5b4d7
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2020 Red Hat
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15#include "frames_v2.h"
16
17#include <ostream>
18
19#include <fmt/format.h>
20
21namespace ceph::msgr::v2 {
22
23// Unpads bufferlist to unpadded_len.
24static void unpad_zero(bufferlist& bl, uint32_t unpadded_len) {
25 ceph_assert(bl.length() >= unpadded_len);
26 if (bl.length() > unpadded_len) {
27 bl.splice(unpadded_len, bl.length() - unpadded_len);
28 }
29}
30
31// Discards trailing empty segments, unless there is just one segment.
32// A frame always has at least one (possibly empty) segment.
33static size_t calc_num_segments(const bufferlist segment_bls[],
34 size_t segment_count) {
35 ceph_assert(segment_count > 0 && segment_count <= MAX_NUM_SEGMENTS);
36 for (size_t i = segment_count; i-- > 0; ) {
37 if (segment_bls[i].length() > 0) {
38 return i + 1;
39 }
40 }
41 return 1;
42}
43
44static void check_segment_crc(const bufferlist& segment_bl,
45 uint32_t expected_crc) {
46 uint32_t crc = segment_bl.crc32c(-1);
47 if (crc != expected_crc) {
48 throw FrameError(fmt::format(
49 "bad segment crc calculated={} expected={}", crc, expected_crc));
50 }
51}
52
53// Returns true if the frame is ready for dispatching, or false if
54// it was aborted by the sender and must be dropped.
55static bool check_epilogue_late_status(__u8 late_status) {
56 __u8 aborted = late_status & FRAME_LATE_STATUS_ABORTED_MASK;
57 if (aborted != FRAME_LATE_STATUS_ABORTED &&
58 aborted != FRAME_LATE_STATUS_COMPLETE) {
59 throw FrameError(fmt::format("bad late_status"));
60 }
61 return aborted == FRAME_LATE_STATUS_COMPLETE;
62}
63
64void FrameAssembler::fill_preamble(Tag tag,
65 preamble_block_t& preamble) const {
66 // FIPS zeroization audit 20191115: this memset is not security related.
67 ::memset(&preamble, 0, sizeof(preamble));
68
69 preamble.tag = static_cast<__u8>(tag);
70 for (size_t i = 0; i < m_descs.size(); i++) {
71 preamble.segments[i].length = m_descs[i].logical_len;
72 preamble.segments[i].alignment = m_descs[i].align;
73 }
74 preamble.num_segments = m_descs.size();
75 preamble.crc = ceph_crc32c(
76 0, reinterpret_cast<const unsigned char*>(&preamble),
77 sizeof(preamble) - sizeof(preamble.crc));
78}
79
80uint64_t FrameAssembler::get_frame_logical_len() const {
81 ceph_assert(!m_descs.empty());
82 uint64_t logical_len = 0;
83 for (size_t i = 0; i < m_descs.size(); i++) {
84 logical_len += m_descs[i].logical_len;
85 }
86 return logical_len;
87}
88
89uint64_t FrameAssembler::get_frame_onwire_len() const {
90 ceph_assert(!m_descs.empty());
91 uint64_t onwire_len = get_preamble_onwire_len();
92 for (size_t i = 0; i < m_descs.size(); i++) {
93 onwire_len += get_segment_onwire_len(i);
94 }
95 onwire_len += get_epilogue_onwire_len();
96 return onwire_len;
97}
98
99bufferlist FrameAssembler::asm_crc_rev0(const preamble_block_t& preamble,
100 bufferlist segment_bls[]) const {
101 epilogue_crc_rev0_block_t epilogue;
102 // FIPS zeroization audit 20191115: this memset is not security related.
103 ::memset(&epilogue, 0, sizeof(epilogue));
104
105 bufferlist frame_bl(sizeof(preamble) + sizeof(epilogue));
106 frame_bl.append(reinterpret_cast<const char*>(&preamble), sizeof(preamble));
107 for (size_t i = 0; i < m_descs.size(); i++) {
108 ceph_assert(segment_bls[i].length() == m_descs[i].logical_len);
109 epilogue.crc_values[i] = segment_bls[i].crc32c(-1);
110 if (segment_bls[i].length() > 0) {
111 frame_bl.claim_append(segment_bls[i]);
112 }
113 }
114 frame_bl.append(reinterpret_cast<const char*>(&epilogue), sizeof(epilogue));
115 return frame_bl;
116}
117
118bufferlist FrameAssembler::asm_secure_rev0(const preamble_block_t& preamble,
119 bufferlist segment_bls[]) const {
120 bufferlist preamble_bl(sizeof(preamble));
121 preamble_bl.append(reinterpret_cast<const char*>(&preamble),
122 sizeof(preamble));
123
124 epilogue_secure_rev0_block_t epilogue;
125 // FIPS zeroization audit 20191115: this memset is not security related.
126 ::memset(&epilogue, 0, sizeof(epilogue));
127 bufferlist epilogue_bl(sizeof(epilogue));
128 epilogue_bl.append(reinterpret_cast<const char*>(&epilogue),
129 sizeof(epilogue));
130
131 // preamble + MAX_NUM_SEGMENTS + epilogue
132 uint32_t onwire_lens[MAX_NUM_SEGMENTS + 2];
133 onwire_lens[0] = preamble_bl.length();
134 for (size_t i = 0; i < m_descs.size(); i++) {
135 onwire_lens[i + 1] = segment_bls[i].length(); // already padded
136 }
137 onwire_lens[m_descs.size() + 1] = epilogue_bl.length();
138 m_crypto->tx->reset_tx_handler(onwire_lens,
139 onwire_lens + m_descs.size() + 2);
140 m_crypto->tx->authenticated_encrypt_update(preamble_bl);
141 for (size_t i = 0; i < m_descs.size(); i++) {
142 if (segment_bls[i].length() > 0) {
143 m_crypto->tx->authenticated_encrypt_update(segment_bls[i]);
144 }
145 }
146 m_crypto->tx->authenticated_encrypt_update(epilogue_bl);
147 return m_crypto->tx->authenticated_encrypt_final();
148}
149
150bufferlist FrameAssembler::asm_crc_rev1(const preamble_block_t& preamble,
151 bufferlist segment_bls[]) const {
152 epilogue_crc_rev1_block_t epilogue;
153 // FIPS zeroization audit 20191115: this memset is not security related.
154 ::memset(&epilogue, 0, sizeof(epilogue));
155 epilogue.late_status |= FRAME_LATE_STATUS_COMPLETE;
156
157 bufferlist frame_bl(sizeof(preamble) + FRAME_CRC_SIZE + sizeof(epilogue));
158 frame_bl.append(reinterpret_cast<const char*>(&preamble), sizeof(preamble));
159
160 ceph_assert(segment_bls[0].length() == m_descs[0].logical_len);
161 if (segment_bls[0].length() > 0) {
162 uint32_t crc = segment_bls[0].crc32c(-1);
163 frame_bl.claim_append(segment_bls[0]);
164 encode(crc, frame_bl);
165 }
166 if (m_descs.size() == 1) {
167 return frame_bl; // no epilogue if only one segment
168 }
169
170 for (size_t i = 1; i < m_descs.size(); i++) {
171 ceph_assert(segment_bls[i].length() == m_descs[i].logical_len);
172 epilogue.crc_values[i - 1] = segment_bls[i].crc32c(-1);
173 if (segment_bls[i].length() > 0) {
174 frame_bl.claim_append(segment_bls[i]);
175 }
176 }
177 frame_bl.append(reinterpret_cast<const char*>(&epilogue), sizeof(epilogue));
178 return frame_bl;
179}
180
181bufferlist FrameAssembler::asm_secure_rev1(const preamble_block_t& preamble,
182 bufferlist segment_bls[]) const {
183 bufferlist preamble_bl;
184 if (segment_bls[0].length() > FRAME_PREAMBLE_INLINE_SIZE) {
185 // first segment is partially inlined, inline buffer is full
186 preamble_bl.reserve(sizeof(preamble));
187 preamble_bl.append(reinterpret_cast<const char*>(&preamble),
188 sizeof(preamble));
189 segment_bls[0].splice(0, FRAME_PREAMBLE_INLINE_SIZE, &preamble_bl);
190 } else {
191 // first segment is fully inlined, inline buffer may need padding
192 uint32_t pad_len = FRAME_PREAMBLE_INLINE_SIZE - segment_bls[0].length();
193 preamble_bl.reserve(sizeof(preamble) + pad_len);
194 preamble_bl.append(reinterpret_cast<const char*>(&preamble),
195 sizeof(preamble));
196 preamble_bl.claim_append(segment_bls[0]);
197 if (pad_len > 0) {
198 preamble_bl.append_zero(pad_len);
199 }
200 }
201
202 m_crypto->tx->reset_tx_handler({preamble_bl.length()});
203 m_crypto->tx->authenticated_encrypt_update(preamble_bl);
204 auto frame_bl = m_crypto->tx->authenticated_encrypt_final();
205
206 if (segment_bls[0].length() > 0) {
207 m_crypto->tx->reset_tx_handler({segment_bls[0].length()});
208 m_crypto->tx->authenticated_encrypt_update(segment_bls[0]);
f67539c2 209 frame_bl.claim_append(m_crypto->tx->authenticated_encrypt_final());
f6b5b4d7
TL
210 }
211 if (m_descs.size() == 1) {
212 return frame_bl; // no epilogue if only one segment
213 }
214
215 epilogue_secure_rev1_block_t epilogue;
216 // FIPS zeroization audit 20191115: this memset is not security related.
217 ::memset(&epilogue, 0, sizeof(epilogue));
218 epilogue.late_status |= FRAME_LATE_STATUS_COMPLETE;
219 bufferlist epilogue_bl(sizeof(epilogue));
220 epilogue_bl.append(reinterpret_cast<const char*>(&epilogue),
221 sizeof(epilogue));
222
223 // MAX_NUM_SEGMENTS - 1 + epilogue
224 uint32_t onwire_lens[MAX_NUM_SEGMENTS];
225 for (size_t i = 1; i < m_descs.size(); i++) {
226 onwire_lens[i - 1] = segment_bls[i].length(); // already padded
227 }
228 onwire_lens[m_descs.size() - 1] = epilogue_bl.length();
229 m_crypto->tx->reset_tx_handler(onwire_lens, onwire_lens + m_descs.size());
230 for (size_t i = 1; i < m_descs.size(); i++) {
231 if (segment_bls[i].length() > 0) {
232 m_crypto->tx->authenticated_encrypt_update(segment_bls[i]);
233 }
234 }
235 m_crypto->tx->authenticated_encrypt_update(epilogue_bl);
f67539c2 236 frame_bl.claim_append(m_crypto->tx->authenticated_encrypt_final());
f6b5b4d7
TL
237 return frame_bl;
238}
239
240bufferlist FrameAssembler::assemble_frame(Tag tag, bufferlist segment_bls[],
241 const uint16_t segment_aligns[],
242 size_t segment_count) {
243 m_descs.resize(calc_num_segments(segment_bls, segment_count));
244 for (size_t i = 0; i < m_descs.size(); i++) {
245 m_descs[i].logical_len = segment_bls[i].length();
246 m_descs[i].align = segment_aligns[i];
247 }
248
249 preamble_block_t preamble;
250 fill_preamble(tag, preamble);
251
252 if (m_crypto->rx) {
253 for (size_t i = 0; i < m_descs.size(); i++) {
254 ceph_assert(segment_bls[i].length() == m_descs[i].logical_len);
255 // We're padding segments to biggest cipher's block size. Although
256 // AES-GCM can live without that as it's a stream cipher, we don't
257 // want to be fixed to stream ciphers only.
258 uint32_t padded_len = get_segment_padded_len(i);
259 if (padded_len > segment_bls[i].length()) {
260 uint32_t pad_len = padded_len - segment_bls[i].length();
261 segment_bls[i].reserve(pad_len);
262 segment_bls[i].append_zero(pad_len);
263 }
264 }
265 if (m_is_rev1) {
266 return asm_secure_rev1(preamble, segment_bls);
267 }
268 return asm_secure_rev0(preamble, segment_bls);
269 }
270 if (m_is_rev1) {
271 return asm_crc_rev1(preamble, segment_bls);
272 }
273 return asm_crc_rev0(preamble, segment_bls);
274}
275
276Tag FrameAssembler::disassemble_preamble(bufferlist& preamble_bl) {
277 if (m_crypto->rx) {
278 m_crypto->rx->reset_rx_handler();
279 if (m_is_rev1) {
280 ceph_assert(preamble_bl.length() == FRAME_PREAMBLE_WITH_INLINE_SIZE +
281 get_auth_tag_len());
282 m_crypto->rx->authenticated_decrypt_update_final(preamble_bl);
283 } else {
284 ceph_assert(preamble_bl.length() == sizeof(preamble_block_t));
285 m_crypto->rx->authenticated_decrypt_update(preamble_bl);
286 }
287 } else {
288 ceph_assert(preamble_bl.length() == sizeof(preamble_block_t));
289 }
290
291 // I expect ceph_le32 will make the endian conversion for me. Passing
292 // everything through ::Decode is unnecessary.
293 auto preamble = reinterpret_cast<const preamble_block_t*>(
294 preamble_bl.c_str());
295 // check preamble crc before any further processing
296 uint32_t crc = ceph_crc32c(
297 0, reinterpret_cast<const unsigned char*>(preamble),
298 sizeof(*preamble) - sizeof(preamble->crc));
299 if (crc != preamble->crc) {
300 throw FrameError(fmt::format(
301 "bad preamble crc calculated={} expected={}", crc, preamble->crc));
302 }
303
304 // see calc_num_segments()
305 if (preamble->num_segments < 1 ||
306 preamble->num_segments > MAX_NUM_SEGMENTS) {
307 throw FrameError(fmt::format(
308 "bad number of segments num_segments={}", preamble->num_segments));
309 }
310 if (preamble->num_segments > 1 &&
311 preamble->segments[preamble->num_segments - 1].length == 0) {
312 throw FrameError("last segment empty");
313 }
314
315 m_descs.resize(preamble->num_segments);
316 for (size_t i = 0; i < m_descs.size(); i++) {
317 m_descs[i].logical_len = preamble->segments[i].length;
318 m_descs[i].align = preamble->segments[i].alignment;
319 }
320 return static_cast<Tag>(preamble->tag);
321}
322
323bool FrameAssembler::disasm_all_crc_rev0(bufferlist segment_bls[],
324 bufferlist& epilogue_bl) const {
325 ceph_assert(epilogue_bl.length() == sizeof(epilogue_crc_rev0_block_t));
326 auto epilogue = reinterpret_cast<const epilogue_crc_rev0_block_t*>(
327 epilogue_bl.c_str());
328
329 for (size_t i = 0; i < m_descs.size(); i++) {
330 ceph_assert(segment_bls[i].length() == m_descs[i].logical_len);
331 check_segment_crc(segment_bls[i], epilogue->crc_values[i]);
332 }
333 return !(epilogue->late_flags & FRAME_LATE_FLAG_ABORTED);
334}
335
336bool FrameAssembler::disasm_all_secure_rev0(bufferlist segment_bls[],
337 bufferlist& epilogue_bl) const {
338 for (size_t i = 0; i < m_descs.size(); i++) {
339 ceph_assert(segment_bls[i].length() == get_segment_padded_len(i));
340 if (segment_bls[i].length() > 0) {
341 m_crypto->rx->authenticated_decrypt_update(segment_bls[i]);
342 unpad_zero(segment_bls[i], m_descs[i].logical_len);
343 }
344 }
345
346 ceph_assert(epilogue_bl.length() == sizeof(epilogue_secure_rev0_block_t) +
347 get_auth_tag_len());
348 m_crypto->rx->authenticated_decrypt_update_final(epilogue_bl);
349 auto epilogue = reinterpret_cast<const epilogue_secure_rev0_block_t*>(
350 epilogue_bl.c_str());
351 return !(epilogue->late_flags & FRAME_LATE_FLAG_ABORTED);
352}
353
354void FrameAssembler::disasm_first_crc_rev1(bufferlist& preamble_bl,
355 bufferlist& segment_bl) const {
356 ceph_assert(preamble_bl.length() == sizeof(preamble_block_t));
357 if (m_descs[0].logical_len > 0) {
358 ceph_assert(segment_bl.length() == m_descs[0].logical_len +
359 FRAME_CRC_SIZE);
360 bufferlist::const_iterator it(&segment_bl, m_descs[0].logical_len);
361 uint32_t expected_crc;
362 decode(expected_crc, it);
363 segment_bl.splice(m_descs[0].logical_len, FRAME_CRC_SIZE);
364 check_segment_crc(segment_bl, expected_crc);
365 } else {
366 ceph_assert(segment_bl.length() == 0);
367 }
368}
369
370bool FrameAssembler::disasm_remaining_crc_rev1(bufferlist segment_bls[],
371 bufferlist& epilogue_bl) const {
372 ceph_assert(epilogue_bl.length() == sizeof(epilogue_crc_rev1_block_t));
373 auto epilogue = reinterpret_cast<const epilogue_crc_rev1_block_t*>(
374 epilogue_bl.c_str());
375
376 for (size_t i = 1; i < m_descs.size(); i++) {
377 ceph_assert(segment_bls[i].length() == m_descs[i].logical_len);
378 check_segment_crc(segment_bls[i], epilogue->crc_values[i - 1]);
379 }
380 return check_epilogue_late_status(epilogue->late_status);
381}
382
383void FrameAssembler::disasm_first_secure_rev1(bufferlist& preamble_bl,
384 bufferlist& segment_bl) const {
385 ceph_assert(preamble_bl.length() == FRAME_PREAMBLE_WITH_INLINE_SIZE);
386 uint32_t padded_len = get_segment_padded_len(0);
387 if (padded_len > FRAME_PREAMBLE_INLINE_SIZE) {
388 ceph_assert(segment_bl.length() == padded_len + get_auth_tag_len() -
389 FRAME_PREAMBLE_INLINE_SIZE);
390 m_crypto->rx->reset_rx_handler();
391 m_crypto->rx->authenticated_decrypt_update_final(segment_bl);
392 // prepend the inline buffer (already decrypted) to segment_bl
393 bufferlist tmp;
394 segment_bl.swap(tmp);
395 preamble_bl.splice(sizeof(preamble_block_t), FRAME_PREAMBLE_INLINE_SIZE,
396 &segment_bl);
f67539c2 397 segment_bl.claim_append(std::move(tmp));
f6b5b4d7
TL
398 } else {
399 ceph_assert(segment_bl.length() == 0);
400 preamble_bl.splice(sizeof(preamble_block_t), FRAME_PREAMBLE_INLINE_SIZE,
401 &segment_bl);
402 }
403 unpad_zero(segment_bl, m_descs[0].logical_len);
404 ceph_assert(segment_bl.length() == m_descs[0].logical_len);
405}
406
407bool FrameAssembler::disasm_remaining_secure_rev1(
408 bufferlist segment_bls[], bufferlist& epilogue_bl) const {
409 m_crypto->rx->reset_rx_handler();
410 for (size_t i = 1; i < m_descs.size(); i++) {
411 ceph_assert(segment_bls[i].length() == get_segment_padded_len(i));
412 if (segment_bls[i].length() > 0) {
413 m_crypto->rx->authenticated_decrypt_update(segment_bls[i]);
414 unpad_zero(segment_bls[i], m_descs[i].logical_len);
415 }
416 }
417
418 ceph_assert(epilogue_bl.length() == sizeof(epilogue_secure_rev1_block_t) +
419 get_auth_tag_len());
420 m_crypto->rx->authenticated_decrypt_update_final(epilogue_bl);
421 auto epilogue = reinterpret_cast<const epilogue_secure_rev1_block_t*>(
422 epilogue_bl.c_str());
423 return check_epilogue_late_status(epilogue->late_status);
424}
425
426void FrameAssembler::disassemble_first_segment(bufferlist& preamble_bl,
427 bufferlist& segment_bl) const {
428 ceph_assert(!m_descs.empty());
429 if (m_is_rev1) {
430 if (m_crypto->rx) {
431 disasm_first_secure_rev1(preamble_bl, segment_bl);
432 } else {
433 disasm_first_crc_rev1(preamble_bl, segment_bl);
434 }
435 } else {
436 // noop, everything is handled in disassemble_remaining_segments()
437 }
438}
439
440bool FrameAssembler::disassemble_remaining_segments(
441 bufferlist segment_bls[], bufferlist& epilogue_bl) const {
442 ceph_assert(!m_descs.empty());
443 if (m_is_rev1) {
444 if (m_descs.size() == 1) {
445 // no epilogue if only one segment
446 ceph_assert(epilogue_bl.length() == 0);
447 return true;
448 }
449 if (m_crypto->rx) {
450 return disasm_remaining_secure_rev1(segment_bls, epilogue_bl);
451 }
452 return disasm_remaining_crc_rev1(segment_bls, epilogue_bl);
453 }
454 if (m_crypto->rx) {
455 return disasm_all_secure_rev0(segment_bls, epilogue_bl);
456 }
457 return disasm_all_crc_rev0(segment_bls, epilogue_bl);
458}
459
460std::ostream& operator<<(std::ostream& os, const FrameAssembler& frame_asm) {
461 if (!frame_asm.m_descs.empty()) {
462 os << frame_asm.get_preamble_onwire_len();
463 for (size_t i = 0; i < frame_asm.m_descs.size(); i++) {
464 os << " + " << frame_asm.get_segment_onwire_len(i)
465 << " (logical " << frame_asm.m_descs[i].logical_len
466 << "/" << frame_asm.m_descs[i].align << ")";
467 }
468 os << " + " << frame_asm.get_epilogue_onwire_len() << " ";
469 }
470 os << "rev1=" << frame_asm.m_is_rev1
471 << " rx=" << frame_asm.m_crypto->rx.get()
472 << " tx=" << frame_asm.m_crypto->tx.get();
473 return os;
474}
475
476} // namespace ceph::msgr::v2