如何将 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_ATRUSTEE_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.

在那之前有一个解决方法,它令人讨厌地涉及重新定义使用的记录,因为您可以简单地用 DWORDs 替换所有枚举以强制使用正确的大小。新类型如下所示:

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);