shill: openvpn: Support no-OTP user/password authentication.

This also fixes a flimflam bug where passwords were sent unescaped, so \ and "
weren't legal password characters in no-OTP authentication mode.

BUG=chromium:137970
TEST=unit tests

Change-Id: I0caad371a5db91739eaf83fc587ad88df1433c77
Reviewed-on: https://gerrit.chromium.org/gerrit/28266
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 e667733..cc0aba4 100644
--- a/openvpn_management_server.cc
+++ b/openvpn_management_server.cc
@@ -200,9 +200,7 @@
     if (message.find("SC:") != string::npos) {
       PerformStaticChallenge(tag);
     } else {
-      NOTIMPLEMENTED()
-          << ": User/password (no-OTP) authentication not implemented.";
-      driver_->Cleanup(Service::kStateFailure);
+      PerformAuthentication(tag);
     }
   } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
     SupplyTPMToken(tag);
@@ -261,6 +259,23 @@
   driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
 }
 
+void OpenVPNManagementServer::PerformAuthentication(const string &tag) {
+  LOG(INFO) << "Perform authentication: " << tag;
+  string user =
+      driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
+  string password =
+      driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
+  if (user.empty() || password.empty()) {
+    NOTIMPLEMENTED() << ": Missing credentials:"
+                     << (user.empty() ? " no-user" : "")
+                     << (password.empty() ? " no-password" : "");
+    driver_->Cleanup(Service::kStateFailure);
+    return;
+  }
+  SendUsername(tag, user);
+  SendPassword(tag, password);
+}
+
 void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
   SLOG(VPN, 2) << __func__ << "(" << tag << ")";
   string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
@@ -320,6 +335,18 @@
   return true;
 }
 
+// static
+string OpenVPNManagementServer::EscapeToQuote(const string &str) {
+  string escaped;
+  for (string::const_iterator it = str.begin(); it != str.end(); ++it) {
+    if (*it == '\\' || *it == '"') {
+      escaped += '\\';
+    }
+    escaped += *it;
+  }
+  return escaped;
+}
+
 void OpenVPNManagementServer::Send(const string &data) {
   SLOG(VPN, 2) << __func__;
   ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
@@ -341,7 +368,9 @@
 void OpenVPNManagementServer::SendPassword(const string &tag,
                                            const string &password) {
   SLOG(VPN, 2) << __func__;
-  Send(StringPrintf("password \"%s\" \"%s\"\n", tag.c_str(), password.c_str()));
+  Send(StringPrintf("password \"%s\" \"%s\"\n",
+                    tag.c_str(),
+                    EscapeToQuote(password).c_str()));
 }
 
 void OpenVPNManagementServer::SendHoldRelease() {