diff --git a/SPECS/ceph/CVE-2025-30156.patch b/SPECS/ceph/CVE-2025-30156.patch new file mode 100644 index 00000000000..be9b1a5da4e --- /dev/null +++ b/SPECS/ceph/CVE-2025-30156.patch @@ -0,0 +1,727 @@ +From 2bca6a93a6c3bffdad14be4dd43b91deac2ad598 Mon Sep 17 00:00:00 2001 +From: Yehuda Sadeh +Date: Fri, 21 Feb 2025 16:18:58 -0500 +Subject: [PATCH] auth/crypto: add support for aes256-hmac384-192 + +Using the encryption standard set in RFC 8009. This is the +encryption that is used in Kerberos 5, so naming this variation +as AES256KRB5. + +Signed-off-by: Yehuda Sadeh +Change-Id: 124b2b4f-df5c-4480-9d92-6503cf9caa9a +(cherry picked from commit ceabe08ae4057f53e155933f5efa0a69c48d970d) +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/ceph/ceph/commit/2ba086255de09c8b95177717cc7e9dd4377e2f0f.patch +--- + src/auth/Crypto.cc | 418 +++++++++++++++++++++++++++++++++++++ + src/common/ceph_context.cc | 5 + + src/common/ceph_context.h | 1 + + src/include/ceph_fs.h | 5 +- + src/test/crypto.cc | 109 +++++++++- + 5 files changed, 527 insertions(+), 11 deletions(-) + +diff --git a/src/auth/Crypto.cc b/src/auth/Crypto.cc +index ce666e8..7ed87b1 100644 +--- a/src/auth/Crypto.cc ++++ b/src/auth/Crypto.cc +@@ -17,6 +17,7 @@ + #include + + #include ++#include + + #include "Crypto.h" + +@@ -33,6 +34,10 @@ + #include "common/debug.h" + #include + ++#include ++ ++#define dout_subsys ceph_subsys_auth ++ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +@@ -46,6 +51,8 @@ using ceph::bufferlist; + using ceph::bufferptr; + using ceph::Formatter; + ++using boost::endian::native_to_big; ++ + + // use getentropy() if available. it uses the same source of randomness + // as /dev/urandom without the filesystem overhead +@@ -480,6 +487,415 @@ CryptoKeyHandler *CryptoAES::get_key_handler(const bufferptr& secret, + } + + ++// --------------------------------------------------- ++ ++/* ++ * AES256CTS-HMAC384-192 ++ */ ++class CryptoAES256KRB5 : public CryptoHandler { ++public: ++ CryptoAES256KRB5() { } ++ ~CryptoAES256KRB5() override {} ++ int get_type() const override { ++ return CEPH_CRYPTO_AES256KRB5; ++ } ++ int create(CryptoRandom *random, bufferptr& secret) override; ++ int validate_secret(const bufferptr& secret) override; ++ CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override; ++}; ++ ++static constexpr const std::size_t AES256KRB5_KEY_LEN{32}; ++static constexpr const std::size_t AES256KRB5_BLOCK_LEN{16}; ++static constexpr const std::size_t AES256KRB5_HASH_LEN{24}; ++static constexpr const std::size_t SHA384_LEN{48}; ++ ++class CryptoAES256KRB5KeyHandler : public CryptoKeyHandler { ++ EVP_CIPHER *cipher{nullptr}; ++ ++ ceph::bufferlist ki; ++ const unsigned char *ki_raw; ++ ceph::bufferlist ke; ++ const unsigned char *ke_raw; ++ ++static void dump_buf(CephContext *cct, string title, const unsigned char *buf, int len) ++{ ++ std::stringstream ss; ++ ss << std::endl << title << std::endl; ++ for (int i = 0; i < len; ++i) { ++ if (i != 0 && i % 16 == 0) { ++ ss << std::endl; ++ } ++ ss << fmt::format("{:02x} ", buf[i]); ++ } ++ ss << std::endl; ++ ldout(cct, 0) << ss.str() << dendl; ++} ++ ++ int calc_hmac_sha384(const unsigned char *data, ++ int data_len, ++ const unsigned char* hmac_key, ++ int key_size, ++ const unsigned char *iv, ++ int iv_size, ++ char *out, ++ int out_size, ++ ostringstream& err) const { ++ unsigned int len = 0; ++ char _out[SHA384_LEN]; ++ char *pout; ++ bool need_trim = (out_size < (int)sizeof(_out)); ++ if (need_trim) { ++ pout = _out; ++ } else { ++ pout = out; ++ } ++ ++ /* IV is prepended to the plaintext */ ++ ceph::bufferptr iv_buf(reinterpret_cast(iv), iv_size); ++ ceph::bufferlist source; ++ source.push_back(iv_buf); ++ source.append((const char *)data, data_len); ++ ++ HMAC(EVP_sha384(), hmac_key, key_size, ++ reinterpret_cast(source.c_str()), source.length(), ++ (unsigned char *)pout, &len); ++ ++ if (len != SHA384_LEN) { ++ err << "Unexpected calculated SHA384 length"; ++ return -EIO; ++ } ++ ++ if (need_trim) { ++ memcpy(out, pout, out_size); ++ len = out_size; ++ } ++ ++ return len; ++ } ++ ++ int calc_kx(const ceph::bufferptr& secret, ++ uint32_t usage, ++ uint8_t type, ++ int k, ++ ceph::bufferlist& out, ++ ostringstream& err) { ++ ++ struct plain_data { ++ unsigned char prefix[4] = { 0, 0, 0, 1 }; ++ uint32_t usage; ++ uint8_t type; ++ uint8_t c = 0; ++ uint32_t k; ++ ++ plain_data(uint32_t _usage, uint8_t _type, uint32_t _k) : ++ usage(native_to_big(_usage)), ++ type(_type), ++ k(native_to_big(_k * 8)) {} ++ } __attribute__((packed)) data(usage, type, k); ++ ++ ceph::bufferptr bp(reinterpret_cast(&data), sizeof(data)); ++ ++ ceph::bufferptr sha384(SHA384_LEN); ++ int r = calc_hmac_sha384((const unsigned char *)bp.c_str(), bp.length(), ++ reinterpret_cast(secret.c_str()), ++ secret.length(), ++ nullptr, 0, /* no IV */ ++ sha384.c_str(), ++ SHA384_LEN, ++ err); ++ ++ bufferlist bl; ++ bl.append(sha384); ++ bl.splice(0, k, &out); ++ ++ return r; ++ } ++ ++ int encrypt_AES256_CTS(CephContext *cct, ++ ceph::bufferlist& plaintext, ++ const unsigned char* iv, int iv_size, ++ unsigned char *ciphertext, ++ int ciphertext_len) const { ++ if (!cipher) { ++ return -EINVAL; /* initialization error */ ++ } ++ ++ if ((size_t)ciphertext_len < plaintext.length()) { ++ return -EINVAL; ++ } ++ ++ OSSL_PARAM params[2] = { OSSL_PARAM_construct_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, (char *)"CS3", 0), ++ OSSL_PARAM_construct_end()}; ++ ++ EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); ++ if (!ctx) { ++ ldout(cct, 20) << "EVP_CIPHER_CTX_new() returned null" << dendl; ++ return -EIO; ++ } ++ ++ if (!EVP_EncryptInit_ex2(ctx, cipher, ke_raw, iv, params)) { ++ ldout(cct, 20) << "EVP_EncryptInit() failed" << dendl; ++ return -EIO; ++ } ++ ++ int encrypted_len = 0; ++ int len; ++ ++ auto ret = EVP_EncryptUpdate(ctx, ciphertext + encrypted_len, &len, (const unsigned char *)plaintext.c_str(), plaintext.length()); ++ if (ret != 1) { ++ ldout(cct, 20) << "EVP_EncryptUpdate(len=" << plaintext.length() << ") returned " << ret << dendl; ++ return -EIO; ++ } ++ encrypted_len += len; ++ ++ ret = EVP_EncryptFinal_ex(ctx, ciphertext + encrypted_len, &len); ++ if (ret != 1) { ++ ldout(cct, 20) << "EVP_EncryptFinal_ex() returned " << ret << dendl; ++ return -EIO; ++ } ++ encrypted_len += len; ++ ++ EVP_CIPHER_CTX_free(ctx); ++ ++ return encrypted_len; ++ } ++ ++ int decrypt_AES256_CTS(ceph::bufferlist& ciphertext, ++ const unsigned char* key, const unsigned char* iv, ++ int iv_size, ++ ceph::bufferptr& plaintext) const { ++ if (!cipher) { ++ return -EINVAL; /* initialization error really */ ++ } ++ ++ OSSL_PARAM params[2] = { OSSL_PARAM_construct_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, (char *)"CS3", 0), ++ OSSL_PARAM_construct_end()}; ++ ++ EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); ++ if (!ctx) { ++ return -EIO; ++ } ++ ++ if (!EVP_DecryptInit_ex2(ctx, cipher, key, iv, params)) { ++ return -EIO; ++ } ++ ++ int len; ++ auto dest = reinterpret_cast(plaintext.c_str()); ++ int plaintext_len = 0; ++ ++ int max = ciphertext.length(); ++ auto iter = ciphertext.cbegin(); ++ while (!iter.end()) { ++ const char *p; ++ int chunk_len = iter.get_ptr_and_advance(max, &p); ++ ++ if (EVP_DecryptUpdate(ctx, dest + plaintext_len, &len, (const unsigned char *)p, chunk_len) != 1) { ++ return -EIO; ++ } ++ plaintext_len += len; ++ } ++ ++ if (EVP_DecryptFinal_ex(ctx, dest + plaintext_len, &len) != 1) { ++ return -EIO; ++ } ++ plaintext_len += len; ++ ++ EVP_CIPHER_CTX_free(ctx); ++ ++ return 0; ++ } ++ ++public: ++ CryptoAES256KRB5KeyHandler() : CryptoKeyHandler(CryptoKeyHandler::BLOCK_SIZE_16B()) { ++ } ++ ++ using CryptoKeyHandler::encrypt; ++ using CryptoKeyHandler::decrypt; ++ ++ int init(const ceph::bufferptr& s, ostringstream& err) { ++ cipher = EVP_CIPHER_fetch(NULL, "AES-256-CBC-CTS", NULL); ++ secret = s; ++ ++ int r = calc_kx(secret, 0x2 /* usage */, ++ 0x55 /* Ki type */, ++ AES256KRB5_HASH_LEN /* 192 bit */, ++ ki, ++ err); ++ if (r < 0) { ++ return r; ++ } ++ ki_raw = reinterpret_cast(ki.c_str()); /* needed so that we can use ki in const methods */ ++ ++ r = calc_kx(secret, 0x2 /* usage */, ++ 0xAA /* Ke type */, ++ 32 /* 256 bit */, ++ ke, ++ err); ++ if (r < 0) { ++ return r; ++ } ++ ke_raw = reinterpret_cast(ke.c_str()); /* same reason as with ki */ ++ ++ return 0; ++ } ++ ++ int encrypt(CephContext *cct, const ceph::bufferlist& in, ++ ceph::bufferlist& out, ++ std::string* /* unused */) const override { ++ // encrypted (confounder | data) | hash ++ ceph::bufferptr out_tmp{static_cast( ++ AES256KRB5_BLOCK_LEN + in.length() + AES256KRB5_HASH_LEN)}; ++ ++ /* encrypted (confounder data) */ ++ char *aes_enc = out_tmp.c_str(); ++ int aes_enc_len = AES256KRB5_BLOCK_LEN + in.length(); ++ ++ /* plaintext confounder */ ++ bufferptr confounder(AES256KRB5_BLOCK_LEN); ++ cct->random()->get_bytes(confounder.c_str(), confounder.length()); ++ ++ // combine confounder with input data ++ ceph::bufferlist incopy; ++ incopy.append(confounder); ++ incopy.append(in); ++ ++ // reinitialize IV each time. It might be unnecessary depending on ++ // actual implementation but at the interface layer we are obliged ++ // to deliver IV as non-const. ++ static_assert(strlen_ct(CEPH_AES_IV) == AES256KRB5_BLOCK_LEN); ++ unsigned char iv[AES_BLOCK_LEN]; ++ memset(iv, 0, sizeof(iv)); ++ ++ int r = encrypt_AES256_CTS(cct, incopy, iv, sizeof(iv), (unsigned char *)aes_enc, aes_enc_len); ++ if (r < 0) { ++ return r; ++ } ++ aes_enc_len = r; ++ ++ char *hmac = out_tmp.c_str() + AES256KRB5_BLOCK_LEN + in.length(); ++ ++ ostringstream err; ++ r = calc_hmac_sha384((const unsigned char *)aes_enc, aes_enc_len, ++ ki_raw, ki.length(), ++ iv, sizeof(iv), ++ hmac, AES256KRB5_HASH_LEN, err); ++ if (r < 0) { ++ return r; ++ } ++ ++ out.append(out_tmp); ++ return 0; ++ } ++ ++ int decrypt(CephContext *cct, const ceph::bufferlist& in, ++ ceph::bufferlist& out, ++ std::string* /* unused */) const override { ++ ++ if (in.length() < AES256KRB5_BLOCK_LEN + AES256KRB5_HASH_LEN) { /* minimum size: confounder + hmac */ ++ return -EINVAL; ++ } ++ ++ // needed because of .c_str() on const. It's a shallow copy. ++ bufferlist incopy(in); ++ ++ ceph::bufferlist indata; ++ ++ /* after this: ++ * indata holds: encrypted (confounder | plaintext) ++ * incopy holds: hmac hash of indata ++ */ ++ incopy.splice(0, in.length() - AES256KRB5_HASH_LEN, &indata); ++ ++ auto& inhash = incopy; ++ ++ // make a local, modifiable copy of IV. ++ static_assert(strlen_ct(CEPH_AES_IV) == AES_BLOCK_LEN); ++ unsigned char iv[AES_BLOCK_LEN]; ++ memset(iv, 0, sizeof(iv)); ++ ++ ++ /* first need to compare hmac to calculated hmac */ ++ char hmac[AES256KRB5_HASH_LEN]; ++ ostringstream err; ++ int r = calc_hmac_sha384((const unsigned char *)indata.c_str(), indata.length(), ++ ki_raw, ki.length(), ++ iv, sizeof(iv), ++ hmac, sizeof(hmac), err); ++ if (r < 0) { ++ return r; ++ } ++ ++ int len = r; ++ ++ if ((size_t)len != inhash.length()) { ++ return -EPERM; ++ } ++ ++ if (memcmp(hmac, inhash.c_str(), sizeof(hmac)) != 0) { ++ return -EPERM; ++ } ++ ++ /* will consist of confounder | plaintext */ ++ bufferptr tmp_out(indata.length()); ++ ++ r = decrypt_AES256_CTS(indata, ++ ke_raw, iv, sizeof(iv), ++ tmp_out); ++ if (r < 0) { ++ return r; ++ } ++ ++ auto confounder_len = AES256KRB5_BLOCK_LEN; ++ ++ if (tmp_out.length() < confounder_len) { ++ /* should at least consist of the confounder */ ++ return -EPERM; ++ } ++ ++ int data_len = tmp_out.length() - AES256KRB5_BLOCK_LEN; ++ ++ out.append(tmp_out.c_str() + AES256KRB5_BLOCK_LEN, data_len); ++ ++ return 0; ++ } ++ ++}; ++ ++ ++// ------------------------------------------------------------ ++ ++int CryptoAES256KRB5::create(CryptoRandom *random, bufferptr& secret) ++{ ++ bufferptr buf(AES256KRB5_KEY_LEN); ++ random->get_bytes(buf.c_str(), buf.length()); ++ secret = std::move(buf); ++ return 0; ++} ++ ++int CryptoAES256KRB5::validate_secret(const bufferptr& secret) ++{ ++ if (secret.length() < AES256KRB5_KEY_LEN) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++CryptoKeyHandler *CryptoAES256KRB5::get_key_handler(const bufferptr& secret, ++ string& error) ++{ ++ CryptoAES256KRB5KeyHandler *ckh = new CryptoAES256KRB5KeyHandler; ++ ostringstream oss; ++ if (ckh->init(secret, oss) < 0) { ++ error = oss.str(); ++ delete ckh; ++ return NULL; ++ } ++ return ckh; ++} ++ ++ + + + // -- +@@ -606,6 +1022,8 @@ CryptoHandler *CryptoHandler::create(int type) + return new CryptoNone; + case CEPH_CRYPTO_AES: + return new CryptoAES; ++ case CEPH_CRYPTO_AES256KRB5: ++ return new CryptoAES256KRB5; + default: + return NULL; + } +diff --git a/src/common/ceph_context.cc b/src/common/ceph_context.cc +index d26f245..c659087 100644 +--- a/src/common/ceph_context.cc ++++ b/src/common/ceph_context.cc +@@ -717,6 +717,7 @@ CephContext::CephContext(uint32_t module_type_, + _heartbeat_map(NULL), + _crypto_none(NULL), + _crypto_aes(NULL), ++ _crypto_aes256krb5(NULL), + _plugin_registry(NULL), + #ifdef CEPH_DEBUG_MUTEX + _lockdep_obs(NULL), +@@ -778,6 +779,7 @@ CephContext::CephContext(uint32_t module_type_, + + _crypto_none = CryptoHandler::create(CEPH_CRYPTO_NONE); + _crypto_aes = CryptoHandler::create(CEPH_CRYPTO_AES); ++ _crypto_aes256krb5 = CryptoHandler::create(CEPH_CRYPTO_AES256KRB5); + _crypto_random.reset(new CryptoRandom()); + + lookup_or_create_singleton_object("mempool_obs", false, this); +@@ -826,6 +828,7 @@ CephContext::~CephContext() + + delete _crypto_none; + delete _crypto_aes; ++ delete _crypto_aes256krb5; + if (_crypto_inited > 0) { + ceph_assert(_crypto_inited == 1); // or else someone explicitly did + // init but not shutdown +@@ -1012,6 +1015,8 @@ CryptoHandler *CephContext::get_crypto_handler(int type) + return _crypto_none; + case CEPH_CRYPTO_AES: + return _crypto_aes; ++ case CEPH_CRYPTO_AES256KRB5: ++ return _crypto_aes256krb5; + default: + return NULL; + } +diff --git a/src/common/ceph_context.h b/src/common/ceph_context.h +index f187764..b3b3ac2 100644 +--- a/src/common/ceph_context.h ++++ b/src/common/ceph_context.h +@@ -355,6 +355,7 @@ private: + // crypto + CryptoHandler *_crypto_none; + CryptoHandler *_crypto_aes; ++ CryptoHandler *_crypto_aes256krb5; + std::unique_ptr _crypto_random; + + // experimental +diff --git a/src/include/ceph_fs.h b/src/include/ceph_fs.h +index 28440c8..e0cc0a3 100644 +--- a/src/include/ceph_fs.h ++++ b/src/include/ceph_fs.h +@@ -92,8 +92,9 @@ struct ceph_dir_layout { + } __attribute__ ((packed)); + + /* crypto algorithms */ +-#define CEPH_CRYPTO_NONE 0x0 +-#define CEPH_CRYPTO_AES 0x1 ++#define CEPH_CRYPTO_NONE 0x0 ++#define CEPH_CRYPTO_AES 0x1 ++#define CEPH_CRYPTO_AES256KRB5 0x2 /* AES256-CTS-HMAC384-192 */ + + #define CEPH_AES_IV "cephsageyudagreg" + +diff --git a/src/test/crypto.cc b/src/test/crypto.cc +index 819d41c..a8d1ec4 100644 +--- a/src/test/crypto.cc ++++ b/src/test/crypto.cc +@@ -57,7 +57,7 @@ TEST(AES, Encrypt) { + bufferlist cipher; + std::string error; + CryptoKeyHandler *kh = h->get_key_handler(secret, error); +- int r = kh->encrypt(plaintext, cipher, &error); ++ int r = kh->encrypt(g_ceph_context, plaintext, cipher, &error); + ASSERT_EQ(r, 0); + ASSERT_EQ(error, ""); + +@@ -99,7 +99,7 @@ TEST(AES, EncryptNoBl) { + + // we need to deduce size first + const CryptoKey::out_slice_t probe_slice { 0, nullptr }; +- const auto needed = kh->encrypt(plain_slice, probe_slice); ++ const auto needed = kh->encrypt(g_ceph_context, plain_slice, probe_slice); + ASSERT_GE(needed, plain_slice.length); + + boost::container::small_vector< +@@ -107,7 +107,7 @@ TEST(AES, EncryptNoBl) { + //unsigned char, sizeof(plaintext) + kh->get_block_size()> buf; + unsigned char, sizeof(plaintext) + 16> buf(needed); + const CryptoKey::out_slice_t cipher_slice { needed, buf.data() }; +- const auto cipher_size = kh->encrypt(plain_slice, cipher_slice); ++ const auto cipher_size = kh->encrypt(g_ceph_context, plain_slice, cipher_slice); + ASSERT_EQ(cipher_size, needed); + + const unsigned char want_cipher[] = { +@@ -149,7 +149,7 @@ TEST(AES, Decrypt) { + std::string error; + bufferlist plaintext; + CryptoKeyHandler *kh = h->get_key_handler(secret, error); +- int r = kh->decrypt(cipher, plaintext, &error); ++ int r = kh->decrypt(g_ceph_context, cipher, plaintext, &error); + ASSERT_EQ(r, 0); + ASSERT_EQ(error, ""); + +@@ -191,7 +191,7 @@ TEST(AES, DecryptNoBl) { + + CryptoKey::in_slice_t cipher_slice { sizeof(ciphertext), ciphertext }; + CryptoKey::out_slice_t plain_slice { sizeof(plaintext), plaintext }; +- const auto plain_size = kh->decrypt(cipher_slice, plain_slice); ++ const auto plain_size = kh->decrypt(g_ceph_context, cipher_slice, plain_slice); + + ASSERT_EQ(plain_size, sizeof(want_plaintext)); + +@@ -217,7 +217,7 @@ static void aes_loop_cephx() { + + // we need to deduce size first + const CryptoKey::out_slice_t probe_slice { 0, nullptr }; +- const auto needed = kh->encrypt(plain_slice, probe_slice); ++ const auto needed = kh->encrypt(g_ceph_context, plain_slice, probe_slice); + ASSERT_GE(needed, plain_slice.length); + + boost::container::small_vector< +@@ -228,7 +228,7 @@ static void aes_loop_cephx() { + std::size_t cipher_size; + for (std::size_t i = 0; i < 1000000; i++) { + const CryptoKey::out_slice_t cipher_slice { needed, buf.data() }; +- cipher_size = kh->encrypt(plain_slice, cipher_slice); ++ cipher_size = kh->encrypt(g_ceph_context, plain_slice, cipher_slice); + ASSERT_EQ(cipher_size, needed); + } + } +@@ -262,7 +262,7 @@ static void aes_loop(const std::size_t text_size) { + + std::string error; + CryptoKeyHandler *kh = h->get_key_handler(secret, error); +- int r = kh->encrypt(plaintext, cipher, &error); ++ int r = kh->encrypt(g_ceph_context, plaintext, cipher, &error); + ASSERT_EQ(r, 0); + ASSERT_EQ(error, ""); + +@@ -274,7 +274,7 @@ static void aes_loop(const std::size_t text_size) { + CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES); + std::string error; + CryptoKeyHandler *ckh = h->get_key_handler(secret, error); +- int r = ckh->decrypt(cipher, plaintext, &error); ++ int r = ckh->decrypt(g_ceph_context, cipher, plaintext, &error); + ASSERT_EQ(r, 0); + ASSERT_EQ(error, ""); + +@@ -340,3 +340,94 @@ TEST(AES, LoopKey_29) { + TEST(AES, LoopKey_32) { + aes_loopkey(32); + } ++ ++static void dump_buf(string title, const unsigned char *buf, int len) ++{ ++ std::cout << title << std::endl; ++ for (int i = 0; i < len; ++i) { ++ if (i != 0 && i % 16 == 0) { ++ cout << std::endl; ++ } ++ std::cout << std::format("{:02x} ", buf[i]); ++ } ++ std::cout << std::endl << std::endl; ++} ++ ++ ++TEST(AES256KRB5, Encrypt) { ++ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES256KRB5); ++ unsigned char secret_s[] = { ++ 0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98, ++ 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52 }; ++ ++ bufferptr secret((char *)secret_s, sizeof(secret_s)); ++ ++ unsigned char plaintext_s[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; ++ ++ bufferlist plaintext; ++ plaintext.append((char *)plaintext_s, sizeof(plaintext_s)); ++ ++ bufferlist cipher; ++ std::string error; ++ CryptoKeyHandler *kh = h->get_key_handler(secret, error); ++ int r = kh->encrypt(g_ceph_context, plaintext, cipher, &error); ++ ASSERT_EQ(r, 0); ++ ASSERT_EQ(error, ""); ++ ++ unsigned char want_cipher[] = { ++ 0x4E, 0xD7, 0xB3, 0x7C, 0x2B, 0xCA, 0xC8, 0xF7, 0x4F, 0x23, 0xC1, 0xCF, 0x07, 0xE6, 0x2B, 0xC7, ++ 0xB7, 0x5F, 0xB3, 0xF6, 0x37, 0xB9, 0xF5, 0x59, 0xC7, 0xF6, 0x64, 0xF6, 0x9E, 0xAB, 0x7B, 0x60, ++ 0x92, 0x23, 0x75, 0x26, 0xEA, 0x0D, 0x1F, 0x61, 0xCB, 0x20, 0xD6, 0x9D, 0x10, 0xF2 ++ }; ++ char cipher_s[sizeof(want_cipher)]; ++ ++ ASSERT_EQ(sizeof(cipher_s), cipher.length()); ++ cipher.cbegin().copy(sizeof(cipher_s), &cipher_s[0]); ++ ++ dump_buf("ENCRYPTED:", (unsigned char *)cipher.c_str(), cipher.length()); ++ dump_buf("EXPECTED:", want_cipher, sizeof(want_cipher)); ++ ++ int err; ++ err = memcmp(cipher_s, want_cipher, sizeof(want_cipher)); ++ ASSERT_EQ(0, err); ++ ++ delete kh; ++} ++ ++TEST(AES256KRB5, Decrypt) { ++ CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES256KRB5); ++ unsigned char secret_s[] = { ++ 0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98, ++ 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52 }; ++ ++ bufferptr secret((const char *)secret_s, sizeof(secret_s)); ++ ++ unsigned char cipher_s[] = { ++ 0x4E, 0xD7, 0xB3, 0x7C, 0x2B, 0xCA, 0xC8, 0xF7, 0x4F, 0x23, 0xC1, 0xCF, 0x07, 0xE6, 0x2B, 0xC7, ++ 0xB7, 0x5F, 0xB3, 0xF6, 0x37, 0xB9, 0xF5, 0x59, 0xC7, 0xF6, 0x64, 0xF6, 0x9E, 0xAB, 0x7B, 0x60, ++ 0x92, 0x23, 0x75, 0x26, 0xEA, 0x0D, 0x1F, 0x61, 0xCB, 0x20, 0xD6, 0x9D, 0x10, 0xF2 ++ }; ++ ++ bufferlist cipher; ++ cipher.append((char *)cipher_s, sizeof(cipher_s)); ++ ++ unsigned char want_plaintext[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; ++ char plaintext_s[sizeof(want_plaintext)]; ++ ++ std::string error; ++ bufferlist plaintext; ++ CryptoKeyHandler *kh = h->get_key_handler(secret, error); ++ int r = kh->decrypt(g_ceph_context, cipher, plaintext, &error); ++ ASSERT_EQ(r, 0); ++ ASSERT_EQ(error, ""); ++ ++ ASSERT_EQ(sizeof(plaintext_s), plaintext.length()); ++ plaintext.cbegin().copy(sizeof(plaintext_s), &plaintext_s[0]); ++ ++ int err; ++ err = memcmp(plaintext_s, want_plaintext, sizeof(want_plaintext)); ++ ASSERT_EQ(0, err); ++ ++ delete kh; ++} ++ +-- +2.45.4 + diff --git a/SPECS/ceph/ceph.spec b/SPECS/ceph/ceph.spec index d32cfdfe070..5fc46db9fcb 100644 --- a/SPECS/ceph/ceph.spec +++ b/SPECS/ceph/ceph.spec @@ -5,7 +5,7 @@ Summary: User space components of the Ceph file system Name: ceph Version: 18.2.2 -Release: 12%{?dist} +Release: 13%{?dist} License: LGPLv2 and LGPLv3 and CC-BY-SA and GPLv2 and Boost and BSD and MIT and Public Domain and GPLv3 and ASL-2.0 URL: https://ceph.io/ Vendor: Microsoft Corporation @@ -32,6 +32,7 @@ Patch17: CVE-2025-52555.patch Patch18: CVE-2024-48916.patch Patch19: CVE-2025-9648.patch Patch20: CVE-2024-47866.patch +Patch21: CVE-2025-30156.patch # # Copyright (C) 2004-2019 The Ceph Project Developers. See COPYING file # at the top-level directory of this distribution and at @@ -2022,6 +2023,9 @@ exit 0 %config %{_sysconfdir}/prometheus/ceph/ceph_default_alerts.yml %changelog +* Fri Aug 28 2026 Azure Linux Security Servicing Account - 18.2.2-13 +- Patch for CVE-2025-30156 + * Thu Nov 13 2025 Azure Linux Security Servicing Account - 18.2.2-12 - Patch for CVE-2024-47866