view ui/sslconnection_curl.cpp @ 1395:a2574a029322

Fix Base 64 signature size calculation. If the signature byte size is not equally dividable by three the base 64 encoding needs three additional bytes. The value is now fixed to avoid such errors in the future.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 26 Jan 2015 13:17:32 +0100
parents 0eb723242536
children
line wrap: on
line source
/* 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"
#include "logging.h"

#include <polarssl/ssl.h>
#include <QSaveFile>

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_SSL_VERIFYPEER, 1L) != CURLE_OK) {
        /* Should be default anyway */
        qDebug() << "Setting verifypeer failed";
        return;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, mErrBuf) != CURLE_OK) {
        qDebug() << "Setting errorbuf failed";
        return;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2) != CURLE_OK) {
        qDebug() << "Setting ssl version 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().toLocal8Bit().constData()) != CURLE_OK) {
        qDebug() << "Failed to set ca certificate";
        return;
    }

    /* If the build fails here maybe you probably forgot to apply the
     * trustbridge patches to curl */
    if (curl_easy_setopt(mCurl, CURLOPT_PEERCERT,
                mCertFile.fileName().toLocal8Bit().constData()) != CURLE_OK) {
        qDebug() << "Failed set peer certificate.";
        return;
    }
    mInitialized = true;
    if (g_debug) {
        curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1L);
    }
}

SSLConnectionCurl::~SSLConnectionCurl() {
    if (mCurl) {
        curl_easy_cleanup (mCurl);
    }
    if (mInitialized) {
        mCertFile.close();
    }
    curl_global_cleanup();
}

int SSLConnectionCurl::connect() {
    CURLcode retval;

    if (curl_easy_setopt(mCurl, CURLOPT_URL, mUrl.toEncoded().constData()) != CURLE_OK) {
        qDebug() << "Failed to set URL";
        return -1;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 1L) != CURLE_OK) {
        qDebug() << "Failed to set connect only option";
        return -1;
    }
    retval = curl_easy_perform(mCurl);
    if (retval != CURLE_OK) {
        qDebug() << "Failed to connect: " << mErrBuf << " retval: " << retval;
        if (retval == CURLE_PEER_FAILED_VERIFICATION) {
            mErrorState = InvalidCertificate;
            return -1;
        }
        if (retval == CURLE_SSL_CONNECT_ERROR) {
            mErrorState = SSLHandshakeFailed;
            return -1;
        }

        mErrorState = NoConnection;
        return -1;
    }
    mConnected = true;
    return 0;
}

/* Globally do this as we can't pass "this" (the ptr) to the c function */
size_t ssl_curl_max_write, ssl_curl_written;

size_t write_data(void *ptr, size_t size, size_t nmemb,
        QSaveFile *fp)
{
    if (ssl_curl_max_write < ssl_curl_written) {
        qDebug() << "Aborting write. Too much data.";
        return 0;
    }
    size_t written = fp->write((const char *)ptr, size * nmemb);
    if (written != size * nmemb) {
        qDebug() << "Failed to write data. Written: " << written 
                 << " requested: " << size * nmemb;
        return 0;
    }
    ssl_curl_written += written;
    return written;
}

size_t debug_write(void *ptr, size_t size, size_t nmemb,
        void *unused)
{
    Q_UNUSED(unused);

    qDebug() << QString::fromUtf8((const char *)ptr, size * nmemb);
    return size *nmemb;
}

bool SSLConnectionCurl::downloadFile(const QString &resource,
                                     const QString &fileName,
                                     size_t maxSize)
{
    ssl_curl_written = 0;
    ssl_curl_max_write = maxSize;
    QSaveFile outputFile(fileName);
    // Open / Create the file to write to.
    if (!outputFile.open(QIODevice::WriteOnly)) {
        qDebug() << "Failed to open file";
        return false;
    }
    QUrl urlCopy = mUrl;
    urlCopy.setPath(resource);

    if (curl_easy_setopt(mCurl, CURLOPT_URL, urlCopy.toEncoded().constData()) != CURLE_OK) {
        qDebug() << "Failed to set URL";
        return false;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 0L) != CURLE_OK) {
        qDebug() << "Failed to set connect";
        return false;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_HEADER, 0L) != CURLE_OK) {
        qDebug() << "Failed to set header";
        return false;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_NOBODY, 0L) != CURLE_OK) {
        qDebug() << "Failed to set no body";
        return false;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, write_data) != CURLE_OK) {
        qDebug() << "Failed to set write function";
        return false;
    }

    if (curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &outputFile) != CURLE_OK) {
        qDebug() << "Failed to set write function";
        return false;
    }

    if (curl_easy_perform (mCurl) != CURLE_OK) {
        qDebug() << "Failed to perform download.";
        return false;
    }

    if (!outputFile.commit()) {
        qDebug() << "Failed to commit data to filesystem.";
        return false;
    }

    return true;
}

QDateTime SSLConnectionCurl::getLastModifiedHeader(const QString &resource) {
    QUrl urlCopy = mUrl;
    urlCopy.setPath(resource);

    if (curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, debug_write) != CURLE_OK) {
        qDebug() << "Failed to set write function";
        return QDateTime();
    }

    if (curl_easy_setopt(mCurl, CURLOPT_URL, urlCopy.toEncoded().constData()) != CURLE_OK) {
        qDebug() << "Failed to set URL";
        return QDateTime();
    }

    if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 0L) != CURLE_OK) {
        qDebug() << "Failed to set connect";
        return QDateTime();
    }

    if (curl_easy_setopt(mCurl, CURLOPT_HEADER, 1L) != CURLE_OK) {
        qDebug() << "Failed to set header";
        return QDateTime();
    }

    if (curl_easy_setopt(mCurl, CURLOPT_NOBODY, 1L) != CURLE_OK) {
        qDebug() << "Failed to set no body";
        return QDateTime();
    }

    if (curl_easy_setopt(mCurl, CURLOPT_FILETIME, 1L) != CURLE_OK) {
        qDebug() << "Failed to set filetime";
        return QDateTime();
    }

    if (curl_easy_perform (mCurl) != CURLE_OK) {
        qDebug() << "Failed to perform last modified check.";
        return QDateTime();
    }
    long filetime = 0;

    if (curl_easy_getinfo (mCurl, CURLINFO_FILETIME, &filetime) != CURLE_OK) {
        qDebug() << "Failed to get filetime";
        return QDateTime();
    }

    if (filetime == -1) {
        qDebug() << "Invalid Time";
        return QDateTime();
    }
    return QDateTime::fromTime_t(filetime);
}

void SSLConnectionCurl::setProxy(const QUrl& proxyUrl) {
    if (curl_easy_setopt(mCurl, CURLOPT_PROXY, proxyUrl.toEncoded().constData()) != CURLE_OK) {
        qDebug() << "Failed to set proxy";
        return;
    }
}

void SSLConnectionCurl::setCiphersuites(int ciphers[]) {
    QStringList cipher_list;
    for (int i = 0; ciphers[i] != 0; i++) {
        cipher_list << ssl_get_ciphersuite_name(ciphers[i]);
    }

    if (curl_easy_setopt(mCurl, CURLOPT_SSL_CIPHER_LIST,
                cipher_list.join(":").toLatin1().constData()) != CURLE_OK) {
        qDebug() << "Failed to set cipher list";
        return;
    }
}

http://wald.intevation.org/projects/trustbridge/