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.
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 DWORD
s 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);