static HRESULT ExecuteStrings( __in SCA_DB* psdList, __in SCA_SQLSTR* psssList, __in BOOL fInstall ) { HRESULT hr = S_FALSE; // assume nothing will be done int iRollback = -1; int iOldRollback = iRollback; LPCWSTR wzOldDb = NULL; UINT uiCost = 0; WCHAR* pwzCustomActionData = NULL; WCHAR wzNumber[64]; // loop through all sql strings for (SCA_SQLSTR* psss = psssList; psss; psss = psss->psssNext) { // if installing this component if ((fInstall && (psss->iAttributes & SCASQL_EXECUTE_ON_INSTALL) && WcaIsInstalling(psss->isInstalled, psss->isAction) && !WcaIsReInstalling(psss->isInstalled, psss->isAction)) || (fInstall && (psss->iAttributes & SCASQL_EXECUTE_ON_REINSTALL) && WcaIsReInstalling(psss->isInstalled, psss->isAction)) || (!fInstall && (psss->iAttributes & SCASQL_EXECUTE_ON_UNINSTALL) && WcaIsUninstalling(psss->isInstalled, psss->isAction))) { // determine if this is a rollback scheduling or normal deferred scheduling if (psss->iAttributes & SCASQL_ROLLBACK) { iRollback = 1; } else { iRollback = 0; } // if we need to create a connection to a new server\database if (!wzOldDb || 0 != lstrcmpW(wzOldDb, psss->wzSqlDb) || iOldRollback != iRollback) { const SCA_DB* psd = ScaDbsFindDatabase(psss->wzSqlDb, psdList); if (!psd) { ExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "failed to find data for Database: %ls", psss->wzSqlDb); } if (-1 == iOldRollback) { iOldRollback = iRollback; } Assert(0 == iOldRollback || 1 == iOldRollback); // if there was custom action data before, schedule the action to write it if (pwzCustomActionData && *pwzCustomActionData) { Assert(pwzCustomActionData && *pwzCustomActionData && uiCost); hr = WcaDoDeferredAction(1 == iOldRollback ? L"RollbackExecuteSqlStrings" : L"ExecuteSqlStrings", pwzCustomActionData, uiCost); ExitOnFailure1(hr, "failed to schedule ExecuteSqlStrings action, rollback: %d", iOldRollback); iOldRollback = iRollback; *pwzCustomActionData = L'\0'; uiCost = 0; } Assert(!pwzCustomActionData || (pwzCustomActionData && 0 == *pwzCustomActionData) && 0 == uiCost); hr = WcaWriteStringToCaData(psd->wzKey, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Server Database String to CustomActionData for Database String: %ls", psd->wzKey); hr = WcaWriteStringToCaData(psd->wzServer, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Server to CustomActionData for Database String: %ls", psd->wzKey); hr = WcaWriteStringToCaData(psd->wzInstance, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Instance to CustomActionData for Database String: %ls", psd->wzKey); hr = WcaWriteStringToCaData(psd->wzDatabase, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Database to CustomActionData for Database String: %ls", psd->wzKey); hr = ::StringCchPrintfW(wzNumber, countof(wzNumber), L"%d", psd->iAttributes); ExitOnFailure(hr, "Failed to format attributes integer value to string"); hr = WcaWriteStringToCaData(wzNumber, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Attributes to CustomActionData for Database String: %ls", psd->wzKey); hr = ::StringCchPrintfW(wzNumber, countof(wzNumber), L"%d", psd->fUseIntegratedAuth); ExitOnFailure(hr, "Failed to format UseIntegratedAuth integer value to string"); hr = WcaWriteStringToCaData(wzNumber, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL IntegratedAuth flag to CustomActionData for Database String: %ls", psd->wzKey); hr = WcaWriteStringToCaData(psd->scau.wzName, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL UserName to CustomActionData for Database String: %ls", psd->wzKey); hr = WcaWriteStringToCaData(psd->scau.wzPassword, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Password to CustomActionData for Database String: %ls", psd->wzKey); uiCost += COST_SQL_CONNECTDB; wzOldDb = psss->wzSqlDb; } WcaLog(LOGMSG_VERBOSE, "Scheduling SQL string: %ls", psss->pwzSql); hr = WcaWriteStringToCaData(psss->wzKey, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to add SQL Key to CustomActionData for SQL string: %ls", psss->wzKey); hr = WcaWriteIntegerToCaData(psss->iAttributes, &pwzCustomActionData); ExitOnFailure1(hr, "failed to add attributes to CustomActionData for SQL string: %ls", psss->wzKey); hr = WcaWriteStringToCaData(psss->pwzSql, &pwzCustomActionData); ExitOnFailure1(hr, "Failed to to add SQL Query to CustomActionData for SQL string: %ls", psss->wzKey); uiCost += COST_SQL_STRING; } } if (pwzCustomActionData && *pwzCustomActionData) { Assert(pwzCustomActionData && *pwzCustomActionData && uiCost); hr = WcaDoDeferredAction(1 == iRollback ? L"RollbackExecuteSqlStrings" : L"ExecuteSqlStrings", pwzCustomActionData, uiCost); ExitOnFailure(hr, "Failed to schedule ExecuteSqlStrings action"); *pwzCustomActionData = L'\0'; uiCost = 0; } LExit: ReleaseStr(pwzCustomActionData); return hr; }
extern "C" HRESULT DependencyPlanPackageBegin( __in BOOL fPerMachine, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan ) { HRESULT hr = S_OK; STRINGDICT_HANDLE sdIgnoredDependents = NULL; DEPENDENCY* rgDependents = NULL; UINT cDependents = 0; HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; // Make sure the package defines at least one provider. if (0 == pPackage->cDependencyProviders) { LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); ExitFunction1(hr = S_OK); } // Make sure the package is in the same scope as the bundle. if (fPerMachine != pPackage->fPerMachine) { LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); ExitFunction1(hr = S_OK); } // If we're uninstalling the package, check if any dependents are registered. if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { // Build up a list of dependents to ignore, including the current bundle. hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); ExitOnFailure(hr, "Failed to build the list of ignored dependents."); // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. hr = DictKeyExists(sdIgnoredDependents, L"ALL"); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); } else { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); if (E_FILENOTFOUND != hr) { ExitOnFailure1(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); } else { hr = S_OK; } } } } // Calculate the dependency actions before the package itself is planned. CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); // If dependents were found, change the action to not uninstall the package. if (0 < cDependents) { LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); for (DWORD i = 0; i < cDependents; ++i) { const DEPENDENCY* pDependency = &rgDependents[i]; LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); } pPackage->fDependencyManagerWasHere = TRUE; pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } else // use the calculated dependency actions as the provider actions if there are any non-imported providers { // that will need to be registered. BOOL fAllImportedProviders = TRUE; // assume all providers were imported. for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; if (!pProvider->fImported) { fAllImportedProviders = FALSE; break; } } if (!fAllImportedProviders) { pPackage->providerExecute = dependencyExecuteAction; pPackage->providerRollback = dependencyRollbackAction; } } // If the package will be removed, add its providers to the growing list in the plan. if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); ExitOnFailure1(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); } } pPackage->dependencyExecute = dependencyExecuteAction; pPackage->dependencyRollback = dependencyRollbackAction; LExit: ReleaseDependencyArray(rgDependents, cDependents); ReleaseDict(sdIgnoredDependents); return hr; }
static HRESULT RecursePath( __in_z LPCWSTR wzPath, __in_z LPCWSTR wzId, __in_z LPCWSTR wzComponent, __in_z LPCWSTR wzProperty, __in int iMode, __inout DWORD* pdwCounter, __inout MSIHANDLE* phTable, __inout MSIHANDLE* phColumns ) { HRESULT hr = S_OK; DWORD er; LPWSTR sczSearch = NULL; LPWSTR sczProperty = NULL; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATAW wfd; LPWSTR sczNext = NULL; // First recurse down to all the child directories. hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath); ExitOnFailure1(hr, "Failed to allocate file search string in path: %S", wzPath); hFind = ::FindFirstFileW(sczSearch, &wfd); if (INVALID_HANDLE_VALUE == hFind) { er = ::GetLastError(); if (ERROR_PATH_NOT_FOUND == er) { ExitFunction1(hr = S_FALSE); } else { hr = HRESULT_FROM_WIN32(er); } ExitOnFailure1(hr, "Failed to find all files in path: %S", wzPath); } do { // Skip files and the dot directories. if (FILE_ATTRIBUTE_DIRECTORY != (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) { continue; } hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName); ExitOnFailure2(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath); hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, pdwCounter, phTable, phColumns); ExitOnFailure1(hr, "Failed to recurse path: %S", sczNext); } while (::FindNextFileW(hFind, &wfd)); er = ::GetLastError(); if (ERROR_NO_MORE_FILES == er) { hr = S_OK; } else { hr = HRESULT_FROM_WIN32(er); ExitOnFailure1(hr, "Failed while looping through files in directory: %S", wzPath); } // Finally, set a property that points at our path. hr = StrAllocFormatted(&sczProperty, L"_%s_%u", wzProperty, *pdwCounter); ExitOnFailure1(hr, "Failed to allocate Property for RemoveFile table with property: %S.", wzProperty); ++(*pdwCounter); hr = WcaSetProperty(sczProperty, wzPath); ExitOnFailure2(hr, "Failed to set Property: %S with path: %S", sczProperty, wzPath); // Add the row to remove any files and another row to remove the folder. hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode); ExitOnFailure2(hr, "Failed to add row to remove all files for WixRemoveFolderEx row: %S under path:", wzId, wzPath); hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode); ExitOnFailure2(hr, "Failed to add row to remove folder for WixRemoveFolderEx row: %S under path: %S", wzId, wzPath); LExit: if (INVALID_HANDLE_VALUE != hFind) { ::FindClose(hFind); } ReleaseStr(sczNext); ReleaseStr(sczProperty); ReleaseStr(sczSearch); return hr; }
extern "C" HRESULT ExeEngineParsePackageFromXml( __in IXMLDOMNode* pixnExePackage, __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; LPWSTR scz = NULL; // @DetectCondition hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition); ExitOnFailure(hr, "Failed to get @DetectCondition."); // @InstallArguments hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments); ExitOnFailure(hr, "Failed to get @InstallArguments."); // @UninstallArguments hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments); ExitOnFailure(hr, "Failed to get @UninstallArguments."); // @RepairArguments hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments); ExitOnFailure(hr, "Failed to get @RepairArguments."); // @Repairable hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @Repairable."); } // @Protocol hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz); if (SUCCEEDED(hr)) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1)) { pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1)) { pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1)) { pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE; } else { hr = E_UNEXPECTED; ExitOnFailure1(hr, "Invalid protocol type: %ls", scz); } } else if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @Protocol."); } // select exit code nodes hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes); ExitOnFailure(hr, "Failed to select exit code nodes."); // get exit code node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get exit code node count."); if (cNodes) { // allocate memory for exit codes pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*)MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE); ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs."); pPackage->Exe.cExitCodes = cNodes; // parse package elements for (DWORD i = 0; i < cNodes; ++i) { BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // @Type hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); ExitOnFailure(hr, "Failed to get @Type."); if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"success", -1)) { pExitCode->type = BURN_EXE_EXIT_CODE_TYPE_SUCCESS; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"error", -1)) { pExitCode->type = BURN_EXE_EXIT_CODE_TYPE_ERROR; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"scheduleReboot", -1)) { pExitCode->type = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"forceReboot", -1)) { pExitCode->type = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT; } else { hr = E_UNEXPECTED; ExitOnFailure1(hr, "Invalid exit code type: %ls", scz); } // @Code hr = XmlGetAttributeEx(pixnNode, L"Code", &scz); ExitOnFailure(hr, "Failed to get @Code."); if (L'*' == scz[0]) { pExitCode->fWildcard = TRUE; } else { hr = StrStringToUInt32(scz, 0, (UINT*)&pExitCode->dwCode); ExitOnFailure1(hr, "Failed to parse @Code value: %ls", scz); } // prepare next iteration ReleaseNullObject(pixnNode); } } hr = S_OK; LExit: ReleaseObject(pixnNodes); ReleaseObject(pixnNode); ReleaseStr(scz); return hr; }
HRESULT CpiRollbackConfigurePartitionUsers( LPWSTR* ppwzData, CPI_ROLLBACK_DATA* pRollbackDataList ) { HRESULT hr = S_OK; int iRollbackStatus; CPI_PARTITION_USER_ATTRIBUTES attrs; ::ZeroMemory(&attrs, sizeof(attrs)); // read action text hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); ExitOnFailure(hr, "Failed to send action start message"); // get count int iCnt = 0; hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); ExitOnFailure(hr, "Failed to read count"); for (int i = 0; i < iCnt; i++) { // read partition attributes from CustomActionData hr = ReadPartitionUserAttributes(ppwzData, &attrs); ExitOnFailure(hr, "Failed to read attributes"); // rollback status hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); if (S_FALSE == hr) continue; // not found, nothing to rollback // progress message hr = CpiActionDataMessage(1, attrs.pwzAccount); ExitOnFailure(hr, "Failed to send progress messages"); if (S_FALSE == hr) ExitFunction(); // action switch (attrs.iActionType) { case atCreate: hr = CreatePartitionUser(&attrs); ExitOnFailure1(hr, "Failed to create partition user, key: %S", attrs.pwzKey); break; case atRemove: hr = RemovePartitionUser(&attrs); ExitOnFailure1(hr, "Failed to remove partition user, key: %S", attrs.pwzKey); break; } // check rollback status if (0 == iRollbackStatus) continue; // operation did not complete, skip progress // progress hr = WcaProgressMessage(attrs.iActionCost, FALSE); ExitOnFailure(hr, "Failed to update progress"); } hr = S_OK; LExit: // clean up FreePartitionUserAttributes(&attrs); return hr; }
/****************************************************************** CaSchedServiceConfig - entry point for CaSchedServiceConfig Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaExecServiceConfig ********************************************************************/ extern "C" UINT __stdcall SchedServiceConfig( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedServiceConfig"); HRESULT hr = S_OK; UINT uiResult = ERROR_SUCCESS; DWORD dwError = 0; LPWSTR pwzData = NULL; int iData = 0; BOOL fExistingService = FALSE; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; INSTALLSTATE isInstalled; INSTALLSTATE isAction; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; LPSERVICE_FAILURE_ACTIONSW psfa; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzRollbackCustomActionData = NULL; DWORD cServices = 0; DWORD dwRestartDelay = 0; WCHAR wzActionName[32] = { 0 }; DWORD dwSizeNeeded = 0; // initialize hr = WcaInitialize(hInstall, "SchedServiceConfig"); ExitOnFailure(hr, "failed to initialize"); //Get a handle to the service control manager hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (hSCM == NULL) ExitOnLastError(hr, "failed to get handle to SCM"); // loop through all the services to be configured hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView); ExitOnFailure(hr, "failed to open view on ServiceConfig table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData); ExitOnFailure(hr, "failed to get object NewService"); fExistingService = 1 != iData; // Get component name hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData); ExitOnFailure(hr, "failed to get component name"); // check if we are installing this Component hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(hr), "failed to get install state for Component: %S", pwzData); // We want to configure either a service we're installing or one already on the box if (WcaIsInstalling(isInstalled, isAction)) { // Check if we're configuring an existing service if (fExistingService) { // Confirm the service is actually on the box hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "failed to get object NewService"); //Get a handle to the service hService = ::OpenServiceW(hSCM, pwzData, SERVICE_QUERY_CONFIG); if (hService == NULL) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); if (hr == ERROR_SERVICE_DOES_NOT_EXIST) { ExitOnFailure1(hr, "Service \"%s\" does not exist on this system.", pwzData); } else { ExitOnFailure1(hr, "Failed to get handle to the service \"%S\".", pwzData); } } // Get Current Service Config info if(!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &dwSizeNeeded) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { ExitOnLastError(hr, "Failed to get current service config info."); } // Alloc space we were told we needed psfa = (LPSERVICE_FAILURE_ACTIONSW) MemAlloc(dwSizeNeeded, TRUE); ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions."); // Now do the real query if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, dwSizeNeeded, &dwSizeNeeded)) ExitOnLastError(hr, "failed to Query Service."); // Build up rollback CA data so we can restore service state if necessary hr = WcaWriteStringToCaData(pwzData, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // If this service struct is empty, fill in defualt values if(psfa->cActions < 3) { hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } else { // psfa actually had actions defined, so use them // action 1 hr = GetSCActionTypeString(psfa->lpsaActions[0].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[0].Type) dwRestartDelay = psfa->lpsaActions[0].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // action 2 hr = GetSCActionTypeString(psfa->lpsaActions[1].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[1].Type) dwRestartDelay = psfa->lpsaActions[1].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // action 3 hr = GetSCActionTypeString(psfa->lpsaActions[2].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[2].Type) dwRestartDelay = psfa->lpsaActions[2].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } hr = WcaWriteIntegerToCaData(psfa->dwResetPeriod / (24 * 60 * 60), &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaWriteIntegerToCaData(dwRestartDelay, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); // check for value being null if(!psfa->lpCommand) psfa->lpCommand = L""; hr = WcaWriteStringToCaData(psfa->lpCommand, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // check for value being null if(!psfa->lpRebootMsg) psfa->lpRebootMsg = L""; hr = WcaWriteStringToCaData(psfa->lpRebootMsg, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Clear up per-service values if(psfa) MemFree(psfa); } // add the data to the CustomActionData (for install) hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "failed to get name of service"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get first failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get second failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get third failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData); if (hr == S_FALSE) // deal w/ possible null value iData = 0; ExitOnFailure(hr, "failed to get reset period in days between service restart attempts."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData); if (hr == S_FALSE) // deal w/ possible null value iData = 0; ExitOnFailure(hr, "failed to get server restart delay value."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get command line to run on service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); cServices++; ::CloseServiceHandle(hService); hService = NULL; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all objects to secure"); // setup CustomActionData and add to progress bar for download if (pwzRollbackCustomActionData && *pwzRollbackCustomActionData) { Assert(0 < cServices); hr = WcaDoDeferredAction(L"ExecServiceConfigRollback", pwzRollbackCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } // schedule the custom action and add to progress bar if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cServices); hr = WcaDoDeferredAction(L"ExecServiceConfig", pwzCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } LExit: // Clean up handles if (hService != NULL) ::CloseServiceHandle(hService); if (hSCM != NULL) ::CloseServiceHandle(hSCM); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzRollbackCustomActionData); ReleaseStr(pwzData); if (FAILED(hr)) uiResult = ERROR_INSTALL_FAILURE; return WcaFinalize(uiResult); }
/****************************************************************** SchedAddinRegistration - entry point for AddinRegistration Custom Action ********************************************************************/ HRESULT SchedAddinRegistration(MSIHANDLE hInstall, BOOL fInstall) { // AssertSz(FALSE, "debug SchedRegisterAddins"); HRESULT hr = S_OK; LPWSTR pwzCustomActionData = NULL; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; LPWSTR pwzData = NULL; LPWSTR pwzTemp = NULL; LPWSTR pwzComponent = NULL; LPWSTR pwzId = NULL; LPWSTR pwzFile = NULL; LPWSTR pwzFriendlyName = NULL; LPWSTR pwzDescription = NULL; int iBitness = 0; int iCommandLineSafe = 1; int iLoadBehavior = 0; LPWSTR pwzAllUsers = NULL; int nAddins = 0; hr = WcaGetProperty(L"ALLUSERS", &pwzAllUsers); ExitOnFailure(hr, "failed to read value of ALLUSERS property"); // loop through all the RegisterAddin records hr = WcaOpenExecuteView(vcsRegisterAddinsQuery, &hView); ExitOnFailure(hr, "failed to open view on AddinRegistration table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { ++nAddins; // Get Id hr = WcaGetRecordString(hRec, arqId, &pwzId); ExitOnFailure(hr, "failed to get AddinRegistration.AddinRegistration"); // Get File hr = WcaGetRecordString(hRec, arqFile, &pwzData); ExitOnFailure1(hr, "failed to get AddinRegistration.File_ for record: %ls", pwzId); hr = StrAllocFormatted(&pwzTemp, L"[#%ls]", pwzData); ExitOnFailure1(hr, "failed to format file string for file: %ls", pwzData); hr = WcaGetFormattedString(pwzTemp, &pwzFile); ExitOnFailure1(hr, "failed to get formatted string for file: %ls", pwzData); // Get name hr = WcaGetRecordFormattedString(hRec, arqName, &pwzFriendlyName); ExitOnFailure1(hr, "failed to get AddinRegistration.Name for record: %ls", pwzId); // Get description hr = WcaGetRecordFormattedString(hRec, arqDescription, &pwzDescription); ExitOnFailure1(hr, "failed to get AddinRegistration.Description for record: %ls", pwzId); // Get description hr = WcaGetRecordInteger(hRec, arqBitness, &iBitness); ExitOnFailure1(hr, "failed to get AddinRegistration.Bitnesss for record: %ls", pwzId); // Get description hr = WcaGetRecordInteger(hRec, arqCommandLineSafe, &iCommandLineSafe); ExitOnFailure1(hr, "failed to get AddinRegistration.CommandLineSafe for record: %ls", pwzId); // Get description hr = WcaGetRecordInteger(hRec, arqLoadBehavior, &iLoadBehavior); ExitOnFailure1(hr, "failed to get AddinRegistration.LoadBehavior for record: %ls", pwzId); // get component and its install/action states hr = WcaGetRecordString(hRec, arqComponent, &pwzComponent); ExitOnFailure(hr, "failed to get addin component id"); // we need to know if the component's being installed, uninstalled, or reinstalled WCA_TODO todo = WcaGetComponentToDo(pwzComponent); // skip this entry if this is the install CA and we are uninstalling the component if (fInstall && WCA_TODO_UNINSTALL == todo) { continue; } // skip this entry if this is an uninstall CA and we are not uninstalling the component if (!fInstall && WCA_TODO_UNINSTALL != todo) { continue; } // write custom action data: operation, instance guid, path, directory hr = WcaWriteIntegerToCaData(todo, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write operation to custom action data for instance id: %ls", pwzId); hr = WcaWriteStringToCaData(pwzId, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write id to custom action data for instance id: %ls", pwzId); hr = WcaWriteStringToCaData(pwzFile, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write custom action data for instance id: %ls", pwzId); hr = WcaWriteStringToCaData(pwzFriendlyName, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write addin name to custom action data for instance id: %ls", pwzId); hr = WcaWriteStringToCaData(pwzDescription, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write addin description to custom action data for instance id: %ls", pwzId); hr = WcaWriteIntegerToCaData(iBitness, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write Bitness to custom action data for instance id: %ls", pwzId); hr = WcaWriteIntegerToCaData(iCommandLineSafe, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write CommandLineSafe to custom action data for instance id: %ls", pwzId); hr = WcaWriteIntegerToCaData(iLoadBehavior, &pwzCustomActionData); ExitOnFailure1(hr, "failed to write LoadBehavior to custom action data for instance id: %ls", pwzId); hr = WcaWriteStringToCaData(pwzAllUsers, &pwzCustomActionData); ExitOnFailure(hr, "failed to write allusers property to custom action data for instance id: %ls", pwzId); } if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all files to create native images for"); // Schedule the install custom action if (pwzCustomActionData && *pwzCustomActionData) { WcaLog(LOGMSG_STANDARD, "Scheduling Addin Registration (%ls)", pwzCustomActionData); hr = WcaDoDeferredAction(L"RollbackAddinRegistration", pwzCustomActionData, nAddins * COST_REGISTER_ADDIN); ExitOnFailure(hr, "Failed to schedule addin registration rollback"); hr = WcaDoDeferredAction(L"ExecAddinRegistration", pwzCustomActionData, nAddins * COST_REGISTER_ADDIN); ExitOnFailure(hr, "Failed to schedule addin registration execution"); } LExit: ReleaseStr(pwzAllUsers); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzId); ReleaseStr(pwzData); ReleaseStr(pwzData); ReleaseStr(pwzTemp); ReleaseStr(pwzComponent); ReleaseStr(pwzFile); ReleaseStr(pwzFriendlyName); ReleaseStr(pwzDescription); return hr; }
/******************************************************************** AllocateAcl - allocate an acl and populate it with this user and permission information user could be user or domain\user ********************************************************************/ HRESULT AllocateAcl(SCA_SMBP* pssp, PACL* ppACL) { HRESULT hr = S_OK; EXPLICIT_ACCESSW* pEA = NULL; DWORD cEA = 0; DWORD dwCounter = 0; PSID psid = NULL; LPCWSTR wzUser = NULL; DWORD nPermissions = 0; DWORD nErrorReturn = 0; ACCESS_MODE accessMode = NOT_USED_ACCESS; cEA = pssp->dwUserPermissionCount + 1; if (cEA >= MAXSIZE_T / sizeof(EXPLICIT_ACCESSW)) { ExitOnFailure1(hr = E_OUTOFMEMORY, "Too many user permissions to allocate: %u", cEA); } pEA = static_cast<EXPLICIT_ACCESSW*>(MemAlloc(cEA * sizeof(EXPLICIT_ACCESSW), TRUE)); ExitOnNull(pEA, hr, E_OUTOFMEMORY, "failed to allocate memory for explicit access structure"); // figure out how big the psid is for (dwCounter = 0; dwCounter < pssp->dwUserPermissionCount; ++dwCounter) { wzUser = pssp->pUserPerms[dwCounter].wzUser; nPermissions = pssp->pUserPerms[dwCounter].nPermissions; accessMode = pssp->pUserPerms[dwCounter].accessMode; // // create the appropriate SID // // figure out the right user to put into the access block if (0 == lstrcmpW(wzUser, L"Everyone")) { hr = AclGetWellKnownSid(WinWorldSid, &psid); } else if (0 == lstrcmpW(wzUser, L"Administrators")) { hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid); } else if (0 == lstrcmpW(wzUser, L"LocalSystem")) { hr = AclGetWellKnownSid(WinLocalSystemSid, &psid); } else if (0 == lstrcmpW(wzUser, L"LocalService")) { hr = AclGetWellKnownSid(WinLocalServiceSid, &psid); } else if (0 == lstrcmpW(wzUser, L"NetworkService")) { hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid); } else if (0 == lstrcmpW(wzUser, L"AuthenticatedUser")) { hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid); } else if (0 == lstrcmpW(wzUser, L"Guests")) { hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid); } else if(0 == lstrcmpW(wzUser, L"CREATOR OWNER")) { hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid); } else { hr = AclGetAccountSid(NULL, wzUser, &psid); } ExitOnFailure1(hr, "failed to get sid for account: %ls", wzUser); // we now have a valid pSid, fill in the EXPLICIT_ACCESS /* Permissions options: (see sca.sdh for defined sdl options) #define GENERIC_READ (0x80000000L) 2147483648 #define GENERIC_WRITE (0x40000000L) 1073741824 #define GENERIC_EXECUTE (0x20000000L) 536870912 #define GENERIC_ALL (0x10000000L) 268435456 */ pEA[dwCounter].grfAccessPermissions = nPermissions; pEA[dwCounter].grfAccessMode = accessMode; pEA[dwCounter].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; #pragma prefast(push) #pragma prefast(disable:25029) ::BuildTrusteeWithSidW(&(pEA[dwCounter].Trustee), psid); #pragma prefast(pop) } // create a new ACL that contains the ACE *ppACL = NULL; #pragma prefast(push) #pragma prefast(disable:25029) nErrorReturn = ::SetEntriesInAclW(dwCounter, pEA, NULL, ppACL); #pragma prefast(pop) ExitOnFailure(hr = HRESULT_FROM_WIN32(nErrorReturn), "failed to allocate ACL"); LExit: if (psid) { AclFreeSid(psid); } ReleaseMem(pEA); return hr; }
HRESULT ScaWebAppExtensionsRead( LPCWSTR wzApplication, SCA_WEB_APPLICATION_EXTENSION** ppswappextList ) { HRESULT hr = S_OK; PMSIHANDLE hView, hRec; SCA_WEB_APPLICATION_EXTENSION* pswappext = NULL; LPWSTR pwzData = NULL; // check pre-requisites hr = WcaTableExists(L"IIsWebApplicationExtension"); if (S_FALSE == hr) ExitFunction(); // convert the string into a msi record hRec = ::MsiCreateRecord(1); hr = WcaSetRecordString(hRec, 1, wzApplication); ExitOnFailure(hr, "Failed to set record to look up Web Application"); // open and execute the view on the applicatoin extension table hr = WcaOpenView(vcsWebAppExtensionQuery, &hView); ExitOnFailure(hr, "Failed to open view on IIsWebApplicationExtension table"); hr = WcaExecuteView(hView, hRec); ExitOnFailure1(hr, "Failed to execute view on IIsWebApplicationExtension table looking Application: %S", wzApplication); // get the application extention information while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = NewAppExt(&pswappext); ExitOnFailure(hr, "failed to create new web app extension"); // get the extension hr = WcaGetRecordString(hRec, wappextqExtension, &pwzData); ExitOnFailure(hr, "Failed to get Web Application Extension"); StringCchCopyW(pswappext->wzExtension, countof(pswappext->wzExtension), pwzData); // application extension verbs hr = WcaGetRecordString(hRec, wappextqVerbs, &pwzData); ExitOnFailure1(hr, "Failed to get Verbs for Application: '%S'", wzApplication); StringCchCopyW(pswappext->wzVerbs, countof(pswappext->wzVerbs), pwzData); // extension executeable hr = WcaGetRecordFormattedString(hRec, wappextqExecutable, &pwzData); ExitOnFailure1(hr, "Failed to get Executable for Application: '%S'", wzApplication); StringCchCopyW(pswappext->wzExecutable, countof(pswappext->wzExecutable), pwzData); hr = WcaGetRecordInteger(hRec, wappextqAttributes, &pswappext->iAttributes); if (S_FALSE == hr) { pswappext->iAttributes = 0; hr = S_OK; } ExitOnFailure(hr, "Failed to get App isolation"); *ppswappextList = AddAppExtToList(*ppswappextList, pswappext); pswappext = NULL; // set the appext NULL so it doesn't accidentally get freed below } if (E_NOMOREITEMS == hr) hr = S_OK; LExit: // if anything was left over after an error clean it all up if (pswappext) ScaWebAppExtensionsFreeList(pswappext); ReleaseStr(pwzData); return hr; }
HRESULT CpiApplicationRolesRead( CPI_APPLICATION_LIST* pAppList, CPI_APPLICATION_ROLE_LIST* pAppRoleList ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hView, hRec; CPI_APPLICATION_ROLE* pItm = NULL; LPWSTR pwzData = NULL; BOOL fMatchingArchitecture = FALSE; // loop through all application roles hr = WcaOpenExecuteView(vcsApplicationRoleQuery, &hView); ExitOnFailure(hr, "Failed to execute view on ComPlusApplicationRole table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { // get component hr = WcaGetRecordString(hRec, arqComponent, &pwzData); ExitOnFailure(hr, "Failed to get component"); // check if the component is our processor architecture if (pwzData && *pwzData) { hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); ExitOnFailure(hr, "Failed to get component architecture."); if (!fMatchingArchitecture) { continue; // not the same architecture, ignore } } // create entry pItm = (CPI_APPLICATION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_APPLICATION_ROLE)); if (!pItm) ExitFunction1(hr = E_OUTOFMEMORY); // get component install state if (pwzData && *pwzData) { pItm->fHasComponent = TRUE; er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); } // get key hr = WcaGetRecordString(hRec, arqApplicationRole, &pwzData); ExitOnFailure(hr, "Failed to get key"); StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); // get application hr = WcaGetRecordString(hRec, arqApplication, &pwzData); ExitOnFailure(hr, "Failed to get application"); hr = CpiApplicationFindByKey(pAppList, pwzData, &pItm->pApplication); if (S_FALSE == hr) hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); ExitOnFailure1(hr, "Failed to find application, key: %S", pwzData); // get name hr = WcaGetRecordFormattedString(hRec, arqName, &pwzData); ExitOnFailure(hr, "Failed to get name"); StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); // get properties if (CpiTableExists(cptComPlusApplicationRoleProperty)) { hr = CpiPropertiesRead(vcsApplicationRolePropertyQuery, pItm->wzKey, pdlApplicationRoleProperties, &pItm->pProperties, &pItm->iPropertyCount); ExitOnFailure(hr, "Failed to get properties"); } // set references & increment counters if (pItm->fHasComponent) { if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) { CpiApplicationAddReferenceInstall(pItm->pApplication); pAppRoleList->iInstallCount++; } if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) { CpiApplicationAddReferenceUninstall(pItm->pApplication); pAppRoleList->iUninstallCount++; } } // add entry if (pAppRoleList->pFirst) pItm->pNext = pAppRoleList->pFirst; pAppRoleList->pFirst = pItm; pItm = NULL; } if (E_NOMOREITEMS == hr) hr = S_OK; LExit: // clean up if (pItm) FreeApplicationRole(pItm); ReleaseStr(pwzData); return hr; }
/******************************************************************** CreateShare - create the file share on this computer ********************************************************************/ HRESULT CreateShare(SCA_SMBP* pssp) { if (!pssp || !(pssp->wzKey)) return E_INVALIDARG; HRESULT hr = S_OK; PACL pACL = NULL; SHARE_INFO_502 si; NET_API_STATUS s; DWORD dwParamErr = 0; BOOL fShareExists = SUCCEEDED(DoesShareExist(pssp->wzKey)); PSECURITY_DESCRIPTOR pSD = static_cast<PSECURITY_DESCRIPTOR>(MemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH, TRUE)); ExitOnNull(pSD, hr, E_OUTOFMEMORY, "Failed to allocate memory for security descriptor"); #pragma prefast(push) #pragma prefast(disable:25029) if (!::InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) #pragma prefast(pop) { ExitOnLastError(hr, "failed to initialize security descriptor"); } hr = AllocateAcl(pssp, &pACL); ExitOnFailure(hr, "Failed to allocate ACL for fileshare"); if (NULL == pACL) { WcaLog(LOGMSG_VERBOSE, "Ignoring NULL DACL."); } #pragma prefast(push) #pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs // add the ACL to the security descriptor. else if (!::SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) { ExitOnLastError(hr, "Failed to set security descriptor"); } #pragma prefast(pop) // all that is left is to create the share FillShareInfo(&si, pssp, pSD); // Fail if the directory doesn't exist if (!DirExists(pssp->wzDirectory, NULL)) ExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND), "Can't create a file share on directory that doesn't exist: %ls.", pssp->wzDirectory); WcaLog(LOGMSG_VERBOSE, "Creating file share on directory \'%ls\' named \'%ls\'.", pssp->wzDirectory, pssp->wzKey); if (!fShareExists) { s = ::NetShareAdd(NULL, 502, (BYTE*) &si, &dwParamErr); WcaLog(LOGMSG_VERBOSE, "Adding a new file share."); } else { // The share exists. Write our new permissions over the top. s = ::NetShareSetInfo(NULL, pssp->wzKey, 502, (BYTE*) &si, &dwParamErr); WcaLog(LOGMSG_VERBOSE, "Setting permissions on existing share."); } if (NERR_Success != s) { hr = E_FAIL; if (!fShareExists && NERR_DuplicateShare == s) WcaLog(LOGMSG_VERBOSE, "Duplicate error when existence check failed."); // error codes listed above. ExitOnFailure1(hr, "Failed to create/modify file share: Err: %d", s); } LExit: if (pACL) { ::LocalFree(pACL); } ReleaseMem(pSD); return hr; }
static HRESULT TrusteesInApplicationRolesRead( LPCWSTR pwzQuery, CPI_APPLICATION_ROLE_LIST* pAppRoleList, CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hView, hRec; CPI_USER_IN_APPLICATION_ROLE* pItm = NULL; LPWSTR pwzData = NULL; LPWSTR pwzDomain = NULL; LPWSTR pwzName = NULL; BOOL fMatchingArchitecture = FALSE; // loop through all application roles hr = WcaOpenExecuteView(pwzQuery, &hView); ExitOnFailure(hr, "Failed to execute view on table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { // get component hr = WcaGetRecordString(hRec, tiarqComponent, &pwzData); ExitOnFailure(hr, "Failed to get component"); // check if the component is our processor architecture hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); ExitOnFailure(hr, "Failed to get component architecture."); if (!fMatchingArchitecture) { continue; // not the same architecture, ignore } // create entry pItm = (CPI_USER_IN_APPLICATION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_USER_IN_APPLICATION_ROLE)); if (!pItm) ExitFunction1(hr = E_OUTOFMEMORY); // get component install state er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); // get key hr = WcaGetRecordString(hRec, tiarqUserInApplicationRole, &pwzData); ExitOnFailure(hr, "Failed to get key"); StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); // get application role hr = WcaGetRecordString(hRec, tiarqApplicationRole, &pwzData); ExitOnFailure(hr, "Failed to get application role"); hr = CpiApplicationRoleFindByKey(pAppRoleList, pwzData, &pItm->pApplicationRole); if (S_FALSE == hr) hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); ExitOnFailure1(hr, "Failed to find application role, key: %S", pwzData); // get user domain hr = WcaGetRecordFormattedString(hRec, tiarqDomain, &pwzDomain); ExitOnFailure(hr, "Failed to get domain"); // get user name hr = WcaGetRecordFormattedString(hRec, tiarqName, &pwzName); ExitOnFailure(hr, "Failed to get name"); // build account name hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount); ExitOnFailure(hr, "Failed to build account name"); // set references & increment counters if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) { CpiApplicationRoleAddReferenceInstall(pItm->pApplicationRole); pUsrInAppRoleList->iInstallCount++; } if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) { CpiApplicationRoleAddReferenceUninstall(pItm->pApplicationRole); pUsrInAppRoleList->iUninstallCount++; } // add entry if (pUsrInAppRoleList->pFirst) pItm->pNext = pUsrInAppRoleList->pFirst; pUsrInAppRoleList->pFirst = pItm; pItm = NULL; } if (E_NOMOREITEMS == hr) hr = S_OK; LExit: // clean up if (pItm) FreeUserInApplicationRole(pItm); ReleaseStr(pwzData); ReleaseStr(pwzDomain); ReleaseStr(pwzName); return hr; }
HRESULT CpiApplicationRolesVerifyInstall( CPI_APPLICATION_ROLE_LIST* pList ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; ICatalogObject* piRoleObj = NULL; for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) { // referenced locaters or roles that are being installed if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction))) continue; // if the role is referensed and is not a locater, it must be installed if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction)) MessageExitOnFailure1(hr = E_FAIL, msierrComPlusApplicationRoleDependency, "An application role is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey); // role is a locater if (!pItm->fHasComponent) { // get collection object for role hr = FindObjectForApplicationRole(pItm, &piRoleObj); ExitOnFailure(hr, "Failed to find collection object for role"); // if the role was not found if (S_FALSE == hr) MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusApplicationRoleNotFound, "An application role required by this installation was not found, key: %S", pItm->wzKey); } // role is supposed to be created else if (!CpiIsInstalled(pItm->isInstalled)) { do { // find roles with conflicting name or id hr = FindObjectForApplicationRole(pItm, NULL); ExitOnFailure(hr, "Failed to find collection object for role"); if (S_OK == hr) { er = WcaErrorMessage(msierrComPlusApplicationRoleConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); switch (er) { case IDABORT: ExitOnFailure1(hr = E_FAIL, "An application with a conflictiong name exists, key: %S", pItm->wzKey); break; case IDRETRY: break; case IDIGNORE: default: hr = S_FALSE; // indicate that this is not a conflict } } } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts } // clean up ReleaseNullObject(piRoleObj); } hr = S_OK; LExit: // clean up ReleaseObject(piRoleObj); return hr; }
HRESULT ScaWriteWebError(IMSAdminBase* piMetabase, int iParentType, LPCWSTR wzRoot, SCA_WEB_ERROR* psweList) { // AssertSz(0, "Debug ScaWriteWebError here"); Assert(*wzRoot && psweList); HRESULT hr = S_OK; DWORD cchData = 0; LPWSTR pwzSearchKey = NULL; LPWSTR pwz = NULL; LPWSTR pwzErrors = NULL; LPWSTR pwzCodeSubCode = NULL; LPWSTR pwzAcceptableCodeSubCode = NULL; LPCWSTR wzFoundCodeSubCode = NULL; DWORD_PTR dwFoundCodeSubCodeIndex = 0xFFFFFFFF; BOOL fOldValueFound = FALSE; LPWSTR pwzAcceptableErrors = NULL; LPWSTR pwzNewError = NULL; METADATA_RECORD mr; ::ZeroMemory(&mr, sizeof(mr)); ExitOnNull(piMetabase, hr, E_INVALIDARG, "Failed to write web error, because no metabase was provided"); ExitOnNull(wzRoot, hr, E_INVALIDARG, "Failed to write web error, because no root was provided"); // get the set of all valid custom errors from the metabase mr.dwMDIdentifier = MD_CUSTOM_ERROR_DESC; mr.dwMDAttributes = METADATA_INHERIT; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.dwMDDataLen = cchData = 0; mr.pbMDData = NULL; hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/Info", &mr); ExitOnFailure(hr, "Unable to get set of acceptable error codes for this server."); pwzAcceptableErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); // Check if web errors already exist here mr.dwMDIdentifier = MD_CUSTOM_ERROR; mr.dwMDAttributes = METADATA_INHERIT; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.dwMDDataLen = cchData = 0; mr.pbMDData = NULL; hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzRoot, &mr); if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) { // // If we don't have one already, find an appropriate one to start with // // we can walk up key by key and look for custom errors to inherit hr = StrAllocConcat(&pwzSearchKey, wzRoot, 0); ExitOnFailure1(hr, "Failed to copy root string: %ls", wzRoot); pwz = pwzSearchKey + lstrlenW(pwzSearchKey); while (NULL == pwzErrors) { // find the last slash while (*pwz != '/' && pwz != pwzSearchKey) pwz --; if (pwz == pwzSearchKey) break; *pwz = L'\0'; // Try here. If it's not found, keep walking up the path hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, pwzSearchKey, &mr); if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) hr = S_FALSE; ExitOnFailure1(hr, "failed to discover default error values to start with for web root: %ls while walking up the tree", wzRoot); if (S_OK == hr) { pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); break; } // Don't keep going if we're at the root if (0 == lstrcmpW(pwz + 1, L"W3SVC")) break; } } else { pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); } ExitOnFailure1(hr, "failed to discover default error values to start with for web root: %ls", wzRoot); // The above code should have come up with some value to start pwzErrors off with. Make sure it did. if (NULL == pwzErrors) { ExitOnFailure1(hr = E_UNEXPECTED, "failed to discover default error values to start with for web root: %ls", wzRoot); } // Loop through the web errors for (SCA_WEB_ERROR* pswe = psweList; pswe; pswe = pswe->psweNext) { // Assume that we will have to replace fOldValueFound = TRUE; // If the subcode is 0, that means "*" in MD_CUSTOM_ERROR (thus the special formatting logic) if (0 == pswe->iSubCode) { hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,*", pswe->iErrorCode); ExitOnFailure(hr, "failed to create error code string while installing web error"); } else { hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); } hr = MultiSzFindSubstring(pwzErrors, pwzCodeSubCode, &dwFoundCodeSubCodeIndex, &wzFoundCodeSubCode); ExitOnFailure1(hr, "failed to find existing error code,subcode: %ls", pwzCodeSubCode); // If we didn't find this error code/sub code pair in the list already, make sure it's acceptable to add if (S_FALSE == hr) { // // Make sure this error code/sub code pair is in the "acceptable" list // // If the subcode is 0, that means "0" in MD_CUSTOM_ERROR_DESC (no special formatting logic needed) hr = StrAllocFormatted(&pwzAcceptableCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); // We don't care where it is, just whether it's there or not hr = MultiSzFindSubstring(pwzAcceptableErrors, pwzAcceptableCodeSubCode, NULL, NULL); ExitOnFailure1(hr, "failed to find whether or not error code, subcode: %ls is supported", pwzCodeSubCode); if (S_FALSE == hr) { WcaLog(LOGMSG_VERBOSE, "Skipping error code, subcode: %ls because it is not supported by the server.", pwzCodeSubCode); continue; } // If we didn't find it (and its an acceptable error) then we have nothing to replace fOldValueFound = FALSE; } // Set up the new error string if needed if (*(pswe->wzFile)) { hr = StrAllocFormatted(&pwzNewError, L"%s,FILE,%s", pwzCodeSubCode, pswe->wzFile); ExitOnFailure2(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); } else if (*(pswe->wzURL)) { hr = StrAllocFormatted(&pwzNewError, L"%s,URL,%s", pwzCodeSubCode, pswe->wzURL); ExitOnFailure2(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); } else if (fOldValueFound) { // If no File or URL was specified, they want a default error so remove the old value from the MULTISZ and move on hr = MultiSzRemoveString(&pwzErrors, dwFoundCodeSubCodeIndex); ExitOnFailure1(hr, "failed to remove string for error code sub code: %ls in order to make it 'default'", pwzCodeSubCode); continue; } // If we have something to replace, replace it, otherwise, put it at the beginning (order shouldn't matter) if (fOldValueFound) { hr = MultiSzReplaceString(&pwzErrors, dwFoundCodeSubCodeIndex, pwzNewError); ExitOnFailure1(hr, "failed to replace old error string with new error string for error code,subcode: %ls", pwzCodeSubCode); } else { hr = MultiSzPrepend(&pwzErrors, NULL, pwzNewError); ExitOnFailure1(hr, "failed to prepend new error string for error code,subcode: %ls", pwzCodeSubCode); } } // now write the CustomErrors to the metabase if (weptWeb == iParentType) { hr = ScaWriteMetabaseValue(piMetabase, wzRoot, L"/Root", MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); ExitOnFailure(hr, "Failed to write Web Error to /Root"); } else { hr = ScaWriteMetabaseValue(piMetabase, wzRoot, NULL, MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); ExitOnFailure(hr, "Failed to write Web Error"); } LExit: ReleaseStr(pwzErrors); ReleaseStr(pwzSearchKey); ReleaseStr(pwzCodeSubCode); ReleaseStr(pwzAcceptableCodeSubCode); ReleaseStr(pwzAcceptableErrors); return hr; }
static HRESULT SendRequest( __in BURN_USER_EXPERIENCE* pUX, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in HINTERNET hUrl, __inout_z LPWSTR* psczUrl, __out BOOL* pfRetry, __out BOOL* pfRangesAccepted ) { HRESULT hr = S_OK; BOOL fRetrySend = FALSE; LONG lCode = 0; do { fRetrySend = FALSE; if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it. LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl); // Try to get the HTTP status code and, if good, handle via the switch statement below but if it // fails return the error code from the send request above as the result of the function. HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); ExitOnFailure1(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); } else // get the http status code. { hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); ExitOnFailure1(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); } switch (lCode) { case 200: // OK but range requests don't work. *pfRangesAccepted = FALSE; hr = S_OK; break; case 206: // Partial content means that range requests work! *pfRangesAccepted = TRUE; hr = S_OK; break; // redirection cases case 301: __fallthrough; // file moved case 302: __fallthrough; // temporary case 303: // redirect method hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); ExitOnFailure1(hr, "Failed to get redirect url: %ls", *psczUrl); *pfRetry = TRUE; break; // error cases case 400: // bad request hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); break; case 401: __fallthrough; // unauthorized case 407: __fallthrough; // proxy unauthorized hr = AuthenticationRequired(pUX, wzPackageOrContainerId, wzPayloadId, hUrl, lCode, &fRetrySend, pfRetry); break; case 403: // forbidden hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); break; case 404: // file not found case 410: // gone hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); break; case 405: // method not allowed hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); break; case 408: __fallthrough; // request timedout case 504: // gateway timeout hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); break; case 414: // request URI too long hr = CO_E_PATHTOOLONG; break; case 502: __fallthrough; // server (through a gateway) was not found case 503: // server unavailable hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); break; case 418: // I'm a teapot. default: // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway) // do not overwrite the error code from the failed request. Otherwise, the error was unexpected. if (SUCCEEDED(hr)) { hr = E_UNEXPECTED; } LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl); break; } } while (fRetrySend); LExit: return hr; }
/****************************************************************** CreateFwRuleObject - CoCreate a firewall rule, and set the common set of properties which are shared between port and application firewall rules ********************************************************************/ static HRESULT CreateFwRuleObject( __in BSTR bstrName, __in int iProfile, __in_opt LPCWSTR wzRemoteAddresses, __in LPCWSTR wzPort, __in int iProtocol, __in LPCWSTR wzDescription, __out INetFwRule** ppNetFwRule ) { HRESULT hr = S_OK; BSTR bstrRemoteAddresses = NULL; BSTR bstrPort = NULL; BSTR bstrDescription = NULL; INetFwRule* pNetFwRule = NULL; *ppNetFwRule = NULL; // convert to BSTRs to make COM happy bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses); ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses"); bstrPort = ::SysAllocString(wzPort); ExitOnNull(bstrPort, hr, E_OUTOFMEMORY, "failed SysAllocString for port"); bstrDescription = ::SysAllocString(wzDescription); ExitOnNull(bstrDescription, hr, E_OUTOFMEMORY, "failed SysAllocString for description"); hr = ::CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_ALL, __uuidof(INetFwRule), (void**)&pNetFwRule); ExitOnFailure(hr, "failed to create NetFwRule object"); hr = pNetFwRule->put_Name(bstrName); ExitOnFailure(hr, "failed to set exception name"); hr = pNetFwRule->put_Profiles(static_cast<NET_FW_PROFILE_TYPE2>(iProfile)); ExitOnFailure(hr, "failed to set exception profile"); if (MSI_NULL_INTEGER != iProtocol) { hr = pNetFwRule->put_Protocol(static_cast<NET_FW_IP_PROTOCOL>(iProtocol)); ExitOnFailure(hr, "failed to set exception protocol"); } if (bstrPort && *bstrPort) { hr = pNetFwRule->put_LocalPorts(bstrPort); ExitOnFailure(hr, "failed to set exception port"); } if (bstrRemoteAddresses && *bstrRemoteAddresses) { hr = pNetFwRule->put_RemoteAddresses(bstrRemoteAddresses); ExitOnFailure1(hr, "failed to set exception remote addresses '%ls'", bstrRemoteAddresses); } if (bstrDescription && *bstrDescription) { hr = pNetFwRule->put_Description(bstrDescription); ExitOnFailure1(hr, "failed to set exception description '%ls'", bstrDescription); } *ppNetFwRule = pNetFwRule; pNetFwRule = NULL; LExit: ReleaseBSTR(bstrRemoteAddresses); ReleaseBSTR(bstrPort); ReleaseBSTR(bstrDescription); ReleaseObject(pNetFwRule); return hr; }
/****************************************************************** CaExecServiceConfig - entry point for ServiceConfig Custom Action called as Type 1025 CustomAction (deferred binary DLL) NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t... *******************************************************************/ extern "C" UINT __stdcall ExecServiceConfig( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecServiceConfig"); HRESULT hr = S_OK; UINT uiResult = ERROR_SUCCESS; DWORD dwError = 0; LPVOID lpMsgBuf = NULL; LPWSTR pwzData = NULL; LPWSTR pwz = NULL; LPWSTR pwzServiceName = NULL; LPWSTR pwzFirstFailureActionType = NULL; LPWSTR pwzSecondFailureActionType = NULL; LPWSTR pwzThirdFailureActionType = NULL; LPWSTR pwzProgramCommandLine = NULL; LPWSTR pwzRebootMessage = NULL; DWORD dwResetPeriodInDays = 0; DWORD dwRestartServiceDelayInSeconds = 0; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; DWORD dwOpenServiceAccess = SERVICE_CHANGE_CONFIG; // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2() SERVICE_FAILURE_ACTIONSW sfa; SC_ACTION actions[3]; BOOL fResult = FALSE; // initialize hr = WcaInitialize(hInstall, "ExecServiceConfig"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty( L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %S", pwzData); pwz = pwzData; // loop through all the passed in data while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzServiceName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage); ExitOnFailure(hr, "failed to process CustomActionData"); WcaLog(LOGMSG_VERBOSE, "Configuring Service: %S", pwzServiceName); // build up SC_ACTION array // TODO: why is delay only respected when SC_ACTION_RESTART is requested? actions[0].Type = GetSCActionType(pwzFirstFailureActionType); actions[0].Delay = 0; if (SC_ACTION_RESTART == actions[0].Type) { actions[0].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds dwOpenServiceAccess |= SERVICE_START; // must have SERVICE_START access in order to handle SC_ACTION_RESTART action; } actions[1].Type = GetSCActionType(pwzSecondFailureActionType); actions[1].Delay = 0; if (SC_ACTION_RESTART == actions[1].Type) { actions[1].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds dwOpenServiceAccess |= SERVICE_START; // must have SERVICE_START access in order to handle SC_ACTION_RESTART action; } actions[2].Type = GetSCActionType(pwzThirdFailureActionType); actions[2].Delay = 0; if (SC_ACTION_RESTART == actions[2].Type) { actions[2].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds dwOpenServiceAccess |= SERVICE_START; // must have SERVICE_START access in order to handle SC_ACTION_RESTART action; } // build up the SERVICE_FAILURE_ACTIONSW struct sfa.dwResetPeriod = dwResetPeriodInDays * 24 * 60 * 60; // days to seconds sfa.lpRebootMsg = pwzRebootMessage; sfa.lpCommand = pwzProgramCommandLine; sfa.cActions = 3; // the UI always shows 3 actions, so we'll always do 3 sfa.lpsaActions = actions; // Get a handle to the service control manager (if we don't already have) if (NULL == hSCM) { hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (hSCM == NULL) { dwError = ::GetLastError(); ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); ExitOnFailure1(hr = HRESULT_FROM_WIN32(dwError), "failed to get handle to SCM. Error: %S", (LPWSTR)lpMsgBuf); } } hService = ::OpenServiceW(hSCM, pwzServiceName, dwOpenServiceAccess); if (hService == NULL) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); if (dwError == ERROR_SERVICE_DOES_NOT_EXIST) { ExitOnFailure1(hr, "Service \"%S\" does not exist on this system.", pwzServiceName); } else { ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); ExitOnFailure2(hr, "Failed to get handle to the service \"%S\". Error: %S", pwzServiceName, (LPWSTR)lpMsgBuf); } } // Call ChangeServiceConfig2 to actually set up the failure actions fResult = ChangeServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPVOID)&sfa); if (fResult == FALSE) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); // check if this is a service that can't be modified if(dwError == ERROR_CANNOT_DETECT_PROCESS_ABORT) { WcaLog(LOGMSG_STANDARD, "WARNING: Service \"%S\" is not configurable on this server and will not be set.", pwzServiceName); } ExitOnFailure1(hr, "Cannot change service configuration. Error: %S", (LPWSTR)lpMsgBuf); } // Per-service cleanup dwResetPeriodInDays = 0; dwRestartServiceDelayInSeconds = 0; hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE); ExitOnFailure(hr, "failed to send progress message"); } LExit: // Clean up handles ReleaseStr(pwzServiceName); ReleaseStr(pwzFirstFailureActionType); ReleaseStr(pwzSecondFailureActionType); ReleaseStr(pwzThirdFailureActionType); ReleaseStr(pwzProgramCommandLine); ReleaseStr(pwzRebootMessage); ReleaseStr(pwzData); if (lpMsgBuf) // Allocated with FormatString ::LocalFree(lpMsgBuf); if (hService) ::CloseServiceHandle(hService); if (hSCM) ::CloseServiceHandle(hSCM); if (FAILED(hr)) uiResult = ERROR_INSTALL_FAILURE; return WcaFinalize(uiResult); }
/****************************************************************** AddPortExceptionOnCurrentProfile ********************************************************************/ static HRESULT AddPortExceptionOnCurrentProfile( __in LPCWSTR wzName, __in_opt LPCWSTR wzRemoteAddresses, __in BOOL fIgnoreFailures, __in int iPort, __in int iProtocol ) { HRESULT hr = S_OK; BSTR bstrName = NULL; BSTR bstrRemoteAddresses = NULL; INetFwProfile* pfwProfile = NULL; INetFwOpenPorts* pfwPorts = NULL; INetFwOpenPort* pfwPort = NULL; // convert to BSTRs to make COM happy bstrName = ::SysAllocString(wzName); ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name"); bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses); ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses"); // create and initialize a new open port object hr = ::CoCreateInstance(__uuidof(NetFwOpenPort), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwOpenPort), reinterpret_cast<void**>(&pfwPort)); ExitOnFailure(hr, "failed to create new open port"); hr = pfwPort->put_Port(iPort); ExitOnFailure(hr, "failed to set exception port"); hr = pfwPort->put_Protocol(static_cast<NET_FW_IP_PROTOCOL>(iProtocol)); ExitOnFailure(hr, "failed to set exception protocol"); if (bstrRemoteAddresses && *bstrRemoteAddresses) { hr = pfwPort->put_RemoteAddresses(bstrRemoteAddresses); ExitOnFailure1(hr, "failed to set exception remote addresses '%ls'", bstrRemoteAddresses); } hr = pfwPort->put_Name(bstrName); ExitOnFailure(hr, "failed to set exception name"); // get the firewall profile, its current list of open ports, and add ours hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile); ExitOnFailure(hr, "failed to get firewall profile"); if (S_FALSE == hr) // user or package author chose to ignore missing firewall { ExitFunction(); } hr = pfwProfile->get_GloballyOpenPorts(&pfwPorts); ExitOnFailure(hr, "failed to get open ports"); hr = pfwPorts->Add(pfwPort); ExitOnFailure(hr, "failed to add exception to global list"); LExit: ReleaseBSTR(bstrRemoteAddresses); ReleaseBSTR(bstrName); ReleaseObject(pfwProfile); ReleaseObject(pfwPorts); ReleaseObject(pfwPort); return fIgnoreFailures ? S_OK : hr; }
UINT __stdcall ExecAddinRegistration(MSIHANDLE hInstall) { // AssertSz(FALSE, "debug ExecAddinRegistration"); LPWSTR pwzCustomActionData = NULL; LPWSTR pwzData = NULL; LPWSTR pwz = NULL; int iOperation = 0; LPWSTR pwzId = NULL; LPWSTR pwzFile = NULL; LPWSTR pwzName = NULL; LPWSTR pwzDescription = NULL; int iBitness = REG_KEY_DEFAULT; int iCommandLineSafe = 1; int iLoadBehavior = 3; LPWSTR pwzAllUsers = NULL; HRESULT hr = WcaInitialize(hInstall, "ExecAddinRegistration"); ExitOnFailure(hr, "Failed to initialize"); hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = RegInitialize(); ExitOnFailure(hr, "Failed to initialize the registry functions."); // loop through all the passed in data while (pwz && *pwz) { // extract the custom action data hr = WcaReadIntegerFromCaData(&pwz, &iOperation); ExitOnFailure(hr, "failed to read operation from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzId); ExitOnFailure(hr, "failed to read id from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzFile); ExitOnFailure(hr, "failed to read path from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzName); ExitOnFailure(hr, "failed to read name from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzDescription); ExitOnFailure(hr, "failed to read description from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iBitness); ExitOnFailure(hr, "failed to read bitness from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iCommandLineSafe); ExitOnFailure(hr, "failed to read CommandLineSafe from custom action data"); hr = WcaReadIntegerFromCaData(&pwz, &iLoadBehavior); ExitOnFailure(hr, "failed to read LoadBehavior from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzAllUsers); ExitOnFailure(hr, "failed to read ALLUSERS from custom action data"); BOOL fPerUserInstall = (!pwzAllUsers || !*pwzAllUsers); // if rolling back, swap INSTALL and UNINSTALL if (::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)) { if (WCA_TODO_INSTALL == iOperation) { iOperation = WCA_TODO_UNINSTALL; } else if (WCA_TODO_UNINSTALL == iOperation) { iOperation = WCA_TODO_INSTALL; } } switch (iOperation) { case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: hr = CreateOfficeRegistryKey(pwzId, pwzFile, pwzName, pwzDescription, iCommandLineSafe, iLoadBehavior, fPerUserInstall, iBitness); ExitOnFailure1(hr, "failed to register addin %ls", pwzId); break; case WCA_TODO_UNINSTALL: hr = DeleteOfficeRegistryKey(pwzId, fPerUserInstall, iBitness); ExitOnFailure1(hr, "failed to unregister addin %ls", pwzId); break; } // Tick the progress bar along for this addin hr = WcaProgressMessage(COST_REGISTER_ADDIN, FALSE); ExitOnFailure1(hr, "failed to tick progress bar for addin registration: %ls", pwzId); } LExit: RegUninitialize(); ReleaseStr(pwzAllUsers); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzData); ReleaseStr(pwzId); ReleaseStr(pwzFile); ReleaseStr(pwzName); ReleaseStr(pwzDescription); return WcaFinalize(SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE); }
/****************************************************************** CaExecSecureObjects - entry point for SecureObjects Custom Action called as Type 1025 CustomAction (deferred binary DLL) NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwPermissions\twzObject\t... ******************************************************************/ extern "C" UINT __stdcall ExecSecureObjects( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecSecureObjects"); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPWSTR pwz = NULL; LPWSTR pwzData = NULL; LPWSTR pwzObject = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzDomain = NULL; DWORD dwRevision = 0; LPWSTR pwzUser = NULL; DWORD dwPermissions = 0; LPWSTR pwzAccount = NULL; PSID psid = NULL; EXPLICIT_ACCESSW ea = {0}; SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; PSECURITY_DESCRIPTOR psd = NULL; SECURITY_DESCRIPTOR_CONTROL sdc = {0}; SECURITY_INFORMATION si = {0}; PACL pAclExisting = NULL; // doesn't get freed PACL pAclNew = NULL; PMSIHANDLE hActionRec = ::MsiCreateRecord(1); // // initialize // hr = WcaInitialize(hInstall, "ExecSecureObjects"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %S", pwzData); pwz = pwzData; // // loop through all the passed in data // while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzObject); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzTable); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzDomain); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzUser); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions)); ExitOnFailure(hr, "failed to processCustomActionData"); WcaLog(LOGMSG_VERBOSE, "Securing Object: %S Type: %S User: %S", pwzObject, pwzTable, pwzUser); // // create the appropriate SID // // figure out the right user to put into the access block if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone")) { hr = AclGetWellKnownSid(WinWorldSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators")) { hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem")) { hr = AclGetWellKnownSid(WinLocalSystemSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService")) { hr = AclGetWellKnownSid(WinLocalServiceSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService")) { hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser")) { hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests")) { hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid); } else if(!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER")) { hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid); } else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE")) { hr = AclGetWellKnownSid(WinInteractiveSid, &psid); } else if(!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users")) { hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid); } else { hr = StrAllocFormatted(&pwzAccount, L"%s\\%s", *pwzDomain ? pwzDomain : L".", pwzUser); ExitOnFailure(hr, "failed to build domain user name"); hr = AclGetAccountSid(NULL, pwzAccount, &psid); } ExitOnFailure3(hr, "failed to get sid for account: %S%S%S", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser); // // build up the explicit access // ea.grfAccessPermissions = dwPermissions; ea.grfAccessMode = SET_ACCESS; if (0 == lstrcmpW(L"CreateFolder", pwzTable)) { ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; } else { ea.grfInheritance = NO_INHERITANCE; } ::BuildTrusteeWithSidW(&ea.Trustee, psid); if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) { objectType = SE_SERVICE; // always add these permissions for services // these are basic permissions that are often forgotten dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE; } else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable)) { objectType = SE_FILE_OBJECT; } else if (0 == lstrcmpW(L"Registry", pwzTable)) { objectType = SE_REGISTRY_KEY; } if (SE_UNKNOWN_OBJECT_TYPE != objectType) { er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %S", pwzObject); //Need to see if DACL is protected so getting Descriptor information if(!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) { ExitOnLastError1(hr, "failed to get security descriptor control for object: %S", pwzObject); } er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %S", pwzObject); if (sdc & SE_DACL_PROTECTED) { si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; } else { si = DACL_SECURITY_INFORMATION; } er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL); MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %S", pwzObject); } else { MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %S", pwzTable); } hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE); ExitOnFailure(hr, "failed to send progress message"); objectType = SE_UNKNOWN_OBJECT_TYPE; } LExit: ReleaseStr(pwzUser); ReleaseStr(pwzDomain); ReleaseStr(pwzTable); ReleaseStr(pwzObject); ReleaseStr(pwzData); ReleaseStr(pwzAccount); if (pAclNew) ::LocalFree(pAclNew); if (psd) ::LocalFree(psd); if (psid) AclFreeSid(psid); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
HRESULT __stdcall ScaVirtualDirsRead7( __in SCA_WEB7* pswList, __in SCA_VDIR7** ppsvdList, __in SCA_MIMEMAP** ppsmmList, __in SCA_HTTP_HEADER** ppshhList, __in SCA_WEB_ERROR** ppsweList, __in WCA_WRAPQUERY_HANDLE hUserQuery, __in WCA_WRAPQUERY_HANDLE /*hWebBaseQuery*/, __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, __in WCA_WRAPQUERY_HANDLE hWebAppQuery, __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, __inout LPWSTR *ppwzCustomActionData ) { Assert(ppsvdList); HRESULT hr = S_OK; MSIHANDLE hRec; SCA_VDIR7* pvdir = NULL; LPWSTR pwzData = NULL; WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); if (0 == WcaGetQueryRecords(hWrapQuery)) { WcaLog(LOGMSG_VERBOSE, "Skipping ScaVirtualDirsRead() because IIsWebVirtualDir table not present"); ExitFunction1(hr = S_FALSE); } // loop through all the vdirs while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) { // Add this record's information into the list of things to process. hr = AddVirtualDirToList7(ppsvdList); ExitOnFailure(hr, "failed to add vdir to vdir list"); pvdir = *ppsvdList; // get the darwin information hr = WcaGetRecordString(hRec, vdqComponent, &pwzData); ExitOnFailure(hr, "failed to get IIsWebVirtualDir.Component"); hr = WcaGetRecordInteger(hRec, vdqInstalled, (int *)&pvdir->isInstalled); ExitOnFailure(hr, "Failed to get Component installed state for virtual dir"); hr = WcaGetRecordInteger(hRec, vdqAction, (int *)&pvdir->isAction); ExitOnFailure(hr, "Failed to get Component action state for virtual dir"); // get vdir properties hr = ::StringCchCopyW(pvdir->wzComponent, countof(pvdir->wzComponent), pwzData); ExitOnFailure1(hr, "failed to copy vdir component name: %ls", pwzData); hr = WcaGetRecordString(hRec, vdqWeb, &pwzData); ExitOnFailure(hr, "Failed to get Web for VirtualDir"); hr = ScaWebsGetBase7(pswList, pwzData, pvdir->wzWebName , countof(pvdir->wzWebName)); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); ExitOnFailure(hr, "Failed to get Web Base for VirtualDir"); } if (WcaIsUninstalling(pvdir->isInstalled, pvdir->isAction)) { // If we're uninstalling, ignore any failure to find the existing web hr = S_OK; } hr = WcaGetRecordString(hRec, vdqAlias, &pwzData); ExitOnFailure(hr, "Failed to get Alias for VirtualDir"); hr = ::StringCchCopyW(pvdir->wzVDirRoot, countof(pvdir->wzVDirRoot), pwzData); ExitOnFailure(hr, "Failed to set VDirRoot for VirtualDir"); // get the vdir's directory hr = WcaGetRecordString(hRec, vdqDirectory, &pwzData); ExitOnFailure(hr, "Failed to get Directory for VirtualDir"); // get the web's directory if (INSTALLSTATE_SOURCE == pvdir->isAction) { hr = WcaGetRecordString(hRec, vdqSourcePath, &pwzData); } else { hr = WcaGetRecordString(hRec, vdqTargetPath, &pwzData); } ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory"); // remove trailing backslash(es) while (lstrlenW(pwzData) > 0 && pwzData[lstrlenW(pwzData)-1] == L'\\') { pwzData[lstrlenW(pwzData)-1] = 0; } hr = ::StringCchCopyW(pvdir->wzDirectory, countof(pvdir->wzDirectory), pwzData); ExitOnFailure(hr, "Failed to copy directory string to vdir object"); // get the security information for this web hr = WcaGetRecordString(hRec, vdqProperties, &pwzData); ExitOnFailure(hr, "Failed to get web directory identifier for VirtualDir"); if (*pwzData) { hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &pvdir->swp); ExitOnFailure(hr, "Failed to get web directory for VirtualDir"); pvdir->fHasProperties = TRUE; } // get the application information for this web hr = WcaGetRecordString(hRec, vdqApplication, &pwzData); ExitOnFailure(hr, "Failed to get application identifier for VirtualDir"); if (*pwzData) { hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &pvdir->swapp); ExitOnFailure(hr, "Failed to get application for VirtualDir"); pvdir->fHasApplication = TRUE; } hr = WcaGetRecordString(hRec, vdqVDir, &pwzData); ExitOnFailure(hr, "Failed to get VDir for VirtualDir"); if (*pwzData && *ppsmmList) { hr = ScaGetMimeMap(mmptVDir, pwzData, ppsmmList, &pvdir->psmm); ExitOnFailure(hr, "Failed to get mimemap for VirtualDir"); } if (*pwzData && *ppshhList) { hr = ScaGetHttpHeader(hhptVDir, pwzData, ppshhList, &pvdir->pshh); ExitOnFailure1(hr, "Failed to get custom HTTP headers for VirtualDir: %ls", pwzData); } if (*pwzData && *ppsweList) { hr = ScaGetWebError(weptVDir, pwzData, ppsweList, &pvdir->pswe); ExitOnFailure1(hr, "Failed to get custom web errors for VirtualDir: %ls", pwzData); } } if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure while processing VirtualDirs"); LExit: WcaFinishUnwrapQuery(hWrapQuery); ReleaseStr(pwzData); return hr; }
/****************************************************************** CaSchedSecureObjects - entry point for CaReadSecureObjects Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaSecureObjects ******************************************************************/ extern "C" UINT __stdcall SchedSecureObjects( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedSecureObjects"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzData = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzTargetPath = NULL; LPWSTR pwzFormattedString = NULL; int iRoot = 0; int iAllUsers = 0; LPWSTR pwzKey = NULL; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; MSIHANDLE hViewObject = NULL; // Don't free this since it's always a copy of either hViewService or hViewCreateFolder PMSIHANDLE hViewService = NULL; PMSIHANDLE hViewCreateFolder = NULL; PMSIHANDLE hViewFile = NULL; PMSIHANDLE hViewRegistry = NULL; PMSIHANDLE hRecObject = NULL; INSTALLSTATE isInstalled; INSTALLSTATE isAction; LPWSTR pwzCustomActionData = NULL; DWORD cchCustomActionData = 0; DWORD cObjects = 0; eOBJECTTYPE eType = OT_UNKNOWN; // // initialize // hr = WcaInitialize(hInstall, "SchedSecureObjects"); ExitOnFailure(hr, "failed to initialize"); // // loop through all the objects to be secured // hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView); ExitOnFailure(hr, "failed to open view on SecureObjects table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hViewObject = NULL; eType = OT_UNKNOWN; hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable); ExitOnFailure(hr, "failed to get object table"); // ensure we're looking at a known table if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) { eType = OT_SERVICE; } else if (0 == lstrcmpW(L"CreateFolder", pwzTable)) { eType = OT_FOLDER; } else if (0 == lstrcmpW(L"File", pwzTable)) { eType = OT_FILE; } else if (0 == lstrcmpW(L"Registry", pwzTable)) { eType = OT_REGISTRY; } else { ExitOnFailure1(hr = E_INVALIDARG, "unknown SecureObject.Table: %S", pwzTable); } // if we haven't opened a view on the ServiceInstall/CreateFolder table, do that now if (OT_SERVICE == eType) { if (!hViewService) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_SERVICECOMPONENT, &hViewService); ExitOnFailure(hr, "failed to open view on ServiceInstall table"); } hViewObject = hViewService; } else if (OT_FOLDER == eType) { if (!hViewCreateFolder) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_CREATEFOLDERCOMPONENT, &hViewCreateFolder); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewCreateFolder; } else if (OT_FILE== eType) { if (!hViewFile) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_FILECOMPONENT, &hViewFile); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewFile; } else if (OT_REGISTRY== eType) { if (!hViewRegistry) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_REGISTRYCOMPONENT, &hViewRegistry); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewRegistry; } Assert(hViewObject); // execute a view looking for the object's Component_ hr = WcaExecuteView(hViewObject, hRec); ExitOnFailure1(hr, "failed to execute view on %S table", pwzData); hr = WcaFetchSingleRecord(hViewObject, &hRecObject); ExitOnFailure(hr, "failed to fetch Component for secure object"); hr = WcaGetRecordString(hRecObject, QSOC_COMPONENT, &pwzData); ExitOnFailure(hr, "failed to get Component name for secure object"); // // if we are installing this Component // er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %S", pwzData); if (WcaIsInstalling(isInstalled, isAction)) { // add the data to the CustomActionData hr = WcaGetRecordString(hRecObject, QSOC_OBJECTNAME, &pwzData); ExitOnFailure(hr, "failed to get name of object"); if (OT_SERVICE == eType) { hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_FOLDER == eType) { hr = WcaGetTargetPath(pwzData, &pwzTargetPath); ExitOnFailure1(hr, "failed to get target path for directory id: %S", pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_FILE == eType) { hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzData); ExitOnFailure1(hr, "failed to create formatted string for securing file object: %S", pwzData); hr = WcaGetFormattedString(pwzFormattedString, &pwzTargetPath); ExitOnFailure2(hr, "failed to get file path from formatted string: %S for secure object: %S", pwzFormattedString, pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_REGISTRY == eType) { hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot); ExitOnFailure1(hr, "Failed to get reg key root for secure object: %S", pwzData); hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey); ExitOnFailure1(hr, "Failed to get reg key for secure object: %S", pwzData); // Decode the root value if (-1 == iRoot) { // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers); ExitOnFailure(hr, "failed to get value of ALLUSERS property"); if (1 == iAllUsers) { hr = StrAllocString(&pwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else { hr = StrAllocString(&pwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } } else if (/*msidbRegistryRootClassesRoot*/ 0 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"CLASSES_ROOT\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCR root"); } else if (/*msidbRegistryRootCurrentUser*/ 1 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } else if (/*msidbRegistryRootLocalMachine*/ 2 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else if (/*msidbRegistryRootUsers*/ 3 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"USERS\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKU root"); } else { ExitOnFailure2(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%S' root: %d", pwzData, iRoot); } hr = StrAllocConcat(&pwzTargetPath, pwzKey, 0); ExitOnFailure2(hr, "Failed to concat key: %S for secure object: %S", pwzKey, pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else { AssertSz(FALSE, "How did you get here?"); } hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData); ExitOnFailure(hr, "failed to get domain for user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData); ExitOnFailure(hr, "failed to get user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData); ExitOnFailure(hr, "failed to get domain for user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); cObjects++; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all objects to secure"); // // schedule the custom action and add to progress bar // if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cObjects); hr = WcaDoDeferredAction(L"ExecSecureObjects", pwzCustomActionData, cObjects * COST_SECUREOBJECT); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzData); ReleaseStr(pwzTable); ReleaseStr(pwzTargetPath); ReleaseStr(pwzFormattedString); ReleaseStr(pwzKey); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
extern "C" HRESULT ExeEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WCHAR wzCurrentDirectory[MAX_PATH] = { }; BOOL fChangedCurrentDirectory = FALSE; int nResult = IDNOACTION; LPCWSTR wzArguments = NULL; LPWSTR sczArgumentsFormatted = NULL; LPWSTR sczArgumentsObfuscated = NULL; LPWSTR sczCachedDirectory = NULL; LPWSTR sczExecutablePath = NULL; LPWSTR sczCommand = NULL; LPWSTR sczCommandObfuscated = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; // get cached executable path hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); ExitOnFailure(hr, "Failed to build executable path."); // pick arguments switch (pExecuteAction->exePackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for executable package."); } // build command if (wzArguments && *wzArguments) { hr = VariableFormatString(pVariables, wzArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); ExitOnFailure(hr, "Failed to create executable command."); hr = VariableFormatStringObfuscated(pVariables, wzArguments, &sczArgumentsObfuscated, NULL); ExitOnFailure(hr, "Failed to format obfuscated argument string."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); } else { hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); ExitOnFailure(hr, "Failed to create executable command."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); } ExitOnFailure(hr, "Failed to create obfuscated executable command."); if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { // Add the list of dependencies to ignore, if any, to the burn command line. if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); } // Add the list of ancestors, if any, to the burn command line. if (pExecuteAction->exePackage.sczAncestors) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); } } // Log before we add the secret pipe name and client token for embedded processes. LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); } else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); } else // create and wait for the executable process while sending fake progress to allow cancel. { // Make the cache location of the executable the current directory to help those executables // that expect stuff to be relative to them. if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) { fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); } si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); } if (pExecuteAction->exePackage.fFireAndForget) { ::WaitForInputIdle(pi.hProcess, 5000); ExitFunction(); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); } hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); ExitOnRootFailure1(hr, "Process returned error: 0x%x", dwExitCode); LExit: if (fChangedCurrentDirectory) { ::SetCurrentDirectoryW(wzCurrentDirectory); } StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); ReleaseStr(sczCachedDirectory); ReleaseStr(sczExecutablePath); StrSecureZeroFreeString(sczCommand); ReleaseStr(sczCommandObfuscated); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); // Best effort to clear the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); return hr; }
extern "C" HRESULT WininetDownloadUrl( __in BURN_USER_EXPERIENCE* pUX, __in BURN_CACHE_CALLBACK* pCallback, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in BURN_DOWNLOAD_SOURCE* pDownloadSource, __in DWORD64 dw64AuthoredDownloadSize, __in LPCWSTR wzDestinationPath ) { HRESULT hr = S_OK; LPWSTR sczUrl = NULL; HINTERNET hSession = NULL; DWORD dwTimeout = 0; LPWSTR sczResumePath = NULL; HANDLE hResumeFile = INVALID_HANDLE_VALUE; DWORD64 dw64ResumeOffset = 0; DWORD64 dw64Size = 0; FILETIME ftCreated = { }; // Copy the download source into a working variable to handle redirects then // open the internet session. hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); ExitOnFailure(hr, "Failed to copy download source URL."); hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); ExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. PolcReadNumber(BURN_POLICY_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); if (0 < dwTimeout) { dwTimeout *= 1000; // convert to milliseconds. ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); } // Get the resource size and creation time from the internet. hr = GetResourceMetadata(pUX, wzPackageOrContainerId, wzPayloadId, hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, &dw64Size, &ftCreated); ExitOnFailure1(hr, "Failed to get size and time for URL: %ls", sczUrl); // Ignore failure to initialize resume because we will fall back to full download then // download. InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); hr = DownloadResource(pUX, wzPackageOrContainerId, wzPayloadId, hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCallback); ExitOnFailure1(hr, "Failed to download URL: %ls", sczUrl); // Cleanup the resume file because we successfully downloaded the whole file. if (sczResumePath && *sczResumePath) { ::DeleteFileW(sczResumePath); } LExit: ReleaseFileHandle(hResumeFile); ReleaseStr(sczResumePath); ReleaseInternet(hSession); ReleaseStr(sczUrl); return hr; }
HRESULT CpiConfigurePartitions( LPWSTR* ppwzData, HANDLE hRollbackFile ) { HRESULT hr = S_OK; CPI_PARTITION_ATTRIBUTES attrs; ::ZeroMemory(&attrs, sizeof(attrs)); // read action text hr = CpiActionStartMessage(ppwzData, FALSE); ExitOnFailure(hr, "Failed to send action start message"); // ger partition count int iCnt = 0; hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); ExitOnFailure(hr, "Failed to read count"); // write count to rollback file hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); ExitOnFailure(hr, "Failed to write count to rollback file"); for (int i = 0; i < iCnt; i++) { // read partition attributes from CustomActionData hr = ReadPartitionAttributes(ppwzData, &attrs); ExitOnFailure(hr, "Failed to read attributes"); // progress message hr = CpiActionDataMessage(1, attrs.pwzName); ExitOnFailure(hr, "Failed to send progress messages"); if (S_FALSE == hr) ExitFunction(); // write key to rollback file hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); ExitOnFailure(hr, "Failed to write key to rollback file"); // action switch (attrs.iActionType) { case atCreate: hr = CreatePartition(&attrs); ExitOnFailure1(hr, "Failed to create partition, key: %S", attrs.pwzKey); break; case atRemove: hr = RemovePartition(&attrs); ExitOnFailure1(hr, "Failed to remove partition, key: %S", attrs.pwzKey); break; } // write completion status to rollback file hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); ExitOnFailure(hr, "Failed to write completion status to rollback file"); // progress hr = WcaProgressMessage(attrs.iActionCost, FALSE); ExitOnFailure(hr, "Failed to update progress"); } hr = S_OK; LExit: // clean up FreePartitionAttributes(&attrs); return hr; }
static HRESULT DownloadResource( __in BURN_USER_EXPERIENCE* pUX, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in HINTERNET hSession, __inout_z LPWSTR* psczUrl, __in_z_opt LPCWSTR wzUser, __in_z_opt LPCWSTR wzPassword, __in_z LPCWSTR wzDestinationPath, __in DWORD64 dw64AuthoredResourceLength, __in DWORD64 dw64ResourceLength, __in DWORD64 dw64ResumeOffset, __in HANDLE hResumeFile, __in_opt BURN_CACHE_CALLBACK* pCallback ) { HRESULT hr = S_OK; HANDLE hPayloadFile = INVALID_HANDLE_VALUE; DWORD cbMaxData = 64 * 1024; // 64 KB BYTE* pbData = NULL; BOOL fRangeRequestsAccepted = TRUE; LPWSTR sczRangeRequestHeader = NULL; HINTERNET hConnect = NULL; HINTERNET hUrl = NULL; LONGLONG llLength = 0; hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hPayloadFile) { ExitWithLastError1(hr, "Failed to create download destination file: %ls", wzDestinationPath); } // Allocate a memory block on a page boundary in case we want to do optimal writing. pbData = static_cast<BYTE*>(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); ExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); // Let's try downloading the file assuming that range requests are accepted. If range requests // are not supported we'll have to start over and accept the fact that we only get one shot // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't // like files that big. while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) { hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); ExitOnFailure(hr, "Failed to allocate range request header."); ReleaseNullInternet(hConnect); ReleaseNullInternet(hUrl); hr = MakeRequest(pUX, wzPackageOrContainerId, wzPayloadId, hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, &hConnect, &hUrl, &fRangeRequestsAccepted); ExitOnFailure1(hr, "Failed to request URL for download: %ls", *psczUrl); // If we didn't get the size of the resource from the initial "HEAD" request // then let's try to get the size from this "GET" request. if (0 == dw64ResourceLength) { hr = InternetGetSizeByHandle(hUrl, &llLength); if (SUCCEEDED(hr)) { dw64ResourceLength = llLength; } else // server didn't tell us the resource length. { // Fallback to the authored size of the resource. However, since we // don't really know the size on the server, don't try to use // range requests either. dw64ResourceLength = dw64AuthoredResourceLength; fRangeRequestsAccepted = FALSE; } } // If we just tried to do a range request and found out that it isn't supported, start over. if (!fRangeRequestsAccepted) { // TODO: log a message that the server did not accept range requests. dw64ResumeOffset = 0; } hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCallback); ExitOnFailure1(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); } LExit: ReleaseInternet(hUrl); ReleaseInternet(hConnect); ReleaseStr(sczRangeRequestHeader); if (pbData) { ::VirtualFree(pbData, 0, MEM_RELEASE); } ReleaseFileHandle(hPayloadFile); return hr; }
extern "C" UINT WINAPI WixRemoveFoldersEx( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug WixRemoveFoldersEx"); HRESULT hr = S_OK; PMSIHANDLE hView; PMSIHANDLE hRec; LPWSTR sczId = NULL; LPWSTR sczComponent = NULL; LPWSTR sczProperty = NULL; LPWSTR sczPath = NULL; LPWSTR sczExpandedPath = NULL; int iMode = 0; DWORD dwCounter = 0; DWORD_PTR cchLen = 0; MSIHANDLE hTable = NULL; MSIHANDLE hColumns = NULL; hr = WcaInitialize(hInstall, "WixRemoveFoldersEx"); ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx."); // anything to do? if (S_OK != WcaTableExists(L"WixRemoveFolderEx")) { WcaLog(LOGMSG_STANDARD, "WixRemoveFolderEx table doesn't exist, so there are no folders to remove."); ExitFunction(); } // query and loop through all the remove folders exceptions hr = WcaOpenExecuteView(vcsRemoveFolderExQuery, &hView); ExitOnFailure(hr, "Failed to open view on WixRemoveFolderEx table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordString(hRec, rfqId, &sczId); ExitOnFailure(hr, "Failed to get remove folder identity."); hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent); ExitOnFailure(hr, "Failed to get remove folder component."); hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty); ExitOnFailure(hr, "Failed to get remove folder property."); hr = WcaGetRecordInteger(hRec, feqMode, &iMode); ExitOnFailure(hr, "Failed to get remove folder mode"); hr = WcaGetProperty(sczProperty, &sczPath); ExitOnFailure2(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId); // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen)); if (SUCCEEDED(hr)) { ExitOnFailure2(hr = E_INVALIDARG, "Missing folder property: %S for row: %S", sczProperty, sczId); } hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT); ExitOnFailure2(hr, "Failed to expand path: %S for row: %S", sczPath, sczId); hr = PathBackslashTerminate(&sczExpandedPath); ExitOnFailure1(hr, "Failed to backslash-terminate path: %S", sczExpandedPath); WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId); hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, &dwCounter, &hTable, &hColumns); ExitOnFailure2(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId); } // reaching the end of the list is actually a good thing, not an error if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure occured while processing WixRemoveFolderEx table"); LExit: if (hColumns) { ::MsiCloseHandle(hColumns); } if (hTable) { ::MsiCloseHandle(hTable); } ReleaseStr(sczExpandedPath); ReleaseStr(sczPath); ReleaseStr(sczProperty); ReleaseStr(sczComponent); ReleaseStr(sczId); DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
static HRESULT MakeRequest( __in BURN_USER_EXPERIENCE* pUX, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in HINTERNET hSession, __inout_z LPWSTR* psczSourceUrl, __in_z_opt LPCWSTR wzMethod, __in_z_opt LPCWSTR wzHeaders, __in_z_opt LPCWSTR wzUser, __in_z_opt LPCWSTR wzPassword, __out HINTERNET* phConnect, __out HINTERNET* phUrl, __out BOOL* pfRangeRequestsAccepted ) { HRESULT hr = S_OK; HINTERNET hConnect = NULL; HINTERNET hUrl = NULL; URI_INFO uri = { }; // Try to open the URL. BOOL fRetry; do { fRetry = FALSE; // If the URL was opened close it, so we can reopen it again. ReleaseInternet(hUrl); ReleaseInternet(hConnect); // Open the url. hr = UriCrackEx(*psczSourceUrl, &uri); ExitOnFailure(hr, "Failed to break URL into server and resource parts."); hConnect = ::InternetConnectW(hSession, uri.sczHostName, uri.port, (wzUser && *wzUser) ? wzUser : uri.sczUser, (wzPassword && *wzPassword) ? wzPassword : uri.sczPassword, INTERNET_SCHEME_FTP == uri.scheme ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP, 0, 0); ExitOnNullWithLastError1(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); // Best effort set the proxy username and password, if they were provided. if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) { if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser))) { ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword)); } } hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); ExitOnFailure1(hr, "Failed to open internet URL: %ls", *psczSourceUrl); hr = SendRequest(pUX, wzPackageOrContainerId, wzPayloadId, hUrl, psczSourceUrl, &fRetry, pfRangeRequestsAccepted); ExitOnFailure1(hr, "Failed to send request to URL: %ls", *psczSourceUrl); } while (fRetry); // Okay, we're all ready to start downloading. Update the connection information. *phConnect = hConnect; hConnect = NULL; *phUrl = hUrl; hUrl = NULL; LExit: UriInfoUninitialize(&uri); ReleaseInternet(hUrl); ReleaseInternet(hConnect); return hr; }
/******************************************************************* PipePumpMessages - *******************************************************************/ extern "C" HRESULT PipePumpMessages( __in HANDLE hPipe, __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, __in_opt LPVOID pvContext, __in BURN_PIPE_RESULT* pResult ) { HRESULT hr = S_OK; BURN_PIPE_MESSAGE msg = { }; SIZE_T iData = 0; LPSTR sczMessage = NULL; DWORD dwResult = 0; // Pump messages from child process. while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) { switch (msg.dwMessage) { case BURN_PIPE_MESSAGE_TYPE_LOG: iData = 0; hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); ExitOnFailure(hr, "Failed to read log message."); hr = LogStringWorkRaw(sczMessage); ExitOnFailure1(hr, "Failed to write log message:'%hs'.", sczMessage); dwResult = static_cast<DWORD>(hr); break; case BURN_PIPE_MESSAGE_TYPE_COMPLETE: if (!msg.pvData || sizeof(DWORD) != msg.cbData) { hr = E_INVALIDARG; ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); } pResult->dwResult = *static_cast<DWORD*>(msg.pvData); ExitFunction1(hr = S_OK); // exit loop. case BURN_PIPE_MESSAGE_TYPE_TERMINATE: iData = 0; hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult); ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); if (sizeof(DWORD) * 2 == msg.cbData) { hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); } ExitFunction1(hr = S_OK); // exit loop. default: if (pfnCallback) { hr = pfnCallback(&msg, pvContext, &dwResult); } else { hr = E_INVALIDARG; } ExitOnFailure1(hr, "Failed to process message: %u", msg.dwMessage); break; } // post result hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); ExitOnFailure(hr, "Failed to post result to child process."); FreePipeMessage(&msg); } ExitOnFailure(hr, "Failed to get message over pipe"); if (S_FALSE == hr) { hr = S_OK; } LExit: ReleaseStr(sczMessage); FreePipeMessage(&msg); return hr; }
HRESULT ScaSqlStrsRead( __inout SCA_SQLSTR** ppsssList, __in SCA_ACTION saAction ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hView, hRec; PMSIHANDLE hViewUser, hRecUser; LPWSTR pwzComponent = NULL; LPWSTR pwzData = NULL; SCA_SQLSTR* psss = NULL; if (S_OK != WcaTableExists(L"SqlString") || S_OK != WcaTableExists(L"SqlDatabase")) { WcaLog(LOGMSG_VERBOSE, "Skipping ScaSqlStrsRead() - SqlString and/or SqlDatabase table not present"); ExitFunction1(hr = S_FALSE); } // loop through all the sql strings hr = WcaOpenExecuteView(vcsSqlStringQuery, &hView); ExitOnFailure(hr, "Failed to open view on SqlString table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; hr = WcaGetRecordString(hRec, ssqComponent, &pwzComponent); ExitOnFailure(hr, "Failed to get Component for SQL String."); er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction); hr = HRESULT_FROM_WIN32(er); ExitOnFailure1(hr, "Failed to get state for component: %ls", pwzComponent); // If we're doing install but the Component is not being installed or we're doing // uninstall but the Component is not being uninstalled, skip it. if ((WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL != saAction) || (WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL != saAction)) { continue; } hr = NewSqlStr(&psss); ExitOnFailure(hr, "failed to allocation new sql string element"); psss->isInstalled = isInstalled; psss->isAction = isAction; hr = WcaGetRecordString(hRec, ssqSqlString, &pwzData); ExitOnFailure(hr, "Failed to get SqlString.String"); hr = ::StringCchCopyW(psss->wzKey, countof(psss->wzKey), pwzData); ExitOnFailure1(hr, "Failed to copy SqlString.String: %ls", pwzData); // find the database information for this string hr = WcaGetRecordString(hRec, ssqSqlDb, &pwzData); ExitOnFailure1(hr, "Failed to get SqlString.SqlDb_ for SqlString '%ls'", psss->wzKey); hr = ::StringCchCopyW(psss->wzSqlDb, countof(psss->wzSqlDb), pwzData); ExitOnFailure1(hr, "Failed to copy SqlString.SqlDb_: %ls", pwzData); hr = WcaGetRecordInteger(hRec, ssqAttributes, &psss->iAttributes); ExitOnFailure1(hr, "Failed to get SqlString.Attributes for SqlString '%ls'", psss->wzKey); //get the sequence number for the string (note that this will be sequenced with scripts too) hr = WcaGetRecordInteger(hRec, ssqSequence, &psss->iSequence); ExitOnFailure1(hr, "Failed to get SqlString.Sequence for SqlString '%ls'", psss->wzKey); // execute SQL hr = WcaGetRecordFormattedString(hRec, ssqSQL, &pwzData); ExitOnFailure1(hr, "Failed to get SqlString.SQL for SqlString '%ls'", psss->wzKey); Assert(!psss->pwzSql); hr = StrAllocString(&psss->pwzSql, pwzData, 0); ExitOnFailure1(hr, "Failed to alloc string for SqlString '%ls'", psss->wzKey); *ppsssList = AddSqlStrToList(*ppsssList, psss); psss = NULL; // set the sss to NULL so it doesn't get freed below } if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure occured while reading SqlString table"); LExit: // if anything was left over after an error clean it all up if (psss) { ScaSqlStrsFreeList(psss); } ReleaseStr(pwzData); ReleaseStr(pwzComponent); return hr; }