I set the same ACL with the GUI and with icacls, yet the results are different

Raymond Chen

A customer found that if they used the GUI and the icacls program to deny Delete permission to a folder, the results were different, even though the resulting ACLs are the same.

Create a user, say, Bob, and create a folder, say, C:\test.

With the GUI

  • Right-click the folder and select Properties.
  • Go the Security tab, click Advanced.
  • Click the Add button to add a new ACE.
  • Select Bob as the Principal.
  • Set the Type to Deny.
  • Click Show advanced permissions.
  • Check Delete and uncheck everything else.
  • Click OK a bunch of times to save the changes.

With icacls

  • From a command prompt, type icacls C:\test /deny Bob:D

If you followed the GUI steps, then Bob can open the directory in Explorer. On the other hand, if you followed the icacls steps, then Bob cannot open the directory in Explorer.

In both cases, running icacls to view the permissions report the same results:

C:\> icacls c:\test
test THISPC\Bob:(DENY)(D)
     BUILTIN\Administrators:(I)(OI)(CI)(F)
     NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
     BUILTIN\Users:(I)(OI)(CI)(RX)
     NT AUTHORITY\Authenticated Users:(I)(M)
     NT AUTHORITY\Authenticated Users:(I)(OI)(CI)(IO)(M)

How is it possible that the permissions are identical, yet the results are different depending on how you set the permissions?

The problem is that your tools are lying to you. The Deny ACE on the directory is not what icacls reports.

If you change the security with the GUI, then the Deny ACE is 0x00010000 = DELETE. But if you change it with the icacls program, then the Deny ACE is is 0x00110000 = DELETE | SYNCHRONIZE.

So the icacls program is lying when it says that it denied Delete (D) permission. It actually denied both Delete and Synchronize.

And then on top of that, the icacls program is lying when it says that the actual ACE is a Deny D. It’s hiding the denied SYNCHRONIZE access.

And it’s that denied SYNCHRONIZE access which is the difference. Explorer cannot open a folder where SYNCHRONIZE is denied. (And the command prompt cannot chdir into such a directory either.)

I’m guessing that the icacls is doing this extra work as a courtesy, but it also makes diagnosing problems more difficult.

10 comments

Discussion is closed. Login to edit/delete existing comments.

  • Alex Martin 0

    But I suppose the icacls behavior is a compatibility constraint now and can’t be fixed.

  • Ben Voigt 0

    I would definitely call this a bug. “(D)” is the friendly shorthand for a group of rights (0x00110000) The indicator for Delete alone (0x00010000), without the rest of the group, is “(DE)”.

    icacls should be showing “(D)” for the file it acted on, where both Delete and Synchronize are in the ACE. And it should be showing “(DE)” for the permissions as set by the GUI, where only Delete appears in the ACE.

    From icacls /?:

    a sequence of simple rights:
    N – no access
    F – full access
    M – modify access
    RX – read and execute access
    R – read-only access
    W – write-only access
    D – delete access
    a comma-separated list in parentheses of specific rights:
    DE – delete

    • Dave Bacher 0

      https://docs.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights

      Note: the NTFS.com description of the flag is much better than the Microsoft description, in my opinion. But that’s just my opinion.

      “Note that you cannot use an access-denied ACE to deny only GENERIC_READ or only GENERIC_WRITE access to a file. This is because for file objects, the generic mappings for both GENERIC_READ or GENERIC_WRITE include the SYNCHRONIZE access right. If an ACE denies GENERIC_WRITE access to a trustee, and the trustee requests GENERIC_READ access, the request will fail because the request implicitly includes SYNCHRONIZE access which is implicitly denied by the ACE, and vice versa. Instead of using access-denied ACEs, use access-allowed ACEs to explicitly allow the permitted access rights.”

      So, what I’d expect (I didn’t verify this) is that Explorer would set (DE,S) if you do a grant, but would set only (DE) on a deny. I would think the same for (R) and (W) – that Explorer would always set Synchronize on allow and set it on Deny only for full control (might be missing another case it should set it on). That appears to be what .NET does – adds Synchronize on a grant, removes it on a deny.

      I would think icacls ought to follow that same pattern.

      I think the issue with calling it a bug – and I’m not arguing for the current behavior, to be clear, because making CreateFile fail seems like something our IT department would push to workstations without warning with the best of intentions – is that I think for grants, it’s close to the behavior that you actually want.

      That is, if I’m granting delete, read, write – I almost certainly want to grant synchronize as well. And if I’m enumerating a grant, it’d make sense to omit it if I’m shortcutting permissions for GENERIC_READ and GENERIC_WRITE, etc.

      It’s only really an issue for a deny ACL – where it’s going to break CreateFile for some common cases.

    • Stefan Kanthak 0

      It’s not a bug, it’s PEBKAC: the correct command is “icacls C:\test /deny Bob:DE”

      • Ben Voigt 0

        It’s a bug that “/deny Bob:DE” results in “(D)” on inspection.

        • Stefan Kanthak 0

          Still PEBKAC, or “you too, Brutus”: you use the wrong tool for “inspection”!
          Both CACLS.exe C:\test /S and CACLS.exe C:\test show the correct ACE set by ICACLS.exe C:\test /Deny Bob:(D) as well as ICACLS.exe C:\test /Deny Bob:(DE)
          I recommend to read the title of Raymond’s post, CAREFUL!
          Also ignore the wrong part “even though the resulting ACLs are the same” of his first sentence: the ACLs differ.

          • cheong00 0

            “This command has been deprecated. Please use icacls instead.”

            Btw, that doesn’t change the idea that apparently this is a bug in ICACLS command for displaying information inaccurately.

  • Torrin 0

    Powershell appears to show everything. I’m guessing that’s best bet if this matters to you.

    PS C:\>get-acl C:\test | select -expand access
    . . .
    FileSystemRights : Delete, Synchronize
    AccessControlType : Deny
    IdentityReference : Bob
    . . .

    • Mystery Man 0

      Not everything. For example:


      Get-Acl C:\Windows\system32 | Select-Object -ExpandProperty Access

      FileSystemRights : 268435456
      AccessControlType : Allow
      IdentityReference : CREATOR OWNER
      IsInherited : False
      InheritanceFlags : ContainerInherit, ObjectInherit
      PropagationFlags : InheritOnly

      FileSystemRights : 268435456
      AccessControlType : Allow
      IdentityReference : NT AUTHORITY\SYSTEM
      IsInherited : False
      InheritanceFlags : ContainerInherit, ObjectInherit
      PropagationFlags : InheritOnly

      ...

      Update: 268435456, I believe is “Generic_ALL” or “Full control”

  • Ian Yates 0

    Synchronise is new to me… Off to do some reading. Thanks!

Feedback usabilla icon