shill: OpenVPNDriver: Use an array for CA cert PEM

Switch to an array property for the "OpenVPN.CACertPEM" property.
This allows multiple CA certificates to be specified.

BUG=chromium:249363
TEST=Unit test

Change-Id: If9e94a81dc29fb3b689e9535d87fda771c0d58e5
Reviewed-on: https://gerrit.chromium.org/gerrit/58724
Commit-Queue: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/certificate_file.cc b/certificate_file.cc
index 1a91795..4083a6a 100644
--- a/certificate_file.cc
+++ b/certificate_file.cc
@@ -43,15 +43,6 @@
   }
 }
 
-FilePath CertificateFile::CreatePEMFromString(const string &pem_contents) {
-  string hex_data = ExtractHexData(pem_contents);
-  if (hex_data.empty()) {
-    return FilePath();
-  }
-  return WriteFile(StringPrintf(
-      "%s\n%s%s\n", kPEMHeader, hex_data.c_str(), kPEMFooter));
-}
-
 FilePath CertificateFile::CreatePEMFromStrings(
     const vector<string> &pem_contents) {
   vector<string> pem_output;
diff --git a/certificate_file.h b/certificate_file.h
index ad4ed1d..c3b87a1 100644
--- a/certificate_file.h
+++ b/certificate_file.h
@@ -23,9 +23,8 @@
   explicit CertificateFile(GLib *glib);
   virtual ~CertificateFile();
 
-  // Write out a PEM file from an input string (or vector of strings)
-  // in PEM format.  Returns an empty path on failure.
-  virtual base::FilePath CreatePEMFromString(const std::string &pem_contents);
+  // Write out a PEM file from an input vector of strings in PEM format.
+  // Returns an empty path on failure.
   virtual base::FilePath CreatePEMFromStrings(
       const std::vector<std::string> &pem_contents);
 
diff --git a/certificate_file_unittest.cc b/certificate_file_unittest.cc
index 125dc91..5c957a0 100644
--- a/certificate_file_unittest.cc
+++ b/certificate_file_unittest.cc
@@ -70,53 +70,38 @@
   EXPECT_TRUE(GetOutputFile().empty());
 }
 
