1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/policy/hostname_handler.h"
6
7 #include "base/bind.h"
8 #include "base/strings/string_util.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/browser_process_platform_part.h"
11 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
12 #include "chromeos/network/device_state.h"
13 #include "chromeos/network/network_handler.h"
14 #include "chromeos/network/network_state.h"
15 #include "chromeos/network/network_state_handler.h"
16 #include "chromeos/settings/cros_settings_names.h"
17 #include "chromeos/settings/cros_settings_provider.h"
18 #include "chromeos/system/statistics_provider.h"
19
20 namespace {
21
22 constexpr char kAssetIDPlaceholder[] = "${ASSET_ID}";
23 constexpr char kMachineNamePlaceholder[] = "${MACHINE_NAME}";
24 constexpr char kSerialNumPlaceholder[] = "${SERIAL_NUM}";
25 constexpr char kMACAddressPlaceholder[] = "${MAC_ADDR}";
26 constexpr char kLocationPlaceholder[] = "${LOCATION}";
27
28 // As per RFC 1035, hostname should be 63 characters or less.
29 const int kMaxHostnameLength = 63;
30
IsValidHostnameCharacter(char c)31 bool inline IsValidHostnameCharacter(char c) {
32 return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) || c == '_' || c == '-';
33 }
34
IsValidHostname(const std::string & hostname)35 bool IsValidHostname(const std::string& hostname) {
36 if ((hostname.size() > kMaxHostnameLength) || (hostname.size() == 0))
37 return false;
38 if (hostname[0] == '-')
39 return false; // '-' is not valid for the first char
40 for (const char& c : hostname) {
41 if (!IsValidHostnameCharacter(c))
42 return false;
43 }
44 return true;
45 }
46
47 } // namespace
48
49 namespace policy {
50
HostnameHandler(chromeos::CrosSettings * cros_settings)51 HostnameHandler::HostnameHandler(chromeos::CrosSettings* cros_settings)
52 : cros_settings_(cros_settings) {
53 policy_subscription_ = cros_settings_->AddSettingsObserver(
54 chromeos::kDeviceHostnameTemplate,
55 base::BindRepeating(&HostnameHandler::OnDeviceHostnamePropertyChanged,
56 weak_factory_.GetWeakPtr()));
57 chromeos::NetworkHandler::Get()->network_state_handler()->AddObserver(
58 this, FROM_HERE);
59
60 // Fire it once so we're sure we get an invocation on startup.
61 OnDeviceHostnamePropertyChanged();
62 }
63
~HostnameHandler()64 HostnameHandler::~HostnameHandler() {}
65
Shutdown()66 void HostnameHandler::Shutdown() {
67 if (chromeos::NetworkHandler::IsInitialized()) {
68 chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
69 this, FROM_HERE);
70 }
71 }
72
GetDeviceHostname() const73 const std::string& HostnameHandler::GetDeviceHostname() const {
74 return hostname_;
75 }
76
77 // static
FormatHostname(const std::string & name_template,const std::string & asset_id,const std::string & serial,const std::string & mac,const std::string & machine_name,const std::string & location)78 std::string HostnameHandler::FormatHostname(const std::string& name_template,
79 const std::string& asset_id,
80 const std::string& serial,
81 const std::string& mac,
82 const std::string& machine_name,
83 const std::string& location) {
84 std::string result = name_template;
85 base::ReplaceSubstringsAfterOffset(&result, 0, kAssetIDPlaceholder, asset_id);
86 base::ReplaceSubstringsAfterOffset(&result, 0, kSerialNumPlaceholder, serial);
87 base::ReplaceSubstringsAfterOffset(&result, 0, kMACAddressPlaceholder, mac);
88 base::ReplaceSubstringsAfterOffset(&result, 0, kMachineNamePlaceholder,
89 machine_name);
90 base::ReplaceSubstringsAfterOffset(&result, 0, kLocationPlaceholder,
91 location);
92
93 if (!IsValidHostname(result))
94 return std::string();
95 return result;
96 }
97
DefaultNetworkChanged(const chromeos::NetworkState * network)98 void HostnameHandler::DefaultNetworkChanged(
99 const chromeos::NetworkState* network) {
100 OnDeviceHostnamePropertyChanged();
101 }
102
OnDeviceHostnamePropertyChanged()103 void HostnameHandler::OnDeviceHostnamePropertyChanged() {
104 chromeos::CrosSettingsProvider::TrustedStatus status =
105 cros_settings_->PrepareTrustedValues(
106 base::BindOnce(&HostnameHandler::OnDeviceHostnamePropertyChanged,
107 weak_factory_.GetWeakPtr()));
108 if (status != chromeos::CrosSettingsProvider::TRUSTED)
109 return;
110
111 // Continue when machine statistics are loaded, to avoid blocking.
112 chromeos::system::StatisticsProvider::GetInstance()
113 ->ScheduleOnMachineStatisticsLoaded(base::BindOnce(
114 &HostnameHandler::
115 OnDeviceHostnamePropertyChangedAndMachineStatisticsLoaded,
116 weak_factory_.GetWeakPtr()));
117 }
118
119 void HostnameHandler::
OnDeviceHostnamePropertyChangedAndMachineStatisticsLoaded()120 OnDeviceHostnamePropertyChangedAndMachineStatisticsLoaded() {
121 std::string hostname_template;
122 if (!cros_settings_->GetString(chromeos::kDeviceHostnameTemplate,
123 &hostname_template)) {
124 // Do not set an empty hostname (which would overwrite any custom hostname
125 // set) if DeviceHostnameTemplate is not specified by policy.
126 return;
127 }
128
129 const std::string serial = chromeos::system::StatisticsProvider::GetInstance()
130 ->GetEnterpriseMachineID();
131
132 const std::string asset_id = g_browser_process->platform_part()
133 ->browser_policy_connector_chromeos()
134 ->GetDeviceAssetID();
135
136 const std::string machine_name = g_browser_process->platform_part()
137 ->browser_policy_connector_chromeos()
138 ->GetMachineName();
139
140 const std::string location = g_browser_process->platform_part()
141 ->browser_policy_connector_chromeos()
142 ->GetDeviceAnnotatedLocation();
143
144 chromeos::NetworkStateHandler* handler =
145 chromeos::NetworkHandler::Get()->network_state_handler();
146
147 std::string mac = "MAC_unknown";
148 const chromeos::NetworkState* network = handler->DefaultNetwork();
149 if (network) {
150 const chromeos::DeviceState* device =
151 handler->GetDeviceState(network->device_path());
152 if (device) {
153 mac = device->mac_address();
154 base::ReplaceSubstringsAfterOffset(&mac, 0, ":", "");
155 }
156 }
157
158 hostname_ = FormatHostname(hostname_template, asset_id, serial, mac,
159 machine_name, location);
160 handler->SetHostname(hostname_);
161 }
162
163 } // namespace policy
164