A Windows user's group has an inherited Deny on a folder, but the user has an explicit Allow set directly on a file inside it. Can the user open the file, and how does the access check decide?
answer
- order decides, not allow-versus-deny
- explicit entries come before inherited
- the walk stops early
- only for the rights actually requested
- no DACL and empty DACL are opposites
basics
~20 sUsually yes. Windows walks the DACL in stored order, and canonical order puts explicit entries ahead of inherited ones, so the explicit Allow grants the requested rights before the inherited Deny is ever reached. Deny only wins over Allow within the same tier.
solid answer
~50 s"Deny always beats Allow" is a half-truth — order decides, not type. The access check walks the DACL from the first ACE, accumulating granted rights from Allow entries whose SID is in the caller's token, and stops the moment every requested right is granted or a matching Deny is hit. Canonical order, which the ACL editor and the Windows APIs maintain, is: explicit deny, explicit allow, inherited deny, inherited allow — with nearer ancestors' inherited entries before more distant ones. So an explicit Allow on the file is evaluated before an inherited Deny from the parent and short-circuits the walk. The catch is that it only short-circuits for the rights actually requested: if the caller asks for write and the explicit Allow only grants read, the walk continues and the inherited Deny bites. The kernel itself just walks in stored order, so a hand-built non-canonical ACL can behave differently.
go deeper
Know that a Windows DACL contains both Allow and Deny entries, that Deny is stronger than Allow among entries set at the same place, and that permissions set directly on a file can differ from what the folder grants.
Explain the walk: entries are evaluated in stored order, Allow masks accumulate, the check stops as soon as the requested rights are satisfied or a matching Deny is reached, and canonical order puts explicit before inherited.
Answer the scenario precisely — the explicit Allow wins for the rights it covers while the inherited Deny still applies to the rest — and raise the bypasses: backup and take-ownership privileges, owner rights, and non-canonical ACLs left by scripting.
Argue for a permission strategy that does not depend on Deny entries at all: grant through group membership from a small set of inheritance roots, because scattered deny ACEs make effective access unreviewable and break the moment inheritance is disabled somewhere.
## The access check is a walk, not a comparison When a process calls into the kernel to open a file, it names a *desired access* mask — the specific rights it wants, such as read data plus read attributes. The security reference monitor then evaluates the object's DACL against the caller's access token, which contains the user SID, all group SIDs, and privileges. The algorithm is straightforward: 1. Start with a granted mask of zero and the set of remaining requested rights. 2. For each ACE in the DACL, in stored order, skip entries whose trustee SID is not in the token and entries flagged inherit-only. 3. If it is a **Deny** ACE and its mask overlaps any right still outstanding, stop — access denied. 4. If it is an **Allow** ACE, add its mask to the granted set. If nothing is outstanding any more, stop — access granted. 5. If the list runs out with rights still outstanding, access denied. Two consequences fall straight out. First, the check terminates early, so ACEs after the decisive one are never consulted. Second, deny does not "win" by virtue of being deny — it wins by being reached first. ## Canonical order Because order is everything, Windows defines a canonical ordering and the ACL editing UI and the security APIs maintain it when they rewrite a DACL: 1. explicit deny ACEs 2. explicit allow ACEs 3. inherited deny ACEs 4. inherited allow ACEs Within the inherited groups, entries inherited from the nearest parent come before those inherited from more distant ancestors. This ordering encodes a deliberate policy: **an explicit decision made on the object itself outranks anything inherited from above.** An administrator who deliberately grants Alice access to one file inside a folder that denies her group should get their wish, and they do. ## Walking the scenario Suppose `C:\finance` carries an inheritable Deny for `Finance-Contractors`, of which Alice is a member, and someone has set an explicit Allow (Read & execute) for Alice on `C:\finance\rates.xlsx`. The file's DACL, in canonical order, looks roughly like: ``` CONTOSO\alice:(RX) CONTOSO\Finance-Contractors:(DENY)(I)(F) BUILTIN\Administrators:(I)(F) ``` - Alice opens the file for read. The first ACE matches her SID and grants read; nothing is outstanding; the walk stops. **She gets in**, and the inherited Deny is never examined. - Alice opens the file for write. The first ACE grants read but not write, so write is still outstanding; the walk reaches the group Deny, which covers write; **denied**. That asymmetry is the point of the question. Candidates who have memorised "deny always wins" get the first case wrong; candidates who have memorised "explicit beats inherited" get the second case wrong. The correct mental model is the walk. ## Special DACL cases Two degenerate cases trip people up: - **NULL DACL** — the descriptor has no DACL at all. That means *no restriction*: everyone is granted everything. It is a genuine security bug when it happens, not a lockdown. - **Empty DACL** — a DACL present but containing zero ACEs. The walk finds nothing to grant, so *nobody* gets access. The two look similar in a naive dump and mean opposite things. ## Who escapes the walk The DACL is not the last word: - The **owner** implicitly holds `READ_CONTROL` and `WRITE_DAC`, so an owner locked out by the DACL can simply rewrite it — unless the special OWNER RIGHTS SID is present to restrict that. - **Privileges bypass DACLs by design.** `SeBackupPrivilege` and `SeRestorePrivilege` let backup software read and write files regardless of the ACL; `SeTakeOwnershipPrivilege` lets an administrator seize ownership and then re-grant. This is why "Administrators were denied" is never a durable control against a local administrator. - The **mandatory integrity check** runs before the DACL check, and can deny a write that the DACL would have allowed. ## Non-canonical ACLs The kernel does not enforce canonical order — it walks whatever is stored. A DACL built by a script that appends an Allow before an existing Deny is *non-canonical*, and it will behave differently from what the properties dialog implies. The GUI detects this, warns, and offers to reorder, which silently changes effective access. When you inherit a system with permissions applied by home-grown tooling, that is a real thing to check. ## What to say Describe the walk, name the canonical order, then answer the scenario with both halves: the explicit Allow wins for the rights it grants, and the inherited Deny still applies to everything it does not. Finish with the caveat that the effective-access calculation is per requested right, which is why the Effective Access tab in the file properties dialog exists at all.
- What is the difference between a file that has no DACL and a file with an empty DACL?They are opposites. A NULL DACL means no restrictions are expressed, so every caller is granted everything requested — a security bug when you find it. An empty DACL contains zero ACEs, so the walk grants nothing and everyone is denied, though the owner can still rewrite the DACL and privileged callers can bypass it.
- If an administrator is explicitly denied on a file, can they still read it?Yes, in practice. `SeTakeOwnershipPrivilege` lets them seize ownership and rewrite the DACL, and `SeBackupPrivilege` lets backup-style access read the file regardless of the ACL. Deny ACEs are a control against ordinary users, never against a local administrator.
- What is a non-canonical ACL and why does it matter?One whose ACEs are not in the explicit-deny, explicit-allow, inherited-deny, inherited-allow order — typically produced by a script that appended entries. The kernel walks stored order, so effective access can differ from what the GUI implies; the properties dialog flags it and offers to reorder, which itself changes access.
saying these in an interview costs you the question
- Says Deny always overrides Allow regardless of order
- Thinks the check walks up to the parent folder at access time
- Believes a missing DACL means nobody has access
- Assumes the whole DACL is evaluated before deciding
- Claims a Deny ACE can lock out a local administrator permanently