From 48e6486d291cd1ee3ea243e55a3525e344a71c79 Mon Sep 17 00:00:00 2001 From: Bryan Berns Date: Tue, 9 Aug 2016 23:26:37 -0400 Subject: [PATCH] Added /DomainPaths --- Main.cpp | 10 +++- OperationDomainPaths.cpp | 107 +++++++++++++++++++++++++++++++++++++ OperationDomainPaths.h | 17 ++++++ OperationHelp.cpp | 2 +- OperationMigrateDomain.cpp | 3 ++ OperationRemoveOrphan.cpp | 2 +- OperationSharePaths.cpp | 93 +++++++++++++++++++++++++------- OperationThreads.cpp | 2 +- repacls.vcxproj | 2 + repacls.vcxproj.filters | 6 +++ 10 files changed, 219 insertions(+), 25 deletions(-) create mode 100644 OperationDomainPaths.cpp create mode 100644 OperationDomainPaths.h diff --git a/Main.cpp b/Main.cpp index b055b29..f1ca770 100644 --- a/Main.cpp +++ b/Main.cpp @@ -387,6 +387,11 @@ VOID BeginFileScan() int wmain(int iArgs, WCHAR * aArgs[]) { + // print standard header + wprintf(L"===============================================================================\n"); + wprintf(L"= Repacls Version %hs by Bryan Berns\n", VERSION_STRING); + wprintf(L"===============================================================================\n"); + // translate std::queue oArgList; for (int iArg = 1; iArg < iArgs; iArg++) @@ -447,9 +452,10 @@ int wmain(int iArgs, WCHAR * aArgs[]) // ensure we have permissions to all files EnablePrivs(); - // not general information + // note parameter information + wprintf(L"\n"); wprintf(L"===============================================================================\n"); - wprintf(L"= Repacls Version %hs by Bryan Berns\n", VERSION_STRING); + wprintf(L"= Initial Scan Details\n"); wprintf(L"===============================================================================\n"); for (std::vector::iterator sScanPath = InputOutput::ScanPaths().begin(); sScanPath != InputOutput::ScanPaths().end(); sScanPath++) diff --git a/OperationDomainPaths.cpp b/OperationDomainPaths.cpp new file mode 100644 index 0000000..0bf7fa4 --- /dev/null +++ b/OperationDomainPaths.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib,"activeds.lib") +#pragma comment(lib,"adsiid.lib") +#pragma comment(lib,"netapi32.lib") + +#include "OperationDomainPaths.h" +#include "OperationSharePaths.h" +#include "InputOutput.h" + +ClassFactory * OperationDomainPaths::RegisteredFactory = +new ClassFactory(GetCommand()); + +OperationDomainPaths::OperationDomainPaths(std::queue & oArgList) : Operation(oArgList) +{ + // exit if there are not enough arguments to part + std::vector sSubArgs = ProcessAndCheckArgs(1, oArgList); + + // initialize com only + static HRESULT hComInit = CoInitializeEx(NULL, 0); + + // find a domain controller for the specified domain + PDOMAIN_CONTROLLER_INFO tDomainControllerInfo; + if (DsGetDcName(NULL, sSubArgs[0].c_str(), NULL, NULL, + DS_IS_FLAT_NAME | DS_RETURN_DNS_NAME | DS_TRY_NEXTCLOSEST_SITE | DS_FORCE_REDISCOVERY, + &tDomainControllerInfo) != ERROR_SUCCESS) + { + wprintf(L"ERROR: Could not locate domain controller for domain '%s'\n", sSubArgs[0].c_str()); + exit(-1); + } + + // create a string + std::wstring sPath = std::wstring(L"LDAP://") + (wcsrchr(tDomainControllerInfo->DomainControllerName, '\\') + 1); + + // grab the dns suffix for later use + std::wstring sSuffix = tDomainControllerInfo->DomainName; + NetApiBufferFree(tDomainControllerInfo); + + // bind to global catalog + CComPtr oSearch; + if (FAILED(ADsOpenObject(sPath.c_str(), NULL, NULL, ADS_SECURE_AUTHENTICATION, + IID_IDirectorySearch, (void**)&oSearch))) + { + wprintf(L"ERROR: Could not establish search for domain '%s'\n", sSubArgs[0].c_str()); + exit(-1); + } + + // setup preferences to search entire tree + ADS_SEARCHPREF_INFO SearchPref; + SearchPref.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; + SearchPref.vValue.dwType = ADSTYPE_INTEGER; + SearchPref.vValue.Integer = ADS_SCOPE_SUBTREE; + + // set the search preference. + if (FAILED(oSearch->SetSearchPreference(&SearchPref, 1))) + { + wprintf(L"ERROR: Could not set search preference for domain '%s'\n", sSubArgs[0].c_str()); + exit(-1); + + } + + // create the search filter + WCHAR sSearchFilter[] = L"(&(objectCategory=computer)(|(operatingSystem=*server*)(operatingSystem=*ontap*)(operatingSystem=*netapp*))(!(userAccountControl:1.2.840.113556.1.4.803:=8192))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"; + + // execute the search. + LPWSTR sAttributes[] = { L"cn" }; + ADS_SEARCH_HANDLE hSearch; + if (FAILED(oSearch->ExecuteSearch(sSearchFilter, sAttributes, ARRAYSIZE(sAttributes), &hSearch))) + { + wprintf(L"ERROR: Could not execute search for domain '%s'\n", sSubArgs[0].c_str()); + exit(-1); + } + + // enumerate results + std::vector 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, sAttributes[0], &oColumn)) || + oColumn.dwADsType != ADSTYPE_CASE_IGNORE_STRING) + { + 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[1]) : L"")); + + // free the column. + oSearch->FreeColumn(&oColumn); + } + + // close search handle + if (oSearch->CloseSearchHandle(hSearch) != NULL) + { + wprintf(L"ERROR: Could not close search for domain '%s'\n", sSubArgs[0].c_str()); + exit(-1); + } +}; diff --git a/OperationDomainPaths.h b/OperationDomainPaths.h new file mode 100644 index 0000000..76170f0 --- /dev/null +++ b/OperationDomainPaths.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Operation.h" + +class OperationDomainPaths : public Operation +{ +private: + + // statics used by command registration utility + static std::wstring GetCommand() { return L"DomainPaths"; } + static ClassFactory * RegisteredFactory; + +public: + + // constructors + OperationDomainPaths(std::queue & oArgList); +}; \ No newline at end of file diff --git a/OperationHelp.cpp b/OperationHelp.cpp index c378118..0d743d6 100644 --- a/OperationHelp.cpp +++ b/OperationHelp.cpp @@ -63,7 +63,7 @@ or end of your command as to not confuse them with ordered parameters. of '5' is usually adequate, but can be increased if performing changes over a higher-latency connection. Since changes to a parent directory often affect the inherited security on children, the security of children - objects are always processed after the the security on their parent objects + objects are always processed after the security on their parent objects are fully processed. /WhatIf diff --git a/OperationMigrateDomain.cpp b/OperationMigrateDomain.cpp index 2bf97c9..f41abb0 100644 --- a/OperationMigrateDomain.cpp +++ b/OperationMigrateDomain.cpp @@ -63,6 +63,9 @@ SidActionResult OperationMigrateDomain::DetermineSid(WCHAR * const sSdPart, Obje std::wstring sTargetAccountName = sTargetDomain + (wcsstr(sSourceAccountName.c_str(), L"\\") + 1); PSID tTargetAccountSid = GetSidFromName(sTargetAccountName); + // do a reverse lookup to see if this might be a sid history item + if (GetNameFromSidEx(tTargetAccountSid) == sSourceAccountName) return SidActionResult::Nothing; + // stop processing if the account does not exist if (tTargetAccountSid == nullptr) return SidActionResult::Nothing; diff --git a/OperationRemoveOrphan.cpp b/OperationRemoveOrphan.cpp index 23f9511..131536a 100644 --- a/OperationRemoveOrphan.cpp +++ b/OperationRemoveOrphan.cpp @@ -51,7 +51,7 @@ SidActionResult OperationRemoveOrphan::DetermineSid(WCHAR * const sSdPart, Objec // see if the sid is unresolvable; if it is then this is not an orphan bool bIsOrphan = false; - GetNameFromSid(tCurrentSid, NULL); + GetNameFromSid(tCurrentSid, &bIsOrphan); if (!bIsOrphan) return SidActionResult::Nothing; // update the sid in the ace diff --git a/OperationSharePaths.cpp b/OperationSharePaths.cpp index 0919ac0..18bbb29 100644 --- a/OperationSharePaths.cpp +++ b/OperationSharePaths.cpp @@ -2,6 +2,8 @@ #include #include +#include + #pragma comment(lib, "netapi32.lib") #include "OperationSharePaths.h" @@ -16,65 +18,116 @@ OperationSharePaths::OperationSharePaths(std::queue & oArgList) : std::vector sSubArgs = ProcessAndCheckArgs(1, oArgList); // if extra arguments are specified, parse them + bool bStopOnErrors = false; bool bAdminOnly = false; bool bHiddenIncluded = false; if (sSubArgs.size() == 2) { - if (_wcsicmp(sSubArgs[1].c_str(), L"INCLUDEHIDDEN") == 0) - { - bHiddenIncluded = true; - } - else if (_wcsicmp(sSubArgs[1].c_str(), L"ADMINONLY") == 0) - { - bAdminOnly = true; - } - else + // further split the second arg into a command delimited list + std::wstring sArg = oArgList.front(); oArgList.pop(); + std::wregex oRegex(L","); + std::wsregex_token_iterator oFirst{ sSubArgs[1].begin(), sSubArgs[1].end(), oRegex, -1 }, oLast; + std::vector oShareArgs = { oFirst, oLast }; + + // enumerate list + for (std::vector::iterator sShareArg = oShareArgs.begin(); + sShareArg != oShareArgs.end(); sShareArg++) { - wprintf(L"ERROR: Unrecognized parameter '%s' for command '%s'\n", sSubArgs[1].c_str(), sSubArgs[0].c_str()); - exit(-1); + if (_wcsicmp((*sShareArg).c_str(), L"INCLUDEHIDDEN") == 0) + { + bHiddenIncluded = true; + } + else if (_wcsicmp((*sShareArg).c_str(), L"ADMINONLY") == 0) + { + bAdminOnly = true; + } + if (_wcsicmp((*sShareArg).c_str(), L"STOPONERROR") == 0) + { + bStopOnErrors = true; + } + else + { + wprintf(L"ERROR: Unrecognized parameter '%s' for command '%s'\n", sSubArgs[1].c_str(), sSubArgs[0].c_str()); + exit(-1); + } } } DWORD hResumeHandle = NULL; DWORD iReturn = 0; + std::map mPaths; do { - SHARE_INFO_502 * tInfo; + SHARE_INFO_2 * tInfo; DWORD iEntries = 0; DWORD iTotalEntries = 0; // enumerate file share - iReturn = NetShareEnum((LPWSTR)sSubArgs[0].c_str(), 502, (LPBYTE*)&tInfo, + iReturn = NetShareEnum((LPWSTR)sSubArgs[0].c_str(), 2, (LPBYTE*)&tInfo, MAX_PREFERRED_LENGTH, &iEntries, &iTotalEntries, &hResumeHandle); // check for unknown error if (iReturn != ERROR_SUCCESS && iReturn != ERROR_MORE_DATA) { wprintf(L"ERROR: Could not enumerate shares on '%s'\n", sSubArgs[0].c_str()); - exit(-1); + if (bStopOnErrors) exit(-1); else return; } // process entries for (DWORD iEntry = 0; iEntry < iEntries; iEntry++) { // skip non-disk shares (e.g, printers) - if ((tInfo[iEntry].shi502_type & STYPE_MASK) != STYPE_DISKTREE) continue; + if ((tInfo[iEntry].shi2_type & STYPE_MASK) != STYPE_DISKTREE) continue; // skip administrative share unless admin command was specified - if (bAdminOnly && !CheckBitSet(tInfo[iEntry].shi502_type, STYPE_SPECIAL) || - !bAdminOnly && CheckBitSet(tInfo[iEntry].shi502_type, STYPE_SPECIAL)) continue; + if (bAdminOnly && !CheckBitSet(tInfo[iEntry].shi2_type, STYPE_SPECIAL) || + !bAdminOnly && CheckBitSet(tInfo[iEntry].shi2_type, STYPE_SPECIAL)) continue; // skip hidden shares unless hidden command was specified - WCHAR * cEnd = (wcsrchr(tInfo[iEntry].shi502_netname, '$')); + WCHAR * cEnd = (wcsrchr(tInfo[iEntry].shi2_netname, '$')); if (!bAdminOnly && !bHiddenIncluded && (cEnd != NULL && *(cEnd + 1) == '\0')) continue; + // add a trailing path if the path does not have one + std::wstring sLocalPath = tInfo[iEntry].shi2_path; + if (sLocalPath.back() != L'\\') sLocalPath += L'\\'; + + // convert to uppercase + std::transform(sLocalPath.begin(), sLocalPath.end(), sLocalPath.begin(), ::toupper); + // add path to the share list - InputOutput::ScanPaths().push_back( - L"\\\\" + sSubArgs[0] + L"\\" + tInfo[iEntry].shi502_netname); + mPaths[tInfo[iEntry].shi2_netname] = sLocalPath; } // cleanup NetApiBufferFree(tInfo); } while (iReturn == ERROR_MORE_DATA); + + // enumerate the shares and make sure there are no duplicates + // or child that are contained within parent paths + for (std::map::const_iterator oPathOuter = mPaths.begin(); + oPathOuter != mPaths.end(); oPathOuter++) + { + bool bAddToPathList = true; + for (std::map::const_iterator oPathInner = oPathOuter; + oPathInner != mPaths.end(); oPathInner++) + { + // see if the path is a sub-path of another path + if (oPathInner->first != oPathOuter->first && + oPathOuter->second.find(oPathInner->second) != std::wstring::npos) + { + wprintf(L"NOTE: Share '%s' is included in '%s' on '%s'; skipping\n", + oPathOuter->first.c_str(), oPathInner->first.c_str(), sSubArgs[0].c_str()); + bAddToPathList = false; + break; + } + } + + // add it the resultant array if not found in another path + if (bAddToPathList) + { + InputOutput::ScanPaths().push_back( + L"\\\\" + sSubArgs[0] + L"\\" + oPathOuter->first); + } + } }; \ No newline at end of file diff --git a/OperationThreads.cpp b/OperationThreads.cpp index d626b1e..6f784c3 100644 --- a/OperationThreads.cpp +++ b/OperationThreads.cpp @@ -12,7 +12,7 @@ OperationThreads::OperationThreads(std::queue & oArgList) : Operat // store off the argument InputOutput::MaxThreads() = (short)_wtoi(sSubArgs[0].c_str()); - if (InputOutput::MaxThreads() == 0 || InputOutput::MaxThreads() > 32) + if (InputOutput::MaxThreads() == 0 || InputOutput::MaxThreads() > 50) { // complain wprintf(L"ERROR: Invalid number of threads specified for parameter '%s'.\n", GetCommand().c_str()); diff --git a/repacls.vcxproj b/repacls.vcxproj index 6e4f21a..e11adb6 100644 --- a/repacls.vcxproj +++ b/repacls.vcxproj @@ -180,6 +180,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/repacls.vcxproj.filters b/repacls.vcxproj.filters index 1e2e1ed..0a8afb7 100644 --- a/repacls.vcxproj.filters +++ b/repacls.vcxproj.filters @@ -85,6 +85,9 @@ Source\Operations + + Source\Operations + @@ -186,6 +189,9 @@ Includes\Operations + + Includes\Operations +