Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
security_context_macos.cc
Go to the documentation of this file.
1// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#if !defined(DART_IO_SECURE_SOCKET_DISABLED)
6
7#include "platform/globals.h"
8#if defined(DART_HOST_OS_MACOS)
9
11
12#include <Availability.h>
13#include <CoreFoundation/CoreFoundation.h>
14#include <Security/SecureTransport.h>
15#include <Security/Security.h>
16
17#include <openssl/ssl.h>
18#include <openssl/x509.h>
19
21
22namespace dart {
23namespace bin {
24
25const intptr_t SSLCertContext::kApproximateSize = sizeof(SSLCertContext);
26
27template <typename T>
28class ScopedCFType {
29 public:
30 explicit ScopedCFType(T obj) : obj_(obj) {}
31
32 ~ScopedCFType() {
33 if (obj_ != nullptr) {
34 CFRelease(obj_);
35 }
36 }
37
38 T get() { return obj_; }
39 T* ptr() { return &obj_; }
40 const T get() const { return obj_; }
41
42 DART_WARN_UNUSED_RESULT T release() {
43 T temp = obj_;
44 obj_ = nullptr;
45 return temp;
46 }
47
48 void set(T obj) { obj_ = obj; }
49
50 bool operator==(T other) { return other == get(); }
51
52 bool operator!=(T other) { return other != get(); }
53
54 private:
55 T obj_;
56
58 DISALLOW_COPY_AND_ASSIGN(ScopedCFType);
59};
60
61static void releaseObjects(const void* val, void* context) {
62 CFRelease(val);
63}
64
65template <>
66ScopedCFType<CFMutableArrayRef>::~ScopedCFType() {
67 if (obj_ != nullptr) {
68 CFIndex count = 0;
69 CFArrayApplyFunction(obj_, CFRangeMake(0, CFArrayGetCount(obj_)),
70 releaseObjects, &count);
71 CFRelease(obj_);
72 }
73}
74
75typedef ScopedCFType<CFMutableArrayRef> ScopedCFMutableArrayRef;
76typedef ScopedCFType<CFDataRef> ScopedCFDataRef;
77typedef ScopedCFType<CFStringRef> ScopedCFStringRef;
78typedef ScopedCFType<SecPolicyRef> ScopedSecPolicyRef;
79typedef ScopedCFType<SecCertificateRef> ScopedSecCertificateRef;
80typedef ScopedCFType<SecTrustRef> ScopedSecTrustRef;
81
82const int kNumTrustEvaluateRequestParams = 5;
83
84static SecCertificateRef CreateSecCertificateFromX509(X509* cert) {
85 if (cert == nullptr) {
86 return nullptr;
87 }
88 int length = i2d_X509(cert, nullptr);
89 if (length < 0) {
90 return nullptr;
91 }
92 // This can be `std::make_unique<unsigned char[]>(length)` in C++14
93 // But the Mac toolchain is still using C++11.
94 auto deb_cert = std::unique_ptr<unsigned char[]>(new unsigned char[length]);
95 unsigned char* temp = deb_cert.get();
96 if (i2d_X509(cert, &temp) != length) {
97 return nullptr;
98 }
99 // TODO(bkonyi): we create a copy of the deb_cert here since it's unclear
100 // whether or not SecCertificateCreateWithData takes ownership of the CFData.
101 // Implementation here:
102 // https://opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-55050.2/lib/SecCertificate.cpp.auto.html
103 ScopedCFDataRef cert_buf(CFDataCreate(nullptr, deb_cert.get(), length));
104 return SecCertificateCreateWithData(nullptr, cert_buf.get());
105}
106
107static ssl_verify_result_t CertificateVerificationCallback(SSL* ssl,
108 uint8_t* out_alert) {
109 SSLFilter* filter = static_cast<SSLFilter*>(
110 SSL_get_ex_data(ssl, SSLFilter::filter_ssl_index));
111 SSLCertContext* context = static_cast<SSLCertContext*>(
112 SSL_get_ex_data(ssl, SSLFilter::ssl_cert_context_index));
113
114 const X509TrustState* certificate_trust_state =
115 filter->certificate_trust_state();
116
117 STACK_OF(X509)* chain = SSL_get_peer_full_cert_chain(ssl);
118 const intptr_t chain_length = sk_X509_num(chain);
119 X509* root_cert =
120 chain_length == 0 ? nullptr : sk_X509_value(chain, chain_length - 1);
121 if (certificate_trust_state != nullptr) {
122 // Callback have been previously called to explicitly evaluate root_cert.
123 if (certificate_trust_state->x509() == root_cert) {
124 return certificate_trust_state->is_trusted() ? ssl_verify_ok
125 : ssl_verify_invalid;
126 }
127 }
128
129 // Convert BoringSSL formatted certificates to SecCertificate certificates.
130 ScopedCFMutableArrayRef cert_chain(nullptr);
131 cert_chain.set(CFArrayCreateMutable(nullptr, chain_length, nullptr));
132 for (intptr_t i = 0; i < chain_length; i++) {
133 auto cert = sk_X509_value(chain, i);
134 ScopedSecCertificateRef sec_cert(CreateSecCertificateFromX509(cert));
135 if (sec_cert == nullptr) {
136 return ssl_verify_invalid;
137 }
138 CFArrayAppendValue(cert_chain.get(), sec_cert.release());
139 }
140
141 SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
142 X509_STORE* store = SSL_CTX_get_cert_store(ssl_ctx);
143 // Convert all trusted certificates provided by the user via
144 // setTrustedCertificatesBytes or the command line into SecCertificates.
145 ScopedCFMutableArrayRef trusted_certs(
146 CFArrayCreateMutable(nullptr, 0, nullptr));
147 ASSERT(store != nullptr);
148
149 for (const X509_OBJECT* obj : X509_STORE_get0_objects(store)) {
150 X509* ca = X509_OBJECT_get0_X509(obj);
151 ScopedSecCertificateRef cert(CreateSecCertificateFromX509(ca));
152 if (cert == nullptr) {
153 return ssl_verify_invalid;
154 }
155 CFArrayAppendValue(trusted_certs.get(), cert.release());
156 }
157
158 // Generate a policy for validating chains for SSL.
159 CFStringRef cfhostname = nullptr;
160 if (filter->hostname() != nullptr) {
161 cfhostname = CFStringCreateWithCString(nullptr, filter->hostname(),
162 kCFStringEncodingUTF8);
163 }
164 ScopedCFStringRef hostname(cfhostname);
165 ScopedSecPolicyRef policy(
166 SecPolicyCreateSSL(filter->is_client(), hostname.get()));
167
168 // Create the trust object with the certificates provided by the user.
169 ScopedSecTrustRef trust(nullptr);
170 OSStatus status = SecTrustCreateWithCertificates(cert_chain.get(),
171 policy.get(), trust.ptr());
172 if (status != noErr) {
173 return ssl_verify_invalid;
174 }
175
176 // If the user provided any additional CA certificates, add them to the trust
177 // object.
178 if (CFArrayGetCount(trusted_certs.get()) > 0) {
179 status = SecTrustSetAnchorCertificates(trust.get(), trusted_certs.get());
180 if (status != noErr) {
181 return ssl_verify_invalid;
182 }
183 }
184
185 // Specify whether or not to use the built-in CA certificates for
186 // verification.
187 status =
188 SecTrustSetAnchorCertificatesOnly(trust.get(), !context->trust_builtin());
189 if (status != noErr) {
190 return ssl_verify_invalid;
191 }
192
193 // TrustEvaluateHandler should release all handles.
194 Dart_CObject dart_cobject_trust;
195 dart_cobject_trust.type = Dart_CObject_kInt64;
196 dart_cobject_trust.value.as_int64 =
197 reinterpret_cast<intptr_t>(trust.release());
198
199 Dart_CObject dart_cobject_cert_chain;
200 dart_cobject_cert_chain.type = Dart_CObject_kInt64;
201 dart_cobject_cert_chain.value.as_int64 =
202 reinterpret_cast<intptr_t>(cert_chain.release());
203
204 Dart_CObject dart_cobject_trusted_certs;
205 dart_cobject_trusted_certs.type = Dart_CObject_kInt64;
206 dart_cobject_trusted_certs.value.as_int64 =
207 reinterpret_cast<intptr_t>(trusted_certs.release());
208
209 if (root_cert != nullptr) {
210 X509_up_ref(root_cert);
211 }
212 Dart_CObject dart_cobject_root_cert;
213 dart_cobject_root_cert.type = Dart_CObject_kInt64;
214 dart_cobject_root_cert.value.as_int64 = reinterpret_cast<intptr_t>(root_cert);
215
216 Dart_CObject reply_send_port;
217 reply_send_port.type = Dart_CObject_kSendPort;
218 reply_send_port.value.as_send_port.id = filter->reply_port();
219
220 Dart_CObject array;
222 array.value.as_array.length = kNumTrustEvaluateRequestParams;
223 Dart_CObject* values[] = {&dart_cobject_trust, &dart_cobject_cert_chain,
224 &dart_cobject_trusted_certs,
225 &dart_cobject_root_cert, &reply_send_port};
226 array.value.as_array.values = values;
227
228 Dart_PostCObject(filter->trust_evaluate_reply_port(), &array);
229 return ssl_verify_retry;
230}
231
232static void postReply(Dart_Port reply_port_id,
233 bool success,
234 X509* certificate = nullptr) {
235 Dart_CObject dart_cobject_success;
236 dart_cobject_success.type = Dart_CObject_kBool;
237 dart_cobject_success.value.as_bool = success;
238
239 Dart_CObject dart_cobject_certificate;
240 dart_cobject_certificate.type = Dart_CObject_kInt64;
241 dart_cobject_certificate.value.as_int64 =
242 reinterpret_cast<intptr_t>(certificate);
243
244 Dart_CObject array;
246 array.value.as_array.length = 2;
247 Dart_CObject* values[] = {&dart_cobject_success, &dart_cobject_certificate};
248 array.value.as_array.values = values;
249
250 Dart_PostCObject(reply_port_id, &array);
251}
252
253static void TrustEvaluateHandler(Dart_Port dest_port_id,
255 // This is used for testing to confirm that trust evaluation doesn't block
256 // dart isolate.
257 // First sleep exposes problem where ssl data structures are released/freed
258 // by main isolate before this handler had a chance to access them.
259 // Second sleep(below) is there to maintain same long delay of certificate
260 // verification.
262 usleep(2000 * 1000 /* 2 s*/);
263 }
264
265 CObjectArray request(message);
266 if (request.Length() != kNumTrustEvaluateRequestParams) {
267 FATAL("Malformed trust evaluate message: got %" Pd
268 " parameters "
269 "expected %d\n",
270 request.Length(), kNumTrustEvaluateRequestParams);
271 }
272 CObjectIntptr trust_cobject(request[0]);
273 ScopedSecTrustRef trust(reinterpret_cast<SecTrustRef>(trust_cobject.Value()));
274 CObjectIntptr cert_chain_cobject(request[1]);
275 ScopedCFMutableArrayRef cert_chain(
276 reinterpret_cast<CFMutableArrayRef>(cert_chain_cobject.Value()));
277 CObjectIntptr trusted_certs_cobject(request[2]);
278 ScopedCFMutableArrayRef trusted_certs(
279 reinterpret_cast<CFMutableArrayRef>(trusted_certs_cobject.Value()));
280 CObjectIntptr root_cert_cobject(request[3]);
281 X509* root_cert = reinterpret_cast<X509*>(root_cert_cobject.Value());
282 CObjectSendPort reply_port(request[4]);
283 Dart_Port reply_port_id = reply_port.Value();
284
285 SecTrustResultType trust_result;
287 usleep(3000 * 1000 /* 3 s*/);
288 }
289
290 // Perform the certificate verification.
291 // The result is ignored as we get more information from the following call
292 // to SecTrustGetTrustResult which also happens to match the information we
293 // got from calling SecTrustEvaluate before macOS 10.14.
294 bool res = SecTrustEvaluateWithError(trust.get(), nullptr);
295 USE(res);
296 OSStatus status = SecTrustGetTrustResult(trust.get(), &trust_result);
297
298 postReply(reply_port_id,
299 status == noErr && (trust_result == kSecTrustResultProceed ||
300 trust_result == kSecTrustResultUnspecified),
301 root_cert);
302}
303
305 SSL_set_custom_verify(ssl, SSL_VERIFY_PEER, CertificateVerificationCallback);
306}
307
309 return &TrustEvaluateHandler;
310}
311
313 // First, try to use locations specified on the command line.
314 if (root_certs_file() != nullptr) {
315 LoadRootCertFile(root_certs_file());
316 return;
317 }
318 if (root_certs_cache() != nullptr) {
319 LoadRootCertCache(root_certs_cache());
320 return;
321 }
322 set_trust_builtin(true);
323}
324
325} // namespace bin
326} // namespace dart
327
328#endif // defined(DART_HOST_OS_MACOS)
329
330#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)
int count
bool operator!=(const sk_sp< T > &a, const sk_sp< U > &b)
Definition SkRefCnt.h:355
SI void store(P *ptr, const T &val)
void RegisterCallbacks(SSL *ssl)
static const char * root_certs_file()
TrustEvaluateHandlerFunc GetTrustEvaluateHandler() const
static const intptr_t kApproximateSize
static const char * root_certs_cache()
static bool long_ssl_cert_evaluation()
void set_trust_builtin(bool trust_builtin)
int64_t Dart_Port
Definition dart_api.h:1524
#define DART_WARN_UNUSED_RESULT
Definition dart_api.h:66
@ Dart_CObject_kInt64
@ Dart_CObject_kSendPort
@ Dart_CObject_kArray
@ Dart_CObject_kBool
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
#define ASSERT(E)
#define FATAL(error)
size_t length
Win32Message message
void(* TrustEvaluateHandlerFunc)(Dart_Port dest_port_id, Dart_CObject *message)
static void USE(T &&)
Definition globals.h:618
DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject *message)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network policy
Definition switches.h:248
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition switches.h:76
const myers::Point & get(const myers::Segment &)
#define Pd
Definition globals.h:408
#define DISALLOW_ALLOCATION()
Definition globals.h:604
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition globals.h:581
#define T
struct _Dart_CObject::@86::@87 as_send_port
union _Dart_CObject::@86 value
Dart_CObject_Type type
struct _Dart_CObject::@86::@89 as_array
struct _Dart_CObject ** values