Skip to content

Commit

Permalink
Added /CopyMap
Browse files Browse the repository at this point in the history
- Added /CopyMap command that can be used to specify an input file with account mappings.
- Addresses various compiler warnings.
  • Loading branch information
NoMoreFood committed Jan 7, 2021
1 parent 4a7601a commit 4351411
Show file tree
Hide file tree
Showing 54 changed files with 482 additions and 57 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.
2 changes: 1 addition & 1 deletion Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <string>

// helper functions
VOID EnablePrivs();
VOID EnablePrivs() noexcept;
PSID GetSidFromName(const std::wstring & sAccountName);
std::wstring GetNameFromSid(const PSID tSid, bool * bMarkAsOrphan = nullptr);
std::wstring GetNameFromSidEx(const PSID tSid, bool * bMarkAsOrphan = nullptr);
Expand Down
6 changes: 3 additions & 3 deletions Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ std::wstring GenerateAccessMask(DWORD iCurrentMask)
return sMaskList;
}

VOID EnablePrivs()
VOID EnablePrivs() noexcept
{
// open the current token
HANDLE hToken = NULL;
Expand All @@ -265,7 +265,7 @@ VOID EnablePrivs()
return;
}

WCHAR * sPrivsToSet[] = { SE_RESTORE_NAME, SE_BACKUP_NAME,
const WCHAR * sPrivsToSet[] = { SE_RESTORE_NAME, SE_BACKUP_NAME,
SE_TAKE_OWNERSHIP_NAME, SE_CHANGE_NOTIFY_NAME };
for (auto& i : sPrivsToSet)
{
Expand Down Expand Up @@ -307,7 +307,7 @@ VOID EnablePrivs()

// convert the privilege name to a unicode string format
LSA_UNICODE_STRING sPrivilege;
sPrivilege.Buffer = i;
sPrivilege.Buffer = (PWSTR) i;
sPrivilege.Length = (USHORT)(wcslen(i) * sizeof(WCHAR));
sPrivilege.MaximumLength = (USHORT)((wcslen(i) + 1) * sizeof(WCHAR));

Expand Down
4 changes: 2 additions & 2 deletions Operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <regex>

bool Operation::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
bool Operation::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
{
// return immediately if acl is null
if (tCurrentAcl == nullptr) return false;
Expand Down Expand Up @@ -111,7 +111,7 @@ std::vector<std::wstring> Operation::SplitArgs(std::wstring sInput, const std::w
return { oFirst, oLast };
}

bool Operation::ProcessSidAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID & tCurrentSid, bool & bSidReplacement)
bool Operation::ProcessSidAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID & tCurrentSid, bool & bSidReplacement)
{
PSID tResultantSid;
const SidActionResult tResult = DetermineSid(sSdPart, tObjectEntry, tCurrentSid, tResultantSid);
Expand Down
12 changes: 7 additions & 5 deletions Operation.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once

// mute compatibility concerns
#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS

#include <windows.h>
#include <accctrl.h>
#include <aclapi.h>
Expand Down Expand Up @@ -60,8 +63,7 @@ typedef enum SidActionResult : char
{
Nothing = 0,
Replace = 1 << 0,
Remove = 1 << 1,
Add = 1 << 2
Remove = 1 << 1
}
SidActionResult;

Expand Down Expand Up @@ -90,9 +92,9 @@ class Operation
PSID DefaultSidWhenEmpty = nullptr;

virtual bool ProcessSdAction(std::wstring & sFileName, ObjectEntry & tObjectEntry, PSECURITY_DESCRIPTOR & tDescriptor, bool & bDescReplacement) { return false; }
virtual bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement);
virtual bool ProcessSidAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID & tCurrentSid, bool & bSidReplacement);
virtual SidActionResult DetermineSid(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID const tCurrentSid, PSID & tResultantSid) { return SidActionResult::Nothing; }
virtual bool ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement);
virtual bool ProcessSidAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID & tCurrentSid, bool & bSidReplacement);
virtual SidActionResult DetermineSid(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID const tCurrentSid, PSID & tResultantSid) { return SidActionResult::Nothing; }
virtual void ProcessObjectAction(ObjectEntry & tObjectEntry) { return; }

