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 "bin/security_context.h" |
8 | |
9 | #include <openssl/bio.h> |
10 | #include <openssl/err.h> |
11 | #include <openssl/pkcs12.h> |
12 | #include <openssl/ssl.h> |
13 | #include <openssl/x509.h> |
14 | |
15 | #include "platform/globals.h" |
16 | |
17 | #include "bin/directory.h" |
18 | #include "bin/file.h" |
19 | #include "bin/secure_socket_filter.h" |
20 | #include "bin/secure_socket_utils.h" |
21 | #include "platform/syslog.h" |
22 | |
23 | // Return the error from the containing function if handle is an error handle. |
24 | #define RETURN_IF_ERROR(handle) \ |
25 | { \ |
26 | Dart_Handle __handle = handle; \ |
27 | if (Dart_IsError((__handle))) { \ |
28 | return __handle; \ |
29 | } \ |
30 | } |
31 | |
32 | namespace dart { |
33 | namespace bin { |
34 | |
35 | const char* SSLCertContext::root_certs_file_ = NULL; |
36 | const char* SSLCertContext::root_certs_cache_ = NULL; |
37 | |
38 | int SSLCertContext::CertificateCallback(int preverify_ok, |
39 | X509_STORE_CTX* store_ctx) { |
40 | if (preverify_ok == 1) { |
41 | return 1; |
42 | } |
43 | Dart_Isolate isolate = Dart_CurrentIsolate(); |
44 | if (isolate == NULL) { |
45 | FATAL("CertificateCallback called with no current isolate\n" ); |
46 | } |
47 | X509* certificate = X509_STORE_CTX_get_current_cert(store_ctx); |
48 | int ssl_index = SSL_get_ex_data_X509_STORE_CTX_idx(); |
49 | SSL* ssl = |
50 | static_cast<SSL*>(X509_STORE_CTX_get_ex_data(store_ctx, ssl_index)); |
51 | SSLFilter* filter = static_cast<SSLFilter*>( |
52 | SSL_get_ex_data(ssl, SSLFilter::filter_ssl_index)); |
53 | Dart_Handle callback = filter->bad_certificate_callback(); |
54 | if (Dart_IsNull(callback)) { |
55 | return 0; |
56 | } |
57 | |
58 | // Upref since the Dart X509 object may outlive the SecurityContext. |
59 | if (certificate != NULL) { |
60 | X509_up_ref(certificate); |
61 | } |
62 | Dart_Handle args[1]; |
63 | args[0] = X509Helper::WrappedX509Certificate(certificate); |
64 | if (Dart_IsError(args[0])) { |
65 | filter->callback_error = args[0]; |
66 | return 0; |
67 | } |
68 | Dart_Handle result = Dart_InvokeClosure(callback, 1, args); |
69 | if (!Dart_IsError(result) && !Dart_IsBoolean(result)) { |
70 | result = Dart_NewUnhandledExceptionError(DartUtils::NewDartIOException( |
71 | "HandshakeException" , |
72 | "BadCertificateCallback returned a value that was not a boolean" , |
73 | Dart_Null())); |
74 | } |
75 | if (Dart_IsError(result)) { |
76 | filter->callback_error = result; |
77 | return 0; |
78 | } |
79 | return static_cast<int>(DartUtils::GetBooleanValue(result)); |
80 | } |
81 | |
82 | SSLCertContext* SSLCertContext::GetSecurityContext(Dart_NativeArguments args) { |
83 | SSLCertContext* context; |
84 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
85 | ASSERT(Dart_IsInstance(dart_this)); |
86 | ThrowIfError(Dart_GetNativeInstanceField( |
87 | dart_this, SSLCertContext::kSecurityContextNativeFieldIndex, |
88 | reinterpret_cast<intptr_t*>(&context))); |
89 | if (context == NULL) { |
90 | Dart_PropagateError(Dart_NewUnhandledExceptionError( |
91 | DartUtils::NewInternalError("No native peer" ))); |
92 | } |
93 | return context; |
94 | } |
95 | |
96 | static void DeleteSecurityContext(void* isolate_data, void* context_pointer) { |
97 | SSLCertContext* context = static_cast<SSLCertContext*>(context_pointer); |
98 | context->Release(); |
99 | } |
100 | |
101 | static Dart_Handle SetSecurityContext(Dart_NativeArguments args, |
102 | SSLCertContext* context) { |
103 | Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); |
104 | RETURN_IF_ERROR(dart_this); |
105 | ASSERT(Dart_IsInstance(dart_this)); |
106 | Dart_Handle err = Dart_SetNativeInstanceField( |
107 | dart_this, SSLCertContext::kSecurityContextNativeFieldIndex, |
108 | reinterpret_cast<intptr_t>(context)); |
109 | RETURN_IF_ERROR(err); |
110 | Dart_NewFinalizableHandle(dart_this, context, |
111 | SSLCertContext::kApproximateSize, |
112 | DeleteSecurityContext); |
113 | return Dart_Null(); |
114 | } |
115 | |
116 | static void ReleaseCertificate(void* isolate_data, void* context_pointer) { |
117 | X509* cert = reinterpret_cast<X509*>(context_pointer); |
118 | X509_free(cert); |
119 | } |
120 | |
121 | static intptr_t EstimateX509Size(X509* certificate) { |
122 | intptr_t length = i2d_X509(certificate, NULL); |
123 | return length > 0 ? length : 0; |
124 | } |
125 | |
126 | // Returns the handle for a Dart object wrapping the X509 certificate object. |
127 | // The caller should own a reference to the X509 object whose reference count |
128 | // won't drop to zero before the ReleaseCertificate finalizer runs. |
129 | Dart_Handle X509Helper::WrappedX509Certificate(X509* certificate) { |
130 | if (certificate == NULL) { |
131 | return Dart_Null(); |
132 | } |
133 | Dart_Handle x509_type = |
134 | DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate" ); |
135 | if (Dart_IsError(x509_type)) { |
136 | X509_free(certificate); |
137 | return x509_type; |
138 | } |
139 | Dart_Handle arguments[] = {NULL}; |
140 | Dart_Handle result = |
141 | Dart_New(x509_type, DartUtils::NewString("_" ), 0, arguments); |
142 | if (Dart_IsError(result)) { |
143 | X509_free(certificate); |
144 | return result; |
145 | } |
146 | ASSERT(Dart_IsInstance(result)); |
147 | Dart_Handle status = |
148 | Dart_SetNativeInstanceField(result, SSLCertContext::kX509NativeFieldIndex, |
149 | reinterpret_cast<intptr_t>(certificate)); |
150 | if (Dart_IsError(status)) { |
151 | X509_free(certificate); |
152 | return status; |
153 | } |
154 | const intptr_t approximate_size_of_certificate = |
155 | sizeof(*certificate) + EstimateX509Size(certificate); |
156 | ASSERT(approximate_size_of_certificate > 0); |
157 | Dart_NewFinalizableHandle(result, reinterpret_cast<void*>(certificate), |
158 | approximate_size_of_certificate, |
159 | ReleaseCertificate); |
160 | return result; |
161 | } |
162 | |
163 | static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, |
164 | ScopedMemBIO* bio, |
165 | const char* password) { |
166 | CBS cbs; |
167 | CBS_init(&cbs, bio->data(), bio->length()); |
168 | |
169 | EVP_PKEY* key = NULL; |
170 | ScopedX509Stack cert_stack(sk_X509_new_null()); |
171 | int status = PKCS12_get_key_and_certs(&key, cert_stack.get(), &cbs, password); |
172 | if (status == 0) { |
173 | return status; |
174 | } |
175 | |
176 | X509_STORE* store = SSL_CTX_get_cert_store(context); |
177 | X509* ca; |
178 | while ((ca = sk_X509_shift(cert_stack.get())) != NULL) { |
179 | status = X509_STORE_add_cert(store, ca); |
180 | // X509_STORE_add_cert increments the reference count of cert on success. |
181 | X509_free(ca); |
182 | if (status == 0) { |
183 | return status; |
184 | } |
185 | } |
186 | |
187 | return status; |
188 | } |
189 | |
190 | static int SetTrustedCertificatesBytesPEM(SSL_CTX* context, BIO* bio) { |
191 | X509_STORE* store = SSL_CTX_get_cert_store(context); |
192 | |
193 | int status = 0; |
194 | X509* cert = NULL; |
195 | while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
196 | status = X509_STORE_add_cert(store, cert); |
197 | // X509_STORE_add_cert increments the reference count of cert on success. |
198 | X509_free(cert); |
199 | if (status == 0) { |
200 | return status; |
201 | } |
202 | } |
203 | |
204 | // If no PEM start line is found, it means that we read to the end of the |
205 | // file, or that the file isn't PEM. In the first case, status will be |
206 | // non-zero indicating success. In the second case, status will be 0, |
207 | // indicating that we should try to read as PKCS12. If there is some other |
208 | // error, we return it up to the caller. |
209 | return SecureSocketUtils::NoPEMStartLine() ? status : 0; |
210 | } |
211 | |
212 | void SSLCertContext::SetTrustedCertificatesBytes(Dart_Handle cert_bytes, |
213 | const char* password) { |
214 | int status = 0; |
215 | { |
216 | ScopedMemBIO bio(cert_bytes); |
217 | status = SetTrustedCertificatesBytesPEM(context(), bio.bio()); |
218 | if (status == 0) { |
219 | if (SecureSocketUtils::NoPEMStartLine()) { |
220 | ERR_clear_error(); |
221 | BIO_reset(bio.bio()); |
222 | status = SetTrustedCertificatesBytesPKCS12(context(), &bio, password); |
223 | } |
224 | } else { |
225 | // The PEM file was successfully parsed. |
226 | ERR_clear_error(); |
227 | } |
228 | } |
229 | SecureSocketUtils::CheckStatus(status, "TlsException" , |
230 | "Failure trusting builtin roots" ); |
231 | } |
232 | |
233 | static int SetClientAuthoritiesPKCS12(SSL_CTX* context, |
234 | ScopedMemBIO* bio, |
235 | const char* password) { |
236 | CBS cbs; |
237 | CBS_init(&cbs, bio->data(), bio->length()); |
238 | |
239 | EVP_PKEY* key = NULL; |
240 | ScopedX509Stack cert_stack(sk_X509_new_null()); |
241 | int status = PKCS12_get_key_and_certs(&key, cert_stack.get(), &cbs, password); |
242 | if (status == 0) { |
243 | return status; |
244 | } |
245 | |
246 | X509* ca; |
247 | while ((ca = sk_X509_shift(cert_stack.get())) != NULL) { |
248 | status = SSL_CTX_add_client_CA(context, ca); |
249 | // SSL_CTX_add_client_CA increments the reference count of ca on success. |
250 | X509_free(ca); // The name has been extracted. |
251 | if (status == 0) { |
252 | return status; |
253 | } |
254 | } |
255 | |
256 | return status; |
257 | } |
258 | |
259 | static int SetClientAuthoritiesPEM(SSL_CTX* context, BIO* bio) { |
260 | int status = 0; |
261 | X509* cert = NULL; |
262 | while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
263 | status = SSL_CTX_add_client_CA(context, cert); |
264 | X509_free(cert); // The name has been extracted. |
265 | if (status == 0) { |
266 | return status; |
267 | } |
268 | } |
269 | return SecureSocketUtils::NoPEMStartLine() ? status : 0; |
270 | } |
271 | |
272 | static int SetClientAuthorities(SSL_CTX* context, |
273 | ScopedMemBIO* bio, |
274 | const char* password) { |
275 | int status = SetClientAuthoritiesPEM(context, bio->bio()); |
276 | if (status == 0) { |
277 | if (SecureSocketUtils::NoPEMStartLine()) { |
278 | ERR_clear_error(); |
279 | BIO_reset(bio->bio()); |
280 | status = SetClientAuthoritiesPKCS12(context, bio, password); |
281 | } |
282 | } else { |
283 | // The PEM file was successfully parsed. |
284 | ERR_clear_error(); |
285 | } |
286 | return status; |
287 | } |
288 | |
289 | void SSLCertContext::SetClientAuthoritiesBytes( |
290 | Dart_Handle client_authorities_bytes, |
291 | const char* password) { |
292 | int status; |
293 | { |
294 | ScopedMemBIO bio(client_authorities_bytes); |
295 | status = SetClientAuthorities(context(), &bio, password); |
296 | } |
297 | |
298 | SecureSocketUtils::CheckStatus(status, "TlsException" , |
299 | "Failure in setClientAuthoritiesBytes" ); |
300 | } |
301 | |
302 | void SSLCertContext::LoadRootCertFile(const char* file) { |
303 | if (SSL_LOG_STATUS) { |
304 | Syslog::Print("Looking for trusted roots in %s\n" , file); |
305 | } |
306 | if (!File::Exists(NULL, file)) { |
307 | SecureSocketUtils::ThrowIOException(-1, "TlsException" , |
308 | "Failed to find root cert file" , NULL); |
309 | } |
310 | int status = SSL_CTX_load_verify_locations(context(), file, NULL); |
311 | SecureSocketUtils::CheckStatus(status, "TlsException" , |
312 | "Failure trusting builtin roots" ); |
313 | if (SSL_LOG_STATUS) { |
314 | Syslog::Print("Trusting roots from: %s\n" , file); |
315 | } |
316 | } |
317 | |
318 | void SSLCertContext::AddCompiledInCerts() { |
319 | if (root_certificates_pem == NULL) { |
320 | if (SSL_LOG_STATUS) { |
321 | Syslog::Print("Missing compiled-in roots\n" ); |
322 | } |
323 | return; |
324 | } |
325 | X509_STORE* store = SSL_CTX_get_cert_store(context()); |
326 | BIO* roots_bio = |
327 | BIO_new_mem_buf(const_cast<unsigned char*>(root_certificates_pem), |
328 | root_certificates_pem_length); |
329 | X509* root_cert; |
330 | // PEM_read_bio_X509 reads PEM-encoded certificates from a bio (in our case, |
331 | // backed by a memory buffer), and returns X509 objects, one by one. |
332 | // When the end of the bio is reached, it returns null. |
333 | while ((root_cert = PEM_read_bio_X509(roots_bio, NULL, NULL, NULL)) != NULL) { |
334 | int status = X509_STORE_add_cert(store, root_cert); |
335 | // X509_STORE_add_cert increments the reference count of cert on success. |
336 | X509_free(root_cert); |
337 | if (status == 0) { |
338 | break; |
339 | } |
340 | } |
341 | BIO_free(roots_bio); |
342 | // If there is an error here, it must be the error indicating that we are done |
343 | // reading PEM certificates. |
344 | ASSERT((ERR_peek_error() == 0) || SecureSocketUtils::NoPEMStartLine()); |
345 | ERR_clear_error(); |
346 | } |
347 | |
348 | void SSLCertContext::LoadRootCertCache(const char* cache) { |
349 | if (SSL_LOG_STATUS) { |
350 | Syslog::Print("Looking for trusted roots in %s\n" , cache); |
351 | } |
352 | if (Directory::Exists(NULL, cache) != Directory::EXISTS) { |
353 | SecureSocketUtils::ThrowIOException(-1, "TlsException" , |
354 | "Failed to find root cert cache" , NULL); |
355 | } |
356 | int status = SSL_CTX_load_verify_locations(context(), NULL, cache); |
357 | SecureSocketUtils::CheckStatus(status, "TlsException" , |
358 | "Failure trusting builtin roots" ); |
359 | if (SSL_LOG_STATUS) { |
360 | Syslog::Print("Trusting roots from: %s\n" , cache); |
361 | } |
362 | } |
363 | |
364 | int PasswordCallback(char* buf, int size, int rwflag, void* userdata) { |
365 | char* password = static_cast<char*>(userdata); |
366 | ASSERT(size == PEM_BUFSIZE); |
367 | strncpy(buf, password, size); |
368 | return strlen(password); |
369 | } |
370 | |
371 | static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) { |
372 | ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); |
373 | if (p12.get() == NULL) { |
374 | return NULL; |
375 | } |
376 | |
377 | EVP_PKEY* key = NULL; |
378 | X509* cert = NULL; |
379 | STACK_OF(X509)* ca_certs = NULL; |
380 | int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); |
381 | if (status == 0) { |
382 | return NULL; |
383 | } |
384 | |
385 | // We only care about the private key. |
386 | ScopedX509 delete_cert(cert); |
387 | ScopedX509Stack delete_ca_certs(ca_certs); |
388 | return key; |
389 | } |
390 | |
391 | static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) { |
392 | EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, PasswordCallback, |
393 | const_cast<char*>(password)); |
394 | if (key == NULL) { |
395 | // We try reading data as PKCS12 only if reading as PEM was unsuccessful and |
396 | // if there is no indication that the data is malformed PEM. We assume the |
397 | // data is malformed PEM if it contains the start line, i.e. a line |
398 | // with ----- BEGIN. |
399 | if (SecureSocketUtils::NoPEMStartLine()) { |
400 | // Reset the bio, and clear the error from trying to read as PEM. |
401 | ERR_clear_error(); |
402 | BIO_reset(bio); |
403 | |
404 | // Try to decode as PKCS12. |
405 | key = GetPrivateKeyPKCS12(bio, password); |
406 | } |
407 | } |
408 | return key; |
409 | } |
410 | |
411 | const char* SSLCertContext::GetPasswordArgument(Dart_NativeArguments args, |
412 | intptr_t index) { |
413 | Dart_Handle password_object = |
414 | ThrowIfError(Dart_GetNativeArgument(args, index)); |
415 | const char* password = NULL; |
416 | if (Dart_IsString(password_object)) { |
417 | ThrowIfError(Dart_StringToCString(password_object, &password)); |
418 | if (strlen(password) > PEM_BUFSIZE - 1) { |
419 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
420 | "Password length is greater than 1023 (PEM_BUFSIZE)" )); |
421 | } |
422 | } else if (Dart_IsNull(password_object)) { |
423 | password = "" ; |
424 | } else { |
425 | Dart_ThrowException( |
426 | DartUtils::NewDartArgumentError("Password is not a String or null" )); |
427 | } |
428 | return password; |
429 | } |
430 | |
431 | int AlpnCallback(SSL* ssl, |
432 | const uint8_t** out, |
433 | uint8_t* outlen, |
434 | const uint8_t* in, |
435 | unsigned int inlen, |
436 | void* arg) { |
437 | // 'in' and 'arg' are sequences of (length, data) strings with 1-byte lengths. |
438 | // 'arg' is 0-terminated. Finds the first string in 'arg' that is in 'in'. |
439 | uint8_t* server_list = static_cast<uint8_t*>(arg); |
440 | while (*server_list != 0) { |
441 | uint8_t protocol_length = *server_list++; |
442 | const uint8_t* client_list = in; |
443 | while (client_list < in + inlen) { |
444 | uint8_t client_protocol_length = *client_list++; |
445 | if (client_protocol_length == protocol_length) { |
446 | if (0 == memcmp(server_list, client_list, protocol_length)) { |
447 | *out = client_list; |
448 | *outlen = client_protocol_length; |
449 | return SSL_TLSEXT_ERR_OK; // Success |
450 | } |
451 | } |
452 | client_list += client_protocol_length; |
453 | } |
454 | server_list += protocol_length; |
455 | } |
456 | // TODO(23580): Make failure send a fatal alert instead of ignoring ALPN. |
457 | return SSL_TLSEXT_ERR_NOACK; |
458 | } |
459 | |
460 | // Sets the protocol list for ALPN on a SSL object or a context. |
461 | void SSLCertContext::SetAlpnProtocolList(Dart_Handle protocols_handle, |
462 | SSL* ssl, |
463 | SSLCertContext* context, |
464 | bool is_server) { |
465 | // Enable ALPN (application layer protocol negotiation) if the caller provides |
466 | // a valid list of supported protocols. |
467 | Dart_TypedData_Type protocols_type; |
468 | uint8_t* protocol_string = NULL; |
469 | uint8_t* protocol_string_copy = NULL; |
470 | intptr_t protocol_string_len = 0; |
471 | int status; |
472 | |
473 | Dart_Handle result = Dart_TypedDataAcquireData( |
474 | protocols_handle, &protocols_type, |
475 | reinterpret_cast<void**>(&protocol_string), &protocol_string_len); |
476 | if (Dart_IsError(result)) { |
477 | Dart_PropagateError(result); |
478 | } |
479 | |
480 | if (protocols_type != Dart_TypedData_kUint8) { |
481 | Dart_TypedDataReleaseData(protocols_handle); |
482 | Dart_PropagateError(Dart_NewApiError( |
483 | "Unexpected type for protocols (expected valid Uint8List)." )); |
484 | } |
485 | |
486 | if (protocol_string_len > 0) { |
487 | if (is_server) { |
488 | // ALPN on server connections must be set on an SSL_CTX object, |
489 | // not on the SSL object of the individual connection. |
490 | ASSERT(context != NULL); |
491 | ASSERT(ssl == NULL); |
492 | // Because it must be passed as a single void*, terminate |
493 | // the list of (length, data) strings with a length 0 string. |
494 | protocol_string_copy = |
495 | static_cast<uint8_t*>(malloc(protocol_string_len + 1)); |
496 | memmove(protocol_string_copy, protocol_string, protocol_string_len); |
497 | protocol_string_copy[protocol_string_len] = '\0'; |
498 | SSL_CTX_set_alpn_select_cb(context->context(), AlpnCallback, |
499 | protocol_string_copy); |
500 | context->set_alpn_protocol_string(protocol_string_copy); |
501 | } else { |
502 | // The function makes a local copy of protocol_string, which it owns. |
503 | if (ssl != NULL) { |
504 | ASSERT(context == NULL); |
505 | status = SSL_set_alpn_protos(ssl, protocol_string, protocol_string_len); |
506 | } else { |
507 | ASSERT(context != NULL); |
508 | ASSERT(ssl == NULL); |
509 | status = SSL_CTX_set_alpn_protos(context->context(), protocol_string, |
510 | protocol_string_len); |
511 | } |
512 | ASSERT(status == 0); // The function returns a non-standard status. |
513 | } |
514 | } |
515 | Dart_TypedDataReleaseData(protocols_handle); |
516 | } |
517 | |
518 | static int UseChainBytesPKCS12(SSL_CTX* context, |
519 | ScopedMemBIO* bio, |
520 | const char* password) { |
521 | CBS cbs; |
522 | CBS_init(&cbs, bio->data(), bio->length()); |
523 | |
524 | EVP_PKEY* key = NULL; |
525 | ScopedX509Stack certs(sk_X509_new_null()); |
526 | int status = PKCS12_get_key_and_certs(&key, certs.get(), &cbs, password); |
527 | if (status == 0) { |
528 | return status; |
529 | } |
530 | |
531 | X509* ca = sk_X509_shift(certs.get()); |
532 | status = SSL_CTX_use_certificate(context, ca); |
533 | if (ERR_peek_error() != 0) { |
534 | // Key/certificate mismatch doesn't imply status is 0. |
535 | status = 0; |
536 | } |
537 | X509_free(ca); |
538 | if (status == 0) { |
539 | return status; |
540 | } |
541 | |
542 | SSL_CTX_clear_chain_certs(context); |
543 | |
544 | while ((ca = sk_X509_shift(certs.get())) != NULL) { |
545 | status = SSL_CTX_add0_chain_cert(context, ca); |
546 | // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the |
547 | // call fails. |
548 | if (status == 0) { |
549 | X509_free(ca); |
550 | return status; |
551 | } |
552 | } |
553 | |
554 | return status; |
555 | } |
556 | |
557 | static int UseChainBytesPEM(SSL_CTX* context, BIO* bio) { |
558 | int status = 0; |
559 | ScopedX509 x509(PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL)); |
560 | if (x509.get() == NULL) { |
561 | return 0; |
562 | } |
563 | |
564 | status = SSL_CTX_use_certificate(context, x509.get()); |
565 | if (ERR_peek_error() != 0) { |
566 | // Key/certificate mismatch doesn't imply status is 0. |
567 | status = 0; |
568 | } |
569 | if (status == 0) { |
570 | return status; |
571 | } |
572 | |
573 | SSL_CTX_clear_chain_certs(context); |
574 | |
575 | X509* ca; |
576 | while ((ca = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
577 | status = SSL_CTX_add0_chain_cert(context, ca); |
578 | // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the |
579 | // call fails. |
580 | if (status == 0) { |
581 | X509_free(ca); |
582 | return status; |
583 | } |
584 | // Note that we must not free `ca` if it was successfully added to the |
585 | // chain. We must free the main certificate x509, though since its reference |
586 | // count is increased by SSL_CTX_use_certificate. |
587 | } |
588 | |
589 | return SecureSocketUtils::NoPEMStartLine() ? status : 0; |
590 | } |
591 | |
592 | static int UseChainBytes(SSL_CTX* context, |
593 | ScopedMemBIO* bio, |
594 | const char* password) { |
595 | int status = UseChainBytesPEM(context, bio->bio()); |
596 | if (status == 0) { |
597 | if (SecureSocketUtils::NoPEMStartLine()) { |
598 | ERR_clear_error(); |
599 | BIO_reset(bio->bio()); |
600 | status = UseChainBytesPKCS12(context, bio, password); |
601 | } |
602 | } else { |
603 | // The PEM file was successfully read. |
604 | ERR_clear_error(); |
605 | } |
606 | return status; |
607 | } |
608 | |
609 | int SSLCertContext::UseCertificateChainBytes(Dart_Handle cert_chain_bytes, |
610 | const char* password) { |
611 | ScopedMemBIO bio(cert_chain_bytes); |
612 | return UseChainBytes(context(), &bio, password); |
613 | } |
614 | |
615 | static X509* GetX509Certificate(Dart_NativeArguments args) { |
616 | X509* certificate = NULL; |
617 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
618 | ASSERT(Dart_IsInstance(dart_this)); |
619 | ThrowIfError(Dart_GetNativeInstanceField( |
620 | dart_this, SSLCertContext::kX509NativeFieldIndex, |
621 | reinterpret_cast<intptr_t*>(&certificate))); |
622 | if (certificate == NULL) { |
623 | Dart_PropagateError(Dart_NewUnhandledExceptionError( |
624 | DartUtils::NewInternalError("No native peer" ))); |
625 | } |
626 | return certificate; |
627 | } |
628 | |
629 | Dart_Handle X509Helper::GetDer(Dart_NativeArguments args) { |
630 | X509* certificate = GetX509Certificate(args); |
631 | // When the second argument is NULL, i2d_X509() returns the length of the |
632 | // DER encoded cert in bytes. |
633 | intptr_t length = i2d_X509(certificate, NULL); |
634 | Dart_Handle cert_handle = Dart_NewTypedData(Dart_TypedData_kUint8, length); |
635 | if (Dart_IsError(cert_handle)) { |
636 | Dart_PropagateError(cert_handle); |
637 | } |
638 | Dart_TypedData_Type typ; |
639 | void* dart_cert_bytes = NULL; |
640 | Dart_Handle status = |
641 | Dart_TypedDataAcquireData(cert_handle, &typ, &dart_cert_bytes, &length); |
642 | if (Dart_IsError(status)) { |
643 | Dart_PropagateError(status); |
644 | } |
645 | |
646 | // When the the second argument points to a non-NULL buffer address, |
647 | // i2d_X509 fills that buffer with the DER encoded cert data and increments |
648 | // the buffer pointer. |
649 | unsigned char* tmp = static_cast<unsigned char*>(dart_cert_bytes); |
650 | const intptr_t written_length = i2d_X509(certificate, &tmp); |
651 | ASSERT(written_length <= length); |
652 | if (written_length < 0) { |
653 | Dart_TypedDataReleaseData(cert_handle); |
654 | SecureSocketUtils::ThrowIOException( |
655 | -1, "TlsException" , "Failed to get certificate bytes" , NULL); |
656 | // SecureSocketUtils::ThrowIOException() does not return. |
657 | } |
658 | |
659 | status = Dart_TypedDataReleaseData(cert_handle); |
660 | if (Dart_IsError(status)) { |
661 | Dart_PropagateError(status); |
662 | } |
663 | return cert_handle; |
664 | } |
665 | |
666 | Dart_Handle X509Helper::GetPem(Dart_NativeArguments args) { |
667 | X509* certificate = GetX509Certificate(args); |
668 | BIO* cert_bio = BIO_new(BIO_s_mem()); |
669 | intptr_t status = PEM_write_bio_X509(cert_bio, certificate); |
670 | if (status == 0) { |
671 | BIO_free(cert_bio); |
672 | SecureSocketUtils::ThrowIOException( |
673 | -1, "TlsException" , "Failed to write certificate to PEM" , NULL); |
674 | // SecureSocketUtils::ThrowIOException() does not return. |
675 | } |
676 | |
677 | BUF_MEM* mem = NULL; |
678 | BIO_get_mem_ptr(cert_bio, &mem); |
679 | Dart_Handle pem_string = Dart_NewStringFromUTF8( |
680 | reinterpret_cast<const uint8_t*>(mem->data), mem->length); |
681 | BIO_free(cert_bio); |
682 | if (Dart_IsError(pem_string)) { |
683 | Dart_PropagateError(pem_string); |
684 | } |
685 | |
686 | return pem_string; |
687 | } |
688 | |
689 | Dart_Handle X509Helper::GetSha1(Dart_NativeArguments args) { |
690 | unsigned char sha1_bytes[EVP_MAX_MD_SIZE]; |
691 | X509* certificate = GetX509Certificate(args); |
692 | const EVP_MD* hash_type = EVP_sha1(); |
693 | |
694 | unsigned int sha1_size; |
695 | intptr_t status = X509_digest(certificate, hash_type, sha1_bytes, &sha1_size); |
696 | if (status == 0) { |
697 | SecureSocketUtils::ThrowIOException( |
698 | -1, "TlsException" , "Failed to compute certificate's sha1" , NULL); |
699 | // SecureSocketUtils::ThrowIOException() does not return. |
700 | } |
701 | |
702 | Dart_Handle sha1_handle = Dart_NewTypedData(Dart_TypedData_kUint8, sha1_size); |
703 | if (Dart_IsError(sha1_handle)) { |
704 | Dart_PropagateError(sha1_handle); |
705 | } |
706 | |
707 | Dart_TypedData_Type typ; |
708 | void* dart_sha1_bytes; |
709 | intptr_t length; |
710 | Dart_Handle result = |
711 | Dart_TypedDataAcquireData(sha1_handle, &typ, &dart_sha1_bytes, &length); |
712 | if (Dart_IsError(result)) { |
713 | Dart_PropagateError(result); |
714 | } |
715 | |
716 | memmove(dart_sha1_bytes, sha1_bytes, length); |
717 | |
718 | result = Dart_TypedDataReleaseData(sha1_handle); |
719 | if (Dart_IsError(result)) { |
720 | Dart_PropagateError(result); |
721 | } |
722 | return sha1_handle; |
723 | } |
724 | |
725 | Dart_Handle X509Helper::GetSubject(Dart_NativeArguments args) { |
726 | X509* certificate = GetX509Certificate(args); |
727 | X509_NAME* subject = X509_get_subject_name(certificate); |
728 | char* subject_string = X509_NAME_oneline(subject, NULL, 0); |
729 | if (subject_string == NULL) { |
730 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
731 | "X509.subject failed to find subject's common name." )); |
732 | } |
733 | Dart_Handle subject_handle = Dart_NewStringFromCString(subject_string); |
734 | OPENSSL_free(subject_string); |
735 | return subject_handle; |
736 | } |
737 | |
738 | Dart_Handle X509Helper::GetIssuer(Dart_NativeArguments args) { |
739 | X509* certificate = GetX509Certificate(args); |
740 | X509_NAME* issuer = X509_get_issuer_name(certificate); |
741 | char* issuer_string = X509_NAME_oneline(issuer, NULL, 0); |
742 | if (issuer_string == NULL) { |
743 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
744 | "X509.issuer failed to find issuer's common name." )); |
745 | } |
746 | Dart_Handle issuer_handle = Dart_NewStringFromCString(issuer_string); |
747 | OPENSSL_free(issuer_string); |
748 | return issuer_handle; |
749 | } |
750 | |
751 | static Dart_Handle ASN1TimeToMilliseconds(ASN1_TIME* aTime) { |
752 | ASN1_UTCTIME* epoch_start = M_ASN1_UTCTIME_new(); |
753 | ASN1_UTCTIME_set_string(epoch_start, "700101000000Z" ); |
754 | int days; |
755 | int seconds; |
756 | int result = ASN1_TIME_diff(&days, &seconds, epoch_start, aTime); |
757 | M_ASN1_UTCTIME_free(epoch_start); |
758 | if (result != 1) { |
759 | // TODO(whesse): Propagate an error to Dart. |
760 | Syslog::PrintErr("ASN1Time error %d\n" , result); |
761 | } |
762 | return Dart_NewInteger((86400LL * days + seconds) * 1000LL); |
763 | } |
764 | |
765 | Dart_Handle X509Helper::GetStartValidity(Dart_NativeArguments args) { |
766 | X509* certificate = GetX509Certificate(args); |
767 | ASN1_TIME* not_before = X509_get_notBefore(certificate); |
768 | return ASN1TimeToMilliseconds(not_before); |
769 | } |
770 | |
771 | Dart_Handle X509Helper::GetEndValidity(Dart_NativeArguments args) { |
772 | X509* certificate = GetX509Certificate(args); |
773 | ASN1_TIME* not_after = X509_get_notAfter(certificate); |
774 | return ASN1TimeToMilliseconds(not_after); |
775 | } |
776 | |
777 | void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)( |
778 | Dart_NativeArguments args) { |
779 | SSLCertContext* context = SSLCertContext::GetSecurityContext(args); |
780 | const char* password = SSLCertContext::GetPasswordArgument(args, 2); |
781 | |
782 | int status; |
783 | { |
784 | ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); |
785 | EVP_PKEY* key = GetPrivateKey(bio.bio(), password); |
786 | status = SSL_CTX_use_PrivateKey(context->context(), key); |
787 | // SSL_CTX_use_PrivateKey increments the reference count of key on success, |
788 | // so we have to call EVP_PKEY_free on both success and failure. |
789 | EVP_PKEY_free(key); |
790 | } |
791 | |
792 | // TODO(24184): Handle different expected errors here - file missing, |
793 | // incorrect password, file not a PEM, and throw exceptions. |
794 | // SecureSocketUtils::CheckStatus should also throw an exception in uncaught |
795 | // cases. |
796 | SecureSocketUtils::CheckStatus(status, "TlsException" , |
797 | "Failure in usePrivateKeyBytes" ); |
798 | } |
799 | |
800 | void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) { |
801 | SSLFilter::InitializeLibrary(); |
802 | SSL_CTX* ctx = SSL_CTX_new(TLS_method()); |
803 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLCertContext::CertificateCallback); |
804 | SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION); |
805 | SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM" ); |
806 | SSLCertContext* context = new SSLCertContext(ctx); |
807 | Dart_Handle err = SetSecurityContext(args, context); |
808 | if (Dart_IsError(err)) { |
809 | delete context; |
810 | Dart_PropagateError(err); |
811 | } |
812 | } |
813 | |
814 | void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( |
815 | Dart_NativeArguments args) { |
816 | SSLCertContext* context = SSLCertContext::GetSecurityContext(args); |
817 | Dart_Handle cert_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
818 | const char* password = SSLCertContext::GetPasswordArgument(args, 2); |
819 | |
820 | ASSERT(context != NULL); |
821 | ASSERT(password != NULL); |
822 | context->SetTrustedCertificatesBytes(cert_bytes, password); |
823 | } |
824 | |
825 | void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( |
826 | Dart_NativeArguments args) { |
827 | SSLCertContext* context = SSLCertContext::GetSecurityContext(args); |
828 | Dart_Handle client_authorities_bytes = |
829 | ThrowIfError(Dart_GetNativeArgument(args, 1)); |
830 | const char* password = SSLCertContext::GetPasswordArgument(args, 2); |
831 | |
832 | ASSERT(context != NULL); |
833 | ASSERT(password != NULL); |
834 | |
835 | context->SetClientAuthoritiesBytes(client_authorities_bytes, password); |
836 | } |
837 | |
838 | void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( |
839 | Dart_NativeArguments args) { |
840 | SSLCertContext* context = SSLCertContext::GetSecurityContext(args); |
841 | Dart_Handle cert_chain_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
842 | const char* password = SSLCertContext::GetPasswordArgument(args, 2); |
843 | |
844 | ASSERT(context != NULL); |
845 | ASSERT(password != NULL); |
846 | |
847 | int status = context->UseCertificateChainBytes(cert_chain_bytes, password); |
848 | |
849 | SecureSocketUtils::CheckStatus(status, "TlsException" , |
850 | "Failure in useCertificateChainBytes" ); |
851 | } |
852 | |
853 | void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)( |
854 | Dart_NativeArguments args) { |
855 | SSLCertContext* context = SSLCertContext::GetSecurityContext(args); |
856 | |
857 | ASSERT(context != NULL); |
858 | |
859 | context->TrustBuiltinRoots(); |
860 | } |
861 | |
862 | void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) { |
863 | Dart_SetReturnValue(args, X509Helper::GetDer(args)); |
864 | } |
865 | |
866 | void FUNCTION_NAME(X509_Pem)(Dart_NativeArguments args) { |
867 | Dart_SetReturnValue(args, X509Helper::GetPem(args)); |
868 | } |
869 | |
870 | void FUNCTION_NAME(X509_Sha1)(Dart_NativeArguments args) { |
871 | Dart_SetReturnValue(args, X509Helper::GetSha1(args)); |
872 | } |
873 | |
874 | void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) { |
875 | Dart_SetReturnValue(args, X509Helper::GetSubject(args)); |
876 | } |
877 | |
878 | void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) { |
879 | Dart_SetReturnValue(args, X509Helper::GetIssuer(args)); |
880 | } |
881 | |
882 | void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) { |
883 | Dart_SetReturnValue(args, X509Helper::GetStartValidity(args)); |
884 | } |
885 | |
886 | void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) { |
887 | Dart_SetReturnValue(args, X509Helper::GetEndValidity(args)); |
888 | } |
889 | |
890 | void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)( |
891 | Dart_NativeArguments args) { |
892 | SSLCertContext* context = SSLCertContext::GetSecurityContext(args); |
893 | Dart_Handle protocols_handle = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
894 | Dart_Handle is_server_handle = ThrowIfError(Dart_GetNativeArgument(args, 2)); |
895 | if (Dart_IsBoolean(is_server_handle)) { |
896 | bool is_server = DartUtils::GetBooleanValue(is_server_handle); |
897 | SSLCertContext::SetAlpnProtocolList(protocols_handle, NULL, context, |
898 | is_server); |
899 | } else { |
900 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
901 | "Non-boolean is_server argument passed to SetAlpnProtocols" )); |
902 | } |
903 | } |
904 | |
905 | } // namespace bin |
906 | } // namespace dart |
907 | |
908 | #endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) |
909 | |