Mercurial > trustbridge
diff ui/downloader.cpp @ 27:62cd56cea09b
Start on polarssl Downloader.
This breaks the windows build for now
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Wed, 12 Mar 2014 16:15:52 +0100 |
parents | f4f957c58e0a |
children | d8e93fa1fc93 |
line wrap: on
line diff
--- a/ui/downloader.cpp Tue Mar 11 19:00:35 2014 +0100 +++ b/ui/downloader.cpp Wed Mar 12 16:15:52 2014 +0100 @@ -4,13 +4,30 @@ #define DOWNLOAD_SERVER "https://www.intevation.de" #endif -#ifdef Q_OS_WIN -#endif - #include <QFile> #include <QDir> #include <QDebug> #include <QStandardPaths> +#include <QUuid> +#include <QApplication> + +#include <polarssl/net.h> +#include <polarssl/ssl.h> +#include <polarssl/entropy.h> +#include <polarssl/ctr_drbg.h> +#include <polarssl/error.h> +#include <polarssl/certs.h> + +#define MAX_SW_SIZE 10485760 +#define MAX_LIST_SIZE 1048576 + +QString getErrorMsg(int ret) +{ + char errbuf[255]; + polarssl_strerror(ret, errbuf, 255); + errbuf[254] = '\0'; /* Just to be sure */ + return QString::fromLatin1(errbuf); +} Downloader::Downloader(QObject* parent, const QString& url, const QByteArray& certificate, @@ -18,15 +35,89 @@ const QDateTime& newestList): QThread(parent), mUrl(url), + mPinnedCert(certificate), mLastModSW(newestSW), - mLastModList(newestList) + mLastModList(newestList), + mErrorState(NoError), + mInitialized(false), + mServerFD(-1) { + int ret = -1; + + memset(&mSSL, 0, sizeof(ssl_context)); + if (certificate.isEmpty()) { QFile certResource(":certs/kolab.org"); certResource.open(QFile::ReadOnly); - mCert = certResource.readAll(); + mPinnedCert = certResource.readAll(); certResource.close(); } + + ret = init(); + if (ret == 0) { + mInitialized = true; + } else { + qDebug() << "Initialization error: " + getErrorMsg(ret); + } +} + +int Downloader::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); + + return 0; +} + +Downloader::~Downloader() { + x509_crt_free(&mX509PinnedCert); + entropy_free(&mEntropy); + if (mInitialized) { + ssl_free(&mSSL); + } } QString Downloader::getDataDirectory() @@ -49,3 +140,120 @@ } return cDir.absolutePath(); } + +int Downloader::establishSSLConnection() { + int ret = -1; + const x509_crt *peerCert; + + mErrorState = ErrUnknown; + + if (mServerFD == -1) { + return -1; + } + + 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: " + << getErrorMsg(ret); + return ret; + } + } + + 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"; + +#ifdef RELEASE_BUILD + 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; + } + } + mErrorState = NoError; + return 0; +} + + +void Downloader::run() { + int ret; + + if (!mInitialized) { + emit error(tr("Failed to initialize SSL Module."), ErrUnknown); + return; + } + + ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(), + mUrl.port(443)); + if (ret != 0) { + mErrorState = NoConnection; + emit error(tr("Failed to connect to %1.").arg(mUrl.host()), + mErrorState); + return; + } + + emit progress(tr("Connected"), 1, -1); + + ret = establishSSLConnection(); + if (ret != 0) { + qDebug() << "SSL conncetion failed: " << getErrorMsg(ret); + emit error(tr("Failed to connect to %1.").arg(mUrl.host()), + mErrorState); + return; + } + + qDebug() << "Connected to: " << mUrl.host(); + // TODO + + emit progress(tr("Closing"), 1, -1); + ssl_close_notify (&mSSL); + + if (mServerFD != -1) { + net_close(mServerFD); + mServerFD = -1; + } +}