Operation(std::queue<std::wstring> & oArgList);
Expand Down
2 changes: 1 addition & 1 deletion OperationAddAccountIfMissing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ OperationAddAccountIfMissing::OperationAddAccountIfMissing(std::queue<std::wstri
AppliesToDacl = true;
}

bool OperationAddAccountIfMissing::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
bool OperationAddAccountIfMissing::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
{
return oDelegate->ProcessAclAction(sSdPart, tObjectEntry, tCurrentAcl, bAclReplacement);
}
2 changes: 1 addition & 1 deletion OperationAddAccountIfMissing.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class OperationAddAccountIfMissing : public Operation
public:

// overrides
bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;
bool ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;

// constructors
OperationAddAccountIfMissing(std::queue<std::wstring> & oArgList, const std::wstring & sCommand);
Expand Down
2 changes: 1 addition & 1 deletion OperationCanonicalizeAcls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ OperationCanonicalizeAcls::OperationCanonicalizeAcls(std::queue<std::wstring> &
AppliesToDacl = true;
}

bool OperationCanonicalizeAcls::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
bool OperationCanonicalizeAcls::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
{
// sanity check (null acl is considered valid)
if (tCurrentAcl == nullptr) return false;
Expand Down
2 changes: 1 addition & 1 deletion OperationCanonicalizeAcls.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class OperationCanonicalizeAcls : public Operation
public:

// overrides
bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;
bool ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;

// constructors
OperationCanonicalizeAcls(std::queue<std::wstring> & oArgList, const std::wstring & sCommand);
Expand Down
2 changes: 1 addition & 1 deletion OperationCheckCanonical.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ OperationCheckCanonical::OperationCheckCanonical(std::queue<std::wstring> & oArg
AppliesToDacl = true;
}

bool OperationCheckCanonical::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
bool OperationCheckCanonical::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
{
// sanity check (null acl is considered valid)
if (tCurrentAcl == nullptr) return false;
Expand Down
2 changes: 1 addition & 1 deletion OperationCheckCanonical.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class OperationCheckCanonical : public Operation
static bool IsAclCanonical(PACL & tCurrentAcl);

// overrides
bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;
bool ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;

// constructors
OperationCheckCanonical(std::queue<std::wstring> & oArgList, const std::wstring & sCommand);
Expand Down
2 changes: 1 addition & 1 deletion OperationCompact.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ OperationCompact::OperationCompact(std::queue<std::wstring> & oArgList, const st
AppliesToSacl = true;
}

bool OperationCompact::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
bool OperationCompact::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
{
// sanity check
if (tCurrentAcl == nullptr) return false;
Expand Down
2 changes: 1 addition & 1 deletion OperationCompact.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class OperationCompact : public Operation
public:

// overrides
bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;
bool ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;

// constructors
OperationCompact(std::queue<std::wstring> & oArgList, const std::wstring & sCommand);
Expand Down
2 changes: 1 addition & 1 deletion OperationCopyDomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ OperationCopyDomain::OperationCopyDomain(std::queue<std::wstring> & oArgList, co
if (sSubArgs.size() > 2) ProcessGranularTargetting(sSubArgs.at(2));
}

bool OperationCopyDomain::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
bool OperationCopyDomain::ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement)
{
// check on canonicalization status so if can error if the acl needs to be updated
const bool bAclIsCanonical = OperationCheckCanonical::IsAclCanonical(tCurrentAcl);
Expand Down
2 changes: 1 addition & 1 deletion OperationCopyDomain.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class OperationCopyDomain : public Operation
public:

// overrides
bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;
bool ProcessAclAction(const WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override;

// constructors
OperationCopyDomain(std::queue<std::wstring> & oArgList, const std::wstring & sCommand);
Expand Down
203 changes: 203 additions & 0 deletions OperationCopyMap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#include "OperationCopyMap.h"
#include "OperationCheckCanonical.h"
#include "InputOutput.h"
#include "Functions.h"

#include <fstream>
#include <iostream>
#include <locale>
#include <codecvt>

ClassFactory<OperationCopyMap> OperationCopyMap::RegisteredFactory(GetCommand());

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

// open the file
std::wifstream fFile(sSubArgs.at(0).c_str());

// adapt the stream to read windows unicode files
(void) fFile.imbue(std::locale(fFile.getloc(), new std::codecvt_utf8<wchar_t,
0x10ffff, std::consume_header>));

// read the file line-by-line
std::wstring sLine;
while (std::getline(fFile, sLine))
{
// parse the search and replace account which are separated by a ':' character
// also, sometimes a carriage return appears in the input stream so adding
// it here ensures it is stripped from the very end
std::vector<std::wstring> oLineItems = SplitArgs(sLine, L":|\r");

// verify the line contains at least two elements
if (oLineItems.size() != 2)
{
wprintf(L"ERROR: The replacement map line '%s' is invalid.", sLine.c_str());
exit(-1);
}

// verify search sid
const PSID tSearchSid = GetSidFromName(oLineItems.at(0));
if (tSearchSid == nullptr)
{
wprintf(L"ERROR: The map search value '%s' is invalid.", oLineItems.at(0).c_str());
exit(-1);
}

// verify replace sid
const PSID tReplaceSid = GetSidFromName(oLineItems.at(1));
if (tReplaceSid == nullptr)
{
wprintf(L"ERROR: The map replace value '%s' is invalid.", oLineItems.at(1).c_str());
exit(-1);
}

// get the reverse lookup for the search - since the ACE could contain a sid history sid
// we rely on doing a reverse lookup to normalize any accounts to update
std::wstring sSearchAccount = GetNameFromSidEx(tSearchSid);

// update the map
oCopyMap[sSearchAccount] = tReplaceSid;
}

// cleanup
fFile.close();

// flag this as being an ace-level action
AppliesToDacl = true;
AppliesToSacl = true;
}

bool OperationCopyMap::ProcessAclAction(const WCHAR* const sSdPart, ObjectEntry& tObjectEntry, PACL& tCurrentAcl, bool& bAclReplacement)
{
// check on canonicalization status so if can error if the acl needs to be updated
const bool bAclIsCanonical = OperationCheckCanonical::IsAclCanonical(tCurrentAcl);

// check explicit effective rights from sid (no groups)
bool bAclIsDirty = false;
if (tCurrentAcl != nullptr)
{
ACCESS_ACE* tAceDacl = FirstAce(tCurrentAcl);
for (LONG iEntry = 0; iEntry < tCurrentAcl->AceCount; tAceDacl =
(iEntry == -1) ? FirstAce(tCurrentAcl) : NextAce(tAceDacl), iEntry++)
{
// do not bother with inherited aces
if (IsInherited(tAceDacl)) continue;

// see if this ace matches a sid in the copy list
std::wstring sSourceAccountName = GetNameFromSidEx(&tAceDacl->Sid);
const auto oInteractor = oCopyMap.find(sSourceAccountName);
if (oInteractor == oCopyMap.end()) continue;

// translate the old sid to an account name
std::wstring sTargetAccountName = GetNameFromSidEx(oInteractor->second, nullptr);
if (sTargetAccountName.empty()) continue;

// record the status to report
std::wstring sInfoToReport = L"Copying '" + sSourceAccountName + L"' to '" + sTargetAccountName + L"'";

// determine access mode
ACCESS_MODE tMode = ACCESS_MODE::NOT_USED_ACCESS;
if (tAceDacl->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
{
tMode = GRANT_ACCESS;
}
else if (tAceDacl->Header.AceType == ACCESS_DENIED_ACE_TYPE)
{
tMode = DENY_ACCESS;
}
else if (tAceDacl->Header.AceType == SYSTEM_AUDIT_ACE_TYPE)
{
if (CheckBitSet(tAceDacl->Header.AceFlags, SUCCESSFUL_ACCESS_ACE_FLAG))
{
tMode = (ACCESS_MODE)(tMode | SET_AUDIT_SUCCESS);
}
if (CheckBitSet(tAceDacl->Header.AceFlags, FAILED_ACCESS_ACE_FLAG))
{
tMode = (ACCESS_MODE)(tMode | SET_AUDIT_FAILURE);
}
}
else
{
// unknown type; skipping
continue;
}

// since SetEntriesInAcl reacts poorly / unexpectedly in cases where the
// acl is not canonical, just error out and continue on
if (!bAclIsCanonical)
{
InputOutput::AddError(L"Could not copy account '" + sTargetAccountName +
L"' because access control list is not canonical.", sSdPart);
continue;
}

// create a structure to add the missing permissions
EXPLICIT_ACCESS tEa;
tEa.grfAccessPermissions = tAceDacl->Mask;
tEa.grfAccessMode = tMode;
tEa.grfInheritance = VALID_INHERIT_FLAGS & tAceDacl->Header.AceFlags;
tEa.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
tEa.Trustee.pMultipleTrustee = nullptr;
tEa.Trustee.ptstrName = (LPWSTR) oInteractor->second;
tEa.Trustee.TrusteeForm = TRUSTEE_IS_SID;
tEa.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;

// special case since SetEntriesInAcl does not handle setting both success
// and failure types together
PACL tNewDacl = nullptr;
DWORD iError = 0;
if (CheckBitSet(tEa.grfAccessMode, SET_AUDIT_SUCCESS) &&
CheckBitSet(tEa.grfAccessMode, SET_AUDIT_FAILURE))
{
PACL tNewDaclTmp = nullptr;
tEa.grfAccessMode = SET_AUDIT_SUCCESS;
iError = SetEntriesInAcl(1, &tEa, tCurrentAcl, &tNewDaclTmp);
tEa.grfAccessMode = SET_AUDIT_FAILURE;
if (iError == ERROR_SUCCESS) {
SetEntriesInAcl(1, &tEa, tNewDaclTmp, &tNewDacl);
LocalFree(tNewDaclTmp);
}
}
else
{
// merge the new trustee into the dacl
iError = SetEntriesInAcl(1, &tEa, tCurrentAcl, &tNewDacl);
}

// verify the new acl could be generated
if (iError != ERROR_SUCCESS || tNewDacl == nullptr)
{
//std::wstring sTargetAccountName = GetNameFromSid(tTargetAccountSid);
InputOutput::AddError(L"Could not copy '" + sTargetAccountName +
L"' to access control list (" + std::to_wstring(iError) + L").", sSdPart);
continue;
}

// see if the old and new acl match
if (tCurrentAcl->AclSize == tNewDacl->AclSize &&
memcmp(tCurrentAcl, tNewDacl, tCurrentAcl->AclSize) == 0)
{
// if acls match then no change was made and we do not need
// to mark this as dirty or restart the enumeration
LocalFree(tNewDacl);
}
else
{
// report status
InputOutput::AddInfo(sInfoToReport, sSdPart);

// cleanup the old dacl (if necessary) and assign our new active dacl
if (bAclReplacement) LocalFree(tCurrentAcl);
tCurrentAcl = tNewDacl;
bAclReplacement = true;
bAclIsDirty = true;
iEntry = -1;
}
}
}

return bAclIsDirty;
}
Loading

0 comments on commit 4351411

Please sign in to comment.