view ui/certificate.cpp @ 1402:1adf72328b75 tip

Added tag 1.0 for changeset ee807f64e21e
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 27 Jan 2015 15:18:32 +0100
parents 60e481aa75ca
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 "certificate.h"
#include <QDebug>
#include <QFile>
#include <QStringList>
#include <QObject>
#include <QProcess>
#include <QTemporaryFile>

#include <polarssl/sha1.h>

#include "certhelp.h"
#include "listutil.h"

#ifdef WIN32
#include <cryptuiapi.h>
#endif

/* Qt wrapper around certhelp functions. */
QString getX509Value(x509_name *namebuf, unsigned char *oid) {
    QString retval;
    char * buf = get_oid_valstr(namebuf, oid);
    if (buf == NULL) {
        return retval;
    }
    retval = QString::fromUtf8(buf, -1);
    free(buf);
    return retval;
}

void Certificate::parseDetails(const QByteArray& cert) {
    x509_crt chain;

    x509_crt_init(&chain);
    if (x509_crt_parse_der(&chain, (const unsigned char *)cert.data(),
                cert.size()) != 0) {
        qDebug() << "Failed to parse cert..";
        return;
    }

    mValidFrom = QDateTime(QDate(chain.valid_from.year,
                                 chain.valid_from.mon,
                                 chain.valid_from.day),
                           QTime(chain.valid_from.hour,
                                 chain.valid_from.min,
                                 chain.valid_from.sec));

    mValidTo = QDateTime(QDate(chain.valid_to.year,
                               chain.valid_to.mon,
                               chain.valid_to.day),
                         QTime(chain.valid_to.hour,
                               chain.valid_to.min,
                               chain.valid_to.sec));

    mSubjectCN = getX509Value(&(chain.subject), CERT_OID_CN);
    mSubjectOU = getX509Value(&(chain.subject), CERT_OID_OU);
    mSubjectO = getX509Value(&(chain.subject), CERT_OID_O);
    mSubjectSN = getX509Value(&(chain.subject), CERT_OID_SN);

    /* Calculate sha1 fingerprint */
    unsigned char sha1sum[20];
    sha1(chain.raw.p, chain.raw.len, sha1sum);
    for (int i=0; i < 20; i++) {
        mFingerprint += QString("%1").arg(sha1sum[i], 0, 16).rightJustified(2, '0');
        if (i != 19) {
            mFingerprint += ":";
        }
        mFingerprint = mFingerprint.toUpper();
    }

    x509_crt_free(&chain);

    mDetails = QObject::tr("Certificate:\n"
            "    <bold>%1</bold>\n"
            "    %2, %3\n\n"
            "Serial number:\n"
            "%4\n"
            "Valid from: <bold>%5</bold> to <bold>%6</bold>\n\n"
            "Issued by: ..")
            .arg(mSubjectCN)
            .arg(mSubjectO)
            .arg(mSubjectOU)
            .arg(mSubjectSN)
            .arg(QLocale::system().toString(mValidFrom))
            .arg(QLocale::system().toString(mValidTo));

}

Certificate::Certificate(const QByteArray& derData) :
    mValid(false),
    mEditable(false)
{
    if (derData.isEmpty()) {
        return;
    }

    parseDetails(derData);

    mValid = !mSubjectCN.isEmpty();

    /* Default is installation for new certificates */
    mBaseLine = QString::fromLatin1("I:") + derData.toBase64();
}

Certificate::Certificate(const QString& b64Line) :
    mValid(false),
    mEditable(false)
{
    if (b64Line.isEmpty()) {
        return;
    }

    /* Cut of the first two chars (e.g. I: and decode) */
    QByteArray derData = QByteArray::fromBase64(
            b64Line.right(b64Line.size() - 2).toLatin1());

    parseDetails(derData);

    /* If the subject CN is set then at least one x509parse
     * in polarssl was successfull. And a root certificate
     * always needs to have a subject CN */
    mValid = !mSubjectCN.isEmpty();

    mBaseLine = b64Line;
}

QString Certificate::shortDescription() const {
    if (!isValid()) {
        return QObject::tr("Failed to parse certificate");
    }

    QString ret = mSubjectCN; /* Necessary by definition */
    /*
    if (!mSubjectO.isEmpty()) {
        ret += " - " + mSubjectO;
    }
    */
    return ret;
}

