view ui/sslconnection_curl.cpp @ 975:b3695a3399de

(issue86) Install into default directories on Linux If the mozilla process is now started as root it will try to write into the default directories for NSS Shared and mozilla / thunderbird profiles. Cinst will now start the mozilla process once as root.
author Andre Heinecke <andre.heinecke@intevation.de>
date Fri, 29 Aug 2014 12:59:44 +0200
parents 879a634d0a40
children 6a3d284b9c16
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 <QSaveFile>

#define CONNECTION_DEBUG

SSLConnectionCurl::SSLConnectionCurl(const QString& url,
                             const QByteArray& certificate):
    SSLConnection (url, certificate),
    mCurl (NULL)
{
    if (certificate.isEmpty()) {
        /* Security: curl does not support leaf certificate pinning. So
         * while the bare connection pins the certificate directly the
         * curl implementation pins the issuer of the peer certificate
         *
         * CURLINFO_TLS_SESSION is also not implmented for polarssl
         * so there is no way to obtain / verify peer certificate manually
         * at this point.
         **/
#ifdef RELEASE_BUILD
#error "Curl release build is not supported at this moment."
#else
        QFile certResource(":certs/geotrust");
#endif
        certResource.open(QFile::ReadOnly);
        mPinnedCert = certResource.readAll();
        certResource.close();
    }

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

    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 write temporary 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;
        }

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

/* Globally do this as we can't pass this 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)
{
    QSaveFile outputFile(fileName);
    ssl_curl_written = 0;
    ssl_curl_max_write = maxSize;
    // 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;
    }
    qDebug() << "Set proxy to: " << proxyUrl;
}

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