diff options
author | Petr Mrázek <peterix@gmail.com> | 2015-09-29 00:48:33 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2015-09-29 00:49:54 +0200 |
commit | 22c0d5cf46643d509f8c3ee2b17c54774b8099a6 (patch) | |
tree | 18bdef6a9f53dafaa281ac0e943636989c9b3061 | |
parent | 12b14c3400e11bb712efe760ed7a2cf46d2f75c7 (diff) | |
download | MultiMC-22c0d5cf46643d509f8c3ee2b17c54774b8099a6.tar MultiMC-22c0d5cf46643d509f8c3ee2b17c54774b8099a6.tar.gz MultiMC-22c0d5cf46643d509f8c3ee2b17c54774b8099a6.tar.lz MultiMC-22c0d5cf46643d509f8c3ee2b17c54774b8099a6.tar.xz MultiMC-22c0d5cf46643d509f8c3ee2b17c54774b8099a6.zip |
GH-1202 rebuild SSL certs on start on OSX
-rw-r--r-- | application/CMakeLists.txt | 9 | ||||
-rw-r--r-- | application/CertWorkaround.cpp | 120 | ||||
-rw-r--r-- | application/CertWorkaround.h | 3 | ||||
-rw-r--r-- | application/MultiMC.cpp | 5 |
4 files changed, 137 insertions, 0 deletions
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 <stdexcept> +#include <iostream> + +#include <QByteArray> +#include <QSslSocket> + +#include <Security/Security.h> + +// 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<QSslCertificate> LoadCertificatesFromKeyChain(const std::string &keyChainPath = std::string()) +{ + QList<QSslCertificate> 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<UInt32 *>(&certData.Length), + reinterpret_cast<void **>(&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<const char *>(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<QSslCertificate> existingCerts = QSslSocket::defaultCaCertificates(); + QList<QSslCertificate> 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); |