From 1c29f42d33c900dc1aa0f6a3b8679d879ac303d5 Mon Sep 17 00:00:00 2001 From: Bevan Weiss Date: Fri, 3 Jan 2025 15:06:15 +1100 Subject: [PATCH] Fix ups for Domain Group creation / removal. Signed-off-by: Bevan Weiss --- src/ext/Util/ca/precomp.h | 1 + src/ext/Util/ca/scaexec.cpp | 6 +++--- src/ext/Util/ca/scagroup.cpp | 17 +++++++++++++++-- src/ext/Util/ca/scanet.cpp | 9 +++++++-- src/ext/Util/ca/scanet.h | 14 +++++++++++++- .../burn/WixTestTools/RuntimeFactAttribute.cs | 4 ++-- .../UtilExtensionGroupTests.cs | 6 ++++-- 7 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/ext/Util/ca/precomp.h b/src/ext/Util/ca/precomp.h index 9456c6916..0d7cae183 100644 --- a/src/ext/Util/ca/precomp.h +++ b/src/ext/Util/ca/precomp.h @@ -51,5 +51,6 @@ #include "scasmb.h" #include "scasmbexec.h" #include "utilca.h" +#include "scanet.h" #include "..\..\caDecor.h" diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp index a2ecdaab8..ee3c164f3 100644 --- a/src/ext/Util/ca/scaexec.cpp +++ b/src/ext/Util/ca/scaexec.cpp @@ -716,7 +716,7 @@ static HRESULT RemoveGroupInternal( // if (!(SCAG_DONT_CREATE_GROUP & iAttributes)) { - hr = GetDomainFromServerName(&pwzServerName, wzDomain, DS_WRITABLE_REQUIRED); + hr = GetDomainServerName(wzDomain, &pwzServerName, DS_WRITABLE_REQUIRED); NET_API_STATUS er = ::NetLocalGroupDel(pwzServerName, wzName); hr = HRESULT_FROM_WIN32(er); @@ -1284,7 +1284,7 @@ extern "C" UINT __stdcall CreateGroup( if (!(SCAG_DONT_CREATE_GROUP & iAttributes)) { - hr = GetDomainFromServerName(&pwzServerName, pwzDomain, DS_WRITABLE_REQUIRED); + hr = GetDomainServerName(pwzDomain, &pwzServerName, DS_WRITABLE_REQUIRED); ExitOnFailure(hr, "failed to find writable server for domain %ls.", pwzDomain); // Set the group's comment @@ -1680,7 +1680,7 @@ HRESULT AlterGroupMembership(BOOL fRemove, BOOL fIsRollback) } - hr = GetDomainFromServerName(&pwzServerName, pwzParentDomain, DS_WRITABLE_REQUIRED); + hr = GetDomainServerName(pwzParentDomain, &pwzServerName, DS_WRITABLE_REQUIRED); ExitOnFailure(hr, "failed to obtain writable server for domain %ls", pwzParentDomain); if (*pwzChildDomain) diff --git a/src/ext/Util/ca/scagroup.cpp b/src/ext/Util/ca/scagroup.cpp index bab438eaa..666100b00 100644 --- a/src/ext/Util/ca/scagroup.cpp +++ b/src/ext/Util/ca/scagroup.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" LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Group`=?"; enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain }; @@ -458,7 +457,20 @@ HRESULT ScaGroupExecute( // and removing groups. Note: MSDN says that it is safe to call these APIs from any // user, so we should be safe calling it during immediate mode. - hr = GetDomainServerName(psg->wzDomain, &pwzServerName); + hr = GetDomainServerName(psg->wzDomain, &pwzServerName, 0); + if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN) == hr) + { + if (SCAG_NON_VITAL & psg->iAttributes) + { + WcaLog(LOGMSG_VERBOSE, "Domain does not exist for non-vital group: %ls\\%ls - continuing", psg->wzDomain, psg->wzName); + hr = S_OK; + goto ExitCurrentGroup; + } + else + { + ExitOnFailure(hr, "Domain does not exist for vital group: %ls\\%ls - aborting", psg->wzDomain, psg->wzName); + } + } er = ::NetLocalGroupGetInfo(pwzServerName, psg->wzName, 0, reinterpret_cast(&pGroupInfo)); if (NERR_Success == er) @@ -597,6 +609,7 @@ HRESULT ScaGroupExecute( ExitOnFailure(hr, "failed to schedule RemoveGroup"); } + ExitCurrentGroup: ReleaseNullStr(pwzScriptKey); ReleaseNullStr(pwzActionData); ReleaseNullStr(pwzRollbackData); diff --git a/src/ext/Util/ca/scanet.cpp b/src/ext/Util/ca/scanet.cpp index a8ad03169..ad6c8b01d 100644 --- a/src/ext/Util/ca/scanet.cpp +++ b/src/ext/Util/ca/scanet.cpp @@ -8,8 +8,13 @@ HRESULT GetDomainServerName(LPCWSTR pwzDomain, LPWSTR* ppwzServerName, ULONG fla DWORD er = ERROR_SUCCESS; PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; HRESULT hr = S_OK; + WCHAR pwzComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD cchComputerName = countof(pwzComputerName); - if (pwzDomain && *pwzDomain) + hr = ::GetComputerNameW(pwzComputerName, &cchComputerName); + ExitOnFailure(hr, "failed to obtain computer name"); + + if (pwzDomain && *pwzDomain && 0!=lstrcmpiW(pwzComputerName, pwzDomain) && 0!=lstrcmpiW(L".", pwzDomain)) { er = ::DsGetDcNameW(NULL, pwzDomain, NULL, NULL, flags, &pDomainControllerInfo); if (RPC_S_SERVER_UNAVAILABLE == er) @@ -21,7 +26,7 @@ HRESULT GetDomainServerName(LPCWSTR pwzDomain, LPWSTR* ppwzServerName, ULONG fla if (ERROR_SUCCESS == er && pDomainControllerInfo->DomainControllerName) { // Skip the \\ prefix if present. - if ('\\' == *pDomainControllerInfo->DomainControllerName && '\\' == *pDomainControllerInfo->DomainControllerName + 1) + if ('\\' == *pDomainControllerInfo->DomainControllerName && '\\' == *(pDomainControllerInfo->DomainControllerName + 1) ) { hr = StrAllocString(ppwzServerName, pDomainControllerInfo->DomainControllerName + 2, 0); } diff --git a/src/ext/Util/ca/scanet.h b/src/ext/Util/ca/scanet.h index 1fee61f82..efe6a408e 100644 --- a/src/ext/Util/ca/scanet.h +++ b/src/ext/Util/ca/scanet.h @@ -1,4 +1,16 @@ #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 GetDomainServerName(LPCWSTR pwzDomain, LPWSTR* ppwzServerName, ULONG flags = 0); + +/** + * Locates a domain controller (server name) for a given input domain. + * Flags can be provided where required (as per those for DsGetDcName) for a specific server to be returned. + * NOTE: Where the domain provided is identical to the local machine, this function will return NULL, such that the + * result can be provided directly to NetUserAdd or similar functions. + * + * @param pwzDomain Pointer to the domain name to be queried + * @param ppwzServerName Pointer to the server name to be returned + * @param flags Flags to be used in the DsGetDcName call(s) + * @return HRESULT to indicate if an error was encountered + */ +HRESULT GetDomainServerName(LPCWSTR pwzDomain, LPWSTR* ppwzServerName, ULONG flags); diff --git a/src/test/burn/WixTestTools/RuntimeFactAttribute.cs b/src/test/burn/WixTestTools/RuntimeFactAttribute.cs index d7f56f708..2644a1cc0 100644 --- a/src/test/burn/WixTestTools/RuntimeFactAttribute.cs +++ b/src/test/burn/WixTestTools/RuntimeFactAttribute.cs @@ -47,6 +47,8 @@ static RuntimeFactAttribute() var domainTestsEnabledString = Environment.GetEnvironmentVariable(RequiredDomainEnvironmentVariableName); RuntimeDomainTestsEnabled = Boolean.TryParse(domainTestsEnabledString, out var domainTestsEnabled) && domainTestsEnabled; + + RunningOnWindowsServer = IsWindowsServer(); } public bool DomainRequired @@ -63,8 +65,6 @@ public bool DomainRequired this.Skip = $"These tests require the test host to be running as a domain member ({(RunningInDomain ? "passed" : "failed")}). These tests affect both MACHINE AND DOMAIN state. To accept the consequences, set the {RequiredDomainEnvironmentVariableName} environment variable to true ({(RuntimeDomainTestsEnabled ? "passed" : "failed")})."; } } - - RunningOnWindowsServer = IsWindowsServer(); } private bool _RequireWindowsServer; diff --git a/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs b/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs index cee357a6b..e379047d0 100644 --- a/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs +++ b/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs @@ -151,12 +151,14 @@ public void FailsIfNonDomainGroupExists() [RuntimeFact] public void FailsIfRestrictedDomain() { + var testDomain = "DOESNOTEXIST"; + var testGroup = "testName1"; var productRestrictedDomain = this.CreatePackageInstaller("ProductRestrictedDomain"); - string logFile = productRestrictedDomain.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "TESTDOMAIN=DOESNOTEXIST"); + string logFile = productRestrictedDomain.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, $"TESTDOMAIN={testDomain}"); // Verify expected error message in the log file - Assert.True(LogVerifier.MessageInLogFile(logFile, "CreateGroup: Error 0x8007054b: failed to find Domain DOESNOTEXIST.")); + Assert.True(LogVerifier.MessageInLogFile(logFile, $"ConfigureGroups: Error 0x8007054b: Domain does not exist for vital group: {testDomain}\\{testGroup} - aborting")); } // Verify that a group can be created with a group comment