From 15346cba7d412ac9a775e0d435bcb894a7706c78 Mon Sep 17 00:00:00 2001 From: Bevan Weiss Date: Sat, 6 Jul 2024 21:03:57 +1000 Subject: [PATCH] Group Add/Remove working. Local group membership Add/Remove working, however with BUILTIN local system groups .NET doesn't appear to locate them as either groups nor basic security Principals. Still needs work to fix the test for nested groups. Ideally with some way to test for domain groups. Signed-off-by: Bevan Weiss --- src/ext/Util/ca/scacost.h | 2 + src/ext/Util/ca/scaexec.cpp | 454 ++++++++++-------- src/ext/Util/ca/scagroup.cpp | 412 ++++++++++++++-- src/ext/Util/ca/scagroup.h | 10 +- src/ext/Util/ca/scanet.cpp | 7 +- src/ext/Util/ca/scasched.cpp | 6 + src/ext/Util/ca/utilca.def | 4 + .../UtilExtensionFixture.cs | 8 +- src/ext/Util/wixext/UtilCompiler.cs | 7 +- src/ext/Util/wixext/UtilDecompiler.cs | 18 +- src/ext/Util/wixlib/UtilExtension.wxs | 7 +- .../Util/wixlib/UtilExtension_Platform.wxi | 20 +- src/ext/caDecor.h | 10 + src/ext/caDecor.wxi | 5 + .../burn/WixTestTools/UserGroupVerifier.cs | 24 +- .../ProductCommentFail/product_fail.wxs | 2 +- .../ProductFail/product_fail.wxs | 2 +- .../ProductNestedGroups/product.wxs | 6 +- .../UtilExtensionGroupTests.cs | 8 +- 19 files changed, 735 insertions(+), 277 deletions(-) diff --git a/src/ext/Util/ca/scacost.h b/src/ext/Util/ca/scacost.h index 978e40bc3..d10860057 100644 --- a/src/ext/Util/ca/scacost.h +++ b/src/ext/Util/ca/scacost.h @@ -11,6 +11,8 @@ const UINT COST_USER_ADD = 10000; const UINT COST_USER_DELETE = 10000; const UINT COST_GROUP_ADD = 10000; const UINT COST_GROUP_DELETE = 10000; +const UINT COST_GROUPMEMBERSHIP_ADD = 10000; +const UINT COST_GROUPMEMBERSHIP_DELETE = 10000; const UINT COST_PERFMONMANIFEST_REGISTER = 1000; const UINT COST_PERFMONMANIFEST_UNREGISTER = 1000; diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp index cc54150ba..136828aa3 100644 --- a/src/ext/Util/ca/scaexec.cpp +++ b/src/ext/Util/ca/scaexec.cpp @@ -293,146 +293,6 @@ static HRESULT RemoveUserFromGroup( return hr; } -static HRESULT AddGroupToGroup( - __in LPWSTR wzMember, - __in LPCWSTR wzMemberDomain, - __in LPCWSTR wzGroup, - __in LPCWSTR wzGroupDomain -) -{ - Assert(wzMember && *wzMember && wzMemberDomain && wzGroup && *wzGroup && wzGroupDomain); - - HRESULT hr = S_OK; - IADsGroup* pGroup = NULL; - BSTR bstrMember = NULL; - BSTR bstrGroup = NULL; - LPWSTR pwzMember = NULL; - LPWSTR pwzServerName = NULL; - LOCALGROUP_MEMBERS_INFO_3 lgmi {}; - - GetDomainServerName(wzGroupDomain, &pwzServerName); - - // Try adding it to the local group - if (wzMemberDomain) - { - hr = StrAllocFormatted(&pwzMember, L"%s\\%s", wzMemberDomain, wzMember); - ExitOnFailure(hr, "failed to allocate group domain string"); - } - - lgmi.lgrmi3_domainandname = (NULL == pwzMember ? wzMember : pwzMember); - NET_API_STATUS ui = ::NetLocalGroupAddMembers(pwzServerName, wzGroup, 3, reinterpret_cast(&lgmi), 1); - hr = HRESULT_FROM_WIN32(ui); - if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error - { - hr = S_OK; - } - - // - // If we failed, try active directory - // - if (FAILED(hr)) - { - WcaLog(LOGMSG_VERBOSE, "Failed to add group: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzMember, wzMemberDomain, wzGroup, wzGroupDomain, hr); - - hr = UserCreateADsPath(wzMemberDomain, wzMember, &bstrMember); - ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzMember, wzMemberDomain); - - hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup); - ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain); - - hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast(bstrGroup)); - - hr = pGroup->Add(bstrMember); - if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr)) - hr = S_OK; - - ExitOnFailure(hr, "Failed to add group %ls to group '%ls'.", reinterpret_cast(bstrMember), reinterpret_cast(bstrGroup)); - } - -LExit: - ReleaseStr(pwzServerName); - ReleaseStr(pwzMember); - ReleaseBSTR(bstrMember); - ReleaseBSTR(bstrGroup); - ReleaseObject(pGroup); - - return hr; -} - -static HRESULT RemoveGroupFromGroup( - __in LPWSTR wzMember, - __in LPCWSTR wzMemberDomain, - __in LPCWSTR wzGroup, - __in LPCWSTR wzGroupDomain -) -{ - Assert(wzMember && *wzMember && wzMemberDomain && wzGroup && *wzGroup && wzGroupDomain); - - HRESULT hr = S_OK; - IADsGroup* pGroup = NULL; - BSTR bstrMember = NULL; - BSTR bstrGroup = NULL; - LPWSTR pwzMember = NULL; - LPWSTR pwzServerName = NULL; - LOCALGROUP_MEMBERS_INFO_3 lgmi {}; - - GetDomainServerName(wzGroupDomain, &pwzServerName, DS_WRITABLE_REQUIRED); - - // Try removing it from the local group - if (wzMemberDomain) - { - hr = StrAllocFormatted(&pwzMember, L"%s\\%s", wzMemberDomain, wzMember); - ExitOnFailure(hr, "failed to allocate group domain string"); - } - - lgmi.lgrmi3_domainandname = (NULL == pwzMember ? wzMember : pwzMember); - NET_API_STATUS ui = ::NetLocalGroupDelMembers(pwzServerName, wzGroup, 3, reinterpret_cast(&lgmi), 1); - hr = HRESULT_FROM_WIN32(ui); - if (HRESULT_FROM_WIN32(ERROR_MEMBER_NOT_IN_ALIAS) == hr - || HRESULT_FROM_WIN32(NERR_GroupNotFound) == hr - || HRESULT_FROM_WIN32(ERROR_NO_SUCH_MEMBER) == hr) // if they're already not a member of the group, or the group doesn't exist, don't report an error - { - hr = S_OK; - } - - // - // If we failed, try active directory - // - if (FAILED(hr)) - { - WcaLog(LOGMSG_VERBOSE, "Failed to remove group: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzMember, wzMemberDomain, wzGroup, wzGroupDomain, hr); - - hr = UserCreateADsPath(wzMemberDomain, wzMember, &bstrMember); - ExitOnFailure(hr, "failed to create group ADsPath in order to remove group: %ls domain: %ls from a group", wzMember, wzMemberDomain); - - hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup); - ExitOnFailure(hr, "failed to create group ADsPath in order to remove group from group: %ls domain: %ls", wzGroup, wzGroupDomain); - - hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - if ((HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)) // if parent group not found, no need to remove membership from group - { - hr = S_OK; - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast(bstrGroup)); - - hr = pGroup->Remove(bstrMember); - if ((HRESULT_FROM_WIN32(ERROR_MEMBER_NOT_IN_ALIAS) == hr)) // if already not a member, no need to worry about error - hr = S_OK; - ExitOnFailure(hr, "Failed to remove group %ls from group '%ls'.", reinterpret_cast(bstrMember), reinterpret_cast(bstrGroup)); - } - -LExit: - ReleaseStr(pwzServerName); - ReleaseStr(pwzMember); - ReleaseBSTR(bstrMember); - ReleaseBSTR(bstrGroup); - ReleaseObject(pGroup); - - return hr; -} - static HRESULT GetUserHasRight( __in LSA_HANDLE hPolicy, __in PSID pUserSid, @@ -866,7 +726,6 @@ static HRESULT RemoveUserInternal( } static HRESULT RemoveGroupInternal( - LPWSTR wzGroupCaData, LPWSTR wzDomain, LPWSTR wzName, int iAttributes @@ -874,9 +733,6 @@ static HRESULT RemoveGroupInternal( { HRESULT hr = S_OK; - LPWSTR pwz = NULL; - LPWSTR pwzGroup = NULL; - LPWSTR pwzGroupDomain = NULL; LPWSTR pwzServerName = NULL; // @@ -895,41 +751,9 @@ static HRESULT RemoveGroupInternal( } ExitOnFailure(hr, "failed to delete group: %ls", wzName); } - else - { - // - // Remove the group from other groups - // - pwz = wzGroupCaData; - while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup))) - { - hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain); - if (FAILED(hr)) - { - WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup); - } - else - { - hr = RemoveGroupFromGroup(wzName, wzDomain, pwzGroup, pwzGroupDomain); - if (FAILED(hr)) - { - WcaLogError(hr, "failed to remove group: %ls from group %ls, continuing anyway.", wzName, pwzGroup); - } - } - } - - if (E_NOMOREITEMS == hr) // if there are no more items, all is well - { - hr = S_OK; - } - - ExitOnFailure(hr, "failed to get next group from which to remove group:%ls", wzName); - } LExit: ReleaseStr(pwzServerName); - ReleaseStr(pwzGroup); - ReleaseStr(pwzGroupDomain); return hr; } @@ -1414,6 +1238,7 @@ extern "C" UINT __stdcall RemoveUser( return WcaFinalize(er); } + /******************************************************************** CreateGroup - CUSTOM ACTION ENTRY POINT for creating groups @@ -1434,8 +1259,6 @@ extern "C" UINT __stdcall CreateGroup( LPWSTR pwzDomain = NULL; LPWSTR pwzComment = NULL; LPWSTR pwzScriptKey = NULL; - LPWSTR pwzGroup = NULL; - LPWSTR pwzGroupDomain = NULL; int iAttributes = 0; BOOL fInitializedCom = FALSE; @@ -1575,24 +1398,6 @@ extern "C" UINT __stdcall CreateGroup( } } MessageExitOnFailure(hr, msierrGRPFailedGroupCreate, "failed to create group: %ls", pwzName); - - // - // Add the groups to groups - // - while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup))) - { - hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain); - ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup); - - WcaLog(LOGMSG_STANDARD, "Adding group %ls\\%ls to group %ls\\%ls", pwzDomain, pwzName, pwzGroupDomain, pwzGroup); - hr = AddGroupToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain); - MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add group: %ls to group %ls", pwzName, pwzGroup); - } - if (E_NOMOREITEMS == hr) // if there are no more items, all is well - { - hr = S_OK; - } - ExitOnFailure(hr, "failed to get next group in which to include group: %ls", pwzName); } LExit: @@ -1608,8 +1413,6 @@ extern "C" UINT __stdcall CreateGroup( ReleaseStr(pwzDomain); ReleaseStr(pwzComment); ReleaseStr(pwzScriptKey); - ReleaseStr(pwzGroup); - ReleaseStr(pwzGroupDomain); if (fInitializedCom) { @@ -1726,7 +1529,7 @@ extern "C" UINT __stdcall CreateGroupRollback( } } - hr = RemoveGroupInternal(pwz, pwzDomain, pwzName, iAttributes); + hr = RemoveGroupInternal(pwzDomain, pwzName, iAttributes); LExit: WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE); @@ -1803,7 +1606,7 @@ extern "C" UINT __stdcall RemoveGroup( hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); ExitOnFailure(hr, "failed to read attributes from custom action data"); - hr = RemoveGroupInternal(pwz, pwzDomain, pwzName, iAttributes); + hr = RemoveGroupInternal(pwzDomain, pwzName, iAttributes); LExit: ReleaseStr(pwzData); @@ -1823,3 +1626,254 @@ extern "C" UINT __stdcall RemoveGroup( return WcaFinalize(er); } + +HRESULT AlterGroupMembership(bool remove, bool isRollback = false) +{ + HRESULT hr = S_OK; + NET_API_STATUS er = ERROR_SUCCESS; + + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzParentName = NULL; + LPWSTR pwzParentDomain = NULL; + LPWSTR pwzChildName = NULL; + LPWSTR pwzChildDomain = NULL; + int iAttributes = 0; + LPWSTR pwzChildFullName = NULL; + LPWSTR pwzServerName = NULL; + LOCALGROUP_MEMBERS_INFO_3 memberInfo3 = {}; + WCA_CASCRIPT_HANDLE phRollbackScript = NULL; + + if (isRollback) + { + // Get a CaScript key + hr = WcaCaScriptOpen(WCA_ACTION_NONE, WCA_CASCRIPT_ROLLBACK, FALSE, remove ? L"AddGroupMembershipRollback" : L"RemoveGroupMembershipRollback", &phRollbackScript); + hr = WcaCaScriptReadAsCustomActionData(phRollbackScript, &pwzData); + } + else + { + hr = WcaCaScriptCreate(WCA_ACTION_NONE, WCA_CASCRIPT_ROLLBACK, FALSE, remove ? L"RemoveGroupMembershipRollback" : L"AddGroupMembershipRollback", TRUE, &phRollbackScript); + hr = WcaGetProperty(L"CustomActionData", &pwzData); + } + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); + + // + // Read in the CustomActionData + // + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &pwzParentName); + ExitOnFailure(hr, "failed to read group name from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzParentDomain); + ExitOnFailure(hr, "failed to read domain from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzChildName); + ExitOnFailure(hr, "failed to read group comment from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzChildDomain); + ExitOnFailure(hr, "failed to read group comment from custom action data"); + + hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); + ExitOnFailure(hr, "failed to read attributes from custom action data"); + + hr = GetDomainServerName(pwzParentDomain, &pwzServerName, DS_WRITABLE_REQUIRED); + ExitOnFailure(hr, "failed to contact domain server %ls", pwzParentDomain); + + if (*pwzChildDomain) + { + StrAllocFormatted(&pwzChildFullName, L"%ls\\%ls", pwzChildDomain, pwzChildName); + } + else + { + StrAllocFormatted(&pwzChildFullName, L"%ls", pwzChildName); + } + memberInfo3.lgrmi3_domainandname = pwzChildFullName; + + if (remove) + { + er = ::NetLocalGroupDelMembers(pwzServerName, pwzParentName, 3, (LPBYTE)&memberInfo3, 1); + } + else + { + er = ::NetLocalGroupAddMembers(pwzServerName, pwzParentName, 3, (LPBYTE)&memberInfo3, 1); + } + hr = HRESULT_FROM_WIN32(er); + + if (S_OK == hr && !isRollback) + { + // we need to log rollback data, we can just use exactly the same data we used to do the initial action though + WcaCaScriptWriteString(phRollbackScript, pwzParentName); + WcaCaScriptWriteString(phRollbackScript, pwzParentDomain); + WcaCaScriptWriteString(phRollbackScript, pwzChildName); + WcaCaScriptWriteString(phRollbackScript, pwzChildDomain); + WcaCaScriptWriteNumber(phRollbackScript, iAttributes); + WcaCaScriptFlush(phRollbackScript); + WcaCaScriptClose(phRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE); + } + + if (remove) + { + if (HRESULT_FROM_WIN32(NERR_GroupNotFound) == hr + || HRESULT_FROM_WIN32(ERROR_NO_SUCH_MEMBER) == hr + || HRESULT_FROM_WIN32(ERROR_MEMBER_NOT_IN_ALIAS) == hr) + { + hr = S_OK; + } + } + else + { + if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) + { + hr = S_OK; + } + } + +LExit: + ReleaseStr(pwzData); + ReleaseStr(pwzParentName); + ReleaseStr(pwzParentDomain); + ReleaseStr(pwzChildName); + ReleaseStr(pwzChildDomain); + ReleaseStr(pwzChildFullName); + ReleaseStr(pwzServerName); + + if (SCAG_NON_VITAL & iAttributes) + { + return S_OK; + } + return hr; +} + +/******************************************************************** + AddGroupmembership - CUSTOM ACTION ENTRY POINT for creating groups + + Input: deferred CustomActionData - + ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes + * *****************************************************************/ +extern "C" UINT __stdcall AddGroupMembership( + __in MSIHANDLE hInstall +) +{ + //AssertSz(0, "Debug AddGroupMembership"); + + HRESULT hr = S_OK; + + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "AddGroupMembership"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = AlterGroupMembership(false, false); + +LExit: + if (fInitializedCom) + { + ::CoUninitialize(); + } + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILED : ERROR_SUCCESS); +} + +/******************************************************************** + AddGroupmembership - CUSTOM ACTION ENTRY POINT for creating groups + + Input: deferred CustomActionData - + ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes + * *****************************************************************/ +extern "C" UINT __stdcall AddGroupMembershipRollback( + __in MSIHANDLE hInstall +) +{ + //AssertSz(0, "Debug AddGroupMembershipRollback"); + + HRESULT hr = S_OK; + + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "AddGroupMembershipRollback"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = AlterGroupMembership(true, true); + +LExit: + if (fInitializedCom) + { + ::CoUninitialize(); + } + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILED : ERROR_SUCCESS); +} + +/******************************************************************** + RemoveGroupMembership - CUSTOM ACTION ENTRY POINT for creating groups + + Input: deferred CustomActionData - + ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes + * *****************************************************************/ +extern "C" UINT __stdcall RemoveGroupMembership( + __in MSIHANDLE hInstall +) +{ + //AssertSz(0, "Debug RemoveGroupMembership"); + + HRESULT hr = S_OK; + + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "RemoveGroupMembership"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = AlterGroupMembership(true, false); + +LExit: + if (fInitializedCom) + { + ::CoUninitialize(); + } + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILED : ERROR_SUCCESS); +} + +/******************************************************************** + RemoveGroupMembershipRollback - CUSTOM ACTION ENTRY POINT for creating groups + + Input: deferred CustomActionData - + ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes + * *****************************************************************/ +extern "C" UINT __stdcall RemoveGroupMembershipRollback( + __in MSIHANDLE hInstall +) +{ + //AssertSz(0, "Debug RemoveGroupMembershipRollback"); + + HRESULT hr = S_OK; + + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "RemoveGroupMembershipRollback"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = AlterGroupMembership(false, true); + +LExit: + if (fInitializedCom) + { + ::CoUninitialize(); + } + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILED : ERROR_SUCCESS); +} diff --git a/src/ext/Util/ca/scagroup.cpp b/src/ext/Util/ca/scagroup.cpp index c484c1d2c..3d2c3bebe 100644 --- a/src/ext/Util/ca/scagroup.cpp +++ b/src/ext/Util/ca/scagroup.cpp @@ -6,8 +6,11 @@ LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Group`=?"; enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain }; -LPCWSTR vcsGroupGroupQuery = L"SELECT `Parent_`, `Child_` FROM `Wix6GroupGroup` WHERE `Child_`=?"; -enum eGroupGroupQuery { vggqParent = 1, vggqChild }; +LPCWSTR vcsGroupParentsQuery = L"SELECT `Parent_`,`Component_`,`Name`,`Domain`,`Child_` FROM `Wix6GroupGroup`,`Wix4Group` WHERE `Wix6GroupGroup`.`Parent_`=`Wix4Group`.`Group` AND `Wix6GroupGroup`.`Child_`=?"; +enum eGroupParentsQuery { vgpqParent = 1, vgpqParentComponent, vgpqParentName, vgpqParentDomain, vgpqChild }; + +LPCWSTR vcsGroupChildrenQuery = L"SELECT `Parent_`,`Child_`,`Component_`,`Name`,`Domain` FROM `Wix6GroupGroup`,`Wix4Group` WHERE `Wix6GroupGroup`.`Child_`=`Wix4Group`.`Group` AND `Wix6GroupGroup`.`Parent_`=?"; +enum eGroupChildrenQuery { vgcqParent = 1, vgcqChild, vgcqChildComponent, vgcqChildName, vgcqChildDomain }; LPCWSTR vActionableGroupQuery = L"SELECT `Group`,`Component_`,`Name`,`Domain`,`Comment`,`Attributes` FROM `Wix4Group`,`Wix6Group` WHERE `Component_` IS NOT NULL AND `Group`=`Group_`"; enum eActionableGroupQuery { vagqGroup = 1, vagqComponent, vagqName, vagqDomain, vagqComment, vagqAttributes }; @@ -16,7 +19,6 @@ static HRESULT AddGroupToList( __inout SCA_GROUP** ppsgList ); - HRESULT __stdcall ScaGetGroup( __in LPCWSTR wzGroup, __out SCA_GROUP* pscag @@ -160,11 +162,149 @@ void ScaGroupFreeList( { psgDelete = psgList; psgList = psgList->psgNext; + ScaGroupFreeList(psgDelete->psgParents); + ScaGroupFreeList(psgDelete->psgChildren); MemFree(psgDelete); } } +HRESULT ScaGroupGetParents( + __inout SCA_GROUP* psg +) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + SCA_GROUP* psgParent = NULL; + PMSIHANDLE hView, hParamRec, hRec; + LPWSTR pwzTempStr = NULL; + + if (S_OK != WcaTableExists(L"Wix6GroupGroup")) + { + WcaLog(LOGMSG_VERBOSE, "Wix6GroupGroup Table does not exist, exiting"); + ExitFunction1(hr = S_FALSE); + } + + // setup the query parameter record + hParamRec = ::MsiCreateRecord(1); + hr = WcaSetRecordString(hParamRec, 1, psg->wzKey); + + // + // loop through all the groups + // + hr = WcaOpenView(vcsGroupParentsQuery, &hView); + ExitOnFailure(hr, "failed to open view on Wix6GroupGroup,Wix4Group table(s)"); + hr = WcaExecuteView(hView, hParamRec); + ExitOnFailure(hr, "failed to open view on Wix4Group,Wix6Group table(s)"); + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + hr = AddGroupToList(&psg->psgParents); + ExitOnFailure(hr, "failed to add group to list"); + + psgParent = psg->psgParents; + + if (::MsiRecordIsNull(hRec, vgcqChildComponent)) + { + psgParent->isInstalled = INSTALLSTATE_NOTUSED; + psgParent->isAction = INSTALLSTATE_NOTUSED; + } + else + { + hr = WcaGetRecordString(hRec, vgpqParentComponent, &pwzTempStr); + ExitOnFailure(hr, "failed to get Wix4Group.Component"); + wcsncpy_s(psgParent->wzComponent, pwzTempStr, MAX_DARWIN_KEY); + ReleaseNullStr(pwzTempStr); + + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), psgParent->wzComponent, &psgParent->isInstalled, &psgParent->isAction); + hr = HRESULT_FROM_WIN32(er); + ExitOnFailure(hr, "failed to get Component state for Wix4Group"); + } + + hr = WcaGetRecordString(hRec, vgpqParentName, &pwzTempStr); + ExitOnFailure(hr, "failed to get Wix4Group.Name"); + wcsncpy_s(psgParent->wzName, pwzTempStr, MAX_DARWIN_COLUMN); + ReleaseNullStr(pwzTempStr); + + + hr = WcaGetRecordString(hRec, vgpqParentDomain, &pwzTempStr); + ExitOnFailure(hr, "failed to get Wix4Group.Domain"); + wcsncpy_s(psgParent->wzDomain, pwzTempStr, MAX_DARWIN_COLUMN); + ReleaseNullStr(pwzTempStr); + } + +LExit: + ReleaseNullStr(pwzTempStr); + return hr; +} + +HRESULT ScaGroupGetChildren( + __inout SCA_GROUP* psg +) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + SCA_GROUP* psgChild = NULL; + PMSIHANDLE hView, hParamRec, hRec; + LPWSTR pwzTempStr = NULL; + + if (S_OK != WcaTableExists(L"Wix6GroupGroup")) + { + WcaLog(LOGMSG_VERBOSE, "Wix6GroupGroup Table does not exist, exiting"); + ExitFunction1(hr = S_FALSE); + } + + // setup the query parameter record + hParamRec = ::MsiCreateRecord(1); + hr = WcaSetRecordString(hParamRec, 1, psg->wzKey); + + // + // loop through all the groups + // + hr = WcaOpenView(vcsGroupChildrenQuery, &hView); + ExitOnFailure(hr, "failed to open view on Wix6GroupGroup,Wix4Group table(s)"); + hr = WcaExecuteView(hView, hParamRec); + ExitOnFailure(hr, "failed to open view on Wix4Group,Wix6Group table(s)"); + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + hr = AddGroupToList(&psg->psgChildren); + ExitOnFailure(hr, "failed to add group to list"); + + psgChild = psg->psgChildren; + + if (::MsiRecordIsNull(hRec, vgcqChildComponent)) + { + psgChild->isInstalled = INSTALLSTATE_NOTUSED; + psgChild->isAction = INSTALLSTATE_NOTUSED; + } + else + { + hr = WcaGetRecordString(hRec, vgcqChildComponent, &pwzTempStr); + ExitOnFailure(hr, "failed to get Wix4Group.Component"); + wcsncpy_s(psgChild->wzComponent, pwzTempStr, MAX_DARWIN_KEY); + ReleaseNullStr(pwzTempStr); + + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), psgChild->wzComponent, &psgChild->isInstalled, &psgChild->isAction); + hr = HRESULT_FROM_WIN32(er); + ExitOnFailure(hr, "failed to get Component state for Wix4Group"); + } + + hr = WcaGetRecordString(hRec, vgcqChildName, &pwzTempStr); + ExitOnFailure(hr, "failed to get Wix4Group.Name"); + wcsncpy_s(psgChild->wzName, pwzTempStr, MAX_DARWIN_COLUMN); + ReleaseNullStr(pwzTempStr); + + + hr = WcaGetRecordString(hRec, vgcqChildDomain, &pwzTempStr); + ExitOnFailure(hr, "failed to get Wix4Group.Domain"); + wcsncpy_s(psgChild->wzDomain, pwzTempStr, MAX_DARWIN_COLUMN); + ReleaseNullStr(pwzTempStr); + } + +LExit: + ReleaseNullStr(pwzTempStr); + return hr; +} + HRESULT ScaGroupRead( __out SCA_GROUP** ppsgList @@ -179,7 +319,7 @@ HRESULT ScaGroupRead( LPWSTR pwzData = NULL; - BOOL fGroupGroupExists = FALSE; + //BOOL fGroupGroupExists = FALSE; SCA_GROUP *psg = NULL; @@ -196,11 +336,6 @@ HRESULT ScaGroupRead( ExitFunction1(hr = S_FALSE); } - if (S_OK == WcaTableExists(L"Wix6GroupGroup")) - { - fGroupGroupExists = TRUE; - } - // // loop through all the groups // @@ -230,21 +365,26 @@ HRESULT ScaGroupRead( psg->isAction = isAction; hr = ::StringCchCopyW(psg->wzComponent, countof(psg->wzComponent), pwzData); ExitOnFailure(hr, "failed to copy component name: %ls", pwzData); + ReleaseNullStr(pwzData); hr = WcaGetRecordString(hRec, vagqGroup, &pwzData); ExitOnFailure(hr, "failed to get Wix4Group.Group"); hr = ::StringCchCopyW(psg->wzKey, countof(psg->wzKey), pwzData); ExitOnFailure(hr, "failed to copy group key: %ls", pwzData); + ReleaseNullStr(pwzData); hr = WcaGetRecordFormattedString(hRec, vagqName, &pwzData); ExitOnFailure(hr, "failed to get Wix4Group.Name"); hr = ::StringCchCopyW(psg->wzName, countof(psg->wzName), pwzData); ExitOnFailure(hr, "failed to copy group name: %ls", pwzData); + ReleaseNullStr(pwzData); hr = WcaGetRecordFormattedString(hRec, vagqDomain, &pwzData); ExitOnFailure(hr, "failed to get Wix4Group.Domain"); hr = ::StringCchCopyW(psg->wzDomain, countof(psg->wzDomain), pwzData); ExitOnFailure(hr, "failed to copy group domain: %ls", pwzData); + ReleaseNullStr(pwzData); + hr = WcaGetRecordFormattedString(hRec, vagqComment, &pwzData); ExitOnFailure(hr, "failed to get Wix6Group.Comment"); hr = ::StringCchCopyW(psg->wzComment, countof(psg->wzComment), pwzData); @@ -253,36 +393,9 @@ HRESULT ScaGroupRead( hr = WcaGetRecordInteger(hRec, vagqAttributes, &psg->iAttributes); ExitOnFailure(hr, "failed to get Wix6Group.Attributes"); - // Check if this group is to be added to any other groups - if (fGroupGroupExists) - { - hGroupRec = ::MsiCreateRecord(1); - hr = WcaSetRecordString(hGroupRec, 1, psg->wzKey); - ExitOnFailure(hr, "Failed to create group record for querying Wix6GroupGroup table"); - - hr = WcaOpenExecuteView(vcsGroupGroupQuery, &hGroupGroupView); - ExitOnFailure(hr, "Failed to open view on Wix6GroupGroup table for group %ls", psg->wzKey);/* - hr = WcaExecuteView(hGroupGroupView, hGroupRec); - ExitOnFailure(hr, "Failed to execute view on Wix6GroupGroup table for group: %ls", psg->wzKey);*/ + ScaGroupGetParents(psg); - while (S_OK == (hr = WcaFetchRecord(hGroupGroupView, &hRec))) - { - hr = WcaGetRecordString(hRec, vggqParent, &pwzData); - ExitOnFailure(hr, "failed to get Wix6GroupGroup.Parent"); - - hr = AddGroupToList(&(psg->psgGroups)); - ExitOnFailure(hr, "failed to add group to list"); - - hr = ScaGetGroup(pwzData, psg->psgGroups); - ExitOnFailure(hr, "failed to get information for group: %ls", pwzData); - } - - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "failed to enumerate selected rows from Wix4UserGroup table"); - } + ScaGroupGetChildren(psg); } } @@ -301,7 +414,6 @@ HRESULT ScaGroupRead( /* **************************************************************** ScaGroupExecute - Schedules group account creation or removal based on component state. - ******************************************************************/ HRESULT ScaGroupExecute( __in SCA_GROUP *psgList @@ -428,7 +540,7 @@ HRESULT ScaGroupExecute( hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData); ExitOnFailure(hr, "failed to add group attributes to rollback custom action data for group: %ls", psg->wzKey); - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateGroupRollback"), pwzRollbackData, COST_GROUP_DELETE); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"CreateGroupRollback"), pwzRollbackData, COST_GROUP_DELETE); ExitOnFailure(hr, "failed to schedule CreateGroupRollback"); } else @@ -441,7 +553,7 @@ HRESULT ScaGroupExecute( // // Schedule the creation now. // - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateGroup"), pwzActionData, COST_GROUP_ADD); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"CreateGroup"), pwzActionData, COST_GROUP_ADD); ExitOnFailure(hr, "failed to schedule CreateGroup"); } else if (((GROUP_EXISTS_YES == geGroupExists) @@ -457,7 +569,7 @@ HRESULT ScaGroupExecute( // // Note: We can't rollback the removal of a group which is why RemoveGroup is a commit // CustomAction. - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RemoveGroup"), pwzActionData, COST_GROUP_DELETE); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroup"), pwzActionData, COST_GROUP_DELETE); ExitOnFailure(hr, "failed to schedule RemoveGroup"); } @@ -501,3 +613,219 @@ static HRESULT AddGroupToList( LExit: return hr; } + +/* **************************************************************** +ScaGroupMembershipRemoveParentsExecute - Schedules group membership removal +based on parent/child component state +******************************************************************/ +HRESULT ScaGroupMembershipRemoveParentsExecute( + __in SCA_GROUP* psg +) +{ + HRESULT hr = S_OK; + LPWSTR pwzActionData = NULL; + + for (SCA_GROUP* psgp = psg->psgParents; psgp; psgp = psgp->psgNext) + { + Assert(psgp->wzName); + if (WcaIsUninstalling(psg->isInstalled, psg->isAction) + || WcaIsUninstalling(psgp->isInstalled, psgp->isAction)) + { + hr = WcaWriteStringToCaData(psgp->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group name to custom action data: %ls", psgp->wzName); + hr = WcaWriteStringToCaData(psgp->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgp->wzDomain); + hr = WcaWriteStringToCaData(psg->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group name to custom action data: %ls", psg->wzName); + hr = WcaWriteStringToCaData(psg->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain); + hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); + ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE); + + LExit: + ReleaseNullStr(pwzActionData); + if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) + { + return hr; + } + } + } + return S_OK; +} + +/* **************************************************************** +ScaGroupMembershipRemoveChildrenExecute - +******************************************************************/ +HRESULT ScaGroupMembershipRemoveChildrenExecute( + __in SCA_GROUP* psg +) +{ + HRESULT hr = S_OK; + LPWSTR pwzActionData = NULL; + + for (SCA_GROUP* psgc = psg->psgChildren; psgc; psgc = psgc->psgNext) + { + Assert(psgc->wzName); + if (WcaIsUninstalling(psg->isInstalled, psg->isAction) + || WcaIsUninstalling(psgc->isInstalled, psgc->isAction)) + { + hr = WcaWriteStringToCaData(psg->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group name to custom action data: %ls", psg->wzName); + hr = WcaWriteStringToCaData(psg->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psg->wzDomain); + hr = WcaWriteStringToCaData(psgc->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group name to custom action data: %ls", psgc->wzName); + hr = WcaWriteStringToCaData(psgc->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psgc->wzDomain); + hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); + ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE); + + LExit: + ReleaseNullStr(pwzActionData); + + if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) + { + return hr; + } + } + } + return S_OK; +} + +/* **************************************************************** +ScaGroupMembershipRemoveExecute - Schedules group membership removal +based on parent/child component state +******************************************************************/ +HRESULT ScaGroupMembershipRemoveExecute( + __in SCA_GROUP* psgList +) +{ + HRESULT hr = S_OK; + + // Loop through all the users to be configured. + for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext) + { + Assert(psg->wzName); + // first we loop through the Parents + hr = ScaGroupMembershipRemoveParentsExecute(psg); + ExitOnFailure(hr, "Failed to remove parent membership for vital group: %ls", psg->wzKey); + + // then through the Children + hr = ScaGroupMembershipRemoveChildrenExecute(psg); + ExitOnFailure(hr, "Failed to remove child membership for vital group: %ls", psg->wzKey); + } + +LExit: + return hr; +} + +/* **************************************************************** +ScaGroupMembershipAddParentsExecute - Schedules group membership removal +based on parent/child component state +******************************************************************/ +HRESULT ScaGroupMembershipAddParentsExecute( + __in SCA_GROUP* psg +) +{ + HRESULT hr = S_OK; + LPWSTR pwzActionData = NULL; + + for (SCA_GROUP* psgp = psg->psgParents; psgp; psgp = psgp->psgNext) + { + Assert(psgp->wzName); + if (WcaIsInstalling(psg->isInstalled, psg->isAction) + || WcaIsInstalling(psgp->isInstalled, psgp->isAction)) + { + hr = WcaWriteStringToCaData(psgp->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgp->wzName); + hr = WcaWriteStringToCaData(psgp->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgp->wzDomain); + hr = WcaWriteStringToCaData(psg->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group name to custom action data: %ls", psg->wzName); + hr = WcaWriteStringToCaData(psg->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain); + hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); + ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD); + + LExit: + ReleaseNullStr(pwzActionData); + + if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) + { + return hr; + } + } + } + return S_OK; +} + +/* **************************************************************** +ScaGroupMembershipAddChildrenExecute - Schedules group membership removal +based on parent/child component state +******************************************************************/ +HRESULT ScaGroupMembershipAddChildrenExecute( + __in SCA_GROUP* psg +) +{ + HRESULT hr = S_OK; + LPWSTR pwzActionData = NULL; + + // then through the Children + for (SCA_GROUP* psgc = psg->psgChildren; psgc; psgc = psgc->psgNext) + { + Assert(psgc->wzName); + if (WcaIsInstalling(psg->isInstalled, psg->isAction) + || WcaIsInstalling(psgc->isInstalled, psgc->isAction)) + { + hr = WcaWriteStringToCaData(psg->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group name to custom action data: %ls", psg->wzName); + hr = WcaWriteStringToCaData(psg->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain); + hr = WcaWriteStringToCaData(psgc->wzName, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgc->wzName); + hr = WcaWriteStringToCaData(psgc->wzDomain, &pwzActionData); + ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgc->wzDomain); + hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); + ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD); + + LExit: + ReleaseNullStr(pwzActionData); + if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) + { + return hr; + } + } + } + return S_OK; +} + +/* **************************************************************** +ScaGroupMembershipAddExecute - Schedules group membership addition +based on parent/child component state +******************************************************************/ +HRESULT ScaGroupMembershipAddExecute( + __in SCA_GROUP* psgList +) +{ + HRESULT hr = S_OK; + + // Loop through all the users to be configured. + for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext) + { + Assert(psg->wzName); + // first we loop through the Parents + hr = ScaGroupMembershipAddParentsExecute(psg); + ExitOnFailure(hr, "Failed to add parent membership for vital group: %ls", psg->wzKey); + + // then through the Children + hr = ScaGroupMembershipAddChildrenExecute(psg); + ExitOnFailure(hr, "Failed to add child membership for vital group: %ls", psg->wzKey); + } + +LExit: + return hr; +} diff --git a/src/ext/Util/ca/scagroup.h b/src/ext/Util/ca/scagroup.h index 8666d8521..e16d7a95e 100644 --- a/src/ext/Util/ca/scagroup.h +++ b/src/ext/Util/ca/scagroup.h @@ -21,11 +21,13 @@ struct SCA_GROUP WCHAR wzComment[MAX_DARWIN_COLUMN + 1]; INT iAttributes; - SCA_GROUP* psgGroups; + SCA_GROUP* psgParents; + SCA_GROUP* psgChildren; SCA_GROUP *psgNext; }; + // prototypes HRESULT __stdcall ScaGetGroup( __in LPCWSTR wzGroup, @@ -42,6 +44,12 @@ void ScaGroupFreeList( HRESULT ScaGroupRead( __inout SCA_GROUP** ppsgList ); +HRESULT ScaGroupMembershipRemoveExecute( + __in SCA_GROUP* psgList + ); +HRESULT ScaGroupMembershipAddExecute( + __in SCA_GROUP* psgList +); HRESULT ScaGroupExecute( __in SCA_GROUP*psgList ); diff --git a/src/ext/Util/ca/scanet.cpp b/src/ext/Util/ca/scanet.cpp index 11ee487de..1198280fa 100644 --- a/src/ext/Util/ca/scanet.cpp +++ b/src/ext/Util/ca/scanet.cpp @@ -1,7 +1,6 @@ // 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" -#include "scanet.h" HRESULT GetDomainServerName(LPCWSTR pwzDomain, LPWSTR* ppwzServerName, ULONG flags) @@ -25,17 +24,17 @@ HRESULT GetDomainServerName(LPCWSTR pwzDomain, LPWSTR* ppwzServerName, ULONG fla if ('\\' == *pDomainControllerInfo->DomainControllerName && '\\' == *pDomainControllerInfo->DomainControllerName + 1) { hr = StrAllocString(ppwzServerName, pDomainControllerInfo->DomainControllerName + 2, 0); - ExitOnFailure(hr, "failed to allocate memory for string"); } else { hr = StrAllocString(ppwzServerName, pDomainControllerInfo->DomainControllerName, 0); - ExitOnFailure(hr, "failed to allocate memory for string"); } + ExitOnFailure(hr, "failed to allocate memory for string"); } else { - StrAllocString(ppwzServerName, pwzDomain, 0); + hr = StrAllocString(ppwzServerName, pwzDomain, 0); + ExitOnFailure(hr, "failed to allocate memory for string"); hr = HRESULT_FROM_WIN32(er); ExitOnFailure(hr, "failed to contact domain %ls", pwzDomain); } diff --git a/src/ext/Util/ca/scasched.cpp b/src/ext/Util/ca/scasched.cpp index 1351fbfde..cef7fecba 100644 --- a/src/ext/Util/ca/scasched.cpp +++ b/src/ext/Util/ca/scasched.cpp @@ -153,9 +153,15 @@ extern "C" UINT __stdcall ConfigureGroups( hr = ScaGroupRead(&psgList); ExitOnFailure(hr, "failed to read Wix4Group,Wix6Group table(s)"); + hr = ScaGroupMembershipRemoveExecute(psgList); + ExitOnFailure(hr, "failed to remove Group Memberships") + hr = ScaGroupExecute(psgList); ExitOnFailure(hr, "failed to add/remove Group actions"); + hr = ScaGroupMembershipAddExecute(psgList); + ExitOnFailure(hr, "failed to add Group Memberships") + LExit: if (psgList) { diff --git a/src/ext/Util/ca/utilca.def b/src/ext/Util/ca/utilca.def index 18a19d12e..2aeb54f4c 100644 --- a/src/ext/Util/ca/utilca.def +++ b/src/ext/Util/ca/utilca.def @@ -46,6 +46,10 @@ EXPORTS CreateGroup CreateGroupRollback RemoveGroup + AddGroupMembership + AddGroupMembershipRollback + RemoveGroupMembership + RemoveGroupMembershipRollback CreateUser CreateUserRollback RemoveUser diff --git a/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs b/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs index d71dd8244..c9a184ab8 100644 --- a/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs +++ b/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs @@ -404,10 +404,10 @@ public void CanCreateUserGroupWithComment() WixAssert.CompareLineByLine(new[] { "Binary:Wix4UtilCA_X64\t[Binary data]", - "CustomAction:Wix4ConfigureGroups_X64\t1\tWix4UtilCA_X64\tConfigureGroups\t", - "CustomAction:Wix4CreateGroup_X64\t11265\tWix4UtilCA_X64\tCreateGroup\t", - "CustomAction:Wix4CreateGroupRollback_X64\t11521\tWix4UtilCA_X64\tCreateGroupRollback\t", - "CustomAction:Wix4RemoveGroup_X64\t11841\tWix4UtilCA_X64\tRemoveGroup\t", + "CustomAction:Wix6ConfigureGroups_X64\t1\tWix4UtilCA_X64\tConfigureGroups\t", + "CustomAction:Wix6CreateGroup_X64\t3073\tWix4UtilCA_X64\tCreateGroup\t", + "CustomAction:Wix6CreateGroupRollback_X64\t3329\tWix4UtilCA_X64\tCreateGroupRollback\t", + "CustomAction:Wix6RemoveGroup_X64\t3649\tWix4UtilCA_X64\tRemoveGroup\t", "Wix4Group:TEST_GROUP00\tComponent1\ttestName00\t", "Wix4Group:TEST_GROUP01\tComponent1\ttestName01\t", "Wix4Group:TEST_GROUP02\tComponent1\ttestName02\t", diff --git a/src/ext/Util/wixext/UtilCompiler.cs b/src/ext/Util/wixext/UtilCompiler.cs index aff7dd0da..4b1e43b52 100644 --- a/src/ext/Util/wixext/UtilCompiler.cs +++ b/src/ext/Util/wixext/UtilCompiler.cs @@ -1475,7 +1475,7 @@ private void ParseGroupElement(Intermediate intermediate, IntermediateSection se if (null != componentId) { - this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureGroups", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix6ConfigureGroups", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } foreach (var child in element.Elements()) @@ -1488,7 +1488,7 @@ private void ParseGroupElement(Intermediate intermediate, IntermediateSection se this.ParseGroupRefElement(intermediate, section, child, id.Id, groupType:true); break; default: - //this.ParseHelper.UnexpectedElement(element, child); + this.ParseHelper.UnexpectedElement(element, child); break; } } @@ -1561,6 +1561,9 @@ private void ParseGroupRefElement(Intermediate intermediate, IntermediateSection } else { + // Add reference to bring in fragment + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix6AddGroupMembership", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + section.AddSymbol(new GroupGroupSymbol(sourceLineNumbers) { ChildGroupRef = childId, diff --git a/src/ext/Util/wixext/UtilDecompiler.cs b/src/ext/Util/wixext/UtilDecompiler.cs index 53b75b8d8..a236ece91 100644 --- a/src/ext/Util/wixext/UtilDecompiler.cs +++ b/src/ext/Util/wixext/UtilDecompiler.cs @@ -478,14 +478,26 @@ private void DecompileGroupGroup6Table(Table table) { foreach (var row in table.Rows) { + var parentId = row.FieldAsString(0); + var parentExists = this.DecompilerHelper.TryGetIndexedElement("Group", parentId, out var parentGroup); + var childId = row.FieldAsString(1); - if (this.DecompilerHelper.TryGetIndexedElement("Group", childId, out var group)) + var childExists = this.DecompilerHelper.TryGetIndexedElement("Group", childId, out var childGroup); + + if (parentExists && childExists) { - group.Add(new XElement(UtilConstants.GroupRefName, new XAttribute("Id", row.FieldAsString(0)))); + childGroup.Add(new XElement(UtilConstants.GroupRefName, new XAttribute("Id", parentId))); } else { - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Parent_", childId, "Group")); + if(!parentExists) + { + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Parent_", parentId, "Group")); + } + if (!childExists) + { + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Child_", childId, "Group")); + } } } } diff --git a/src/ext/Util/wixlib/UtilExtension.wxs b/src/ext/Util/wixlib/UtilExtension.wxs index c812f73d4..f459d51af 100644 --- a/src/ext/Util/wixlib/UtilExtension.wxs +++ b/src/ext/Util/wixlib/UtilExtension.wxs @@ -17,11 +17,16 @@ - + + + + + + diff --git a/src/ext/Util/wixlib/UtilExtension_Platform.wxi b/src/ext/Util/wixlib/UtilExtension_Platform.wxi index df53c7d4b..71166d4fc 100644 --- a/src/ext/Util/wixlib/UtilExtension_Platform.wxi +++ b/src/ext/Util/wixlib/UtilExtension_Platform.wxi @@ -134,18 +134,24 @@ - - - - + + + - - + - + + + + + + + + + diff --git a/src/ext/caDecor.h b/src/ext/caDecor.h index 060032cf8..4a09e0759 100644 --- a/src/ext/caDecor.h +++ b/src/ext/caDecor.h @@ -21,3 +21,13 @@ #else #define CUSTOM_ACTION_DECORATION5(f) L"Wix5" f L"_X86" #endif + +#if defined(_M_ARM64) +#define CUSTOM_ACTION_DECORATION6(f) L"Wix6" f L"_A64" +#elif defined(_M_AMD64) +#define CUSTOM_ACTION_DECORATION6(f) L"Wix6" f L"_X64" +#elif defined(_M_ARM) +#define CUSTOM_ACTION_DECORATION6(f) L"Wix6" f L"_ARM" +#else +#define CUSTOM_ACTION_DECORATION6(f) L"Wix6" f L"_X86" +#endif diff --git a/src/ext/caDecor.wxi b/src/ext/caDecor.wxi index 256d7586b..236b21540 100644 --- a/src/ext/caDecor.wxi +++ b/src/ext/caDecor.wxi @@ -14,6 +14,11 @@ + + + + + diff --git a/src/test/burn/WixTestTools/UserGroupVerifier.cs b/src/test/burn/WixTestTools/UserGroupVerifier.cs index 2f874057b..52a1a6bfb 100644 --- a/src/test/burn/WixTestTools/UserGroupVerifier.cs +++ b/src/test/burn/WixTestTools/UserGroupVerifier.cs @@ -151,7 +151,7 @@ public static void VerifyIsNotMemberOf(string domainName, string memberName, par /// list of groups to check for membership private static void IsMemberOf(string domainName, string memberName, bool shouldBeMember, params string[] groupNames) { - GroupPrincipal group = GetGroup(domainName, memberName); + Principal group = GetPrincipal(domainName, memberName); Assert.False(null == group, String.Format("Group '{0}' was not found under domain '{1}'.", memberName, domainName)); bool missedAGroup = false; @@ -186,11 +186,29 @@ private static GroupPrincipal GetGroup(string domainName, string groupName) { if (String.IsNullOrEmpty(domainName)) { - return GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Machine), IdentityType.Name, groupName); + return GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Machine), groupName); } else { - return GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain,domainName), IdentityType.Name, groupName); + return GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain,domainName), groupName); + } + } + + /// + /// Returns the Principal object for a given name + /// + /// Domain name to look under, if Empty the LocalMachine is assumed as the domain + /// + /// Principal Object if found, or null other wise + private static Principal GetPrincipal(string domainName, string name) + { + if (String.IsNullOrEmpty(domainName)) + { + return Principal.FindByIdentity(new PrincipalContext(ContextType.Machine), name); + } + else + { + return Principal.FindByIdentity(new PrincipalContext(ContextType.Domain, domainName), name); } } } diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs index 29b908da0..4e70717f8 100644 --- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs +++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs @@ -8,7 +8,7 @@ - + diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs index fb35bc1e1..3013e5a02 100644 --- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs +++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs @@ -11,7 +11,7 @@ - + diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs index 191d605c0..15328cb32 100644 --- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs +++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs @@ -12,11 +12,11 @@ - + - + @@ -26,8 +26,6 @@ - - diff --git a/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs b/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs index 796c4ecdf..d7cf31682 100644 --- a/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs +++ b/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs @@ -256,11 +256,11 @@ public void CanNestGroups() productNestedGroups.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS); // Verify group nested membership - UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Administrators", new string[] { "testName1", "testName2" }); - UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Power Users", new string[] { "testName1" }); + UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Authenticated Users", new string[] { "testName1", "testName2" }); + UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Everyone", new string[] { "testName1" }); - UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Administrators", new string[] { "testName3" }); - UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Power Users", new string[] { "testName2", "testName3" }); + UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Authenticated Users", new string[] { "testName3" }); + UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Everyone", new string[] { "testName2", "testName3" }); // clean up UserGroupVerifier.DeleteLocalGroup("testName1");