From 6b9366e7748857f14d5b0f92ced70c08ab5235b7 Mon Sep 17 00:00:00 2001 From: Daniel Molkentin 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) --- 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 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 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 +#endif + +#include + +#ifdef Q_OS_OSX +# include +#endif + + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_OSX +namespace { + +bool hasTrustedSslServerPolicy(SecPolicyRef policy, CFDictionaryRef props) { + QCFType 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(&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(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 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(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 QSslSocketPrivate::systemCaCertificates() +{ + ensureInitialized(); + + QList systemCerts; + // SecTrustSettingsCopyCertificates is not defined on iOS. +#ifdef Q_OS_OSX + QCFType 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 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 -#ifdef Q_OS_DARWIN -# include -#endif - -#ifdef Q_OS_OSX -# include -#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 QSslSocketPrivate::systemCaCertificates() { ensureInitialized(); @@ -624,25 +617,7 @@ QList QSslSocketPrivate::systemCaCertificates() timer.start(); #endif QList systemCerts; - // note: also check implementation in openssl_mac.cpp -#if defined(Q_OS_OSX) - // SecTrustSettingsCopyCertificates is not defined on iOS. - QCFType 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 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 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