shill: vpn: Add support for the OpenVPN.TLSAuthContents property.

BUG=chromium-os:28793
TEST=unit tests

Change-Id: Ib168fb506487e87a87fbb8c631d723b4f0241dc7
Reviewed-on: https://gerrit.chromium.org/gerrit/19452
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Sam Leffler <sleffler@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/openvpn_driver.cc b/openvpn_driver.cc
index 5c1f6a5..6373d87 100644
--- a/openvpn_driver.cc
+++ b/openvpn_driver.cc
@@ -6,6 +6,7 @@
 
 #include <arpa/inet.h>
 
+#include <base/file_util.h>
 #include <base/logging.h>
 #include <base/string_number_conversions.h>
 #include <base/string_split.h>
@@ -124,7 +125,12 @@
 }
 
 void OpenVPNDriver::Cleanup(Service::ConnectState state) {
+  VLOG(2) << __func__ << "(" << Service::ConnectStateToString(state) << ")";
   management_server_->Stop();
+  if (!tls_auth_file_.empty()) {
+    file_util::Delete(tls_auth_file_, false);
+    tls_auth_file_.clear();
+  }
   if (child_watch_tag_) {
     glib_->SourceRemove(child_watch_tag_);
     child_watch_tag_ = 0;
@@ -403,11 +409,22 @@
   AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
   AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
   AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
-
-  // TODO(petkov): Implement this.
-  LOG_IF(ERROR, args_.ContainsString(flimflam::kOpenVPNTLSAuthContentsProperty))
-      << "Support for --tls-auth not implemented yet.";
-
+  {
+    string contents =
+        args_.LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
+    if (!contents.empty()) {
+      if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
+          file_util::WriteFile(
+              tls_auth_file_, contents.data(), contents.size()) !=
+          static_cast<int>(contents.size())) {
+        Error::PopulateAndLog(
+            error, Error::kInternalError, "Unable to setup tls-auth file.");
+        return;
+      }
+      options->push_back("--tls-auth");
+      options->push_back(tls_auth_file_.value());
+    }
+  }
   AppendValueOption(
       flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
   AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
diff --git a/openvpn_driver.h b/openvpn_driver.h
index 1470454..74a156f 100644
--- a/openvpn_driver.h
+++ b/openvpn_driver.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include <base/file_path.h>
 #include <base/memory/scoped_ptr.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
@@ -161,6 +162,7 @@
   scoped_ptr<RPCTask> rpc_task_;
   std::string tunnel_interface_;
   VPNRefPtr device_;
+  FilePath tls_auth_file_;
 
   // The PID of the spawned openvpn process. May be 0 if no process has been
   // spawned yet or the process has died.
diff --git a/openvpn_driver_unittest.cc b/openvpn_driver_unittest.cc
index 832cb1a..15a7ed0 100644
--- a/openvpn_driver_unittest.cc
+++ b/openvpn_driver_unittest.cc
@@ -352,7 +352,9 @@
 
 TEST_F(OpenVPNDriverTest, InitOptions) {
   static const char kHost[] = "192.168.2.254";
+  static const char kTLSAuthContents[] = "SOME-RANDOM-CONTENTS\n";
   args_.SetString(flimflam::kProviderHostProperty, kHost);
+  args_.SetString(flimflam::kOpenVPNTLSAuthContentsProperty, kTLSAuthContents);
   SetArgs();
   driver_->rpc_task_.reset(new RPCTask(&control_, this));
   driver_->tunnel_interface_ = kInterfaceName;
@@ -376,6 +378,12 @@
     ExpectInFlags(options, "--dev", kInterfaceName);
     EXPECT_EQ("openvpn", options.back());
     EXPECT_EQ(kInterfaceName, driver_->tunnel_interface_);
+    ASSERT_FALSE(driver_->tls_auth_file_.empty());
+    ExpectInFlags(options, "--tls-auth", driver_->tls_auth_file_.value());
+    string contents;
+    EXPECT_TRUE(
+        file_util::ReadFileToString(driver_->tls_auth_file_, &contents));
+    EXPECT_EQ(kTLSAuthContents, contents);
   }
 }
 
@@ -437,6 +445,8 @@
 }
 
 TEST_F(OpenVPNDriverTest, Cleanup) {
+  driver_->Cleanup(Service::kStateIdle);  // Ensure no crash.
+
   const unsigned int kTag = 123;
   const int kPID = 123456;
   driver_->child_watch_tag_ = kTag;
@@ -445,6 +455,11 @@
   driver_->tunnel_interface_ = kInterfaceName;
   driver_->device_ = device_;
   driver_->service_ = service_;
+  FilePath tls_auth_file;
+  EXPECT_TRUE(file_util::CreateTemporaryFile(&tls_auth_file));
+  EXPECT_FALSE(tls_auth_file.empty());
+  EXPECT_TRUE(file_util::PathExists(tls_auth_file));
+  driver_->tls_auth_file_ = tls_auth_file;
   // Stop will be called twice -- once by Cleanup and once by the destructor.
   EXPECT_CALL(*management_server_, Stop()).Times(2);
   EXPECT_CALL(glib_, SourceRemove(kTag));
@@ -459,6 +474,8 @@
   EXPECT_TRUE(driver_->tunnel_interface_.empty());
   EXPECT_FALSE(driver_->device_);
   EXPECT_FALSE(driver_->service_);
+  EXPECT_FALSE(file_util::PathExists(tls_auth_file));
+  EXPECT_TRUE(driver_->tls_auth_file_.empty());
 }
 
 TEST_F(OpenVPNDriverTest, SpawnOpenVPN) {