| OLD | NEW |
| (Empty) |
| 1 // Copyright 2011 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/client/bundle_creator.h" | |
| 17 #include <atlsafe.h> | |
| 18 #include "omaha/base/debug.h" | |
| 19 #include "omaha/base/error.h" | |
| 20 #include "omaha/base/logging.h" | |
| 21 #include "omaha/base/safe_format.h" | |
| 22 #include "omaha/base/string.h" | |
| 23 #include "omaha/base/utils.h" | |
| 24 #include "omaha/base/vista_utils.h" | |
| 25 #include "omaha/client/client_utils.h" | |
| 26 #include "omaha/common/command_line.h" | |
| 27 #include "omaha/common/config_manager.h" | |
| 28 #include "omaha/common/const_goopdate.h" | |
| 29 #include "omaha/common/goopdate_utils.h" | |
| 30 #include "omaha/common/lang.h" | |
| 31 #include "omaha/common/update3_utils.h" | |
| 32 #include "goopdate/omaha3_idl.h" | |
| 33 | |
| 34 namespace omaha { | |
| 35 | |
| 36 namespace bundle_creator { | |
| 37 | |
| 38 namespace internal { | |
| 39 | |
| 40 // display_language and install_source can be empty. | |
| 41 HRESULT SetBundleProperties(const CString& display_language, | |
| 42 const CString& display_name, | |
| 43 const CString& install_source, | |
| 44 const CString& session_id, | |
| 45 IAppBundle* app_bundle) { | |
| 46 ASSERT1(!display_name.IsEmpty()); | |
| 47 ASSERT1(app_bundle); | |
| 48 | |
| 49 CString process_language = lang::GetLanguageForProcess(display_language); | |
| 50 HRESULT hr = app_bundle->put_displayLanguage(CComBSTR(process_language)); | |
| 51 if (FAILED(hr)) { | |
| 52 return hr; | |
| 53 } | |
| 54 | |
| 55 hr = app_bundle->put_displayName(CComBSTR(display_name)); | |
| 56 if (FAILED(hr)) { | |
| 57 return hr; | |
| 58 } | |
| 59 | |
| 60 hr = app_bundle->put_sessionId(CComBSTR(session_id)); | |
| 61 if (FAILED(hr)) { | |
| 62 return hr; | |
| 63 } | |
| 64 | |
| 65 if (!install_source.IsEmpty()) { | |
| 66 hr = app_bundle->put_installSource(CComBSTR(install_source)); | |
| 67 if (FAILED(hr)) { | |
| 68 return hr; | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 return S_OK; | |
| 73 } | |
| 74 | |
| 75 // Do not use the apps member of extra_args. Those values are handled by | |
| 76 // PopulateAppSpecificData. | |
| 77 HRESULT PopulateCommonData(const CommandLineExtraArgs& extra_args, | |
| 78 bool is_eula_accepted, | |
| 79 IApp* app) { | |
| 80 ASSERT1(app); | |
| 81 | |
| 82 HRESULT hr = S_OK; | |
| 83 | |
| 84 // Set as soon as possible so pings can occur in error cases. | |
| 85 hr = app->put_isEulaAccepted(is_eula_accepted ? VARIANT_TRUE : VARIANT_FALSE); | |
| 86 if (FAILED(hr)) { | |
| 87 return hr; | |
| 88 } | |
| 89 | |
| 90 if (!extra_args.language.IsEmpty()) { | |
| 91 hr = app->put_language(CComBSTR(extra_args.language)); | |
| 92 if (FAILED(hr)) { | |
| 93 return hr; | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 if (!IsEqualGUID(GUID_NULL, extra_args.installation_id)) { | |
| 98 hr = app->put_iid(CComBSTR(GuidToString(extra_args.installation_id))); | |
| 99 if (FAILED(hr)) { | |
| 100 return hr; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 if (!extra_args.brand_code.IsEmpty()) { | |
| 105 hr = app->put_brandCode(CComBSTR(extra_args.brand_code)); | |
| 106 if (FAILED(hr)) { | |
| 107 return hr; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 if (!extra_args.client_id.IsEmpty()) { | |
| 112 hr = app->put_clientId(CComBSTR(extra_args.client_id)); | |
| 113 if (FAILED(hr)) { | |
| 114 return hr; | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 if (!extra_args.referral_id.IsEmpty()) { | |
| 119 hr = app->put_referralId(CComBSTR(extra_args.referral_id)); | |
| 120 if (FAILED(hr)) { | |
| 121 return hr; | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 if (extra_args.browser_type != BROWSER_UNKNOWN) { | |
| 126 hr = app->put_browserType(extra_args.browser_type); | |
| 127 if (FAILED(hr)) { | |
| 128 return hr; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 hr = app->put_usageStatsEnable(extra_args.usage_stats_enable); | |
| 133 if (FAILED(hr)) { | |
| 134 return hr; | |
| 135 } | |
| 136 | |
| 137 return S_OK; | |
| 138 } | |
| 139 | |
| 140 HRESULT PopulateAppSpecificData(const CommandLineAppArgs& app_args, | |
| 141 IApp* app) { | |
| 142 ASSERT1(app); | |
| 143 | |
| 144 HRESULT hr = app->put_displayName(CComBSTR(app_args.app_name)); | |
| 145 if (FAILED(hr)) { | |
| 146 return hr; | |
| 147 } | |
| 148 | |
| 149 if (!app_args.ap.IsEmpty()) { | |
| 150 hr = app->put_ap(CComBSTR(app_args.ap)); | |
| 151 if (FAILED(hr)) { | |
| 152 return hr; | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 if (!app_args.tt_token.IsEmpty()) { | |
| 157 hr = app->put_ttToken(CComBSTR(app_args.tt_token)); | |
| 158 if (FAILED(hr)) { | |
| 159 return hr; | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 if (!app_args.encoded_installer_data.IsEmpty()) { | |
| 164 CString decoded_installer_data; | |
| 165 HRESULT hr = Utf8UrlEncodedStringToWideString( | |
| 166 app_args.encoded_installer_data, | |
| 167 &decoded_installer_data); | |
| 168 ASSERT(SUCCEEDED(hr), (_T("[Utf8UrlEncodedStringToWideString][0x%x]"), hr)); | |
| 169 if (FAILED(hr) || CString(decoded_installer_data).Trim().IsEmpty()) { | |
| 170 return GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS; | |
| 171 } | |
| 172 | |
| 173 hr = app->put_clientInstallData(CComBSTR(decoded_installer_data)); | |
| 174 if (FAILED(hr)) { | |
| 175 return hr; | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 if (!app_args.install_data_index.IsEmpty()) { | |
| 180 hr = app->put_serverInstallDataIndex(CComBSTR(app_args.install_data_index)); | |
| 181 if (FAILED(hr)) { | |
| 182 return hr; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 if (!app_args.experiment_labels.IsEmpty()) { | |
| 187 hr = app->put_labels(CComBSTR(app_args.experiment_labels)); | |
| 188 if (FAILED(hr)) { | |
| 189 return hr; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 return S_OK; | |
| 194 } | |
| 195 | |
| 196 // Obtains tokens and passes them to put_altTokens when running as Local System. | |
| 197 // Does not do anything for per-user instances or when not run as Local System. | |
| 198 HRESULT SetAltTokens(bool is_machine, IAppBundle* app_bundle) { | |
| 199 ASSERT1(app_bundle); | |
| 200 | |
| 201 if (!is_machine) { | |
| 202 return S_OK; | |
| 203 } | |
| 204 | |
| 205 bool is_local_system = false; | |
| 206 HRESULT hr = IsSystemProcess(&is_local_system); | |
| 207 if (FAILED(hr)) { | |
| 208 return hr; | |
| 209 } | |
| 210 | |
| 211 if (!is_local_system) { | |
| 212 // Do not need alternate tokens. | |
| 213 return S_OK; | |
| 214 } | |
| 215 | |
| 216 CAccessToken primary_token; | |
| 217 hr = primary_token.GetProcessToken(TOKEN_ALL_ACCESS) ? | |
| 218 S_OK : HRESULTFromLastError(); | |
| 219 if (FAILED(hr)) { | |
| 220 CORE_LOG(LE, (_T("[GetProcessToken failed][0x%x]"), hr)); | |
| 221 return hr; | |
| 222 } | |
| 223 | |
| 224 CAccessToken impersonation_token; | |
| 225 HANDLE user_token = NULL; | |
| 226 | |
| 227 vista::GetLoggedOnUserToken(&user_token); | |
| 228 | |
| 229 if (user_token) { | |
| 230 impersonation_token.Attach(user_token); | |
| 231 } else if (!primary_token.CreateImpersonationToken(&impersonation_token)) { | |
| 232 hr = HRESULTFromLastError(); | |
| 233 CORE_LOG(LE, (_T("[CreateImpersonationToken failed][0x%x]"), hr)); | |
| 234 return hr; | |
| 235 } | |
| 236 | |
| 237 hr = app_bundle->put_altTokens( | |
| 238 reinterpret_cast<ULONG_PTR>(impersonation_token.GetHandle()), | |
| 239 reinterpret_cast<ULONG_PTR>(primary_token.GetHandle()), | |
| 240 ::GetCurrentProcessId()); | |
| 241 if (FAILED(hr)) { | |
| 242 CORE_LOG(LE, (_T("[put_altTokens failed][0x%x]"), hr)); | |
| 243 return hr; | |
| 244 } | |
| 245 | |
| 246 return S_OK; | |
| 247 } | |
| 248 | |
| 249 } // namespace internal | |
| 250 | |
| 251 HRESULT Create(bool is_machine, | |
| 252 const CString& display_language, | |
| 253 const CString& install_source, | |
| 254 const CString& session_id, | |
| 255 bool is_interactive, | |
| 256 IAppBundle** app_bundle) { | |
| 257 CORE_LOG(L2, (_T("[bundle_creator::Create]"))); | |
| 258 ASSERT1(app_bundle); | |
| 259 | |
| 260 CComPtr<IGoogleUpdate3> server; | |
| 261 HRESULT hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server); | |
| 262 if (FAILED(hr)) { | |
| 263 CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class][0x%08x]"), hr)); | |
| 264 return hr; | |
| 265 } | |
| 266 | |
| 267 CComPtr<IAppBundle> app_bundle_ptr; | |
| 268 hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr); | |
| 269 if (FAILED(hr)) { | |
| 270 CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%08x]"), hr)); | |
| 271 return hr; | |
| 272 } | |
| 273 | |
| 274 hr = internal::SetBundleProperties(display_language, | |
| 275 client_utils::GetUpdateAllAppsBundleName(), | |
| 276 install_source, | |
| 277 session_id, | |
| 278 app_bundle_ptr); | |
| 279 if (FAILED(hr)) { | |
| 280 return hr; | |
| 281 } | |
| 282 | |
| 283 hr = internal::SetAltTokens(is_machine, app_bundle_ptr); | |
| 284 if (FAILED(hr)) { | |
| 285 return hr; | |
| 286 } | |
| 287 | |
| 288 hr = app_bundle_ptr->put_priority(is_interactive ? | |
| 289 INSTALL_PRIORITY_HIGH : | |
| 290 INSTALL_PRIORITY_LOW); | |
| 291 if (FAILED(hr)) { | |
| 292 return hr; | |
| 293 } | |
| 294 | |
| 295 hr = app_bundle_ptr->initialize(); | |
| 296 if (FAILED(hr)) { | |
| 297 return hr; | |
| 298 } | |
| 299 | |
| 300 *app_bundle = app_bundle_ptr.Detach(); | |
| 301 return S_OK; | |
| 302 } | |
| 303 | |
| 304 HRESULT CreateFromCommandLine(bool is_machine, | |
| 305 bool is_eula_accepted, | |
| 306 bool is_offline, | |
| 307 const CString& offline_directory, | |
| 308 const CommandLineExtraArgs& extra_args, | |
| 309 const CString& install_source, | |
| 310 const CString& session_id, | |
| 311 bool is_interactive, | |
| 312 IAppBundle** app_bundle) { | |
| 313 CORE_LOG(L2, (_T("[bundle_creator::CreateFromCommandLine]"))); | |
| 314 ASSERT1(app_bundle); | |
| 315 | |
| 316 CComPtr<IGoogleUpdate3> server; | |
| 317 HRESULT hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server); | |
| 318 if (FAILED(hr)) { | |
| 319 CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class][0x%08x]"), hr)); | |
| 320 return hr; | |
| 321 } | |
| 322 | |
| 323 CComPtr<IAppBundle> app_bundle_ptr; | |
| 324 hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr); | |
| 325 if (FAILED(hr)) { | |
| 326 CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%08x]"), hr)); | |
| 327 return hr; | |
| 328 } | |
| 329 | |
| 330 hr = internal::SetBundleProperties(extra_args.language, | |
| 331 extra_args.bundle_name, | |
| 332 install_source, | |
| 333 session_id, | |
| 334 app_bundle_ptr); | |
| 335 if (FAILED(hr)) { | |
| 336 return hr; | |
| 337 } | |
| 338 | |
| 339 if (is_offline) { | |
| 340 CString offline_dir(offline_directory); | |
| 341 if (offline_dir.IsEmpty()) { | |
| 342 // For Omaha2 compatibility. | |
| 343 offline_dir = is_machine ? | |
| 344 ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() : | |
| 345 ConfigManager::Instance()->GetUserOfflineStorageDir(); | |
| 346 } | |
| 347 | |
| 348 hr = app_bundle_ptr->put_offlineDirectory(CComBSTR(offline_dir)); | |
| 349 if (FAILED(hr)) { | |
| 350 return hr; | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 hr = internal::SetAltTokens(is_machine, app_bundle_ptr); | |
| 355 if (FAILED(hr)) { | |
| 356 return hr; | |
| 357 } | |
| 358 | |
| 359 hr = app_bundle_ptr->put_priority(is_interactive ? | |
| 360 INSTALL_PRIORITY_HIGH : | |
| 361 INSTALL_PRIORITY_LOW); | |
| 362 if (FAILED(hr)) { | |
| 363 return hr; | |
| 364 } | |
| 365 | |
| 366 hr = app_bundle_ptr->initialize(); | |
| 367 if (FAILED(hr)) { | |
| 368 return hr; | |
| 369 } | |
| 370 | |
| 371 for (size_t i = 0; i < extra_args.apps.size(); ++i) { | |
| 372 const CommandLineAppArgs& app_args(extra_args.apps[i]); | |
| 373 | |
| 374 const CComBSTR app_id(GuidToString(app_args.app_guid)); | |
| 375 | |
| 376 CComPtr<IApp> app; | |
| 377 hr = update3_utils::CreateApp(app_id, app_bundle_ptr, &app); | |
| 378 if (FAILED(hr)) { | |
| 379 return hr; | |
| 380 } | |
| 381 | |
| 382 hr = internal::PopulateCommonData(extra_args, is_eula_accepted, app); | |
| 383 if (FAILED(hr)) { | |
| 384 return hr; | |
| 385 } | |
| 386 | |
| 387 hr = internal::PopulateAppSpecificData(app_args, app); | |
| 388 if (FAILED(hr)) { | |
| 389 return hr; | |
| 390 } | |
| 391 } | |
| 392 | |
| 393 *app_bundle = app_bundle_ptr.Detach(); | |
| 394 return S_OK; | |
| 395 } | |
| 396 | |
| 397 HRESULT CreateForOnDemand(bool is_machine, | |
| 398 const CString& app_id, | |
| 399 const CString& install_source, | |
| 400 const CString& session_id, | |
| 401 HANDLE impersonation_token, | |
| 402 HANDLE primary_token, | |
| 403 IAppBundle** app_bundle) { | |
| 404 CORE_LOG(L2, (_T("[bundle_creator::CreateForOnDemand]"))); | |
| 405 ASSERT1(app_bundle); | |
| 406 | |
| 407 CComPtr<IGoogleUpdate3> server; | |
| 408 HRESULT hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server); | |
| 409 if (FAILED(hr)) { | |
| 410 CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class failed][0x%x]"), hr)); | |
| 411 return hr; | |
| 412 } | |
| 413 | |
| 414 CComPtr<IAppBundle> app_bundle_ptr; | |
| 415 hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr); | |
| 416 if (FAILED(hr)) { | |
| 417 CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%x]"), hr)); | |
| 418 return hr; | |
| 419 } | |
| 420 | |
| 421 // ::CoSetProxyBlanket() settings are per proxy. For OnDemand, after | |
| 422 // unmarshaling the interface, we need to set the blanket on this new proxy. | |
| 423 // The proxy blanket on the IAppBundle interface are set explicitly only for | |
| 424 // OnDemand, because OnDemand is a unique case of being a COM server as well | |
| 425 // as a COM client. The default security settings set for the OnDemand COM | |
| 426 // server are more restrictive and rightly so, as compared to the settings | |
| 427 // that we set for a COM client such as our Omaha3 UI. Hence the need to | |
| 428 // explicitly set the proxy blanket settings and lower the security | |
| 429 // requirements only when calling out on this interface. | |
| 430 hr = update3_utils::SetProxyBlanketAllowImpersonate(app_bundle_ptr); | |
| 431 if (FAILED(hr)) { | |
| 432 return hr; | |
| 433 } | |
| 434 | |
| 435 if (is_machine) { | |
| 436 hr = app_bundle_ptr->put_altTokens( | |
| 437 reinterpret_cast<ULONG_PTR>(impersonation_token), | |
| 438 reinterpret_cast<ULONG_PTR>(primary_token), | |
| 439 ::GetCurrentProcessId()); | |
| 440 if (FAILED(hr)) { | |
| 441 CORE_LOG(LE, (_T("[put_altTokens failed][0x%x]"), hr)); | |
| 442 return hr; | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 hr = internal::SetBundleProperties(CString(), | |
| 447 _T("On Demand Bundle"), | |
| 448 install_source, | |
| 449 session_id, | |
| 450 app_bundle_ptr); | |
| 451 if (FAILED(hr)) { | |
| 452 return hr; | |
| 453 } | |
| 454 | |
| 455 hr = app_bundle_ptr->initialize(); | |
| 456 if (FAILED(hr)) { | |
| 457 return hr; | |
| 458 } | |
| 459 | |
| 460 CComPtr<IApp> app; | |
| 461 hr = update3_utils::CreateInstalledApp(CComBSTR(app_id), | |
| 462 app_bundle_ptr, | |
| 463 &app); | |
| 464 if (FAILED(hr)) { | |
| 465 CORE_LOG(LE, (_T("[CreateInstalledApp failed][0x%x]"), hr)); | |
| 466 return hr; | |
| 467 } | |
| 468 | |
| 469 *app_bundle = app_bundle_ptr.Detach(); | |
| 470 return S_OK; | |
| 471 } | |
| 472 | |
| 473 } // namespace bundle_creator | |
| 474 | |
| 475 } // namespace omaha | |
| OLD | NEW |