Implement API for adding custom roots via a string (#178)
* Implement API for adding custom roots via a string. SocketTLSOptions API design needs work, but the IXSocketOpenSSL implementation feels good to me. * Improve API design for specifying roots from memory. * Add in-memory root CAs mbedtls implementation. * Fix bug in newer versions of OpenSSL with in-memory certificate handling.
This commit is contained in:
parent
646b18bf28
commit
677f79b0ea
@ -104,10 +104,18 @@ namespace ix
|
|||||||
{
|
{
|
||||||
; // FIXME
|
; // FIXME
|
||||||
}
|
}
|
||||||
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
|
else {
|
||||||
{
|
if (_tlsOptions.isUsingInMemoryCAs()) {
|
||||||
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
|
const char *buffer = _tlsOptions.caFile.c_str();
|
||||||
return false;
|
size_t bufferSize = _tlsOptions.caFile.size() + 1; // Needs to include null terminating character otherwise mbedtls will fail.
|
||||||
|
if (mbedtls_x509_crt_parse(&_cacert, (const unsigned char *)buffer, bufferSize) < 0) {
|
||||||
|
errMsg = "Cannot parse CA from memory.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0) {
|
||||||
|
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
|
mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
|
||||||
|
@ -195,6 +195,61 @@ namespace ix
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots) {
|
||||||
|
// Create certificate store
|
||||||
|
X509_STORE *certificate_store = SSL_CTX_get_cert_store(_ssl_context);
|
||||||
|
if (certificate_store == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Configure to allow intermediate certs
|
||||||
|
X509_STORE_set_flags(certificate_store, X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN);
|
||||||
|
|
||||||
|
// Create a new buffer and populate it with the roots
|
||||||
|
BIO *buffer = BIO_new_mem_buf((void *)roots.c_str(), static_cast<int>(roots.length()));
|
||||||
|
if (buffer == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Read each root in the buffer and add to the certificate store
|
||||||
|
bool success = true;
|
||||||
|
size_t number_of_roots = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Read the next root in the buffer
|
||||||
|
X509 *root = PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void *)"");
|
||||||
|
if (root == nullptr) {
|
||||||
|
// No more certs left in the buffer, we're done.
|
||||||
|
ERR_clear_error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try adding the root to the certificate store
|
||||||
|
ERR_clear_error();
|
||||||
|
if (!X509_STORE_add_cert(certificate_store, root)) {
|
||||||
|
// Failed to add. If the error is unrelated to the x509 lib or the cert already exists, we're safe to continue.
|
||||||
|
unsigned long error = ERR_get_error();
|
||||||
|
if (ERR_GET_LIB(error) != ERR_LIB_X509 || ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
|
||||||
|
// Failed. Clean up and bail.
|
||||||
|
success = false;
|
||||||
|
X509_free(root);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up and loop
|
||||||
|
X509_free(root);
|
||||||
|
number_of_roots++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up buffer
|
||||||
|
BIO_free(buffer);
|
||||||
|
|
||||||
|
// Make sure we loaded at least one certificate.
|
||||||
|
if (number_of_roots == 0)
|
||||||
|
success = false;
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a hostname matches a pattern
|
* Check whether a hostname matches a pattern
|
||||||
*/
|
*/
|
||||||
@ -402,21 +457,27 @@ namespace ix
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
} else {
|
||||||
else if (SSL_CTX_load_verify_locations(
|
if (_tlsOptions.isUsingInMemoryCAs()) {
|
||||||
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
|
// Load from memory
|
||||||
{
|
openSSLAddCARootsFromString(_tlsOptions.caFile);
|
||||||
auto sslErr = ERR_get_error();
|
} else {
|
||||||
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + _tlsOptions.caFile +
|
if (SSL_CTX_load_verify_locations(
|
||||||
"\") failed: ";
|
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
|
||||||
errMsg += ERR_error_string(sslErr, nullptr);
|
{
|
||||||
return false;
|
auto sslErr = ERR_get_error();
|
||||||
}
|
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + _tlsOptions.caFile +
|
||||||
|
"\") failed: ";
|
||||||
|
errMsg += ERR_error_string(sslErr, nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SSL_CTX_set_verify(_ssl_context,
|
SSL_CTX_set_verify(_ssl_context,
|
||||||
SSL_VERIFY_PEER,
|
SSL_VERIFY_PEER,
|
||||||
[](int preverify, X509_STORE_CTX*) -> int { return preverify; });
|
[](int preverify, X509_STORE_CTX*) -> int { return preverify; });
|
||||||
SSL_CTX_set_verify_depth(_ssl_context, 4);
|
SSL_CTX_set_verify_depth(_ssl_context, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -526,26 +587,31 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const char* root_ca_file = _tlsOptions.caFile.c_str();
|
if (_tlsOptions.isUsingInMemoryCAs()) {
|
||||||
STACK_OF(X509_NAME) * rootCAs;
|
// Load from memory
|
||||||
rootCAs = SSL_load_client_CA_file(root_ca_file);
|
openSSLAddCARootsFromString(_tlsOptions.caFile);
|
||||||
if (rootCAs == NULL)
|
} else {
|
||||||
{
|
const char* root_ca_file = _tlsOptions.caFile.c_str();
|
||||||
auto sslErr = ERR_get_error();
|
STACK_OF(X509_NAME) * rootCAs;
|
||||||
errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile +
|
rootCAs = SSL_load_client_CA_file(root_ca_file);
|
||||||
"') failed: ";
|
if (rootCAs == NULL)
|
||||||
errMsg += ERR_error_string(sslErr, nullptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SSL_CTX_set_client_CA_list(_ssl_context, rootCAs);
|
|
||||||
if (SSL_CTX_load_verify_locations(_ssl_context, root_ca_file, nullptr) != 1)
|
|
||||||
{
|
{
|
||||||
auto sslErr = ERR_get_error();
|
auto sslErr = ERR_get_error();
|
||||||
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
|
errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile +
|
||||||
_tlsOptions.caFile + "\") failed: ";
|
"') failed: ";
|
||||||
errMsg += ERR_error_string(sslErr, nullptr);
|
errMsg += ERR_error_string(sslErr, nullptr);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SSL_CTX_set_client_CA_list(_ssl_context, rootCAs);
|
||||||
|
if (SSL_CTX_load_verify_locations(_ssl_context, root_ca_file, nullptr) != 1)
|
||||||
|
{
|
||||||
|
auto sslErr = ERR_get_error();
|
||||||
|
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
|
||||||
|
_tlsOptions.caFile + "\") failed: ";
|
||||||
|
errMsg += ERR_error_string(sslErr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ namespace ix
|
|||||||
void openSSLInitialize();
|
void openSSLInitialize();
|
||||||
std::string getSSLError(int ret);
|
std::string getSSLError(int ret);
|
||||||
SSL_CTX* openSSLCreateContext(std::string& errMsg);
|
SSL_CTX* openSSLCreateContext(std::string& errMsg);
|
||||||
|
bool openSSLAddCARootsFromString(const std::string roots);
|
||||||
bool openSSLClientHandshake(const std::string& hostname,
|
bool openSSLClientHandshake(const std::string& hostname,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
@ -58,6 +58,10 @@ namespace ix
|
|||||||
return caFile == kTLSCAFileUseSystemDefaults;
|
return caFile == kTLSCAFileUseSystemDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SocketTLSOptions::isUsingInMemoryCAs() const {
|
||||||
|
return caFile.find("-----BEGIN CERTIFICATE-----") != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
bool SocketTLSOptions::isPeerVerifyDisabled() const
|
bool SocketTLSOptions::isPeerVerifyDisabled() const
|
||||||
{
|
{
|
||||||
return caFile == kTLSCAFileDisableVerify;
|
return caFile == kTLSCAFileDisableVerify;
|
||||||
|
@ -37,6 +37,8 @@ namespace ix
|
|||||||
|
|
||||||
bool isUsingSystemDefaults() const;
|
bool isUsingSystemDefaults() const;
|
||||||
|
|
||||||
|
bool isUsingInMemoryCAs() const;
|
||||||
|
|
||||||
bool isPeerVerifyDisabled() const;
|
bool isPeerVerifyDisabled() const;
|
||||||
|
|
||||||
bool isUsingDefaultCiphers() const;
|
bool isUsingDefaultCiphers() const;
|
||||||
|
Loading…
Reference in New Issue
Block a user