summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2015-09-29 00:48:33 +0200
committerPetr Mrázek <peterix@gmail.com>2015-09-29 00:49:54 +0200
commit22c0d5cf46643d509f8c3ee2b17c54774b8099a6 (patch)
tree18bdef6a9f53dafaa281ac0e943636989c9b3061
parent12b14c3400e11bb712efe760ed7a2cf46d2f75c7 (diff)
downloadMultiMC-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.txt9
-rw-r--r--application/CertWorkaround.cpp120
-rw-r--r--application/CertWorkaround.h3
-rw-r--r--application/MultiMC.cpp5
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);