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
32namespace dart {
33namespace bin {
34
35const char* SSLCertContext::root_certs_file_ = NULL;
36const char* SSLCertContext::root_certs_cache_ = NULL;
37
38int 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
82SSLCertContext* 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
96static void DeleteSecurityContext(void* isolate_data, void* context_pointer) {
97 SSLCertContext* context = static_cast<SSLCertContext*>(context_pointer);
98 context->Release();
99}
100
101static 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
116static void ReleaseCertificate(void* isolate_data, void* context_pointer) {
117 X509* cert = reinterpret_cast<X509*>(context_pointer);
118 X509_free(cert);
119}
120
121static 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.
129Dart_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
163static 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
190static 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
212void 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
233static 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
259static 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
272static 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
289void 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
302void 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
318void 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
348void 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
364int 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
371static 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
391static 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
411const 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
431int 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.
461void 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
518static 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
557static 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
592static 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
609int 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
615static 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
629Dart_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
666Dart_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
689Dart_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
725Dart_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
738Dart_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
751static 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
765Dart_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
771Dart_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
777void 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
800void 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
814void 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
825void 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
838void 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
853void 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
862void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) {
863 Dart_SetReturnValue(args, X509Helper::GetDer(args));
864}
865
866void FUNCTION_NAME(X509_Pem)(Dart_NativeArguments args) {
867 Dart_SetReturnValue(args, X509Helper::GetPem(args));
868}
869
870void FUNCTION_NAME(X509_Sha1)(Dart_NativeArguments args) {
871 Dart_SetReturnValue(args, X509Helper::GetSha1(args));
872}
873
874void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
875 Dart_SetReturnValue(args, X509Helper::GetSubject(args));
876}
877
878void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) {
879 Dart_SetReturnValue(args, X509Helper::GetIssuer(args));
880}
881
882void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) {
883 Dart_SetReturnValue(args, X509Helper::GetStartValidity(args));
884}
885
886void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) {
887 Dart_SetReturnValue(args, X509Helper::GetEndValidity(args));
888}
889
890void 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