dart:io | Change the way SecureSocket initializes the NSS library with an empty database.

BUG=dartbug.com/7104

Review URL: https://codereview.chromium.org//13985012

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21363 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc
index 0f9140b..e66ff35 100644
--- a/runtime/bin/secure_socket.cc
+++ b/runtime/bin/secure_socket.cc
@@ -17,6 +17,7 @@
 #include <prerror.h>
 #include <prinit.h>
 #include <prnetdb.h>
+#include <secmod.h>
 #include <ssl.h>
 #include <sslproto.h>
 
@@ -333,36 +334,57 @@
   bad_certificate_callback_ = ThrowIfError(Dart_NewPersistentHandle(callback));
 }
 
+static const char* builtin_roots_module =
+#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID)
+    "name=\"Root Certs\" library=\"libnssckbi.so\"";
+#elif defined(TARGET_OS_MACOS)
+    "name=\"Root Certs\" library=\"libnssckbi.dylib\"";
+#elif defined(TARGET_OS_WINDOWS)
+    "name=\"Root Certs\" library=\"nssckbi.dll\"";
+#else
+#error Automatic target os detection failed.
+#endif
+
+
 
 void SSLFilter::InitializeLibrary(const char* certificate_database,
                                   const char* password,
                                   bool use_builtin_root_certificates,
                                   bool report_duplicate_initialization) {
   MutexLocker locker(&mutex_);
+  SECStatus status;
   if (!library_initialized_) {
     password_ = strdup(password);  // This one copy persists until Dart exits.
     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
     // TODO(whesse): Verify there are no UTF-8 issues here.
-    PRUint32 init_flags = NSS_INIT_READONLY;
-    if (certificate_database == NULL) {
-      // Passing the empty string as the database path does not try to open
-      // a database in the current directory.
-      certificate_database = "";
-      // The flag NSS_INIT_NOCERTDB is documented to do what we want here,
-      // however it causes the builtins not to be available on Windows.
-      init_flags |= NSS_INIT_FORCEOPEN;
-    }
-    if (!use_builtin_root_certificates) {
-      init_flags |= NSS_INIT_NOMODDB;
-    }
-    SECStatus status = NSS_Initialize(certificate_database,
-                                      "",
-                                      "",
-                                      SECMOD_DB,
-                                      init_flags);
-    if (status != SECSuccess) {
-      mutex_.Unlock();  // MutexLocker destructor not called when throwing.
-      ThrowPRException("Failed NSS_Init call.");
+    if (certificate_database == NULL || certificate_database[0] == '\0') {
+      status = NSS_NoDB_Init(NULL);
+      if (status != SECSuccess) {
+        mutex_.Unlock();  // MutexLocker destructor not called when throwing.
+        ThrowPRException("Failed NSS_NoDB_Init call.");
+      }
+      if (use_builtin_root_certificates) {
+        SECMODModule* module = SECMOD_LoadUserModule(
+            const_cast<char*>(builtin_roots_module), NULL, PR_FALSE);
+        if (!module) {
+          mutex_.Unlock();  // MutexLocker destructor not called when throwing.
+          ThrowPRException("Failed to load builtin root certificates.");
+        }
+      }
+    } else {
+      PRUint32 init_flags = NSS_INIT_READONLY;
+      if (!use_builtin_root_certificates) {
+        init_flags |= NSS_INIT_NOMODDB;
+      }
+      status = NSS_Initialize(certificate_database,
+                              "",
+                              "",
+                              SECMOD_DB,
+                              init_flags);
+      if (status != SECSuccess) {
+        mutex_.Unlock();  // MutexLocker destructor not called when throwing.
+        ThrowPRException("Failed NSS_Init call.");
+      }
     }
     library_initialized_ = true;
 
diff --git a/tests/standalone/io/secure_no_builtin_roots_database_test.dart b/tests/standalone/io/secure_no_builtin_roots_database_test.dart
new file mode 100644
index 0000000..078ba70
--- /dev/null
+++ b/tests/standalone/io/secure_no_builtin_roots_database_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import "dart:io";
+import "dart:uri";
+import "dart:isolate";
+import "dart:async";
+
+void testGoogleUrl() {
+  ReceivePort keepAlive = new ReceivePort();
+  HttpClient client = new HttpClient();
+  client.getUrl(Uri.parse('https://www.google.com'))
+      .then((request) => request.close())
+      .then((response) => Expect.fail("Unexpected successful connection"))
+      .catchError((error) {
+        Expect.isTrue(error is AsyncError);
+        Expect.isTrue(error.error is SocketIOException);
+        keepAlive.close();
+        client.close();
+      });
+}
+
+void InitializeSSL() {
+  // If the built-in root certificates aren't loaded, the connection
+  // should signal an error.  Even when an external database is loaded,
+  // they should not be loaded.
+  Path scriptDir = new Path(new Options().script).directoryPath;
+  Path certificateDatabase = scriptDir.append('pkcert');
+  SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+                          password: 'dartdart',
+                          useBuiltinRoots: false);
+}
+
+void main() {
+  InitializeSSL();
+  testGoogleUrl();
+}