6

How do I programmatically check create file permission for folder? Modify file permission? Delete file permission?
GetNamedSecurityInfo returns that I can write into C:\Program Files but UAC says Access Denied (5)
How can I effective determine access permissions?

My code:

function GetAccessRights(const FileName: String; ObjectType: SE_OBJECT_TYPE; 
  var Access: Cardinal): Cardinal;
var
  SecDesc: PSECURITY_DESCRIPTOR;
  pDacl: PACL;
  Trusteee: TRUSTEE_;
begin
  result := GetNamedSecurityInfo(PChar(FileName), ObjectType, 
    DACL_SECURITY_INFORMATION, nil, nil, @pDacl, nil, SecDesc);
  if ERROR_SUCCESS = result then
  begin
    // the pDacl may be NULL if the object has unrestricted access
    if pDacl <> nil then
    begin
      with Trusteee do
      begin
        pMultipleTrustee := nil;
        MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE;
        TrusteeForm := TRUSTEE_IS_NAME;
        TrusteeType := TRUSTEE_IS_UNKNOWN;
        ptstrName := 'CURRENT_USER';
      end;
      result := GetEffectiveRightsFromAcl(pDacl^, Trusteee, Access);
    end
    else
    begin
      Access := $FFFFFFFF;
      result := ERROR_SUCCESS;
    end;
    if SecDesc <> nil then
      LocalFree(Cardinal(SecDesc));
  end;
end;
barbaris
  • 514
  • 8
  • 25
  • 7
    *It's easier to ask forgiveness than it is to get permission* – David Heffernan Feb 18 '13 at 14:48
  • 1
    Forgiveness is not required for `Program Files`. Windows write file in Virtual Store. – barbaris Feb 18 '13 at 14:52
  • 1
    What for? If you've got a GUI where you'd like to display that information, then you need to find out if you have permissions, you don't have a choice; If you need to actually *write* something, nothing beats trying: create a temporary file and then delete it. I doubt anything else is truly reliable, hint: who can reliably say if you have write access on a mapped network drive, when the server software is Samba? Even with full-access to the configuration files it could tricky! – Cosmin Prund Feb 18 '13 at 14:55
  • @barbaris Only if your app runs virtualized and I'm sure you aren't making that mistake. – David Heffernan Feb 18 '13 at 15:01
  • @DavidHeffernan What do you mean? – barbaris Feb 18 '13 at 15:07
  • @barbaris Virtual Store is only for virtualized apps. Virtualization was a crutch to help migration to UAC. Your app should not be running virtualized. Is it? If your are getting ERROR_ACCESS_DENIED then it would appear that you are not virtualized. In which case forgiveness is required. – David Heffernan Feb 18 '13 at 15:15
  • @DavidHeffernan In this case I'm not getting `Access Denied`. Program quietly writes into Virtual Store. How turn it off? – barbaris Feb 18 '13 at 15:23
  • 4
    @barbaris UAC virtualization is disabled once you include a manifest with an appropriate trustInfo section. You can see whether an application is UAC virtualized in the task manager, there is a custom column for that in the process view. – Jens Mühlenhoff Feb 18 '13 at 15:23
  • 1
    As Jens says, you need an appropriate manifest. All the gory details here: http://technet.microsoft.com/en-us/magazine/2007.06.uac.aspx As for your needs, you'll almost certainly want `asInvoker` in the `requestedExecutionLevel`. – David Heffernan Feb 18 '13 at 15:28

1 Answers1

9

I have been using NT Utilities for this. Worked very well for me with Win2K/XP/Vista/7

Example from my setup project:

uses unitNTSecurity;

function CheckAccessToFile(DesiredAccess: DWORD; const FileOrDirName: string; ObjectName: string): Boolean;
var
  fo: TNTFileObject;
  acl: TAccessControlList;
  ace: TAccessControlElement;
  name: string;
  i: integer;
begin
  Result := False;
  if FileExists(FileOrDirName) or DirectoryExists(FileOrDirName) then
  begin
    fo := TNTFileObject.Create(FileOrDirName);
    acl := TAccessControlList.Create;
    try
      fo.GetDiscretionaryAccessList(acl);
      for i := 0 to acl.ElementCount - 1 do
      begin
        ace := acl.Element[i];
        name := ace.Name; // format is: BUILTIN\Users
        if (CompareText(ObjectName, name) = 0) and
          (ace.Type_ = aeAccessAllowed) and
          (DesiredAccess = ace.Mask) then
        begin
          Result := True;
          Break;
        end;
      end;
    finally
      fo.Free;
      acl.Free;
    end;
  end;
end;

Check for modifypermission:

Result := CheckAccessToFile($001301BF, 'C:\foo', 'BUILTIN\Users');

A note about my answer: The above code answers the OP question:

How do I programmatically check modify permissions

But, if all you need to do, is check that your application is being able to write to a directory, I would not go for this kind of ACL solution, and actually attempt to write a temp file to it, so that I'm 100% sure I can write to it.

I use this code as part of my setup process where I need to grant modify permissions for some directories, so this code is used to check if that directory does not already have these permissions - That might be a very different scenario than yours.

There are a few discussions about this issue:

So you need to select your solution according to your actual scenario.

Community
  • 1
  • 1
kobik
  • 21,001
  • 4
  • 61
  • 121