1
votes

I am trying to write a simple function for windows that answers the following question.

Does user (U) have rights (R) on file (F)?
Where,
   R is some combination of (GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE)
   U does not have to be logged in or impersonated

The code that I wrote is shown below. The application calls the first UserHasPermission that is shown.

The access rights returned by GetEffectiveRightsFromAcl are the same for all user/file combinations that I tested ($001200A9). I double checked and $001200A9 is not just a pointer to the location where the access rights are actually stored.

My question is twofold:
1. Is there a better way of doing this?
2. Can anyone tell me where I am going wrong?

function UserHasPermission(APermission: Longword; out HasPermission: Boolean; AFileName: WideString; AUserName: String; ADomainName: String): Boolean;
    var
      SID: PSID;
      ACL: PACL;
    begin
      SID := nil;
      ACL := nil;
      try
        Result := GetUserSID(SID, AUserNAme, ADomainName);
        Result := Result and GetFileDACL(AFileName, ACL);
        Result := Result and UserHasPermission(APermission, HasPermission, ACL, SID);
      finally
        Dispose(SID);
      end;
    end;

    function UserHasPermission(APermission: Longword; out HasPermission: Boolean; AACL: PACL; AUserSID: PSID): Boolean;
    var
      T: TRUSTEE;
      Rights: ACCESS_MASK;
    begin
      BuildTrusteeWithSid(@T, AUserSID);
      Result := GetEffectiveRightsFromAcl(AACL, @T, @Rights) = ERROR_SUCCESS;
      HasPermission := (Rights and APermission) = APermission;
    end;

    function GetUserSID(out ASID: PSID; AUserName: WideString; const ADomainName: WideString): Boolean;
    var
      NSID, NDomain: Longword;
      Use: SID_NAME_USE;
      DomainName: WideString;
    begin
      Result := False;
      if Length(AUserName) > 0 then
        begin
          if Length(ADomainName) > 0 then
            AUserName := ADomainName + '\' + AUserName;

          // determine memory requirements
          NSID := 0;
          NDomain := 0;
          LookupAccountNameW(nil, PWideChar(AUserName), nil, NSID, nil, NDomain, Use);

          // allocate memory
          GetMem(ASID, NSID);
          SetLength(DomainName, NDomain);

          Result := LookupAccountNameW(nil, PWideChar(AUserName), ASID, NSID, PWideChar(DomainName), NDomain, Use);
        end;
    end;

    function GetFileDACL(AFileName: WideString; out AACL: PACL): Boolean;
    var
      SD: PSecurityDescriptor;
      NSD, NNeeded: Longword;
      Present, Defualted: Longbool;
    begin
      GetFileSecurityW(PWideChar(AFileName), DACL_SECURITY_INFORMATION, nil, 0, NNeeded);
      GetMem(SD, NNeeded);
      try
        NSD := NNeeded;
        Result := GetFileSecurityW(PWideChar(AFileName), DACL_SECURITY_INFORMATION, SD, NSD, NNeeded);
        Result := Result and GetSecurityDescriptorDacl(SD, Present, AACL, Defualted);
        Result := Result and Present;
      finally
        Dispose(SD);
      end;
    end;
1

1 Answers

2
votes

GetEffectiveRightsFromAcl are the same for all user/file combinations that I tested ($001200A9).

That all depends on the ACL, e.g. if Everyone is granted full control then any use will have full control.

The code looks reasonable, and you are using one of the Win32 security APIs (GetEffectiveRightsFromAcl) to do the heavy lifting.

Suggestion: Create very specific ACLs to test your code (SDDL makes this easier), starting with one that makes no grants, then one that only includes a different user.