diff --git a/ixwebsocket/IXSocketMbedTLS.cpp b/ixwebsocket/IXSocketMbedTLS.cpp index 5842fc4c..fd3804cf 100644 --- a/ixwebsocket/IXSocketMbedTLS.cpp +++ b/ixwebsocket/IXSocketMbedTLS.cpp @@ -104,10 +104,18 @@ namespace ix { ; // FIXME } - else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0) - { - errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; - return false; + 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); diff --git a/ixwebsocket/IXSocketOpenSSL.cpp b/ixwebsocket/IXSocketOpenSSL.cpp index a9ca47e9..4e88a23c 100644 --- a/ixwebsocket/IXSocketOpenSSL.cpp +++ b/ixwebsocket/IXSocketOpenSSL.cpp @@ -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(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,21 +457,27 @@ namespace ix return false; } #endif + } 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(); + 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_VERIFY_PEER, + [](int preverify, X509_STORE_CTX*) -> int { return preverify; }); + SSL_CTX_set_verify_depth(_ssl_context, 4); + } } - else if (SSL_CTX_load_verify_locations( - _ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1) - { - 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_VERIFY_PEER, - [](int preverify, X509_STORE_CTX*) -> int { return preverify; }); - SSL_CTX_set_verify_depth(_ssl_context, 4); } else { @@ -526,26 +587,31 @@ namespace ix } else { - const char* root_ca_file = _tlsOptions.caFile.c_str(); - STACK_OF(X509_NAME) * rootCAs; - rootCAs = SSL_load_client_CA_file(root_ca_file); - if (rootCAs == NULL) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile + - "') failed: "; - 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) + 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); + if (rootCAs == NULL) { auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + - _tlsOptions.caFile + "\") failed: "; + errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile + + "') failed: "; 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); + } + } } } diff --git a/ixwebsocket/IXSocketOpenSSL.h b/ixwebsocket/IXSocketOpenSSL.h index 908cc22a..46a4b260 100644 --- a/ixwebsocket/IXSocketOpenSSL.h +++ b/ixwebsocket/IXSocketOpenSSL.h @@ -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); diff --git a/ixwebsocket/IXSocketTLSOptions.cpp b/ixwebsocket/IXSocketTLSOptions.cpp index 2ea22b2a..3780d8d9 100644 --- a/ixwebsocket/IXSocketTLSOptions.cpp +++ b/ixwebsocket/IXSocketTLSOptions.cpp @@ -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; diff --git a/ixwebsocket/IXSocketTLSOptions.h b/ixwebsocket/IXSocketTLSOptions.h index 899122b3..cb480c6c 100644 --- a/ixwebsocket/IXSocketTLSOptions.h +++ b/ixwebsocket/IXSocketTLSOptions.h @@ -36,6 +36,8 @@ namespace ix bool hasCertAndKey() const; bool isUsingSystemDefaults() const; + + bool isUsingInMemoryCAs() const; bool isPeerVerifyDisabled() const;