view ui/sslconnection_curl.cpp @ 1316:ff9cd05e861e

(issue166) Fix certificiate removal The index that should be removed came from the filter proxy model and did not map to the real index. This was broken.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 13 Oct 2014 17:23:35 +0200
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/