Chromium Code Reviews| Index: runtime/bin/secure_socket.cc |
| diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc |
| index 8a451018b20821fed22cf843b2097aecb4042e25..8ec84d6686af791f986cde3c6ba60ca4d4fe622c 100644 |
| --- a/runtime/bin/secure_socket.cc |
| +++ b/runtime/bin/secure_socket.cc |
| @@ -10,9 +10,12 @@ |
| #include <stdio.h> |
| #include <string.h> |
| +#include <certdb.h> |
| #include <key.h> |
| #include <keyt.h> |
| #include <nss.h> |
| +#include <p12.h> |
| +#include <p12plcy.h> |
| #include <pk11pub.h> |
| #include <prerror.h> |
| #include <prinit.h> |
| @@ -40,7 +43,7 @@ bool SSLFilter::library_initialized_ = false; |
| dart::Mutex* SSLFilter::mutex_ = new dart::Mutex(); |
| // The password is needed when creating secure server sockets. It can |
| // be null if only secure client sockets are used. |
| -const char* SSLFilter::password_ = NULL; |
| +char* SSLFilter::password_ = NULL; |
| // Forward declaration. |
| static void ProcessFilter(Dart_Port dest_port_id, |
| @@ -229,7 +232,7 @@ void FUNCTION_NAME(SecureSocket_InitializeLibrary) |
| &certificate_database)); |
| } else if (!Dart_IsNull(certificate_database_object)) { |
| Dart_ThrowException(DartUtils::NewDartArgumentError( |
| - "Non-String certificate directory argument to SetCertificateDatabase")); |
| + "SecureSocket.initialize: database argument is not a String or null")); |
| } |
| // Leave certificate_database as NULL if no value was provided. |
| @@ -244,7 +247,7 @@ void FUNCTION_NAME(SecureSocket_InitializeLibrary) |
| password = ""; |
| } else { |
| Dart_ThrowException(DartUtils::NewDartArgumentError( |
| - "Password argument to SetCertificateDatabase is not a String or null")); |
| + "SecureSocket.initialize: password argument is not a String or null")); |
| } |
| Dart_Handle builtin_roots_object = |
| @@ -255,10 +258,22 @@ void FUNCTION_NAME(SecureSocket_InitializeLibrary) |
| ThrowIfError(Dart_BooleanValue(builtin_roots_object, &builtin_roots)); |
| } else { |
| Dart_ThrowException(DartUtils::NewDartArgumentError( |
| - "UseBuiltinRoots argument to SetCertificateDatabase is not a bool")); |
| + "SecureSocket.initialize: useBuiltinRoots argument is not a bool")); |
| } |
| - SSLFilter::InitializeLibrary(certificate_database, password, builtin_roots); |
| + Dart_Handle read_only_object = |
| + ThrowIfError(Dart_GetNativeArgument(args, 3)); |
| + // Check that the type is boolean, and get the boolean value from it. |
| + bool read_only = true; |
| + if (Dart_IsBoolean(read_only_object)) { |
| + ThrowIfError(Dart_BooleanValue(read_only_object, &read_only)); |
| + } else { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.initialize: readOnly argument is not a bool")); |
| + } |
| + |
| + SSLFilter::InitializeLibrary( |
| + certificate_database, password, builtin_roots, read_only); |
| } |
| @@ -300,6 +315,14 @@ static Dart_Handle X509FromCertificate(CERTCertificate* certificate) { |
| } |
| +char* PasswordCallback(PK11SlotInfo* slot, PRBool retry, void* arg) { |
| + if (!retry) { |
| + return PL_strdup(static_cast<char*>(arg)); // Freed by NSS internals. |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| void FUNCTION_NAME(SecureSocket_AddCertificate) |
| (Dart_NativeArguments args) { |
| Dart_Handle certificate_object = |
| @@ -324,13 +347,22 @@ void FUNCTION_NAME(SecureSocket_AddCertificate) |
| ThrowIfError(Dart_StringToCString(trust_object, |
| &trust_string)); |
| + PK11_SetPasswordFunc(PasswordCallback); |
|
wtc
2013/08/08 20:01:13
We should ideally call PK11_SetPasswordFunc just o
Bill Hesse
2013/08/09 13:24:05
No, it isn't. Removed.
|
| + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); |
| + SECStatus status = PK11_Authenticate(slot, PR_TRUE, SSLFilter::GetPassword()); |
| + if (status == SECFailure) { |
| + ThrowPRException("CertificateException", |
| + "Could not authenticate to certificate database"); |
| + } |
| + PK11_FreeSlot(slot); |
|
wtc
2013/08/08 20:01:13
Free |slot| before lie 353.
Bill Hesse
2013/08/09 13:24:05
Done.
|
| + |
| CERTCertificate* cert = CERT_DecodeCertFromPackage( |
| reinterpret_cast<char*>(certificate), length); |
| if (cert == NULL) { |
| ThrowPRException("CertificateException", "Certificate cannot be decoded"); |
| } |
| CERTCertTrust trust; |
| - SECStatus status = CERT_DecodeTrustString(&trust, trust_string); |
| + status = CERT_DecodeTrustString(&trust, trust_string); |
| if (status != SECSuccess) { |
| ThrowPRException("CertificateException", "Trust string cannot be decoded"); |
| } |
| @@ -345,6 +377,194 @@ void FUNCTION_NAME(SecureSocket_AddCertificate) |
| } |
| +/* |
| + * Called by the PKCS#12 decoder if a certificate's nickname collides with |
| + * the nickname of a different existing certificate in the database. |
| + */ |
| +SECItem* nickname_callback(SECItem *old_nickname, |
| + PRBool *cancel, |
| + void *arg) { |
| + *cancel = PR_TRUE; |
| + return NULL; |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_ImportCertificatesWithPrivateKeys) |
| + (Dart_NativeArguments args) { |
| + Dart_Handle pk12_object = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + if (!Dart_IsList(pk12_object)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.importPrivateCertificates: certificates is not a List")); |
| + } |
| + |
| + Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + if (!Dart_IsString(password_object)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.importPrivateCertificates: password is not a String")); |
| + } |
| + |
| + intptr_t length; |
| + ThrowIfError(Dart_ListLength(pk12_object, &length)); |
| + uint8_t* pk12 = Dart_ScopeAllocate(length + 1); // Why +1 ? |
|
wtc
2013/08/08 20:01:13
Can you try removing "+ 1"?
Bill Hesse
2013/08/09 13:24:05
Done.
|
| + if (pk12 == NULL) { |
| + FATAL("Out of memory in SecureSocket.importPrivateCertificates"); |
| + } |
| + ThrowIfError(Dart_ListGetAsBytes(pk12_object, 0, pk12, length)); |
| + |
| + // A big-endian Unicode (UTF16) password. |
| + intptr_t password_length; |
| + ThrowIfError(Dart_StringLength(password_object, &password_length)); |
| + password_length++; |
| + uint16_t* password = reinterpret_cast<uint16_t*>( |
| + Dart_ScopeAllocate(2 * password_length)); |
|
wtc
2013/08/08 20:01:13
Nit: replace 2 with sizeof(uint16_t). You may igno
Bill Hesse
2013/08/09 13:24:05
Done.
|
| + if (password == NULL) { |
| + FATAL("Out of memory in SecureSocket.importPrivateCertificates"); |
| + } |
| + intptr_t returned_length = password_length; |
| + ThrowIfError(Dart_StringToUTF16(password_object, password, &returned_length)); |
| + ASSERT(password_length == returned_length + 1); |
| + password[password_length - 1] = 0; |
| + for (int i = 0; i < password_length; ++i) { |
| + password[i] = Utils::HostToBigEndian16(password[i]); |
| + } |
| + SECItem p12_password; |
| + p12_password.type = siBuffer; |
| + p12_password.data = reinterpret_cast<unsigned char*>(password); |
| + p12_password.len = 2 * password_length; |
|
wtc
2013/08/08 20:01:13
Nit: same here: replace 2 with sizeof(uint16_t).
Bill Hesse
2013/08/09 13:24:05
Done.
|
| + |
| + Dart_SetReturnValue(args, Dart_Null()); |
| + // Set the password callback for the certificate database we are importing to. |
| + // The password for a slot is gotten from a callback, and it is freed by the |
| + // caller of the callback. The argument to the callback comes from the wincx |
| + // argument to a PK11 function. |
| + PK11_SetPasswordFunc(PasswordCallback); |
| + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); |
| + SECStatus status = PK11_Authenticate(slot, PR_TRUE, SSLFilter::GetPassword()); |
| + if (status == SECFailure) { |
| + PK11_FreeSlot(slot); |
| + ThrowPRException("CertificateException", |
| + "Could not authenticate to certificate database"); |
| + } |
| + |
| + SEC_PKCS12DecoderContext* context = SEC_PKCS12DecoderStart( |
| + &p12_password, |
| + slot, |
| + SSLFilter::GetPassword(), |
| + NULL, |
| + NULL, |
| + NULL, |
| + NULL, |
| + NULL); |
| + PK11_FreeSlot(slot); |
| + if (!context) { |
| + FATAL("Unexpected error: SecureSocket.addPrivateCertificates DecoderStart"); |
| + } |
| + |
| + bool success = |
| + SECSuccess == SEC_PKCS12DecoderUpdate(context, pk12, length) && |
| + SECSuccess == SEC_PKCS12DecoderVerify(context) && |
| + SECSuccess == SEC_PKCS12DecoderValidateBags(context, nickname_callback) && |
| + SECSuccess == SEC_PKCS12DecoderImportBags(context); |
| + SEC_PKCS12DecoderFinish(context); |
| + if (!success) { |
| + ThrowPRException("CertificateException", "Could not import PKCS#12 file"); |
| + } |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_ChangeTrust)(Dart_NativeArguments args) { |
| + Dart_Handle nickname_object = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + if (!Dart_IsString(nickname_object)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.changeTrust: nickname argument is not a String")); |
| + } |
| + const char* nickname; |
| + ThrowIfError(Dart_StringToCString(nickname_object, &nickname)); |
| + |
| + Dart_Handle trust_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + if (!Dart_IsString(trust_object)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.changeTrust: trust argument is not a String")); |
| + } |
| + const char* trust_string; |
| + ThrowIfError(Dart_StringToCString(trust_object, &trust_string)); |
| + |
| + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); |
| + SECStatus status = PK11_Authenticate(slot, PR_TRUE, SSLFilter::GetPassword()); |
| + if (status == SECFailure) { |
| + ThrowPRException("CertificateException", |
| + "Could not authenticate to certificate database"); |
| + } |
| + PK11_FreeSlot(slot); |
| + |
| + CERTCertificate* certificate = |
| + PK11_FindCertFromNickname(nickname, SSLFilter::GetPassword()); |
| + if (certificate == NULL) { |
| + ThrowCertificateException("Cannot find certificate with nickname %s", |
| + nickname); |
| + } |
| + CERTCertTrust trust; |
| + if (SECSuccess != CERT_DecodeTrustString(&trust, trust_string)) { |
| + CERT_DestroyCertificate(certificate); |
| + ThrowPRException("CertificateException", "Trust string cannot be decoded"); |
| + } |
| + if (SECSuccess != |
| + CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), certificate, &trust)) { |
| + CERT_DestroyCertificate(certificate); |
| + ThrowCertificateException("Cannot set trust on certificate %s", nickname); |
| + } |
| + Dart_SetReturnValue(args, X509FromCertificate(certificate)); |
| + CERT_DestroyCertificate(certificate); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_GetCertificate)(Dart_NativeArguments args) { |
| + Dart_Handle nickname_object = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + if (!Dart_IsString(nickname_object)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.getCertificate: nickname argument is not a String")); |
| + } |
| + const char* nickname; |
| + ThrowIfError(Dart_StringToCString(nickname_object, &nickname)); |
| + |
| + CERTCertificate* certificate = PK11_FindCertFromNickname( |
| + nickname, SSLFilter::GetPassword()); |
| + if (certificate != NULL) { |
| + Dart_SetReturnValue(args, X509FromCertificate(certificate)); |
| + CERT_DestroyCertificate(certificate); |
| + } |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_RemoveCertificate)(Dart_NativeArguments args) { |
| + Dart_Handle nickname_object = |
| + ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + if (!Dart_IsString(nickname_object)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "SecureSocket.removeCertificate: nickname is not a String")); |
| + } |
| + const char* nickname; |
| + ThrowIfError(Dart_StringToCString(nickname_object, &nickname)); |
| + |
| + CERTCertificate* certificate = |
| + PK11_FindCertFromNickname(nickname, SSLFilter::GetPassword()); |
| + if (certificate == NULL) { |
| + ThrowCertificateException("Cannot find certificate with nickname %s", |
| + nickname); |
| + } |
| + SECKEYPrivateKey* key = |
| + PK11_FindKeyByAnyCert(certificate, SSLFilter::GetPassword()); |
| + // Free the copy returned from FindKeyByAnyCert. |
| + SECKEY_DestroyPrivateKey(key); |
| + SECStatus status = (key == NULL) ? |
| + SEC_DeletePermCertificate(certificate) : |
| + PK11_DeleteTokenCertAndKey(certificate, SSLFilter::GetPassword()); |
| + CERT_DestroyCertificate(certificate); |
| + if (status != SECSuccess) { |
| + ThrowCertificateException("Cannot remove certificate %s", nickname); |
| + } |
| +} |
| + |
| void FUNCTION_NAME(SecureSocket_PeerCertificate) |
| (Dart_NativeArguments args) { |
| @@ -494,7 +714,7 @@ bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], |
| void SSLFilter::Init(Dart_Handle dart_this) { |
| if (!library_initialized_) { |
| - InitializeLibrary(NULL, "", true, false); |
| + InitializeLibrary(NULL, "", true, true, false); |
| } |
| ASSERT(string_start_ == NULL); |
| string_start_ = Dart_NewPersistentHandle(DartUtils::NewString("start")); |
| @@ -567,14 +787,6 @@ void SSLFilter::RegisterBadCertificateCallback(Dart_Handle callback) { |
| } |
| -char* PasswordCallback(PK11SlotInfo* slot, PRBool retry, void* arg) { |
| - if (!retry) { |
| - return PL_strdup(static_cast<char*>(arg)); // Freed by NSS internals. |
| - } |
| - return NULL; |
| -} |
| - |
| - |
| static const char* builtin_roots_module = |
| #if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) |
| "name=\"Root Certs\" library=\"libnssckbi.so\""; |
| @@ -591,6 +803,7 @@ static const char* builtin_roots_module = |
| void SSLFilter::InitializeLibrary(const char* certificate_database, |
| const char* password, |
| bool use_builtin_root_certificates, |
| + bool read_only, |
| bool report_duplicate_initialization) { |
| MutexLocker locker(mutex_); |
| SECStatus status; |
| @@ -614,7 +827,7 @@ void SSLFilter::InitializeLibrary(const char* certificate_database, |
| } |
| } |
| } else { |
| - PRUint32 init_flags = NSS_INIT_READONLY; |
| + PRUint32 init_flags = read_only ? NSS_INIT_READONLY : 0; |
| if (!use_builtin_root_certificates) { |
| init_flags |= NSS_INIT_NOMODDB; |
| } |
| @@ -633,12 +846,18 @@ void SSLFilter::InitializeLibrary(const char* certificate_database, |
| } |
| library_initialized_ = true; |
| + // Allow encoding and decoding of private keys in PKCS#12 files. |
| + SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1); |
|
wtc
2013/08/08 20:01:13
I assume you found PKCS12_RC2_CBC_40 is required.
Bill Hesse
2013/08/09 13:24:05
Yes. The certificates are encrypted with RC2, the
|
| + SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1); |
| + SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1); |
| + |
| status = NSS_SetDomesticPolicy(); |
| if (status != SECSuccess) { |
| mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
| ThrowPRException("TlsException", |
| "Failed NSS_SetDomesticPolicy call."); |
| } |
| + |
| // Enable TLS, as well as SSL3 and SSL2. |
| status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE); |
| if (status != SECSuccess) { |
| @@ -729,23 +948,20 @@ void SSLFilter::Connect(const char* host_name, |
| const_cast<char*>(certificate_name)); |
| if (certificate == NULL) { |
| ThrowCertificateException( |
| - "Cannot find server certificate by distinguished name: %s", |
| + "Cannot find server certificate with distinguished name %s", |
| certificate_name); |
| } |
| } else { |
| // Look up certificate using the nickname certificate_name. |
| certificate = PK11_FindCertFromNickname( |
| - const_cast<char*>(certificate_name), |
| - static_cast<void*>(const_cast<char*>(password_))); |
| + const_cast<char*>(certificate_name), GetPassword()); |
| if (certificate == NULL) { |
| ThrowCertificateException( |
| - "Cannot find server certificate by nickname: %s", |
| + "Cannot find server certificate with nickname %s", |
| certificate_name); |
| } |
| } |
| - SECKEYPrivateKey* key = PK11_FindKeyByAnyCert( |
| - certificate, |
| - static_cast<void*>(const_cast<char*>(password_))); |
| + SECKEYPrivateKey* key = PK11_FindKeyByAnyCert(certificate, GetPassword()); |
| if (key == NULL) { |
| CERT_DestroyCertificate(certificate); |
| if (PR_GetError() == -8177) { |