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