blob: a33b11a9b777c24bc07a17668c56d459ba54d704 [file] [log] [blame]
// Copyright 2007-2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
//
// Contains the ATL exe server used for OnDemand, as well as the ProcessLauncher
// server used to launch a process.
// The ProcessLauncher server is to be used only by the machine google update.
// The idea is that the machine Google Update is elevated and launching
// a process, say a browser, from that elevated process. This will cause the
// browser to run elevated, which is a bad idea.
// What is needed is the ability to run medium integrity processes from a
// high integrity process. This can be done in two ways:
// 1. Create a service and expose some form of IPC. A service is required
// because admins (even elevated) cannot call CreateProcessAsUser. Admins
// do not posess the required privilege. However the local system account
// has this privilege to call CreateProcessAsUser and hence can be used to
// start the browser with the token of a medium integrity process.
// 2. Create a COM local server. Impersonate a medium integrity user in the
// client. Fortunately the impersonation works, then create instance the COM
// local server. If the COM security is set to use DYNAMIC_CLOAKING, then
// the local server will be created using the thread credentials, allowing
// the COM local server to be launched as medium integrity.
// This class implements the second method listed above.
// The server listens to the machine google update's shut down event.
#include "omaha/goopdate/google_update.h"
#include <windows.h>
#include "omaha/common/debug.h"
#include "omaha/common/event_handler.h"
#include "omaha/common/error.h"
#include "omaha/common/logging.h"
#include "omaha/common/const_object_names.h"
#include "omaha/common/reactor.h"
#include "omaha/common/scoped_any.h"
#include "omaha/common/utils.h"
#include "omaha/goopdate/command_line.h"
#include "omaha/goopdate/const_goopdate.h"
#include "omaha/goopdate/google_update_idl_datax.h"
#include "omaha/goopdate/goopdate_utils.h"
#include "omaha/worker/worker.h"
namespace omaha {
GoogleUpdate::GoogleUpdate() {}
GoogleUpdate::~GoogleUpdate() {}
HRESULT GoogleUpdate::Main() {
// Disable the delay on shutdown mechanism inside ATL.
m_bDelayShutdown = false;
return CAtlExeModuleT<GoogleUpdate>::WinMain(0);
}
bool ShouldRegisterClsid(const CLSID& clsid) {
bool is_machine = goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
// Machine-only CLSIDs.
if (IsEqualGUID(clsid, __uuidof(ProcessLauncherClass)) ||
IsEqualGUID(clsid, __uuidof(OnDemandMachineAppsClass))) {
return is_machine;
}
// User-only CLSIDs.
if (IsEqualGUID(clsid, __uuidof(OnDemandUserAppsClass))) {
return !is_machine;
}
// Unknown CLSIDs.
ASSERT(false, (_T("[Unknown CLSID][%s]"), GuidToString(clsid)));
return false;
}
HRESULT GoogleUpdate::RegisterClassObjects(DWORD, DWORD) throw() {
HRESULT hr = S_FALSE;
for (_ATL_OBJMAP_ENTRY** entry = _AtlComModule.m_ppAutoObjMapFirst;
entry < _AtlComModule.m_ppAutoObjMapLast && SUCCEEDED(hr);
entry++) {
if (*entry != NULL && ShouldRegisterClsid(*(*entry)->pclsid)) {
hr = (*entry)->RegisterClassObject(CLSCTX_LOCAL_SERVER,
REGCLS_SINGLEUSE | REGCLS_SUSPENDED);
}
}
return hr;
}
HRESULT GoogleUpdate::RevokeClassObjects() throw() {
return AtlComModuleRevokeClassObjects(&_AtlComModule);
}
HRESULT RegisterOrUnregisterExe(bool is_register) {
HRESULT hr = S_FALSE;
for (_ATL_OBJMAP_ENTRY** entry = _AtlComModule.m_ppAutoObjMapFirst;
entry < _AtlComModule.m_ppAutoObjMapLast && SUCCEEDED(hr);
entry++) {
if (*entry != NULL) {
const CLSID& clsid = *(*entry)->pclsid;
if (!ShouldRegisterClsid(clsid)) {
continue;
}
hr = is_register ? _AtlComModule.RegisterServer(false, &clsid) :
_AtlComModule.UnregisterServer(false, &clsid);
ASSERT(SUCCEEDED(hr), (_T("[RegisterOrUnregisterExe fail][%d][0x%x][%s]"),
is_register, hr, GuidToString(clsid)));
}
}
return hr;
}
HRESULT RegisterOrUnregisterProxy(bool is_register) {
HRESULT hr = is_register ? PrxDllRegisterServer() : PrxDllUnregisterServer();
if (FAILED(hr)) {
CORE_LOG(LW, (_T("[RegisterOrUnregisterProxy failed][%d][0x%x]"),
is_register, hr));
}
return hr;
}
HRESULT GoogleUpdate::RegisterServer(BOOL, const CLSID*) throw() {
HRESULT hr = goopdate_utils::RegisterOrUnregisterModule(
true, &RegisterOrUnregisterProxy);
if (FAILED(hr)) {
return hr;
}
return goopdate_utils::RegisterOrUnregisterModule(true,
&RegisterOrUnregisterExe);
}
HRESULT GoogleUpdate::UnregisterServer(BOOL, const CLSID*) throw() {
HRESULT hr = goopdate_utils::RegisterOrUnregisterModule(
false, &RegisterOrUnregisterExe);
if (FAILED(hr)) {
return hr;
}
return goopdate_utils::RegisterOrUnregisterModule(false,
&RegisterOrUnregisterProxy);
}
HRESULT GoogleUpdate::PreMessageLoop(int show_cmd) throw() {
bool is_machine = goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
worker_.reset(new Worker(is_machine));
return CAtlExeModuleT<GoogleUpdate>::PreMessageLoop(show_cmd);
}
HRESULT GoogleUpdate::PostMessageLoop() throw() {
HRESULT hr = CAtlExeModuleT<GoogleUpdate>::PostMessageLoop();
worker_.reset();
return hr;
}
} // namespace omaha