system-proxy: Clear user credentials
System-proxy will request and reuse the credentials which a client has
entered in the browser. Users can opt to clear the authentication cache
in the browser.
This CL implements a D-Bus call to clear the proxy credentials from
System-proxy.
BUG=chromium:1098216
TEST=unit tests, manually on DUT
Change-Id: I9a58dd7a4e3e3d7a75d395ce645103d8f798b0a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2308531
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Pavol Marko <pmarko@chromium.org>
Tested-by: Andreea-Elena Costinas <acostinas@google.com>
Commit-Queue: Andreea-Elena Costinas <acostinas@google.com>
diff --git a/system-proxy/dbus/org.chromium.SystemProxy.conf b/system-proxy/dbus/org.chromium.SystemProxy.conf
index 0e9e9c0..3155d04 100644
--- a/system-proxy/dbus/org.chromium.SystemProxy.conf
+++ b/system-proxy/dbus/org.chromium.SystemProxy.conf
@@ -22,6 +22,9 @@
<allow receive_interface="org.chromium.SystemProxy"
receive_member="WorkerActive"
receive_type="signal" />
+ <allow send_destination="org.chromium.SystemProxy"
+ send_interface="org.chromium.SystemProxy"
+ send_member="ClearUserCredentials"/>
</policy>
<!-- For testing. -->
diff --git a/system-proxy/dbus_bindings/org.chromium.SystemProxy.xml b/system-proxy/dbus_bindings/org.chromium.SystemProxy.xml
index 55dea65..9964298 100644
--- a/system-proxy/dbus_bindings/org.chromium.SystemProxy.xml
+++ b/system-proxy/dbus_bindings/org.chromium.SystemProxy.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<node name="/org/chromium/SystemProxy" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+<node name="/org/chromium/SystemProxy"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
<interface name="org.chromium.SystemProxy">
<method name="SetAuthenticationDetails">
<tp:docstring>
- Sets the credentails for authenticating system services and ARC++ apps to the remote web proxy and the Kerberos availability flag.
+ Sets the credentails for authenticating system services and ARC++ apps
+ to the remote web proxy and the Kerberos availability flag.
</tp:docstring>
<arg name="request" type="ay" direction="in">
<tp:docstring>
@@ -20,7 +22,8 @@
</method>
<method name="ShutDown">
<tp:docstring>
- Shuts down the daemon. The daemon will exit with code 0 which prevents an Upstart respawn.
+ Shuts down the daemon. The daemon will exit with code 0 which prevents
+ an Upstart respawn.
</tp:docstring>
<arg name="response" type="ay" direction="out">
<tp:docstring>
@@ -31,7 +34,8 @@
</method>
<signal name="WorkerActive">
<tp:docstring>
- Signal emitted when a system proxy worker is active and accepting traffic.
+ Signal emitted when a system proxy worker is active and accepting
+ traffic.
</tp:docstring>
<arg name="details" type="ay">
<tp:docstring>
@@ -42,7 +46,8 @@
</signal>
<signal name="AuthenticationRequired">
<tp:docstring>
- Signal emitted when a system proxy worker requires credentials for proxy authentication.
+ Signal emitted when a system proxy worker requires credentials for
+ proxy authentication.
</tp:docstring>
<arg name="details" type="ay">
<tp:docstring>
@@ -51,5 +56,23 @@
</arg>
<annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
</signal>
+ <method name="ClearUserCredentials">
+ <tp:docstring>
+ Removes the user credentials from System-proxy. Credentials set for
+ system services via policy will still be available for proxy
+ authentication.
+ </tp:docstring>
+ <arg name="request" type="ay" direction="in">
+ <tp:docstring>
+ Serialized ClearUserCredentialsRequest message.
+ </tp:docstring>
+ </arg>
+ <arg name="response" type="ay" direction="out">
+ <tp:docstring>
+ Serialized ClearUserCredentialsResponse message.
+ </tp:docstring>
+ </arg>
+ <annotation name="org.chromium.DBus.Method.Kind" value="simple" />
+ </method>
</interface>
</node>
diff --git a/system-proxy/proto/worker_common.proto b/system-proxy/proto/worker_common.proto
index 8f8b097..f733986 100644
--- a/system-proxy/proto/worker_common.proto
+++ b/system-proxy/proto/worker_common.proto
@@ -67,6 +67,8 @@
optional bytes krb5conf_path = 3;
}
+message ClearUserCredentials {}
+
message WorkerConfigs {
oneof params {
Credentials credentials = 1;
@@ -74,5 +76,6 @@
SocketAddress listening_address = 2;
ProxyResolutionReply proxy_resolution_reply = 3;
KerberosConfig kerberos_config = 4;
+ ClearUserCredentials clear_user_credentials = 5;
}
}
diff --git a/system-proxy/sandboxed_worker.cc b/system-proxy/sandboxed_worker.cc
index 8fd772e..d03dc5c 100644
--- a/system-proxy/sandboxed_worker.cc
+++ b/system-proxy/sandboxed_worker.cc
@@ -142,6 +142,19 @@
return true;
}
+bool SandboxedWorker::ClearUserCredentials() {
+ worker::ClearUserCredentials clear_user_credentials;
+ worker::WorkerConfigs configs;
+ *configs.mutable_clear_user_credentials() = clear_user_credentials;
+
+ if (!WriteProtobuf(stdin_pipe_.get(), configs)) {
+ LOG(ERROR) << "Failed to send request to clear user credentials for worker "
+ << pid_;
+ return false;
+ }
+ return true;
+}
+
bool SandboxedWorker::Stop() {
if (is_being_terminated_)
return true;
diff --git a/system-proxy/sandboxed_worker.h b/system-proxy/sandboxed_worker.h
index e631c9d..e4b9e7b 100644
--- a/system-proxy/sandboxed_worker.h
+++ b/system-proxy/sandboxed_worker.h
@@ -45,6 +45,10 @@
// pipes and sets |local_proxy_host_and_port_|.
bool SetListeningAddress(uint32_t addr, int port);
+ // Sends a request to clear the user credentials to the worker via
+ // communication pipes.
+ bool ClearUserCredentials();
+
// Terminates the child process by sending a SIGTERM signal.
virtual bool Stop();
@@ -66,6 +70,7 @@
FRIEND_TEST(SystemProxyAdaptorTest, ProxyResolutionFilter);
FRIEND_TEST(SystemProxyAdaptorTest, ProtectionSpaceAuthenticationRequired);
FRIEND_TEST(SystemProxyAdaptorTest, ProtectionSpaceNoCredentials);
+ FRIEND_TEST(SystemProxyAdaptorTest, ClearUserCredentials);
void OnMessageReceived();
void OnErrorReceived();
diff --git a/system-proxy/server_proxy.cc b/system-proxy/server_proxy.cc
index 8c510ac..1db2772 100644
--- a/system-proxy/server_proxy.cc
+++ b/system-proxy/server_proxy.cc
@@ -198,6 +198,10 @@
unsetenv(kKrb5CCEnvKey);
}
}
+
+ if (config.has_clear_user_credentials()) {
+ auth_cache_.clear();
+ }
}
bool ServerProxy::HandleSignal(const struct signalfd_siginfo& siginfo) {
diff --git a/system-proxy/server_proxy.h b/system-proxy/server_proxy.h
index 4b9a96d..c3ca270 100644
--- a/system-proxy/server_proxy.h
+++ b/system-proxy/server_proxy.h
@@ -77,6 +77,7 @@
FRIEND_TEST(ServerProxyTest, HandlePendingAuthRequests);
FRIEND_TEST(ServerProxyTest, HandlePendingAuthRequestsCachedCredentials);
FRIEND_TEST(ServerProxyTest, HandlePendingAuthRequestsNoCredentials);
+ FRIEND_TEST(ServerProxyTest, ClearUserCredentials);
bool HandleSignal(const struct signalfd_siginfo& siginfo);
diff --git a/system-proxy/server_proxy_test.cc b/system-proxy/server_proxy_test.cc
index 4e01395..c358f1f 100644
--- a/system-proxy/server_proxy_test.cc
+++ b/system-proxy/server_proxy_test.cc
@@ -436,4 +436,27 @@
EXPECT_EQ("test_user:test_pwd", actual_credentials);
}
+// This test verifies that the stored credentials are removed when receiving a
+// |ClearUserCredentials| request.
+TEST_F(ServerProxyTest, ClearUserCredentials) {
+ worker::ProtectionSpace protection_space;
+ protection_space.set_origin(kFakeProxyAddress);
+ protection_space.set_scheme("Basic");
+ protection_space.set_realm("Proxy test realm");
+ // Add an entry in the cache.
+ server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
+ "test_user:test_pwd";
+
+ worker::ClearUserCredentials clear_user_credentials;
+ worker::WorkerConfigs configs;
+ *configs.mutable_clear_user_credentials() = clear_user_credentials;
+ // Redirect the worker stdin and stdout pipes.
+ RedirectStdPipes();
+ // Send the config to the worker's stdin input.
+ EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
+ brillo_loop_.RunOnce(false);
+ // Expect that the credentials were cleared.
+ EXPECT_EQ(0, server_proxy_->auth_cache_.size());
+}
+
} // namespace system_proxy
diff --git a/system-proxy/system_proxy_adaptor.cc b/system-proxy/system_proxy_adaptor.cc
index 4105130..2e0323f 100644
--- a/system-proxy/system_proxy_adaptor.cc
+++ b/system-proxy/system_proxy_adaptor.cc
@@ -8,6 +8,7 @@
#include <vector>
#include <base/location.h>
+#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <brillo/dbus/dbus_object.h>
#include <brillo/message_loops/message_loop.h>
@@ -145,15 +146,13 @@
LOG(INFO) << "Received shutdown request.";
std::string error_message;
- if (system_services_worker_ && system_services_worker_->IsRunning()) {
- if (!system_services_worker_->Stop())
- error_message =
- "Failure to terminate worker process for system services traffic.";
+ if (!ResetWorker(/* user_traffic=*/false)) {
+ error_message =
+ "Failure to terminate worker process for system services traffic.";
}
- if (arc_worker_ && arc_worker_->IsRunning()) {
- if (!arc_worker_->Stop())
- error_message += "Failure to terminate worker process for arc traffic.";
+ if (!ResetWorker(/* user_traffic=*/true)) {
+ error_message += "Failure to terminate worker process for arc traffic.";
}
ShutDownResponse response;
@@ -167,6 +166,35 @@
return SerializeProto(response);
}
+std::vector<uint8_t> SystemProxyAdaptor::ClearUserCredentials(
+ const std::vector<uint8_t>& request_blob) {
+ LOG(INFO) << "Received request to clear user credentials.";
+ std::string error_message;
+ ClearUserCredentials(/*user_traffic=*/false, &error_message);
+ ClearUserCredentials(/*user_traffic=*/true, &error_message);
+
+ ClearUserCredentialsResponse response;
+ if (!error_message.empty())
+ response.set_error_message(error_message);
+ return SerializeProto(response);
+}
+
+void SystemProxyAdaptor::ClearUserCredentials(bool user_traffic,
+ std::string* error_message) {
+ SandboxedWorker* worker = GetWorker(user_traffic);
+ if (!worker) {
+ return;
+ }
+ if (!worker->ClearUserCredentials()) {
+ error_message->append(
+ base::StringPrintf("Failure to clear user credentials for worker with "
+ "pid %s. Restarting worker.",
+ std::to_string(worker->pid()).c_str()));
+ ResetWorker(user_traffic);
+ CreateWorkerIfNeeded(user_traffic);
+ }
+}
+
void SystemProxyAdaptor::GetChromeProxyServersAsync(
const std::string& target_url,
const brillo::http::GetChromeProxyServersCallback& callback) {
@@ -234,6 +262,27 @@
return worker->Start();
}
+bool SystemProxyAdaptor::ResetWorker(bool user_traffic) {
+ SandboxedWorker* worker =
+ user_traffic ? arc_worker_.get() : system_services_worker_.get();
+ if (!worker) {
+ return true;
+ }
+ if (!worker->Stop()) {
+ return false;
+ }
+ if (user_traffic) {
+ arc_worker_.reset();
+ } else {
+ system_services_worker_.reset();
+ }
+ return true;
+}
+
+SandboxedWorker* SystemProxyAdaptor::GetWorker(bool user_traffic) {
+ return user_traffic ? arc_worker_.get() : system_services_worker_.get();
+}
+
// Called when the patchpanel D-Bus service becomes available.
void SystemProxyAdaptor::OnPatchpanelServiceAvailable(bool is_available) {
if (!is_available) {
@@ -247,7 +296,7 @@
void SystemProxyAdaptor::ConnectNamespace(SandboxedWorker* worker,
bool user_traffic) {
- DCHECK(system_services_worker_->IsRunning());
+ DCHECK(worker->IsRunning());
DCHECK_GT(netns_reconnect_attempts_available_, 0);
--netns_reconnect_attempts_available_;
// TODO(b/160736881, acostinas): Remove the delay after patchpanel
@@ -255,8 +304,7 @@
brillo::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&SystemProxyAdaptor::ConnectNamespaceTask,
- weak_ptr_factory_.GetWeakPtr(), worker,
- /* user_traffic= */ false),
+ weak_ptr_factory_.GetWeakPtr(), worker, user_traffic),
kConnectNamespaceDelay);
}
diff --git a/system-proxy/system_proxy_adaptor.h b/system-proxy/system_proxy_adaptor.h
index 8772112..942fa26 100644
--- a/system-proxy/system_proxy_adaptor.h
+++ b/system-proxy/system_proxy_adaptor.h
@@ -48,6 +48,8 @@
std::vector<uint8_t> SetAuthenticationDetails(
const std::vector<uint8_t>& request_blob) override;
std::vector<uint8_t> ShutDown() override;
+ std::vector<uint8_t> ClearUserCredentials(
+ const std::vector<uint8_t>& request_blob) override;
void GetChromeProxyServersAsync(
const std::string& target_url,
@@ -71,6 +73,8 @@
FRIEND_TEST(SystemProxyAdaptorTest, ProxyResolutionFilter);
FRIEND_TEST(SystemProxyAdaptorTest, ProtectionSpaceAuthenticationRequired);
FRIEND_TEST(SystemProxyAdaptorTest, ProtectionSpaceNoCredentials);
+ FRIEND_TEST(SystemProxyAdaptorTest, ClearUserCredentials);
+ FRIEND_TEST(SystemProxyAdaptorTest, ClearUserCredentialsRestartService);
void SetCredentialsTask(SandboxedWorker* worker,
const worker::Credentials& credentials);
@@ -85,11 +89,24 @@
bool StartWorker(SandboxedWorker* worker, bool user_traffic);
+ // Terminates the worker process for traffic indicated by |user_traffic| and
+ // frees the SandboxedWorker associated with it.
+ bool ResetWorker(bool user_traffic);
+
+ // Returns a pointer to the worker process associated with |user_traffic|. Can
+ // return nullptr.
+ SandboxedWorker* GetWorker(bool user_traffic);
+
// Checks if a worker process exists and if not creates one and sends a
// request to patchpanel to setup the network namespace for it. Returns true
// if the worker exists or was created successfully, false otherwise.
bool CreateWorkerIfNeeded(bool user_traffic);
+ // Sends a request to the worker process associated with |user_traffic| to
+ // clear the cached user credentials. If sending the request fails, the worker
+ // will be restarted.
+ void ClearUserCredentials(bool user_traffic, std::string* error_message);
+
// Called when the patchpanel D-Bus service becomes available.
void OnPatchpanelServiceAvailable(bool is_available);
diff --git a/system-proxy/system_proxy_adaptor_test.cc b/system-proxy/system_proxy_adaptor_test.cc
index e5a4a69..e5fa515 100644
--- a/system-proxy/system_proxy_adaptor_test.cc
+++ b/system-proxy/system_proxy_adaptor_test.cc
@@ -61,7 +61,10 @@
~FakeSandboxedWorker() override = default;
bool Start() override { return is_running_ = true; }
- bool Stop() override { return is_running_ = false; }
+ bool Stop() override {
+ is_running_ = false;
+ return true;
+ }
bool IsRunning() override { return is_running_; }
std::string local_proxy_host_and_port() override {
@@ -83,6 +86,7 @@
protected:
std::unique_ptr<SandboxedWorker> CreateWorker() override {
+ ++create_worker_count_;
return std::make_unique<FakeSandboxedWorker>(
weak_ptr_factory_.GetWeakPtr());
}
@@ -96,7 +100,10 @@
FRIEND_TEST(SystemProxyAdaptorTest, ProxyResolutionFilter);
FRIEND_TEST(SystemProxyAdaptorTest, ProtectionSpaceAuthenticationRequired);
FRIEND_TEST(SystemProxyAdaptorTest, ProtectionSpaceNoCredentials);
+ FRIEND_TEST(SystemProxyAdaptorTest, ClearUserCredentials);
+ FRIEND_TEST(SystemProxyAdaptorTest, ClearUserCredentialsRestartService);
+ int create_worker_count_ = 0;
base::WeakPtrFactory<FakeSystemProxyAdaptor> weak_ptr_factory_;
};
@@ -297,7 +304,7 @@
EXPECT_TRUE(adaptor_->system_services_worker_->IsRunning());
adaptor_->ShutDown();
- EXPECT_FALSE(adaptor_->system_services_worker_->IsRunning());
+ EXPECT_FALSE(adaptor_->system_services_worker_);
}
TEST_F(SystemProxyAdaptorTest, ConnectNamespace) {
@@ -414,4 +421,48 @@
protection_space.SerializeAsString());
}
+TEST_F(SystemProxyAdaptorTest, ClearUserCredentials) {
+ adaptor_->system_services_worker_ = adaptor_->CreateWorker();
+ ASSERT_TRUE(adaptor_->system_services_worker_.get());
+
+ int fds[2];
+ ASSERT_TRUE(base::CreateLocalNonBlockingPipe(fds));
+ base::ScopedFD read_scoped_fd(fds[0]);
+ // Reset the worker stdin pipe to read the input from the other endpoint.
+ adaptor_->system_services_worker_->stdin_pipe_.reset(fds[1]);
+
+ ClearUserCredentialsRequest request;
+ std::vector<uint8_t> proto_blob(request.ByteSizeLong());
+ request.SerializeToArray(proto_blob.data(), proto_blob.size());
+ adaptor_->ClearUserCredentials(proto_blob);
+ brillo_loop_.RunOnce(false);
+
+ // Expect that a request to clear user credentials has been sent to the
+ // worker.
+ worker::WorkerConfigs config;
+ ASSERT_TRUE(ReadProtobuf(read_scoped_fd.get(), &config));
+ EXPECT_TRUE(config.has_clear_user_credentials());
+}
+
+// Tests that the sandboxed worker is restarted if the request to clear the
+// credentials fails.
+TEST_F(SystemProxyAdaptorTest, ClearUserCredentialsRestartService) {
+ EXPECT_CALL(*bus_, GetObjectProxy(patchpanel::kPatchPanelServiceName, _))
+ .WillOnce(Return(mock_patchpanel_proxy_.get()));
+
+ adaptor_->system_services_worker_ = adaptor_->CreateWorker();
+ ASSERT_TRUE(adaptor_->system_services_worker_.get());
+ EXPECT_EQ(1, adaptor_->create_worker_count_);
+
+ ClearUserCredentialsRequest request;
+ std::vector<uint8_t> proto_blob(request.ByteSizeLong());
+ request.SerializeToArray(proto_blob.data(), proto_blob.size());
+ // This request will fail because we didn't set up a communication pipe.
+ adaptor_->ClearUserCredentials(proto_blob);
+ brillo_loop_.RunOnce(false);
+
+ ASSERT_TRUE(adaptor_->system_services_worker_.get());
+ EXPECT_EQ(2, adaptor_->create_worker_count_);
+}
+
} // namespace system_proxy