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