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.cc b/openvpn_management_server.cc
index 06c9576..fa4aa48 100644
--- a/openvpn_management_server.cc
+++ b/openvpn_management_server.cc
@@ -162,18 +162,34 @@
   if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
     return false;
   }
-  if (message.find("'Auth'") != string::npos) {
+  string tag = ParseNeedPasswordTag(message);
+  if (tag == "Auth") {
     if (message.find("SC:") != string::npos) {
-      PerformStaticChallenge();
+      PerformStaticChallenge(tag);
     } else {
       NOTIMPLEMENTED()
           << "User/password (no-OTP) authentication not implemented.";
     }
+  } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
+    SupplyTPMToken(tag);
   }
   return true;
 }
 
-void OpenVPNManagementServer::PerformStaticChallenge() {
+// static
+string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
+  size_t start = message.find('\'');
+  if (start == string::npos) {
+    return string();
+  }
+  size_t end = message.find('\'', start + 1);
+  if (end == string::npos) {
+    return string();
+  }
+  return message.substr(start + 1, end - start - 1);
+}
+
+void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
   string user =
       driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
   string password =
@@ -194,14 +210,23 @@
     LOG(ERROR) << "Unable to base64-encode credentials.";
     return;
   }
-  SendUsername("Auth", user);
-  SendPassword("Auth", StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
+  SendUsername(tag, user);
+  SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
   glib_->Free(b64_otp);
   glib_->Free(b64_password);
   // Don't reuse OTP.
   driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
 }
 
+void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
+  string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
+  if (pin.empty()) {
+    NOTIMPLEMENTED() << "Missing PIN.";
+    return;
+  }
+  SendPassword(tag, pin);
+}
+
 bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
     const string &message) {
   if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {