#include "chrome/browser/ash/net/system_proxy_manager.h"
#include <string>
#include "ash/constants/ash_features.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "chrome/browser/ash/notifications/request_system_proxy_credentials_view.h"
#include "chrome/browser/ash/notifications/system_proxy_notification.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/login/login_display_host.h"
#include "chrome/browser/ui/login/login_handler.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/dbus/system_proxy/system_proxy_client.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/proxy/proxy_config_service_impl.h"
#include "chromeos/ash/components/network/proxy/ui_proxy_config_service.h"
#include "chromeos/ash/experiences/arc/arc_prefs.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/host_port_pair.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/http/http_auth_scheme.h"
#include "net/http/http_util.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "ui/aura/window.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
namespace ash {
namespace {
const char kSystemProxyService[] = "system-proxy-service";
class SystemProxyLoginHandler : public content::LoginDelegate {
public:
SystemProxyLoginHandler() = default;
~SystemProxyLoginHandler() override = default;
SystemProxyLoginHandler(const SystemProxyLoginHandler&) = delete;
SystemProxyLoginHandler& operator=(const SystemProxyLoginHandler&) = delete;
void AuthenticateWithCredentials(
const std::string& username,
const std::string& password,
content::LoginDelegate::LoginAuthRequiredCallback
auth_required_callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&SystemProxyLoginHandler::InvokeWithCredentials,
weak_factory_.GetWeakPtr(), username, password,
std::move(auth_required_callback)));
}
private:
void InvokeWithCredentials(const std::string& username,
const std::string& password,
content::LoginDelegate::LoginAuthRequiredCallback
auth_required_callback) {
std::move(auth_required_callback)
.Run(std::make_optional<net::AuthCredentials>(
base::UTF8ToUTF16(username), base::UTF8ToUTF16(password)));
}
base::WeakPtrFactory<SystemProxyLoginHandler> weak_factory_{this};
};
SystemProxyManager::SystemProxyState DetermineSystemProxyState(
bool policy_enabled) {
if (policy_enabled)
return SystemProxyManager::SystemProxyState::kEnabledForAll;
if (base::FeatureList::IsEnabled(features::kSystemProxyForSystemServices)) {
return SystemProxyManager::SystemProxyState::kEnabledForSystemServices;
}
return SystemProxyManager::SystemProxyState::kDisabled;
}
SystemProxyManager* g_system_proxy_manager_ = nullptr;
}
SystemProxyManager::SystemProxyManager(PrefService* local_state) {
SystemProxyClient::Get()->SetWorkerActiveSignalCallback(base::BindRepeating(
&SystemProxyManager::OnWorkerActive, weak_factory_.GetWeakPtr()));
SystemProxyClient::Get()->SetAuthenticationRequiredSignalCallback(
base::BindRepeating(&SystemProxyManager::OnAuthenticationRequired,
weak_factory_.GetWeakPtr()));
SystemProxyClient::Get()->ConnectToWorkerSignals();
local_state_ = local_state;
local_state_pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
local_state_pref_change_registrar_->Init(local_state_);
local_state_pref_change_registrar_->Add(
prefs::kKerberosEnabled,
base::BindRepeating(&SystemProxyManager::OnKerberosEnabledChanged,
weak_factory_.GetWeakPtr()));
DCHECK(NetworkHandler::IsInitialized());
network_state_handler_observer_.Observe(
NetworkHandler::Get()->network_state_handler());
system_proxy_state_ = DetermineSystemProxyState(false);
if (system_proxy_state_ == SystemProxyState::kEnabledForSystemServices) {
SendPolicyAuthenticationCredentials("",
"",
true);
}
}
SystemProxyManager::~SystemProxyManager() {
if (IsEnabled()) {
SendShutDownRequest(system_proxy::TrafficOrigin::ALL);
}
DCHECK(NetworkHandler::IsInitialized());
}
void SystemProxyManager::Initialize(PrefService* local_state) {
g_system_proxy_manager_ = new SystemProxyManager(local_state);
}
SystemProxyManager* SystemProxyManager::Get() {
return g_system_proxy_manager_;
}
void SystemProxyManager::Shutdown() {
if (g_system_proxy_manager_) {
delete g_system_proxy_manager_;
g_system_proxy_manager_ = nullptr;
}
}
std::string SystemProxyManager::SystemServicesProxyPacString(
chromeos::SystemProxyOverride system_proxy_override) const {
if (system_proxy_override == chromeos::SystemProxyOverride::kOptOut ||
system_services_address_.empty()) {
return std::string();
}
if (system_proxy_state_ == SystemProxyState::kEnabledForAll ||
(system_proxy_state_ == SystemProxyState::kEnabledForSystemServices &&
system_proxy_override == chromeos::SystemProxyOverride::kOptIn)) {
return "PROXY " + system_services_address_;
}
return std::string();
}
void SystemProxyManager::StartObservingPrimaryProfilePrefs(Profile* profile) {
primary_profile_ = profile;
extension_prefs_util_ = std::make_unique<extensions::PrefsUtil>(profile);
profile_pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
profile_pref_change_registrar_->Init(primary_profile_->GetPrefs());
profile_pref_change_registrar_->Add(
prefs::kKerberosActivePrincipalName,
base::BindRepeating(&SystemProxyManager::OnKerberosAccountChanged,
base::Unretained(this)));
profile_pref_change_registrar_->Add(
arc::prefs::kArcEnabled,
base::BindRepeating(&SystemProxyManager::OnArcEnabledChanged,
weak_factory_.GetWeakPtr()));
profile_pref_change_registrar_->Add(
proxy_config::prefs::kProxy,
base::BindRepeating(&SystemProxyManager::OnProxyConfigChanged,
base::Unretained(this)));
if (IsEnabled()) {
OnProxyConfigChanged();
OnKerberosAccountChanged();
}
if (system_proxy_state_ == SystemProxyState::kEnabledForAll) {
OnArcEnabledChanged();
}
}
void SystemProxyManager::StopObservingPrimaryProfilePrefs() {
profile_pref_change_registrar_->RemoveAll();
profile_pref_change_registrar_.reset();
extension_prefs_util_.reset();
primary_profile_ = nullptr;
}
void SystemProxyManager::ClearUserCredentials() {
if (!IsEnabled()) {
return;
}
system_proxy::ClearUserCredentialsRequest request;
SystemProxyClient::Get()->ClearUserCredentials(
request, base::BindOnce(&SystemProxyManager::OnClearUserCredentials,
weak_factory_.GetWeakPtr()));
}
void SystemProxyManager::SetPolicySettings(
bool system_proxy_enabled,
const std::string& system_services_username,
const std::string& system_services_password,
const std::vector<std::string>& auth_schemes) {
system_services_username_ = system_services_username;
system_services_password_ = system_services_password;
policy_credentials_auth_schemes_ = auth_schemes;
if (system_proxy_state_ == SystemProxyState::kDisabled &&
!system_proxy_enabled) {
return;
}
system_proxy_state_ = DetermineSystemProxyState(system_proxy_enabled);
if (system_proxy_state_ == SystemProxyState::kDisabled) {
system_services_address_.clear();
SetUserTrafficProxyPref(std::string());
CloseAuthenticationUI();
SendShutDownRequest(system_proxy::TrafficOrigin::ALL);
return;
}
if (system_proxy_state_ == SystemProxyState::kEnabledForSystemServices) {
SendPolicyAuthenticationCredentials("",
"",
true);
SetUserTrafficProxyPref(std::string());
SendShutDownRequest(system_proxy::TrafficOrigin::USER);
}
if (IsManagedProxyConfigured() &&
system_proxy_state_ == SystemProxyState::kEnabledForAll) {
SendPolicyAuthenticationCredentials(system_services_username_,
system_services_password_,
true);
} else {
SendPolicyAuthenticationCredentials("",
"",
true);
}
if (IsArcEnabled() &&
system_proxy_state_ == SystemProxyState::kEnabledForAll) {
OnArcEnabledChanged();
}
}
void SystemProxyManager::OnKerberosEnabledChanged() {
SendKerberosAuthenticationDetails();
}
void SystemProxyManager::OnKerberosAccountChanged() {
if (!local_state_->GetBoolean(prefs::kKerberosEnabled)) {
return;
}
SendKerberosAuthenticationDetails();
}
void SystemProxyManager::OnArcEnabledChanged() {
if (system_proxy_state_ != SystemProxyState::kEnabledForAll) {
return;
}
if (!IsArcEnabled()) {
system_proxy::ShutDownRequest request;
request.set_traffic_type(system_proxy::TrafficOrigin::USER);
SystemProxyClient::Get()->ShutDownProcess(
request, base::BindOnce(&SystemProxyManager::OnShutDownProcess,
weak_factory_.GetWeakPtr()));
return;
}
if (local_state_->GetBoolean(prefs::kKerberosEnabled)) {
SendKerberosAuthenticationDetails();
return;
}
system_proxy::SetAuthenticationDetailsRequest request;
request.set_traffic_type(system_proxy::TrafficOrigin::USER);
SystemProxyClient::Get()->SetAuthenticationDetails(
request, base::BindOnce(&SystemProxyManager::OnSetAuthenticationDetails,
weak_factory_.GetWeakPtr()));
}
bool SystemProxyManager::IsArcEnabled() const {
return primary_profile_ &&
primary_profile_->GetPrefs()->GetBoolean(arc::prefs::kArcEnabled);
}
bool SystemProxyManager::IsEnabled() const {
return system_proxy_state_ != SystemProxyState::kDisabled;
}
void SystemProxyManager::SendUserAuthenticationCredentials(
const system_proxy::ProtectionSpace& protection_space,
const std::string& username,
const std::string& password) {
if (!IsEnabled()) {
return;
}
system_proxy::Credentials user_credentials;
user_credentials.set_username(username);
user_credentials.set_password(password);
system_proxy::SetAuthenticationDetailsRequest request;
request.set_traffic_type(
IsArcEnabled() && system_proxy_state_ == SystemProxyState::kEnabledForAll
? system_proxy::TrafficOrigin::ALL
: system_proxy::TrafficOrigin::SYSTEM);
*request.mutable_credentials() = user_credentials;
*request.mutable_protection_space() = protection_space;
SystemProxyClient::Get()->SetAuthenticationDetails(
request, base::BindOnce(&SystemProxyManager::OnSetAuthenticationDetails,
weak_factory_.GetWeakPtr()));
}
void SystemProxyManager::SendPolicyAuthenticationCredentials(
const std::string& username,
const std::string& password,
bool force_send) {
if (!IsEnabled())
return;
if (!force_send &&
(last_sent_username_ == username && last_sent_password_ == password)) {
return;
}
last_sent_username_ = username;
last_sent_password_ = password;
system_proxy::SetAuthenticationDetailsRequest request;
system_proxy::Credentials credentials;
credentials.set_username(username);
credentials.set_password(password);
for (const auto& auth_scheme : policy_credentials_auth_schemes_) {
credentials.add_policy_credentials_auth_schemes(auth_scheme);
}
*request.mutable_credentials() = credentials;
request.set_traffic_type(system_proxy::TrafficOrigin::SYSTEM);
SystemProxyClient::Get()->SetAuthenticationDetails(
request, base::BindOnce(&SystemProxyManager::OnSetAuthenticationDetails,
weak_factory_.GetWeakPtr()));
}
void SystemProxyManager::SendKerberosAuthenticationDetails() {
if (!IsEnabled()) {
return;
}
system_proxy::SetAuthenticationDetailsRequest request;
request.set_traffic_type(
IsArcEnabled() && system_proxy_state_ == SystemProxyState::kEnabledForAll
? system_proxy::TrafficOrigin::ALL
: system_proxy::TrafficOrigin::SYSTEM);
request.set_kerberos_enabled(
local_state_->GetBoolean(prefs::kKerberosEnabled));
if (primary_profile_) {
request.set_active_principal_name(
primary_profile_->GetPrefs()
->GetValue(prefs::kKerberosActivePrincipalName)
.GetString());
}
SystemProxyClient::Get()->SetAuthenticationDetails(
request, base::BindOnce(&SystemProxyManager::OnSetAuthenticationDetails,
weak_factory_.GetWeakPtr()));
}
void SystemProxyManager::SendEmptyCredentials(
const system_proxy::ProtectionSpace& protection_space) {
SendUserAuthenticationCredentials(protection_space,
std::string(),
std::string());
}
void SystemProxyManager::SendShutDownRequest(
system_proxy::TrafficOrigin traffic) {
system_proxy::ShutDownRequest request;
request.set_traffic_type(traffic);
SystemProxyClient::Get()->ShutDownProcess(
request, base::BindOnce(&SystemProxyManager::OnShutDownProcess,
weak_factory_.GetWeakPtr()));
}
void SystemProxyManager::SetSystemProxyEnabledForTest(bool enabled) {
system_proxy_state_ =
enabled ? SystemProxyState::kEnabledForAll : SystemProxyState::kDisabled;
}
void SystemProxyManager::SetSystemServicesProxyUrlForTest(
const std::string& local_proxy_url) {
system_services_address_ = local_proxy_url;
}
void SystemProxyManager::SetSendAuthDetailsClosureForTest(
base::RepeatingClosure closure) {
send_auth_details_closure_for_test_ = closure;
}
RequestSystemProxyCredentialsView*
SystemProxyManager::GetActiveAuthDialogForTest() {
return active_auth_dialog_;
}
void SystemProxyManager::CloseAuthDialogForTest() {
DCHECK(auth_widget_);
auth_widget_->CloseNow();
}
void SystemProxyManager::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kSystemProxyUserTrafficHostAndPort,
std::string());
}
bool SystemProxyManager::CanUsePolicyCredentials(
const net::AuthChallengeInfo& auth_info,
bool first_auth_attempt) {
if (!auth_info.is_proxy || !first_auth_attempt) {
return false;
}
if (!LoginState::IsInitialized() ||
(!LoginState::Get()->IsManagedGuestSessionUser() &&
!LoginState::Get()->IsKioskSession())) {
VLOG(1) << "Only kiosk app and MGS can reuse the policy provided proxy "
"credentials for authentication";
return false;
}
if (system_proxy_state_ != SystemProxyState::kEnabledForAll ||
system_services_username_.empty() || system_services_password_.empty()) {
return false;
}
if (!IsManagedProxyConfigured())
return false;
if (!policy_credentials_auth_schemes_.empty()) {
if (!base::Contains(policy_credentials_auth_schemes_, auth_info.scheme)) {
VLOG(1) << "Auth scheme not allowed by policy";
return false;
}
}
return true;
}
std::unique_ptr<content::LoginDelegate> SystemProxyManager::CreateLoginDelegate(
content::LoginDelegate::LoginAuthRequiredCallback auth_required_callback) {
auto login_delegate = std::make_unique<SystemProxyLoginHandler>();
login_delegate->AuthenticateWithCredentials(
system_services_username_, system_services_password_,
std::move(auth_required_callback));
return std::move(login_delegate);
}
void SystemProxyManager::OnSetAuthenticationDetails(
const system_proxy::SetAuthenticationDetailsResponse& response) {
if (response.has_error_message()) {
NET_LOG(ERROR)
<< "Failed to set system traffic credentials for system proxy: "
<< kSystemProxyService << ", Error: " << response.error_message();
}
if (send_auth_details_closure_for_test_)
send_auth_details_closure_for_test_.Run();
}
void SystemProxyManager::DefaultNetworkChanged(const NetworkState* network) {
if (!network)
return;
OnProxyConfigChanged();
}
void SystemProxyManager::OnProxyConfigChanged() {
if (!IsManagedProxyConfigured()) {
SendPolicyAuthenticationCredentials("", "",
false);
return;
}
SendPolicyAuthenticationCredentials(system_services_username_,
system_services_password_,
false);
}
bool SystemProxyManager::IsManagedProxyConfigured() {
DCHECK(NetworkHandler::IsInitialized());
NetworkHandler* network_handler = NetworkHandler::Get();
base::Value::Dict proxy_settings;
if (NetworkHandler::HasUiProxyConfigService() &&
network_handler->network_state_handler()->DefaultNetwork()) {
NetworkHandler::GetUiProxyConfigService()->MergeEnforcedProxyConfig(
network_handler->network_state_handler()->DefaultNetwork()->guid(),
&proxy_settings);
}
if (proxy_settings.empty())
return false;
if (IsProxyConfiguredByUserViaExtension())
return false;
return true;
}
bool SystemProxyManager::IsProxyConfiguredByUserViaExtension() {
if (!extension_prefs_util_)
return false;
std::optional<extensions::api::settings_private::PrefObject> pref =
extension_prefs_util_->GetPref(proxy_config::prefs::kProxy);
return pref && pref->extension_can_be_disabled &&
*pref->extension_can_be_disabled;
}
void SystemProxyManager::OnShutDownProcess(
const system_proxy::ShutDownResponse& response) {
if (response.has_error_message() && !response.error_message().empty()) {
NET_LOG(ERROR) << "Failed to shutdown system proxy process: "
<< kSystemProxyService
<< ", error: " << response.error_message();
}
}
void SystemProxyManager::OnClearUserCredentials(
const system_proxy::ClearUserCredentialsResponse& response) {
if (response.has_error_message() && !response.error_message().empty()) {
NET_LOG(ERROR) << "Failed to clear user credentials: "
<< kSystemProxyService
<< ", error: " << response.error_message();
}
}
void SystemProxyManager::OnWorkerActive(
const system_proxy::WorkerActiveSignalDetails& details) {
if (details.traffic_origin() == system_proxy::TrafficOrigin::SYSTEM) {
system_services_address_ = details.local_proxy_url();
return;
}
if (system_proxy_state_ != SystemProxyState::kEnabledForAll)
return;
SetUserTrafficProxyPref(details.local_proxy_url());
}
void SystemProxyManager::SetUserTrafficProxyPref(
const std::string& user_traffic_address) {
if (!primary_profile_) {
return;
}
primary_profile_->GetPrefs()->SetString(
prefs::kSystemProxyUserTrafficHostAndPort, user_traffic_address);
}
void SystemProxyManager::OnAuthenticationRequired(
const system_proxy::AuthenticationRequiredDetails& details) {
system_proxy::ProtectionSpace protection_space =
details.proxy_protection_space();
if (!primary_profile_) {
SendEmptyCredentials(protection_space);
return;
}
if (details.has_bad_cached_credentials() &&
details.bad_cached_credentials()) {
ShowAuthenticationNotification(protection_space,
details.bad_cached_credentials());
return;
}
net::ProxyServer proxy_server = net::ProxyUriToProxyServer(
protection_space.origin(), net::ProxyServer::Scheme::SCHEME_HTTP);
if (!proxy_server.is_valid()) {
SendEmptyCredentials(protection_space);
return;
}
primary_profile_->GetDefaultStoragePartition()
->GetNetworkContext()
->LookupProxyAuthCredentials(
proxy_server, protection_space.scheme(),
net::HttpUtil::Unquote(protection_space.realm()),
base::BindOnce(
&SystemProxyManager::LookupProxyAuthCredentialsCallback,
weak_factory_.GetWeakPtr(), protection_space));
}
void SystemProxyManager::LookupProxyAuthCredentialsCallback(
const system_proxy::ProtectionSpace& protection_space,
const std::optional<net::AuthCredentials>& credentials) {
if (!credentials) {
ShowAuthenticationNotification(protection_space, false);
return;
}
std::string username;
std::string password;
if (credentials) {
username = base::UTF16ToUTF8(credentials->username());
password = base::UTF16ToUTF8(credentials->password());
if (notification_handler_ ||
(active_auth_dialog_ &&
active_auth_dialog_->GetProxyServer() == protection_space.origin())) {
CloseAuthenticationUI();
}
}
SendUserAuthenticationCredentials(protection_space, username, password);
}
void SystemProxyManager::ShowAuthenticationNotification(
const system_proxy::ProtectionSpace& protection_space,
bool show_error) {
if (active_auth_dialog_)
return;
notification_handler_ = std::make_unique<SystemProxyNotification>(
protection_space, show_error,
base::BindOnce(&SystemProxyManager::ShowAuthenticationDialog,
weak_factory_.GetWeakPtr()));
notification_handler_->Show();
}
void SystemProxyManager::ShowAuthenticationDialog(
const system_proxy::ProtectionSpace& protection_space,
bool show_error_label) {
if (active_auth_dialog_)
return;
if (notification_handler_)
notification_handler_->Close();
active_auth_dialog_ = new RequestSystemProxyCredentialsView(
protection_space.origin(), show_error_label,
base::BindOnce(&SystemProxyManager::OnDialogClosed,
weak_factory_.GetWeakPtr(), protection_space));
active_auth_dialog_->SetAcceptCallback(
base::BindRepeating(&SystemProxyManager::OnDialogAccepted,
weak_factory_.GetWeakPtr(), protection_space));
active_auth_dialog_->SetCancelCallback(
base::BindRepeating(&SystemProxyManager::OnDialogCanceled,
weak_factory_.GetWeakPtr(), protection_space));
auth_widget_ = views::DialogDelegate::CreateDialogWidget(
active_auth_dialog_, nullptr, nullptr);
auth_widget_->Show();
}
void SystemProxyManager::OnDialogAccepted(
const system_proxy::ProtectionSpace& protection_space) {
SendUserAuthenticationCredentials(
protection_space, base::UTF16ToUTF8(active_auth_dialog_->GetUsername()),
base::UTF16ToUTF8(active_auth_dialog_->GetPassword()));
}
void SystemProxyManager::OnDialogCanceled(
const system_proxy::ProtectionSpace& protection_space) {
SendEmptyCredentials(protection_space);
}
void SystemProxyManager::OnDialogClosed(
const system_proxy::ProtectionSpace& protection_space) {
active_auth_dialog_ = nullptr;
auth_widget_ = nullptr;
}
void SystemProxyManager::CloseAuthenticationUI() {
if (notification_handler_) {
notification_handler_->Close();
notification_handler_.reset();
}
if (!auth_widget_)
return;
auth_widget_->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
}
}