如何将 EXPLICIT_ACCESS 与 SetEntriesInAcl 的 jedi-winutils 正确对齐?
How to align EXPLICIT_ACCESS properly with jedi-winutils for SetEntriesInAclA?
我正在尝试使用 SetEntriesInAclA
从 Free Pascal 中的 EXPLICIT_ACCESS_A
数组创建一个新的 ACL,但我不断收到来自 SetEntriesInAclA
的错误代码 87(无效参数)
以下代码:
uses
sysutils,
JwaWinNT,
JwaAclApi,
JwaAccCtrl,
JwaSDDL,
jwawinbase,
jwawinsta,
jwawintype,
jwawinerror;
function SetupAccess(owner: jwawinnt.PSID; var acl: jwawinnt.PACL): bool;
const
EA_COUNT = 3;
var
sidAuthWorld: jwawinnt.SID_IDENTIFIER_AUTHORITY;
sidAuthNT: jwawinnt.SID_IDENTIFIER_AUTHORITY;
everyoneSID: jwawinnt.PSID;
adminSID: jwawinnt.PSID;
ea: Array[0..(EA_COUNT-1)] of jwaaccctrl.EXPLICIT_ACCESS_A;
status: jwawintype.DWORD;
begin
try
begin
WriteLn(IntToStr(SizeOf(jwaaccctrl.EXPLICIT_ACCESS_A)));
sidAuthWorld := jwawinnt.SECURITY_WORLD_SID_AUTHORITY;
sidAuthNT := jwawinnt.SECURITY_NT_AUTHORITY;
if not (jwawinbase.AllocateAndInitializeSid(@sidAuthWorld, 1, jwawinnt.SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, everyoneSID)
and AllocateAndInitializeSid(@sidAuthNT, 2, jwawinnt.SECURITY_BUILTIN_DOMAIN_RID,
jwawinnt.DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, adminSID)) then
begin
WriteLn('Could not allocate SIDs: ' + SysErrorMessage(getLastError()));
Result := false;
end
else
begin
jwawinbase.ZeroMemory(@ea, EA_COUNT * sizeOf(EXPLICIT_ACCESS_A));
ea[0].grfAccessPermissions := GENERIC_ALL;
ea[0].grfAccessMode := DENY_ACCESS;
ea[0].grfInheritance := NO_INHERITANCE;
ea[0].Trustee.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
ea[0].Trustee.pMultipleTrustee := nil;
ea[0].Trustee.TrusteeForm := TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType := TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName := pointer(everyoneSID);
ea[1].grfAccessPermissions := GENERIC_ALL;
ea[1].grfAccessMode := SET_ACCESS;
ea[1].grfInheritance := NO_INHERITANCE;
ea[1].Trustee.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
ea[1].Trustee.pMultipleTrustee := nil;
ea[1].Trustee.TrusteeForm := TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType := TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName := pointer(adminSID);
ea[2].grfAccessPermissions := GENERIC_ALL;
ea[2].grfAccessMode := SET_ACCESS;
ea[2].grfInheritance := NO_INHERITANCE;
ea[2].Trustee.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
ea[2].Trustee.pMultipleTrustee := nil;
ea[2].Trustee.TrusteeForm := TRUSTEE_IS_SID;
ea[2].Trustee.TrusteeType := TRUSTEE_IS_USER;
ea[2].Trustee.ptstrName := pointer(owner);
status := jwaaclapi.SetEntriesInAclA(2, @ea, nil, acl);
if status = ERROR_SUCCESS then
Result := true
else
begin
WriteLn('Error in SetEntriesInAcl: ' + IntToStr(status));
Result := false;
end;
end;
end
finally
If Assigned(everyoneSID) then
jwawinbase.FreeSID(everyoneSID);
If Assigned(adminSID) then
jwawinbase.FreeSID(adminSID);
end
end;
在谷歌搜索问题时,我遇到了 this question,这暗示该问题可能与 EXPLICIT_ACCESS_A
记录的对齐有关。
果然 WriteLn(IntToStr(SizeOf(jwaaccctrl.EXPLICIT_ACCESS_A)));
显示它的大小为 20。这是因为 jedi 将结构声明为 packed
。从链接的问题我收集到 EXPLICIT_ACCESS_A
应该
大小为 24,但在 C++ 中测试后,它实际上似乎是 32。
我还尝试将 EXPLICIT_ACCESS_A
和 TRUSTEE_A
结构复制到我的
单位并删除 packed
关键字。这产生了大小为 24 的结构,
但仍然是同样的错误。
结论:我可以使上面的代码与 jedi 一起工作还是它是 jedi 中的 bug/oversight?
另请注意,我正在使用 Lazarus 2.0.4 和 FPC 3.0.4 从 x64 交叉编译到 x86。
对于 32 位,EXPLICIT_ACCESS_A 结构应该是 32 字节。
该记录可能已打包,因为它包含一个联合。
我在 Delphi 中使用 Jedi ApiLib 测试了它 returns 正确的大小:
program SizeTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
JwaAccCtrl;
begin
WriteLn(Format('EXPLICIT_ACCESS_A size: %d', [SizeOf(EXPLICIT_ACCESS_A)]));
输出:
在Delphi中,{$MINENUMSIZE 4} 用于强制枚举为 4 个字节(如在 C 中),也许您需要在 Lazarus/FPC?
中设置它
与我的预期不同,问题与记录对齐无关,而是与记录中枚举的大小有关。 EXPLICIT_ACCESS_A
记录及其子记录共有 8 个字段,WinApi(在 32 位上)预计这些字段的大小均为 4 个字节,因此总大小为 32 个字节(在 32 位上)。在 64 位上,一些字段更大,但枚举大小仍应为 4。如果 FPC 的枚举大小不是 4,则违反此假设并且 WinApi returns 错误代码 87.
我提供的代码是正确的,理论上应该可以工作。该问题是由 FPC 提供的 Jedi 版本中的错误造成的。在那里,枚举大小被 JediApiLib.inc
中的包含错误地重置回 1。这已在 FPC trunk/Revision 43608 中修复,并有望包含在下一个版本中。您可以查看错误报告 here.
在那之前有一个解决方法,它令人讨厌地涉及重新定义使用的记录,因为您可以简单地用 DWORD
s 替换所有枚举以强制使用正确的大小。新类型如下所示:
type
TRUSTEE_FIX = packed record
pMultipleTrustee: Pointer;
MultipleTrusteeOperation: DWORD;
TrusteeForm: DWORD;
TrusteeType: DWORD;
ptstrName: LPSTR;
end;
EXPLICIT_ACCESS_FIX = packed record
grfAccessPermissions: DWORD;
grfAccessMode: DWORD;
grfInheritance: DWORD;
Trustee: TRUSTEE_FIX;
end;
当然,在填写 EXPLICIT_ACCESS
记录时,您需要将枚举值转换为 DWORD
,例如:
jwawinbase.ZeroMemory(@ea, EA_COUNT * sizeOf(EXPLICIT_ACCESS_FIX));
ea[0].grfAccessPermissions := GENERIC_ALL;
ea[0].grfAccessMode := DWORD(DENY_ACCESS);
ea[0].grfInheritance := NO_INHERITANCE;
ea[0].Trustee.MultipleTrusteeOperation := DWORD(NO_MULTIPLE_TRUSTEE);
ea[0].Trustee.pMultipleTrustee := nil;
ea[0].Trustee.TrusteeForm := DWORD(TRUSTEE_IS_SID);
ea[0].Trustee.TrusteeType := DWORD(TRUSTEE_IS_WELL_KNOWN_GROUP);
ea[0].Trustee.ptstrName := pointer(everyoneSID);
status := jwaaclapi.SetEntriesInAclA(EA_COUNT, @ea, nil, acl);
我正在尝试使用 SetEntriesInAclA
从 Free Pascal 中的 EXPLICIT_ACCESS_A
数组创建一个新的 ACL,但我不断收到来自 SetEntriesInAclA
的错误代码 87(无效参数)
以下代码:
uses
sysutils,
JwaWinNT,
JwaAclApi,
JwaAccCtrl,
JwaSDDL,
jwawinbase,
jwawinsta,
jwawintype,
jwawinerror;
function SetupAccess(owner: jwawinnt.PSID; var acl: jwawinnt.PACL): bool;
const
EA_COUNT = 3;
var
sidAuthWorld: jwawinnt.SID_IDENTIFIER_AUTHORITY;
sidAuthNT: jwawinnt.SID_IDENTIFIER_AUTHORITY;
everyoneSID: jwawinnt.PSID;
adminSID: jwawinnt.PSID;
ea: Array[0..(EA_COUNT-1)] of jwaaccctrl.EXPLICIT_ACCESS_A;
status: jwawintype.DWORD;
begin
try
begin
WriteLn(IntToStr(SizeOf(jwaaccctrl.EXPLICIT_ACCESS_A)));
sidAuthWorld := jwawinnt.SECURITY_WORLD_SID_AUTHORITY;
sidAuthNT := jwawinnt.SECURITY_NT_AUTHORITY;
if not (jwawinbase.AllocateAndInitializeSid(@sidAuthWorld, 1, jwawinnt.SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, everyoneSID)
and AllocateAndInitializeSid(@sidAuthNT, 2, jwawinnt.SECURITY_BUILTIN_DOMAIN_RID,
jwawinnt.DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, adminSID)) then
begin
WriteLn('Could not allocate SIDs: ' + SysErrorMessage(getLastError()));
Result := false;
end
else
begin
jwawinbase.ZeroMemory(@ea, EA_COUNT * sizeOf(EXPLICIT_ACCESS_A));
ea[0].grfAccessPermissions := GENERIC_ALL;
ea[0].grfAccessMode := DENY_ACCESS;
ea[0].grfInheritance := NO_INHERITANCE;
ea[0].Trustee.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
ea[0].Trustee.pMultipleTrustee := nil;
ea[0].Trustee.TrusteeForm := TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType := TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName := pointer(everyoneSID);
ea[1].grfAccessPermissions := GENERIC_ALL;
ea[1].grfAccessMode := SET_ACCESS;
ea[1].grfInheritance := NO_INHERITANCE;
ea[1].Trustee.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
ea[1].Trustee.pMultipleTrustee := nil;
ea[1].Trustee.TrusteeForm := TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType := TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName := pointer(adminSID);
ea[2].grfAccessPermissions := GENERIC_ALL;
ea[2].grfAccessMode := SET_ACCESS;
ea[2].grfInheritance := NO_INHERITANCE;
ea[2].Trustee.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
ea[2].Trustee.pMultipleTrustee := nil;
ea[2].Trustee.TrusteeForm := TRUSTEE_IS_SID;
ea[2].Trustee.TrusteeType := TRUSTEE_IS_USER;
ea[2].Trustee.ptstrName := pointer(owner);
status := jwaaclapi.SetEntriesInAclA(2, @ea, nil, acl);
if status = ERROR_SUCCESS then
Result := true
else
begin
WriteLn('Error in SetEntriesInAcl: ' + IntToStr(status));
Result := false;
end;
end;
end
finally
If Assigned(everyoneSID) then
jwawinbase.FreeSID(everyoneSID);
If Assigned(adminSID) then
jwawinbase.FreeSID(adminSID);
end
end;
在谷歌搜索问题时,我遇到了 this question,这暗示该问题可能与 EXPLICIT_ACCESS_A
记录的对齐有关。
果然 WriteLn(IntToStr(SizeOf(jwaaccctrl.EXPLICIT_ACCESS_A)));
显示它的大小为 20。这是因为 jedi 将结构声明为 packed
。从链接的问题我收集到 EXPLICIT_ACCESS_A
应该
大小为 24,但在 C++ 中测试后,它实际上似乎是 32。
我还尝试将 EXPLICIT_ACCESS_A
和 TRUSTEE_A
结构复制到我的
单位并删除 packed
关键字。这产生了大小为 24 的结构,
但仍然是同样的错误。
结论:我可以使上面的代码与 jedi 一起工作还是它是 jedi 中的 bug/oversight?
另请注意,我正在使用 Lazarus 2.0.4 和 FPC 3.0.4 从 x64 交叉编译到 x86。
对于 32 位,EXPLICIT_ACCESS_A 结构应该是 32 字节。 该记录可能已打包,因为它包含一个联合。 我在 Delphi 中使用 Jedi ApiLib 测试了它 returns 正确的大小:
program SizeTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
JwaAccCtrl;
begin
WriteLn(Format('EXPLICIT_ACCESS_A size: %d', [SizeOf(EXPLICIT_ACCESS_A)]));
输出:
在Delphi中,{$MINENUMSIZE 4} 用于强制枚举为 4 个字节(如在 C 中),也许您需要在 Lazarus/FPC?
中设置它与我的预期不同,问题与记录对齐无关,而是与记录中枚举的大小有关。 EXPLICIT_ACCESS_A
记录及其子记录共有 8 个字段,WinApi(在 32 位上)预计这些字段的大小均为 4 个字节,因此总大小为 32 个字节(在 32 位上)。在 64 位上,一些字段更大,但枚举大小仍应为 4。如果 FPC 的枚举大小不是 4,则违反此假设并且 WinApi returns 错误代码 87.
我提供的代码是正确的,理论上应该可以工作。该问题是由 FPC 提供的 Jedi 版本中的错误造成的。在那里,枚举大小被 JediApiLib.inc
中的包含错误地重置回 1。这已在 FPC trunk/Revision 43608 中修复,并有望包含在下一个版本中。您可以查看错误报告 here.
在那之前有一个解决方法,它令人讨厌地涉及重新定义使用的记录,因为您可以简单地用 DWORD
s 替换所有枚举以强制使用正确的大小。新类型如下所示:
type
TRUSTEE_FIX = packed record
pMultipleTrustee: Pointer;
MultipleTrusteeOperation: DWORD;
TrusteeForm: DWORD;
TrusteeType: DWORD;
ptstrName: LPSTR;
end;
EXPLICIT_ACCESS_FIX = packed record
grfAccessPermissions: DWORD;
grfAccessMode: DWORD;
grfInheritance: DWORD;
Trustee: TRUSTEE_FIX;
end;
当然,在填写 EXPLICIT_ACCESS
记录时,您需要将枚举值转换为 DWORD
,例如:
jwawinbase.ZeroMemory(@ea, EA_COUNT * sizeOf(EXPLICIT_ACCESS_FIX));
ea[0].grfAccessPermissions := GENERIC_ALL;
ea[0].grfAccessMode := DWORD(DENY_ACCESS);
ea[0].grfInheritance := NO_INHERITANCE;
ea[0].Trustee.MultipleTrusteeOperation := DWORD(NO_MULTIPLE_TRUSTEE);
ea[0].Trustee.pMultipleTrustee := nil;
ea[0].Trustee.TrusteeForm := DWORD(TRUSTEE_IS_SID);
ea[0].Trustee.TrusteeType := DWORD(TRUSTEE_IS_WELL_KNOWN_GROUP);
ea[0].Trustee.ptstrName := pointer(everyoneSID);
status := jwaaclapi.SetEntriesInAclA(EA_COUNT, @ea, nil, acl);