* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* Description: SSL Auth
*/
#include "datasystem/common/util/ssl_authorization.h"
#include <climits>
#include <memory>
#include <string>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/safestack.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/ossl_typ.h>
#include <openssl/pkcs12.h>
#include <openssl/rsa.h>
#include "datasystem/common/util/format.h"
#include "datasystem/common/util/raii.h"
#include "datasystem/common/util/status_helper.h"
#include "datasystem/common/util/strings_util.h"
namespace datasystem {
bool LoadKeyAndCertFromMemory(SSL_CTX *context, const char *certBuffer, size_t certSize, const char *keyBuffer,
size_t keySize)
{
BIO *certbio = nullptr;
BIO *kbio = nullptr;
X509 *cert = nullptr;
RSA *rsa = nullptr;
Raii freePtr([&certbio, &kbio, &cert, &rsa]() {
BIO_free(certbio);
BIO_free(kbio);
X509_free(cert);
RSA_free(rsa);
});
if (certSize > INT_MAX) {
return false;
}
certbio = BIO_new_mem_buf(certBuffer, static_cast<int>(certSize));
if (certbio == nullptr) {
return false;
}
cert = PEM_read_bio_X509(certbio, nullptr, nullptr, nullptr);
if (cert == nullptr || !SSL_CTX_use_certificate(context, cert)) {
return false;
}
if (keySize > INT_MAX) {
return false;
}
kbio = BIO_new_mem_buf(keyBuffer, static_cast<int>(keySize));
if (kbio == nullptr) {
return false;
}
rsa = PEM_read_bio_RSAPrivateKey(kbio, nullptr, nullptr, nullptr);
if (rsa == nullptr || !SSL_CTX_use_RSAPrivateKey(context, rsa)) {
return false;
}
return true;
}
bool LoadCaFromMemory(SSL_CTX *context, const char *caBuffer, size_t caSize)
{
if (caSize > INT_MAX) {
return false;
}
BIO *cabio = nullptr;
Raii freePtr([&cabio]() { BIO_free(cabio); });
cabio = BIO_new_mem_buf(caBuffer, static_cast<int>(caSize));
if (cabio == nullptr) {
return false;
}
X509_STORE *store = SSL_CTX_get_cert_store(context);
if (store == nullptr) {
return false;
}
for (int i = 0;; i++) {
X509 *cacert = nullptr;
Raii freeCacert([&cacert]() { X509_free(cacert); });
cacert = PEM_read_bio_X509(cabio, nullptr, nullptr, nullptr);
if (cacert == nullptr) {
if (i == 0) {
return false;
} else {
break;
}
}
if (!X509_STORE_add_cert(store, cacert)) {
return false;
}
}
return true;
}
void LoadSslStrings()
{
SSL_load_error_strings();
ERR_load_crypto_strings();
}
std::string GetSslErrorMessage()
{
auto err = ERR_get_error();
if (err != 0) {
std::string msg = "Error: " + std::string(ERR_error_string(err, nullptr));
auto reason = ERR_reason_error_string(err);
if (reason != nullptr) {
msg += ", reason: " + std::string(reason);
}
return msg;
}
return "ssl is ok";
}
Status ParsePKCS12(const std::string &p12Path, const SensitiveValue &passphrase, SensitiveValue &ca,
SensitiveValue &cert, SensitiveValue &key)
{
EVP_PKEY *pkey = nullptr;
X509 *crt = nullptr;
STACK_OF(X509) *caCert = nullptr;
FILE *file = fopen(p12Path.c_str(), "rb");
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(
file != nullptr, K_RUNTIME_ERROR,
FormatString("failed to open file: %s, errno: %d, msg: %s", p12Path, errno, StrErr(errno)));
Raii releaseFile([file] {
int result = fclose(file);
LOG_IF(ERROR, result != 0) << "failed to fclose PKCS#12 file";
});
PKCS12 *p12 = d2i_PKCS12_fp(file, nullptr);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(p12 != nullptr, K_RUNTIME_ERROR, "failed to read PKCS#12 file");
Raii releaseP12([p12] { PKCS12_free(p12); });
if (!PKCS12_parse(p12, passphrase.GetData(), &pkey, &crt, &caCert)) {
RETURN_STATUS_LOG_ERROR(K_RUNTIME_ERROR,
FormatString("parse p12 failed: %s", ERR_reason_error_string(ERR_get_error())));
}
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR((pkey != nullptr && crt != nullptr), K_RUNTIME_ERROR, "failed to parse p12");
Raii releaseCrtAndKey([crt, pkey, caCert] {
X509_free(crt);
EVP_PKEY_free(pkey);
sk_X509_pop_free(caCert, X509_free);
});
BUF_MEM *bptr = nullptr;
BIO *bio = BIO_new(BIO_s_mem());
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(bio != nullptr, K_RUNTIME_ERROR, "failed to alloc bio mem");
Raii releaseBio([bio] { BIO_free(bio); });
int result = PEM_write_bio_PrivateKey(bio, pkey, nullptr, nullptr, 0, nullptr, nullptr);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(result != 0, K_RUNTIME_ERROR, "failed to write pkey to bio");
result = BIO_get_mem_ptr(bio, &bptr);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(result != 0, K_RUNTIME_ERROR, "failed to BIO_get_mem_ptr");
key = SensitiveValue(bptr->data, bptr->length);
BIO_reset(bio);
result = PEM_write_bio_X509(bio, crt);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(result != 0, K_RUNTIME_ERROR, "failed to write cert to bio");
result = BIO_get_mem_ptr(bio, &bptr);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(result != 0, K_RUNTIME_ERROR, "failed to BIO_get_mem_ptr");
cert = SensitiveValue(bptr->data, bptr->length);
std::string tempCa;
for (int32_t i = 0; caCert && i < sk_X509_num(caCert); i++) {
BIO_reset(bio);
result = PEM_write_bio_X509(bio, sk_X509_value(caCert, i));
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(result != 0, K_RUNTIME_ERROR, "failed to write ca to bio");
result = BIO_get_mem_ptr(bio, &bptr);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(result != 0, K_RUNTIME_ERROR, "failed to BIO_get_mem_ptr");
tempCa += std::string(bptr->data, bptr->length);
}
ca = SensitiveValue(tempCa);
return Status::OK();
}
Status DecryptRSAPrivateKeyToMemoryInPemFormat(const std::string &keyPath, const SensitiveValue &passphrase,
SensitiveValue &key)
{
RSA *rsa = nullptr;
FILE *fp = fopen(keyPath.c_str(), "r");
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(
fp != nullptr, K_RUNTIME_ERROR,
FormatString("failed to open file: %s, errno: %d, msg: %s", keyPath, errno, StrErr(errno)));
Raii releaseFile([fp, &keyPath] {
int result = fclose(fp);
LOG_IF(ERROR, result != 0) << FormatString("failed to close file: %s, errno: %d, msg: %s", keyPath, errno,
StrErr(errno));
});
rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, (void *)passphrase.GetData());
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(rsa != nullptr, K_RUNTIME_ERROR, "failed to read RSA privateKey");
Raii releaseRSA([rsa] { RSA_free(rsa); });
BIO *bio = BIO_new(BIO_s_mem());
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(bio != nullptr, K_RUNTIME_ERROR, "failed to alloc bio mem");
Raii releaseBIO([bio] { BIO_free(bio); });
auto ret = PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL);
CHECK_FAIL_RETURN_STATUS_PRINT_ERROR(ret == 1, K_RUNTIME_ERROR, "failed to write bio RSA privateKey");
char *pemStr;
long pemLen = BIO_get_mem_data(bio, &pemStr);
key = SensitiveValue(pemStr, pemLen);
return Status::OK();
}
}