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 andre@349: #include andre@186: #include aheinecke@21: #include andre@1297: #include andre@1297: #include aheinecke@21: andre@378: #include andre@378: aheinecke@338: #include "certhelp.h" andre@349: #include "listutil.h" aheinecke@94: andre@1288: #ifdef WIN32 andre@1288: #include andre@1288: #endif andre@1288: 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: " %1\n" aheinecke@338: " %2, %3\n\n" aheinecke@338: "Serial number:\n" aheinecke@338: "%4\n" aheinecke@338: "Valid from: %5 to %6\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), andre@1108: 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::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: QFile certificateFile(file_name); andre@349: QByteArray fileContent; andre@349: QList 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(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: } andre@349: andre@349: x509_crt *iter = &chain; andre@349: andre@349: while (iter) { andre@349: QByteArray derData(reinterpret_cast(iter->raw.p), andre@349: static_cast(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: } andre@1288: andre@1288: #ifdef WIN32 andre@1288: bool Certificate::showNativeUI(void *parent) andre@1288: { andre@1288: /* Cut of the first two chars (e.g. I: and decode) */ andre@1288: bool retval = false; andre@1288: QByteArray pemData = QByteArray( andre@1288: mBaseLine.right(mBaseLine.size() - 2).toLatin1()); andre@1288: PCCERT_CONTEXT pCert = b64_to_cert_context (pemData.data(), pemData.size()); andre@1288: typedef BOOL (CALLBACK* LPFNVIEWDLG)(DWORD,const void *,HWND,LPCWSTR,DWORD,void *); andre@1288: LPFNVIEWDLG funcPtr; andre@1288: andre@1288: /* CryptUIDlgViewContext is not part of mingw 3.1.0 andre@1288: * so we workaround this by geting the process address dynamically. */ andre@1288: HMODULE hmod = LoadLibraryW(L"cryptui"); andre@1288: andre@1288: if (!hmod) { andre@1288: qDebug() << "Failed to open Cryptui.dll"; andre@1288: goto done; andre@1288: } andre@1288: andre@1288: funcPtr = (LPFNVIEWDLG) GetProcAddress (hmod, "CryptUIDlgViewContext"); andre@1288: if (!funcPtr) { andre@1288: qDebug() << "Failed to find Address of CryptUIDlgViewContext"; andre@1288: goto done; andre@1288: } andre@1288: andre@1288: if (pCert == NULL) { andre@1288: qDebug() << "Failed to parse certificate."; andre@1288: goto done; andre@1288: } andre@1288: andre@1310: funcPtr(CERT_STORE_CERTIFICATE_CONTEXT, andre@1288: pCert, andre@1288: (HWND) parent, andre@1288: NULL, // Default Title andre@1288: 0, andre@1310: NULL); andre@1288: andre@1288: retval = true; andre@1288: done: andre@1288: andre@1288: if (pCert) { andre@1288: CertFreeCertificateContext(pCert); andre@1288: } andre@1288: if (hmod) { andre@1288: FreeLibrary(hmod); andre@1288: } andre@1288: return retval; andre@1288: } andre@1288: #else andre@1288: bool Certificate::showNativeUI(void *parent) andre@1288: { andre@1297: QTemporaryFile *tmpCert = new QTemporaryFile; andre@1297: tmpCert->open(); andre@1297: tmpCert->write("-----BEGIN CERTIFICATE-----\n"); andre@1297: tmpCert->write(mBaseLine.right(mBaseLine.size() - 2).toLatin1()); andre@1297: tmpCert->write("-----END CERTIFICATE-----\n"); andre@1297: tmpCert->close(); andre@1297: QStringList args; andre@1297: args << tmpCert->fileName(); andre@1297: QProcess *viewer = new QProcess(); andre@1297: viewer->setProgram("gcr-viewer"); andre@1297: viewer->setArguments(args); andre@1297: andre@1297: QObject::connect (viewer, SIGNAL(finished(int, QProcess::ExitStatus)), tmpCert, SLOT(deleteLater())); andre@1297: QObject::connect (viewer, SIGNAL(finished(int, QProcess::ExitStatus)), viewer, SLOT(deleteLater())); andre@1297: viewer->start(); andre@1297: return !(!viewer->waitForStarted() || viewer->state() == QProcess::NotRunning); andre@1288: } andre@1288: #endif