view ui/sslconnection_curl.cpp @ 1012:a80abef948fa

(issue86) Initialize NSS db with empty pin in case it needs it.
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 02 Sep 2014 15:20:24 +0200
parents 7dff5c0c569c
children fe2c6666b462
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 <polarssl/ssl.h>
#include <QSaveFile>

#undef 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;
    }

#ifdef RELEASE_BUILD
    if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, 1L) != CURLE_OK) {
#else
    /* For testing we do not have to trust the issuer. This should not
     * be dangerous as we pin the peer certificate directly. */
    if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, 0L) != CURLE_OK) {
#endif
        /* Should be default anyway */
        qDebug() << "Setting verifypeer failed";
        return;
    }

#ifdef RELEASE_BUILD
    if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYHOST, 1L) != CURLE_OK) {
#else
    /* For testing we do not have to trust host. This should not
     * be dangerous as we pin the peer certificate directly. */
    if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYHOST, 0L) != CURLE_OK) {
#endif
        /* Should be default anyway */
        qDebug() << "Setting verifypeer failed";
        return;
    }

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

#ifdef RELEASE_BUILD
    if (curl_easy_setopt(mCurl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2) != CURLE_OK) {
        qDebug() << "Setting ssl version failed.";
        return;
    }
#endif

    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 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().toUtf8().constData()) != CURLE_OK) {
        qDebug() << "Failed set peer 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() {
    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)
{
    qDebug() << "Writing size: " << size << " * " << nmemb;
    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;
}

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_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/