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