Skip to content

Commit

Permalink
Added /DomainPathsWithSite
Browse files Browse the repository at this point in the history
- Added /DomainPathsWithSite that allows specification of a Active Directory site regular expression to filter filter results.
- Added path deduplication for /Path and /SharePaths.
- Modified /DomainPaths to prefer dnsHostName attribute if it is defined.
  • Loading branch information
NoMoreFood committed Aug 1, 2022
1 parent 4699c1b commit 02f7da9
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 30 deletions.
Binary file modified Build/Release/x64/repacls.exe
Binary file not shown.
Binary file modified Build/Release/x86/repacls.exe
Binary file not shown.
Binary file modified Build/Repacls.zip
Binary file not shown.
1 change: 0 additions & 1 deletion Build/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ SET POWERSHELL=POWERSHELL.EXE -NoProfile -NonInteractive -NoLogo
FOR /F "USEBACKQ DELIMS=" %%X IN (`DIR /OD /B /S "%PX86%\Windows Kits\10\SIGNTOOL.exe" ^| FINDSTR x64`) DO SET SIGNTOOL="%%~X"

:: sign the main executables
%SIGNTOOL% sign /sha1 %CERT% /fd sha1 /tr %TSAURL% /td sha1 /d %LIBNAME% /du %LIBURL% "%BINDIR%\x86\*.exe" "%BINDIR%\x64\*.exe"
%SIGNTOOL% sign /sha1 %CERT% /as /fd sha256 /tr %TSAURL% /td sha256 /d %LIBNAME% /du %LIBURL% "%BINDIR%\x86\*.exe" "%BINDIR%\x64\*.exe"

:: zip up executatables
Expand Down
2 changes: 1 addition & 1 deletion ConcurrentQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ConcurrentQueue final
void WaitForEmptyQueues()
{
std::unique_lock<std::mutex> mlock(oQueueMutex);
oIsEmptyCondition.wait(mlock, [&]() noexcept { return iWaiters == 0; });
oIsEmptyCondition.wait(mlock, [&]() noexcept { return iWaiters == 0 && oQueue.empty(); });
}

void SetWaiterCounter(short iWaitCounters)
Expand Down
6 changes: 3 additions & 3 deletions Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ VOID BeginScan(Processor & oProcessor)
oObject->GetChildObjects(oEntry);
}
}));

// add all items to the queue
for (auto sPath : InputOutput::ScanPaths())
for (auto& sPath : InputOutput::ScanPaths())
{
oObject->GetBaseObject(sPath);
}

// wait for queue to be completely empty
oProcessor.GetQueue().WaitForEmptyQueues();

Expand Down
91 changes: 70 additions & 21 deletions OperationDomainPaths.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <LMShare.h>
#include <LMAPIbuf.h>
#include <Iads.h>
Expand All @@ -9,38 +10,57 @@
#pragma comment(lib,"activeds.lib")
#pragma comment(lib,"adsiid.lib")
#pragma comment(lib,"netapi32.lib")
#pragma comment(lib, "ws2_32.lib")

#include "OperationDomainPaths.h"

#include "OperationSharePaths.h"
#include "InputOutput.h"
#include "Helpers.h"

#include <regex>

ClassFactory<OperationDomainPaths> OperationDomainPaths::RegisteredFactory(GetCommand());
ClassFactory<OperationDomainPaths> OperationDomainPaths::RegisteredFactorySite(GetCommandSite());

