Lawrence Barsanti
Lawrence Barsanti

Reputation: 33232

How do you check if a user has access rights for a file with the windows api

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;

Upvotes: 1

Views: 2064

Answers (1)

Richard
Richard

Reputation: 109005

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.

Upvotes: 2

Related Questions