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:
Max Weisel 2020-04-24 18:32:11 -04:00 committed by GitHub
parent 646b18bf28
commit 677f79b0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 34 deletions

View File

@ -104,11 +104,19 @@ namespace ix
{
; // FIXME
}
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
{
else {
if (_tlsOptions.isUsingInMemoryCAs()) {
const char *buffer = _tlsOptions.caFile.c_str();
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);
}

View File

@ -195,6 +195,61 @@ namespace ix
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
*/
@ -402,8 +457,12 @@ namespace ix
return false;
}
#endif
}
else if (SSL_CTX_load_verify_locations(
} else {
if (_tlsOptions.isUsingInMemoryCAs()) {
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
} else {
if (SSL_CTX_load_verify_locations(
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
{
auto sslErr = ERR_get_error();
@ -418,6 +477,8 @@ namespace ix
[](int preverify, X509_STORE_CTX*) -> int { return preverify; });
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
}
}
else
{
SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr);
@ -526,6 +587,10 @@ namespace ix
}
else
{
if (_tlsOptions.isUsingInMemoryCAs()) {
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
} else {
const char* root_ca_file = _tlsOptions.caFile.c_str();
STACK_OF(X509_NAME) * rootCAs;
rootCAs = SSL_load_client_CA_file(root_ca_file);
@ -548,6 +613,7 @@ namespace ix
}
}
}
}
SSL_CTX_set_verify(
_ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);

View File

@ -40,6 +40,7 @@ namespace ix
void openSSLInitialize();
std::string getSSLError(int ret);
SSL_CTX* openSSLCreateContext(std::string& errMsg);
bool openSSLAddCARootsFromString(const std::string roots);
bool openSSLClientHandshake(const std::string& hostname,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);

View File

@ -58,6 +58,10 @@ namespace ix
return caFile == kTLSCAFileUseSystemDefaults;
}
bool SocketTLSOptions::isUsingInMemoryCAs() const {
return caFile.find("-----BEGIN CERTIFICATE-----") != std::string::npos;
}
bool SocketTLSOptions::isPeerVerifyDisabled() const
{
return caFile == kTLSCAFileDisableVerify;

View File

@ -37,6 +37,8 @@ namespace ix
bool isUsingSystemDefaults() const;
bool isUsingInMemoryCAs() const;
bool isPeerVerifyDisabled() const;
bool isUsingDefaultCiphers() const;