mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(test-impact): accept the owner-only DACL as Windows stores it
The pinned tree requires its private parent to be owner-only: a protected
DACL, the current user as owner, one full-access grant to that user. It
accepted only the single-entry encoding (OI|CI). cbm_mkdir_p stamps a fresh
directory with exactly that grant, but written with generic rights, and
Windows stores it as an effective entry plus an inherit-only one
(icacls: user:(F) and user:(OI)(CI)(IO)(F)). The work directory production
creates was therefore refused ("pinning HEAD failed: protected owner-only
DACL policy mismatch"), and every Windows selection ran everything.
Both encodings are now accepted: one or two entries, each an allow entry for
the current user with full access and only inheritance flags, at least one
applying to the object itself. Any other principal, deny entry, partial
mask or flag is still refused.
tree_e_windows_split_owner_dacl_is_owner_only creates the parent with
cbm_mkdir_p and pins on it (red before this change), pins on the split form
written explicitly, and is refused once the inherit-only entry names Everyone.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
2cbd25dabf
commit
4f709c2707
2 files changed
+104
-18
No files matched your search
@@ -59,6 +59,33 @@ bool tpt_win_security(tpt_context *c) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* One DACL entry of the owner-only policy: an allow entry for the current
|
||||
* user with full access and no flag but inheritance; 1 yes, 0 no, -1 broken. */
|
||||
static int tpt_owner_ace(PACL dacl, DWORD index, PSID sid, bool *effective) {
|
||||
void *raw = NULL;
|
||||
if (!GetAce(dacl, index, &raw))
|
||||
return -1;
|
||||
ACCESS_ALLOWED_ACE *ace = raw;
|
||||
if (ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE)
|
||||
return 0;
|
||||
size_t prefix = offsetof(ACCESS_ALLOWED_ACE, SidStart);
|
||||
if (ace->Header.AceSize < prefix + 8)
|
||||
return -1;
|
||||
BYTE inherit = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
||||
if ((ace->Header.AceFlags & ~inherit) ||
|
||||
(ace->Mask != FILE_ALL_ACCESS && ace->Mask != GENERIC_ALL))
|
||||
return 0;
|
||||
const SID *principal = (const SID *)&ace->SidStart;
|
||||
size_t sid_size = 8 + (size_t)principal->SubAuthorityCount * sizeof(DWORD);
|
||||
if (sid_size > ace->Header.AceSize - prefix || !IsValidSid(&ace->SidStart))
|
||||
return -1;
|
||||
if (!EqualSid(&ace->SidStart, sid))
|
||||
return 0;
|
||||
if (!(ace->Header.AceFlags & INHERIT_ONLY_ACE))
|
||||
*effective = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tpt_owner_acl(cbm_pinned_tree_t *t, PSECURITY_DESCRIPTOR sd) {
|
||||
PSID owner = NULL;
|
||||
PACL dacl = NULL;
|
||||
@@ -80,25 +107,19 @@ static int tpt_owner_acl(cbm_pinned_tree_t *t, PSECURITY_DESCRIPTOR sd) {
|
||||
ACL_SIZE_INFORMATION info;
|
||||
if (!GetAclInformation(dacl, &info, sizeof(info), AclSizeInformation))
|
||||
return -1;
|
||||
if (info.AceCount != 1)
|
||||
/* Windows stores one inheritable full-access grant either as a single
|
||||
* OI|CI entry or, when it was written with generic rights (cbm_mkdir_p's
|
||||
* owner stamp), as an effective entry plus an inherit-only one. Both are
|
||||
* the owner-only policy; at least one entry must apply to the object. */
|
||||
if (info.AceCount < 1 || info.AceCount > 2)
|
||||
return 0;
|
||||
void *raw = NULL;
|
||||
if (!GetAce(dacl, 0, &raw))
|
||||
return -1;
|
||||
ACCESS_ALLOWED_ACE *ace = raw;
|
||||
if (ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE)
|
||||
return 0;
|
||||
size_t prefix = offsetof(ACCESS_ALLOWED_ACE, SidStart);
|
||||
if (ace->Header.AceSize < prefix + 8)
|
||||
return -1;
|
||||
if ((ace->Header.AceFlags & ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)) ||
|
||||
(ace->Mask != FILE_ALL_ACCESS && ace->Mask != GENERIC_ALL))
|
||||
return 0;
|
||||
const SID *principal = (const SID *)&ace->SidStart;
|
||||
size_t sid_size = 8 + (size_t)principal->SubAuthorityCount * sizeof(DWORD);
|
||||
if (sid_size > ace->Header.AceSize - prefix || !IsValidSid(&ace->SidStart))
|
||||
return -1;
|
||||
return EqualSid(&ace->SidStart, sid) ? 1 : 0;
|
||||
bool effective = false;
|
||||
for (DWORD i = 0; i < info.AceCount; i++) {
|
||||
int allowed = tpt_owner_ace(dacl, i, sid, &effective);
|
||||
if (allowed != 1)
|
||||
return allowed;
|
||||
}
|
||||
return effective ? 1 : 0;
|
||||
}
|
||||
|
||||
bool tpt_native_acl(tpt_context *c, tpt_object *o, unsigned mode, bool changed) {
|
||||
|
||||
@@ -223,6 +223,31 @@ static bool pt_windows_acl(pt_fixture *fx, const char *path, bool extra) {
|
||||
&descriptor);
|
||||
}
|
||||
|
||||
/* The encoding Windows stores for an inheritable generic grant: an effective
|
||||
* full-access entry for the owner plus an inherit-only one, here naming
|
||||
* `inherited` (the owner, or another principal for the negative). */
|
||||
static bool pt_windows_split_acl(pt_fixture *fx, const char *path, PSID inherited) {
|
||||
PSID owner = pt_current_sid(fx);
|
||||
wchar_t wide[PT_PATH];
|
||||
unsigned char acl_bytes[1024];
|
||||
ACL *acl = (ACL *)acl_bytes;
|
||||
SECURITY_DESCRIPTOR descriptor;
|
||||
return owner && inherited && pt_wide(path, wide) &&
|
||||
InitializeAcl(acl, sizeof(acl_bytes), ACL_REVISION) &&
|
||||
AddAccessAllowedAceEx(acl, ACL_REVISION, 0, FILE_ALL_ACCESS, owner) &&
|
||||
AddAccessAllowedAceEx(acl, ACL_REVISION,
|
||||
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE,
|
||||
FILE_ALL_ACCESS, inherited) &&
|
||||
InitializeSecurityDescriptor(&descriptor, SECURITY_DESCRIPTOR_REVISION) &&
|
||||
SetSecurityDescriptorOwner(&descriptor, owner, FALSE) &&
|
||||
SetSecurityDescriptorDacl(&descriptor, TRUE, acl, FALSE) &&
|
||||
SetSecurityDescriptorControl(&descriptor, SE_DACL_PROTECTED, SE_DACL_PROTECTED) &&
|
||||
SetFileSecurityW(wide,
|
||||
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION |
|
||||
PROTECTED_DACL_SECURITY_INFORMATION,
|
||||
&descriptor);
|
||||
}
|
||||
|
||||
static bool pt_owner_is_current(PSID owner) {
|
||||
HANDLE token = NULL;
|
||||
union {
|
||||
@@ -1771,6 +1796,45 @@ done:
|
||||
return pt_finish(&fx, result);
|
||||
}
|
||||
|
||||
/* The work parent production makes is owner-only: cbm_mkdir_p stamps a fresh
|
||||
* directory with one inheritable grant for the owner, which Windows stores as
|
||||
* an effective entry plus an inherit-only one. The policy refused that form,
|
||||
* so the engine on Windows ran everything (pinning HEAD failed). The same
|
||||
* split naming another principal is still refused. */
|
||||
TEST(tree_e_windows_split_owner_dacl_is_owner_only) {
|
||||
#ifndef _WIN32
|
||||
SKIP_PLATFORM("Win32 DACL encoding");
|
||||
#else
|
||||
pt_fixture fx = {0};
|
||||
int result = 1;
|
||||
char stamped[PT_PATH] = {0};
|
||||
unsigned char world[SECURITY_MAX_SID_SIZE];
|
||||
DWORD world_length = sizeof(world);
|
||||
PT_CHECK(pt_setup_step("setup:init", pt_init(&fx, 40)) &&
|
||||
pt_setup_step("setup:populate", pt_populate(&fx)) &&
|
||||
pt_setup_step("setup:facts", pt_open_facts(&fx)) &&
|
||||
pt_setup_step("setup:control", pt_fact_control(&fx)));
|
||||
PT_CHECK(pt_join(stamped, fx.home, "stamped-parent") && cbm_mkdir_p(stamped, 0700));
|
||||
cbm_pinned_tree_options_t options = pt_options(&fx, CBM_GIT_REV_HEAD);
|
||||
options.private_parent = stamped;
|
||||
cbm_pinned_tree_error_t error;
|
||||
PT_CHECK(cbm_pinned_tree_create(&options, &fx.tree, &error) == CBM_PINNED_TREE_OK);
|
||||
PT_CHECK(pt_close_one(&fx.tree));
|
||||
PT_CHECK(pt_windows_split_acl(&fx, stamped, pt_current_sid(&fx)));
|
||||
PT_CHECK(cbm_pinned_tree_create(&options, &fx.tree, &error) == CBM_PINNED_TREE_OK);
|
||||
PT_CHECK(pt_close_one(&fx.tree));
|
||||
PT_CHECK(CreateWellKnownSid(WinWorldSid, NULL, world, &world_length));
|
||||
PT_CHECK(pt_windows_split_acl(&fx, stamped, world));
|
||||
PT_CHECK(cbm_pinned_tree_create(&options, &fx.tree, &error) == CBM_PINNED_TREE_UNSUPPORTED &&
|
||||
!fx.tree);
|
||||
result = 0;
|
||||
done:
|
||||
if (stamped[0] && !pt_windows_split_acl(&fx, stamped, pt_current_sid(&fx)))
|
||||
result = 1;
|
||||
return pt_finish(&fx, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t calls, stop;
|
||||
} pt_cancel;
|
||||
@@ -2536,6 +2600,7 @@ SUITE(test_impact_tree) {
|
||||
RUN_TEST(tree_c_complete_inventory_and_native_names);
|
||||
RUN_TEST(tree_d_native_collision_and_exact_spelling);
|
||||
RUN_TEST(tree_e_native_permissions_and_acl_policy);
|
||||
RUN_TEST(tree_e_windows_split_owner_dacl_is_owner_only);
|
||||
RUN_TEST(tree_f_resource_limits_and_local_control);
|
||||
RUN_TEST(tree_g_verify_state_and_complete_inventory);
|
||||
RUN_TEST(tree_h_cleanup_owner_and_unrelated_resources);
|
||||
|
||||
Reference in new issue
Block a user