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