Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsslsocket_mac_shared.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2015 ownCloud Inc
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include <QtNetwork/private/qtlsbackend_p.h>
7
8#include <QtNetwork/qsslcertificate.h>
9
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qglobal.h>
12#include <QtCore/qdebug.h>
13
14
15#ifdef Q_OS_MACOS
16
17#include <QtCore/private/qcore_mac_p.h>
18
19#include <CoreFoundation/CFArray.h>
20#include <Security/Security.h>
21
22#endif // Q_OS_MACOS
23
25
26Q_LOGGING_CATEGORY(lcX509, "qt.mac.shared.x509");
27
28#ifdef Q_OS_MACOS
29namespace {
30
31bool hasTrustedSslServerPolicy(SecPolicyRef policy, CFDictionaryRef props) {
32 QCFType<CFDictionaryRef> policyProps = SecPolicyCopyProperties(policy);
33 // only accept certificates with policies for SSL server validation for now
34 if (CFEqual(CFDictionaryGetValue(policyProps, kSecPolicyOid), kSecPolicyAppleSSL)) {
35 CFBooleanRef policyClient;
36 if (CFDictionaryGetValueIfPresent(policyProps, kSecPolicyClient, reinterpret_cast<const void**>(&policyClient)) &&
37 CFEqual(policyClient, kCFBooleanTrue)) {
38 return false; // no client certs
39 }
40 if (!CFDictionaryContainsKey(props, kSecTrustSettingsResult)) {
41 // as per the docs, no trust settings result implies full trust
42 return true;
43 }
44 CFNumberRef number = static_cast<CFNumberRef>(CFDictionaryGetValue(props, kSecTrustSettingsResult));
45 SecTrustSettingsResult settingsResult;
46 CFNumberGetValue(number, kCFNumberSInt32Type, &settingsResult);
47 switch (settingsResult) {
48 case kSecTrustSettingsResultTrustRoot:
49 case kSecTrustSettingsResultTrustAsRoot:
50 return true;
51 default:
52 return false;
53 }
54 }
55 return false;
56}
57
58bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
59{
60 QCFType<CFArrayRef> cfTrustSettings;
61 OSStatus status = SecTrustSettingsCopyTrustSettings(cfCert, SecTrustSettingsDomain(domain), &cfTrustSettings);
62 if (status == noErr) {
63 CFIndex size = CFArrayGetCount(cfTrustSettings);
64 // if empty, trust for everything (as per the Security Framework documentation)
65 if (size == 0) {
66 return true;
67 } else {
68 for (CFIndex i = 0; i < size; ++i) {
69 CFDictionaryRef props = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(cfTrustSettings, i));
70 if (CFDictionaryContainsKey(props, kSecTrustSettingsPolicy)) {
71 if (hasTrustedSslServerPolicy((SecPolicyRef)CFDictionaryGetValue(props, kSecTrustSettingsPolicy), props))
72 return true;
73 }
74 }
75 }
76 }
77
78 return false;
79}
80
81bool canDERBeParsed(CFDataRef derData, const QSslCertificate &qtCert)
82{
83 // We are observing certificates, that while accepted when we copy them
84 // from the keychain(s), later give us 'Failed to create SslCertificate
85 // from QSslCertificate'. It's interesting to know at what step the failure
86 // occurred. Let's check it and skip it below if it's not valid.
87
88 auto checkDer = [](CFDataRef derData, const char *source)
89 {
90 Q_ASSERT(source);
91 Q_ASSERT(derData);
92
93 const auto cfLength = CFDataGetLength(derData);
94 if (cfLength <= 0) {
95 qCWarning(lcX509) << source << "returned faulty DER data with invalid length.";
96 return false;
97 }
98
99 QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, derData);
100 if (!secRef) {
101 qCWarning(lcX509) << source << "returned faulty DER data which cannot be parsed back.";
102 return false;
103 }
104 return true;
105 };
106
107 if (!checkDer(derData, "SecCertificateCopyData")) {
108 qCDebug(lcX509) << "Faulty QSslCertificate is:" << qtCert;// Just in case we managed to parse something.
109 return false;
110 }
111
112 // Generic parser failed?
113 if (qtCert.isNull()) {
114 qCWarning(lcX509, "QSslCertificate failed to parse DER");
115 return false;
116 }
117
118 const QCFType<CFDataRef> qtDerData = qtCert.toDer().toCFData();
119 if (!checkDer(qtDerData, "QSslCertificate")) {
120 qCWarning(lcX509) << "Faulty QSslCertificate is:" << qtCert;
121 return false;
122 }
123
124 return true;
125}
126
127} // unnamed namespace
128#endif // Q_OS_MACOS
129
130namespace QTlsPrivate {
132{
133 QList<QSslCertificate> systemCerts;
134 // SecTrustSettingsCopyCertificates is not defined on iOS.
135#ifdef Q_OS_MACOS
136 // iterate through all enum members, order:
137 // kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainSystem
138 for (int dom = kSecTrustSettingsDomainUser; dom <= int(kSecTrustSettingsDomainSystem); dom++) {
139 QCFType<CFArrayRef> cfCerts;
140 OSStatus status = SecTrustSettingsCopyCertificates(SecTrustSettingsDomain(dom), &cfCerts);
141 if (status == noErr) {
142 const CFIndex size = CFArrayGetCount(cfCerts);
143 for (CFIndex i = 0; i < size; ++i) {
144 SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
145 QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
146 if (isCaCertificateTrusted(cfCert, dom)) {
147 if (derData) {
148 const auto newCert = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
149 if (!canDERBeParsed(derData, newCert)) {
150 // Last attempt to get some information about the certificate:
151 CFShow(cfCert);
152 continue;
153 }
154 systemCerts << newCert;
155 } else {
156 // "Returns NULL if the data passed in the certificate parameter
157 // is not a valid certificate object."
158 qCWarning(lcX509, "SecCertificateCopyData returned invalid DER data (nullptr).");
159 }
160 }
161 }
162 }
163 }
164#endif
165 return systemCerts;
166}
167} // namespace QTlsPrivate
168
169QT_END_NAMESPACE
Namespace containing onternal types that TLS backends implement.
QList< QSslCertificate > systemCaCertificates()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")