[io/ssl] Add an option to bypass trusting system cert roots.

Fixes https://github.com/dart-lang/sdk/issues/45912

TEST=manually remove root certificate for pub.dev, confirm that dart is not able to establish https connection in default configuration to pub.dev, confirm that it can connect with the flag.

Change-Id: I51af7994d7cd7371a17877844dc1bf39cd5e54ca
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/198442
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index e60f6c8..b002ab8 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -470,6 +470,8 @@
   SSLCertContext::set_root_certs_cache(Options::root_certs_cache());
   SSLCertContext::set_long_ssl_cert_evaluation(
       Options::long_ssl_cert_evaluation());
+  SSLCertContext::set_bypass_trusting_system_roots(
+      Options::bypass_trusting_system_roots());
 #endif  // !defined(DART_IO_SECURE_SOCKET_DISABLED)
 
   // The arguments to the VM are at positions 1 through i-1 in argv.
diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h
index 658cea3..a0fc972 100644
--- a/runtime/bin/main_options.h
+++ b/runtime/bin/main_options.h
@@ -48,7 +48,8 @@
   V(suppress_core_dump, suppress_core_dump)                                    \
   V(enable_service_port_fallback, enable_service_port_fallback)                \
   V(disable_dart_dev, disable_dart_dev)                                        \
-  V(long_ssl_cert_evaluation, long_ssl_cert_evaluation)
+  V(long_ssl_cert_evaluation, long_ssl_cert_evaluation)                        \
+  V(bypass_trusting_system_roots, bypass_trusting_system_roots)
 
 // Boolean flags that have a short form.
 #define SHORT_BOOL_OPTIONS_LIST(V)                                             \
diff --git a/runtime/bin/security_context.cc b/runtime/bin/security_context.cc
index f5d3a00..4ea0f10 100644
--- a/runtime/bin/security_context.cc
+++ b/runtime/bin/security_context.cc
@@ -35,6 +35,7 @@
 const char* SSLCertContext::root_certs_file_ = NULL;
 const char* SSLCertContext::root_certs_cache_ = NULL;
 bool SSLCertContext::long_ssl_cert_evaluation_ = false;
+bool SSLCertContext::bypass_trusting_system_roots_ = false;
 
 int SSLCertContext::CertificateCallback(int preverify_ok,
                                         X509_STORE_CTX* store_ctx) {
diff --git a/runtime/bin/security_context.h b/runtime/bin/security_context.h
index 5090cdb..e8800e2 100644
--- a/runtime/bin/security_context.h
+++ b/runtime/bin/security_context.h
@@ -93,6 +93,14 @@
     long_ssl_cert_evaluation_ = long_ssl_cert_evaluation;
   }
 
+  static bool bypass_trusting_system_roots() {
+    return bypass_trusting_system_roots_;
+  }
+  static void set_bypass_trusting_system_roots(
+      bool bypass_trusting_system_roots) {
+    bypass_trusting_system_roots_ = bypass_trusting_system_roots;
+  }
+
  private:
   void AddCompiledInCerts();
   void LoadRootCertFile(const char* file);
@@ -107,6 +115,7 @@
   bool trust_builtin_;
 
   static bool long_ssl_cert_evaluation_;
+  static bool bypass_trusting_system_roots_;
 
   DISALLOW_COPY_AND_ASSIGN(SSLCertContext);
 };
diff --git a/runtime/bin/security_context_linux.cc b/runtime/bin/security_context_linux.cc
index e4a9312..b251a0a 100644
--- a/runtime/bin/security_context_linux.cc
+++ b/runtime/bin/security_context_linux.cc
@@ -40,21 +40,27 @@
     return;
   }
 
-  // On Linux, we use the compiled-in trusted certs as a last resort. First,
-  // we try to find the trusted certs in various standard locations. A good
-  // discussion of the complexities of this endeavor can be found here:
-  //
-  // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
-  const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt";
-  const char* cachedir = "/etc/ssl/certs";
-  if (File::Exists(NULL, bundle)) {
-    LoadRootCertFile(bundle);
-    return;
-  }
+  if (bypass_trusting_system_roots()) {
+    if (SSL_LOG_STATUS) {
+      Syslog::Print("Bypass trusting Linux built-in roots\n");
+    }
+  } else {
+    // On Linux, we use the compiled-in trusted certs as a last resort. First,
+    // we try to find the trusted certs in various standard locations. A good
+    // discussion of the complexities of this endeavor can be found here:
+    //
+    // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
+    const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt";
+    const char* cachedir = "/etc/ssl/certs";
+    if (File::Exists(NULL, bundle)) {
+      LoadRootCertFile(bundle);
+      return;
+    }
 
-  if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) {
-    LoadRootCertCache(cachedir);
-    return;
+    if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) {
+      LoadRootCertCache(cachedir);
+      return;
+    }
   }
 
   // Fall back on the compiled-in certs if the standard locations don't exist,
diff --git a/runtime/bin/security_context_win.cc b/runtime/bin/security_context_win.cc
index 950f4be..6b19a89 100644
--- a/runtime/bin/security_context_win.cc
+++ b/runtime/bin/security_context_win.cc
@@ -190,12 +190,18 @@
     return;
   }
 
-  if (SSL_LOG_STATUS) {
-    Syslog::Print("Trusting Windows built-in roots\n");
-  }
-  X509_STORE* store = SSL_CTX_get_cert_store(context());
-  if (AddCertificatesFromRootStore(store)) {
-    return;
+  if (bypass_trusting_system_roots()) {
+    if (SSL_LOG_STATUS) {
+      Syslog::Print("Bypass trusting Windows built-in roots\n");
+    }
+  } else {
+    if (SSL_LOG_STATUS) {
+      Syslog::Print("Trusting Windows built-in roots\n");
+    }
+    X509_STORE* store = SSL_CTX_get_cert_store(context());
+    if (AddCertificatesFromRootStore(store)) {
+      return;
+    }
   }
   // Reset store. SSL_CTX_set_cert_store will take ownership of store. A manual
   // free is not needed.