| Index: runtime/bin/secure_socket.cc
|
| diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc
|
| index 7f62fcf6adf913c003a6e38ac0a1d95f94aeef7d..deae533e3e89512b54629291a83ef03a0416c20f 100644
|
| --- a/runtime/bin/secure_socket.cc
|
| +++ b/runtime/bin/secure_socket.cc
|
| @@ -376,17 +376,79 @@ void CheckStatus(int status,
|
| }
|
|
|
|
|
| +// Where the argument to the constructor is the handle for an object
|
| +// implementing List<int>, this class creates a scope in which a memory-backed
|
| +// BIO is allocated. Leaving the scope cleans up the BIO and the buffer that
|
| +// was used to create it.
|
| +//
|
| +// Do not make Dart_ API calls while in a MemBIOScope.
|
| +// Do not call Dart_PropagateError while in a MemBIOScope.
|
| +class MemBIOScope {
|
| + public:
|
| + explicit MemBIOScope(Dart_Handle object) {
|
| + if (!Dart_IsTypedData(object) && !Dart_IsList(object)) {
|
| + Dart_ThrowException(DartUtils::NewDartArgumentError(
|
| + "Argument is not a List<int>"));
|
| + }
|
| +
|
| + uint8_t* bytes = NULL;
|
| + intptr_t bytes_len = 0;
|
| + bool is_typed_data = false;
|
| + if (Dart_IsTypedData(object)) {
|
| + is_typed_data = true;
|
| + Dart_TypedData_Type typ;
|
| + ThrowIfError(Dart_TypedDataAcquireData(
|
| + object,
|
| + &typ,
|
| + reinterpret_cast<void**>(&bytes),
|
| + &bytes_len));
|
| + } else {
|
| + ASSERT(Dart_IsList(object));
|
| + ThrowIfError(Dart_ListLength(object, &bytes_len));
|
| + bytes = Dart_ScopeAllocate(bytes_len);
|
| + ASSERT(bytes != NULL);
|
| + ThrowIfError(Dart_ListGetAsBytes(object, 0, bytes, bytes_len));
|
| + }
|
| +
|
| + object_ = object;
|
| + bytes_ = bytes;
|
| + bytes_len_ = bytes_len_;
|
| + bio_ = BIO_new_mem_buf(bytes, bytes_len);
|
| + ASSERT(bio_ != NULL);
|
| + is_typed_data_ = is_typed_data;
|
| + }
|
| +
|
| + ~MemBIOScope() {
|
| + ASSERT(bio_ != NULL);
|
| + if (is_typed_data_) {
|
| + BIO_free(bio_);
|
| + ThrowIfError(Dart_TypedDataReleaseData(object_));
|
| + } else {
|
| + BIO_free(bio_);
|
| + }
|
| + }
|
| +
|
| + BIO* bio() {
|
| + ASSERT(bio_ != NULL);
|
| + return bio_;
|
| + }
|
| +
|
| + private:
|
| + Dart_Handle object_;
|
| + uint8_t* bytes_;
|
| + intptr_t bytes_len_;
|
| + BIO* bio_;
|
| + bool is_typed_data_;
|
| +
|
| + DISALLOW_ALLOCATION();
|
| + DISALLOW_COPY_AND_ASSIGN(MemBIOScope);
|
| +};
|
| +
|
| +
|
| void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
|
| Dart_NativeArguments args) {
|
| SSL_CTX* context = GetSecurityContext(args);
|
|
|
| - Dart_Handle key_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
|
| - if (!Dart_IsTypedData(key_object) && !Dart_IsList(key_object)) {
|
| - Dart_ThrowException(DartUtils::NewDartArgumentError(
|
| - "keyBytes argument to SecurityContext.usePrivateKeyBytes "
|
| - "is not a List<int>"));
|
| - }
|
| -
|
| Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, 2));
|
| const char* password = NULL;
|
| if (Dart_IsString(password_object)) {
|
| @@ -403,39 +465,12 @@ void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
|
| "SecurityContext.usePrivateKey password is not a String or null"));
|
| }
|
|
|
| - uint8_t* key_bytes = NULL;
|
| - intptr_t key_bytes_len = 0;
|
| - bool is_typed_data = false;
|
| - if (Dart_IsTypedData(key_object)) {
|
| - is_typed_data = true;
|
| - Dart_TypedData_Type typ;
|
| - ThrowIfError(Dart_TypedDataAcquireData(
|
| - key_object,
|
| - &typ,
|
| - reinterpret_cast<void**>(&key_bytes),
|
| - &key_bytes_len));
|
| - } else {
|
| - ASSERT(Dart_IsList(key_object));
|
| - ThrowIfError(Dart_ListLength(key_object, &key_bytes_len));
|
| - key_bytes = new uint8_t[key_bytes_len];
|
| - Dart_Handle err =
|
| - Dart_ListGetAsBytes(key_object, 0, key_bytes, key_bytes_len);
|
| - if (Dart_IsError(err)) {
|
| - delete[] key_bytes;
|
| - Dart_PropagateError(err);
|
| - }
|
| - }
|
| - ASSERT(key_bytes != NULL);
|
| -
|
| - BIO* bio = BIO_new_mem_buf(key_bytes, key_bytes_len);
|
| - EVP_PKEY *key = PEM_read_bio_PrivateKey(
|
| - bio, NULL, PasswordCallback, const_cast<char*>(password));
|
| - int status = SSL_CTX_use_PrivateKey(context, key);
|
| - BIO_free(bio);
|
| - if (is_typed_data) {
|
| - ThrowIfError(Dart_TypedDataReleaseData(key_object));
|
| - } else {
|
| - delete[] key_bytes;
|
| + int status;
|
| + {
|
| + MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
|
| + EVP_PKEY *key = PEM_read_bio_PrivateKey(
|
| + bio.bio(), NULL, PasswordCallback, const_cast<char*>(password));
|
| + status = SSL_CTX_use_PrivateKey(context, key);
|
| }
|
|
|
| // TODO(24184): Handle different expected errors here - file missing,
|
| @@ -445,29 +480,44 @@ void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
|
| }
|
|
|
|
|
| -void FUNCTION_NAME(SecurityContext_SetTrustedCertificates)(
|
| - Dart_NativeArguments args) {
|
| - SSL_CTX* context = GetSecurityContext(args);
|
| - Dart_Handle filename_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
|
| - const char* filename = NULL;
|
| - if (Dart_IsString(filename_object)) {
|
| - ThrowIfError(Dart_StringToCString(filename_object, &filename));
|
| +static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) {
|
| + X509_STORE* store = SSL_CTX_get_cert_store(context);
|
| +
|
| + int status = 0;
|
| + X509* cert = NULL;
|
| + while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
|
| + status = X509_STORE_add_cert(store, cert);
|
| + if (status == 0) {
|
| + X509_free(cert);
|
| + return status;
|
| + }
|
| }
|
| - Dart_Handle directory_object = ThrowIfError(Dart_GetNativeArgument(args, 2));
|
| - const char* directory = NULL;
|
| - if (Dart_IsString(directory_object)) {
|
| - ThrowIfError(Dart_StringToCString(directory_object, &directory));
|
| - } else if (Dart_IsNull(directory_object)) {
|
| - directory = NULL;
|
| +
|
| + uint32_t err = ERR_peek_last_error();
|
| + if ((ERR_GET_LIB(err) == ERR_LIB_PEM) &&
|
| + (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
|
| + // Reached the end of the buffer.
|
| + ERR_clear_error();
|
| } else {
|
| - Dart_ThrowException(DartUtils::NewDartArgumentError(
|
| - "Directory argument to SecurityContext.setTrustedCertificates is not "
|
| - "a String or null"));
|
| + // Some real error happened.
|
| + status = 0;
|
| }
|
|
|
| - int status = SSL_CTX_load_verify_locations(context, filename, directory);
|
| - CheckStatus(
|
| - status, "TlsException", "SSL_CTX_load_verify_locations");
|
| + return status;
|
| +}
|
| +
|
| +
|
| +void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)(
|
| + Dart_NativeArguments args) {
|
| + SSL_CTX* context = GetSecurityContext(args);
|
| + int status;
|
| + {
|
| + MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
|
| + status = SetTrustedCertificatesBytes(context, bio.bio());
|
| + }
|
| + CheckStatus(status,
|
| + "TlsException",
|
| + "Failure in setTrustedCertificatesBytes");
|
| }
|
|
|
|
|
| @@ -489,17 +539,10 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
|
| }
|
|
|
|
|
| -static int UseChainBytes(
|
| - SSL_CTX* context, uint8_t* chain_bytes, intptr_t chain_bytes_len) {
|
| +static int UseChainBytes(SSL_CTX* context, BIO* bio) {
|
| int status = 0;
|
| - BIO* bio = BIO_new_mem_buf(chain_bytes, chain_bytes_len);
|
| - if (bio == NULL) {
|
| - return 0;
|
| - }
|
| -
|
| X509* x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
| if (x509 == NULL) {
|
| - BIO_free(bio);
|
| return 0;
|
| }
|
|
|
| @@ -510,7 +553,6 @@ static int UseChainBytes(
|
| }
|
| if (status == 0) {
|
| X509_free(x509);
|
| - BIO_free(bio);
|
| return status;
|
| }
|
|
|
| @@ -525,7 +567,6 @@ static int UseChainBytes(
|
| if (status == 0) {
|
| X509_free(ca);
|
| X509_free(x509);
|
| - BIO_free(bio);
|
| return status;
|
| }
|
| // Note that we must not free `ca` if it was successfully added to the
|
| @@ -544,7 +585,6 @@ static int UseChainBytes(
|
| }
|
|
|
| X509_free(x509);
|
| - BIO_free(bio);
|
| return status;
|
| }
|
|
|
| @@ -552,65 +592,61 @@ static int UseChainBytes(
|
| void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
|
| Dart_NativeArguments args) {
|
| SSL_CTX* context = GetSecurityContext(args);
|
| + int status;
|
| + {
|
| + MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
|
| + status = UseChainBytes(context, bio.bio());
|
| + }
|
| + CheckStatus(status,
|
| + "TlsException",
|
| + "Failure in useCertificateChainBytes");
|
| +}
|
|
|
| - Dart_Handle chain_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
|
| - if (!Dart_IsTypedData(chain_object) && !Dart_IsList(chain_object)) {
|
| - Dart_ThrowException(DartUtils::NewDartArgumentError(
|
| - "chainBytes argument to SecurityContext.useCertificateChainBytes "
|
| - "is not a List<int>"));
|
| +
|
| +static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio) {
|
| + STACK_OF(X509_NAME)* result = sk_X509_NAME_new_null();
|
| + if (result == NULL) {
|
| + return NULL;
|
| }
|
|
|
| - uint8_t* chain_bytes = NULL;
|
| - intptr_t chain_bytes_len = 0;
|
| - bool is_typed_data = false;
|
| - if (Dart_IsTypedData(chain_object)) {
|
| - is_typed_data = true;
|
| - Dart_TypedData_Type typ;
|
| - ThrowIfError(Dart_TypedDataAcquireData(
|
| - chain_object,
|
| - &typ,
|
| - reinterpret_cast<void**>(&chain_bytes),
|
| - &chain_bytes_len));
|
| - } else {
|
| - ASSERT(Dart_IsList(chain_object));
|
| - ThrowIfError(Dart_ListLength(chain_object, &chain_bytes_len));
|
| - chain_bytes = new uint8_t[chain_bytes_len];
|
| - Dart_Handle err =
|
| - Dart_ListGetAsBytes(chain_object, 0, chain_bytes, chain_bytes_len);
|
| - if (Dart_IsError(err)) {
|
| - delete[] chain_bytes;
|
| - Dart_PropagateError(err);
|
| + while (true) {
|
| + X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
| + if (x509 == NULL) {
|
| + break;
|
| }
|
| - }
|
| - ASSERT(chain_bytes != NULL);
|
|
|
| - int status = UseChainBytes(context, chain_bytes, chain_bytes_len);
|
| + X509_NAME* x509_name = X509_get_subject_name(x509);
|
| + if (x509_name == NULL) {
|
| + sk_X509_NAME_pop_free(result, X509_NAME_free);
|
| + X509_free(x509);
|
| + return NULL;
|
| + }
|
|
|
| - if (is_typed_data) {
|
| - ThrowIfError(Dart_TypedDataReleaseData(chain_object));
|
| - } else {
|
| - delete[] chain_bytes;
|
| + // Duplicate the name to put it on the stack.
|
| + x509_name = X509_NAME_dup(x509_name);
|
| + if (x509_name == NULL) {
|
| + sk_X509_NAME_pop_free(result, X509_NAME_free);
|
| + X509_free(x509);
|
| + return NULL;
|
| + }
|
| + sk_X509_NAME_push(result, x509_name);
|
| + X509_free(x509);
|
| }
|
| - CheckStatus(status,
|
| - "TlsException",
|
| - "Failure in useCertificateChainBytes");
|
| +
|
| + return result;
|
| }
|
|
|
|
|
| -void FUNCTION_NAME(SecurityContext_SetClientAuthorities)(
|
| +void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)(
|
| Dart_NativeArguments args) {
|
| SSL_CTX* context = GetSecurityContext(args);
|
| - Dart_Handle filename_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
|
| - const char* filename = NULL;
|
| - if (Dart_IsString(filename_object)) {
|
| - ThrowIfError(Dart_StringToCString(filename_object, &filename));
|
| - } else {
|
| - Dart_ThrowException(DartUtils::NewDartArgumentError(
|
| - "file argument in SecurityContext.setClientAuthorities"
|
| - " is not a String"));
|
| - }
|
| STACK_OF(X509_NAME)* certificate_names;
|
| - certificate_names = SSL_load_client_CA_file(filename);
|
| +
|
| + {
|
| + MemBIOScope bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
|
| + certificate_names = GetCertificateNames(bio.bio());
|
| + }
|
| +
|
| if (certificate_names != NULL) {
|
| SSL_CTX_set_client_CA_list(context, certificate_names);
|
| } else {
|
|
|