1 // Copyright 2020 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/credential_provider/setup/setup_utils.h"
6
7 #include <Windows.h>
8
9 #include <string>
10
11 #include "base/files/file_util.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/registry.h"
15 #include "chrome/common/chrome_version.h"
16 #include "chrome/credential_provider/common/gcp_strings.h"
17 #include "chrome/credential_provider/gaiacp/gaia_resources.h"
18 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
19 #include "chrome/credential_provider/gaiacp/logging.h"
20 #include "chrome/credential_provider/gaiacp/reg_utils.h"
21 #include "chrome/credential_provider/setup/gcpw_files.h"
22
23 namespace credential_provider {
24
25 namespace switches {
26
27 const char kParentHandle[] = "parent-handle";
28 const char kInstallPath[] = "install-path";
29 const char kUninstall[] = "uninstall";
30
31 const char kEnableStats[] = "enable-stats";
32 const char kDisableStats[] = "disable-stats";
33
34 const char kInstallerData[] = "installerdata";
35
36 const char kStandaloneInstall[] = "standalone";
37 } // namespace switches
38
39 namespace {
40
41 // Path to the msi json value inside the dictionary which is parsed from
42 // installer data argument in the command line.
43 const char kMsiJsonPath[] = "distribution.msi";
44
45 // The registry name which is saved to indicate installation source.
46 const wchar_t kMsiInstall[] = L"msi";
47
48 // Parses the json data and returns it as a dictionary. If the json data isn't
49 // valid, returns nullptr.
ParseDistributionPreferences(const std::string & json_data)50 base::DictionaryValue* ParseDistributionPreferences(
51 const std::string& json_data) {
52 JSONStringValueDeserializer json(json_data);
53 std::string error;
54 std::unique_ptr<base::Value> root(json.Deserialize(nullptr, &error));
55 if (!root.get()) {
56 LOGFN(WARNING) << "Failed to parse initial prefs file: " << error;
57 return nullptr;
58 }
59 if (!root->is_dict()) {
60 LOGFN(WARNING) << "Failed to parse installer data file";
61 return nullptr;
62 }
63 return static_cast<base::DictionaryValue*>(root.release());
64 }
65
66 } // namespace
67
StandaloneInstallerConfigurator()68 StandaloneInstallerConfigurator::StandaloneInstallerConfigurator()
69 : is_msi_installation_(false) {}
70
~StandaloneInstallerConfigurator()71 StandaloneInstallerConfigurator::~StandaloneInstallerConfigurator() {}
72
73 // static
74 StandaloneInstallerConfigurator**
GetInstanceStorage()75 StandaloneInstallerConfigurator::GetInstanceStorage() {
76 static StandaloneInstallerConfigurator* instance =
77 new StandaloneInstallerConfigurator();
78
79 return &instance;
80 }
81
82 // static
Get()83 StandaloneInstallerConfigurator* StandaloneInstallerConfigurator::Get() {
84 return *GetInstanceStorage();
85 }
86
87 // Sets the installer source for GCPW. When installed through MSI,
88 // contains installer data file name as argument.
ConfigureInstallationType(const base::CommandLine & cmdline)89 void StandaloneInstallerConfigurator::ConfigureInstallationType(
90 const base::CommandLine& cmdline) {
91 // There are following scenarios for installations:
92 // First time install from MSI
93 // First time install from EXE
94 // MSIs before this kMsiInstall registry gets auto-updated
95 // MSIs with kMsiInstall registry gets auto-updated
96 // EXEs with kMsiInstall registry gets auto-updated
97
98 // |kStandaloneInstall| indicates fresh installation.
99 if (cmdline.HasSwitch(switches::kStandaloneInstall)) {
100 base::Value* is_msi = nullptr;
101 if (cmdline.HasSwitch(switches::kInstallerData)) {
102 base::FilePath prefs_path(
103 cmdline.GetSwitchValuePath(switches::kInstallerData));
104
105 if (InitializeFromInstallerData(prefs_path))
106 is_msi = installer_data_dictionary_->FindPath(kMsiJsonPath);
107 }
108
109 is_msi_installation_ = false;
110 if (is_msi && is_msi->is_bool() && is_msi->GetBool()) {
111 is_msi_installation_ = true;
112 }
113 } else {
114 // Honor the registry if it is found, otherwise fall back to MSI
115 // installation.
116 is_msi_installation_ =
117 GetUpdaterClientsAppPathFlagOrDefault(kMsiInstall, 1);
118 }
119
120 HRESULT hr =
121 SetUpdaterClientsAppPathFlag(kMsiInstall, is_msi_installation_ ? 1 : 0);
122 if (FAILED(hr))
123 LOGFN(ERROR) << "SetGlobalFlag failed" << putHR(hr);
124 }
125
GetCurrentDate()126 base::string16 StandaloneInstallerConfigurator::GetCurrentDate() {
127 static const wchar_t kDateFormat[] = L"yyyyMMdd";
128 wchar_t date_str[base::size(kDateFormat)] = {0};
129 int len = GetDateFormatW(LOCALE_INVARIANT, 0, nullptr, kDateFormat, date_str,
130 base::size(date_str));
131 if (len) {
132 --len; // Subtract terminating \0.
133 } else {
134 LOGFN(ERROR) << "GetDateFormat failed";
135 return L"";
136 }
137
138 return base::string16(date_str, len);
139 }
140
IsStandaloneInstallation() const141 bool StandaloneInstallerConfigurator::IsStandaloneInstallation() const {
142 return !is_msi_installation_;
143 }
144
AddUninstallKey(const base::FilePath & install_path)145 HRESULT StandaloneInstallerConfigurator::AddUninstallKey(
146 const base::FilePath& install_path) {
147 LOGFN(VERBOSE);
148
149 if (is_msi_installation_)
150 return S_OK;
151
152 std::wstring uninstall_reg = kRegUninstall;
153 uninstall_reg.append(L"\\");
154 uninstall_reg.append(kRegUninstallProduct);
155
156 base::win::RegKey key;
157 LONG status =
158 key.Create(HKEY_LOCAL_MACHINE, uninstall_reg.c_str(), KEY_SET_VALUE);
159 if (status != ERROR_SUCCESS) {
160 HRESULT hr = HRESULT_FROM_WIN32(status);
161 LOGFN(ERROR) << "Unable to create " << uninstall_reg << " hr=" << putHR(hr);
162 return hr;
163 }
164
165 base::CommandLine uninstall_string(
166 install_path.Append(kCredentialProviderSetupExe));
167 uninstall_string.AppendSwitch(switches::kUninstall);
168
169 status = key.WriteValue(kRegUninstallString,
170 uninstall_string.GetCommandLineString().c_str());
171 if (status != ERROR_SUCCESS) {
172 HRESULT hr = HRESULT_FROM_WIN32(status);
173 LOGFN(ERROR) << "Unable to write " << kRegUninstallString
174 << " hr=" << putHR(hr);
175 return hr;
176 }
177
178 status = key.WriteValue(kRegUninstallDisplayName,
179 GetStringResource(IDS_PROJNAME_BASE).c_str());
180 if (status != ERROR_SUCCESS) {
181 HRESULT hr = HRESULT_FROM_WIN32(status);
182 LOGFN(ERROR) << "Unable to write " << kRegUninstallDisplayName
183 << " hr=" << putHR(hr);
184 return hr;
185 }
186
187 status = key.WriteValue(kRegInstallLocation, install_path.value().c_str());
188 if (status != ERROR_SUCCESS) {
189 HRESULT hr = HRESULT_FROM_WIN32(status);
190 LOGFN(ERROR) << "Unable to write " << kRegInstallLocation
191 << " hr=" << putHR(hr);
192 return hr;
193 }
194
195 status = key.WriteValue(
196 kRegDisplayIcon,
197 (install_path.Append(kCredentialProviderSetupExe).value() + L",0")
198 .c_str());
199 if (status != ERROR_SUCCESS) {
200 HRESULT hr = HRESULT_FROM_WIN32(status);
201 LOGFN(ERROR) << "Unable to write " << kRegDisplayIcon
202 << " hr=" << putHR(hr);
203 return hr;
204 }
205
206 status = key.WriteValue(kRegNoModify, 1);
207 if (status != ERROR_SUCCESS) {
208 HRESULT hr = HRESULT_FROM_WIN32(status);
209 LOGFN(ERROR) << "Unable to write " << kRegNoModify << " hr=" << putHR(hr);
210 return hr;
211 }
212
213 status = key.WriteValue(kRegNoRepair, 1);
214 if (status != ERROR_SUCCESS) {
215 HRESULT hr = HRESULT_FROM_WIN32(status);
216 LOGFN(ERROR) << "Unable to write " << kRegNoRepair << " hr=" << putHR(hr);
217 return hr;
218 }
219
220 status = key.WriteValue(kRegPublisherName, kRegPublisher);
221 if (status != ERROR_SUCCESS) {
222 HRESULT hr = HRESULT_FROM_WIN32(status);
223 LOGFN(ERROR) << "Unable to write " << kRegPublisherName
224 << " hr=" << putHR(hr);
225 return hr;
226 }
227
228 status = key.WriteValue(kRegInstallDate, GetCurrentDate().c_str());
229 if (status != ERROR_SUCCESS) {
230 HRESULT hr = HRESULT_FROM_WIN32(status);
231 LOGFN(ERROR) << "Unable to write " << kRegInstallDate
232 << " hr=" << putHR(hr);
233 return hr;
234 }
235
236 base::Version version(CHROME_VERSION_STRING);
237
238 status = key.WriteValue(kRegVersion,
239 base::ASCIIToUTF16(version.GetString()).c_str());
240 if (status != ERROR_SUCCESS) {
241 HRESULT hr = HRESULT_FROM_WIN32(status);
242 LOGFN(ERROR) << "Unable to write " << kRegVersion << " hr=" << putHR(hr);
243 return hr;
244 }
245
246 status = key.WriteValue(kRegDisplayVersion,
247 base::ASCIIToUTF16(version.GetString()).c_str());
248 if (status != ERROR_SUCCESS) {
249 HRESULT hr = HRESULT_FROM_WIN32(status);
250 LOGFN(ERROR) << "Unable to write " << kRegDisplayVersion
251 << " hr=" << putHR(hr);
252 return hr;
253 }
254
255 const std::vector<uint32_t>& version_components = version.components();
256 if (version_components.size() == 4) {
257 status = key.WriteValue(kRegVersionMajor,
258 static_cast<DWORD>(version_components[2]));
259 if (status != ERROR_SUCCESS) {
260 HRESULT hr = HRESULT_FROM_WIN32(status);
261 LOGFN(ERROR) << "Unable to write " << kRegVersionMajor
262 << " hr=" << putHR(hr);
263 return hr;
264 }
265
266 status = key.WriteValue(kRegVersionMinor,
267 static_cast<DWORD>(version_components[3]));
268 if (status != ERROR_SUCCESS) {
269 HRESULT hr = HRESULT_FROM_WIN32(status);
270 LOGFN(ERROR) << "Unable to write " << kRegVersionMinor
271 << " hr=" << putHR(hr);
272 return hr;
273 }
274 }
275
276 return S_OK;
277 }
278
RemoveUninstallKey()279 HRESULT StandaloneInstallerConfigurator::RemoveUninstallKey() {
280 LOGFN(VERBOSE);
281 base::win::RegKey key;
282
283 LONG status = key.Create(HKEY_LOCAL_MACHINE, kRegUninstall, KEY_SET_VALUE);
284 if (status != ERROR_SUCCESS) {
285 HRESULT hr = HRESULT_FROM_WIN32(status);
286 LOGFN(ERROR) << "Unable to create " << kRegUninstall << " hr=" << putHR(hr);
287 return hr;
288 }
289
290 status = key.DeleteKey(kRegUninstallProduct);
291 if (status != ERROR_SUCCESS) {
292 HRESULT hr = HRESULT_FROM_WIN32(status);
293 LOGFN(ERROR) << "Unable to delete " << kRegUninstallProduct
294 << " hr=" << putHR(hr);
295 return hr;
296 }
297 return S_OK;
298 }
299
InitializeFromInstallerData(base::FilePath prefs_path)300 bool StandaloneInstallerConfigurator::InitializeFromInstallerData(
301 base::FilePath prefs_path) {
302 std::string json_data;
303 if (base::PathExists(prefs_path) &&
304 !base::ReadFileToString(prefs_path, &json_data)) {
305 LOGFN(ERROR) << "Failed to read preferences from " << prefs_path.value();
306 return false;
307 }
308
309 if (json_data.empty()) {
310 LOGFN(WARNING) << "Installer data is empty!";
311 return false;
312 }
313
314 installer_data_dictionary_.reset(ParseDistributionPreferences(json_data));
315
316 if (!installer_data_dictionary_) {
317 LOGFN(WARNING) << "Installer data is empty!";
318 return false;
319 }
320
321 return true;
322 }
323
324 } // namespace credential_provider
325