nextcloud-desktop/admin/qt/patches/0016-QSslSocket-evaluate-CAs-in-all-keychain-categories.patch
2015-11-25 12:42:58 +01:00

279 lines
10 KiB
Diff

From 6b9366e7748857f14d5b0f92ced70c08ab5235b7 Mon Sep 17 00:00:00 2001
From: Daniel Molkentin <danimo@owncloud.com>
Date: Wed, 25 Nov 2015 12:37:27 +0100
Subject: [PATCH 2/2] QSslSocket: evaluate CAs in all keychain categories
This will make sure that certs in the domainUser (login),
and domainAdmin (per machine) keychain are being picked up
in systemCaCertificates() in addition to the (usually immutable)
DomainSystem keychain.
Also consider the trust settings on OS X: If a certificate
is either fully trusted or trusted for the purpose of SSL,
it will be accepted.
[ChangeLog][Platform Specific Changes] OS X now accepts trusted
certificates from the login and system keychains.
(Backport of fe3a84138e266c425f11353f7d8dc28a588af89e to Qt 5.4)
Task-number: QTBUG-32898
Change-Id: Ia23083d5af74388eeee31ba07239735cbbe64368
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
---
src/network/ssl/qsslsocket.cpp | 4 +
src/network/ssl/qsslsocket_mac_shared.cpp | 148 ++++++++++++++++++++++++++++++
src/network/ssl/qsslsocket_openssl.cpp | 30 +-----
src/network/ssl/ssl.pri | 4 +-
4 files changed, 158 insertions(+), 28 deletions(-)
create mode 100644 src/network/ssl/qsslsocket_mac_shared.cpp
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 8887f47..6347c20 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -1446,6 +1446,10 @@ QList<QSslCertificate> QSslSocket::defaultCaCertificates()
returned by defaultCaCertificates(). You can replace that database
with your own with setDefaultCaCertificates().
+ \note: On OS X, only certificates that are either trusted for all
+ purposes or trusted for the purpose of SSL in the keychain will be
+ returned.
+
\sa caCertificates(), defaultCaCertificates(), setDefaultCaCertificates()
*/
QList<QSslCertificate> QSslSocket::systemCaCertificates()
diff --git a/src/network/ssl/qsslsocket_mac_shared.cpp b/src/network/ssl/qsslsocket_mac_shared.cpp
new file mode 100644
index 0000000..60fea4c
--- /dev/null
+++ b/src/network/ssl/qsslsocket_mac_shared.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 ownCloud Inc
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QSSLSOCKET_DEBUG
+//#define QT_DECRYPT_SSL_TRAFFIC
+
+#include "qsslsocket.h"
+
+#ifndef QT_NO_OPENSSL
+# include "qsslsocket_openssl_p.h"
+# include "qsslsocket_openssl_symbols_p.h"
+#endif
+
+#include "qsslcertificate_p.h"
+
+#ifdef Q_OS_DARWIN
+# include <private/qcore_mac_p.h>
+#endif
+
+#include <QtCore/qdebug.h>
+
+#ifdef Q_OS_OSX
+# include <Security/Security.h>
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_OSX
+namespace {
+
+bool hasTrustedSslServerPolicy(SecPolicyRef policy, CFDictionaryRef props) {
+ QCFType<CFDictionaryRef> policyProps = SecPolicyCopyProperties(policy);
+ // only accept certificates with policies for SSL server validation for now
+ if (CFEqual(CFDictionaryGetValue(policyProps, kSecPolicyOid), kSecPolicyAppleSSL)) {
+ CFBooleanRef policyClient;
+ if (CFDictionaryGetValueIfPresent(policyProps, kSecPolicyClient, reinterpret_cast<const void**>(&policyClient)) &&
+ CFEqual(policyClient, kCFBooleanTrue)) {
+ return false; // no client certs
+ }
+ if (!CFDictionaryContainsKey(props, kSecTrustSettingsResult)) {
+ // as per the docs, no trust settings result implies full trust
+ return true;
+ }
+ CFNumberRef number = static_cast<CFNumberRef>(CFDictionaryGetValue(props, kSecTrustSettingsResult));
+ SecTrustSettingsResult settingsResult;
+ CFNumberGetValue(number, kCFNumberSInt32Type, &settingsResult);
+ switch (settingsResult) {
+ case kSecTrustSettingsResultTrustRoot:
+ case kSecTrustSettingsResultTrustAsRoot:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
+{
+ QCFType<CFArrayRef> cfTrustSettings;
+ OSStatus status = SecTrustSettingsCopyTrustSettings(cfCert, domain, &cfTrustSettings);
+ if (status == noErr) {
+ CFIndex size = CFArrayGetCount(cfTrustSettings);
+ // if empty, trust for everything (as per the Security Framework documentation)
+ if (size == 0) {
+ return true;
+ } else {
+ for (CFIndex i = 0; i < size; ++i) {
+ CFDictionaryRef props = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(cfTrustSettings, i));
+ if (CFDictionaryContainsKey(props, kSecTrustSettingsPolicy)) {
+ if (hasTrustedSslServerPolicy((SecPolicyRef)CFDictionaryGetValue(props, kSecTrustSettingsPolicy), props))
+ return true;
+ }
+ }
+ }
+ } else {
+ qWarning("Error receiving trust for a CA certificate");
+ }
+ return false;
+}
+
+} // anon namespace
+#endif // Q_OS_OSX
+
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
+{
+ ensureInitialized();
+
+ QList<QSslCertificate> systemCerts;
+ // SecTrustSettingsCopyCertificates is not defined on iOS.
+#ifdef Q_OS_OSX
+ QCFType<CFArrayRef> cfCerts;
+ // iterate through all enum members, order:
+ // kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainSystem
+ for (int dom = kSecTrustSettingsDomainUser; dom <= kSecTrustSettingsDomainSystem; dom++) {
+ OSStatus status = SecTrustSettingsCopyCertificates(dom, &cfCerts);
+ if (status == noErr) {
+ const CFIndex size = CFArrayGetCount(cfCerts);
+ for (CFIndex i = 0; i < size; ++i) {
+ SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
+ if (::isCaCertificateTrusted(cfCert, dom)) {
+ if (derData == NULL) {
+ qWarning("Error retrieving a CA certificate from the system store");
+ } else {
+ systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ }
+ }
+ }
+ }
+ }
+#endif
+ return systemCerts;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 7d0fe00..7415e32 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -71,14 +71,6 @@
#include <string.h>
-#ifdef Q_OS_DARWIN
-# include <private/qcore_mac_p.h>
-#endif
-
-#ifdef Q_OS_OSX
-# include <Security/Security.h>
-#endif
-
QT_BEGIN_NAMESPACE
#if defined(Q_OS_WIN)
@@ -616,6 +608,7 @@ void QSslSocketPrivate::resetDefaultCiphers()
setDefaultCiphers(defaultCiphers);
}
+#ifndef Q_OS_DARWIN // Apple implementation in qsslsocket_mac_shared.cpp
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
{
ensureInitialized();
@@ -624,25 +617,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
timer.start();
#endif
QList<QSslCertificate> systemCerts;
- // note: also check implementation in openssl_mac.cpp
-#if defined(Q_OS_OSX)
- // SecTrustSettingsCopyCertificates is not defined on iOS.
- QCFType<CFArrayRef> cfCerts;
-
- OSStatus status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
- if (status == noErr ) {
- const CFIndex size = CFArrayGetCount(cfCerts);
- for (CFIndex i = 0; i < size; ++i) {
- SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
- QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
- if (derData == NULL) {
- qWarning("error retrieving a CA certificate from the system store");
- } else {
- systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
- }
- }
- }
-#elif defined(Q_OS_WIN)
+#if defined(Q_OS_WIN)
if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
HCERTSTORE hSystemStore;
#if defined(Q_OS_WINCE)
@@ -719,6 +694,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
return systemCerts;
}
+#endif // Q_OS_DARWIN
void QSslSocketBackendPrivate::startClientEncryption()
{
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index 384e149..9546f18 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -45,7 +45,9 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
ssl/qsslsocket_openssl.cpp \
ssl/qsslsocket_openssl_symbols.cpp
-android:!android-no-sdk: SOURCES += ssl/qsslsocket_openssl_android.cpp
+ darwin:SOURCES += ssl/qsslsocket_mac_shared.cpp
+
+ android:!android-no-sdk: SOURCES += ssl/qsslsocket_openssl_android.cpp
# Add optional SSL libs
# Static linking of OpenSSL with msvc:
--
1.9.1