Search code examples
delphiwinapiaclfreepascalmemory-alignment

How to align EXPLICIT_ACCESS properly with jedi-winutils for SetEntriesInAclA?


I am trying to create a new ACL from an array of EXPLICIT_ACCESS_A in Free Pascal using SetEntriesInAclA, but I keep getting error code 87 (Invalid Parameter) from SetEntriesInAclA with the following code:

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;

While googling the issue, I came across this question, which hinted that the issue is probably related to the alignment of the EXPLICIT_ACCESS_A record.

And sure enough WriteLn(IntToStr(SizeOf(jwaaccctrl.EXPLICIT_ACCESS_A))); shows, that it is of size 20. This is because jedi declares the struct as packed. From the linked question I gather that EXPLICIT_ACCESS_A should be of size 24, but after testing this in C++, it actually seems to be 32.

I also tried copying the EXPLICIT_ACCESS_A and TRUSTEE_A structs to my unit and removing the packed keyword. This yielded structures of size 24, but still the same error.

In conclusion: Can I make above code work with jedi or is it a bug/oversight in jedi?

Also note that I am cross compiling from x64 to x86 using Lazarus 2.0.4 and FPC 3.0.4.


Solution

  • Differently from what I expected, the problem was not related to record-alignment, but to the size of the enums in the record. The EXPLICIT_ACCESS_A record and its sub record have a total of 8 fields which are all expected to be 4 bytes big by WinApi (on 32-bit), resulting in a total size of 32 bytes (on 32-bit). On 64-bit, some fields are bigger, but enum size still should be 4. If FPC's enum size is not 4 then this assumption is violated and WinApi returns error code 87.

    The code I provided is correct and should work in theory. The issue was created by an error in the version of Jedi provided by FPC. There, the Enum size was erroneously reset back to 1 by an include in JediApiLib.inc. This has been fixed in FPC trunk/Revision 43608 and will hopefully be included in the next release. You can check out the bug report here.

    Until then there is a workaround which annoyingly involves redefining the used records, as you can simply replace all Enums with DWORDs to force the correct size. The new types would look like this:

    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; 
    

    Of course, you'll need to convert the Enum values to DWORD when filling an EXPLICIT_ACCESS record like here for example:

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