system-proxy: Use login password for NTLM token

Implements reusing the Chrome OS login password for generating NTLM
auth tokens when policy IntegratedWebAuthenticationAllowed is enabled.

The password is read from the kernel keyring using libpasswordprovider.

BUG=b:173097096
TEST=SystemProxyAdaptorTest.GenerateNtlmAuthMessage

Cq-Depend: chromium:2545814
Change-Id: Ib0176ef0172aaab623e79b88d7fc009078c9032e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2557637
Tested-by: Andreea-Elena Costinas <acostinas@google.com>
Commit-Queue: Andreea-Elena Costinas <acostinas@google.com>
Reviewed-by: Pavol Marko <pmarko@chromium.org>
diff --git a/system-proxy/BUILD.gn b/system-proxy/BUILD.gn
index e88adcc..6128fae 100644
--- a/system-proxy/BUILD.gn
+++ b/system-proxy/BUILD.gn
@@ -46,6 +46,7 @@
     "libbrillo",
     "libchrome",
     "libminijail",
+    "libpasswordprovider",
     "libpatchpanel-client",
     "libpatchpanel-util",
     "openssl",
diff --git a/system-proxy/init/system-proxy.conf b/system-proxy/init/system-proxy.conf
index d51bd60..c5ecd03 100644
--- a/system-proxy/init/system-proxy.conf
+++ b/system-proxy/init/system-proxy.conf
@@ -74,6 +74,10 @@
   # Run as system-proxy user and group.
   args="${args} -u system-proxy -g system-proxy"
 
+  # Inherit system-proxy's supplementary groups, in particular
+  # 'password-viewers' to read the login password.
+  args="${args} -G"
+
   # Run with root permissions so that the daemon can start sandboxed processes.
   args="${args} -c cap_sys_admin=e"
 
diff --git a/system-proxy/seccomp/system-proxy-seccomp-amd64.policy b/system-proxy/seccomp/system-proxy-seccomp-amd64.policy
index 0138a03..4ae7a55 100644
--- a/system-proxy/seccomp/system-proxy-seccomp-amd64.policy
+++ b/system-proxy/seccomp/system-proxy-seccomp-amd64.policy
@@ -87,3 +87,10 @@
 getdents: 1
 recvmsg: 1
 shutdown: 1
+# Required for System-proxy to read the Chrome OS login password as part of a
+# feature that will allow users to authenticate to a network resource using the
+# Chrome OS password.
+request_key: 1
+keyctl: 1
+mlock: 1
+munlock: 1
diff --git a/system-proxy/seccomp/system-proxy-seccomp-arm.policy b/system-proxy/seccomp/system-proxy-seccomp-arm.policy
index 83788fa..f7ed147 100644
--- a/system-proxy/seccomp/system-proxy-seccomp-arm.policy
+++ b/system-proxy/seccomp/system-proxy-seccomp-arm.policy
@@ -97,3 +97,10 @@
 recv: 1
 lstat64: 1
 shutdown: 1
+# Required for System-proxy to read the Chrome OS login password as part of a
+# feature that will allow users to authenticate to a network resource using the
+# Chrome OS password.
+request_key: 1
+keyctl: 1
+mlock: 1
+munlock: 1
diff --git a/system-proxy/seccomp/system-proxy-seccomp-arm64.policy b/system-proxy/seccomp/system-proxy-seccomp-arm64.policy
index 13370e7..2fc3777 100644
--- a/system-proxy/seccomp/system-proxy-seccomp-arm64.policy
+++ b/system-proxy/seccomp/system-proxy-seccomp-arm64.policy
@@ -84,3 +84,10 @@
 getrandom: 1
 getdents: 1
 shutdown: 1
+# Required for System-proxy to read the Chrome OS login password as part of a
+# feature that will allow users to authenticate to a network resource using the
+# Chrome OS password.
+request_key: 1
+keyctl: 1
+mlock: 1
+munlock: 1
diff --git a/system-proxy/system_proxy_adaptor.cc b/system-proxy/system_proxy_adaptor.cc
index 13221d0..451086c 100644
--- a/system-proxy/system_proxy_adaptor.cc
+++ b/system-proxy/system_proxy_adaptor.cc
@@ -17,6 +17,8 @@
 #include <chromeos/dbus/service_constants.h>
 #include <chromeos/patchpanel/dbus/client.h>
 #include <dbus/object_proxy.h>
+#include <libpasswordprovider/password.h>
+#include <libpasswordprovider/password_provider.h>
 
 #include "system-proxy/kerberos_client.h"
 #include "system-proxy/net/ntlm/ntlm_client.h"
@@ -259,9 +261,20 @@
 
   base::string16 domain = base::UTF8ToUTF16(ntlm_request.domain());
   base::string16 username = base::UTF8ToUTF16(ntlm_request.username());
-  // TODO(acostinas) Replace placeholder password with Chrome OS login password
-  // from libpasswordprovider.
-  base::string16 pwd = base::ASCIIToUTF16("Password");
+  // Get login password from |password_provider_|.
+  password_provider::PasswordProviderInterface* password_provider =
+      GetPasswordProvider();
+  std::unique_ptr<password_provider::Password> login_password =
+      password_provider->GetPassword();
+  if (!login_password || login_password->size() == 0) {
+    response.set_error_message(
+        "Failed to retrieve the Chrome OS login password");
+    return SerializeProto(response);
+  }
+  // TODO(acostinas, b/175623932): Instead of creating a UTF-16 password, update
+  // the NTLM code to accept password_provider::Password.
+  std::string password =
+      std::string(login_password->GetRaw(), login_password->size());
 
   if (ntlm_request.client_challenge().size() != net::ntlm::kChallengeLen) {
     response.set_error_message("NTLM client challenge has unexpected length");
@@ -276,7 +289,7 @@
 
   net::ntlm::NtlmClient client(features);
   std::vector<uint8_t> auth_message = client.GenerateAuthenticateMessage(
-      domain, username, pwd, ntlm_request.hostname(),
+      domain, username, base::UTF8ToUTF16(password), ntlm_request.hostname(),
       ntlm_request.channel_bindings(), ntlm_request.spn(),
       (uint64_t)ntlm_request.client_time(), client_challenge,
       server_challenge_message);
@@ -473,4 +486,13 @@
   SendAuthenticationRequiredSignal(SerializeProto(details));
 }
 
