]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "LoadRequest.h" | |
5 | ||
6 | #include "common/dout.h" | |
7 | #include "common/errno.h" | |
8 | #include "librbd/Utils.h" | |
9 | #include "librbd/crypto/Utils.h" | |
1e59de90 TL |
10 | #include "librbd/crypto/LoadRequest.h" |
11 | #include "librbd/crypto/luks/Magic.h" | |
f67539c2 TL |
12 | #include "librbd/io/AioCompletion.h" |
13 | #include "librbd/io/ImageDispatchSpec.h" | |
14 | #include "librbd/io/ReadResult.h" | |
15 | ||
16 | #define dout_subsys ceph_subsys_rbd | |
17 | #undef dout_prefix | |
18 | #define dout_prefix *_dout << "librbd::crypto::luks::LoadRequest: " << this \ | |
19 | << " " << __func__ << ": " | |
20 | ||
21 | namespace librbd { | |
22 | namespace crypto { | |
23 | namespace luks { | |
24 | ||
25 | using librbd::util::create_context_callback; | |
26 | ||
27 | template <typename I> | |
28 | LoadRequest<I>::LoadRequest( | |
1e59de90 TL |
29 | I* image_ctx, encryption_format_t format, std::string_view passphrase, |
30 | std::unique_ptr<CryptoInterface>* result_crypto, | |
31 | std::string* detected_format_name, | |
f67539c2 TL |
32 | Context* on_finish) : m_image_ctx(image_ctx), |
33 | m_format(format), | |
1e59de90 | 34 | m_passphrase(passphrase), |
f67539c2 TL |
35 | m_on_finish(on_finish), |
36 | m_result_crypto(result_crypto), | |
1e59de90 | 37 | m_detected_format_name(detected_format_name), |
f67539c2 TL |
38 | m_initial_read_size(DEFAULT_INITIAL_READ_SIZE), |
39 | m_header(image_ctx->cct), m_offset(0) { | |
40 | } | |
41 | ||
42 | template <typename I> | |
43 | void LoadRequest<I>::set_initial_read_size(uint64_t read_size) { | |
44 | m_initial_read_size = read_size; | |
45 | } | |
46 | ||
47 | template <typename I> | |
48 | void LoadRequest<I>::send() { | |
f67539c2 TL |
49 | auto ctx = create_context_callback< |
50 | LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this); | |
51 | read(m_initial_read_size, ctx); | |
52 | } | |
53 | ||
54 | template <typename I> | |
55 | void LoadRequest<I>::read(uint64_t end_offset, Context* on_finish) { | |
56 | auto length = end_offset - m_offset; | |
57 | auto aio_comp = io::AioCompletion::create_and_start( | |
58 | on_finish, librbd::util::get_image_ctx(m_image_ctx), | |
59 | io::AIO_TYPE_READ); | |
60 | ZTracer::Trace trace; | |
61 | auto req = io::ImageDispatchSpec::create_read( | |
62 | *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp, | |
1e59de90 | 63 | {{m_offset, length}}, io::ImageArea::DATA, io::ReadResult{&m_bl}, |
f67539c2 TL |
64 | m_image_ctx->get_data_io_context(), 0, 0, trace); |
65 | req->send(); | |
66 | } | |
67 | ||
68 | template <typename I> | |
69 | bool LoadRequest<I>::handle_read(int r) { | |
1e59de90 TL |
70 | ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; |
71 | ||
f67539c2 TL |
72 | if (r < 0) { |
73 | lderr(m_image_ctx->cct) << "error reading from image: " << cpp_strerror(r) | |
74 | << dendl; | |
75 | finish(r); | |
76 | return false; | |
77 | } | |
78 | ||
1e59de90 TL |
79 | // first, check LUKS magic at the beginning of the image |
80 | // If no magic is detected, caller may assume image is actually plaintext | |
81 | if (m_offset == 0) { | |
82 | if (Magic::is_luks(m_bl) > 0 || Magic::is_rbd_clone(m_bl) > 0) { | |
83 | *m_detected_format_name = "LUKS"; | |
84 | } else { | |
85 | *m_detected_format_name = crypto::LoadRequest<I>::UNKNOWN_FORMAT; | |
86 | finish(-EINVAL); | |
87 | return false; | |
88 | } | |
89 | ||
90 | if (m_image_ctx->parent != nullptr && Magic::is_rbd_clone(m_bl) > 0) { | |
91 | r = Magic::replace_magic(m_image_ctx->cct, m_bl); | |
92 | if (r < 0) { | |
93 | m_image_ctx->image_lock.lock_shared(); | |
94 | auto image_size = m_image_ctx->get_image_size(m_image_ctx->snap_id); | |
95 | m_image_ctx->image_lock.unlock_shared(); | |
96 | ||
97 | auto max_header_size = std::min(MAXIMUM_HEADER_SIZE, image_size); | |
98 | ||
99 | if (r == -EINVAL && m_bl.length() < max_header_size) { | |
100 | m_bl.clear(); | |
101 | auto ctx = create_context_callback< | |
102 | LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this); | |
103 | read(max_header_size, ctx); | |
104 | return false; | |
105 | } | |
106 | ||
107 | lderr(m_image_ctx->cct) << "error replacing rbd clone magic: " | |
108 | << cpp_strerror(r) << dendl; | |
109 | finish(r); | |
110 | return false; | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
115 | // setup interface with libcryptsetup | |
116 | r = m_header.init(); | |
117 | if (r < 0) { | |
118 | finish(r); | |
119 | return false; | |
120 | } | |
121 | ||
122 | m_offset += m_bl.length(); | |
123 | ||
f67539c2 TL |
124 | // write header to libcryptsetup interface |
125 | r = m_header.write(m_bl); | |
126 | if (r < 0) { | |
127 | finish(r); | |
128 | return false; | |
129 | } | |
130 | ||
f67539c2 | 131 | m_bl.clear(); |
1e59de90 | 132 | |
f67539c2 TL |
133 | return true; |
134 | } | |
135 | ||
136 | template <typename I> | |
137 | void LoadRequest<I>::handle_read_header(int r) { | |
1e59de90 TL |
138 | ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; |
139 | ||
f67539c2 TL |
140 | if (!handle_read(r)) { |
141 | return; | |
142 | } | |
143 | ||
144 | const char* type; | |
145 | switch (m_format) { | |
1e59de90 TL |
146 | case RBD_ENCRYPTION_FORMAT_LUKS: |
147 | type = CRYPT_LUKS; | |
148 | break; | |
149 | case RBD_ENCRYPTION_FORMAT_LUKS1: | |
150 | type = CRYPT_LUKS1; | |
151 | break; | |
152 | case RBD_ENCRYPTION_FORMAT_LUKS2: | |
153 | type = CRYPT_LUKS2; | |
154 | break; | |
155 | default: | |
156 | lderr(m_image_ctx->cct) << "unsupported format type: " << m_format | |
157 | << dendl; | |
158 | finish(-EINVAL); | |
159 | return; | |
f67539c2 TL |
160 | } |
161 | ||
162 | // parse header via libcryptsetup | |
163 | r = m_header.load(type); | |
164 | if (r != 0) { | |
165 | if (m_offset < MAXIMUM_HEADER_SIZE) { | |
166 | // perhaps we did not feed the entire header to libcryptsetup, retry | |
167 | auto ctx = create_context_callback< | |
168 | LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this); | |
169 | read(MAXIMUM_HEADER_SIZE, ctx); | |
170 | return; | |
171 | } | |
172 | ||
173 | finish(r); | |
174 | return; | |
175 | } | |
176 | ||
1e59de90 TL |
177 | // gets actual LUKS version (only used for logging) |
178 | ceph_assert(*m_detected_format_name == "LUKS"); | |
179 | *m_detected_format_name = m_header.get_format_name(); | |
180 | ||
f67539c2 TL |
181 | auto cipher = m_header.get_cipher(); |
182 | if (strcmp(cipher, "aes") != 0) { | |
183 | lderr(m_image_ctx->cct) << "unsupported cipher: " << cipher << dendl; | |
184 | finish(-ENOTSUP); | |
185 | return; | |
186 | } | |
187 | ||
188 | auto cipher_mode = m_header.get_cipher_mode(); | |
189 | if (strcmp(cipher_mode, "xts-plain64") != 0) { | |
190 | lderr(m_image_ctx->cct) << "unsupported cipher mode: " << cipher_mode | |
191 | << dendl; | |
192 | finish(-ENOTSUP); | |
193 | return; | |
194 | } | |
195 | ||
1e59de90 TL |
196 | m_image_ctx->image_lock.lock_shared(); |
197 | uint64_t image_size = m_image_ctx->get_image_size(CEPH_NOSNAP); | |
198 | m_image_ctx->image_lock.unlock_shared(); | |
199 | ||
200 | if (m_header.get_data_offset() > image_size) { | |
201 | lderr(m_image_ctx->cct) << "image is too small, data offset " | |
202 | << m_header.get_data_offset() << dendl; | |
203 | finish(-EINVAL); | |
204 | return; | |
205 | } | |
206 | ||
207 | uint64_t stripe_period = m_image_ctx->get_stripe_period(); | |
208 | if (m_header.get_data_offset() % stripe_period != 0) { | |
209 | lderr(m_image_ctx->cct) << "incompatible stripe pattern, data offset " | |
210 | << m_header.get_data_offset() << dendl; | |
211 | finish(-EINVAL); | |
212 | return; | |
213 | } | |
214 | ||
f67539c2 TL |
215 | read_volume_key(); |
216 | return; | |
217 | } | |
218 | ||
219 | template <typename I> | |
220 | void LoadRequest<I>::handle_read_keyslots(int r) { | |
1e59de90 TL |
221 | ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; |
222 | ||
f67539c2 TL |
223 | if (!handle_read(r)) { |
224 | return; | |
225 | } | |
226 | ||
227 | read_volume_key(); | |
228 | } | |
229 | ||
230 | template <typename I> | |
231 | void LoadRequest<I>::read_volume_key() { | |
232 | char volume_key[64]; | |
233 | size_t volume_key_size = sizeof(volume_key); | |
234 | ||
235 | auto r = m_header.read_volume_key( | |
1e59de90 | 236 | m_passphrase.data(), m_passphrase.size(), |
f67539c2 TL |
237 | reinterpret_cast<char*>(volume_key), &volume_key_size); |
238 | if (r != 0) { | |
239 | auto keyslots_end_offset = m_header.get_data_offset(); | |
240 | if (m_offset < keyslots_end_offset) { | |
241 | // perhaps we did not feed the the necessary keyslot, retry | |
242 | auto ctx = create_context_callback< | |
243 | LoadRequest<I>, &LoadRequest<I>::handle_read_keyslots>(this); | |
244 | read(keyslots_end_offset, ctx); | |
245 | return; | |
246 | } | |
247 | ||
248 | finish(r); | |
249 | return; | |
250 | } | |
251 | ||
252 | r = util::build_crypto( | |
253 | m_image_ctx->cct, reinterpret_cast<unsigned char*>(volume_key), | |
254 | volume_key_size, m_header.get_sector_size(), | |
255 | m_header.get_data_offset(), m_result_crypto); | |
1e59de90 | 256 | ceph_memzero_s(volume_key, 64, 64); |
f67539c2 TL |
257 | finish(r); |
258 | } | |
259 | ||
260 | template <typename I> | |
261 | void LoadRequest<I>::finish(int r) { | |
1e59de90 TL |
262 | ldout(m_image_ctx->cct, 20) << "r=" << r << dendl; |
263 | ||
f67539c2 TL |
264 | m_on_finish->complete(r); |
265 | delete this; | |
266 | } | |
267 | ||
268 | } // namespace luks | |
269 | } // namespace crypto | |
270 | } // namespace librbd | |
271 | ||
272 | template class librbd::crypto::luks::LoadRequest<librbd::ImageCtx>; |