From a12b13c36d1da707cf541595cf5f38338f37cc68 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 19 Jan 2019 19:19:15 -0600 Subject: Import code from old v4 repo --- src/ca/mqcost.h | 9 + src/ca/mqexec.cpp | 214 +++++++++++ src/ca/mqqueueexec.cpp | 927 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ca/mqqueueexec.h | 30 ++ src/ca/mqqueuesched.cpp | 582 ++++++++++++++++++++++++++++++ src/ca/mqqueuesched.h | 92 +++++ src/ca/mqsched.cpp | 219 ++++++++++++ src/ca/mqutilexec.cpp | 380 ++++++++++++++++++++ src/ca/mqutilexec.h | 23 ++ src/ca/mqutilsched.cpp | 43 +++ src/ca/mqutilsched.h | 9 + 11 files changed, 2528 insertions(+) create mode 100644 src/ca/mqcost.h create mode 100644 src/ca/mqexec.cpp create mode 100644 src/ca/mqqueueexec.cpp create mode 100644 src/ca/mqqueueexec.h create mode 100644 src/ca/mqqueuesched.cpp create mode 100644 src/ca/mqqueuesched.h create mode 100644 src/ca/mqsched.cpp create mode 100644 src/ca/mqutilexec.cpp create mode 100644 src/ca/mqutilexec.h create mode 100644 src/ca/mqutilsched.cpp create mode 100644 src/ca/mqutilsched.h (limited to 'src/ca') diff --git a/src/ca/mqcost.h b/src/ca/mqcost.h new file mode 100644 index 00000000..a40b7437 --- /dev/null +++ b/src/ca/mqcost.h @@ -0,0 +1,9 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define COST_MESSAGE_QUEUE_CREATE 10000 +#define COST_MESSAGE_QUEUE_DELETE 10000 + +#define COST_MESSAGE_QUEUE_PERMISSION_ADD 10000 +#define COST_MESSAGE_QUEUE_PERMISSION_REMOVE 10000 diff --git a/src/ca/mqexec.cpp b/src/ca/mqexec.cpp new file mode 100644 index 00000000..bac54f31 --- /dev/null +++ b/src/ca/mqexec.cpp @@ -0,0 +1,214 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** + DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} + +/******************************************************************** + MessageQueuingExecuteInstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingExecuteInstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingExecuteInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingExecuteInstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingExecuteInstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // create message queues + hr = MqiCreateMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to create message queues"); + if (S_FALSE == hr) ExitFunction(); + + // add message queue permissions + hr = MqiAddMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to add message queue permissions"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + MessageQueuingRollbackInstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingRollbackInstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingRollbackInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingRollbackInstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingRollbackInstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // add message queue permissions + hr = MqiRollbackAddMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to rollback add message queue permissions"); + + // create message queues + hr = MqiRollbackCreateMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to rollback create message queues"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + MessageQueuingExecuteUninstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingExecuteUninstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingExecuteUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingExecuteUninstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingExecuteUninstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // remove message queue permissions + hr = MqiRemoveMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to remove message queue permissions"); + if (S_FALSE == hr) ExitFunction(); + + // delete message queues + hr = MqiDeleteMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to delete message queues"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + MessageQueuingRollbackUninstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingRollbackUninstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingRollbackUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingRollbackUninstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingRollbackUninstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // delete message queues + hr = MqiRollbackDeleteMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to delete message queues"); + + // remove message queue permissions + hr = MqiRollbackRemoveMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to remove message queue permissions"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ca/mqqueueexec.cpp b/src/ca/mqqueueexec.cpp new file mode 100644 index 00000000..f5b99da7 --- /dev/null +++ b/src/ca/mqqueueexec.cpp @@ -0,0 +1,927 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private typedefs + +typedef HRESULT (__stdcall *MQCreateQueueFunc)(PSECURITY_DESCRIPTOR, MQQUEUEPROPS*, LPWSTR, LPDWORD); +typedef HRESULT (__stdcall *MQDeleteQueueFunc)(LPCWSTR); +typedef HRESULT (__stdcall *MQPathNameToFormatNameFunc)(LPCWSTR, LPWSTR, LPDWORD); +typedef HRESULT (__stdcall *MQGetQueueSecurityFunc)(LPCWSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, LPDWORD); +typedef HRESULT (__stdcall *MQSetQueueSecurityFunc)(LPCWSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); + + +// private enums + +enum eMessageQueueAttributes +{ + mqaAuthenticate = (1 << 0), + mqaJournal = (1 << 1), + mqaTransactional = (1 << 2) +}; + +enum eMessageQueuePrivacyLevel +{ + mqplNone = 0, + mqplOptional = 1, + mqplBody = 2 +}; + +enum eMessageQueuePermission +{ + mqpDeleteMessage = (1 << 0), + mqpPeekMessage = (1 << 1), + mqpWriteMessage = (1 << 2), + mqpDeleteJournalMessage = (1 << 3), + mqpSetQueueProperties = (1 << 4), + mqpGetQueueProperties = (1 << 5), + mqpDeleteQueue = (1 << 6), + mqpGetQueuePermissions = (1 << 7), + mqpChangeQueuePermissions = (1 << 8), + mqpTakeQueueOwnership = (1 << 9), + mqpReceiveMessage = (1 << 10), + mqpReceiveJournalMessage = (1 << 11), + mqpQueueGenericRead = (1 << 12), + mqpQueueGenericWrite = (1 << 13), + mqpQueueGenericExecute = (1 << 14), + mqpQueueGenericAll = (1 << 15) +}; + + +// private structs + +struct MQI_MESSAGE_QUEUE_ATTRIBUTES +{ + LPWSTR pwzKey; + int iBasePriority; + int iJournalQuota; + LPWSTR pwzLabel; + LPWSTR pwzMulticastAddress; + LPWSTR pwzPathName; + int iPrivLevel; + int iQuota; + LPWSTR pwzServiceTypeGuid; + int iAttributes; +}; + +struct MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES +{ + LPWSTR pwzKey; + LPWSTR pwzPathName; + LPWSTR pwzDomain; + LPWSTR pwzName; + int iPermissions; +}; + + +// prototypes for private helper functions + +static HRESULT ReadMessageQueueAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static void FreeMessageQueueAttributes( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static HRESULT ReadMessageQueuePermissionAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ); +static void FreeMessageQueuePermissionAttributes( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ); +static HRESULT CreateMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static HRESULT DeleteMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static HRESULT SetMessageQueuePermissions( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs, + BOOL fRevoke + ); +static void SetAccessPermissions( + int iPermissions, + LPDWORD pgrfAccessPermissions + ); + + +// private variables + +static HMODULE ghMQRT; +static MQCreateQueueFunc gpfnMQCreateQueue; +static MQDeleteQueueFunc gpfnMQDeleteQueue; +static MQPathNameToFormatNameFunc gpfnMQPathNameToFormatName; +static MQGetQueueSecurityFunc gpfnMQGetQueueSecurity; +static MQSetQueueSecurityFunc gpfnMQSetQueueSecurity; + + +// function definitions + +HRESULT MqiInitialize() +{ + HRESULT hr = S_OK; + + // load mqrt.dll + ghMQRT = ::LoadLibraryW(L"mqrt.dll"); + ExitOnNull(ghMQRT, hr, E_FAIL, "Failed to load mqrt.dll"); + + // get MQCreateQueue function address + gpfnMQCreateQueue = (MQCreateQueueFunc)::GetProcAddress(ghMQRT, "MQCreateQueue"); + ExitOnNull(gpfnMQCreateQueue, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQCreateQueue() function"); + + // get MQDeleteQueue function address + gpfnMQDeleteQueue = (MQDeleteQueueFunc)::GetProcAddress(ghMQRT, "MQDeleteQueue"); + ExitOnNull(gpfnMQDeleteQueue, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQDeleteQueue() function"); + + // get MQPathNameToFormatName function address + gpfnMQPathNameToFormatName = (MQPathNameToFormatNameFunc)::GetProcAddress(ghMQRT, "MQPathNameToFormatName"); + ExitOnNull(gpfnMQPathNameToFormatName, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQPathNameToFormatName() function"); + + // get MQGetQueueSecurity function address + gpfnMQGetQueueSecurity = (MQGetQueueSecurityFunc)::GetProcAddress(ghMQRT, "MQGetQueueSecurity"); + ExitOnNull(gpfnMQGetQueueSecurity, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQGetQueueSecurity() function"); + + // get MQSetQueueSecurity function address + gpfnMQSetQueueSecurity = (MQSetQueueSecurityFunc)::GetProcAddress(ghMQRT, "MQSetQueueSecurity"); + ExitOnNull(gpfnMQSetQueueSecurity, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQSetQueueSecurity() function"); + + hr = S_OK; + +LExit: + return hr; +} + +void MqiUninitialize() +{ + if (ghMQRT) + ::FreeLibrary(ghMQRT); +} + +HRESULT MqiCreateMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages, key: %S", attrs.pwzKey); + + // create message queue + hr = CreateMessageQueue(&attrs); + ExitOnFailure(hr, "Failed to create message queue, key: %S", attrs.pwzKey); + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_CREATE, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackCreateMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // create message queue + hr = DeleteMessageQueue(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to delete message queue, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiDeleteMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages, key: %S", attrs.pwzKey); + + // create message queue + hr = DeleteMessageQueue(&attrs); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to delete queue, hr: 0x%x, key: %S", hr, attrs.pwzKey); + continue; + } + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_DELETE, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackDeleteMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // create message queue + hr = CreateMessageQueue(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to create message queue, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiAddMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, FALSE); + ExitOnFailure(hr, "Failed to add message queue permission"); + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_PERMISSION_ADD, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackAddMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, TRUE); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to rollback add message queue permission, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + +HRESULT MqiRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, TRUE); + ExitOnFailure(hr, "Failed to remove message queue permission"); + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_PERMISSION_ADD, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, FALSE); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to rollback remove message queue permission, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadMessageQueueAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // read message queue information from custom action data + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iBasePriority); + ExitOnFailure(hr, "Failed to read base priority from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iJournalQuota); + ExitOnFailure(hr, "Failed to read journal quota from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzLabel); + ExitOnFailure(hr, "Failed to read label from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzMulticastAddress); + ExitOnFailure(hr, "Failed to read multicast address from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPathName); + ExitOnFailure(hr, "Failed to read path name from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iPrivLevel); + ExitOnFailure(hr, "Failed to read privacy level from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iQuota); + ExitOnFailure(hr, "Failed to read quota from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzServiceTypeGuid); + ExitOnFailure(hr, "Failed to read service type guid from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iAttributes); + ExitOnFailure(hr, "Failed to read attributes from custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeMessageQueueAttributes( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzLabel); + ReleaseStr(pAttrs->pwzMulticastAddress); + ReleaseStr(pAttrs->pwzPathName); + ReleaseStr(pAttrs->pwzServiceTypeGuid); +} + +static HRESULT ReadMessageQueuePermissionAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // read message queue permission information from custom action data + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPathName); + ExitOnFailure(hr, "Failed to read path name from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzDomain); + ExitOnFailure(hr, "Failed to read domain from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzName); + ExitOnFailure(hr, "Failed to read name from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iPermissions); + ExitOnFailure(hr, "Failed to read permissions from custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeMessageQueuePermissionAttributes( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzPathName); + ReleaseStr(pAttrs->pwzDomain); + ReleaseStr(pAttrs->pwzName); +} + +static HRESULT CreateMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + SECURITY_DESCRIPTOR sd; + PSID pOwner = NULL; + DWORD cbDacl = 0; + PACL pDacl = NULL; + QUEUEPROPID aPropID[11]; + MQPROPVARIANT aPropVar[11]; + MQQUEUEPROPS props; + + GUID guidType; + + DWORD dwFormatNameLength = 0; + + ::ZeroMemory(&sd, sizeof(sd)); + ::ZeroMemory(aPropID, sizeof(aPropID)); + ::ZeroMemory(aPropVar, sizeof(aPropVar)); + ::ZeroMemory(&props, sizeof(props)); + ::ZeroMemory(&guidType, sizeof(guidType)); + + // initialize security descriptor + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to initialize security descriptor"); + + // set security descriptor owner + hr = PcaAccountNameToSid(L"\\Administrators", &pOwner); + ExitOnFailure(hr, "Failed to get sid for account name"); + + if (!::SetSecurityDescriptorOwner(&sd, pOwner, FALSE)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set security descriptor owner"); + + // set security descriptor DACL + cbDacl = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + ::GetLengthSid(pOwner); + pDacl = (PACL)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, cbDacl); + ExitOnNull(pDacl, hr, E_OUTOFMEMORY, "Failed to allocate buffer for DACL"); + + if (!::InitializeAcl(pDacl, cbDacl, ACL_REVISION)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to initialize DACL"); + + if (!::AddAccessAllowedAce(pDacl, ACL_REVISION, MQSEC_QUEUE_GENERIC_ALL, pOwner)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to add ACE to DACL"); + + if (!::SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set security descriptor DACL"); + + // set property values + props.aPropID = aPropID; + props.aPropVar = aPropVar; + + aPropID[0] = PROPID_Q_LABEL; + aPropVar[0].vt = VT_LPWSTR; + aPropVar[0].pwszVal = pAttrs->pwzLabel; + + aPropID[1] = PROPID_Q_PATHNAME; + aPropVar[1].vt = VT_LPWSTR; + aPropVar[1].pwszVal = pAttrs->pwzPathName; + + aPropID[2] = PROPID_Q_AUTHENTICATE; + aPropVar[2].vt = VT_UI1; + aPropVar[2].bVal = mqaAuthenticate == (pAttrs->iAttributes & mqaAuthenticate); + + aPropID[3] = PROPID_Q_JOURNAL; + aPropVar[3].vt = VT_UI1; + aPropVar[3].bVal = mqaJournal == (pAttrs->iAttributes & mqaJournal); + + aPropID[4] = PROPID_Q_TRANSACTION; + aPropVar[4].vt = VT_UI1; + aPropVar[4].bVal = mqaTransactional == (pAttrs->iAttributes & mqaTransactional); + + props.cProp = 5; + + if (MSI_NULL_INTEGER != pAttrs->iBasePriority) + { + aPropID[props.cProp] = PROPID_Q_BASEPRIORITY; + aPropVar[props.cProp].vt = VT_I2; + aPropVar[props.cProp].iVal = (SHORT)pAttrs->iBasePriority; + props.cProp++; + } + + if (MSI_NULL_INTEGER != pAttrs->iJournalQuota) + { + aPropID[props.cProp] = PROPID_Q_JOURNAL_QUOTA; + aPropVar[props.cProp].vt = VT_UI4; + aPropVar[props.cProp].ulVal = (ULONG)pAttrs->iJournalQuota; + props.cProp++; + } + + if (*pAttrs->pwzMulticastAddress) + { + aPropID[props.cProp] = PROPID_Q_MULTICAST_ADDRESS; + aPropVar[props.cProp].vt = VT_LPWSTR; + aPropVar[props.cProp].pwszVal = pAttrs->pwzMulticastAddress; + props.cProp++; + } + + if (MSI_NULL_INTEGER != pAttrs->iPrivLevel) + { + aPropID[props.cProp] = PROPID_Q_PRIV_LEVEL; + aPropVar[props.cProp].vt = VT_UI4; + switch (pAttrs->iPrivLevel) + { + case mqplNone: + aPropVar[props.cProp].ulVal = MQ_PRIV_LEVEL_NONE; + break; + case mqplBody: + aPropVar[props.cProp].ulVal = MQ_PRIV_LEVEL_BODY; + break; + case mqplOptional: + aPropVar[props.cProp].ulVal = MQ_PRIV_LEVEL_OPTIONAL; + break; + } + props.cProp++; + } + + if (MSI_NULL_INTEGER != pAttrs->iQuota) + { + aPropID[props.cProp] = PROPID_Q_QUOTA; + aPropVar[props.cProp].vt = VT_UI4; + aPropVar[props.cProp].ulVal = (ULONG)pAttrs->iQuota; + props.cProp++; + } + + if (*pAttrs->pwzServiceTypeGuid) + { + // parse guid string + hr = PcaGuidFromString(pAttrs->pwzServiceTypeGuid, &guidType); + ExitOnFailure(hr, "Failed to parse service type GUID string"); + + aPropID[props.cProp] = PROPID_Q_TYPE; + aPropVar[props.cProp].vt = VT_CLSID; + aPropVar[props.cProp].puuid = &guidType; + props.cProp++; + } + + // create message queue + hr = gpfnMQCreateQueue(&sd, &props, NULL, &dwFormatNameLength); + ExitOnFailure(hr, "Failed to create message queue"); + + // log + WcaLog(LOGMSG_VERBOSE, "Message queue created, key: %S, PathName: '%S'", pAttrs->pwzKey, pAttrs->pwzPathName); + + hr = S_OK; + +LExit: + // clean up + if (pOwner) + ::HeapFree(::GetProcessHeap(), 0, pOwner); + if (pDacl) + ::HeapFree(::GetProcessHeap(), 0, pDacl); + + return hr; +} + +static HRESULT DeleteMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzFormatName = NULL; + DWORD dwCount = 128; + + // get format name + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to allocate format name string"); + do { + hr = gpfnMQPathNameToFormatName(pAttrs->pwzPathName, pwzFormatName, &dwCount); + switch (hr) + { + case MQ_ERROR_QUEUE_NOT_FOUND: + ExitFunction1(hr = S_OK); // nothing to delete + case MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL: + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to reallocate format name string"); + hr = S_FALSE; // retry + break; + default: + ExitOnFailure(hr, "Failed to get format name"); + hr = S_OK; + } + } while (S_FALSE == hr); + + // delete queue + hr = gpfnMQDeleteQueue(pwzFormatName); + ExitOnFailure(hr, "Failed to delete queue"); + + // log + WcaLog(LOGMSG_VERBOSE, "Message queue deleted, key: %S, PathName: '%S'", pAttrs->pwzKey, pAttrs->pwzPathName); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzFormatName); + + return hr; +} + +static HRESULT SetMessageQueuePermissions( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs, + BOOL fRevoke + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + DWORD dw = 0; + + LPWSTR pwzAccount = NULL; + LPWSTR pwzFormatName = NULL; + + PSECURITY_DESCRIPTOR psd = NULL; + PSECURITY_DESCRIPTOR ptsd = NULL; + + PACL pAclExisting = NULL; + PACL pAclNew = NULL; + BOOL fDaclPresent = FALSE; + BOOL fDaclDefaulted = FALSE; + + PSID psid = NULL; + + EXPLICIT_ACCESSW ea; + SECURITY_DESCRIPTOR sdNew; + + ::ZeroMemory(&ea, sizeof(ea)); + ::ZeroMemory(&sdNew, sizeof(sdNew)); + + // get format name + dw = 128; + hr = StrAlloc(&pwzFormatName, dw); + ExitOnFailure(hr, "Failed to allocate format name string"); + do { + hr = gpfnMQPathNameToFormatName(pAttrs->pwzPathName, pwzFormatName, &dw); + if (MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL == hr) + { + hr = StrAlloc(&pwzFormatName, dw); + ExitOnFailure(hr, "Failed to reallocate format name string"); + hr = S_FALSE; // retry + } + else + { + ExitOnFailure(hr, "Failed to get format name"); + hr = S_OK; + } + } while (S_FALSE == hr); + + // get queue security information + dw = 256; + psd = (PSECURITY_DESCRIPTOR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dw); + ExitOnNull(psd, hr, E_OUTOFMEMORY, "Failed to allocate buffer for security descriptor"); + do { + hr = gpfnMQGetQueueSecurity(pwzFormatName, DACL_SECURITY_INFORMATION, psd, dw, &dw); + if (MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL == hr) + { + ptsd = (PSECURITY_DESCRIPTOR)::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, psd, dw); + ExitOnNull(ptsd, hr, E_OUTOFMEMORY, "Failed to reallocate buffer for security descriptor"); + psd = ptsd; + hr = S_FALSE; // retry + } + else + { + ExitOnFailure(hr, "Failed to get queue security information"); + hr = S_OK; + } + } while (S_FALSE == hr); + + // get dacl + if (!::GetSecurityDescriptorDacl(psd, &fDaclPresent, &pAclExisting, &fDaclDefaulted)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to get DACL for security descriptor"); + if (!fDaclPresent || !pAclExisting) + ExitOnFailure(hr = E_ACCESSDENIED, "Failed to get DACL for security descriptor, access denied"); + + // build account name string + hr = PcaBuildAccountName(pAttrs->pwzDomain, pAttrs->pwzName, &pwzAccount); + ExitOnFailure(hr, "Failed to build account name string"); + + // get sid for account name + hr = PcaAccountNameToSid(pwzAccount, &psid); + ExitOnFailure(hr, "Failed to get SID for account name"); + + // set acl entry + SetAccessPermissions(pAttrs->iPermissions, &ea.grfAccessPermissions); + ea.grfAccessMode = fRevoke ? REVOKE_ACCESS : SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ::BuildTrusteeWithSidW(&ea.Trustee, psid); + + er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set ACL entry"); + + // create new security descriptor + if (!::InitializeSecurityDescriptor(&sdNew, SECURITY_DESCRIPTOR_REVISION)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to initialize security descriptor"); + + if (!::SetSecurityDescriptorDacl(&sdNew, TRUE, pAclNew, FALSE)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set DACL for security descriptor"); + + // set queue security information + hr = gpfnMQSetQueueSecurity(pwzFormatName, DACL_SECURITY_INFORMATION, &sdNew); + ExitOnFailure(hr, "Failed to set queue security information"); + + // log + WcaLog(LOGMSG_VERBOSE, "Permission set for message queue, key: %S, PathName: '%S'", pAttrs->pwzKey, pAttrs->pwzPathName); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzFormatName); + ReleaseStr(pwzAccount); + + if (psd) + ::HeapFree(::GetProcessHeap(), 0, psd); + if (psid) + ::HeapFree(::GetProcessHeap(), 0, psid); + if (pAclNew) + ::LocalFree(pAclNew); + + return hr; +} + +static void SetAccessPermissions( + int iPermissions, + LPDWORD pgrfAccessPermissions + ) +{ + if (iPermissions & mqpDeleteMessage) + *pgrfAccessPermissions |= MQSEC_DELETE_MESSAGE; + if (iPermissions & mqpPeekMessage) + *pgrfAccessPermissions |= MQSEC_PEEK_MESSAGE; + if (iPermissions & mqpWriteMessage) + *pgrfAccessPermissions |= MQSEC_WRITE_MESSAGE; + if (iPermissions & mqpDeleteJournalMessage) + *pgrfAccessPermissions |= MQSEC_DELETE_JOURNAL_MESSAGE; + if (iPermissions & mqpSetQueueProperties) + *pgrfAccessPermissions |= MQSEC_SET_QUEUE_PROPERTIES; + if (iPermissions & mqpGetQueueProperties) + *pgrfAccessPermissions |= MQSEC_GET_QUEUE_PROPERTIES; + if (iPermissions & mqpDeleteQueue) + *pgrfAccessPermissions |= MQSEC_DELETE_QUEUE; + if (iPermissions & mqpGetQueuePermissions) + *pgrfAccessPermissions |= MQSEC_GET_QUEUE_PERMISSIONS; + if (iPermissions & mqpChangeQueuePermissions) + *pgrfAccessPermissions |= MQSEC_CHANGE_QUEUE_PERMISSIONS; + if (iPermissions & mqpTakeQueueOwnership) + *pgrfAccessPermissions |= MQSEC_TAKE_QUEUE_OWNERSHIP; + if (iPermissions & mqpReceiveMessage) + *pgrfAccessPermissions |= MQSEC_RECEIVE_MESSAGE; + if (iPermissions & mqpReceiveJournalMessage) + *pgrfAccessPermissions |= MQSEC_RECEIVE_JOURNAL_MESSAGE; + if (iPermissions & mqpQueueGenericRead) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_READ; + if (iPermissions & mqpQueueGenericWrite) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_WRITE; + if (iPermissions & mqpQueueGenericExecute) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_EXECUTE; + if (iPermissions & mqpQueueGenericAll) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_ALL; +} diff --git a/src/ca/mqqueueexec.h b/src/ca/mqqueueexec.h new file mode 100644 index 00000000..37ceea50 --- /dev/null +++ b/src/ca/mqqueueexec.h @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT MqiInitialize(); +void MqiUninitialize(); +HRESULT MqiCreateMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackCreateMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiDeleteMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackDeleteMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiAddMessageQueuePermissions( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackAddMessageQueuePermissions( + LPWSTR* ppwzData + ); +HRESULT MqiRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ); diff --git a/src/ca/mqqueuesched.cpp b/src/ca/mqqueuesched.cpp new file mode 100644 index 00000000..4f40a4aa --- /dev/null +++ b/src/ca/mqqueuesched.cpp @@ -0,0 +1,582 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsMessageQueueQuery = + L"SELECT `MessageQueue`, `Component_`, `BasePriority`, `JournalQuota`, `Label`, `MulticastAddress`, `PathName`, `PrivLevel`, `Quota`, `ServiceTypeGuid`, `Attributes` FROM `MessageQueue`"; +enum eMessageQueueQuery { mqqMessageQueue = 1, mqqComponent, mqqBasePriority, mqqJournalQuota, mqqLabel, mqqMulticastAddress, mqqPathName, mqqPrivLevel, mqqQuota, mqqServiceTypeGuid, mqqAttributes }; + +LPCWSTR vcsMessageQueueUserPermissionQuery = + L"SELECT `MessageQueueUserPermission`, `MessageQueue_`, `MessageQueueUserPermission`.`Component_`, `Domain`, `Name`, `Permissions` FROM `MessageQueueUserPermission`, `User` WHERE `User_` = `User`"; +LPCWSTR vcsMessageQueueGroupPermissionQuery = + L"SELECT `MessageQueueGroupPermission`, `MessageQueue_`, `MessageQueueGroupPermission`.`Component_`, `Domain`, `Name`, `Permissions` FROM `MessageQueueGroupPermission`, `Group` WHERE `Group_` = `Group`"; +enum eMessageQueuePermissionQuery { mqpqMessageQueuePermission = 1, mqpqMessageQueue, mqpqComponent, mqpqDomain, mqpqName, mqpqPermissions }; + + +// prototypes for private helper functions + +static HRESULT MqiMessageQueueFindByKey( + MQI_MESSAGE_QUEUE_LIST* pList, + LPCWSTR pwzKey, + MQI_MESSAGE_QUEUE** ppItm + ); +static HRESULT AddMessageQueueToActionData( + MQI_MESSAGE_QUEUE* pItm, + LPWSTR* ppwzActionData + ); +static HRESULT MessageQueueTrusteePermissionsRead( + LPCWSTR pwzQuery, + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ); +static HRESULT AddMessageQueuePermissionToActionData( + MQI_MESSAGE_QUEUE_PERMISSION* pItm, + LPWSTR* ppwzActionData + ); + + +// private typedefs + +typedef HRESULT (__stdcall *MQPathNameToFormatNameFunc)(LPCWSTR, LPWSTR, LPDWORD); + + +// private variables + +static HMODULE ghMQRT; +static MQPathNameToFormatNameFunc gpfnMQPathNameToFormatName; + + +// function definitions + +HRESULT MqiInitialize() +{ + HRESULT hr = S_OK; + + // load mqrt.dll + ghMQRT = ::LoadLibraryW(L"mqrt.dll"); + if (!ghMQRT) + { + ExitFunction1(hr = S_FALSE); + } + + // get MQPathNameToFormatName function address + gpfnMQPathNameToFormatName = (MQPathNameToFormatNameFunc)::GetProcAddress(ghMQRT, "MQPathNameToFormatName"); + ExitOnNullWithLastError(gpfnMQPathNameToFormatName, hr, "Failed get address for MQPathNameToFormatName() function"); + + hr = S_OK; + +LExit: + return hr; +} + +void MqiUninitialize() +{ + if (ghMQRT) + { + ::FreeLibrary(ghMQRT); + } +} + +HRESULT MqiMessageQueueRead( + MQI_MESSAGE_QUEUE_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + MQI_MESSAGE_QUEUE* pItm = NULL; + LPWSTR pwzData = NULL; + + // loop through all partitions + hr = WcaOpenExecuteView(vcsMessageQueueQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on MessageQueue table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (MQI_MESSAGE_QUEUE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MQI_MESSAGE_QUEUE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, mqqMessageQueue, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get component install state + hr = WcaGetRecordString(hRec, mqqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get base priority + hr = WcaGetRecordInteger(hRec, mqqBasePriority, &pItm->iBasePriority); + ExitOnFailure(hr, "Failed to get base priority"); + + // get journal quota + hr = WcaGetRecordInteger(hRec, mqqJournalQuota, &pItm->iJournalQuota); + ExitOnFailure(hr, "Failed to get journal quota"); + + // get label + hr = WcaGetRecordFormattedString(hRec, mqqLabel, &pwzData); + ExitOnFailure(hr, "Failed to get label"); + StringCchCopyW(pItm->wzLabel, countof(pItm->wzLabel), pwzData); + + // get multicast address + hr = WcaGetRecordFormattedString(hRec, mqqMulticastAddress, &pwzData); + ExitOnFailure(hr, "Failed to get multicast address"); + StringCchCopyW(pItm->wzMulticastAddress, countof(pItm->wzMulticastAddress), pwzData); + + // get path name + hr = WcaGetRecordFormattedString(hRec, mqqPathName, &pwzData); + ExitOnFailure(hr, "Failed to get path name"); + StringCchCopyW(pItm->wzPathName, countof(pItm->wzPathName), pwzData); + + // get privacy level + hr = WcaGetRecordInteger(hRec, mqqPrivLevel, &pItm->iPrivLevel); + ExitOnFailure(hr, "Failed to get privacy level"); + + // get quota + hr = WcaGetRecordInteger(hRec, mqqQuota, &pItm->iQuota); + ExitOnFailure(hr, "Failed to get quota"); + + // get service type guid + hr = WcaGetRecordFormattedString(hRec, mqqServiceTypeGuid, &pwzData); + ExitOnFailure(hr, "Failed to get service type guid"); + StringCchCopyW(pItm->wzServiceTypeGuid, countof(pItm->wzServiceTypeGuid), pwzData); + + // get attributes + hr = WcaGetRecordInteger(hRec, mqqAttributes, &pItm->iAttributes); + ExitOnFailure(hr, "Failed to get attributes"); + + // increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + pList->iInstallCount++; + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + pList->iUninstallCount++; + + // add entry + pItm->pNext = pList->pFirst; + pList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + ::HeapFree(::GetProcessHeap(), 0, pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT MqiMessageQueueVerify( + MQI_MESSAGE_QUEUE_LIST* pList + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzFormatName = NULL; + DWORD dwCount = 128; + + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queues that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // get format name + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to allocate format name string"); + do { + hr = gpfnMQPathNameToFormatName(pItm->wzPathName, pwzFormatName, &dwCount); + switch (hr) + { + case MQ_ERROR_QUEUE_NOT_FOUND: + break; // break + case MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL: + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to reallocate format name string"); + hr = S_FALSE; // retry + break; + default: + ExitOnFailure(hr, "Failed to get format name"); + hr = S_OK; + } + } while (S_FALSE == hr); + + if (MQ_ERROR_QUEUE_NOT_FOUND == hr) + { + continue; + } + pItm->fExists = TRUE; + pList->iInstallCount--; + + // clean up + ReleaseNullStr(pwzFormatName); + } + + hr = S_OK; + +LExit: + ReleaseStr(pwzFormatName); + return hr; +} + +HRESULT MqiMessageQueueInstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queues that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // if the queue exists we should not try to create it + if (pItm->fExists && !fRollback) + { + continue; + } + + // add message queue to action data + hr = AddMessageQueueToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT MqiMessageQueueUninstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queues that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // if we did not create the queue we should not try to delete it + if (pItm->fExists && fRollback) + { + continue; + } + + // add message queue to action data + hr = AddMessageQueueToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +void MqiMessageQueueFreeList( + MQI_MESSAGE_QUEUE_LIST* pList + ) +{ + MQI_MESSAGE_QUEUE* pItm = pList->pFirst; + while (pItm) + { + MQI_MESSAGE_QUEUE* pDelete = pItm; + pItm = pItm->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +HRESULT MqiMessageQueuePermissionRead( + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ) +{ + HRESULT hr = S_OK; + + // read message queue user permissions + if (S_OK == WcaTableExists(L"MessageQueueUserPermission")) + { + hr = MessageQueueTrusteePermissionsRead(vcsMessageQueueUserPermissionQuery, pMessageQueueList, pList); + ExitOnFailure(hr, "Failed to read message queue user permissions"); + } + + // read message queue group permissions + if (S_OK == WcaTableExists(L"MessageQueueGroupPermission")) + { + hr = MessageQueueTrusteePermissionsRead(vcsMessageQueueGroupPermissionQuery, pMessageQueueList, pList); + ExitOnFailure(hr, "Failed to read message queue group permissions"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT MqiMessageQueuePermissionInstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queue permissions that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // add message queue permission to action data + hr = AddMessageQueuePermissionToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue permission to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT MqiMessageQueuePermissionUninstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queue permissions that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // add message queue permission to action data + hr = AddMessageQueuePermissionToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue permission to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +void MqiMessageQueuePermissionFreeList( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ) +{ + MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; + while (pItm) + { + MQI_MESSAGE_QUEUE_PERMISSION* pDelete = pItm; + pItm = pItm->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + + +// helper function definitions + +static HRESULT MqiMessageQueueFindByKey( + MQI_MESSAGE_QUEUE_LIST* pList, + LPCWSTR pwzKey, + MQI_MESSAGE_QUEUE** ppItm + ) +{ + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppItm = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +static HRESULT AddMessageQueueToActionData( + MQI_MESSAGE_QUEUE* pItm, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add message queue information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iBasePriority, ppwzActionData); + ExitOnFailure(hr, "Failed to add base priority to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iJournalQuota, ppwzActionData); + ExitOnFailure(hr, "Failed to add journal quota to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzLabel, ppwzActionData); + ExitOnFailure(hr, "Failed to add label to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzMulticastAddress, ppwzActionData); + ExitOnFailure(hr, "Failed to add multicast address to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzPathName, ppwzActionData); + ExitOnFailure(hr, "Failed to add path name to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iPrivLevel, ppwzActionData); + ExitOnFailure(hr, "Failed to add privacy level to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iQuota, ppwzActionData); + ExitOnFailure(hr, "Failed to add quota to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzServiceTypeGuid, ppwzActionData); + ExitOnFailure(hr, "Failed to add service type guid to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iAttributes, ppwzActionData); + ExitOnFailure(hr, "Failed to add attributes to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT MessageQueueTrusteePermissionsRead( + LPCWSTR pwzQuery, + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + LPWSTR pwzData = NULL; + + MQI_MESSAGE_QUEUE_PERMISSION* pItm = NULL; + + // loop through all application roles + hr = WcaOpenExecuteView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (MQI_MESSAGE_QUEUE_PERMISSION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MQI_MESSAGE_QUEUE_PERMISSION)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, mqpqMessageQueuePermission, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get component + hr = WcaGetRecordString(hRec, mqpqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get message queue + hr = WcaGetRecordString(hRec, mqpqMessageQueue, &pwzData); + ExitOnFailure(hr, "Failed to get application role"); + + hr = MqiMessageQueueFindByKey(pMessageQueueList, pwzData, &pItm->pMessageQueue); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find message queue, key: %S", pwzData); + + // get user domain + hr = WcaGetRecordFormattedString(hRec, mqpqDomain, &pwzData); + ExitOnFailure(hr, "Failed to get domain"); + StringCchCopyW(pItm->wzDomain, countof(pItm->wzDomain), pwzData); + + // get user name + hr = WcaGetRecordFormattedString(hRec, mqpqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // get permissions + hr = WcaGetRecordInteger(hRec, mqpqPermissions, &pItm->iPermissions); + ExitOnFailure(hr, "Failed to get permissions"); + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + pList->iInstallCount++; + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + pList->iUninstallCount++; + + // add entry + if (pList->pFirst) + pItm->pNext = pList->pFirst; + pList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzData); + + if (pItm) + ::HeapFree(::GetProcessHeap(), 0, pItm); + + return hr; +} + +static HRESULT AddMessageQueuePermissionToActionData( + MQI_MESSAGE_QUEUE_PERMISSION* pItm, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add message queue information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data"); + hr = WcaWriteStringToCaData(pItm->pMessageQueue->wzPathName, ppwzActionData); + ExitOnFailure(hr, "Failed to add path name to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzDomain, ppwzActionData); + ExitOnFailure(hr, "Failed to add domain to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add name to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iPermissions, ppwzActionData); + ExitOnFailure(hr, "Failed to add permissions to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/mqqueuesched.h b/src/ca/mqqueuesched.h new file mode 100644 index 00000000..b063ca28 --- /dev/null +++ b/src/ca/mqqueuesched.h @@ -0,0 +1,92 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct MQI_MESSAGE_QUEUE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + int iBasePriority; + int iJournalQuota; + WCHAR wzLabel[MAX_DARWIN_COLUMN + 1]; + WCHAR wzMulticastAddress[MAX_DARWIN_COLUMN + 1]; + WCHAR wzPathName[MAX_DARWIN_COLUMN + 1]; + int iPrivLevel; + int iQuota; + WCHAR wzServiceTypeGuid[MAX_DARWIN_COLUMN + 1]; + int iAttributes; + + INSTALLSTATE isInstalled, isAction; + BOOL fExists; + + MQI_MESSAGE_QUEUE* pNext; +}; + +struct MQI_MESSAGE_QUEUE_LIST +{ + MQI_MESSAGE_QUEUE* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + +struct MQI_MESSAGE_QUEUE_PERMISSION +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzDomain[MAX_DARWIN_COLUMN + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + int iPermissions; + + MQI_MESSAGE_QUEUE* pMessageQueue; + + INSTALLSTATE isInstalled, isAction; + + MQI_MESSAGE_QUEUE_PERMISSION* pNext; +}; + +struct MQI_MESSAGE_QUEUE_PERMISSION_LIST +{ + MQI_MESSAGE_QUEUE_PERMISSION* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + + +// function prototypes + +HRESULT MqiInitialize(); +void MqiUninitialize(); +HRESULT MqiMessageQueueRead( + MQI_MESSAGE_QUEUE_LIST* pList + ); +HRESULT MqiMessageQueueVerify( + MQI_MESSAGE_QUEUE_LIST* pList + ); +HRESULT MqiMessageQueueInstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ); +HRESULT MqiMessageQueueUninstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ); +void MqiMessageQueueFreeList( + MQI_MESSAGE_QUEUE_LIST* pList + ); +HRESULT MqiMessageQueuePermissionRead( + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ); +HRESULT MqiMessageQueuePermissionInstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ); +HRESULT MqiMessageQueuePermissionUninstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ); +void MqiMessageQueuePermissionFreeList( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ); diff --git a/src/ca/mqsched.cpp b/src/ca/mqsched.cpp new file mode 100644 index 00000000..cefce853 --- /dev/null +++ b/src/ca/mqsched.cpp @@ -0,0 +1,219 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +/******************************************************************** + DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} + +/******************************************************************** + MessageQueuingInstall - CUSTOM ACTION ENTRY POINT for installing MSMQ message queues + +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + MQI_MESSAGE_QUEUE_LIST lstMessageQueues; + MQI_MESSAGE_QUEUE_PERMISSION_LIST lstMessageQueuePermissions; + + int iCost = 0; + LPWSTR pwzRollbackActionData = NULL; + LPWSTR pwzExecuteActionData = NULL; + + ::ZeroMemory(&lstMessageQueues, sizeof(lstMessageQueues)); + ::ZeroMemory(&lstMessageQueuePermissions, sizeof(lstMessageQueuePermissions)); + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingInstall"); + ExitOnFailure(hr, "Failed to initialize"); + + do + { + hr = MqiInitialize(); + if (S_FALSE == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to load mqrt.dll."); + er = WcaErrorMessage(msierrMsmqCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction1(hr = E_FAIL); // bail with error + case IDRETRY: + break; // retry + case IDIGNORE: __fallthrough; + default: + ExitFunction1(hr = S_OK); // pretend everything is okay and bail + } + } + ExitOnFailure(hr, "Failed to initialize MSMQ."); + } while (S_FALSE == hr); + + // read message queues + hr = MqiMessageQueueRead(&lstMessageQueues); + ExitOnFailure(hr, "Failed to read MessageQueue table"); + + // read message queue permissions + hr = MqiMessageQueuePermissionRead(&lstMessageQueues, &lstMessageQueuePermissions); + ExitOnFailure(hr, "Failed to read message queue permissions"); + + // verify message queue elementes + hr = MqiMessageQueueVerify(&lstMessageQueues); + ExitOnFailure(hr, "Failed to verify message queue elements."); + + if (lstMessageQueues.iInstallCount || lstMessageQueuePermissions.iInstallCount) + { + // schedule rollback action + hr = MqiMessageQueuePermissionInstall(&lstMessageQueuePermissions, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to rollback action data"); + + hr = MqiMessageQueueInstall(&lstMessageQueues, TRUE, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queues to rollback action data"); + + hr = WcaDoDeferredAction(L"MessageQueuingRollbackInstall", pwzRollbackActionData, 0); + ExitOnFailure(hr, "Failed to schedule MessageQueuingRollbackInstall"); + + // schedule execute action + hr = MqiMessageQueueInstall(&lstMessageQueues, FALSE, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queues to execute action data"); + iCost += lstMessageQueues.iInstallCount * COST_MESSAGE_QUEUE_CREATE; + + hr = MqiMessageQueuePermissionInstall(&lstMessageQueuePermissions, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to execute action data"); + iCost += lstMessageQueues.iInstallCount * COST_MESSAGE_QUEUE_PERMISSION_ADD; + + hr = WcaDoDeferredAction(L"MessageQueuingExecuteInstall", pwzExecuteActionData, iCost); + ExitOnFailure(hr, "Failed to schedule MessageQueuingExecuteInstall"); + } + + hr = S_OK; + +LExit: + // clean up + MqiMessageQueueFreeList(&lstMessageQueues); + MqiMessageQueuePermissionFreeList(&lstMessageQueuePermissions); + + ReleaseStr(pwzRollbackActionData); + ReleaseStr(pwzExecuteActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** + MessageQueuingUninstall - CUSTOM ACTION ENTRY POINT for uninstalling MSMQ message queues + +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + MQI_MESSAGE_QUEUE_LIST lstMessageQueues; + MQI_MESSAGE_QUEUE_PERMISSION_LIST lstMessageQueuePermissions; + + int iCost = 0; + LPWSTR pwzRollbackActionData = NULL; + LPWSTR pwzExecuteActionData = NULL; + + ::ZeroMemory(&lstMessageQueues, sizeof(lstMessageQueues)); + ::ZeroMemory(&lstMessageQueuePermissions, sizeof(lstMessageQueuePermissions)); + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingUninstall"); + ExitOnFailure(hr, "Failed to initialize"); + + do + { + hr = MqiInitialize(); + if (S_FALSE == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to load mqrt.dll."); + er = WcaErrorMessage(msierrMsmqCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction1(hr = E_FAIL); // bail with error + case IDRETRY: + break; // retry + case IDIGNORE: __fallthrough; + default: + ExitFunction1(hr = S_OK); // pretend everything is okay and bail + } + } + ExitOnFailure(hr, "Failed to initialize MSMQ."); + } while (S_FALSE == hr); + + // read message queues + hr = MqiMessageQueueRead(&lstMessageQueues); + ExitOnFailure(hr, "Failed to read MessageQueue table"); + + // read message queue permissions + hr = MqiMessageQueuePermissionRead(&lstMessageQueues, &lstMessageQueuePermissions); + ExitOnFailure(hr, "Failed to read message queue permissions"); + + if (lstMessageQueues.iUninstallCount || lstMessageQueuePermissions.iUninstallCount) + { + // schedule rollback action + hr = MqiMessageQueueUninstall(&lstMessageQueues, TRUE, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queues to rollback action data"); + + hr = MqiMessageQueuePermissionUninstall(&lstMessageQueuePermissions, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to rollback action data"); + + hr = WcaDoDeferredAction(L"MessageQueuingRollbackUninstall", pwzRollbackActionData, 0); + ExitOnFailure(hr, "Failed to schedule MessageQueuingRollbackUninstall"); + + // schedule execute action + hr = MqiMessageQueuePermissionUninstall(&lstMessageQueuePermissions, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to execute action data"); + + hr = MqiMessageQueueUninstall(&lstMessageQueues, FALSE, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queues to execute action data"); + iCost += lstMessageQueues.iUninstallCount * COST_MESSAGE_QUEUE_DELETE; + + hr = WcaDoDeferredAction(L"MessageQueuingExecuteUninstall", pwzExecuteActionData, iCost); + ExitOnFailure(hr, "Failed to schedule MessageQueuingExecuteUninstall"); + } + + hr = S_OK; + +LExit: + // clean up + MqiMessageQueueFreeList(&lstMessageQueues); + MqiMessageQueuePermissionFreeList(&lstMessageQueuePermissions); + + ReleaseStr(pwzRollbackActionData); + ReleaseStr(pwzExecuteActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ca/mqutilexec.cpp b/src/ca/mqutilexec.cpp new file mode 100644 index 00000000..a9c56e02 --- /dev/null +++ b/src/ca/mqutilexec.cpp @@ -0,0 +1,380 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct PCA_WELLKNOWN_SID +{ + LPCWSTR pwzName; + SID_IDENTIFIER_AUTHORITY iaIdentifierAuthority; + BYTE nSubAuthorityCount; + DWORD dwSubAuthority[8]; +}; + + +// well known SIDs + +PCA_WELLKNOWN_SID wsWellKnownSids[] = { + {L"\\Everyone", SECURITY_WORLD_SID_AUTHORITY, 1, {SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\Administrators", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0}}, + {L"\\LocalSystem", SECURITY_NT_AUTHORITY, 1, {SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\LocalService", SECURITY_NT_AUTHORITY, 1, {SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\NetworkService", SECURITY_NT_AUTHORITY, 1, {SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\AuthenticatedUser", SECURITY_NT_AUTHORITY, 1, {SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\Guests", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0}}, + {L"\\Users", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0}}, + {L"\\CREATOR OWNER", SECURITY_NT_AUTHORITY, 1, {SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0}}, + {NULL, SECURITY_NULL_SID_AUTHORITY, 0, {0, 0, 0, 0, 0, 0, 0, 0}} +}; + + +// prototypes for private helper functions + +static HRESULT CreateSidFromDomainRidPair( + PSID pDomainSid, + DWORD dwRid, + PSID* ppSid + ); +static HRESULT InitLsaUnicodeString( + PLSA_UNICODE_STRING plusStr, + LPCWSTR pwzStr, + DWORD dwLen + ); +static void FreeLsaUnicodeString( + PLSA_UNICODE_STRING plusStr + ); + + +// function definitions + +HRESULT PcaActionDataMessage( + DWORD cArgs, + ... + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hRec; + va_list args; + + // record + hRec = ::MsiCreateRecord(cArgs); + ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Failed to create record"); + + va_start(args, cArgs); + for (DWORD i = 1; i <= cArgs; i++) + { + LPCWSTR pwzArg = va_arg(args, WCHAR*); + if (pwzArg && *pwzArg) + { + er = ::MsiRecordSetStringW(hRec, i, pwzArg); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set record field string"); + } + } + va_end(args); + + // message + er = WcaProcessMessage(INSTALLMESSAGE_ACTIONDATA, hRec); + if (0 == er || IDOK == er || IDYES == er) + { + hr = S_OK; + } + else if (ERROR_INSTALL_USEREXIT == er || IDABORT == er || IDCANCEL == er) + { + WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit + hr = S_FALSE; + } + else + hr = E_UNEXPECTED; + +LExit: + return hr; +} + +HRESULT PcaAccountNameToSid( + LPCWSTR pwzAccountName, + PSID* ppSid + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + PSID pSid = NULL; + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + LSA_UNICODE_STRING lusName; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_SID pltsSid = NULL; + + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + ::ZeroMemory(&lusName, sizeof(lusName)); + + // identify well known SIDs + for (PCA_WELLKNOWN_SID* pWS = wsWellKnownSids; pWS->pwzName; pWS++) + { + if (0 == lstrcmpiW(pwzAccountName, pWS->pwzName)) + { + // allocate SID buffer + pSid = (PSID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, ::GetSidLengthRequired(pWS->nSubAuthorityCount)); + ExitOnNull(pSid, hr, E_OUTOFMEMORY, "Failed to allocate buffer for SID"); + + // initialize SID + ::InitializeSid(pSid, &pWS->iaIdentifierAuthority, pWS->nSubAuthorityCount); + + // copy sub autorities + for (DWORD i = 0; i < pWS->nSubAuthorityCount; i++) + *::GetSidSubAuthority(pSid, i) = pWS->dwSubAuthority[i]; + + break; + } + } + + // lookup name + if (!pSid) + { + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // create account name lsa unicode string + hr = InitLsaUnicodeString(&lusName, pwzAccountName, wcslen(pwzAccountName)); + ExitOnFailure(hr, "Failed to initialize account name string"); + + // lookup name + st = ::LsaLookupNames(lsahPolicy, 1, &lusName, &plrdsDomains, &pltsSid); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to lookup account names"); + + if (SidTypeDomain == pltsSid->Use) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Domain SIDs not supported"); + + // convert sid + hr = CreateSidFromDomainRidPair(plrdsDomains->Domains[pltsSid->DomainIndex].Sid, pltsSid->RelativeId, &pSid); + ExitOnFailure(hr, "Failed to convert SID"); + } + + *ppSid = pSid; + pSid = NULL; + + hr = S_OK; + +LExit: + // clean up + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltsSid) + ::LsaFreeMemory(pltsSid); + FreeLsaUnicodeString(&lusName); + + return hr; +} + +HRESULT PcaSidToAccountName( + PSID pSid, + LPWSTR* ppwzAccountName + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_NAME pltnName = NULL; + + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // lookup SID + st = ::LsaLookupSids(lsahPolicy, 1, &pSid, &plrdsDomains, &pltnName); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to lookup SID"); + + if (SidTypeDomain == pltnName->Use) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Domain SIDs not supported"); + + // format account name string + if (SidTypeWellKnownGroup != pltnName->Use) + { + PLSA_UNICODE_STRING plusDomain = &plrdsDomains->Domains[pltnName->DomainIndex].Name; + hr = StrAllocString(&pwzDomain, plusDomain->Buffer, plusDomain->Length / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate name string"); + } + + hr = StrAllocString(&pwzName, pltnName->Name.Buffer, pltnName->Name.Length / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate domain string"); + + hr = StrAllocFormatted(ppwzAccountName, L"%s\\%s", pwzDomain ? pwzDomain : L"", pwzName); + ExitOnFailure(hr, "Failed to format account name string"); + + hr = S_OK; + +LExit: + // clean up + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltnName) + ::LsaFreeMemory(pltnName); + + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} + +HRESULT PcaBuildAccountName( + LPCWSTR pwzDomain, + LPCWSTR pwzName, + LPWSTR* ppwzAccount + ) +{ + HRESULT hr = S_OK; + + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + ::ZeroMemory(wzComputerName, sizeof(wzComputerName)); + + // if domain is '.', get computer name + if (0 == lstrcmpW(pwzDomain, L".")) + { + DWORD dwSize = countof(wzComputerName); + if (!::GetComputerNameW(wzComputerName, &dwSize)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to get computer name"); + } + + // build account name + hr = StrAllocFormatted(ppwzAccount, L"%s\\%s", *wzComputerName ? wzComputerName : pwzDomain, pwzName); + ExitOnFailure(hr, "Failed to build domain user name"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT PcaGuidFromString( + LPCWSTR pwzGuid, + LPGUID pGuid + ) +{ + HRESULT hr = S_OK; + + int cch = 0; + + WCHAR wz[39]; + ::ZeroMemory(wz, sizeof(wz)); + + cch = lstrlenW(pwzGuid); + + if (38 == cch && L'{' == pwzGuid[0] && L'}' == pwzGuid[37]) + StringCchCopyW(wz, countof(wz), pwzGuid); + else if (36 == cch) + StringCchPrintfW(wz, countof(wz), L"{%s}", pwzGuid); + else + ExitFunction1(hr = E_INVALIDARG); + + hr = ::CLSIDFromString(wz, pGuid); + +LExit: + return hr; +} + + +// helper function definitions + +static HRESULT CreateSidFromDomainRidPair( + PSID pDomainSid, + DWORD dwRid, + PSID* ppSid + ) +{ + HRESULT hr = S_OK; + + PSID pSid = NULL; + + // get domain SID sub authority count + UCHAR ucSubAuthorityCount = *::GetSidSubAuthorityCount(pDomainSid); + + // allocate SID buffer + DWORD dwLengthRequired = ::GetSidLengthRequired(ucSubAuthorityCount + (UCHAR)1); + if (*ppSid) + { + SIZE_T ccb = ::HeapSize(::GetProcessHeap(), 0, *ppSid); + if (-1 == ccb) + ExitOnFailure(hr = E_FAIL, "Failed to get size of SID buffer"); + + if (ccb < dwLengthRequired) + { + pSid = (PSID)::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, *ppSid, dwLengthRequired); + ExitOnNull(pSid, hr, E_OUTOFMEMORY, "Failed to reallocate buffer for SID, len: %d", dwLengthRequired); + *ppSid = pSid; + } + } + else + { + *ppSid = (PSID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwLengthRequired); + ExitOnNull(*ppSid, hr, E_OUTOFMEMORY, "Failed to allocate buffer for SID, len: %d", dwLengthRequired); + } + + ::InitializeSid(*ppSid, ::GetSidIdentifierAuthority(pDomainSid), ucSubAuthorityCount + (UCHAR)1); + + // copy sub autorities + DWORD i = 0; + for (; i < ucSubAuthorityCount; i++) + *::GetSidSubAuthority(*ppSid, i) = *::GetSidSubAuthority(pDomainSid, i); + *::GetSidSubAuthority(*ppSid, i) = dwRid; + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT InitLsaUnicodeString( + PLSA_UNICODE_STRING plusStr, + LPCWSTR pwzStr, + DWORD dwLen + ) +{ + HRESULT hr = S_OK; + + plusStr->Length = (USHORT)dwLen * sizeof(WCHAR); + plusStr->MaximumLength = (USHORT)(dwLen + 1) * sizeof(WCHAR); + + plusStr->Buffer = (WCHAR*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR) * (dwLen + 1)); + ExitOnNull(plusStr->Buffer, hr, E_OUTOFMEMORY, "Failed to allocate account name string"); + + hr = StringCchCopyW(plusStr->Buffer, dwLen + 1, pwzStr); + ExitOnFailure(hr, "Failed to copy buffer"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeLsaUnicodeString( + PLSA_UNICODE_STRING plusStr + ) +{ + if (plusStr->Buffer) + ::HeapFree(::GetProcessHeap(), 0, plusStr->Buffer); +} diff --git a/src/ca/mqutilexec.h b/src/ca/mqutilexec.h new file mode 100644 index 00000000..d3dc17a1 --- /dev/null +++ b/src/ca/mqutilexec.h @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +HRESULT PcaActionDataMessage( + DWORD cArgs, + ... + ); +HRESULT PcaAccountNameToSid( + LPCWSTR pwzAccountName, + PSID* ppSid + ); +HRESULT PcaSidToAccountName( + PSID pSid, + LPWSTR* ppwzAccountName + ); +HRESULT PcaBuildAccountName( + LPCWSTR pwzDomain, + LPCWSTR pwzName, + LPWSTR* ppwzAccount + ); +HRESULT PcaGuidFromString( + LPCWSTR pwzGuid, + GUID* pGuid + ); diff --git a/src/ca/mqutilsched.cpp b/src/ca/mqutilsched.cpp new file mode 100644 index 00000000..4353a6d6 --- /dev/null +++ b/src/ca/mqutilsched.cpp @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +HRESULT PcaGuidToRegFormat( + LPWSTR pwzGuid, + LPWSTR pwzDest, + SIZE_T cchDest + ) +{ + HRESULT hr = S_OK; + + GUID guid = GUID_NULL; + int cch = 0; + + WCHAR wz[39]; + ::ZeroMemory(wz, sizeof(wz)); + + cch = lstrlenW(pwzGuid); + + if (38 == cch && L'{' == pwzGuid[0] && L'}' == pwzGuid[37]) + StringCchCopyW(wz, countof(wz), pwzGuid); + else if (36 == cch) + StringCchPrintfW(wz, countof(wz), L"{%s}", pwzGuid); + else + ExitFunction1(hr = E_INVALIDARG); + + // convert string to guid + hr = ::CLSIDFromString(wz, &guid); + ExitOnFailure(hr, "Failed to parse guid string"); + + // convert guid to string + if (0 == ::StringFromGUID2(guid, pwzDest, cchDest)) + ExitOnFailure(hr = E_FAIL, "Failed to convert guid to string"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/mqutilsched.h b/src/ca/mqutilsched.h new file mode 100644 index 00000000..e172257d --- /dev/null +++ b/src/ca/mqutilsched.h @@ -0,0 +1,9 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT PcaGuidToRegFormat( + LPWSTR pwzGuid, + LPWSTR pwzDest, + SIZE_T cchDest + ); -- cgit v1.2.3-55-g6feb