view ui/sslconnection_curl.cpp @ 1070:f110a3f6e387

(issue114) Fine tune ACL propagation using mkdir_p the ACL of the parent directories would propagate to all subdirectories and objects in the directory. Now we only use ACL propagation in the last directory to make sure that files we might create in that directory inherit the correct (resitricted) ACL
author Andre Heinecke <andre.heinecke@intevation.de>
date Wed, 10 Sep 2014 16:41:36 +0200
parents fe2c6666b462
children 508c96e72f62
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;
    }

#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;
    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)
{
    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;
}

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/