Mercurial > trustbridge
view ui/downloader.cpp @ 28:e783fd99a9eb
Add public key parsing
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Thu, 13 Mar 2014 12:01:33 +0000 |
parents | 62cd56cea09b |
children | d8e93fa1fc93 |
line wrap: on
line source
#include "downloader.h" #ifndef DOWNLOAD_SERVER #define DOWNLOAD_SERVER "https://www.intevation.de" #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, const QDateTime& newestSW, const QDateTime& newestList): QThread(parent), mUrl(url), mPinnedCert(certificate), mLastModSW(newestSW), 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); 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() { QString candidate = QStandardPaths::writableLocation(QStandardPaths::DataLocation); if (candidate.isEmpty()) { qDebug() << "Could not find writeable locaction for me"; return QString(); } QDir cDir(candidate); if (!cDir.exists()) { if (!cDir.mkpath(candidate)) { qDebug() << "Could not create path to: " << candidate; return QString(); } } 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; } }