OperationDomainPaths::OperationDomainPaths(std::queue<std::wstring> & oArgList, const std::wstring & sCommand) : Operation(oArgList)
OperationDomainPaths::OperationDomainPaths(std::queue<std::wstring>& oArgList, const std::wstring& sCommand) : Operation(oArgList)
{
// exit if there are not enough arguments to parse
const std::vector<std::wstring> sSubArgs = ProcessAndCheckArgs(1, oArgList);

// address site argument option
bool bFilterSite = _wcsicmp(sCommand.c_str(), GetCommandSite().c_str()) == 0;
std::vector<std::wstring> sSiteArgs;
if (bFilterSite) sSiteArgs = ProcessAndCheckArgs(1, oArgList);

// initialize com for this thread
InitThreadCom();

// initialize windows socket library
WSADATA tWinSockData;
if (WSAStartup(MAKEWORD(2, 2), &tWinSockData) != 0)
{
wprintf(L"Could not initialize Windows Sockets.\n");
std::exit(-1);
}

// find a domain controller for the specified domain
PDOMAIN_CONTROLLER_INFO tDomainControllerInfo;
PDOMAIN_CONTROLLER_INFO pDomainControllerInfo;
if (DsGetDcName(nullptr, sSubArgs.at(0).c_str(), nullptr, nullptr,
DS_IS_FLAT_NAME | DS_RETURN_DNS_NAME | DS_TRY_NEXTCLOSEST_SITE | DS_FORCE_REDISCOVERY,
&tDomainControllerInfo) != ERROR_SUCCESS)
&pDomainControllerInfo) != ERROR_SUCCESS)
{
wprintf(L"ERROR: Could not locate domain controller for domain '%s'\n", sSubArgs.at(0).c_str());
std::exit(-1);
}

// create a string
std::wstring sPath = std::wstring(L"LDAP://") + (wcsrchr(tDomainControllerInfo->DomainControllerName, '\\') + 1);
// grab the domain controller name
const std::wstring sDomainController = pDomainControllerInfo->DomainControllerName;
const std::wstring sDomainSuffix = pDomainControllerInfo->DomainName;
NetApiBufferFree(pDomainControllerInfo);

// grab the dns suffix for later use
std::wstring sSuffix = tDomainControllerInfo->DomainName;
NetApiBufferFree(tDomainControllerInfo);
// create a string
std::wstring sPath = std::wstring(L"LDAP://") + (wcsrchr(sDomainController.c_str(), '\\') + 1);

