aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
aheinecke@404:  * Software engineering by Intevation GmbH
aheinecke@404:  *
aheinecke@404:  * This file is Free Software under the GNU GPL (v>=2)
aheinecke@404:  * and comes with ABSOLUTELY NO WARRANTY!
aheinecke@404:  * See LICENSE.txt for details.
aheinecke@404:  */
aheinecke@21: #include "certificate.h"
aheinecke@82: #include <QDebug>
andre@349: #include <QFile>
andre@186: #include <QStringList>
aheinecke@21: #include <QObject>
aheinecke@21: 
andre@378: #include <polarssl/sha1.h>
andre@378: 
aheinecke@338: #include "certhelp.h"
andre@349: #include "listutil.h"
aheinecke@94: 
aheinecke@338: /* Qt wrapper around certhelp functions. */
aheinecke@338: QString getX509Value(x509_name *namebuf, unsigned char *oid) {
aheinecke@338:     QString retval;
aheinecke@338:     char * buf = get_oid_valstr(namebuf, oid);
aheinecke@338:     if (buf == NULL) {
aheinecke@338:         return retval;
aheinecke@338:     }
aheinecke@338:     retval = QString::fromUtf8(buf, -1);
aheinecke@338:     free(buf);
aheinecke@338:     return retval;
aheinecke@338: }
aheinecke@338: 
aheinecke@338: void Certificate::parseDetails(const QByteArray& cert) {
aheinecke@338:     x509_crt chain;
aheinecke@338: 
aheinecke@338:     x509_crt_init(&chain);
aheinecke@338:     if (x509_crt_parse_der(&chain, (const unsigned char *)cert.data(),
aheinecke@338:                 cert.size()) != 0) {
aheinecke@338:         qDebug() << "Failed to parse cert..";
aheinecke@338:         return;
aheinecke@338:     }
aheinecke@338: 
aheinecke@338:     mValidFrom = QDateTime(QDate(chain.valid_from.year,
aheinecke@338:                                  chain.valid_from.mon,
aheinecke@338:                                  chain.valid_from.day),
aheinecke@338:                            QTime(chain.valid_from.hour,
aheinecke@338:                                  chain.valid_from.min,
aheinecke@338:                                  chain.valid_from.sec));
aheinecke@338: 
aheinecke@338:     mValidTo = QDateTime(QDate(chain.valid_to.year,
aheinecke@338:                                chain.valid_to.mon,
aheinecke@338:                                chain.valid_to.day),
aheinecke@338:                          QTime(chain.valid_to.hour,
aheinecke@338:                                chain.valid_to.min,
aheinecke@338:                                chain.valid_to.sec));
aheinecke@338: 
aheinecke@338:     mSubjectCN = getX509Value(&(chain.subject), CERT_OID_CN);
aheinecke@338:     mSubjectOU = getX509Value(&(chain.subject), CERT_OID_OU);
aheinecke@338:     mSubjectO = getX509Value(&(chain.subject), CERT_OID_O);
aheinecke@338:     mSubjectSN = getX509Value(&(chain.subject), CERT_OID_SN);
andre@378: 
andre@378:     /* Calculate sha1 fingerprint */
andre@378:     unsigned char sha1sum[20];
andre@378:     sha1(chain.raw.p, chain.raw.len, sha1sum);
andre@378:     for (int i=0; i < 20; i++) {
aheinecke@403:         mFingerprint += QString("%1").arg(sha1sum[i], 0, 16).rightJustified(2, '0');
andre@378:         if (i != 19) {
andre@378:             mFingerprint += ":";
andre@378:         }
andre@379:         mFingerprint = mFingerprint.toUpper();
andre@378:     }
andre@378: 
aheinecke@338:     x509_crt_free(&chain);
aheinecke@338: 
aheinecke@338:     mDetails = QObject::tr("Certificate:\n"
aheinecke@338:             "    <bold>%1</bold>\n"
aheinecke@338:             "    %2, %3\n\n"
aheinecke@338:             "Serial number:\n"
aheinecke@338:             "%4\n"
aheinecke@338:             "Valid from: <bold>%5</bold> to <bold>%6</bold>\n\n"
aheinecke@338:             "Issued by: ..")
aheinecke@338:             .arg(mSubjectCN)
aheinecke@338:             .arg(mSubjectO)
aheinecke@338:             .arg(mSubjectOU)
aheinecke@338:             .arg(mSubjectSN)
aheinecke@338:             .arg(QLocale::system().toString(mValidFrom))
aheinecke@338:             .arg(QLocale::system().toString(mValidTo));
andre@378: 
aheinecke@338: }
andre@186: 
andre@349: Certificate::Certificate(const QByteArray& derData) :
bernhard@547:     mValid(false),
bernhard@547:     mEditable(false)
andre@349: {
andre@349:     if (derData.isEmpty()) {
andre@349:         return;
andre@349:     }
andre@349: 
andre@349:     parseDetails(derData);
andre@349: 
andre@349:     mValid = !mSubjectCN.isEmpty();
andre@349: 
andre@356:     /* Default is installation for new certificates */
andre@356:     mBaseLine = QString::fromLatin1("I:") + derData.toBase64();
andre@349: }
andre@349: 
aheinecke@83: Certificate::Certificate(const QString& b64Line) :
bernhard@547:     mValid(false),
bernhard@547:     mEditable(false)
aheinecke@81: {
andre@204:     if (b64Line.isEmpty()) {
andre@204:         return;
andre@204:     }
andre@204: 
aheinecke@83:     /* Cut of the first two chars (e.g. I: and decode) */
andre@349:     QByteArray derData = QByteArray::fromBase64(
aheinecke@83:             b64Line.right(b64Line.size() - 2).toLatin1());
aheinecke@83: 
andre@349:     parseDetails(derData);
andre@186: 
aheinecke@338:     /* If the subject CN is set then at least one x509parse
aheinecke@338:      * in polarssl was successfull. And a root certificate
aheinecke@338:      * always needs to have a subject CN */
aheinecke@338:     mValid = !mSubjectCN.isEmpty();
aheinecke@83: 
aheinecke@83:     mBaseLine = b64Line;
aheinecke@81: }
andre@186: 
aheinecke@338: QString Certificate::shortDescription() const {
aheinecke@338:     if (!isValid()) {
aheinecke@338:         return QObject::tr("Failed to parse certificate");
aheinecke@338:     }
aheinecke@338: 
aheinecke@338:     QString ret = mSubjectCN; /* Necessary by definition */
aheinecke@530:     /*
aheinecke@338:     if (!mSubjectO.isEmpty()) {
aheinecke@338:         ret += " - " + mSubjectO;
aheinecke@338:     }
aheinecke@530:     */
aheinecke@338:     return ret;
andre@186: }
andre@349: 
andre@349: QList<Certificate> Certificate::fromFileName(const QString& file_name) {
andre@349:     /* We read the file using Qt to avoid filename encoding problems
andre@349:      * on Windows */
andre@349: 
andre@349:     /* TODO change qDebug errors into messageboxes */
andre@349:     QFile certificateFile(file_name);
andre@349:     QByteArray fileContent;
andre@349:     QList<Certificate> retval;
andre@349:     x509_crt chain;
andre@349:     int ret = 0;
andre@349:     if (!certificateFile.open(QIODevice::ReadOnly)) {
andre@349:         qDebug() << "Failed to read file.";
andre@349:         return retval;
andre@349:     }
andre@349: 
andre@349:     if (certificateFile.size() > MAX_LINE_LENGTH * MAX_LINES) {
andre@349:         qDebug() << "File too large";
andre@349:         return retval;
andre@349:     }
andre@349: 
andre@349:     fileContent = certificateFile.readAll();
andre@349: 
andre@349:     x509_crt_init(&chain);
andre@349: 
andre@349:     ret = x509_crt_parse(&chain,
andre@349:             reinterpret_cast<const unsigned char*>(fileContent.constData()),
andre@349:             fileContent.size());
andre@349: 
andre@349:     if (ret < 0) {
andre@349:         qDebug() << "Failed to parse certificates.";
andre@349:         return retval;
andre@349:     }
andre@349: 
andre@349:     if (ret > 0) {
andre@349:         qDebug() << "Some certificates could not be parsed.";
andre@349:         /* Maybe return here? */
andre@349:     }
andre@349: 
andre@349:     x509_crt *iter = &chain;
andre@349: 
andre@349:     while (iter) {
andre@349:         QByteArray derData(reinterpret_cast<const char*>(iter->raw.p),
andre@349:                 static_cast<int>(iter->raw.len));
andre@349:         retval << Certificate(derData);
andre@349:         iter = iter->next;
andre@349:     }
andre@349:     x509_crt_free(&chain);
andre@349: 
andre@349:     return retval;
andre@349: }
rrenkert@355: 
rrenkert@352: void Certificate::setInstallCert(bool install)
rrenkert@352: {
rrenkert@352:     if (install && mBaseLine.startsWith("R:")) {
rrenkert@352:         mBaseLine.replace(0, 1, "I");
rrenkert@352:     }
rrenkert@352:     else if (!install && mBaseLine.startsWith("I:")) {
rrenkert@352:         mBaseLine.replace(0, 1, "R");
rrenkert@352:     }
rrenkert@352: }