Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2242 lines
65 KiB

//*************************************************************
//
// Copyright (c) Microsoft Corporation 1998
// All rights reserved
//
// apis.cxx
//
//*************************************************************
#include "appmgext.hxx"
typedef struct
{
CManagedAppProcessor * pManApp;
CAppInfo * pAppInfo;
BOOL bUninstallsCompleted;
CLoadMsi * pLoadMsi;
CLoadSfc * pLoadSfc;
boolean bStatus;
INT64 SRSequence;
} APPCONTEXT, * PAPPCONTEXT;
CRITICAL_SECTION gAppCS;
static BOOL
SetSystemRestorePoint(
IN WCHAR * pwszApplicationName,
IN OUT PAPPCONTEXT pAppContext
);
static void
CheckLocalCall(
IN handle_t hRpc
);
WCHAR*
GetGpoNameFromGuid(
IN PGROUP_POLICY_OBJECT pGpoList,
IN GUID* pGpoGuid
);
DWORD
GetPlatformCompatibleCOMClsCtx(
DWORD Architecture,
DWORD dwClsCtx
);
void
LogRsopInstallData(
CManagedAppProcessor* pManApp,
CAppInfo* pAppInfo
);
DWORD
WINAPI
GetManagedAppsProc(
LPVOID pvArpContext
);
error_status_t
InstallBegin(
IN handle_t hRpc,
IN APPKEY * pAppType,
OUT PINSTALLCONTEXT * ppInstallContext,
OUT APPLICATION_INFO * pInstallInfo,
OUT UNINSTALL_APPS * pUninstallApps
)
{
CManagedAppProcessor * pManApp;
CAppList LocalApps( NULL );
HANDLE hUserToken;
HKEY hkRoot;
uCLSSPEC ClassSpec;
QUERYCONTEXT QueryContext;
PACKAGEDISPINFO PackageInfo;
PAPPCONTEXT pAppContext;
GUID DeploymentId;
CAppInfo * pAppInfo;
CAppInfo * pUpgradedApp;
CAppInfo * pLocalApp;
WCHAR wszGuid[40];
WCHAR wszProductId[40];
WCHAR * pwszDeploymentId;
DWORD Size;
DWORD UninstallApps;
DWORD n;
DWORD Status;
HRESULT hr;
BOOL bEnterCritSec;
BOOL bStatus;
CRsopAppContext RsopContext( CRsopAppContext::INSTALL, NULL, pAppType );
PGROUP_POLICY_OBJECT pGPOList;
*ppInstallContext = 0;
memset( pInstallInfo, 0, sizeof(APPLICATION_INFO) );
memset( pUninstallApps, 0, sizeof(UNINSTALL_APPS) );
CheckLocalCall( hRpc );
pManApp = 0;
pAppInfo = 0;
hUserToken = 0;
hkRoot = 0;
pAppContext = 0;
bEnterCritSec = FALSE;
pGPOList = NULL;
Status = RpcImpersonateClient( NULL );
if ( Status != ERROR_SUCCESS )
return Status;
if ( ERROR_SUCCESS == Status )
{
bStatus = OpenThreadToken(
GetCurrentThread(),
TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
TRUE,
&hUserToken );
if ( ! bStatus )
Status = GetLastError();
}
if ( ERROR_SUCCESS == Status )
Status = RegOpenCurrentUser( GENERIC_ALL, &hkRoot );
if ( Status != ERROR_SUCCESS )
{
CloseHandle( hUserToken );
RevertToSelf();
return Status;
}
gpEvents->SetToken( hUserToken );
LogTime();
//
// Set the query context -- the LANG_SYSTEM_DEFAULT value
// tells CsGetAppInfo that we should use our built-in language precedence
// algorithm in filtering packages -- otherwise, it will only consider
// packages that match exactly the locale specified in the .Locale member
//
QueryContext.Locale = LANG_SYSTEM_DEFAULT;
//
// For architecture, we use the architecture of the calling process to override
// the architecture of this process
//
GetDefaultPlatform( &QueryContext.Platform, TRUE, pAppType->ProcessorArchitecture );
QueryContext.dwContext = CLSCTX_ALL;
QueryContext.dwVersionHi = -1;
QueryContext.dwVersionLo = -1;
switch ( pAppType->Type )
{
case APPNAME :
ClassSpec.tyspec = TYSPEC_PACKAGENAME;
ClassSpec.tagged_union.ByName.pPackageName = pAppType->uType.AppName.Name;
ClassSpec.tagged_union.ByName.PolicyId = pAppType->uType.AppName.PolicyId;
DebugMsg((DM_VERBOSE, IDS_INSTALL_APPNAME, pAppType->uType.AppName.Name));
break;
case FILEEXT :
ClassSpec.tyspec = TYSPEC_FILEEXT;
ClassSpec.tagged_union.pFileExt = pAppType->uType.FileExt;
DebugMsg((DM_VERBOSE, IDS_INSTALL_FILEEXT, pAppType->uType.FileExt));
break;
case PROGID :
ClassSpec.tyspec = TYSPEC_PROGID;
ClassSpec.tagged_union.pProgId = pAppType->uType.ProgId;
DebugMsg((DM_VERBOSE, IDS_INSTALL_PROGID, pAppType->uType.ProgId));
break;
case COMCLASS :
ClassSpec.tyspec = TYSPEC_CLSID;
ClassSpec.tagged_union.clsid = pAppType->uType.COMClass.Clsid;
QueryContext.dwContext = GetPlatformCompatibleCOMClsCtx( pAppType->ProcessorArchitecture, pAppType->uType.COMClass.ClsCtx );
GuidToString( pAppType->uType.COMClass.Clsid, wszGuid );
DebugMsg((DM_VERBOSE, IDS_INSTALL_COMCLASS, wszGuid, QueryContext.dwContext));
break;
}
hr = CsGetAppInfo( &ClassSpec, &QueryContext, &PackageInfo );
RevertToSelf();
if ( S_OK == hr )
{
WCHAR* pszPolicyName;
Status = RpcImpersonateClient( NULL );
if ( ERROR_SUCCESS == Status )
{
Status = GetCurrentUserGPOList( &pGPOList );
RevertToSelf();
}
if ( ERROR_SUCCESS != Status )
goto InstallAppExit;
pszPolicyName = GetGpoNameFromGuid(
pGPOList,
&(PackageInfo.GpoId) );
//
// We've seen instance where getting the policy names fails because the gpo
// history key for appmgmt in hklm was missing. This is remotely possible
// if some registry api fails. In this instance we can't just let every
// install from ARP fail, so we'll just have to chug along with an empty
// policy name.
//
if ( ! pszPolicyName )
pszPolicyName = L"";
switch ( PackageInfo.PathType )
{
case DrwFilePath :
DebugMsg((DM_VERBOSE, IDS_INSTALL_DARWIN, PackageInfo.pszPackageName, pszPolicyName));
break;
case SetupNamePath :
DebugMsg((DM_VERBOSE, IDS_INSTALL_SETUP, PackageInfo.pszPackageName, pszPolicyName));
pInstallInfo->pwszDeploymentName = StringDuplicate( PackageInfo.pszPackageName );
pInstallInfo->pwszGPOName = StringDuplicate( pszPolicyName );
pInstallInfo->pwszSetupCommand = StringDuplicate( PackageInfo.pszScriptPath );
goto InstallAppExit;
default :
DebugMsg((DM_VERBOSE, IDS_INSTALL_UNKNOWN, PackageInfo.PathType, PackageInfo.pszPackageName, pszPolicyName));
Status = CS_E_PACKAGE_NOTFOUND;
goto InstallAppExit;
}
}
else
{
DebugMsg((DM_VERBOSE, IDS_GETAPPINFO_FAIL, hr));
memset( &PackageInfo, 0, sizeof(PackageInfo) );
Status = (DWORD) hr;
}
if ( ERROR_SUCCESS == Status )
{
pAppContext = new APPCONTEXT;
if ( pAppContext )
{
pAppContext->pManApp = 0;
pAppContext->pAppInfo = 0;
pAppContext->bUninstallsCompleted = FALSE;
pAppContext->pLoadMsi = new CLoadMsi( Status );
pAppContext->pLoadSfc = 0;
pAppContext->bStatus = FALSE;
pAppContext->SRSequence = 0;
if ( Status != ERROR_SUCCESS )
{
delete pAppContext->pLoadMsi;
pAppContext->pLoadMsi = 0;
}
if ( ! pAppContext->pLoadMsi )
{
delete pAppContext;
pAppContext = 0;
}
}
if ( ! pAppContext )
{
Status = ERROR_OUTOFMEMORY;
}
}
if ( Status != ERROR_SUCCESS )
goto InstallAppExit;
RtlEnterCriticalSection( &gAppCS );
bEnterCritSec = TRUE;
pManApp = new CManagedAppProcessor( 0, hUserToken, hkRoot, NULL, TRUE, FALSE, &RsopContext, Status );
if ( ! pManApp )
Status = ERROR_OUTOFMEMORY;
if ( ERROR_SUCCESS == Status )
Status = pManApp->SetPolicyListFromGPOList( pGPOList );
if ( ERROR_SUCCESS == Status )
{
pAppInfo = new CAppInfo( pManApp, &PackageInfo, TRUE, bStatus );
if ( ! pAppInfo || ! bStatus )
Status = ERROR_OUTOFMEMORY;
}
if ( ERROR_SUCCESS == Status )
{
GuidToString( PackageInfo.ProductCode, wszProductId);
Status = pManApp->GetOrderedLocalAppList( LocalApps );
}
if ( ERROR_SUCCESS == Status )
Status = pManApp->Impersonate();
if ( Status != ERROR_SUCCESS )
goto InstallAppExit;
pAppContext->pManApp = pManApp;
pAppContext->pAppInfo = pAppInfo;
//
// When servicing a demand install outside of ARP we prevent faulting in
// any deployment of a particular product different from what is already
// on the machine (if any) and we also prevent faulting in any app which
// upgrades another app already on the machine. This is to prevent a
// subsequent upgrade, transform conflict, or language mismatch from
// occuring while the app is likely in use.
//
// So below we are checking these cases plus, in the case of an ARP install,
// adding any such apps to an additional list for processing of orphan and
// uninstall actions.
//
for ( LocalApps.Reset(), pLocalApp = (CAppInfo *) LocalApps.GetCurrentItem();
pLocalApp;
pLocalApp = (CAppInfo *) LocalApps.GetCurrentItem() )
{
if ( ! (pLocalApp->_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) )
{
LocalApps.MoveNext();
continue;
}
//
// This is the check for a similar product id but a different
// deployment instance.
//
if ( (0 == lstrcmpi( pLocalApp->_pwszProductId, wszProductId )) &&
(memcmp( &pLocalApp->_DeploymentId, &PackageInfo.PackageGuid, sizeof(GUID) ) != 0) )
{
if ( pAppType->Type != APPNAME )
{
// Abort if not doing an ARP install.
Status = CS_E_PACKAGE_NOTFOUND;
DebugMsg((DM_VERBOSE, IDS_DEMAND_BLOCK1, pAppInfo->_pwszDeploymentName));
break;
}
else
{
LocalApps.MoveNext();
pLocalApp->Remove();
pManApp->AppList().InsertFIFO( pLocalApp );
continue;
}
}
//
// This is the check to see if this new package is set to upgrade any
// existing app we have.
//
for ( n = 0; n < PackageInfo.cUpgrades; n++ )
{
if ( (PackageInfo.prgUpgradeInfoList[n].Flag & (UPGFLG_Uninstall | UPGFLG_NoUninstall)) &&
(0 == memcmp( &pLocalApp->_DeploymentId, &PackageInfo.prgUpgradeInfoList[n].PackageGuid, sizeof(GUID) )) )
{
if ( pAppType->Type != APPNAME )
{
// Abort if not doing an ARP install.
Status = CS_E_PACKAGE_NOTFOUND;
DebugMsg((DM_VERBOSE, IDS_DEMAND_BLOCK2, pAppInfo->_pwszDeploymentName));
}
else
{
LocalApps.MoveNext();
pLocalApp->Remove();
pManApp->AppList().InsertFIFO( pLocalApp );
}
break;
}
}
if ( CS_E_PACKAGE_NOTFOUND == Status )
break;
if ( n < PackageInfo.cUpgrades )
continue;
//
// This is the check to see if this app is superceded by an app which is
// already installed. Note that in some instances this may actually be
// a case where the new app upgrades the local app because of
// policy precedence upgrade reversal. The result is the same for demand
// installs, but slightly different for ARP installs where we must ensure
// that the upgrade logic is invoked.
//
for ( pwszDeploymentId = pLocalApp->_pwszSupercededIds;
pwszDeploymentId && *pwszDeploymentId;
pwszDeploymentId += GUIDSTRLEN + 1 )
{
StringToGuid( pwszDeploymentId, &DeploymentId );
if ( 0 == memcmp( &DeploymentId, &PackageInfo.PackageGuid, sizeof(GUID) ) )
{
if ( pAppType->Type != APPNAME )
{
// Abort if not doing an ARP install.
Status = CS_E_PACKAGE_NOTFOUND;
DebugMsg((DM_VERBOSE, IDS_DEMAND_BLOCK2, pAppInfo->_pwszDeploymentName));
}
else
{
//
// There is an app already installed which has the new package
// in it's override list. Either a previous upgrade setting is no
// longer set or this is a policy precedence violation case where
// the upgrade needs to be reversed.
// Not the prettiest solution, trading a late product change for least
// invasive code change.
//
if ( pManApp->GPOList().Compare( pLocalApp->_pwszGPOId, pAppInfo->_pwszGPOId ) < 0 )
{
PACKAGEDISPINFO LocalAppPackageInfo;
CAppInfo * pNewApp = 0;
memset( &LocalAppPackageInfo, 0, sizeof(LocalAppPackageInfo) );
ClassSpec.tyspec = TYSPEC_OBJECTID;
memcpy( &ClassSpec.tagged_union.ByObjectId.ObjectId, &pLocalApp->_DeploymentId, sizeof(GUID) );
StringToGuid( pLocalApp->_pwszGPOId, &ClassSpec.tagged_union.ByObjectId.PolicyId );
hr = CsGetAppInfo( &ClassSpec, NULL, &LocalAppPackageInfo );
if ( S_OK == hr )
{
pNewApp = new CAppInfo( pManApp, &LocalAppPackageInfo, FALSE, bStatus );
if ( ! bStatus )
{
delete pNewApp;
pNewApp = 0;
}
ReleasePackageInfo( &LocalAppPackageInfo );
}
if ( pNewApp )
{
pManApp->AppList().InsertFIFO( pAppInfo );
Status = pNewApp->InitializePass0();
pAppInfo->Remove();
pManApp->AppList().InsertFIFO( pNewApp );
}
if ( ! pNewApp || (Status != ERROR_SUCCESS) )
Status = CS_E_PACKAGE_NOTFOUND;
}
}
break;
}
}
if ( CS_E_PACKAGE_NOTFOUND == Status )
break;
LocalApps.MoveNext();
}
LocalApps.ResetEnd();
if ( ERROR_SUCCESS == Status )
{
//
// When doing a fileext/progid/clsid demand install, we don't want to
// set the full install state bit for the first time. This will enable
// the full install option to still be applied at the next foreground
// policy processing.
//
if ( (pAppType->Type != APPNAME) && ! (pAppInfo->_State & APPSTATE_INSTALL) )
pAppInfo->_ActFlags &= ~ACTFLG_InstallUserAssign;
pAppInfo->InitializePass0();
pAppInfo->SetActionPass1();
pAppInfo->SetActionPass2();
pAppInfo->SetActionPass3();
pAppInfo->SetActionPass4();
Status = pAppInfo->_Status;
}
pManApp->Revert();
if ( ERROR_SUCCESS == Status )
{
bStatus = pAppInfo->CopyToApplicationInfo( pInstallInfo );
if ( ! bStatus )
Status = ERROR_OUTOFMEMORY;
}
if ( Status != ERROR_SUCCESS )
goto InstallAppExit;
for ( pManApp->AppList().Reset(), pUpgradedApp = (CAppInfo *) pManApp->AppList().GetCurrentItem();
pUpgradedApp;
pManApp->AppList().MoveNext(), pUpgradedApp = (CAppInfo *) pManApp->AppList().GetCurrentItem() )
{
APPLICATION_INFO * pOldApplicationInfo;
bStatus = FALSE;
if ( (pUpgradedApp->_Action != ACTION_UNINSTALL) && (pUpgradedApp->_Action != ACTION_ORPHAN) )
continue;
pOldApplicationInfo = pUninstallApps->ApplicationInfo;
pUninstallApps->ApplicationInfo = (APPLICATION_INFO *) LocalAlloc( 0, (pUninstallApps->Products + 1) * sizeof(APPLICATION_INFO) );
if ( pUninstallApps->ApplicationInfo )
{
if ( pOldApplicationInfo )
memcpy( pUninstallApps->ApplicationInfo, pOldApplicationInfo, pUninstallApps->Products * sizeof(APPLICATION_INFO) );
bStatus = pUpgradedApp->CopyToApplicationInfo( &pUninstallApps->ApplicationInfo[pUninstallApps->Products] );
}
LocalFree( pOldApplicationInfo );
if ( ! bStatus )
{
pUninstallApps->Products = 0;
Status = ERROR_OUTOFMEMORY;
goto InstallAppExit;
}
else
{
pUninstallApps->Products++;
}
}
pManApp->AppList().ResetEnd();
//
// If we're doing a progid, file extension, or clsid based activation, we search
// for the specific Darwin identifier.
//
if ( pAppType->Type != APPNAME )
{
const DWORD dwMaxStrLen = 128;
HKEY hkPolicy;
HKEY hkClasses;
HKEY hkProgId;
HKEY hkScratch;
WCHAR wszScratch[dwMaxStrLen];
WCHAR wszDarwinId[128];
WCHAR * pwszProgId;
DWORD ScriptFlags;
hkPolicy = 0;
hkClasses = 0;
wszDarwinId[0] = 0;
Status = RegOpenKeyEx(
hkRoot,
POLICYKEY,
0,
KEY_ALL_ACCESS,
&hkPolicy );
if ( ERROR_SUCCESS == Status )
{
//
// We can use a fixed temp name since we are in a crit sec here. This
// key must be non volatile since that is how Darwin will do all of
// their creates under this key.
//
Status = RegCreateKeyEx(
hkPolicy,
L"TempClasses",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hkClasses,
NULL );
}
if ( ERROR_SUCCESS == Status )
{
Status = pManApp->Impersonate();
if ( ERROR_SUCCESS == Status )
{
Status = pAppInfo->CopyScriptIfNeeded();
pManApp->Revert();
}
}
if ( ERROR_SUCCESS == Status )
{
//
// Must include the MACHINEASSIGN flag since we are not impersonating.
// That's a little quirk in the semantics of the Msi API.
//
ScriptFlags = SCRIPTFLAGS_MACHINEASSIGN;
//
// In the progid case we need to advertise both extension and class data.
// This is because different progids are registered in these two
// cases and we want to catch both. In the former case they are progids
// associated with file extensions and in the latter case with clsids.
//
if ( (FILEEXT == pAppType->Type) || (PROGID == pAppType->Type) )
ScriptFlags |= SCRIPTFLAGS_REGDATA_EXTENSIONINFO;
if ( (PROGID == pAppType->Type) || (COMCLASS == pAppType->Type) )
ScriptFlags |= SCRIPTFLAGS_REGDATA_CLASSINFO;
Status = (*gpfnMsiAdvertiseScript)(
pAppInfo->LocalScriptPath(),
ScriptFlags,
&hkClasses,
FALSE );
}
if ( Status != ERROR_SUCCESS )
{
gpEvents->Install(
Status,
pAppInfo);
goto InstallAppDescriptorAbort;
}
//
// Now we grovel our temporary registry dump for a darwin id under the
// class info that was requested.
//
if ( pAppType->Type != COMCLASS )
{
//
// Looking for a shell-open command verb. First figure out the
// ProgID.
//
if ( FILEEXT == pAppType->Type )
{
Status = RegOpenKeyEx(
hkClasses,
pAppType->uType.FileExt,
0,
KEY_ALL_ACCESS,
&hkScratch );
Size = sizeof(wszScratch);
if ( ERROR_SUCCESS == Status )
{
Status = RegQueryValueEx(
hkScratch,
L"",
NULL,
NULL,
(PBYTE) wszScratch,
&Size );
RegCloseKey( hkScratch );
}
pwszProgId = wszScratch;
}
else
{
pwszProgId = pAppType->uType.ProgId;
}
if ( ERROR_SUCCESS == Status )
{
Status = RegOpenKeyEx(
hkClasses,
pwszProgId,
0,
KEY_ALL_ACCESS,
&hkProgId );
}
if ( ERROR_SUCCESS == Status )
{
Status = RegOpenKeyEx(
hkProgId,
L"shell\\open\\command",
0,
KEY_ALL_ACCESS,
&hkScratch );
RegCloseKey( hkProgId );
}
Size = sizeof(wszDarwinId);
if ( ERROR_SUCCESS == Status )
{
Status = RegQueryValueEx(
hkScratch,
L"command",
NULL,
NULL,
(PBYTE) wszDarwinId,
&Size );
if ( (ERROR_SUCCESS == Status) && DebugLevelOn( DM_VERBOSE ) )
{
DebugMsg((DM_VERBOSE, IDS_PROGID_FOUND, pwszProgId));
}
RegCloseKey( hkScratch );
}
}
else // COMCLASS == pAppType->Type
{
//
// Looking for a com clsid. We check both the inproc & localserver
// keys if those clsctx bits are set.
//
hr = StringCchCopy( wszScratch, dwMaxStrLen, L"CLSID\\" );
if (FAILED(hr))
{
Status = HRESULT_CODE(hr);
goto InstallAppDescriptorAbort;
}
hr = StringCchCopy( &wszScratch[6], dwMaxStrLen-6, wszGuid );
if (FAILED(hr))
{
Status = HRESULT_CODE(hr);
goto InstallAppDescriptorAbort;
}
if ( pAppType->uType.COMClass.ClsCtx & CLSCTX_INPROC_SERVER )
{
hr = StringCchCopy( &wszScratch[6+GUIDSTRLEN], dwMaxStrLen-(6+GUIDSTRLEN),L"\\InprocServer32" );
if (FAILED(hr))
{
Status = HRESULT_CODE(hr);
goto InstallAppDescriptorAbort;
}
Status = RegOpenKeyEx(
hkClasses,
wszScratch,
0,
KEY_ALL_ACCESS,
&hkScratch );
Size = sizeof(wszDarwinId);
if ( ERROR_SUCCESS == Status )
{
Status = RegQueryValueEx(
hkScratch,
L"InprocServer32",
NULL,
NULL,
(PBYTE) wszDarwinId,
&Size );
if ( ERROR_SUCCESS == Status )
DebugMsg((DM_VERBOSE, IDS_CLSID_INPROC_FOUND));
RegCloseKey( hkScratch );
}
}
if ( (0 == wszDarwinId[0]) && (pAppType->uType.COMClass.ClsCtx & CLSCTX_LOCAL_SERVER) )
{
hr = StringCchCopy( &wszScratch[6+GUIDSTRLEN], dwMaxStrLen-(6+GUIDSTRLEN),L"\\LocalServer32" );
if (FAILED(hr))
{
Status = HRESULT_CODE(hr);
goto InstallAppDescriptorAbort;
}
Status = RegOpenKeyEx(
hkClasses,
wszScratch,
0,
KEY_ALL_ACCESS,
&hkScratch );
Size = sizeof(wszDarwinId);
if ( ERROR_SUCCESS == Status )
{
Status = RegQueryValueEx(
hkScratch,
L"LocalServer32",
NULL,
NULL,
(PBYTE) wszDarwinId,
&Size );
if ( ERROR_SUCCESS == Status )
DebugMsg((DM_VERBOSE, IDS_CLSID_LOCAL_FOUND));
RegCloseKey( hkScratch );
}
}
}
//
// We're done with the temp reg data, so blow it away now.
//
// Must include the MACHINEASSIGN flag since we are not impersonating.
//
(void) (*gpfnMsiAdvertiseScript)(
pAppInfo->LocalScriptPath(),
ScriptFlags,
&hkClasses,
TRUE );
InstallAppDescriptorAbort:
if ( hkClasses )
{
RegCloseKey( hkClasses );
RegDeleteKey( hkPolicy, L"TempClasses" );
}
if ( hkPolicy )
RegCloseKey( hkPolicy );
if ( ERROR_SUCCESS == Status )
{
pInstallInfo->pwszDescriptor = (PWCHAR) LocalAlloc( 0, (lstrlen(wszDarwinId) + 1) * sizeof(WCHAR) );
if ( pInstallInfo->pwszDescriptor )
{
hr = StringCchCopy( pInstallInfo->pwszDescriptor,lstrlen(wszDarwinId) + 1, wszDarwinId );
if (FAILED(hr))
{
Status = HRESULT_CODE(hr);
}
}
else
Status = ERROR_OUTOFMEMORY;
}
//
// If we fail to find a darwin id under the specific class data that
// was requested, then we fall back to doing a full product based
// install. Since the DS query succeeded, we have a valid app, but
// there just isn't any darwin id registered for the specific class
// data in the advertisement data.
//
// This could be a packaging problem, limitation, or design.
//
} // if ( pAppType->Type != APPNAME )
if ( (ERROR_SUCCESS == Status) && (pAppInfo->_State & APPSTATE_SCRIPT_NOT_EXISTED) )
{
SetSystemRestorePoint( pAppInfo->_pwszDeploymentName, pAppContext );
}
InstallAppExit:
if ( bEnterCritSec )
RtlLeaveCriticalSection( &gAppCS );
if ( Status != ERROR_SUCCESS )
{
for ( ; pUninstallApps->Products; )
FreeApplicationInfo( &pUninstallApps->ApplicationInfo[--pUninstallApps->Products] );
LocalFree( pUninstallApps->ApplicationInfo );
pUninstallApps->Products = 0;
pUninstallApps->ApplicationInfo = 0;
FreeApplicationInfo( pInstallInfo );
memset( pInstallInfo, 0, sizeof(APPLICATION_INFO) );
if ( pManApp )
{
if ( pAppInfo )
{
//
// Since this call has failed, the client will not call
// the InstallEnd method to log the failure, so we must
// log the failure in this call
//
(void) LogRsopInstallData( pManApp, pAppInfo );
}
delete pManApp;
}
if ( pAppInfo )
delete pAppInfo;
if ( pAppContext )
{
delete pAppContext->pLoadMsi;
delete pAppContext;
}
}
else
{
*ppInstallContext = pAppContext;
}
if ( ((long)Status) > 0 )
DebugMsg((DM_VERBOSE, IDS_INSTALL_STATUS1, Status));
else
DebugMsg((DM_VERBOSE, IDS_INSTALL_STATUS2, Status));
gpEvents->ClearToken();
if ( hUserToken )
CloseHandle( hUserToken );
if ( hkRoot )
RegCloseKey( hkRoot );
if ( pGPOList )
FreeGPOList( pGPOList );
ReleasePackageInfo( &PackageInfo );
return Status;
}
error_status_t
InstallManageApp(
IN PINSTALLCONTEXT pInstallContext,
IN PWSTR pwszDeploymentId,
IN DWORD RollbackStatus,
OUT boolean * pbInstall
)
{
PAPPCONTEXT pAppContext;
CAppInfo * pAppInfo;
GUID DeploymentId;
DWORD Status;
*pbInstall = FALSE;
pAppContext = (PAPPCONTEXT) pInstallContext;
StringToGuid( pwszDeploymentId, &DeploymentId );
Status = pAppContext->pManApp->Impersonate();
if ( Status != ERROR_SUCCESS )
return Status;
gpEvents->SetToken( pAppContext->pManApp->UserToken() );
if ( memcmp( &DeploymentId, &pAppContext->pAppInfo->_DeploymentId, sizeof(GUID) ) == 0 )
{
pAppContext->bUninstallsCompleted = TRUE;
Status = pAppContext->pAppInfo->ProcessApplyActions();
if ( ERROR_SUCCESS == Status )
{
if ( pAppContext->pManApp->GetRsopContext()->IsRsopEnabled() && pAppContext->pManApp->GetRsopContext()->IsDiagnosticModeEnabled() )
Status = pAppContext->pAppInfo->ProcessTransformConflicts();
}
goto InstallManageAppEnd;
}
for ( pAppContext->pManApp->AppList().Reset();
pAppInfo = (CAppInfo *) pAppContext->pManApp->AppList().GetCurrentItem();
pAppContext->pManApp->AppList().MoveNext() )
{
if ( memcmp( &DeploymentId, &pAppInfo->_DeploymentId, sizeof(GUID) ) == 0 )
break;
}
if ( ! pAppInfo )
{
Status = ERROR_NOT_FOUND;
goto InstallManageAppEnd;
}
//
// Re-assigning one of the upgraded apps because of a failed upgrade. Not needed for
// apps orphaned during the upgrade.
//
if ( ACTION_UNINSTALL == pAppInfo->_Action )
{
DWORD ScriptFlags = SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS;
*pbInstall = (pAppInfo->_State & APPSTATE_INSTALL) ? 1 : 0;
if ( pAppInfo->_State & APPSTATE_ASSIGNED )
ScriptFlags |= SCRIPTFLAGS_REGDATA_EXTENSIONINFO;
Status = pAppInfo->Assign( ScriptFlags, TRUE, FALSE );
//
// Record an event so that we can track this as an RSoP failed setting if
// necessary
//
if ( ERROR_SUCCESS != Status )
{
gpEvents->Assign( Status, pAppInfo );
}
}
//
// Remember that this application was rolled back
//
pAppInfo->_bRollback = TRUE;
gpEvents->UpgradeAbort( RollbackStatus, pAppContext->pAppInfo, pAppInfo, ! pAppContext->bUninstallsCompleted );
InstallManageAppEnd:
gpEvents->ClearToken();
pAppContext->pManApp->Revert();
return Status;
}
error_status_t
InstallUnmanageApp(
IN PINSTALLCONTEXT pInstallContext,
IN PWSTR pwszDeploymentId,
IN boolean bUnadvertiseOnly
)
{
PAPPCONTEXT pAppContext;
CAppInfo * pAppInfo;
GUID DeploymentId;
DWORD Status;
pAppContext = (PAPPCONTEXT) pInstallContext;
StringToGuid( pwszDeploymentId, &DeploymentId );
Status = pAppContext->pManApp->Impersonate();
if ( Status != ERROR_SUCCESS )
goto InstallUnmanageAppRemoveScript;
gpEvents->SetToken( pAppContext->pManApp->UserToken() );
if ( memcmp( &DeploymentId, &pAppContext->pAppInfo->_DeploymentId, sizeof(GUID) ) == 0 )
{
Status = pAppContext->pAppInfo->Unassign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO, TRUE );
//
// Record an event so that we can track this as an RSoP failed setting if
// necessary
//
if ( ERROR_SUCCESS != Status )
{
gpEvents->Unassign( Status, pAppContext->pAppInfo );
}
goto InstallUnmanageAppEnd;
}
for ( pAppContext->pManApp->AppList().Reset();
pAppInfo = (CAppInfo *) pAppContext->pManApp->AppList().GetCurrentItem();
pAppContext->pManApp->AppList().MoveNext() )
{
if ( memcmp( &DeploymentId, &pAppInfo->_DeploymentId, sizeof(GUID) ) == 0 )
break;
}
if ( ! pAppInfo )
{
Status = ERROR_NOT_FOUND;
goto InstallUnmanageAppEnd;
}
//
// Unassigning one of the upgraded apps.
//
if ( bUnadvertiseOnly )
{
Status = pAppInfo->Unassign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS | SCRIPTFLAGS_REGDATA_EXTENSIONINFO, FALSE );
}
else
{
Status = pAppInfo->Unassign( 0, TRUE );
//
// Record an event so that we can track this as an RSoP failed setting if
// necessary
//
if ( ERROR_SUCCESS != Status )
{
gpEvents->Unassign( Status, pAppInfo );
}
gpEvents->UpgradeComplete( pAppContext->pAppInfo, pAppInfo );
}
InstallUnmanageAppEnd:
gpEvents->ClearToken();
pAppContext->pManApp->Revert();
InstallUnmanageAppRemoveScript:
//
// Be sure to remove the script for a failed upgrade app
//
if ( ( ERROR_SUCCESS != Status ) && ! bUnadvertiseOnly )
{
DeleteFile( pAppContext->pAppInfo->_pwszLocalScriptPath );
}
return Status;
}
error_status_t
InstallEnd(
IN boolean bStatus,
IN OUT PINSTALLCONTEXT * ppInstallContext
)
{
//
// We are done installing -- now log the results
//
if ( ppInstallContext && *ppInstallContext )
{
PAPPCONTEXT pAppContext;
CManagedAppProcessor* pManApp;
pAppContext = (PAPPCONTEXT) *ppInstallContext;
if ( pAppContext )
pAppContext->bStatus = (boolean) bStatus;
pManApp = pAppContext->pManApp;
if ( pManApp && pAppContext )
{
(void) LogRsopInstallData( pManApp, pAppContext->pAppInfo );
}
}
if ( ppInstallContext )
{
PINSTALLCONTEXT_rundown( *ppInstallContext );
*ppInstallContext = 0;
}
return ERROR_SUCCESS;
}
void
PINSTALLCONTEXT_rundown(
IN PINSTALLCONTEXT pInstallContext
)
{
PAPPCONTEXT pAppContext;
pAppContext = (PAPPCONTEXT) pInstallContext;
if ( pAppContext && (pAppContext->SRSequence != 0) )
{
RESTOREPOINTINFO RestoreInfo;
STATEMGRSTATUS SRStatus;
RestoreInfo.dwEventType = END_NESTED_SYSTEM_CHANGE;
RestoreInfo.dwRestorePtType = pAppContext->bStatus ? 0 : CANCELLED_OPERATION;
RestoreInfo.llSequenceNumber = pAppContext->SRSequence;
RestoreInfo.szDescription[0] = 0;
(void) (*gpfnSRSetRetorePointW)( &RestoreInfo, &SRStatus );
}
//
// If this app failed to be installed, ensure that the status is
// set properly (in upgrade cases, the status for the application may
// not be set. If this status is not set as a failure, the
// application's state (such as the script) may not be set
//
if ( pAppContext && pAppContext->pAppInfo && ! pAppContext->bStatus )
{
pAppContext->pAppInfo->ForceFailureStatus();
}
if (pAppContext)
{
delete pAppContext->pManApp;
delete pAppContext->pAppInfo;
delete pAppContext->pLoadMsi;
if ( pAppContext->pLoadSfc )
delete pAppContext->pLoadSfc;
delete pAppContext;
}
}
DWORD
RemoveAppHelper(
IN WCHAR * ProductCode,
IN HANDLE hUserToken,
IN HKEY hKeyRoot,
IN DWORD ARPStatus,
OUT BOOL * pbProductFound
)
{
CAppInfo * pAppInfo;
CAppInfo * pHighestAssignedApp;
CAppInfo * pRemovedApp;
DWORD Status;
*pbProductFound = FALSE;
CRsopAppContext RsopContext( CRsopAppContext::REMOVAL );
CManagedAppProcessor ManApps( hUserToken ? 0 : GPO_INFO_FLAG_MACHINE,
hUserToken,
hKeyRoot,
NULL,
FALSE,
FALSE,
&RsopContext,
Status );
if ( ERROR_SUCCESS != Status )
return Status;
CAppList LocalApps( NULL, ManApps.GetRsopContext() );
Status = ManApps.LoadPolicyList();
if ( ERROR_SUCCESS != Status )
return Status;
Status = ManApps.GetOrderedLocalAppList( LocalApps );
if ( ERROR_SUCCESS == Status )
Status = ManApps.Impersonate();
if ( Status != ERROR_SUCCESS )
return Status;
pHighestAssignedApp = 0;
pRemovedApp = 0;
LocalApps.Reset();
for ( pAppInfo = (CAppInfo *) LocalApps.GetCurrentItem();
pAppInfo;
LocalApps.MoveNext(), pAppInfo = (CAppInfo *) LocalApps.GetCurrentItem() )
{
if ( (lstrcmpi( pAppInfo->_pwszProductId, ProductCode ) != 0) ||
! (pAppInfo->_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) )
{
continue;
}
pRemovedApp = pAppInfo;
*pbProductFound = TRUE;
if ( pAppInfo->_State & APPSTATE_PUBLISHED )
{
//
// We perform no actual unassignments unless ARP actually uninstalled the app
//
//
// We unassign using the same scriptflags we do during assignment
// to handle cases where the initial install action fails and
// we are called to undo the original assigment. In normal success
// cases this is redundant.
//
if ( ERROR_SUCCESS == ARPStatus )
{
DebugMsg((DM_VERBOSE, IDS_REMOVEAPP_MATCH1, pAppInfo->_pwszDeploymentName, ProductCode));
pAppInfo->Unassign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS | SCRIPTFLAGS_REGDATA_EXTENSIONINFO, TRUE );
}
//
// Set this action for RSOP as a way to remember to log a removal entry for this app
//
pAppInfo->SetAction(
ACTION_UNINSTALL,
APP_ATTRIBUTE_REMOVALCAUSE_USER,
NULL);
}
else
{
//
// We only reassign if ARP was able to uninstall the app
//
if ( ERROR_SUCCESS == ARPStatus )
{
DebugMsg((DM_VERBOSE, IDS_REMOVEAPP_MATCH2, pAppInfo->_pwszDeploymentName, ProductCode));
}
pHighestAssignedApp = pAppInfo;
}
}
if ( ( ERROR_SUCCESS != ARPStatus ) &&
pRemovedApp )
{
//
// If ARP failed to uninstall the highest app, log a failure status
//
gpEvents->Uninstall(
ARPStatus,
pRemovedApp);
}
//
// Reassign the highest priority assigned app with this product id if
// one exists.
//
BOOL bRsopLogReassign;
bRsopLogReassign = FALSE;
if ( pHighestAssignedApp )
{
//
// We only reassign the app if it was successfully uninstalled
//
if ( ERROR_SUCCESS == ARPStatus )
{
Status = pHighestAssignedApp->Assign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS | SCRIPTFLAGS_REGDATA_EXTENSIONINFO, TRUE, FALSE );
if ( Status != ERROR_SUCCESS )
gpEvents->Assign( Status, pHighestAssignedApp );
}
if ( ManApps.GetRsopContext()->IsRsopEnabled() )
{
//
// Remember to write a removal entry for this reassigned app if it
// was the app that was removed
//
bRsopLogReassign = ( pHighestAssignedApp == pRemovedApp );
}
}
ManApps.Revert();
//
// We must log rsop data after we revert because the user may not have
// access to her own rsop namespace
//
//
// Obtain exclusive access to log data -- this will disable rsop
// if implicit access cannot be obtained
//
(void) ManApps.GetRsopContext()->GetExclusiveLoggingAccess( NULL == hUserToken );
if ( ManApps.GetRsopContext()->IsRsopEnabled() )
{
//
// Now log all the uninstalled published apps which would have been marked above
// as having the action to uninstall
//
(void) LocalApps.WriteLog( CAppList::RSOP_FILTER_REMOVALSONLY );
//
// Now log the highest reassigned app
//
if ( bRsopLogReassign )
{
//
// Log the actual uninstall entry
//
(void) LocalApps.MarkRSOPEntryAsRemoved( pHighestAssignedApp, FALSE );
}
else if ( pHighestAssignedApp )
{
//
// We need to write a new entry for the assigned app that was not previously
// applied but is now due to the fact that the higher precedence application
// was removed
//
(void) LocalApps.WriteAppToRsopLog( pHighestAssignedApp );
}
}
(void) ManApps.GetRsopContext()->ReleaseExclusiveLoggingAccess();
//
// Whenever the user does an app uninstall, we force a full run of policy
// during the next logon to pick up any app that should now apply. Note that
// later we use a gp engine api to do this due to the NT 5.1 foreground async
// gp refresh feature, but for compatibility with NT 5.0 (roaming), we must
// continue to set our own registry value
//
if ( *pbProductFound && hUserToken )
{
Status = RegSetValueEx(
ManApps.AppmgmtKey(),
FULLPOLICY,
0,
REG_DWORD,
(LPBYTE) pbProductFound,
sizeof(*pbProductFound) );
//
// Ensure that if async policy is enabled, we get a synchronous refresh at
// the next logon
//
if ( ERROR_SUCCESS == Status )
{
Status = ForceSynchronousRefresh( ManApps.UserToken() );
}
}
return Status;
}
error_status_t
ARPRemoveApp(
IN handle_t hRpc,
IN WCHAR * pwszProductCode,
IN DWORD ARPStatus
)
{
HANDLE hUserToken;
HKEY hKeyRoot;
DWORD Status;
BOOL bStatus;
BOOL bProductFound;
CheckLocalCall( hRpc );
hUserToken = NULL;
hKeyRoot = NULL;
bProductFound = FALSE;
CLoadMsi LoadMsi( Status );
if ( ERROR_SUCCESS == Status )
Status = RpcImpersonateClient( NULL );
if ( ERROR_SUCCESS == Status )
{
Status = RegOpenCurrentUser( GENERIC_ALL, &hKeyRoot );
if ( ERROR_SUCCESS == Status )
{
bStatus = OpenThreadToken(
GetCurrentThread(),
TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
TRUE,
&hUserToken );
if ( ! bStatus )
{
Status = GetLastError();
RegCloseKey( hKeyRoot );
}
}
RevertToSelf();
}
if ( Status != ERROR_SUCCESS )
return Status;
gpEvents->SetToken( hUserToken );
LogTime();
DebugMsg((DM_VERBOSE, IDS_REMOVEAPP, pwszProductCode));
Status = RemoveAppHelper( pwszProductCode, hUserToken, hKeyRoot, ARPStatus, &bProductFound );
if ( ! bProductFound )
{
if ( IsMemberOfAdminGroup( hUserToken ) )
Status = RemoveAppHelper( pwszProductCode, NULL, HKEY_LOCAL_MACHINE, ARPStatus, &bProductFound );
}
DebugMsg((DM_VERBOSE, IDS_REMOVEAPP_STATUS, Status));
gpEvents->ClearToken();
if ( hUserToken )
CloseHandle( hUserToken );
if ( hKeyRoot )
RegCloseKey( hKeyRoot );
return Status;
}
error_status_t
GetManagedApps(
IN handle_t hRpc,
IN GUID * pCategory,
IN DWORD dwQueryFlags,
IN DWORD dwInfoLevel,
OUT MANAGED_APPLIST * pAppList
)
{
HANDLE hUserToken;
HANDLE hEventAppsEnumerated;
error_status_t Status;
BOOL bStatus;
CheckLocalCall( hRpc );
hUserToken = NULL;
hEventAppsEnumerated = NULL;
if ( ! pAppList )
return ERROR_INVALID_PARAMETER;
//
// Clear this structure so that random
// garbage doesn't get marshalled back.
//
memset(pAppList, 0, sizeof(*pAppList));
//
// Validate the parameters passed in by the client.
//
if ( dwInfoLevel != MANAGED_APPS_INFOLEVEL_DEFAULT )
return ERROR_INVALID_PARAMETER;
switch (dwQueryFlags)
{
case MANAGED_APPS_USERAPPLICATIONS:
if (pCategory)
return ERROR_INVALID_PARAMETER;
break;
case MANAGED_APPS_FROMCATEGORY:
if (!pCategory)
return ERROR_INVALID_PARAMETER;
break;
default:
return ERROR_INVALID_PARAMETER;
}
//
// Now prepare to initiate the query -- first
// we need to get some user specific information
// to build the object which performs the query,
// so we impersonate.
//
Status = RpcImpersonateClient( NULL );
if ( Status != ERROR_SUCCESS )
return Status;
bStatus = OpenThreadToken(
GetCurrentThread(),
TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
TRUE,
&hUserToken );
if ( ! bStatus )
Status = GetLastError();
RevertToSelf();
if ( Status != ERROR_SUCCESS )
goto GetManagedAppsExit;
//
// We will create a separate thread to retrieve the ARP apps -- this allows
// this thread to wait for a signal from the ARP thread that app enumeration is done,
// and we can return the list at that point. The second thread will continue to
// execute since it needs to log rsop data -- this approach frees us from having to
// wait for rsop logging, which can take 10 times longer than it took us to retrieve
// the apps from the ds
//
//
// Below we create the event that the enumeration thread will use to signal this
// thread that enumeration is finished.
//
hEventAppsEnumerated = CreateEvent(
NULL,
TRUE, // manual reset
FALSE, // initially nonsignaled
NULL);
if ( ! hEventAppsEnumerated )
{
Status = GetLastError();
goto GetManagedAppsExit;
}
//
// We allocate a structure to pass to the enumeration thread containing all the
// context it needs to enumerate apps. Note that this structure is stack allocated,
// so the enumeration thread may only access it as long as this thread lives -- once
// it signals us that enumeration is complete, it may no longer access this structure
//
ARPCONTEXT ArpContext;
ArpContext.pCategory = pCategory;
ArpContext.pAppList = pAppList;
ArpContext.hUserToken = hUserToken;
ArpContext.hEventAppsEnumerated = hEventAppsEnumerated;
ArpContext.Status = ERROR_SUCCESS; // out parameter for the second thread to indicate status
HANDLE hThread;
hThread = CreateThread(
NULL,
0,
GetManagedAppsProc,
&ArpContext,
0,
NULL);
if ( ! hThread )
Status = GetLastError();
if ( ERROR_SUCCESS != Status )
goto GetManagedAppsExit;
//
// Wait for enumeration in the second thread to complete, or
// for the second thread itself to complete
//
HANDLE rgTasks[] = { hEventAppsEnumerated, hThread };
(void) WaitForMultipleObjects(
sizeof(rgTasks) / sizeof(*rgTasks),
rgTasks,
FALSE,
INFINITE);
//
// Retrieve the enumeration thread's status
//
Status = ArpContext.Status;
//
// Because tests assume they can check RSoP data as soon as
// the api has completed, if the tests are waiting for policy
// events to be signaled already, we'll also wait for rsop to finish
//
if ( gDebugLevel & DL_EVENT )
{
(void) WaitForSingleObject( hThread, INFINITE );
}
CloseHandle( hThread );
GetManagedAppsExit:
if ( hUserToken )
CloseHandle( hUserToken );
if ( hEventAppsEnumerated )
CloseHandle( hEventAppsEnumerated );
return Status;
}
error_status_t
RsopReportInstallFailure(
IN PINSTALLCONTEXT pInstallContext,
IN PWSTR pwszDeploymentId,
IN DWORD dwEventId
)
{
GUID DeploymentId;
PAPPCONTEXT pAppContext;
CManagedAppProcessor* pManApp;
CAppInfo* pAppInfo;
pAppContext = (PAPPCONTEXT) pInstallContext;
pManApp = pAppContext->pManApp;
if ( ! pManApp || ! pManApp->GetRsopContext()->IsDiagnosticModeEnabled() )
{
return ERROR_SUCCESS;
}
//
// Assume that the failure happened in the app that
// we're trying to install
//
pAppInfo = pAppContext->pAppInfo;
if ( ! pAppInfo )
{
return ERROR_SUCCESS;
}
StringToGuid( pwszDeploymentId, &DeploymentId );
//
// Check to see if the failure was in the app that we're
// trying to install
//
if ( ! IsEqualGUID( pAppInfo->DeploymentId(), DeploymentId ) )
{
//
// If not, see if the requested app is one of the apps
// that was uninstalled before trying to apply the
// target app, or was reinstalled as part of a rollback
// from failure
//
pAppInfo = pManApp->AppList().Find( DeploymentId );
if ( pAppInfo )
{
//
// The failure happened during an uninstall for
// a rip and replace upgrade, so we need to
// log a failure for the upgrade as well
//
pAppContext->pAppInfo->SetRsopFailureStatus(
ERROR_GEN_FAILURE,
dwEventId);
}
}
//
// If we found the app requested by the caller, log a failure
// status for it -- the error we pass to the method is only
// used as a check against ERROR_SUCCESS, so we do not
// need to pass the actual error that occurred
//
if ( pAppInfo )
{
pAppInfo->SetRsopFailureStatus(
ERROR_GEN_FAILURE,
dwEventId);
}
return ERROR_SUCCESS;
}
error_status_t
GetManagedAppCategories(
IN handle_t hRpc,
IN OUT APPCATEGORYLIST* pCategoryList
)
{
DWORD Status;
HRESULT hr;
APPCATEGORYINFOLIST AppCategories;
HANDLE hUserToken;
HKEY hkRoot;
CheckLocalCall( hRpc );
hr = S_OK;
hUserToken = NULL;
hkRoot = NULL;
memset( pCategoryList, 0, sizeof( *pCategoryList ) );
memset( &AppCategories, 0, sizeof( AppCategories ) );
//
// Now prepare to initiate the query -- first
// we need to get some user specific information
// to build the object which performs the query,
// so we impersonate.
//
Status = RpcImpersonateClient( NULL );
if ( ERROR_SUCCESS != Status )
return Status;
BOOL bStatus;
bStatus = OpenThreadToken(
GetCurrentThread(),
TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
TRUE,
&hUserToken );
if ( ! bStatus )
Status = GetLastError();
if ( ERROR_SUCCESS == Status )
Status = RegOpenCurrentUser( GENERIC_ALL, &hkRoot );
if ( ERROR_SUCCESS == Status )
hr = CsGetAppCategories( &AppCategories );
RevertToSelf();
if ( ( ERROR_SUCCESS != Status ) || FAILED( hr ) )
goto GetManagedAppCategoriesExit;
gpEvents->SetToken( hUserToken );
//
// The rpc interface is such that our out parameter is just
// one allocation -- references within each array element are allocated
// within the block, so we must first calculate how big the block is
//
if ( SUCCEEDED( hr ) )
{
DWORD cbSize;
DWORD iCat;
cbSize = sizeof( APPCATEGORY ) * AppCategories.cCategory;
for (iCat = 0; iCat < AppCategories.cCategory; iCat++)
{
cbSize += ( lstrlen( AppCategories.pCategoryInfo[iCat].pszDescription ) + 1 ) *
sizeof( WCHAR );
}
pCategoryList->pCategoryInfo = (APPCATEGORY*) midl_user_allocate( cbSize );
if ( ! pCategoryList->pCategoryInfo )
{
hr = E_OUTOFMEMORY;
}
}
//
// Now that we have sufficient memory, we can copy the data
//
if ( SUCCEEDED( hr ) )
{
WCHAR* wszDescriptions;
DWORD iCat;
wszDescriptions = (WCHAR*) &( pCategoryList->pCategoryInfo[ AppCategories.cCategory ] );
pCategoryList->cCategory = AppCategories.cCategory;
for (iCat = 0; iCat < AppCategories.cCategory; iCat++)
{
pCategoryList->pCategoryInfo[ iCat ].Locale = AppCategories.pCategoryInfo[iCat].Locale;
pCategoryList->pCategoryInfo[ iCat ].AppCategoryId = AppCategories.pCategoryInfo[iCat].AppCategoryId;
pCategoryList->pCategoryInfo[ iCat ].pszDescription = wszDescriptions;
hr = StringCchCopy( wszDescriptions,
lstrlen(AppCategories.pCategoryInfo[iCat].pszDescription)+1,
AppCategories.pCategoryInfo[iCat].pszDescription );
if (FAILED(hr))
{
break;
}
wszDescriptions += lstrlen( wszDescriptions ) + 1;
}
}
//
// If we have successfully generated results to return to the caller, log
// those results
//
if ( SUCCEEDED( hr ) )
{
HRESULT hrLog;
DWORD StatusLog;
CRsopAppContext RsopContext( CRsopAppContext::ARPLIST );
CManagedAppProcessor AppProcessor(
0,
hUserToken,
hkRoot,
NULL,
TRUE,
FALSE,
&RsopContext,
StatusLog );
if ( ERROR_SUCCESS == StatusLog )
{
CCategoryInfoLog CategoryLog( AppProcessor.GetRsopContext(), &AppCategories );
Status = AppProcessor.GetRsopContext()->GetExclusiveLoggingAccess( NULL == hUserToken );
if ( ERROR_SUCCESS == Status )
{
hrLog = CategoryLog.WriteLog();
}
AppProcessor.GetRsopContext()->ReleaseExclusiveLoggingAccess();
}
else
{
hrLog = HRESULT_FROM_WIN32( StatusLog );
}
if ( FAILED(hrLog) )
{
RsopContext.DisableRsop( hrLog );
}
}
Status = GetWin32ErrFromHResult( hr );
//
// Free the internal version of the category list
//
ReleaseAppCategoryInfoList( &AppCategories );
GetManagedAppCategoriesExit:
if ( hUserToken )
{
CloseHandle( hUserToken );
}
if ( hkRoot )
{
RegCloseKey( hkRoot );
}
return Status;
}
DWORD
WINAPI
GetManagedAppsProc(
LPVOID pvArpContext
)
{
ARPCONTEXT* pArpContext;
pArpContext = (ARPCONTEXT*) pvArpContext;
HANDLE hUserToken;
error_status_t Status;
BOOL bStatus;
HKEY hkRoot;
hUserToken = NULL;
hkRoot = NULL;
Status = ERROR_SUCCESS;
bStatus = DuplicateTokenEx(
pArpContext->hUserToken,
TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_IMPERSONATE,
NULL,
SecurityImpersonation,
TokenImpersonation,
&hUserToken);
if ( ! bStatus )
Status = GetLastError();
if ( bStatus )
{
bStatus = ImpersonateLoggedOnUser( hUserToken );
if ( ! bStatus )
Status = GetLastError();
}
if ( ERROR_SUCCESS == Status )
Status = RegOpenCurrentUser( GENERIC_ALL, &hkRoot );
RevertToSelf();
if ( Status != ERROR_SUCCESS )
{
pArpContext->Status = Status;
goto GetManagedAppsProcExit;
}
gpEvents->SetToken( hUserToken );
LogTime();
//
// Now that we have a valid GPOInfo object, we can construct
// an app processor object to do the query
//
{
CRsopAppContext RsopContext( CRsopAppContext::ARPLIST, pArpContext->hEventAppsEnumerated );
CManagedAppProcessor AppProcessor( 0,
hUserToken,
hkRoot,
NULL,
TRUE,
FALSE,
&RsopContext,
Status );
if ( ERROR_SUCCESS == Status )
{
Status = AppProcessor.GetManagedApplications( pArpContext->pCategory, pArpContext );
}
}
GetManagedAppsProcExit:
gpEvents->ClearToken();
if ( hkRoot )
RegCloseKey( hkRoot );
if ( hUserToken )
CloseHandle( hUserToken );
return 0;
}
BOOL
SetSystemRestorePoint(
IN WCHAR * pwszApplicationName,
IN OUT PAPPCONTEXT pAppContext
)
{
RESTOREPOINTINFO RestoreInfo;
STATEMGRSTATUS SRStatus;
HKEY hkInstallerPolicy;
DWORD CheckpointPolicy;
DWORD CheckpointPolicySize;
DWORD InstallLen, NameLen;
DWORD Status;
BOOL bStatus;
Status = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
L"Software\\Policies\\Microsoft\\Windows\\Installer",
0,
KEY_READ,
&hkInstallerPolicy );
if ( Status != ERROR_SUCCESS )
return FALSE;
CheckpointPolicy = 0;
CheckpointPolicySize = sizeof(CheckpointPolicy);
(void) RegQueryValueEx(
hkInstallerPolicy,
L"LimitSystemRestoreCheckpointing",
NULL,
NULL,
(LPBYTE) &CheckpointPolicy,
&CheckpointPolicySize );
RegCloseKey( hkInstallerPolicy );
if ( CheckpointPolicy != 0 )
return FALSE;
if ( ! LoadLoadString() )
return FALSE;
Status = (*pfnLoadStringW)( ghDllInstance, IDS_INSTALLED, RestoreInfo.szDescription, sizeof(RestoreInfo.szDescription) / sizeof(WCHAR) );
if ( 0 == Status )
return FALSE;
pAppContext->pLoadSfc = new CLoadSfc( Status );
if ( Status != ERROR_SUCCESS )
{
delete pAppContext->pLoadSfc;
pAppContext->pLoadSfc = 0;
}
if ( ! pAppContext->pLoadSfc )
return FALSE;
RestoreInfo.dwEventType = BEGIN_NESTED_SYSTEM_CHANGE;
RestoreInfo.dwRestorePtType = APPLICATION_INSTALL;
RestoreInfo.llSequenceNumber = 0;
InstallLen = lstrlen(RestoreInfo.szDescription);
NameLen = lstrlen(pwszApplicationName);
if ( InstallLen + NameLen >= MAX_DESC_W )
{
lstrcpyn( &RestoreInfo.szDescription[InstallLen], pwszApplicationName, MAX_DESC_W - InstallLen - 1 );
RestoreInfo.szDescription[MAX_DESC_W - 1] = 0;
}
else
{
HRESULT hr;
hr = StringCchCopy( &RestoreInfo.szDescription[InstallLen], MAX_DESC_W,pwszApplicationName );
if (FAILED(hr))
{
return FALSE;
}
}
bStatus = (*gpfnSRSetRetorePointW)( &RestoreInfo, &SRStatus );
if ( bStatus )
pAppContext->SRSequence = SRStatus.llSequenceNumber;
return bStatus;
}
void
CheckLocalCall(
IN handle_t hRpc
)
{
UINT Type;
DWORD Status;
Status = I_RpcBindingInqTransportType( hRpc, &Type);
if ( (Status != RPC_S_OK) ||
(Type != TRANSPORT_TYPE_LPC) )
RpcRaiseException( ERROR_ACCESS_DENIED );
}
WCHAR*
GetGpoNameFromGuid(
IN PGROUP_POLICY_OBJECT pGpoList,
IN GUID* pGpoGuid
)
{
PGROUP_POLICY_OBJECT pNextGpo;
WCHAR wszTargetGuid[MAX_SZGUID_LEN];
WCHAR* pszPolicyName;
pszPolicyName = NULL;
(void) GuidToString(
*pGpoGuid,
wszTargetGuid);
while (pGpoList)
{
pNextGpo = pGpoList->pNext;
if ( lstrcmpi(
pGpoList->szGPOName,
wszTargetGuid) == 0 )
{
pszPolicyName = pGpoList->lpDisplayName;
break;
}
pGpoList = pNextGpo;
}
return pszPolicyName;
}
DWORD
GetPlatformCompatibleCOMClsCtx(
DWORD Architecture,
DWORD dwClsCtx
)
{
if ( (PROCESSOR_ARCHITECTURE_AMD64 == Architecture) ||
(PROCESSOR_ARCHITECTURE_IA64 == Architecture) )
{
//
// On 64-bit, if we have any inproc server contexts, we need to
// ensure that we specifically ask for 64-bit inproc servers
//
if ( dwClsCtx & CLSCTX_INPROC )
{
DWORD dwInproc64;
dwInproc64 = 0;
if ( dwClsCtx & CLSCTX_INPROC_SERVER )
{
dwInproc64 |= CLSCTX64_INPROC_SERVER;
}
if ( dwClsCtx & CLSCTX_INPROC_HANDLER )
{
dwInproc64 |= CLSCTX64_INPROC_HANDLER;
}
//
// Now remove the standard inproc bits, which are interpreted
// as 32-bit inproc
//
dwClsCtx &= ~CLSCTX_INPROC;
//
// Add in the 64-bit inproc bits that we support
//
dwClsCtx |= dwInproc64;
}
}
return dwClsCtx;
}
void
LogRsopInstallData(
CManagedAppProcessor* pManApp,
CAppInfo* pAppInfo
)
{
HRESULT hr;
if ( ! pManApp->GetRsopContext()->IsRsopEnabled() )
{
return;
}
hr = pManApp->GetRsopContext()->GetExclusiveLoggingAccess( NULL == pManApp->UserToken() );
//
// If the call above failed, rsop will be disabled so we have nothing to do
//
if ( FAILED(hr) )
{
return;
}
//
// First log the installed application
//
pManApp->AppList().WriteAppToRsopLog( pAppInfo );
//
// Now write all the removal entries
//
pManApp->AppList().WriteLog( CAppList::RSOP_FILTER_REMOVALSONLY );
//
// Since we have installed an app, the resultant set has changed, so
// update the RSoP version to ensure that we detect the change if
// we roam to another machine
//
(void) pManApp->GetRsopContext()->WriteCurrentRsopVersion( pManApp->AppmgmtKey() );
(void) pManApp->GetRsopContext()->ReleaseExclusiveLoggingAccess();
}