summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/TLS/OpenSSL/OpenSSLContext.cpp')
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp159
1 files changed, 159 insertions, 0 deletions
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
new file mode 100644
index 0000000..3ce8d54
--- /dev/null
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -0,0 +1,159 @@
+#include <vector>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+
+#include "Swiften/TLS/OpenSSL/OpenSSLContext.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+namespace Swift {
+
+static const int SSL_READ_BUFFERSIZE = 8192;
+
+void freeX509Stack(STACK_OF(X509)* stack) {
+ sk_X509_free(stack);
+}
+
+OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) {
+ ensureLibraryInitialized();
+ context_ = SSL_CTX_new(TLSv1_client_method());
+}
+
+OpenSSLContext::~OpenSSLContext() {
+ SSL_free(handle_);
+ SSL_CTX_free(context_);
+}
+
+void OpenSSLContext::ensureLibraryInitialized() {
+ static bool isLibraryInitialized = false;
+ if (!isLibraryInitialized) {
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ isLibraryInitialized = true;
+ }
+}
+
+void OpenSSLContext::connect() {
+ handle_ = SSL_new(context_);
+ // Ownership of BIOs is ransferred
+ readBIO_ = BIO_new(BIO_s_mem());
+ writeBIO_ = BIO_new(BIO_s_mem());
+ SSL_set_bio(handle_, readBIO_, writeBIO_);
+
+ state_ = Connecting;
+ doConnect();
+}
+
+void OpenSSLContext::doConnect() {
+ int connectResult = SSL_connect(handle_);
+ int error = SSL_get_error(handle_, connectResult);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ state_ = Connected;
+ onConnected();
+ //X509* x = SSL_get_peer_certificate(handle_);
+ //std::cout << x->name << std::endl;
+ //const char* comp = SSL_get_current_compression(handle_);
+ //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
+ break;
+ case SSL_ERROR_WANT_READ:
+ sendPendingDataToNetwork();
+ break;
+ default:
+ state_ = Error;
+ onError();
+ }
+}
+
+void OpenSSLContext::sendPendingDataToNetwork() {
+ int size = BIO_pending(writeBIO_);
+ if (size > 0) {
+ ByteArray data;
+ data.resize(size);
+ BIO_read(writeBIO_, data.getData(), size);
+ onDataForNetwork(data);
+ }
+}
+
+void OpenSSLContext::handleDataFromNetwork(const ByteArray& data) {
+ BIO_write(readBIO_, data.getData(), data.getSize());
+ switch (state_) {
+ case Connecting:
+ doConnect();
+ break;
+ case Connected:
+ sendPendingDataToApplication();
+ break;
+ case Start: assert(false); break;
+ case Error: assert(false); break;
+ }
+}
+
+void OpenSSLContext::handleDataFromApplication(const ByteArray& data) {
+ if (SSL_write(handle_, data.getData(), data.getSize()) >= 0) {
+ sendPendingDataToNetwork();
+ }
+ else {
+ state_ = Error;
+ onError();
+ }
+}
+
+void OpenSSLContext::sendPendingDataToApplication() {
+ ByteArray data;
+ data.resize(SSL_READ_BUFFERSIZE);
+ int ret = SSL_read(handle_, data.getData(), data.getSize());
+ while (ret > 0) {
+ data.resize(ret);
+ onDataForApplication(data);
+ data.resize(SSL_READ_BUFFERSIZE);
+ ret = SSL_read(handle_, data.getData(), data.getSize());
+ }
+ if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) {
+ state_ = Error;
+ onError();
+ }
+}
+
+bool OpenSSLContext::setClientCertificate(const PKCS12Certificate& certificate) {
+ if (certificate.isNull()) {
+ return false;
+ }
+
+ // Create a PKCS12 structure
+ BIO* bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, certificate.getData().getData(), certificate.getData().getSize());
+ boost::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free);
+ BIO_free(bio);
+ if (!pkcs12) {
+ return false;
+ }
+
+ // Parse PKCS12
+ X509 *certPtr = 0;
+ EVP_PKEY* privateKeyPtr = 0;
+ STACK_OF(X509)* caCertsPtr = 0;
+ int result = PKCS12_parse(pkcs12.get(), certificate.getPassword().getUTF8Data(), &privateKeyPtr, &certPtr, &caCertsPtr);
+ if (result != 1) {
+ return false;
+ }
+ boost::shared_ptr<X509> cert(certPtr, X509_free);
+ boost::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free);
+ boost::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);
+
+ // Use the key & certificates
+ if (SSL_CTX_use_certificate(context_, cert.get()) != 1) {
+ return false;
+ }
+ if (SSL_CTX_use_PrivateKey(context_, privateKey.get()) != 1) {
+ return false;
+ }
+ for (int i = 0; i < sk_X509_num(caCerts.get()); ++i) {
+ SSL_CTX_add_extra_chain_cert(context_, sk_X509_value(caCerts.get(), i));
+ }
+ return true;
+}
+
+}