[PATCH 1 of 2] Curl based implementation of sslconnection
Wald Commits
scm-commit at wald.intevation.org
Thu Aug 14 11:25:17 CEST 2014
# HG changeset patch
# User Andre Heinecke <andre.heinecke at intevation.de>
# Date 1407951308 -7200
# Node ID d1c951b3012d44b177f913b7144cd8ae33830e0c
# Parent 7bd75417e14e07f34b83e1059e42ea4c4c45692b
Curl based implementation of sslconnection
diff -r 7bd75417e14e -r d1c951b3012d INSTALL
--- a/INSTALL Thu Aug 14 08:19:30 2014 +0200
+++ b/INSTALL Wed Aug 13 19:35:08 2014 +0200
@@ -61,6 +61,21 @@
cmake .. -DCMAKE_C_FLAGS=-fpic -DCMAKE_INSTALL_PREFIX=$YOURPREFIX
make && make test && make install
+Libcurl:
+ wget http://curl.haxx.se/download/curl-7.37.1.tar.{gz,gz.asc}
+ gpg2 --verify curl-7.37.1.tar.gz.asc
+ tar -xf curl-7.37.1.tar.gz
+ cd curl-7.37.1/
+
+ ./configure --prefix=$YOURPREFIX \
+ --without-nghttp2 --without-libidn --without-winidn --without-libssh2 \
+ --without-librtmp --without-libmetalink --without-axtls --without-nss \
+ --without-cyassl --without-ssl --without-gnutls --disable-gopher --disable-smtp \
+ --disable-imap --disable-pop3 --disable-tftp --disable-telnet --disable-dict \
+ --disable-proxy --disable-rtsp --disable-ldaps --disable-ldap --disable-file \
+ --disable-ftp --enable-http --enable-shared=no -enable-static=yes \
+ --with-polarssl=$YOURPREFIX --without-ca-bundle --without-ca-path
+
To compile the software you can use plain cmake. An out of source build is
highly suggested. For build options see CMakeList.txt
diff -r 7bd75417e14e -r d1c951b3012d ui/CMakeLists.txt
--- a/ui/CMakeLists.txt Thu Aug 14 08:19:30 2014 +0200
+++ b/ui/CMakeLists.txt Wed Aug 13 19:35:08 2014 +0200
@@ -12,6 +12,9 @@
add_definitions(${Qt5Widgets_DEFINITIONS})
find_package(Qt5LinguistTools)
+find_package(CURL)
+
+include_directories(${CURL_INCLUDE_DIRS})
# Common code is used in either the client or the administrator
# application or in unit tests.
@@ -19,10 +22,17 @@
${CMAKE_CURRENT_SOURCE_DIR}/certificatelist.cpp
${CMAKE_CURRENT_SOURCE_DIR}/certificate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/downloader.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/sslconnection.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/sslconnection_bare.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sslhelp.cpp
)
+if (${CURL_FOUND})
+ set(UICOMMON_SOURCES ${UICOMMON_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/sslconnection_curl.cpp)
+ add_definitions(-DUSE_CURL)
+else()
+ MESSAGE(STATUS "Warning curl not found only bare polarssl ssl will be supported.")
+endif()
+
# Cmake does not correctly identify gcc windres when cross compiling
# making this line neccessary to set the correct flags for it.
# See: http://public.kitware.com/Bug/view.php?id=11773
@@ -194,6 +204,8 @@
Qt5::Widgets
ui_common
trustbridge_common
+ ${CURL_LIBRARIES}
+ z
${POLARSSL_LIBRARIES}
${EXTRA_STATIC_LIBS}
${PROFILING_LIBS})
diff -r 7bd75417e14e -r d1c951b3012d ui/downloader.cpp
--- a/ui/downloader.cpp Thu Aug 14 08:19:30 2014 +0200
+++ b/ui/downloader.cpp Wed Aug 13 19:35:08 2014 +0200
@@ -28,6 +28,8 @@
#define MAX_SW_SIZE 15728640
#define MAX_LIST_SIZE 1048576
+#include "sslconnection_curl.h"
+#include "sslconnection_bare.h"
Downloader::Downloader(QObject* parent, const QString& url,
const QByteArray& certificate,
@@ -41,13 +43,18 @@
mLastModList(newestList),
mResourceSW(resourceSW),
mResourceList(resourceList),
- mDownloadSW(downloadSW),
- mSSLConnection(url, certificate)
+ mDownloadSW(downloadSW)
{
+#ifdef USE_CURL
+ mSSLConnection = new SSLConnectionCurl(url, certificate);
+#else
+ mSSLConnection = new SSLConnectionBare(url, certificate);
+#endif
}
Downloader::~Downloader() {
+ delete mSSLConnection;
}
QString Downloader::getDataDirectory()
@@ -112,13 +119,13 @@
QString headRequest =
QString::fromLatin1("HEAD %1 HTTP/1.0\r\n\r\n").arg(resource);
- ret = mSSLConnection.write(headRequest.toUtf8());
+ ret = mSSLConnection->write(headRequest.toUtf8());
if (ret != 0) {
emit error (tr("Connection lost"), SSLConnection::ConnectionLost);
return QDateTime();
}
- response = mSSLConnection.read(1024);
+ response = mSSLConnection->read(1024);
qDebug() << "Response from server was: " << response;
@@ -152,7 +159,7 @@
QSaveFile outputFile(fileName);
- ret = mSSLConnection.write(getRequest.toUtf8());
+ ret = mSSLConnection->write(getRequest.toUtf8());
// Open / Create the file to write to.
if (!outputFile.open(QIODevice::WriteOnly)) {
@@ -170,7 +177,7 @@
do {
/* Read the response in 8KiB chunks */
int responseSize = 0;
- QByteArray response = mSSLConnection.read(8192);
+ QByteArray response = mSSLConnection->read(8192);
if (response.isNull()) {
qDebug() << "Error reading response";
emit error(tr("Connection lost"), SSLConnection::ConnectionLost);
@@ -198,16 +205,16 @@
QDateTime remoteModList;
QDateTime remoteModSW;
- if (!mSSLConnection.initialized()) {
+ if (!mSSLConnection->initialized()) {
emit error(tr("Failed to initialize SSL Module."), SSLConnection::ErrUnknown);
return;
}
- ret = mSSLConnection.connect();
+ ret = mSSLConnection->connect();
if (ret != 0) {
emit error(tr("Failed to connect."),
- mSSLConnection.getLastError());
+ mSSLConnection->getLastError());
return;
}
diff -r 7bd75417e14e -r d1c951b3012d ui/downloader.h
--- a/ui/downloader.h Thu Aug 14 08:19:30 2014 +0200
+++ b/ui/downloader.h Wed Aug 13 19:35:08 2014 +0200
@@ -83,7 +83,7 @@
bool mDownloadSW;
- SSLConnection mSSLConnection;
+ SSLConnection *mSSLConnection;
/** @brief get the last modified header of a resource.
*
diff -r 7bd75417e14e -r d1c951b3012d ui/sslconnection.cpp
--- a/ui/sslconnection.cpp Thu Aug 14 08:19:30 2014 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,367 +0,0 @@
-/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU GPL (v>=2)
- * and comes with ABSOLUTELY NO WARRANTY!
- * See LICENSE.txt for details.
- */
-/* TODO: Wrap ssl_session in a class for reuse.
- * see programs/ssl/ssl_client2.c for example of session reuse */
-#include "sslconnection.h"
-#include "sslhelp.h"
-
-#include <QFile>
-#include <QUuid>
-#include <QApplication>
-
-#define MAX_IO_TRIES 10
-#define MAX_RESETS 10
-
-#ifdef CONNECTION_DEBUG
-static void my_debug(void *ctx, int level, const char *str)
-{
- fprintf((FILE *) ctx, "%s", str);
- fflush((FILE *) ctx);
-}
-#endif
-
-SSLConnection::SSLConnection(const QString& url,
- const QByteArray& certificate):
- mUrl(url),
- mPinnedCert(certificate),
- mInitialized(false),
- mConnected(false),
- mNeedsReset(false),
- mServerFD(-1),
- mErrorState(NoError)
-{
- int ret = -1;
-
- memset(&mSSL, 0, sizeof(ssl_context));
- memset(&mSavedSession, 0, sizeof( ssl_session ) );
-
- if (certificate.isEmpty()) {
- QFile certResource(":certs/intevation.de");
- certResource.open(QFile::ReadOnly);
- mPinnedCert = certResource.readAll();
- certResource.close();
- }
-
- ret = init();
- if (ret == 0) {
- mInitialized = true;
- } else {
- qDebug() << "Initialization error: " + getPolarSSLErrorMsg(ret);
- }
-}
-
-int SSLConnection::init()
-{
- int ret = -1;
- QUuid uuid = QUuid::createUuid();
- QString personalString = QApplication::applicationName() + uuid.toString();
- QByteArray personalBa = personalString.toLocal8Bit();
-
- x509_crt_init(&mX509PinnedCert);
- entropy_init(&mEntropy);
-
- ret = ssl_init(&mSSL);
- if (ret != 0) {
- /* The only documented error is malloc failed */
- mErrorState = ErrUnknown;
- return ret;
- }
-
- /*
- * Initialize random generator.
- * Personalisation string, does not need to be random but
- * should be unique according to documentation.
- *
- * the ctr_drbg structure does not need to be freed explicitly.
- */
- ret = ctr_drbg_init(&mCtr_drbg, entropy_func, &mEntropy,
- (const unsigned char*) personalBa.constData(),
- personalBa.size());
- if (ret != 0) {
- ssl_free(&mSSL);
- mErrorState = ErrUnknown;
- return ret;
- }
-
- ret = x509_crt_parse(&mX509PinnedCert,
- (const unsigned char*) mPinnedCert.constData(),
- mPinnedCert.size());
- if (ret != 0){
- ssl_free(&mSSL);
- mErrorState = InvalidPinnedCertificate;
- return ret;
- }
-
- ssl_set_endpoint(&mSSL, SSL_IS_CLIENT);
- ssl_set_authmode(&mSSL, SSL_VERIFY_OPTIONAL);
- ssl_set_ca_chain(&mSSL, &mX509PinnedCert, NULL, NULL);
- ssl_set_renegotiation(&mSSL, SSL_RENEGOTIATION_DISABLED);
- ssl_set_rng(&mSSL, ctr_drbg_random, &mCtr_drbg);
-#ifdef RELEASE_BUILD
- ssl_set_min_version(&mSSL, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3);
-#endif
-
-#ifdef CONNECTION_DEBUG
- ssl_set_dbg(&mSSL, my_debug, stdout);
-#endif
-
- return 0;
-}
-
-SSLConnection::~SSLConnection() {
- disconnect();
- x509_crt_free(&mX509PinnedCert);
- entropy_free(&mEntropy);
- if (mInitialized) {
- ssl_free(&mSSL);
- }
-}
-
-void SSLConnection::disconnect() {
- if (mConnected) {
- ssl_close_notify(&mSSL);
- if (mServerFD != -1) {
- net_close(mServerFD);
- mServerFD = -1;
- }
- ssl_session_free(&mSavedSession);
- mConnected = false;
- }
-}
-
-int SSLConnection::connect() {
- int ret = -1;
-
- if (!mInitialized) {
- mErrorState = ErrUnknown;
- return -1;
- }
-
- ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
- mUrl.port(443));
-
- if (ret != 0) {
- qDebug() << "Connect failed: " << getPolarSSLErrorMsg(ret);
- mErrorState = NoConnection;
- return ret;
- }
-
- ssl_set_bio(&mSSL, net_recv, &mServerFD,
- net_send, &mServerFD);
-
- while ((ret = ssl_handshake(&mSSL)) != 0) {
- if (ret != POLARSSL_ERR_NET_WANT_READ &&
- ret != POLARSSL_ERR_NET_WANT_WRITE) {
- qDebug() << "SSL Handshake failed: " << getPolarSSLErrorMsg(ret);
- mErrorState = SSLHandshakeFailed;
- return ret;
- }
- }
-
- ret = ssl_get_session(&mSSL, &mSavedSession);
- if (ret != 0) {
- qDebug() << "SSL get session failed: " << getPolarSSLErrorMsg(ret);
-
- mErrorState = NoConnection;
- return ret;
- }
- printf( " ok\n [ Ciphersuite is %s ]\n",
- ssl_get_ciphersuite( &mSSL) );
- ret = validateCertificate();
-
- if (ret == 0) {
- mConnected = true;
- }
- return ret;
-}
-
-int SSLConnection::validateCertificate()
-{
- int ret = -1;
- const x509_crt *peerCert = NULL;
-
- /* we might want to set the verify function
- * with ssl_set_verify before to archive the
- * certificate pinning. */
-
- ret = ssl_get_verify_result(&mSSL);
-
- if (ret != 0 ) {
- if((ret & BADCERT_EXPIRED) != 0)
- qDebug() << "server certificate has expired";
- if((ret & BADCERT_REVOKED) != 0)
- qDebug() << "server certificate has been revoked";
- if((ret & BADCERT_CN_MISMATCH) != 0)
- qDebug() << "CN mismatch";
- if((ret & BADCERT_NOT_TRUSTED) != 0)
- qDebug() << "self-signed or not signed by a trusted CA";
- ret = -1;
-#ifdef RELEASE_BUILD
- mErrorState = InvalidCertificate;
- return -1;
-#endif
- }
-
- peerCert = ssl_get_peer_cert(&mSSL);
-
- if (!peerCert) {
- mErrorState = InvalidCertificate;
- qDebug() << "Failed to get peer cert";
- return -1;
- }
-
- if (peerCert->raw.len == 0 ||
- peerCert->raw.len != mX509PinnedCert.raw.len) {
- mErrorState = InvalidCertificate;
- qDebug() << "Certificate length mismatch";
- return -1;
- }
-
- /* You can never be sure what those c++ operators do..
- if (mPinnedCert != QByteArray::fromRawData(
- (const char*) peerCert->raw.p,
- peerCert->raw.len)) {
- qDebug() << "Certificate content mismatch";
- }
- */
-
- for (unsigned int i = 0; i < peerCert->raw.len; i++) {
- if (peerCert->raw.p[i] != mX509PinnedCert.raw.p[i]) {
- qDebug() << "Certificate content mismatch";
- mErrorState = InvalidCertificate;
- return -1;
- }
- }
- return 0;
-}
-
-int SSLConnection::write (const QByteArray& request)
-{
- unsigned int tries = 0;
- int ret = -1;
-
- const unsigned char *buf = (const unsigned char *) request.constData();
- size_t len = (size_t) request.size();
-
- if (mNeedsReset) {
- ret = reset();
- if (ret != 0) {
- qDebug() << "Reset failed: " << getPolarSSLErrorMsg(ret);
- return ret;
- }
- }
-
- qDebug() << "Sending request: " << request;
- /* According to doc for ssl_write:
- *
- * When this function returns POLARSSL_ERR_NET_WANT_WRITE,
- * it must be called later with the same arguments,
- * until it returns a positive value.
- */
- do {
- ret = ssl_write(&mSSL, buf, len);
- if (ret >= 0) {
- if ((unsigned int) ret == len) {
- return 0;
- } else {
- qDebug() << "Write failed to write everything";
- return -1;
- }
- }
- if (ret != POLARSSL_ERR_NET_WANT_WRITE &&
- ret != POLARSSL_ERR_NET_WANT_READ) {
- return ret;
- }
- tries++;
- net_usleep(100000); /* sleep 100ms to give the socket a chance
- to clean up. */
- } while (tries < MAX_IO_TRIES);
-
- return ret;
-}
-
-
-int SSLConnection::reset()
-{
- int ret = -1;
- ssl_close_notify(&mSSL);
-
- ret = ssl_session_reset(&mSSL);
- if (ret != 0)
- {
- qDebug() << "SSL Connection reset failed: "
- << getPolarSSLErrorMsg(ret);
- return ret;
- }
-
- ssl_set_session(&mSSL, &mSavedSession);
-
- ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
- mUrl.port(443));
-
- if (ret != 0) {
- mErrorState = NoConnection;
- qDebug() << "Connection failed." << getPolarSSLErrorMsg(ret);
- return ret;
- }
-
- while ((ret = ssl_handshake(&mSSL)) != 0) {
- if (ret != POLARSSL_ERR_NET_WANT_READ &&
- ret != POLARSSL_ERR_NET_WANT_WRITE) {
- qDebug() << "SSL Handshake failed: "
- << getPolarSSLErrorMsg(ret);
- mErrorState = SSLHandshakeFailed;
- return ret;
- }
- }
-
- qDebug() << "Reset connection. ";
- /* Validation should not be necessary as we reused a saved
- * session. But just to be sure. */
- return validateCertificate();
-}
-
-QByteArray SSLConnection::read(size_t len)
-{
- unsigned char buf[len];
- QByteArray retval("");
- int ret = -1;
- unsigned int tries = 0;
-
- mNeedsReset = true;
- do {
- memset (buf, 0, sizeof(buf));
- ret = ssl_read(&mSSL, buf, len);
- if (ret == 0 ||
- ret == POLARSSL_ERR_SSL_CONN_EOF ||
- ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) {
- /* EOF */
- return retval;
- }
- if (ret == POLARSSL_ERR_NET_WANT_WRITE ||
- ret == POLARSSL_ERR_NET_WANT_READ) {
- net_usleep(100000); /* sleep 100ms to give the socket a chance
- to recover */
- tries++;
- }
- if (ret <= 0) {
- qDebug() << "Read failed: " << getPolarSSLErrorMsg(ret);
- return QByteArray();
- }
- if (len < (len - (unsigned int) ret)) {
- /* Should never happen if ssl_read behaves */
- qDebug() << "integer overflow in polarSSLRead";
- return QByteArray();
- }
- len -= (unsigned int) ret;
- retval.append((const char *)buf, ret);
- } while (len > 0 && tries < MAX_IO_TRIES);
-
- return retval;
-}
-
diff -r 7bd75417e14e -r d1c951b3012d ui/sslconnection.h
--- a/ui/sslconnection.h Thu Aug 14 08:19:30 2014 +0200
+++ b/ui/sslconnection.h Wed Aug 13 19:35:08 2014 +0200
@@ -18,13 +18,6 @@
#include <QString>
#include <QByteArray>
-#include <polarssl/entropy.h>
-#include <polarssl/net.h>
-#include <polarssl/ssl.h>
-#include <polarssl/ctr_drbg.h>
-#include <polarssl/error.h>
-#include <polarssl/certs.h>
-
class SSLConnection
{
public:
@@ -47,12 +40,19 @@
* @param[in] certificate optional certificate to validate https connection
*/
SSLConnection(const QString& url,
- const QByteArray& certificate = QByteArray());
+ const QByteArray& certificate = QByteArray()) :
+ mUrl(url),
+ mPinnedCert(certificate),
+ mInitialized(false),
+ mConnected(false),
+ mNeedsReset(false),
+ mServerFD(-1),
+ mErrorState(NoError) {};
- ~SSLConnection();
+ virtual ~SSLConnection() {};
/** @brief write */
- int write(const QByteArray& request);
+ virtual int write(const QByteArray& request) = 0;
/**
* @brief read at most len bytes and reset the connection
@@ -61,7 +61,7 @@
*
* @returns a byte array containing the data or
* a NULL byte array on error*/
- QByteArray read(size_t len);
+ virtual QByteArray read(size_t len) = 0;
bool initialized() { return mInitialized; }
bool connected() { return mConnected; }
@@ -70,51 +70,19 @@
/** @brief: Establish the connection
*
- * @returns 0 on success otherwise a polarssl error or -1 is returned
+ * @returns 0 on success otherwise an error or -1 is returned
*/
- int connect();
+ virtual int connect() = 0;
-private:
+protected:
QUrl mUrl;
QByteArray mPinnedCert;
- x509_crt mX509PinnedCert;
- entropy_context mEntropy;
- ctr_drbg_context mCtr_drbg;
- ssl_context mSSL;
- ssl_session mSavedSession;
bool mInitialized;
bool mConnected; /* A connection was established */
bool mNeedsReset; /* The connection needs to be reset before the next
write */
int mServerFD;
SSLConnection::ErrorCode mErrorState;
- /* @brief: Initialize polarssl structures
- *
- * This wraps polarssl initialization functions
- * that can return an error.
- * Sets the error state accordingly.
- *
- * @returns: 0 on success a polarssl error otherwise.
- */
- int init();
-
- /* @brief Reset the connection.
- *
- * Resets the https connection and does another handshake.
- *
- * @returns: 0 on success a polarssl error or -1 otherwise. */
- int reset();
-
- /* @brief validates that the certificate matches the pinned one.
- *
- * Checks the peer certificate of mSSL and validates that the
- * certificate matches mPinnedCertificate.
- *
- * @returns: 0 on success a polarssl error or -1 otherwise. */
- int validateCertificate();
-
- /* @brief disconnects the connection */
- void disconnect();
};
#endif
diff -r 7bd75417e14e -r d1c951b3012d ui/sslconnection_bare.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/sslconnection_bare.cpp Wed Aug 13 19:35:08 2014 +0200
@@ -0,0 +1,362 @@
+/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU GPL (v>=2)
+ * and comes with ABSOLUTELY NO WARRANTY!
+ * See LICENSE.txt for details.
+ */
+/* TODO: Wrap ssl_session in a class for reuse.
+ * see programs/ssl/ssl_client2.c for example of session reuse */
+#include "sslconnection_bare.h"
+#include "sslhelp.h"
+
+#include <QFile>
+#include <QUuid>
+#include <QApplication>
+
+#define MAX_IO_TRIES 10
+#define MAX_RESETS 10
+
+#ifdef CONNECTION_DEBUG
+static void my_debug(void *ctx, int level, const char *str)
+{
+ fprintf((FILE *) ctx, "%s", str);
+ fflush((FILE *) ctx);
+}
+#endif
+
+SSLConnectionBare::SSLConnectionBare(const QString& url,
+ const QByteArray& certificate):
+ SSLConnection (url, certificate)
+{
+ int ret = -1;
+
+ memset(&mSSL, 0, sizeof(ssl_context));
+ memset(&mSavedSession, 0, sizeof( ssl_session ) );
+
+ if (certificate.isEmpty()) {
+ QFile certResource(":certs/intevation.de");
+ certResource.open(QFile::ReadOnly);
+ mPinnedCert = certResource.readAll();
+ certResource.close();
+ }
+
+ ret = init();
+ if (ret == 0) {
+ mInitialized = true;
+ } else {
+ qDebug() << "Initialization error: " + getPolarSSLErrorMsg(ret);
+ }
+}
+
+int SSLConnectionBare::init()
+{
+ int ret = -1;
+ QUuid uuid = QUuid::createUuid();
+ QString personalString = QApplication::applicationName() + uuid.toString();
+ QByteArray personalBa = personalString.toLocal8Bit();
+
+ x509_crt_init(&mX509PinnedCert);
+ entropy_init(&mEntropy);
+
+ ret = ssl_init(&mSSL);
+ if (ret != 0) {
+ /* The only documented error is malloc failed */
+ mErrorState = ErrUnknown;
+ return ret;
+ }
+
+ /*
+ * Initialize random generator.
+ * Personalisation string, does not need to be random but
+ * should be unique according to documentation.
+ *
+ * the ctr_drbg structure does not need to be freed explicitly.
+ */
+ ret = ctr_drbg_init(&mCtr_drbg, entropy_func, &mEntropy,
+ (const unsigned char*) personalBa.constData(),
+ personalBa.size());
+ if (ret != 0) {
+ ssl_free(&mSSL);
+ mErrorState = ErrUnknown;
+ return ret;
+ }
+
+ ret = x509_crt_parse(&mX509PinnedCert,
+ (const unsigned char*) mPinnedCert.constData(),
+ mPinnedCert.size());
+ if (ret != 0){
+ ssl_free(&mSSL);
+ mErrorState = InvalidPinnedCertificate;
+ return ret;
+ }
+
+ ssl_set_endpoint(&mSSL, SSL_IS_CLIENT);
+ ssl_set_authmode(&mSSL, SSL_VERIFY_OPTIONAL);
+ ssl_set_ca_chain(&mSSL, &mX509PinnedCert, NULL, NULL);
+ ssl_set_renegotiation(&mSSL, SSL_RENEGOTIATION_DISABLED);
+ ssl_set_rng(&mSSL, ctr_drbg_random, &mCtr_drbg);
+#ifdef RELEASE_BUILD
+ ssl_set_min_version(&mSSL, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3);
+#endif
+
+#ifdef CONNECTION_DEBUG
+ ssl_set_dbg(&mSSL, my_debug, stdout);
+#endif
+
+ return 0;
+}
+
+SSLConnectionBare::~SSLConnectionBare() {
+ disconnect();
+ x509_crt_free(&mX509PinnedCert);
+ entropy_free(&mEntropy);
+ if (mInitialized) {
+ ssl_free(&mSSL);
+ }
+}
+
+void SSLConnectionBare::disconnect() {
+ if (mConnected) {
+ ssl_close_notify(&mSSL);
+ if (mServerFD != -1) {
+ net_close(mServerFD);
+ mServerFD = -1;
+ }
+ ssl_session_free(&mSavedSession);
+ mConnected = false;
+ }
+}
+
+int SSLConnectionBare::connect() {
+ int ret = -1;
+
+ if (!mInitialized) {
+ mErrorState = ErrUnknown;
+ return -1;
+ }
+
+ ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
+ mUrl.port(443));
+
+ if (ret != 0) {
+ qDebug() << "Connect failed: " << getPolarSSLErrorMsg(ret);
+ mErrorState = NoConnection;
+ return ret;
+ }
+
+ ssl_set_bio(&mSSL, net_recv, &mServerFD,
+ net_send, &mServerFD);
+
+ while ((ret = ssl_handshake(&mSSL)) != 0) {
+ if (ret != POLARSSL_ERR_NET_WANT_READ &&
+ ret != POLARSSL_ERR_NET_WANT_WRITE) {
+ qDebug() << "SSL Handshake failed: " << getPolarSSLErrorMsg(ret);
+ mErrorState = SSLHandshakeFailed;
+ return ret;
+ }
+ }
+
+ ret = ssl_get_session(&mSSL, &mSavedSession);
+ if (ret != 0) {
+ qDebug() << "SSL get session failed: " << getPolarSSLErrorMsg(ret);
+
+ mErrorState = NoConnection;
+ return ret;
+ }
+ printf( " ok\n [ Ciphersuite is %s ]\n",
+ ssl_get_ciphersuite( &mSSL) );
+ ret = validateCertificate();
+
+ if (ret == 0) {
+ mConnected = true;
+ }
+ return ret;
+}
+
+int SSLConnectionBare::validateCertificate()
+{
+ int ret = -1;
+ const x509_crt *peerCert = NULL;
+
+ /* we might want to set the verify function
+ * with ssl_set_verify before to archive the
+ * certificate pinning. */
+
+ ret = ssl_get_verify_result(&mSSL);
+
+ if (ret != 0 ) {
+ if((ret & BADCERT_EXPIRED) != 0)
+ qDebug() << "server certificate has expired";
+ if((ret & BADCERT_REVOKED) != 0)
+ qDebug() << "server certificate has been revoked";
+ if((ret & BADCERT_CN_MISMATCH) != 0)
+ qDebug() << "CN mismatch";
+ if((ret & BADCERT_NOT_TRUSTED) != 0)
+ qDebug() << "self-signed or not signed by a trusted CA";
+ ret = -1;
+#ifdef RELEASE_BUILD
+ mErrorState = InvalidCertificate;
+ return -1;
+#endif
+ }
+
+ peerCert = ssl_get_peer_cert(&mSSL);
+
+ if (!peerCert) {
+ mErrorState = InvalidCertificate;
+ qDebug() << "Failed to get peer cert";
+ return -1;
+ }
+
+ if (peerCert->raw.len == 0 ||
+ peerCert->raw.len != mX509PinnedCert.raw.len) {
+ mErrorState = InvalidCertificate;
+ qDebug() << "Certificate length mismatch";
+ return -1;
+ }
+
+ /* You can never be sure what those c++ operators do..
+ if (mPinnedCert != QByteArray::fromRawData(
+ (const char*) peerCert->raw.p,
+ peerCert->raw.len)) {
+ qDebug() << "Certificate content mismatch";
+ }
+ */
+
+ for (unsigned int i = 0; i < peerCert->raw.len; i++) {
+ if (peerCert->raw.p[i] != mX509PinnedCert.raw.p[i]) {
+ qDebug() << "Certificate content mismatch";
+ mErrorState = InvalidCertificate;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int SSLConnectionBare::write (const QByteArray& request)
+{
+ unsigned int tries = 0;
+ int ret = -1;
+
+ const unsigned char *buf = (const unsigned char *) request.constData();
+ size_t len = (size_t) request.size();
+
+ if (mNeedsReset) {
+ ret = reset();
+ if (ret != 0) {
+ qDebug() << "Reset failed: " << getPolarSSLErrorMsg(ret);
+ return ret;
+ }
+ }
+
+ qDebug() << "Sending request: " << request;
+ /* According to doc for ssl_write:
+ *
+ * When this function returns POLARSSL_ERR_NET_WANT_WRITE,
+ * it must be called later with the same arguments,
+ * until it returns a positive value.
+ */
+ do {
+ ret = ssl_write(&mSSL, buf, len);
+ if (ret >= 0) {
+ if ((unsigned int) ret == len) {
+ return 0;
+ } else {
+ qDebug() << "Write failed to write everything";
+ return -1;
+ }
+ }
+ if (ret != POLARSSL_ERR_NET_WANT_WRITE &&
+ ret != POLARSSL_ERR_NET_WANT_READ) {
+ return ret;
+ }
+ tries++;
+ net_usleep(100000); /* sleep 100ms to give the socket a chance
+ to clean up. */
+ } while (tries < MAX_IO_TRIES);
+
+ return ret;
+}
+
+
+int SSLConnectionBare::reset()
+{
+ int ret = -1;
+ ssl_close_notify(&mSSL);
+
+ ret = ssl_session_reset(&mSSL);
+ if (ret != 0)
+ {
+ qDebug() << "SSL Connection reset failed: "
+ << getPolarSSLErrorMsg(ret);
+ return ret;
+ }
+
+ ssl_set_session(&mSSL, &mSavedSession);
+
+ ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
+ mUrl.port(443));
+
+ if (ret != 0) {
+ mErrorState = NoConnection;
+ qDebug() << "Connection failed." << getPolarSSLErrorMsg(ret);
+ return ret;
+ }
+
+ while ((ret = ssl_handshake(&mSSL)) != 0) {
+ if (ret != POLARSSL_ERR_NET_WANT_READ &&
+ ret != POLARSSL_ERR_NET_WANT_WRITE) {
+ qDebug() << "SSL Handshake failed: "
+ << getPolarSSLErrorMsg(ret);
+ mErrorState = SSLHandshakeFailed;
+ return ret;
+ }
+ }
+
+ qDebug() << "Reset connection. ";
+ /* Validation should not be necessary as we reused a saved
+ * session. But just to be sure. */
+ return validateCertificate();
+}
+
+QByteArray SSLConnectionBare::read(size_t len)
+{
+ unsigned char buf[len];
+ QByteArray retval("");
+ int ret = -1;
+ unsigned int tries = 0;
+
+ mNeedsReset = true;
+ do {
+ memset (buf, 0, sizeof(buf));
+ ret = ssl_read(&mSSL, buf, len);
+ if (ret == 0 ||
+ ret == POLARSSL_ERR_SSL_CONN_EOF ||
+ ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) {
+ /* EOF */
+ return retval;
+ }
+ if (ret == POLARSSL_ERR_NET_WANT_WRITE ||
+ ret == POLARSSL_ERR_NET_WANT_READ) {
+ net_usleep(100000); /* sleep 100ms to give the socket a chance
+ to recover */
+ tries++;
+ }
+ if (ret <= 0) {
+ qDebug() << "Read failed: " << getPolarSSLErrorMsg(ret);
+ return QByteArray();
+ }
+ if (len < (len - (unsigned int) ret)) {
+ /* Should never happen if ssl_read behaves */
+ qDebug() << "integer overflow in polarSSLRead";
+ return QByteArray();
+ }
+ len -= (unsigned int) ret;
+ retval.append((const char *)buf, ret);
+ } while (len > 0 && tries < MAX_IO_TRIES);
+
+ return retval;
+}
+
+
diff -r 7bd75417e14e -r d1c951b3012d ui/sslconnection_bare.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/sslconnection_bare.h Wed Aug 13 19:35:08 2014 +0200
@@ -0,0 +1,87 @@
+#ifndef UI_SSLCONNECTION_BARE_H
+#define UI_SSLCONNECTION_BARE_H
+/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU GPL (v>=2)
+ * and comes with ABSOLUTELY NO WARRANTY!
+ * See LICENSE.txt for details.
+ */
+
+#include "sslconnection.h"
+
+#include <polarssl/entropy.h>
+#include <polarssl/net.h>
+#include <polarssl/ssl.h>
+#include <polarssl/ctr_drbg.h>
+#include <polarssl/error.h>
+#include <polarssl/certs.h>
+
+/**
+ * @file sslconnection_bare.h
+ * @brief SSLConnection doing bare SSL over PolarSSL
+ * */
+
+class SSLConnectionBare : public SSLConnection
+{
+public:
+ SSLConnectionBare(const QString& url,
+ const QByteArray& certificate = QByteArray());
+
+ ~SSLConnectionBare();
+
+ /** @brief write */
+ int write(const QByteArray& request);
+
+ /**
+ * @brief read at most len bytes and reset the connection
+ *
+ * @param [in] len Amount of bytes to read.
+ *
+ * @returns a byte array containing the data or
+ * a NULL byte array on error*/
+ QByteArray read(size_t len);
+
+ /** @brief: Establish the connection
+ *
+ * @returns 0 on success otherwise an error or -1 is returned
+ */
+ int connect();
+
+private:
+ x509_crt mX509PinnedCert;
+ entropy_context mEntropy;
+ ctr_drbg_context mCtr_drbg;
+ ssl_context mSSL;
+ ssl_session mSavedSession;
+
+ /* @brief: Initialize polarssl structures
+ *
+ * This wraps polarssl initialization functions
+ * that can return an error.
+ * Sets the error state accordingly.
+ *
+ * @returns: 0 on success a polarssl error otherwise.
+ */
+ int init();
+
+ /* @brief Reset the connection.
+ *
+ * Resets the https connection and does another handshake.
+ *
+ * @returns: 0 on success a polarssl error or -1 otherwise. */
+ int reset();
+
+ /* @brief validates that the certificate matches the pinned one.
+ *
+ * Checks the peer certificate of mSSL and validates that the
+ * certificate matches mPinnedCertificate.
+ *
+ * @returns: 0 on success a polarssl error or -1 otherwise. */
+ int validateCertificate();
+
+ /* @brief disconnects the connection */
+ void disconnect();
+};
+
+#endif // UI_SSLCONNECTION_BARE_H
diff -r 7bd75417e14e -r d1c951b3012d ui/sslconnection_curl.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/sslconnection_curl.cpp Wed Aug 13 19:35:08 2014 +0200
@@ -0,0 +1,133 @@
+/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU GPL (v>=2)
+ * and comes with ABSOLUTELY NO WARRANTY!
+ * See LICENSE.txt for details.
+ */
+
+#include "sslconnection_curl.h"
+
+#define CONNECTION_DEBUG
+
+SSLConnectionCurl::SSLConnectionCurl(const QString& url,
+ const QByteArray& certificate):
+ SSLConnection (url, certificate),
+ mCurl (NULL)
+{
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+ mCurl = curl_easy_init();
+
+ if (!mCurl) {
+ qDebug() << "Failed to initialize curl";
+ return;
+ }
+
+ if (curl_easy_setopt(mCurl, CURLOPT_URL, QUrl(url).toEncoded().constData()) != CURLE_OK) {
+ qDebug() << "Setting url failed";
+ return;
+ }
+
+ if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, 1L) != CURLE_OK) {
+ /* Should be default anyway */
+ qDebug() << "Setting verifypeer failed";
+ return;
+ }
+
+ if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYHOST, 0L) != CURLE_OK) {
+ /* There are no limitiations for the pinned certificate */
+ qDebug() << "Setting verifyhost failed";
+ return;
+ }
+
+ mCertFile.open();
+ if (mCertFile.write(mPinnedCert) != mPinnedCert.size()) {
+ qDebug() << "Failed to write temporary certificate";
+ return;
+ }
+ mCertFile.close();
+
+ if (curl_easy_setopt(mCurl, CURLOPT_CAINFO,
+ mCertFile.fileName().toUtf8().constData()) != CURLE_OK) {
+ qDebug() << "Failed to write temporary certificate";
+ return;
+ }
+ mInitialized = true;
+
+#ifdef CONNECTION_DEBUG
+ curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1L);
+#endif
+}
+
+SSLConnectionCurl::~SSLConnectionCurl() {
+ if (mCurl) {
+ curl_easy_cleanup (mCurl);
+ }
+ if (mInitialized) {
+ mCertFile.close();
+ }
+ curl_global_cleanup();
+}
+
+int SSLConnectionCurl::connect() {
+ if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 1L) != CURLE_OK) {
+ qDebug() << "Failed to set connect only option";
+ return -1;
+ }
+ if (curl_easy_perform (mCurl) != CURLE_OK) {
+ qDebug() << "Failed to connect";
+ mErrorState = NoConnection;
+ return -1;
+ }
+ mConnected = true;
+ return 0;
+}
+
+int SSLConnectionCurl::write(const QByteArray& request) {
+ size_t written = 0;
+
+ if (curl_easy_send (mCurl, request.constData(), request.size(), &written) != CURLE_OK) {
+ qDebug() << "Failed to send request";
+ return -1;
+ }
+ if (written != (size_t)request.size()) {
+ qDebug() << "Failed to write everything";
+ return -1;
+ }
+ return 0;
+}
+
+QByteArray SSLConnectionCurl::read(size_t len)
+{
+ unsigned char buf[len];
+ QByteArray retval("");
+ CURLcode ret;
+ size_t read = 0;
+ unsigned int tries = 0;
+
+ do {
+ memset (buf, 0, sizeof(buf));
+ ret = curl_easy_recv (mCurl, buf, len, &read);
+ if (ret == CURLE_OK && read == 0) {
+ return retval;
+ }
+ if (ret == CURLE_AGAIN) {
+ tries++;
+ continue;
+ }
+ if (ret != CURLE_OK) {
+ qDebug() << "Read failed.";
+ return QByteArray();
+ }
+ if (len < (len - read)) {
+ /* Should never happen if ssl_read behaves */
+ qDebug() << "integer overflow in polarSSLRead";
+ return QByteArray();
+ }
+
+ len -= read;
+ retval.append((const char *)buf, read);
+ } while (len > 0 && tries < 10);
+
+ return retval;
+}
diff -r 7bd75417e14e -r d1c951b3012d ui/sslconnection_curl.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/sslconnection_curl.h Wed Aug 13 19:35:08 2014 +0200
@@ -0,0 +1,50 @@
+#ifndef UI_SSLCONNECTION_CURL_H
+#define UI_SSLCONNECTION_CURL_H
+/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU GPL (v>=2)
+ * and comes with ABSOLUTELY NO WARRANTY!
+ * See LICENSE.txt for details.
+ */
+
+#include "sslconnection.h"
+#include <curl/curl.h>
+
+#include <QTemporaryFile>
+
+class SSLConnectionCurl : public SSLConnection
+{
+public:
+ SSLConnectionCurl(const QString& url,
+ const QByteArray& certificate = QByteArray());
+
+ ~SSLConnectionCurl();
+
+ /** @brief write */
+ int write(const QByteArray& request);
+
+ /**
+ * @brief read at most len bytes and reset the connection
+ *
+ * @param [in] len Amount of bytes to read.
+ *
+ * @returns a byte array containing the data or
+ * a NULL byte array on error*/
+ QByteArray read(size_t len);
+
+ /** @brief: Establish the connection
+ *
+ * @returns 0 on success otherwise an error or -1 is returned
+ */
+ int connect();
+private:
+ CURL *mCurl;
+ QTemporaryFile mCertFile;
+};
+
+/**
+ * @file sslconnection_curl.h
+ * @brief SSLConnection utilizing libcurl for http.
+ */
+#endif // UI_SSLCONNECTION_CURL_H
diff -r 7bd75417e14e -r d1c951b3012d ui/tests/CMakeLists.txt
--- a/ui/tests/CMakeLists.txt Thu Aug 14 08:19:30 2014 +0200
+++ b/ui/tests/CMakeLists.txt Wed Aug 13 19:35:08 2014 +0200
@@ -47,6 +47,8 @@
ui_common
Qt5::Test Qt5::Widgets
trustbridge_common
+ ${CURL_LIBRARIES}
+ z
${POLARSSL_LIBRARIES}
${EXTRA_STATIC_LIBS})
endmacro()
More information about the Trustbridge-commits
mailing list