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