QList<Certificate> Certificate::fromFileName(const QString& file_name) {
    /* We read the file using Qt to avoid filename encoding problems
     * on Windows */

    QFile certificateFile(file_name);
    QByteArray fileContent;
    QList<Certificate> retval;
    x509_crt chain;
    int ret = 0;
    if (!certificateFile.open(QIODevice::ReadOnly)) {
        qDebug() << "Failed to read file.";
        return retval;
    }

    if (certificateFile.size() > MAX_LINE_LENGTH * MAX_LINES) {
        qDebug() << "File too large";
        return retval;
    }

    fileContent = certificateFile.readAll();

    x509_crt_init(&chain);

    ret = x509_crt_parse(&chain,
            reinterpret_cast<const unsigned char*>(fileContent.constData()),
            fileContent.size());

    if (ret < 0) {
        qDebug() << "Failed to parse certificates.";
        return retval;
    }

    if (ret > 0) {
        qDebug() << "Some certificates could not be parsed.";
    }

    x509_crt *iter = &chain;

    while (iter) {
        QByteArray derData(reinterpret_cast<const char*>(iter->raw.p),
                static_cast<int>(iter->raw.len));
        retval << Certificate(derData);
        iter = iter->next;
    }
    x509_crt_free(&chain);

    return retval;
}

void Certificate::setInstallCert(bool install)
{
    if (install && mBaseLine.startsWith("R:")) {
        mBaseLine.replace(0, 1, "I");
    }
    else if (!install && mBaseLine.startsWith("I:")) {
        mBaseLine.replace(0, 1, "R");
    }
}

#ifdef WIN32
bool Certificate::showNativeUI(void *parent)
{
    /* Cut of the first two chars (e.g. I: and decode) */
    bool retval = false;
    QByteArray pemData = QByteArray(
            mBaseLine.right(mBaseLine.size() - 2).toLatin1());
    PCCERT_CONTEXT pCert = b64_to_cert_context (pemData.data(), pemData.size());
    typedef BOOL (CALLBACK* LPFNVIEWDLG)(DWORD,const void *,HWND,LPCWSTR,DWORD,void *);
    LPFNVIEWDLG funcPtr;

    /* CryptUIDlgViewContext is not part of mingw 3.1.0
     * so we workaround this by geting the process address dynamically. */
    HMODULE hmod = LoadLibraryW(L"cryptui");

    if (!hmod) {
        qDebug() << "Failed to open Cryptui.dll";
        goto done;
    }

    funcPtr = (LPFNVIEWDLG) GetProcAddress (hmod, "CryptUIDlgViewContext");
    if (!funcPtr) {
        qDebug() << "Failed to find Address of CryptUIDlgViewContext";
        goto done;
    }

    if (pCert == NULL) {
        qDebug() << "Failed to parse certificate.";
        goto done;
    }

    funcPtr(CERT_STORE_CERTIFICATE_CONTEXT,
                 pCert,
                 (HWND) parent,
                 NULL, // Default Title
                 0,
                 NULL);

    retval = true;
done:

    if (pCert) {
        CertFreeCertificateContext(pCert);
    }
    if (hmod) {
        FreeLibrary(hmod);
    }
    return retval;
}
#else
bool Certificate::showNativeUI(void *parent)
{
    QTemporaryFile *tmpCert = new QTemporaryFile;
    tmpCert->open();
    tmpCert->write("-----BEGIN CERTIFICATE-----\n");
    tmpCert->write(mBaseLine.right(mBaseLine.size() - 2).toLatin1());
    tmpCert->write("-----END CERTIFICATE-----\n");
    tmpCert->close();
    QStringList args;
    args << tmpCert->fileName();
    QProcess *viewer = new QProcess();
    viewer->setProgram("gcr-viewer");
    viewer->setArguments(args);

    QObject::connect (viewer, SIGNAL(finished(int, QProcess::ExitStatus)), tmpCert, SLOT(deleteLater()));
    QObject::connect (viewer, SIGNAL(finished(int, QProcess::ExitStatus)), viewer, SLOT(deleteLater()));
    viewer->start();
    return !(!viewer->waitForStarted() || viewer->state() == QProcess::NotRunning);
}
#endif

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