Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(459)

Unified Diff: runtime/bin/secure_socket.cc

Issue 21716004: dart:io | Add SecureSocket.importPrivateCertificates, that reads a PKCS#12 file. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Cleanup the CL Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/bin/secure_socket.cc
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc
index ad6ae8209e48fbb292dd26ea8476b7a1ece759ec..d2d58182e641325e290503ea496c6a0c32fd02a3 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,
@@ -105,6 +108,14 @@ static void SetFilter(Dart_NativeArguments args, SSLFilter* filter) {
}
Søren Gjesse 2013/08/07 07:32:28 Maybe move this function closer to where it is use
Bill Hesse 2013/08/08 17:39:21 I have had to keep moving it up and up, as I add u
+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_Init)(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
@@ -244,7 +255,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.
@@ -259,7 +270,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 =
@@ -270,10 +281,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);
Dart_ExitScope();
}
@@ -363,6 +386,170 @@ 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) {
wtc 2013/08/06 17:52:48 Nit: fix the indentation of the second and third f
Bill Hesse 2013/08/08 17:39:21 Done.
+ *cancel = PR_TRUE;
+ return NULL;
+}
+
+
+void FUNCTION_NAME(SecureSocket_ImportPrivateCertificates)
wtc 2013/08/06 17:52:48 Certificates with corresponding private keys are c
Bill Hesse 2013/08/08 17:39:21 Done.
+ (Dart_NativeArguments args) {
+ Dart_EnterScope();
+ 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 ?
Søren Gjesse 2013/08/07 07:32:28 Yes, why + 1?
+ if (pk12 == NULL) {
+ FATAL("Out of memory in SecureSocket.importPrivateCertificates");
+ }
+ ThrowIfError(Dart_ListGetAsBytes(pk12_object, 0, pk12, length));
+
+ // A big-endian Unicode (UTF16) password.
Søren Gjesse 2013/08/07 07:32:28 So the PKCS12 functions use a password in big-endi
Søren Gjesse 2013/08/07 07:32:28 Also you could consider building this in Dart code
Bill Hesse 2013/08/08 17:39:21 The behavior depends on whether the host is big-en
+ 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));
+ 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 = (unsigned char*)password;
wtc 2013/08/06 17:52:48 Nit: reinterpret_cast<unsigned char*>(password)
Bill Hesse 2013/08/08 17:39:21 Done.
+ p12_password.len = 2 * password_length;
+
+ Dart_SetReturnValue(args, Dart_Null());
+ // Set the password callback for the certificate database we are importing to.
Søren Gjesse 2013/08/07 07:32:28 Please explain how this callback is used by nss. I
Bill Hesse 2013/08/08 17:39:21 It returns a newly allocated copy of its argument.
+ PK11_SetPasswordFunc(PasswordCallback);
+ PK11SlotInfo* slot = PK11_GetInternalKeySlot();
+ SECStatus status = PK11_Authenticate(slot, PR_TRUE, SSLFilter::GetPassword());
+ if (status == SECFailure) {
+ ThrowPRException("CertificateException",
+ "Could not authenticate to certificate database");
+ }
+
+ SEC_PKCS12DecoderContext* context = SEC_PKCS12DecoderStart(
+ &p12_password,
+ slot,
+ SSLFilter::GetPassword(),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!context) {
+ FATAL("Unexpected error: SecureSocket.addPrivateCertificates DecoderStart");
+ }
+
+ if (SECSuccess == SEC_PKCS12DecoderUpdate(context, pk12, length) &&
+ SECSuccess == SEC_PKCS12DecoderVerify(context) &&
+ SECSuccess == SEC_PKCS12DecoderValidateBags(context, nickname_callback) &&
+ SECSuccess == SEC_PKCS12DecoderImportBags(context)) {
+ SEC_PKCS12DecoderFinish(context);
Søren Gjesse 2013/08/07 07:32:28 "Hiding" the Dart_ExitScope here for the succesful
Bill Hesse 2013/08/08 17:39:21 No more EnterScope and ExitScopes.
+ Dart_ExitScope();
+ return;
+ } else {
wtc 2013/08/06 17:52:48 Nit: some projects have a convention of omitting "
Bill Hesse 2013/08/08 17:39:21 Code rewritten differently.
+ SEC_PKCS12DecoderFinish(context);
+ ThrowPRException("CertificateException",
+ "Could not import PKCS#12 file");
+ }
Søren Gjesse 2013/08/07 07:32:28 Nice use of Dart_ScopeAllocate - Only the context
Bill Hesse 2013/08/08 17:39:21 The password callback stays the same for the entir
+}
+
+
+void FUNCTION_NAME(SecureSocket_ChangeTrust)(Dart_NativeArguments args) {
+ Dart_EnterScope();
+ 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));
+
+ CERTCertificate* certificate =
+ PK11_FindCertFromNickname(nickname, SSLFilter::GetPassword());
+ if (certificate == NULL) {
+ ThrowCertificateException("Cannot find certificate with nickname %s",
+ nickname);
+ }
+ CERTCertTrust trust;
+ SECStatus status = CERT_DecodeTrustString(&trust, trust_string);
+ if (status != SECSuccess) {
+ ThrowPRException("CertificateException", "Trust string cannot be decoded");
wtc 2013/08/06 17:52:48 If these ThrowPRException calls also return from t
Bill Hesse 2013/08/08 17:39:21 Done.
+ }
+ status = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), certificate, &trust);
+ if (status != SECSuccess) {
+ ThrowCertificateException("Cannot set trust on certificate %s", nickname);
+ }
+
+ CERT_DestroyCertificate(certificate);
+ Dart_ExitScope();
+}
+
+
+void FUNCTION_NAME(SecureSocket_RemoveCertificate)(Dart_NativeArguments args) {
+ Dart_EnterScope();
+ 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());
+ if (status == SECFailure) {
+ CERT_DestroyCertificate(certificate);
+ ThrowCertificateException("Cannot remove certificate %s", nickname);
+ }
wtc 2013/08/06 17:52:48 CERT_DestroyCertificate(certificate) should also b
Bill Hesse 2013/08/08 17:39:21 Done.
+ Dart_ExitScope();
+}
+
void FUNCTION_NAME(SecureSocket_PeerCertificate)
(Dart_NativeArguments args) {
@@ -516,7 +703,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"));
@@ -589,14 +776,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\"";
@@ -613,6 +792,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;
@@ -636,7 +816,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;
}
@@ -661,6 +841,16 @@ void SSLFilter::InitializeLibrary(const char* certificate_database,
ThrowPRException("TlsException",
"Failed NSS_SetDomesticPolicy call.");
}
+
+ // Allow encoding and decoding of private keys in PKCS#12 files (.pk files).
wtc 2013/08/06 17:52:48 Common file suffixes for PKCS #11 files are .p12 a
+ SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
+ SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
+ SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
+ SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
+ SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
+ SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
+ SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
wtc 2013/08/06 17:52:48 I know these function calls are modeled after the
Bill Hesse 2013/08/08 17:39:21 Done.
+
// Enable TLS, as well as SSL3 and SSL2.
status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
if (status != SECSuccess) {
@@ -751,23 +941,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());
wtc 2013/08/06 17:52:48 You don't need to pass the password as the "void *
if (key == NULL) {
CERT_DestroyCertificate(certificate);
if (PR_GetError() == -8177) {

Powered by Google App Engine
This is Rietveld 408576698