// bind to global catalog
CComPtr<IDirectorySearch> oSearch;
Expand Down Expand Up @@ -70,33 +90,62 @@ OperationDomainPaths::OperationDomainPaths(std::queue<std::wstring> & oArgList,
"(!(userAccountControl:1.2.840.113556.1.4.803:=8192))(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(msDS-isRODC=true)))";

// execute the search.
LPCWSTR sAttributes[] = { L"cn" };
LPCWSTR sAttributes[] = { L"dNSHostName", L"cn" };
ADS_SEARCH_HANDLE hSearch;
if (FAILED(oSearch->ExecuteSearch(sSearchFilter, (LPWSTR*) sAttributes, _countof(sAttributes), &hSearch)))
if (FAILED(oSearch->ExecuteSearch(sSearchFilter, (LPWSTR*)sAttributes, _countof(sAttributes), &hSearch)))
{
wprintf(L"ERROR: Could not execute search for domain '%s'\n", sSubArgs.at(0).c_str());
std::exit(-1);
}

// enumerate results
std::vector<std::wstring> sServers;
for (HRESULT hResult = oSearch->GetFirstRow(hSearch); hResult == S_OK; hResult = oSearch->GetNextRow(hSearch))
{
// get the data from the column
ADS_SEARCH_COLUMN oColumn;
if (FAILED(oSearch->GetColumn(hSearch, (LPWSTR) sAttributes[0], &oColumn)) ||
oColumn.dwADsType != ADSTYPE_CASE_IGNORE_STRING)
std::wstring sHostName;
ADS_SEARCH_COLUMN oColumn = {};
if (!FAILED(oSearch->GetColumn(hSearch, (LPWSTR)sAttributes[0], &oColumn)))
{
continue;
sHostName = std::wstring(oColumn.pADsValues->CaseIgnoreString);
}
else if (!FAILED(oSearch->GetColumn(hSearch, (LPWSTR)sAttributes[1], &oColumn)))
{
sHostName = std::wstring(oColumn.pADsValues->CaseIgnoreString) + L"." + sDomainSuffix;
}
else continue;

// cleanup ad search column
oSearch->FreeColumn(&oColumn);

// filter only allow servers in the specified site
if (bFilterSite)
{
// fetch the the address information
PADDRINFOW pAddressInfo;
if (GetAddrInfo(sHostName.c_str(), NULL, NULL, &pAddressInfo) != 0) continue;
SOCKET_ADDRESS tAddressArray[1];
tAddressArray[0].lpSockaddr = pAddressInfo->ai_addr;
tAddressArray[0].iSockaddrLength = (int)pAddressInfo->ai_addrlen;

// fetch the site name associated with the device
LPWSTR* sSiteName = NULL;
bool bMatchSite = false;
if (DsAddressToSiteNames(sDomainController.c_str(), 1, tAddressArray, &sSiteName) == NO_ERROR)
{
bMatchSite = std::regex_match(std::wstring(sSiteName[0]), std::wregex(sSiteArgs.at(0)));
NetApiBufferFree(sSiteName);
}

// cleanup
if (sSiteName != NULL) FreeAddrInfo(pAddressInfo);

// skip this name is not match
if (!bMatchSite) continue;
}

// add the server to our list
oArgList.push(L"/SharePaths");
oArgList.push(std::wstring(oColumn.pADsValues->CaseIgnoreString) + L"." + sSuffix +
((sSubArgs.size() == 2) ? (L":" + sSubArgs.at(1)) : L""));

// free the column.
oSearch->FreeColumn(&oColumn);
oArgList.push(sHostName + ((sSubArgs.size() == 2) ? (L":" + sSubArgs.at(1)) : L""));
}

// close search handle
Expand Down
2 changes: 2 additions & 0 deletions OperationDomainPaths.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ class OperationDomainPaths final : public Operation

// statics used by command registration utility
static std::wstring GetCommand() { return L"DomainPaths"; }
static std::wstring GetCommandSite() { return L"DomainPathsWithSite"; }
static ClassFactory<OperationDomainPaths> RegisteredFactory;
static ClassFactory<OperationDomainPaths> RegisteredFactorySite;

public:

Expand Down
4 changes: 4 additions & 0 deletions OperationHelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ or end of your command as to not confuse them with ordered parameters.
the shares of any particular computer cannot be read; if not specified
an error will be shown on the screen but processing will continue.
/DomainPathsWithSite <DomainName>[:StopOnError|<See /SharePaths>] [ADSite]
Same as /DomainPaths but will further filter to the specified Active
Directory site name of the device. ADSite can be a regular expression.
/Quiet
Hides all non-error output. This option will greatly enhance performance if
a large number of changes are being processed. Alternatively, it is
Expand Down
6 changes: 5 additions & 1 deletion OperationPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@ OperationPath::OperationPath(std::queue<std::wstring> & oArgList, const std::wst
const std::vector<std::wstring> sSubArgs = ProcessAndCheckArgs(1, oArgList, L"\\0");

// store off the argument
InputOutput::ScanPaths().push_back(sSubArgs.at(0));
if (std::find(InputOutput::ScanPaths().begin(),
InputOutput::ScanPaths().end(), sSubArgs.at(0)) == InputOutput::ScanPaths().end())
{
InputOutput::ScanPaths().push_back(sSubArgs.at(0));
}
};
7 changes: 4 additions & 3 deletions OperationSharePaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,11 @@ OperationSharePaths::OperationSharePaths(std::queue<std::wstring> & oArgList, co
}

// add it the resultant array if not found in another path
if (bAddToPathList)
const std::wstring sPathToAdd = L"\\\\" + sSubArgs.at(0) + L"\\" + oPathOuter->first;
if (bAddToPathList && std::find(InputOutput::ScanPaths().begin(),
InputOutput::ScanPaths().end(), sPathToAdd) == InputOutput::ScanPaths().end())
{
InputOutput::ScanPaths().push_back(
L"\\\\" + sSubArgs.at(0) + L"\\" + oPathOuter->first);
InputOutput::ScanPaths().push_back(sPathToAdd);
}
}
};
Binary file modified Resource.rc
Binary file not shown.

0 comments on commit 02f7da9

Please sign in to comment.