Skip to content

Commit

Permalink
KUDU-3448 Plumbing for encrypting key material
Browse files Browse the repository at this point in the history
Key material for the internal PKI and token signing keys are stored in
the syscatalog table in clear text, which is okay when volume-level
encryption or Kudu's built-in data at rest encryption is used, but in
some cases, this is either not used, or it's not enough (FISMA).

To allow storing these key materials in encrypted form in the syscatalog
table, this patch adds the necessary plumbing in Kudu's OpenSSL wrapper.
It is now possible to pass a password callback function to the utility
functions responsible for reading from and writing to OpenSSL BIO and
strings.

Change-Id: I24c5ac8ea0f9a4cab0f35ecccb1b7b00f3acefa8
Reviewed-on: http://gerrit.cloudera.org:8080/19615
Tested-by: Kudu Jenkins
Reviewed-by: Alexey Serbin <[email protected]>
  • Loading branch information
attilabukor authored and alexeyserbin committed Mar 18, 2023
1 parent 7133b89 commit dfdaa69
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
62 changes: 62 additions & 0 deletions src/kudu/security/crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ struct RsaPrivateKeyTraits : public SslTypeTraits<EVP_PKEY> {
static constexpr auto kWritePemFunc = &PemWritePrivateKey;
static constexpr auto kWriteDerFunc = &i2d_PrivateKey_bio;
};
struct RsaEncryptedPrivateKeyTraits : public SslTypeTraits<EVP_PKEY> {
static constexpr auto kReadPemFunc = &PEM_read_bio_PrivateKey;
static constexpr auto kReadDerFunc = &d2i_PKCS8PrivateKey_bio;
static constexpr auto kWritePemFunc = &PEM_write_bio_PKCS8PrivateKey;
static constexpr auto kWriteDerFunc = &i2d_PKCS8PrivateKey_bio;
};
struct RsaPublicKeyTraits : public SslTypeTraits<EVP_PKEY> {
static constexpr auto kReadPemFunc = &PEM_read_bio_PUBKEY;
static constexpr auto kReadDerFunc = &d2i_PUBKEY_bio;
Expand All @@ -92,6 +98,50 @@ template<> struct SslTypeTraits<EVP_MD_CTX> {
#endif
};

template<>
Status FromBIO<EVP_PKEY, RsaEncryptedPrivateKeyTraits>(BIO* bio, DataFormat format,
c_unique_ptr<EVP_PKEY>* ret, const PasswordCallback& cb) {
CHECK(bio);
switch (format) {
case DataFormat::DER:
*ret = ssl_make_unique(RsaEncryptedPrivateKeyTraits::kReadDerFunc(
bio, nullptr, &TLSPasswordCB, const_cast<PasswordCallback*>(&cb)));
break;
case DataFormat::PEM:
*ret = ssl_make_unique(RsaEncryptedPrivateKeyTraits::kReadPemFunc(
bio, nullptr, &TLSPasswordCB, const_cast<PasswordCallback*>(&cb)));
break;
}
if (PREDICT_FALSE(!*ret)) {
return Status::RuntimeError("error reading private key", GetOpenSSLErrors());
}
return Status::OK();
}

template<>
Status ToBIO<EVP_PKEY, RsaEncryptedPrivateKeyTraits>(BIO* bio, DataFormat format,
EVP_PKEY* obj, const PasswordCallback& cb) {
CHECK(bio);
CHECK(obj);
switch (format) {
case DataFormat::DER:
OPENSSL_RET_NOT_OK(RsaEncryptedPrivateKeyTraits::kWriteDerFunc(
bio, obj, EVP_aes_256_cbc(), nullptr, 0, &TLSPasswordCB,
const_cast<PasswordCallback*>(&cb)),
"error exporting data in DER format");
break;
case DataFormat::PEM:
OPENSSL_RET_NOT_OK(RsaEncryptedPrivateKeyTraits::kWritePemFunc(
bio, obj, EVP_aes_256_cbc(), nullptr, 0, &TLSPasswordCB,
const_cast<PasswordCallback*>(&cb)),
"error exporting data in PEM format");
break;
}
OPENSSL_RET_NOT_OK(BIO_flush(bio), "error flushing BIO");
return Status::OK();
}


namespace {

const EVP_MD* GetMessageDigest(DigestType digest_type) {
Expand Down Expand Up @@ -184,11 +234,23 @@ Status PrivateKey::FromString(const std::string& data, DataFormat format) {
data, format, &data_);
}

Status PrivateKey::FromEncryptedString(const std::string& data, DataFormat format,
const PasswordCallback& password_cb) {
return ::kudu::security::FromString<RawDataType, RsaEncryptedPrivateKeyTraits>(
data, format, &data_, password_cb);
}

Status PrivateKey::ToString(std::string* data, DataFormat format) const {
return ::kudu::security::ToString<RawDataType, RsaPrivateKeyTraits>(
data, format, data_.get());
}

Status PrivateKey::ToEncryptedString(std::string* data, DataFormat format,
const PasswordCallback& password_cb) const {
return ::kudu::security::ToString<RawDataType, RsaEncryptedPrivateKeyTraits>(
data, format, data_.get(), password_cb);
}

Status PrivateKey::FromFile(const std::string& fpath, DataFormat format,
const PasswordCallback& password_cb) {
return ::kudu::security::FromFile<RawDataType, RsaPrivateKeyTraits>(
Expand Down
5 changes: 5 additions & 0 deletions src/kudu/security/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ class PrivateKey : public RawDataWrapper<EVP_PKEY> {
Status FromString(const std::string& data, DataFormat format) WARN_UNUSED_RESULT;
Status ToString(std::string* data, DataFormat format) const WARN_UNUSED_RESULT;

Status FromEncryptedString(const std::string& data, DataFormat format,
const PasswordCallback& password_cb) WARN_UNUSED_RESULT;
Status ToEncryptedString(std::string* data, DataFormat format,
const PasswordCallback& password_cb) const WARN_UNUSED_RESULT;

// If 'cb' is set, it will be called to obtain the password necessary to decrypt
// the private key file in 'fpath'.
Status FromFile(const std::string& fpath, DataFormat format,
Expand Down
13 changes: 8 additions & 5 deletions src/kudu/util/openssl_util_bio.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ namespace security {


template<typename TYPE, typename Traits = SslTypeTraits<TYPE>>
Status ToBIO(BIO* bio, DataFormat format, TYPE* obj) {
Status ToBIO(BIO* bio, DataFormat format, TYPE* obj,
const PasswordCallback& /*cb*/ = PasswordCallback()) {
CHECK(bio);
CHECK(obj);
switch (format) {
Expand Down Expand Up @@ -85,7 +86,8 @@ Status FromBIO(BIO* bio, DataFormat format, c_unique_ptr<TYPE>* ret,

template<typename Type, typename Traits = SslTypeTraits<Type>>
Status FromString(const std::string& data, DataFormat format,
c_unique_ptr<Type>* ret) {
c_unique_ptr<Type>* ret,
const PasswordCallback& cb = PasswordCallback()) {
const void* mdata = reinterpret_cast<const void*>(data.data());
auto bio = ssl_make_unique(BIO_new_mem_buf(
#if OPENSSL_VERSION_NUMBER < 0x10002000L
Expand All @@ -94,16 +96,17 @@ Status FromString(const std::string& data, DataFormat format,
mdata,
#endif
data.size()));
RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret)),
RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret, cb)),
"unable to load data from memory");
return Status::OK();
}

template<typename Type, typename Traits = SslTypeTraits<Type>>
Status ToString(std::string* data, DataFormat format, Type* obj) {
Status ToString(std::string* data, DataFormat format, Type* obj,
const PasswordCallback& cb = PasswordCallback()) {
CHECK(data);
auto bio = ssl_make_unique(BIO_new(BIO_s_mem()));
RETURN_NOT_OK_PREPEND((ToBIO<Type, Traits>(bio.get(), format, obj)),
RETURN_NOT_OK_PREPEND((ToBIO<Type, Traits>(bio.get(), format, obj, cb)),
"error serializing data");
BUF_MEM* membuf;
OPENSSL_CHECK_OK(BIO_get_mem_ptr(bio.get(), &membuf));
Expand Down

0 comments on commit dfdaa69

Please sign in to comment.