diff --git a/Build/Release/x64/repacls.exe b/Build/Release/x64/repacls.exe index 729e2ae..f81afb8 100644 Binary files a/Build/Release/x64/repacls.exe and b/Build/Release/x64/repacls.exe differ diff --git a/Build/Release/x86/repacls.exe b/Build/Release/x86/repacls.exe index b22c33e..f40fce2 100644 Binary files a/Build/Release/x86/repacls.exe and b/Build/Release/x86/repacls.exe differ diff --git a/Build/Repacls.zip b/Build/Repacls.zip index 1ffd2c8..714f528 100644 Binary files a/Build/Repacls.zip and b/Build/Repacls.zip differ diff --git a/Operation.h b/Operation.h index ad243b1..4f115e7 100644 --- a/Operation.h +++ b/Operation.h @@ -21,7 +21,6 @@ typedef ACCESS_ACE *PACCESS_ACE; // macros to iterate through access control entries #define FirstAce(Acl) ((ACCESS_ACE *)((PUCHAR)(Acl) + sizeof(ACL))) #define NextAce(Ace) ((ACCESS_ACE *)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize)) -#define NextAceWithRestart(Acl,Ace,Restart) ((Restart) ? FirstAce(Acl) : NextAce(Ace)) // define our own version of sid length since its faster #define GetLengthSid(x) (sizeof(SID) + (((SID *) (x))->SubAuthorityCount - 1) * sizeof(((SID *) (x))->SubAuthority)) diff --git a/OperationCanonicalizeAcls.cpp b/OperationCanonicalizeAcls.cpp new file mode 100644 index 0000000..1779c03 --- /dev/null +++ b/OperationCanonicalizeAcls.cpp @@ -0,0 +1,69 @@ +#include "OperationCanonicalizeAcls.h" +#include "OperationCheckCanonical.h" +#include "DriverKitPartial.h" +#include "InputOutput.h" +#include "Functions.h" + +ClassFactory * OperationCanonicalizeAcls::RegisteredFactory = +new ClassFactory(GetCommand()); + +OperationCanonicalizeAcls::OperationCanonicalizeAcls(std::queue & oArgList) : Operation(oArgList) +{ + // flag this as being an ace-level action + AppliesToDacl = true; +} + +bool OperationCanonicalizeAcls::ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) +{ + // sanity check (null acl is considered valid) + if (tCurrentAcl == NULL) return false; + + // use the simpler algorithm to determine if correct + bool bHasProblems = false; + OperationCheckCanonical::AceOrder oOrderOverall = OperationCheckCanonical::Unspecified; + ACCESS_ACE * tCheckAce = FirstAce(tCurrentAcl); + for (ULONG iEntry = 0; iEntry < tCurrentAcl->AceCount; tCheckAce = NextAce(tCheckAce), iEntry++) + { + // determine the overall over type of this ace + OperationCheckCanonical::AceOrder oThisAceOrder = OperationCheckCanonical::DetermineAceOrder(tCheckAce); + + // make sure this order is not less then the current order + if (oThisAceOrder < oOrderOverall) break; + { + bHasProblems = true; + break; + } + + oOrderOverall = oThisAceOrder; + } + + // if no problem, then no need to perform a sort + if (!bHasProblems) + { + return false; + } + + BYTE tNewAclBuffer[MAXWORD]; + ACCESS_ACE * tNewAce = (ACCESS_ACE *) &tNewAclBuffer; + for (int iAceOrder = 0; iAceOrder < OperationCheckCanonical::MaxAceOrder; iAceOrder++) + { + ACCESS_ACE * tAce = FirstAce(tCurrentAcl); + for (ULONG iEntry = 0; iEntry < tCurrentAcl->AceCount; tAce = NextAce(tAce), iEntry++) + { + // determine the overall over type of this ace + OperationCheckCanonical::AceOrder oThisAceOrder = OperationCheckCanonical::DetermineAceOrder(tAce); + + // copy the ace if it matches the sequential order (explicit deny, explicit allow, ...) + if (iAceOrder == oThisAceOrder) + { + memcpy(tNewAce, tAce, tAce->Header.AceSize); + tNewAce = NextAce(tNewAce); + } + } + } + + // recopy the updated list back into the original dacl memory space + memcpy(FirstAce(tCurrentAcl), &tNewAclBuffer, (PBYTE) tNewAce - (PBYTE) &tNewAclBuffer); + InputOutput::AddInfo(L"Access control list was canonicalized", sSdPart); + return true; +} diff --git a/OperationCanonicalizeAcls.h b/OperationCanonicalizeAcls.h new file mode 100644 index 0000000..c5520e8 --- /dev/null +++ b/OperationCanonicalizeAcls.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Operation.h" + +class OperationCanonicalizeAcls : public Operation +{ +private: + + // statics used by command registration utility + static std::wstring GetCommand() { return L"CanonicalizeAcls"; } + static ClassFactory * RegisteredFactory; + +public: + + // overrides + bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override; + + // constructors + OperationCanonicalizeAcls(std::queue & oArgList); +}; + diff --git a/OperationCheckCanonical.cpp b/OperationCheckCanonical.cpp index 11471e3..0b66a4f 100644 --- a/OperationCheckCanonical.cpp +++ b/OperationCheckCanonical.cpp @@ -17,25 +17,12 @@ bool OperationCheckCanonical::ProcessAclAction(WCHAR * const sSdPart, ObjectEntr // sanity check (null acl is considered valid) if (tCurrentAcl == NULL) return false; - enum AceOrder : unsigned char - { - Unspecified = 0, - Explicit = 1 << 0, - Deny = 1 << 1, - Allow = 1 << 2, - Inherited = 1 << 3 - }; - - unsigned char oOrderOverall = Unspecified; + AceOrder oOrderOverall = Unspecified; ACCESS_ACE * tAce = FirstAce(tCurrentAcl); for (ULONG iEntry = 0; iEntry < tCurrentAcl->AceCount; tAce = NextAce(tAce), iEntry++) { // check inheritance bits - unsigned char oThisAceOrder = (IsInherited(tAce)) ? Inherited : Unspecified; - - // check allow/deny - oThisAceOrder |= (tAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ? Allow : Unspecified; - oThisAceOrder |= (tAce->Header.AceType == ACCESS_DENIED_ACE_TYPE) ? Deny : Unspecified; + AceOrder oThisAceOrder = DetermineAceOrder(tAce); // make sure this order is not less then the current order if (oThisAceOrder < oOrderOverall) @@ -49,3 +36,24 @@ bool OperationCheckCanonical::ProcessAclAction(WCHAR * const sSdPart, ObjectEntr // report the return false; } + +OperationCheckCanonical::AceOrder OperationCheckCanonical::DetermineAceOrder(ACCESS_ACE * tAce) +{ + // determine ace order + if (IsInherited(tAce)) + { + if (tAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) return InheritedAllow; + if (tAce->Header.AceType == ACCESS_ALLOWED_CALLBACK_ACE_TYPE) return InheritedAllow; + if (tAce->Header.AceType == ACCESS_DENIED_ACE_TYPE) return InheritedDeny; + if (tAce->Header.AceType == ACCESS_DENIED_CALLBACK_ACE_TYPE) return InheritedDeny; + } + else + { + if (tAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) return ExplicitAllow; + if (tAce->Header.AceType == ACCESS_ALLOWED_CALLBACK_ACE_TYPE) return ExplicitAllow; + if (tAce->Header.AceType == ACCESS_DENIED_ACE_TYPE) return ExplicitDeny; + if (tAce->Header.AceType == ACCESS_DENIED_CALLBACK_ACE_TYPE) return ExplicitDeny; + } + + return Unspecified; +} \ No newline at end of file diff --git a/OperationCheckCanonical.h b/OperationCheckCanonical.h index 8813e09..dc4d830 100644 --- a/OperationCheckCanonical.h +++ b/OperationCheckCanonical.h @@ -12,6 +12,20 @@ class OperationCheckCanonical : public Operation public: + // public enums + enum AceOrder : unsigned char + { + Unspecified = 0, + ExplicitDeny = 1, + ExplicitAllow = 2, + InheritedDeny = 3, + InheritedAllow = 4, + MaxAceOrder + }; + + // public functions + static AceOrder DetermineAceOrder(ACCESS_ACE * tAce); + // overrides bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement) override; diff --git a/OperationHelp.cpp b/OperationHelp.cpp index b81a912..72ebad9 100644 --- a/OperationHelp.cpp +++ b/OperationHelp.cpp @@ -167,6 +167,11 @@ Commands That Can Alter Security (When /WhatIf Is Not Present) std::wcout << LR"( +/CanonicalizeAcls + This command will look for out-of-order ACEs within the DACL and reorder + them to be canonical. Canonical order is as follows: explicit deny, explicit + allow, inherited deny, inherited allow. + /Compact This command will look for mergeable entries in the security descriptor and merge them. For example, running icacls.exe /grant Everyone:R diff --git a/Version.h b/Version.h index 2185ed7..bf3fe5e 100644 --- a/Version.h +++ b/Version.h @@ -1,4 +1,4 @@ #pragma once -#define VERSION_STRING "1.10.0.3" -#define VERSION_COMMA 1,10,0,3 +#define VERSION_STRING "1.11.0.0" +#define VERSION_COMMA 1,11,0,0 diff --git a/repacls.vcxproj b/repacls.vcxproj index 29a3a0d..e09ad7e 100644 --- a/repacls.vcxproj +++ b/repacls.vcxproj @@ -193,6 +193,7 @@ + @@ -230,6 +231,7 @@ + diff --git a/repacls.vcxproj.filters b/repacls.vcxproj.filters index 935635b..e0d8dd2 100644 --- a/repacls.vcxproj.filters +++ b/repacls.vcxproj.filters @@ -109,6 +109,9 @@ Source\Operations + + Source\Operations + @@ -234,6 +237,9 @@ Includes\Operations + + Includes\Operations +