summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/TLS/OpenSSL/OpenSSLContext.cpp')
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp38
1 files changed, 38 insertions, 0 deletions
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index f90b4a8..47e7175 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -65,60 +65,65 @@ namespace {
};
std::unique_ptr<SSL_CTX> createSSL_CTX(OpenSSLContext::Mode mode) {
std::unique_ptr<SSL_CTX> sslCtx;
switch (mode) {
case OpenSSLContext::Mode::Client:
sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method()));
break;
case OpenSSLContext::Mode::Server:
sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_server_method()));
break;
}
return sslCtx;
}
std::string openSSLInternalErrorToString() {
auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
ERR_print_errors(bio.get());
std::string errorString;
errorString.resize(BIO_pending(bio.get()));
BIO_read(bio.get(), (void*)errorString.data(), errorString.size());
return errorString;
}
}
OpenSSLContext::OpenSSLContext(Mode mode) : mode_(mode), state_(State::Start) {
ensureLibraryInitialized();
context_ = createSSL_CTX(mode_);
SSL_CTX_set_options(context_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ if (mode_ == Mode::Server) {
+ SSL_CTX_set_tlsext_servername_arg(context_.get(), this);
+ SSL_CTX_set_tlsext_servername_callback(context_.get(), OpenSSLContext::handleServerNameCallback);
+ }
+
// TODO: implement CRL checking
// TODO: download CRL (HTTP transport)
// TODO: cache CRL downloads for configurable time period
// TODO: implement OCSP support
// TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt
// Load system certs
#if defined(SWIFTEN_PLATFORM_WINDOWS)
X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT");
if (systemStore) {
PCCERT_CONTEXT certContext = NULL;
while (true) {
certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, certContext);
if (!certContext) {
break;
}
OpenSSLCertificate cert(createByteArray(certContext->pbCertEncoded, certContext->cbCertEncoded));
if (store && cert.getInternalX509()) {
X509_STORE_add_cert(store, cert.getInternalX509().get());
}
}
}
#elif !defined(SWIFTEN_PLATFORM_MACOSX)
SSL_CTX_set_default_verify_paths(context_.get());
#elif defined(SWIFTEN_PLATFORM_MACOSX) && !defined(SWIFTEN_PLATFORM_IPHONE)
// On Mac OS X 10.5 (OpenSSL < 0.9.8), OpenSSL does not automatically look in the system store.
// On Mac OS X 10.6 (OpenSSL >= 0.9.8), OpenSSL *does* look in the system store to determine trust.
// However, if there is a certificate error, it will always emit the "Invalid CA" error if we didn't add
// the certificates first. See
@@ -149,124 +154,153 @@ OpenSSLContext::OpenSSLContext(Mode mode) : mode_(mode), state_(State::Start) {
OpenSSLContext::~OpenSSLContext() {
}
void OpenSSLContext::ensureLibraryInitialized() {
static OpenSSLInitializerFinalizer openSSLInit;
}
void OpenSSLContext::initAndSetBIOs() {
// Ownership of BIOs is transferred
readBIO_ = BIO_new(BIO_s_mem());
writeBIO_ = BIO_new(BIO_s_mem());
SSL_set_bio(handle_.get(), readBIO_, writeBIO_);
}
void OpenSSLContext::accept() {
assert(mode_ == Mode::Server);
handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
if (!handle_) {
state_ = State::Error;
onError(std::make_shared<TLSError>());
return;
}
initAndSetBIOs();
state_ = State::Accepting;
doAccept();
}
void OpenSSLContext::connect() {
+ connect(std::string());
+}
+
+void OpenSSLContext::connect(const std::string& requestedServerName) {
assert(mode_ == Mode::Client);
handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
if (!handle_) {
state_ = State::Error;
onError(std::make_shared<TLSError>());
return;
}
+ if (!requestedServerName.empty()) {
+ if (SSL_set_tlsext_host_name(handle_.get(), const_cast<char*>(requestedServerName.c_str())) != 1) {
+ SWIFT_LOG(error) << "Failed on SSL_set_tlsext_host_name()." << std::endl;
+ }
+ }
+
// Ownership of BIOs is transferred to the SSL_CTX instance in handle_.
initAndSetBIOs();
state_ = State::Connecting;
doConnect();
}
void OpenSSLContext::doAccept() {
auto acceptResult = SSL_accept(handle_.get());
auto error = SSL_get_error(handle_.get(), acceptResult);
switch (error) {
case SSL_ERROR_NONE: {
state_ = State::Connected;
//std::cout << x->name << std::endl;
//const char* comp = SSL_get_current_compression(handle_.get());
//std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
onConnected();
// The following call is important so the client knowns the handshake is finished.
sendPendingDataToNetwork();
break;
}
case SSL_ERROR_WANT_READ:
sendPendingDataToNetwork();
break;
case SSL_ERROR_WANT_WRITE:
sendPendingDataToNetwork();
break;
default:
SWIFT_LOG(warning) << openSSLInternalErrorToString() << std::endl;
state_ = State::Error;
onError(std::make_shared<TLSError>());
+ sendPendingDataToNetwork();
}
}
void OpenSSLContext::doConnect() {
int connectResult = SSL_connect(handle_.get());
int error = SSL_get_error(handle_.get(), connectResult);
switch (error) {
case SSL_ERROR_NONE: {
state_ = State::Connected;
//std::cout << x->name << std::endl;
//const char* comp = SSL_get_current_compression(handle_.get());
//std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
onConnected();
break;
}
case SSL_ERROR_WANT_READ:
sendPendingDataToNetwork();
break;
default:
SWIFT_LOG(warning) << openSSLInternalErrorToString() << std::endl;
state_ = State::Error;
onError(std::make_shared<TLSError>());
}
}
+int OpenSSLContext::handleServerNameCallback(SSL* ssl, int*, void* arg) {
+ if (ssl == nullptr)
+ return SSL_TLSEXT_ERR_NOACK;
+
+ const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (servername) {
+ auto serverNameString = std::string(servername);
+ auto context = reinterpret_cast<OpenSSLContext*>(arg);
+ context->onServerNameRequested(serverNameString);
+
+ if (context->abortTLSHandshake_) {
+ context->abortTLSHandshake_ = false;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
+ return SSL_TLSEXT_ERR_OK;
+}
+
void OpenSSLContext::sendPendingDataToNetwork() {
int size = BIO_pending(writeBIO_);
if (size > 0) {
SafeByteArray data;
data.resize(size);
BIO_read(writeBIO_, vecptr(data), size);
onDataForNetwork(data);
}
}
void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) {
BIO_write(readBIO_, vecptr(data), data.size());
switch (state_) {
case State::Accepting:
doAccept();
break;
case State::Connecting:
doConnect();
break;
case State::Connected:
sendPendingDataToApplication();
break;
case State::Start: assert(false); break;
case State::Error: /*assert(false);*/ break;
}
}
void OpenSSLContext::handleDataFromApplication(const SafeByteArray& data) {
if (SSL_write(handle_.get(), vecptr(data), data.size()) >= 0) {
sendPendingDataToNetwork();
@@ -358,60 +392,64 @@ bool OpenSSLContext::setPrivateKey(const PrivateKey::ref& privateKey) {
BIO_write(bio.get(), vecptr(privateKey->getData()), int(privateKey->getData().size()));
SafeByteArray safePassword;
void* password = nullptr;
if (privateKey->getPassword()) {
safePassword = privateKey->getPassword().get();
safePassword.push_back(0);
password = safePassword.data();
}
auto resultKey = PEM_read_bio_PrivateKey(bio.get(), nullptr, empty_or_preset_password_cb, password);
if (resultKey) {
if (handle_) {
auto result = SSL_use_PrivateKey(handle_.get(), resultKey);;
if (result != 1) {
return false;
}
}
else {
auto result = SSL_CTX_use_PrivateKey(context_.get(), resultKey);
if (result != 1) {
return false;
}
}
}
else {
return false;
}
return true;
}
+void OpenSSLContext::setAbortTLSHandshake(bool abort) {
+ abortTLSHandshake_ = abort;
+}
+
bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
std::shared_ptr<PKCS12Certificate> pkcs12Certificate = std::dynamic_pointer_cast<PKCS12Certificate>(certificate);
if (!pkcs12Certificate || pkcs12Certificate->isNull()) {
return false;
}
// Create a PKCS12 structure
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, vecptr(pkcs12Certificate->getData()), pkcs12Certificate->getData().size());
std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, nullptr), PKCS12_free);
BIO_free(bio);
if (!pkcs12) {
return false;
}
// Parse PKCS12
X509 *certPtr = nullptr;
EVP_PKEY* privateKeyPtr = nullptr;
STACK_OF(X509)* caCertsPtr = nullptr;
SafeByteArray password(pkcs12Certificate->getPassword());
password.push_back(0);
int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(password)), &privateKeyPtr, &certPtr, &caCertsPtr);
if (result != 1) {
return false;
}
std::shared_ptr<X509> cert(certPtr, X509_free);
std::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free);
std::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);
// Use the key & certificates