]> git.proxmox.com Git - ceph.git/blame - ceph/src/auth/Crypto.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / auth / Crypto.cc
CommitLineData
7c673cae
FG
1// vim: ts=8 sw=2 smarttab
2/*
3 * Ceph - scalable distributed file system
4 *
5 * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
6 *
7 * This is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License version 2.1, as published by the Free Software
10 * Foundation. See file COPYING.
11 *
12 */
13
11fdf7f2 14#include <array>
7c673cae 15#include <sstream>
91327a77 16#include <limits>
11fdf7f2 17
91327a77
AA
18#include <fcntl.h>
19
7c673cae 20#include "Crypto.h"
11fdf7f2
TL
21#ifdef USE_OPENSSL
22# include <openssl/aes.h>
7c673cae
FG
23#endif
24
11fdf7f2 25#include "include/ceph_assert.h"
7c673cae
FG
26#include "common/Clock.h"
27#include "common/armor.h"
11fdf7f2 28#include "common/ceph_context.h"
7c673cae
FG
29#include "common/ceph_crypto.h"
30#include "common/hex.h"
31#include "common/safe_io.h"
32#include "include/ceph_fs.h"
33#include "include/compat.h"
34#include "common/Formatter.h"
35#include "common/debug.h"
36#include <errno.h>
37
11fdf7f2
TL
38// use getentropy() if available. it uses the same source of randomness
39// as /dev/urandom without the filesystem overhead
40#ifdef HAVE_GETENTROPY
41
42#include <unistd.h>
43
44CryptoRandom::CryptoRandom() : fd(0) {}
45CryptoRandom::~CryptoRandom() = default;
46
47void CryptoRandom::get_bytes(char *buf, int len)
48{
49 auto ret = TEMP_FAILURE_RETRY(::getentropy(buf, len));
50 if (ret < 0) {
51 throw std::system_error(errno, std::system_category());
52 }
53}
54
55#else // !HAVE_GETENTROPY
56
57// open /dev/urandom once on construction and reuse the fd for all reads
58CryptoRandom::CryptoRandom()
59 : fd(TEMP_FAILURE_RETRY(::open("/dev/urandom", O_CLOEXEC|O_RDONLY)))
60{
61 if (fd < 0) {
62 throw std::system_error(errno, std::system_category());
63 }
64}
65
66CryptoRandom::~CryptoRandom()
7c673cae 67{
7c673cae 68 VOID_TEMP_FAILURE_RETRY(::close(fd));
7c673cae
FG
69}
70
11fdf7f2 71void CryptoRandom::get_bytes(char *buf, int len)
7c673cae 72{
11fdf7f2
TL
73 auto ret = safe_read_exact(fd, buf, len);
74 if (ret < 0) {
75 throw std::system_error(-ret, std::system_category());
76 }
77}
78
79#endif
80
81
82// ---------------------------------------------------
83// fallback implementation of the bufferlist-free
84// interface.
85
86std::size_t CryptoKeyHandler::encrypt(
87 const CryptoKeyHandler::in_slice_t& in,
88 const CryptoKeyHandler::out_slice_t& out) const
89{
90 ceph::bufferptr inptr(reinterpret_cast<const char*>(in.buf), in.length);
91 ceph::bufferlist plaintext;
92 plaintext.append(std::move(inptr));
93
94 ceph::bufferlist ciphertext;
95 std::string error;
96 const int ret = encrypt(plaintext, ciphertext, &error);
97 if (ret != 0 || !error.empty()) {
98 throw std::runtime_error(std::move(error));
99 }
100
101 // we need to specify the template parameter explicitly as ::length()
102 // returns unsigned int, not size_t.
103 const auto todo_len = \
104 std::min<std::size_t>(ciphertext.length(), out.max_length);
105 memcpy(out.buf, ciphertext.c_str(), todo_len);
106
107 return todo_len;
7c673cae
FG
108}
109
11fdf7f2
TL
110std::size_t CryptoKeyHandler::decrypt(
111 const CryptoKeyHandler::in_slice_t& in,
112 const CryptoKeyHandler::out_slice_t& out) const
7c673cae 113{
11fdf7f2
TL
114 ceph::bufferptr inptr(reinterpret_cast<const char*>(in.buf), in.length);
115 ceph::bufferlist ciphertext;
116 ciphertext.append(std::move(inptr));
117
118 ceph::bufferlist plaintext;
119 std::string error;
120 const int ret = decrypt(ciphertext, plaintext, &error);
121 if (ret != 0 || !error.empty()) {
122 throw std::runtime_error(std::move(error));
123 }
124
125 // we need to specify the template parameter explicitly as ::length()
126 // returns unsigned int, not size_t.
127 const auto todo_len = \
128 std::min<std::size_t>(plaintext.length(), out.max_length);
129 memcpy(out.buf, plaintext.c_str(), todo_len);
130
131 return todo_len;
7c673cae
FG
132}
133
11fdf7f2
TL
134sha256_digest_t CryptoKeyHandler::hmac_sha256(
135 const ceph::bufferlist& in) const
136{
137 ceph::crypto::HMACSHA256 hmac((const unsigned char*)secret.c_str(), secret.length());
138
139 for (const auto& bptr : in.buffers()) {
140 hmac.Update((const unsigned char *)bptr.c_str(), bptr.length());
141 }
142 sha256_digest_t ret;
143 hmac.Final(ret.v);
144
145 return ret;
146}
7c673cae
FG
147
148// ---------------------------------------------------
149
150class CryptoNoneKeyHandler : public CryptoKeyHandler {
151public:
11fdf7f2
TL
152 CryptoNoneKeyHandler()
153 : CryptoKeyHandler(CryptoKeyHandler::BLOCK_SIZE_0B()) {
154 }
155
156 using CryptoKeyHandler::encrypt;
157 using CryptoKeyHandler::decrypt;
158
7c673cae
FG
159 int encrypt(const bufferlist& in,
160 bufferlist& out, std::string *error) const override {
161 out = in;
162 return 0;
163 }
164 int decrypt(const bufferlist& in,
165 bufferlist& out, std::string *error) const override {
166 out = in;
167 return 0;
168 }
169};
170
171class CryptoNone : public CryptoHandler {
172public:
173 CryptoNone() { }
174 ~CryptoNone() override {}
175 int get_type() const override {
176 return CEPH_CRYPTO_NONE;
177 }
11fdf7f2 178 int create(CryptoRandom *random, bufferptr& secret) override {
7c673cae
FG
179 return 0;
180 }
181 int validate_secret(const bufferptr& secret) override {
182 return 0;
183 }
184 CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override {
185 return new CryptoNoneKeyHandler;
186 }
187};
188
189
190// ---------------------------------------------------
191
192
193class CryptoAES : public CryptoHandler {
194public:
195 CryptoAES() { }
196 ~CryptoAES() override {}
197 int get_type() const override {
198 return CEPH_CRYPTO_AES;
199 }
11fdf7f2 200 int create(CryptoRandom *random, bufferptr& secret) override;
7c673cae
FG
201 int validate_secret(const bufferptr& secret) override;
202 CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override;
203};
204
11fdf7f2
TL
205#ifdef USE_OPENSSL
206// when we say AES, we mean AES-128
207static constexpr const std::size_t AES_KEY_LEN{16};
208static constexpr const std::size_t AES_BLOCK_LEN{16};
7c673cae
FG
209
210class CryptoAESKeyHandler : public CryptoKeyHandler {
11fdf7f2
TL
211 AES_KEY enc_key;
212 AES_KEY dec_key;
7c673cae 213
11fdf7f2 214public:
7c673cae 215 CryptoAESKeyHandler()
11fdf7f2 216 : CryptoKeyHandler(CryptoKeyHandler::BLOCK_SIZE_16B()) {
7c673cae
FG
217 }
218
219 int init(const bufferptr& s, ostringstream& err) {
220 secret = s;
221
11fdf7f2
TL
222 const int enc_key_ret = \
223 AES_set_encrypt_key((const unsigned char*)secret.c_str(),
224 AES_KEY_LEN * CHAR_BIT, &enc_key);
225 if (enc_key_ret != 0) {
226 err << "cannot set OpenSSL encrypt key for AES: " << enc_key_ret;
7c673cae
FG
227 return -1;
228 }
7c673cae 229
11fdf7f2
TL
230 const int dec_key_ret = \
231 AES_set_decrypt_key((const unsigned char*)secret.c_str(),
232 AES_KEY_LEN * CHAR_BIT, &dec_key);
233 if (dec_key_ret != 0) {
234 err << "cannot set OpenSSL decrypt key for AES: " << dec_key_ret;
7c673cae
FG
235 return -1;
236 }
237
7c673cae
FG
238 return 0;
239 }
7c673cae 240
11fdf7f2
TL
241 int encrypt(const ceph::bufferlist& in,
242 ceph::bufferlist& out,
243 std::string* /* unused */) const override {
244 // we need to take into account the PKCS#7 padding. There *always* will
245 // be at least one byte of padding. This stays even to input aligned to
246 // AES_BLOCK_LEN. Otherwise we would face ambiguities during decryption.
247 // To exemplify:
248 // 16 + p2align(10, 16) -> 16
249 // 16 + p2align(16, 16) -> 32 including 16 bytes for padding.
250 ceph::bufferptr out_tmp{static_cast<unsigned>(
251 AES_BLOCK_LEN + p2align<std::size_t>(in.length(), AES_BLOCK_LEN))};
252
253 // let's pad the data
254 std::uint8_t pad_len = out_tmp.length() - in.length();
255 ceph::bufferptr pad_buf{pad_len};
256 memset(pad_buf.c_str(), pad_len, pad_len);
257
258 // form contiguous buffer for block cipher. The ctor copies shallowly.
259 ceph::bufferlist incopy(in);
260 incopy.append(std::move(pad_buf));
261 const auto in_buf = reinterpret_cast<unsigned char*>(incopy.c_str());
262
263 // reinitialize IV each time. It might be unnecessary depending on
264 // actual implementation but at the interface layer we are obliged
265 // to deliver IV as non-const.
266 static_assert(strlen_ct(CEPH_AES_IV) == AES_BLOCK_LEN);
267 unsigned char iv[AES_BLOCK_LEN];
268 memcpy(iv, CEPH_AES_IV, AES_BLOCK_LEN);
269
270 // we aren't using EVP because of performance concerns. Profiling
271 // shows the cost is quite high. Endianness might be an issue.
272 // However, as they would affect Cephx, any fallout should pop up
273 // rather early, hopefully.
274 AES_cbc_encrypt(in_buf, reinterpret_cast<unsigned char*>(out_tmp.c_str()),
275 out_tmp.length(), &enc_key, iv, AES_ENCRYPT);
276
277 out.append(out_tmp);
278 return 0;
7c673cae
FG
279 }
280
11fdf7f2
TL
281 int decrypt(const ceph::bufferlist& in,
282 ceph::bufferlist& out,
283 std::string* /* unused */) const override {
284 // PKCS#7 padding enlarges even empty plain-text to take 16 bytes.
285 if (in.length() < AES_BLOCK_LEN || in.length() % AES_BLOCK_LEN) {
286 return -1;
7c673cae 287 }
7c673cae 288
11fdf7f2
TL
289 // needed because of .c_str() on const. It's a shallow copy.
290 ceph::bufferlist incopy(in);
291 const auto in_buf = reinterpret_cast<unsigned char*>(incopy.c_str());
7c673cae 292
11fdf7f2
TL
293 // make a local, modifiable copy of IV.
294 static_assert(strlen_ct(CEPH_AES_IV) == AES_BLOCK_LEN);
295 unsigned char iv[AES_BLOCK_LEN];
296 memcpy(iv, CEPH_AES_IV, AES_BLOCK_LEN);
7c673cae 297
11fdf7f2
TL
298 ceph::bufferptr out_tmp{in.length()};
299 AES_cbc_encrypt(in_buf, reinterpret_cast<unsigned char*>(out_tmp.c_str()),
300 in.length(), &dec_key, iv, AES_DECRYPT);
7c673cae 301
11fdf7f2
TL
302 // BE CAREFUL: we cannot expose any single bit of information about
303 // the cause of failure. Otherwise we'll face padding oracle attack.
304 // See: https://en.wikipedia.org/wiki/Padding_oracle_attack.
305 const auto pad_len = \
306 std::min<std::uint8_t>(out_tmp[in.length() - 1], AES_BLOCK_LEN);
307 out_tmp.set_length(in.length() - pad_len);
308 out.append(std::move(out_tmp));
7c673cae 309
11fdf7f2
TL
310 return 0;
311 }
7c673cae 312
11fdf7f2
TL
313 std::size_t encrypt(const in_slice_t& in,
314 const out_slice_t& out) const override {
315 if (out.buf == nullptr) {
316 // 16 + p2align(10, 16) -> 16
317 // 16 + p2align(16, 16) -> 32
318 return AES_BLOCK_LEN + p2align<std::size_t>(in.length, AES_BLOCK_LEN);
7c673cae
FG
319 }
320
11fdf7f2
TL
321 // how many bytes of in.buf hang outside the alignment boundary and how
322 // much padding we need.
323 // length = 23 -> tail_len = 7, pad_len = 9
324 // length = 32 -> tail_len = 0, pad_len = 16
325 const std::uint8_t tail_len = in.length % AES_BLOCK_LEN;
326 const std::uint8_t pad_len = AES_BLOCK_LEN - tail_len;
327 static_assert(std::numeric_limits<std::uint8_t>::max() > AES_BLOCK_LEN);
328
329 std::array<unsigned char, AES_BLOCK_LEN> last_block;
330 memcpy(last_block.data(), in.buf + in.length - tail_len, tail_len);
331 memset(last_block.data() + tail_len, pad_len, pad_len);
332
333 // need a local copy because AES_cbc_encrypt takes `iv` as non-const.
334 // Useful because it allows us to encrypt in two steps: main + tail.
335 static_assert(strlen_ct(CEPH_AES_IV) == AES_BLOCK_LEN);
336 std::array<unsigned char, AES_BLOCK_LEN> iv;
337 memcpy(iv.data(), CEPH_AES_IV, AES_BLOCK_LEN);
338
339 const std::size_t main_encrypt_size = \
340 std::min(in.length - tail_len, out.max_length);
341 AES_cbc_encrypt(in.buf, out.buf, main_encrypt_size, &enc_key, iv.data(),
342 AES_ENCRYPT);
343
344 const std::size_t tail_encrypt_size = \
345 std::min(AES_BLOCK_LEN, out.max_length - main_encrypt_size);
346 AES_cbc_encrypt(last_block.data(), out.buf + main_encrypt_size,
347 tail_encrypt_size, &enc_key, iv.data(), AES_ENCRYPT);
348
349 return main_encrypt_size + tail_encrypt_size;
350 }
7c673cae 351
11fdf7f2
TL
352 std::size_t decrypt(const in_slice_t& in,
353 const out_slice_t& out) const override {
354 if (in.length % AES_BLOCK_LEN != 0 || in.length < AES_BLOCK_LEN) {
355 throw std::runtime_error("input not aligned to AES_BLOCK_LEN");
356 } else if (out.buf == nullptr) {
357 // essentially it would be possible to decrypt into a buffer that
358 // doesn't include space for any PKCS#7 padding. We don't do that
359 // for the sake of performance and simplicity.
360 return in.length;
361 } else if (out.max_length < in.length) {
362 throw std::runtime_error("output buffer too small");
7c673cae
FG
363 }
364
11fdf7f2
TL
365 static_assert(strlen_ct(CEPH_AES_IV) == AES_BLOCK_LEN);
366 std::array<unsigned char, AES_BLOCK_LEN> iv;
367 memcpy(iv.data(), CEPH_AES_IV, AES_BLOCK_LEN);
7c673cae 368
11fdf7f2
TL
369 AES_cbc_encrypt(in.buf, out.buf, in.length, &dec_key, iv.data(),
370 AES_DECRYPT);
371
372 // NOTE: we aren't handling partial decrypt. PKCS#7 padding must be
373 // at the end. If it's malformed, don't say a word to avoid risk of
374 // having an oracle. All we need to ensure is valid buffer boundary.
375 const auto pad_len = \
376 std::min<std::uint8_t>(out.buf[in.length - 1], AES_BLOCK_LEN);
377 return in.length - pad_len;
7c673cae
FG
378 }
379};
380
381#else
382# error "No supported crypto implementation found."
383#endif
384
385
386
387// ------------------------------------------------------------
388
11fdf7f2 389int CryptoAES::create(CryptoRandom *random, bufferptr& secret)
7c673cae 390{
11fdf7f2
TL
391 bufferptr buf(AES_KEY_LEN);
392 random->get_bytes(buf.c_str(), buf.length());
393 secret = std::move(buf);
7c673cae
FG
394 return 0;
395}
396
397int CryptoAES::validate_secret(const bufferptr& secret)
398{
11fdf7f2 399 if (secret.length() < AES_KEY_LEN) {
7c673cae
FG
400 return -EINVAL;
401 }
402
403 return 0;
404}
405
406CryptoKeyHandler *CryptoAES::get_key_handler(const bufferptr& secret,
407 string& error)
408{
409 CryptoAESKeyHandler *ckh = new CryptoAESKeyHandler;
410 ostringstream oss;
411 if (ckh->init(secret, oss) < 0) {
412 error = oss.str();
413 delete ckh;
414 return NULL;
415 }
416 return ckh;
417}
418
419
420
421
422// --
423
424
425// ---------------------------------------------------
426
427
428void CryptoKey::encode(bufferlist& bl) const
429{
11fdf7f2
TL
430 using ceph::encode;
431 encode(type, bl);
432 encode(created, bl);
7c673cae 433 __u16 len = secret.length();
11fdf7f2 434 encode(len, bl);
7c673cae
FG
435 bl.append(secret);
436}
437
11fdf7f2 438void CryptoKey::decode(bufferlist::const_iterator& bl)
7c673cae 439{
11fdf7f2
TL
440 using ceph::decode;
441 decode(type, bl);
442 decode(created, bl);
7c673cae 443 __u16 len;
11fdf7f2 444 decode(len, bl);
7c673cae
FG
445 bufferptr tmp;
446 bl.copy_deep(len, tmp);
447 if (_set_secret(type, tmp) < 0)
448 throw buffer::malformed_input("malformed secret");
449}
450
451int CryptoKey::set_secret(int type, const bufferptr& s, utime_t c)
452{
453 int r = _set_secret(type, s);
454 if (r < 0)
455 return r;
456 this->created = c;
457 return 0;
458}
459
460int CryptoKey::_set_secret(int t, const bufferptr& s)
461{
462 if (s.length() == 0) {
463 secret = s;
464 ckh.reset();
465 return 0;
466 }
467
468 CryptoHandler *ch = CryptoHandler::create(t);
469 if (ch) {
470 int ret = ch->validate_secret(s);
471 if (ret < 0) {
472 delete ch;
473 return ret;
474 }
475 string error;
476 ckh.reset(ch->get_key_handler(s, error));
477 delete ch;
478 if (error.length()) {
479 return -EIO;
480 }
481 } else {
482 return -EOPNOTSUPP;
483 }
484 type = t;
485 secret = s;
486 return 0;
487}
488
489int CryptoKey::create(CephContext *cct, int t)
490{
491 CryptoHandler *ch = CryptoHandler::create(t);
492 if (!ch) {
493 if (cct)
494 lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << t << ") returned NULL" << dendl;
495 return -EOPNOTSUPP;
496 }
497 bufferptr s;
11fdf7f2 498 int r = ch->create(cct->random(), s);
7c673cae
FG
499 delete ch;
500 if (r < 0)
501 return r;
502
503 r = _set_secret(t, s);
504 if (r < 0)
505 return r;
506 created = ceph_clock_now();
507 return r;
508}
509
510void CryptoKey::print(std::ostream &out) const
511{
512 out << encode_base64();
513}
514
515void CryptoKey::to_str(std::string& s) const
516{
517 int len = secret.length() * 4;
518 char buf[len];
519 hex2str(secret.c_str(), secret.length(), buf, len);
520 s = buf;
521}
522
523void CryptoKey::encode_formatted(string label, Formatter *f, bufferlist &bl)
524{
525 f->open_object_section(label.c_str());
526 f->dump_string("key", encode_base64());
527 f->close_section();
528 f->flush(bl);
529}
530
531void CryptoKey::encode_plaintext(bufferlist &bl)
532{
533 bl.append(encode_base64());
534}
535
536
537// ------------------
538
539CryptoHandler *CryptoHandler::create(int type)
540{
541 switch (type) {
542 case CEPH_CRYPTO_NONE:
543 return new CryptoNone;
544 case CEPH_CRYPTO_AES:
545 return new CryptoAES;
546 default:
547 return NULL;
548 }
549}