From 22c0d5cf46643d509f8c3ee2b17c54774b8099a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 29 Sep 2015 00:48:33 +0200 Subject: GH-1202 rebuild SSL certs on start on OSX --- application/CMakeLists.txt | 9 ++++ application/CertWorkaround.cpp | 120 +++++++++++++++++++++++++++++++++++++++++ application/CertWorkaround.h | 3 ++ application/MultiMC.cpp | 5 ++ 4 files changed, 137 insertions(+) create mode 100644 application/CertWorkaround.cpp create mode 100644 application/CertWorkaround.h (limited to 'application') diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index 90eb3891..13a085d0 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -322,6 +322,15 @@ set(MULTIMC_QRCS resources/certs/certs.qrc ) +set(MultiMC_OSX_source + CertWorkaround.cpp + CertWorkaround.h +) + +if(APPLE) + list(APPEND MULTIMC_SOURCES ${MultiMC_OSX_source}) +endif() + ######## Windows resource files ######## if(WIN32) set(MULTIMC_RCS resources/multimc.rc) diff --git a/application/CertWorkaround.cpp b/application/CertWorkaround.cpp new file mode 100644 index 00000000..900343de --- /dev/null +++ b/application/CertWorkaround.cpp @@ -0,0 +1,120 @@ +#include +#include + +#include +#include + +#include + +// CFRelease will crash if passed NULL +#define SafeCFRelease(ref) \ + if (ref) \ + CFRelease(ref); + +/*! + * \brief LoadCertificatesFromKeyChain Load all certificates from the KeyChain path provided + * and return them as + * QSslCertificates. + * \param keyChainPath The KeyChain path. Pass an empty string to use the + * user's keychain. + * \return A list of new QSslCertificates generated from the + * KeyChain DER data. + */ +static QList LoadCertificatesFromKeyChain(const std::string &keyChainPath = std::string()) +{ + QList qtCerts; + + SecKeychainRef certsKeyChain = NULL; + SecKeychainSearchRef searchItem = NULL; + SecKeychainItemRef itemRef = NULL; + CSSM_DATA certData = {0, 0}; + + try + { + OSStatus status = errSecSuccess; + + // if a keychain path was provided, obtain a pointer + if (!keyChainPath.empty()) + { + status = SecKeychainOpen(keyChainPath.c_str(), &certsKeyChain); + if (status != errSecSuccess) + { + throw status; + } + } + + // build a search query reference for certificates + status = SecKeychainSearchCreateFromAttributes(certsKeyChain, kSecCertificateItemClass, + NULL, &searchItem); + if (status != errSecSuccess) + { + throw status; + } + + // loop through the certificates + while (SecKeychainSearchCopyNext(searchItem, &itemRef) != errSecItemNotFound) + { + // copy the KeyChain item data into a CSSM_DATA struct - this will be the certs Der + // data + status = SecKeychainItemCopyContent(itemRef, NULL, NULL, + reinterpret_cast(&certData.Length), + reinterpret_cast(&certData.Data)); + + if (status != errSecSuccess) + { + throw status; + } + + // create a Qt byte array from the data - the data is NOT copied + const QByteArray byteArray = QByteArray::fromRawData( + reinterpret_cast(certData.Data), certData.Length); + + // create a Qt certificate from the data and add it to the list + QSslCertificate qtCert(byteArray, QSsl::Der); + std::cout << "COMMON NAME: " + << qtCert.issuerInfo(QSslCertificate::CommonName).toStdString().c_str() + << " ORG NAME: " + << qtCert.issuerInfo(QSslCertificate::Organization).toStdString().c_str() + << std::endl; + + qtCerts << qtCert; + } + } + catch (OSStatus status) + { + CFStringRef errorMessage = SecCopyErrorMessageString(status, NULL); + std::cerr << CFStringGetCStringPtr(errorMessage, kCFStringEncodingMacRoman) + << std::endl; + SafeCFRelease(errorMessage); + } + + SecKeychainItemFreeContent(NULL, certData.Data); + SafeCFRelease(itemRef); + SafeCFRelease(searchItem); + SafeCFRelease(certsKeyChain); + + return qtCerts; +} + +void RebuildQtCertificates() +{ + const QList existingCerts = QSslSocket::defaultCaCertificates(); + QList certs = LoadCertificatesFromKeyChain(); + certs += LoadCertificatesFromKeyChain( + "/System/Library/Keychains/SystemRootCertificates.keychain"); + + Q_FOREACH (const QSslCertificate qtCert, certs) + { + if (!existingCerts.contains(qtCert)) + { + std::cout << "cert not known to Qt - adding" << std::endl; + std::cout << "COMMON NAME: " + << qtCert.issuerInfo(QSslCertificate::CommonName).toStdString().c_str() + << " ORG NAME: " + << qtCert.issuerInfo(QSslCertificate::Organization).toStdString().c_str() + << std::endl; + + QSslSocket::addDefaultCaCertificate(qtCert); + } + } +} \ No newline at end of file diff --git a/application/CertWorkaround.h b/application/CertWorkaround.h new file mode 100644 index 00000000..64554698 --- /dev/null +++ b/application/CertWorkaround.h @@ -0,0 +1,3 @@ +#pragma once + +void RebuildQtCertificates(); \ No newline at end of file diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index a893c875..86356cd1 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -283,10 +283,15 @@ MultiMC::~MultiMC() } } +#ifdef Q_OS_MAC +#include "CertWorkaround.h" +#endif + void MultiMC::initSSL() { #ifdef Q_OS_MAC Q_INIT_RESOURCE(certs); + RebuildQtCertificates(); QFile equifaxFile(":/certs/Equifax_Secure_Certificate_Authority.pem"); equifaxFile.open(QIODevice::ReadOnly); QSslCertificate equifaxCert(equifaxFile.readAll(), QSsl::Pem); -- cgit v1.2.3