-TEST_F(CertificateFileTest, CreatePEMFromString) {
+TEST_F(CertificateFileTest, CreatePEMFromStrings) {
   // Create a formatted PEM file from the inner HEX data.
-  FilePath outfile0 = certificate_file_.CreatePEMFromString(kPEMData);
+  const vector<string> kPEMVector0{ kPEMData };
+  FilePath outfile0 = certificate_file_.CreatePEMFromStrings(kPEMVector0);
   EXPECT_FALSE(outfile0.empty());
   EXPECT_TRUE(file_util::PathExists(outfile0));
   EXPECT_TRUE(file_util::ContainsPath(certificate_directory_, outfile0));
   string file_string0;
-  string expected_output = StringPrintf(
-      "%s\n%s%s\n", GetPEMHeader(), kPEMData, GetPEMFooter());
   EXPECT_TRUE(file_util::ReadFileToString(outfile0, &file_string0));
-  EXPECT_EQ(expected_output, file_string0);
+  string expected_output0 = StringPrintf(
+      "%s\n%s%s\n", GetPEMHeader(), kPEMData, GetPEMFooter());
+  EXPECT_EQ(expected_output0, file_string0);
+
   // Create a formatted PEM file from formatted PEM.
-  FilePath outfile1 = certificate_file_.CreatePEMFromString(expected_output);
+  const vector<string> kPEMVector1{ expected_output0, kPEMData };
+  FilePath outfile1 = certificate_file_.CreatePEMFromStrings(kPEMVector1);
   EXPECT_FALSE(outfile1.empty());
   EXPECT_TRUE(file_util::PathExists(outfile1));
   EXPECT_FALSE(file_util::PathExists(outfile0));  // Old file is deleted.
   string file_string1;
   EXPECT_TRUE(file_util::ReadFileToString(outfile1, &file_string1));
-  EXPECT_EQ(expected_output, file_string1);
+  string expected_output1 = StringPrintf(
+      "%s%s", expected_output0.c_str(), expected_output0.c_str());
+  EXPECT_EQ(expected_output1, file_string1);
 
   // Fail to create a PEM file.  Old file should not have been deleted.
-  FilePath outfile2 = certificate_file_.CreatePEMFromString("");
+  const vector<string> kPEMVector2{ kPEMData, "" };
+  FilePath outfile2 = certificate_file_.CreatePEMFromStrings(kPEMVector2);
   EXPECT_TRUE(outfile2.empty());
   EXPECT_TRUE(file_util::PathExists(outfile1));
 }
 
-TEST_F(CertificateFileTest, CreatePEMFromStrings) {
-  // Create a formatted PEM file from the inner HEX data.
-  const vector<string> kPEMVector0{ kPEMData, kPEMData };
-  FilePath outfile0 = certificate_file_.CreatePEMFromStrings(kPEMVector0);
-  EXPECT_FALSE(outfile0.empty());
-  EXPECT_TRUE(file_util::PathExists(outfile0));
-  EXPECT_TRUE(file_util::ContainsPath(certificate_directory_, outfile0));
-  string expected_output = StringPrintf(
-      "%s\n%s%s\n%s\n%s%s\n", GetPEMHeader(), kPEMData, GetPEMFooter(),
-      GetPEMHeader(), kPEMData, GetPEMFooter());
-  string file_string0;
-  EXPECT_TRUE(file_util::ReadFileToString(outfile0, &file_string0));
-  EXPECT_EQ(expected_output, file_string0);
-
-  // Fail to create a PEM file.  Old file should not have been deleted.
-  const vector<string> kPEMVector1{ kPEMData, "" };
-  FilePath outfile1 = certificate_file_.CreatePEMFromStrings(kPEMVector1);
-  EXPECT_TRUE(outfile1.empty());
-  EXPECT_TRUE(file_util::PathExists(outfile0));
-}
-
 TEST_F(CertificateFileTest, CreateDERFromString) {
   // Create a DER file from the inner HEX data.
   const string kPEMString = kPEMData;
@@ -159,7 +144,7 @@
   {
     CertificateFile certificate_file(&glib_);
     certificate_file.set_root_directory(temp_dir_.path());
-    outfile = certificate_file.CreatePEMFromString(kPEMData);
+    outfile = certificate_file.CreatePEMFromStrings(vector<string>{ kPEMData });
     EXPECT_TRUE(file_util::PathExists(outfile));
   }
   // The output file should be deleted when certificate_file goes out-of-scope.
diff --git a/mock_certificate_file.h b/mock_certificate_file.h
index cabbb74..e1f1a39 100644
--- a/mock_certificate_file.h
+++ b/mock_certificate_file.h
@@ -18,6 +18,8 @@
 
   MOCK_METHOD1(CreatePEMFromString,
                base::FilePath(const std::string &pem_contents));
+  MOCK_METHOD1(CreatePEMFromStrings,
+               base::FilePath(const std::vector<std::string> &pem_contents));
   MOCK_METHOD1(CreateDERFromString,
                base::FilePath(const std::string &pem_contents));
 
diff --git a/openvpn_driver.cc b/openvpn_driver.cc
index a2a7479..a5c0174 100644
--- a/openvpn_driver.cc
+++ b/openvpn_driver.cc
@@ -113,7 +113,7 @@
   { flimflam::kOpenVPNUserProperty, 0 },
   { flimflam::kProviderHostProperty, 0 },
   { flimflam::kProviderTypeProperty, 0 },
-  { kOpenVPNCaCertPemProperty, 0 },
+  { kOpenVPNCaCertPemProperty, Property::kArray },
   { kOpenVPNCertProperty, 0 },
   { kOpenVPNKeyProperty, 0 },
   { kOpenVPNPingExitProperty, 0 },
@@ -663,7 +663,10 @@
       args()->LookupString(flimflam::kOpenVPNCaCertProperty, "");
   string ca_cert_nss =
       args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
-  string ca_cert_pem = args()->LookupString(kOpenVPNCaCertPemProperty, "");
+  vector<string> ca_cert_pem;
+  if (args()->ContainsStrings(kOpenVPNCaCertPemProperty)) {
+    ca_cert_pem = args()->GetStrings(kOpenVPNCaCertPemProperty);
+  }
 
   int num_ca_cert_types = 0;
   if (!ca_cert.empty())
@@ -699,12 +702,12 @@
     return true;
   } else if (!ca_cert_pem.empty()) {
     DCHECK(ca_cert.empty() && ca_cert_nss.empty());
-    FilePath certfile = certificate_file_->CreatePEMFromString(ca_cert_pem);
+    FilePath certfile = certificate_file_->CreatePEMFromStrings(ca_cert_pem);
     if (certfile.empty()) {
       Error::PopulateAndLog(
           error,
           Error::kInvalidArguments,
-          "Unable to extract PEM CA certificate.");
+          "Unable to extract PEM CA certificates.");
       return false;
     }
     options->push_back(certfile.value());
diff --git a/openvpn_driver_unittest.cc b/openvpn_driver_unittest.cc
index 9f103a3..99c3d5d 100644
--- a/openvpn_driver_unittest.cc
+++ b/openvpn_driver_unittest.cc
@@ -110,6 +110,10 @@
     driver_->args()->SetString(arg, value);
   }
 
+  void SetArgArray(const string &arg, const vector<string> &value) {
+    driver_->args()->SetStrings(arg, value);
+  }
+
   KeyValueStore *GetArgs() {
     return driver_->args();
   }
@@ -666,8 +670,8 @@
   ExpectInFlags(options, "--ca", kNSSCertfile);
   EXPECT_TRUE(error.IsSuccess());
 
-  static const char kCaCertPEM[] = "---PEM CONTENTS---";
-  SetArg(kOpenVPNCaCertPemProperty, kCaCertPEM);
+  const vector<string> kCaCertPEM{ "---PEM CONTENTS---" };
+  SetArgArray(kOpenVPNCaCertPemProperty, kCaCertPEM);
   EXPECT_FALSE(driver_->InitCAOptions(&options, &error));
   EXPECT_EQ(Error::kInvalidArguments, error.type());
   EXPECT_EQ("Can't specify more than one of CACert, CACertNSS and CACertPEM.",
@@ -678,14 +682,14 @@
   SetArg(flimflam::kProviderHostProperty, "");
   static const char kPEMCertfile[] = "/tmp/pem-cert";
   FilePath pem_cert(kPEMCertfile);
-  EXPECT_CALL(*certificate_file_, CreatePEMFromString(kCaCertPEM))
+  EXPECT_CALL(*certificate_file_, CreatePEMFromStrings(kCaCertPEM))
       .WillOnce(Return(empty_cert))
       .WillOnce(Return(pem_cert));
 
   error.Reset();
   EXPECT_FALSE(driver_->InitCAOptions(&options, &error));
   EXPECT_EQ(Error::kInvalidArguments, error.type());
-  EXPECT_EQ("Unable to extract PEM CA certificate.", error.message());
+  EXPECT_EQ("Unable to extract PEM CA certificates.", error.message());
 
   error.Reset();
   options.clear();