+password_provider::PasswordProviderInterface*
+SystemProxyAdaptor::GetPasswordProvider() {
+  if (!password_provider_) {
+    password_provider_ =
+        std::make_unique<password_provider::PasswordProvider>();
+  }
+  return password_provider_.get();
+}
+
 }  // namespace system_proxy
diff --git a/system-proxy/system_proxy_adaptor.h b/system-proxy/system_proxy_adaptor.h
index c115a5c8..27ccef0 100644
--- a/system-proxy/system_proxy_adaptor.h
+++ b/system-proxy/system_proxy_adaptor.h
@@ -25,6 +25,10 @@
 
 }  // namespace brillo
 
+namespace password_provider {
+class PasswordProviderInterface;
+}
+
 namespace system_proxy {
 
 class KerberosClient;
@@ -73,6 +77,8 @@
  protected:
   virtual std::unique_ptr<SandboxedWorker> CreateWorker();
   virtual void ConnectNamespace(bool user_traffic);
+  virtual password_provider::PasswordProviderInterface* GetPasswordProvider();
+
   // Triggers the |WorkerActive| signal.
   void OnNamespaceConnected(SandboxedWorker* worker, bool user_traffic);
   // Returns a pointer to the worker process associated with |user_traffic|. Can
@@ -154,6 +160,9 @@
   std::unique_ptr<SandboxedWorker> arc_worker_;
   std::unique_ptr<KerberosClient> kerberos_client_;
 
+  std::unique_ptr<password_provider::PasswordProviderInterface>
+      password_provider_;
+
   std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object_;
   base::WeakPtrFactory<SystemProxyAdaptor> weak_ptr_factory_;
 };
diff --git a/system-proxy/system_proxy_adaptor_test.cc b/system-proxy/system_proxy_adaptor_test.cc
index 80292ac..c05b73e 100644
--- a/system-proxy/system_proxy_adaptor_test.cc
+++ b/system-proxy/system_proxy_adaptor_test.cc
@@ -32,6 +32,8 @@
 #include <chromeos/dbus/service_constants.h>
 #include <dbus/kerberos/dbus-constants.h>
 #include <dbus/system_proxy/dbus-constants.h>
+#include <libpasswordprovider/fake_password_provider.h>
+#include <libpasswordprovider/password_provider_test_utils.h>
 
 #include "bindings/worker_common.pb.h"
 #include "system-proxy/kerberos_client.h"
@@ -83,11 +85,18 @@
  public:
   FakeSystemProxyAdaptor(
       std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
-      : SystemProxyAdaptor(std::move(dbus_object)), weak_ptr_factory_(this) {}
+      : SystemProxyAdaptor(std::move(dbus_object)), weak_ptr_factory_(this) {
+    password_provider_ =
+        std::make_unique<password_provider::FakePasswordProvider>();
+  }
   FakeSystemProxyAdaptor(const FakeSystemProxyAdaptor&) = delete;
   FakeSystemProxyAdaptor& operator=(const FakeSystemProxyAdaptor&) = delete;
   ~FakeSystemProxyAdaptor() override = default;
 
+  password_provider::PasswordProviderInterface* GetPasswordProvider() override {
+    return password_provider_.get();
+  }
+
  protected:
   std::unique_ptr<SandboxedWorker> CreateWorker() override {
     ++create_worker_count_;
@@ -108,6 +117,9 @@
   FRIEND_TEST(SystemProxyAdaptorTest, ClearUserCredentialsRestartService);
 
   int create_worker_count_ = 0;
+
+  std::unique_ptr<password_provider::PasswordProviderInterface>
+      password_provider_;
   base::WeakPtrFactory<FakeSystemProxyAdaptor> weak_ptr_factory_;
 };
 
@@ -566,8 +578,21 @@
   std::vector<uint8_t> proto_blob(network_request.ByteSizeLong());
   network_request.SerializeToArray(proto_blob.data(), proto_blob.size());
   proto_blob = adaptor_->GenerateNetworkAuthMessage(proto_blob);
-
   GenerateNetworkAuthMessageResponse response;
+  network_request.SerializeToArray(proto_blob.data(), proto_blob.size());
+  proto_blob = adaptor_->GenerateNetworkAuthMessage(proto_blob);
+  ASSERT_TRUE(response.ParseFromArray(proto_blob.data(), proto_blob.size()));
+  // Password is missing.
+  EXPECT_FALSE(response.has_auth_message());
+
+  // Set login the password.
+  auto password_ptr = password_provider::test::CreatePassword(
+      base::UTF16ToASCII(net::ntlm::test::kPassword));
+  adaptor_->GetPasswordProvider()->SavePassword(*password_ptr);
+
+  proto_blob.resize(network_request.ByteSizeLong());
+  network_request.SerializeToArray(proto_blob.data(), proto_blob.size());
+  proto_blob = adaptor_->GenerateNetworkAuthMessage(proto_blob);
   ASSERT_TRUE(response.ParseFromArray(proto_blob.data(), proto_blob.size()));
   EXPECT_TRUE(response.has_auth_message());
   std::vector<uint8_t> result(response.auth_message().begin(),