shill: vpn: Add PKCS#11 to OpenVPN.

This includes both setting up relevant openvpn options and passing a TPM token /
pin through the management interface. Also, refactor a bit the InitOptions code
to ease testing and readability and some OpenVPNDriver unit test cleanup.

BUG=chromium-os:28948
TEST=unit tests

Change-Id: Iff65d9b4ae29defdf02f1da4ebb4845d1050e144
Reviewed-on: https://gerrit.chromium.org/gerrit/19587
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/openvpn_management_server_unittest.cc b/openvpn_management_server_unittest.cc
index 9623293..acab874 100644
--- a/openvpn_management_server_unittest.cc
+++ b/openvpn_management_server_unittest.cc
@@ -65,6 +65,12 @@
     ExpectSend("password \"Auth\" \"SCRV1:eW95bw==:MTIzNDU2\"\n");
   }
 
+  void ExpectPINResponse() {
+    driver_.args()->SetString(flimflam::kOpenVPNPinProperty, "987654");
+    SetConnectedSocket();
+    ExpectSend("password \"User-Specific TPM Token FOO\" \"987654\"\n");
+  }
+
   InputData CreateInputDataFromString(const string &str) {
     InputData data(
         reinterpret_cast<unsigned char *>(const_cast<char *>(str.data())),
@@ -186,9 +192,11 @@
     string s = "foo\n"
         ">INFO:...\n"
         ">PASSWORD:Need 'Auth' SC:user/password/otp\n"
+        ">PASSWORD:Need 'User-Specific TPM Token FOO' ...\n"
         ">STATE:123,RECONNECTING,detail,...,...";
     InputData data = CreateInputDataFromString(s);
     ExpectStaticChallengeResponse();
+    ExpectPINResponse();
     EXPECT_CALL(driver_, OnReconnecting());
     server_.OnInput(&data);
   }
@@ -216,7 +224,6 @@
 }
 
 TEST_F(OpenVPNManagementServerTest, ProcessNeedPasswordMessageAuthSC) {
-  EXPECT_FALSE(server_.ProcessNeedPasswordMessage("foo"));
   ExpectStaticChallengeResponse();
   EXPECT_TRUE(
       server_.ProcessNeedPasswordMessage(
@@ -224,21 +231,55 @@
   EXPECT_FALSE(driver_.args()->ContainsString(flimflam::kOpenVPNOTPProperty));
 }
 
+TEST_F(OpenVPNManagementServerTest, ProcessNeedPasswordMessageTPMToken) {
+  ExpectPINResponse();
+  EXPECT_TRUE(
+      server_.ProcessNeedPasswordMessage(
+          ">PASSWORD:Need 'User-Specific TPM Token FOO' ..."));
+}
+
+TEST_F(OpenVPNManagementServerTest, ProcessNeedPasswordMessageUnknown) {
+  EXPECT_FALSE(server_.ProcessNeedPasswordMessage("foo"));
+}
+
+TEST_F(OpenVPNManagementServerTest, ParseNeedPasswordTag) {
+  EXPECT_EQ("", OpenVPNManagementServer::ParseNeedPasswordTag(""));
+  EXPECT_EQ("", OpenVPNManagementServer::ParseNeedPasswordTag(" "));
+  EXPECT_EQ("", OpenVPNManagementServer::ParseNeedPasswordTag("'"));
+  EXPECT_EQ("", OpenVPNManagementServer::ParseNeedPasswordTag("''"));
+  EXPECT_EQ("bar",
+            OpenVPNManagementServer::ParseNeedPasswordTag("foo'bar'zoo"));
+  EXPECT_EQ("bar", OpenVPNManagementServer::ParseNeedPasswordTag("foo'bar'"));
+  EXPECT_EQ("bar", OpenVPNManagementServer::ParseNeedPasswordTag("'bar'zoo"));
+  EXPECT_EQ("bar",
+            OpenVPNManagementServer::ParseNeedPasswordTag("foo'bar'zoo'moo"));
+}
+
 TEST_F(OpenVPNManagementServerTest, PerformStaticChallengeNoCreds) {
   // Expect no crash due to null sockets_.
-  server_.PerformStaticChallenge();
+  server_.PerformStaticChallenge("Auth");
   driver_.args()->SetString(flimflam::kOpenVPNUserProperty, "jojo");
-  server_.PerformStaticChallenge();
+  server_.PerformStaticChallenge("Auth");
   driver_.args()->SetString(flimflam::kOpenVPNPasswordProperty, "yoyo");
-  server_.PerformStaticChallenge();
+  server_.PerformStaticChallenge("Auth");
 }
 
 TEST_F(OpenVPNManagementServerTest, PerformStaticChallenge) {
   ExpectStaticChallengeResponse();
-  server_.PerformStaticChallenge();
+  server_.PerformStaticChallenge("Auth");
   EXPECT_FALSE(driver_.args()->ContainsString(flimflam::kOpenVPNOTPProperty));
 }
 
+TEST_F(OpenVPNManagementServerTest, SupplyTPMTokenNoPIN) {
+  // Expect no crash due to null sockets_.
+  server_.SupplyTPMToken("User-Specific TPM Token FOO");
+}
+
+TEST_F(OpenVPNManagementServerTest, SupplyTPMToken) {
+  ExpectPINResponse();
+  server_.SupplyTPMToken("User-Specific TPM Token FOO");
+}
+
 TEST_F(OpenVPNManagementServerTest, Send) {
   const char kMessage[] = "foo\n";
   SetConnectedSocket();