]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/crypto/luks/Header.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / librbd / crypto / luks / Header.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "Header.h"
5
6 #include <errno.h>
7 #include <unistd.h>
8 #include <sys/stat.h>
9 #include <sys/syscall.h>
10 #include "common/dout.h"
11 #include "common/errno.h"
12
13 #define dout_subsys ceph_subsys_rbd
14 #undef dout_prefix
15 #define dout_prefix *_dout << "librbd::crypto::luks::Header: " << this << " " \
16 << __func__ << ": "
17
18 namespace librbd {
19 namespace crypto {
20 namespace luks {
21
22 Header::Header(CephContext* cct) : m_cct(cct), m_fd(-1), m_cd(nullptr) {
23 }
24
25 Header::~Header() {
26 if (m_fd != -1) {
27 close(m_fd);
28 m_fd = -1;
29 }
30 if (m_cd != nullptr) {
31 crypt_free(m_cd);
32 m_cd = nullptr;
33 }
34 }
35
36 void Header::libcryptsetup_log_wrapper(int level, const char* msg, void* header) {
37 ((Header*)header)->libcryptsetup_log(level, msg);
38 }
39
40 void Header::libcryptsetup_log(int level, const char* msg) {
41 switch (level) {
42 case CRYPT_LOG_NORMAL:
43 ldout(m_cct, 5) << "[libcryptsetup] " << msg << dendl;
44 break;
45 case CRYPT_LOG_ERROR:
46 lderr(m_cct) << "[libcryptsetup] " << msg << dendl;
47 break;
48 case CRYPT_LOG_VERBOSE:
49 ldout(m_cct, 10) << "[libcryptsetup] " << msg << dendl;
50 break;
51 case CRYPT_LOG_DEBUG:
52 ldout(m_cct, 20) << "[libcryptsetup] " << msg << dendl;
53 break;
54 }
55 }
56
57 int Header::init() {
58 // create anonymous file
59 m_fd = syscall(SYS_memfd_create, "LibcryptsetupInterface", 0);
60 if (m_fd == -1) {
61 lderr(m_cct) << "error creating anonymous file: " << cpp_strerror(-errno)
62 << dendl;
63 return -errno;
64 }
65 std::string path =
66 "/proc/" + std::to_string(getpid()) + "/fd/" + std::to_string(m_fd);
67
68 if (m_cct->_conf->subsys.should_gather<dout_subsys, 20>()) {
69 crypt_set_debug_level(CRYPT_DEBUG_ALL);
70 }
71
72 // init libcryptsetup handle
73 auto r = crypt_init(&m_cd, path.c_str());
74 if (r != 0) {
75 lderr(m_cct) << "crypt_init failed: " << cpp_strerror(r) << dendl;
76 return r;
77 }
78
79 // redirect logging
80 crypt_set_log_callback(m_cd, &libcryptsetup_log_wrapper, this);
81
82 return 0;
83 }
84
85 int Header::write(const ceph::bufferlist& bl) {
86 ceph_assert(m_fd != -1);
87
88 auto r = bl.write_fd(m_fd);
89 if (r != 0) {
90 lderr(m_cct) << "error writing header: " << cpp_strerror(r) << dendl;
91 }
92 return r;
93 }
94
95 ssize_t Header::read(ceph::bufferlist* bl) {
96 ceph_assert(m_fd != -1);
97
98 // get current header size
99 struct stat st;
100 ssize_t r = fstat(m_fd, &st);
101 if (r < 0) {
102 r = -errno;
103 lderr(m_cct) << "failed to stat anonymous file: " << cpp_strerror(r)
104 << dendl;
105 return r;
106 }
107
108 r = bl->read_fd(m_fd, st.st_size);
109 if (r < 0) {
110 lderr(m_cct) << "error reading header: " << cpp_strerror(r) << dendl;
111 }
112
113 ldout(m_cct, 20) << "read size = " << r << dendl;
114 return r;
115 }
116
117 int Header::format(const char* type, const char* alg, const char* key,
118 size_t key_size, const char* cipher_mode,
119 uint32_t sector_size, uint32_t data_alignment,
120 bool insecure_fast_mode) {
121 ceph_assert(m_cd != nullptr);
122
123 ldout(m_cct, 20) << "sector size: " << sector_size << ", data alignment: "
124 << data_alignment << dendl;
125
126 // required for passing libcryptsetup device size check
127 if (ftruncate(m_fd, 4096) != 0) {
128 lderr(m_cct) << "failed to truncate anonymous file: "
129 << cpp_strerror(-errno) << dendl;
130 return -errno;
131 }
132
133 struct crypt_params_luks1 luks1params;
134 struct crypt_params_luks2 luks2params;
135
136 const size_t converted_data_alignment = data_alignment / 512;
137
138 void* params = nullptr;
139 if (strcmp(type, CRYPT_LUKS1) == 0) {
140 memset(&luks1params, 0, sizeof(luks1params));
141 luks1params.data_alignment = converted_data_alignment;
142 params = &luks1params;
143 } else if (strcmp(type, CRYPT_LUKS2) == 0) {
144 memset(&luks2params, 0, sizeof(luks2params));
145 luks2params.data_alignment = converted_data_alignment;
146 luks2params.sector_size = sector_size;
147 params = &luks2params;
148 }
149
150 // this mode should be used for testing only
151 if (insecure_fast_mode) {
152 struct crypt_pbkdf_type pbkdf;
153 memset(&pbkdf, 0, sizeof(pbkdf));
154 pbkdf.type = CRYPT_KDF_PBKDF2;
155 pbkdf.flags = CRYPT_PBKDF_NO_BENCHMARK;
156 pbkdf.hash = "sha256";
157 pbkdf.iterations = 1000;
158 pbkdf.time_ms = 1;
159 auto r = crypt_set_pbkdf_type(m_cd, &pbkdf);
160 if (r != 0) {
161 lderr(m_cct) << "crypt_set_pbkdf_type failed: " << cpp_strerror(r)
162 << dendl;
163 return r;
164 }
165 }
166
167 auto r = crypt_format(
168 m_cd, type, alg, cipher_mode, NULL, key, key_size, params);
169 if (r != 0) {
170 lderr(m_cct) << "crypt_format failed: " << cpp_strerror(r) << dendl;
171 return r;
172 }
173
174 return 0;
175 }
176
177 int Header::add_keyslot(const char* passphrase, size_t passphrase_size) {
178 ceph_assert(m_cd != nullptr);
179
180 auto r = crypt_keyslot_add_by_volume_key(
181 m_cd, CRYPT_ANY_SLOT, NULL, 0, passphrase, passphrase_size);
182 if (r < 0) {
183 lderr(m_cct) << "crypt_keyslot_add_by_volume_key failed: "
184 << cpp_strerror(r) << dendl;
185 return r;
186 }
187
188 return 0;
189 }
190
191 int Header::load(const char* type) {
192 ceph_assert(m_cd != nullptr);
193
194 // libcryptsetup checks if device size matches the header and keyslots size
195 // in LUKS2, 2 X 4MB header + 128MB keyslots
196 if (ftruncate(m_fd, 136 * 1024 * 1024) != 0) {
197 lderr(m_cct) << "failed to truncate anonymous file: "
198 << cpp_strerror(-errno) << dendl;
199 return -errno;
200 }
201
202 auto r = crypt_load(m_cd, type, NULL);
203 if (r != 0) {
204 lderr(m_cct) << "crypt_load failed: " << cpp_strerror(r) << dendl;
205 return r;
206 }
207
208 ldout(m_cct, 20) << "sector size: " << get_sector_size() << ", data offset: "
209 << get_data_offset() << dendl;
210
211 return 0;
212 }
213
214 int Header::read_volume_key(const char* passphrase, size_t passphrase_size,
215 char* volume_key, size_t* volume_key_size) {
216 ceph_assert(m_cd != nullptr);
217
218 auto r = crypt_volume_key_get(
219 m_cd, CRYPT_ANY_SLOT, volume_key, volume_key_size, passphrase,
220 passphrase_size);
221 if (r < 0) {
222 lderr(m_cct) << "crypt_volume_key_get failed: " << cpp_strerror(r)
223 << dendl;
224 return r;
225 }
226
227 return 0;
228 }
229
230 int Header::get_sector_size() {
231 ceph_assert(m_cd != nullptr);
232 return crypt_get_sector_size(m_cd);
233 }
234
235 uint64_t Header::get_data_offset() {
236 ceph_assert(m_cd != nullptr);
237 return crypt_get_data_offset(m_cd) << 9;
238 }
239
240 const char* Header::get_cipher() {
241 ceph_assert(m_cd != nullptr);
242 return crypt_get_cipher(m_cd);
243 }
244
245 const char* Header::get_cipher_mode() {
246 ceph_assert(m_cd != nullptr);
247 return crypt_get_cipher_mode(m_cd);
248 }
249
250 } // namespace luks
251 } // namespace crypto
252 } // namespace librbd