Chromium Code Reviews| Index: runtime/bin/secure_socket.cc |
| diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc |
| index 7f62fcf6adf913c003a6e38ac0a1d95f94aeef7d..0397f728d5659b0ecd709780cf4eed703320cf8e 100644 |
| --- a/runtime/bin/secure_socket.cc |
| +++ b/runtime/bin/secure_socket.cc |
| @@ -376,17 +376,62 @@ void CheckStatus(int status, |
| } |
| +static bool GetBIOArgument(Dart_Handle object, BIO** bio) { |
|
Bill Hesse
2016/02/05 14:48:17
I would call this FillMemBIO. "Argument" doesn't
zra
2016/02/05 17:18:22
Replaced these functions with MemBIOScope
|
| + ASSERT(bio != NULL); |
| + 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 = new uint8_t[bytes_len]; |
| + Dart_Handle err = |
| + Dart_ListGetAsBytes(object, 0, bytes, bytes_len); |
| + if (Dart_IsError(err)) { |
| + delete[] bytes; |
| + Dart_PropagateError(err); |
| + } |
| + } |
| + |
| + *bio = BIO_new_mem_buf(bytes, bytes_len); |
| + return is_typed_data; |
| +} |
| + |
| + |
|
Bill Hesse
2016/02/05 14:48:17
This seems really fragile (the typed data release)
zra
2016/02/05 17:18:22
Acknowledged.
|
| +static void FreeBIO(Dart_Handle object, BIO* bio, bool is_typed_data) { |
| + if (is_typed_data) { |
| + BIO_free(bio); |
| + ThrowIfError(Dart_TypedDataReleaseData(object)); |
| + } else { |
| + uint8_t* bytes; |
| + uintptr_t bytes_len; |
| + if (BIO_mem_contents( |
| + bio, const_cast<const uint8_t**>(&bytes), &bytes_len) != 0) { |
| + ASSERT(bytes != NULL); |
| + delete[] bytes; |
| + } |
| + BIO_free(bio); |
| + } |
| +} |
| + |
| + |
| 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,40 +448,16 @@ 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 = NULL; |
| + Dart_Handle key_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + bool is_typed_data = GetBIOArgument(key_object, &bio); |
| + ASSERT(bio != 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; |
| - } |
| + |
| + FreeBIO(key_object, bio, is_typed_data); |
| // TODO(24184): Handle different expected errors here - file missing, |
| // incorrect password, file not a PEM, and throw exceptions. |
| @@ -445,29 +466,48 @@ 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); |
| + |
| + BIO* bio = NULL; |
| + Dart_Handle certs_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + bool is_typed_data = GetBIOArgument(certs_object, &bio); |
| + ASSERT(bio != NULL); |
| + |
| + int status = SetTrustedCertificatesBytes(context, bio); |
| + |
| + FreeBIO(certs_object, bio, is_typed_data); |
| + CheckStatus(status, |
| + "TlsException", |
| + "Failure in setTrustedCertificatesBytes"); |
| } |
| @@ -489,17 +529,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 +543,6 @@ static int UseChainBytes( |
| } |
| if (status == 0) { |
| X509_free(x509); |
| - BIO_free(bio); |
| return status; |
| } |
| @@ -525,7 +557,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 +575,6 @@ static int UseChainBytes( |
| } |
| X509_free(x509); |
| - BIO_free(bio); |
| return status; |
| } |
| @@ -553,64 +583,67 @@ void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( |
| Dart_NativeArguments args) { |
| SSL_CTX* context = GetSecurityContext(args); |
| + BIO* bio = NULL; |
| 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>")); |
| + bool is_typed_data = GetBIOArgument(chain_object, &bio); |
| + ASSERT(bio != NULL); |
| + |
| + int status = UseChainBytes(context, bio); |
| + |
| + FreeBIO(chain_object, bio, is_typed_data); |
| + CheckStatus(status, |
| + "TlsException", |
| + "Failure in useCertificateChainBytes"); |
| +} |
| + |
| + |
| +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); |
| + |
| + BIO* bio = NULL; |
| + Dart_Handle certs_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + bool is_typed_data = GetBIOArgument(certs_object, &bio); |
| + ASSERT(bio != NULL); |
| + |
| + STACK_OF(X509_NAME)* certificate_names = GetCertificateNames(bio); |
| + |
| + FreeBIO(certs_object, bio, is_typed_data); |
| + |
| if (certificate_names != NULL) { |
| SSL_CTX_set_client_CA_list(context, certificate_names); |
| } else { |