Show Changes Show Changes
Edit Edit
Print Print
Recent Changes Recent Changes
Subscriptions Subscriptions
Lost and Found Lost and Found
Find References Find References
Rename Rename
Search

History

3/11/2005 1:06:11 AM
List all versions List all versions
What Is ACL Inheritance
.

Windows uses security descriptors (WhatIsASecurityDescriptor) with access control lists (WhatIsAnAccessControlList) to track the security settings of sensitive objects in the operating system. Many of these ACLs are created and managed by the operating system automatically, such as those for kernel objects like processes, threads, shared memory-sections, and so on. Although the programmer is allowed to specify these ACLs manually, it's rarely necessary. But an administrator usually needs to manage ACLs in places like the file system, directory service, and even sometimes the registry. Take the file system as an example. Have you ever counted the number of files on a file server? There can be hundreds of thousands, or even millions, of files, and every single one needs to be assigned an ACL. Windows uses a technique known as inheritance to simplify management of ACLs in large hierarchical systems like this.

One goal of ACL inheritance is to allow an administrator to focus on setting permissions for entire branches of the tree, instead of manually setting permissions one file at a time. Another goal is to avoid chaos.1

ACL inheritance occurs on an ACE-by-ACE basis, and the best way to learn how it works is to see it for yourself. If you're not in front of a computer, go find one. Bring up Explorer and create yourself a temporary directory on a local NTFS drive. Pop quiz: Who owns this new directory (WhatIsOwnership)? If you're not sure, you'll find out soon enough. Bring up the property sheet for your new directory, click the Security tab, and press the Advanced button. Take a peek at the owner of the directory (it should be you personally, or possibly the Administrators group if you're an admin and running on a server version of Windows). Now flip back to the Permissions tab and uncheck the top checkbox in the dialog to block inheritance at this directory. You'll be prompted to either copy or remove inherited permissions; for now just click Remove. You should have a completely empty ACL at this point. Figure 45.1 shows what this looks like (these screenshots are from Windows XP). Pop quiz: What permissions do you have to this directory? Would you be completely locked out if you pressed Apply at this point? If you're not sure, remember that you own this directory and review WhatIsOwnership.

Figure 45.1 Starting with an empty ACL

Press the Add button and provide your own account name when prompted. You're going to grant yourself full control to this directory for our experiments. Figure 45.2 shows the dialog you should be looking at now, after clicking the "Full Control" checkbox under the Allow column.2 Note that I'm collapsing some of these dialogs so they don't take up so much space in the book. The real dialog will be bigger than mine.

Figure 45.2 Adding an ACE

Pay special attention to the combo box that I've highlighted in Figure 45.2. This is where you control inheritance for the ACE you're adding. Note that the default setting says this ACE should be inherited by all types of children, which helps ensure consistency as you add new files and folders. The entire tree below this directory (it's empty at the moment, but not for long) will automatically receive the ACE you just added; thus you'll inherit full control for all files and folders created in this branch of the file system. Now drop that combo box down so you can see all of the inheritance options (Figure 45.3).

Figure 45.3 Inheritance options

This might look a bit daunting, but these options are simply the various combinations of the following three flags, defined in winnt.h:

 // excerpt from winnt.h
 #define OBJECT_INHERIT_ACE     (0x1)
 #define CONTAINER_INHERIT_ACE  (0x2)
 #define INHERIT_ONLY_ACE       (0x8)

If an ACE has neither of the first two flags, it won't be inherited by children—thus "This folder only" in Figure 45.3. You could call this a "noninheritable" ACE because it will never propagate to child objects. On the other hand, if either one of those first two flags are set, we have an "inheritable" ACE. If both of the first two flags are set, we arrive at the default inheritance setting, "This folder, subfolders and files." If only CONTAINER_INHERIT_ACE is set, we get "This folder and subfolders," and so on. If you mix in INHERIT_ONLY_ACE, the ACE doesn't apply to this folder at all! It's just a placeholder to be inherited by children, which leads to the last three inheritance options shown in Figure 45.3.

Last but, well okay, least, is a fourth, rather esoteric flag, NO_PROPAGATE_INHERIT_ACE. If enabled for an inheritable ACE, it says that when the ACE is copied to any children the child's copy should have all inheritance flags turned off, which prevents the ACE from flowing to grandchildren. This flag can be toggled via the checkbox at the bottom of the dialog shown in Figure 45.2.

Press OK to get back to the advanced ACL editor. You should now see a single ACE that looks something like Figure 45.4. Note that when you select this ACE in the permission list all three buttons (Add, Edit, and Remove) are available. You could remove this ACE if you wanted to (not now, though).

Figure 45.4 A direct, inheritable ACE on a folder

Press OK to get back to the summary view of the ACL and note that there's no indication about ACE inheritance in the summary (that is, you can't tell which ACEs are inheritable and which aren't). This is yet another reason why you should go to the "Advanced…" dialog if you want to know what's really going on in an ACL. Press OK again to get back to your folder, and create a child folder there called A. Bring up A's permissions in the advanced ACL editor. Before you do anything else, note that the ACE you defined earlier on the parent directory has automatically propagated to this new child directory. Using the Advanced permission dialog, go ahead and add a second ACE to A that grants Authenticated Users the "List Folder / Read Data" permission. Accept the default inheritance setting for this ACE, and be sure to press Apply to commit the change. You should end up with something that looks like Figure 45.5.

Figure 45.5 Direct versus inherited ACEs

If you click back and forth on the two ACEs, just selecting them in the list, you should notice something interesting. When you select the inherited ACE, the Remove button should be grayed out. You're not allowed to remove that one, but you are allowed to remove the ACE you just added. This brings us to one of the most important concepts of inheritance: When an ACE is inherited, it’s marked with a special flag: INHERITED_ACE. So in Figure 45.5 the first ACE is "direct", that is, not inherited. But the second ACE has the INHERITED_ACE flag and is treated specially. In Windows 2000, the only way you could tell the difference between direct and inherited ACEs was to look for a bright or grayed-out icon in the list box, but as of Windows XP the ACL editor uses some heuristics to show not only that an ACE was inherited but also from which folder it was inherited.3 In figure 45.5 the editor is telling you that the "parent object" (the parent folder) was the one that defined this inherited ACE. Other times you'll see the full path to the directory where the inheritable ACE was originally defined—in other words, where in the tree this inherited ACE originated. My screenshots were taken on a Windows Server 2003 machine, so yours may look a little different, but the idea is the same.

There are two reasons Windows keeps track of inherited ACEs. The first is to avoid chaos by keeping the flow of inherited ACEs consistent througout the hierarchy. Although the user is allowed to add, modify, or remove any direct ACEs on an object, the inherited ACEs should always be managed by the operating system. Click back and forth on those two ACEs once again to see that the system is preventing you from removing the inherited ACE. You might wonder at this point why the editor seems to allow you to "edit" the inherited ACE, but if you click the Edit button, you'll see that the only thing you can do is add a deny ACE, which will be a new direct ACE. The original inherited ACE won't be modified. It can't be: That would break the model and lead to chaos. As long as the operating system ensures the consistency of inherited ACEs, we avoid chaos. Of course, there are low-level Win32 APIs that you can call that will allow you to create an ACL that completely violates these rules, and there are tools out there that use these gnarly old APIs, but I'll steer you clear of them!

To prove to yourself that the ACL editor does in fact keep inheritable ACEs consistent, back up to the parent folder (the temporary folder you created at the beginning of this item), bring up the advanced ACL editor, and edit the single ACE there. Remove the "Delete" permission from the access mask and press OK; then apply your change. Revisit the child folder A and take a close look at the inherited ACE. What you'll see is that the change you made to the parent has flowed down to the child. But notice that your direct ACE that grants permission to Authenticated Users is still there. Because Windows tracks inherited ACEs with the INHERITED_ACE flag, it's very straightforward to keep the inherited ACEs consistent while leaving any direct ACEs alone. Here's how it works: Any time the ACL editor updates an ACL on a folder, Windows visits every child, deleting all of the old inherited ACEs and creating new ones based on the parent's new settings (note that this doesn't touch any direct ACEs on the child). If this results in a child's ACL changing, the same process continues down the tree recursively. Can this be expensive? Yes! Is it worth it? Absolutely. In large systems this helps an administrator cope with hundreds of thousands of objects. It helps avoid chaos by keeping the tree consistent, and consistency in security policy leads to better security!

Go back to the parent folder and edit the ACE again to grant yourself full control once more. Verify that this change has propagated to the child folder. Auto-propagation is a neat feature, don't you agree? It was introduced in Windows 2000, and you should know that it's not perfect. Here's the first problem with it: The Win32 API exposes two families of functions for submitting new ACLs. There are the "low-level" functions such as SetFileSecurity, and then there are the "high-level" functions called SetSecurityInfo and SetNamedSecurityInfo. If you ever need to programmatically update ACLs on folders, registry keys, or any other type of container object in a hierarchy, know this: The low-level functions do NOT auto-propagate inheritable ACEs. You should avoid them! If you use the managed classes being introduced in version 2.0 of the CLR to update ACLs, you'll be fine, because they do the right thing.

There's another tool commonly used to manage file system ACLs called cacls.exe (it ships with Windows). I'm sad to say that as of this writing it uses the low-level functions to update file system ACLs and so it doesn't auto-propagate inheritable ACEs as it should. This tool was written for Windows NT 4, and that's the only place you should use it until it's fixed4. Why do I mention it? Because it's an example of a program that runs on Windows but doesn't follow the modern inheritance paradigm. Using it can lead to chaos, as I'll show you here. I want you to see what happens when you don't use the correct APIs to update ACLs in hierarchical systems like the file system and registry.

Bring up a command prompt and run the following command (substitute my path for the full path to your temporary directory):

 cacls c:\MyTempDirectory /E /G Administrators:F

This command tells cacls.exe to edit the DACL on the specified directory and add a new positive ACE that grants Administrators full control.

After running the command, have a look at the permissions on your directory (use the advanced ACL editor, not the summary view). Note how the new ACE that's been added is inheritable, which seems reasonable. Press Esc a couple times to get out of the editor without accidentally making any changes. Then peek at the child folder's ACL. Note that the inheritable ACL from the parent didn't auto-propagate! So we've got part of the file system that's out of sync now. A scary thing is that anytime the file system is in this state, it's a bit unstable. What I mean is that, if you were to make any change at all to the child folder's ACL via Explorer's ACL editor, suddenly the parent's inheritable ACE would auto-propagate, and you might not even notice it (and it would auto-propagate all the way down the tree). Leaving a user's file system in a state like this is a really bad thing.

Here's an even nastier example:

 cacls c:\MyTempDirectory /T /E /G "Power Users:R"

The /T option tells cacls to recursively add the ACE to all children as well as the main folder. This uses the low-level API to add an inheritable ACE to the MyTempDirectory folder, and then it does the same thing to all the children, which means you end up with direct entries on all the children, not inherited ones! If you subsequently go into Explorer and remove the inheritable ACE for Power Users, it won't have any effect on the children because they each have direct ACEs granting Power Users read access. This is horrible. It may be hard to hear for some folks, but cacls is pure evil. It was written for Windows NT 4 and should have either stayed there or been fixed when Windows 2000 shipped. If anyone from Microsoft is reading, I would be happy to help work on a fix for the program if you send me the source code and corresponding unit tests.

One last note on auto-propagation. If at some folder in the tree you don't have WRITE_DAC permission (that is, you don't have the right to change the permissions on a folder), perhaps because someone else owns that folder, then clearly any permissions that you add upstream in the hierarchy won't flow to this folder. If this happens, you'll end up in the unstable state that I mentioned above while flogging cacls.exe. You see, if the owner of that folder logs in and happens to change its DACL, suddenly the permission that was blocked before will auto-propagate. To avoid this sort of unpredictability, you might consider blocking inheritance completely at any nodes in the hierarchy where ownership changes. I'll show how this can be done toward the end of this item.

So the first reason that Windows tracks inherited ACEs is to enable auto-propagation and avoid chaos, and I think I've pretty much beaten that horse to death. The second reason has to do with priority. Direct ACEs always take precedence over inherited ACEs, and this comes into play whenever you have negative (deny) ACEs. Let me show you with another experiment. But before you continue, clean things up a bit: Change the DACL on your temporary folder so that it grants full control to your account, and remove any other ACEs that might be there from our previous experiments. Visit the A folder and remove any direct ACEs on it as well. Folder A should now only have a single inherited ACE that grants you full control. Apply your changes.

Now for the experiment: Drill all the way down into your temporary directory tree and create a file called alice.txt in the A folder. Grant Alice read permissions to the new file. If you don't have an Alice account, create a temporary local account for her but give it a strong password in case you forget to delete it right away.

Now surf back up to your temporary directory, the parent of the A folder. Add a negative ACE here that denies Alice all permissions to the folder, making sure that the ACE you add is fully inheritable ("This folder, subfolders and files"). You'll be prompted with a dialog that provides a brief explanation of how negative ACEs work. Read it carefully and then press the Yes button to commit the change. Now answer the following question: Will Alice be allowed to open alice.txt for read access, or will she be denied? Try it yourself by compiling the following C# program and running it as Alice (bring up a command prompt for Alice using the technique I described in HowToRunAProgramAsAnotherUser and run the program from there).

 using System;
 using System.IO;


 class ReadAliceFile {
   // TODO: edit this path for your system
   static string path = @"c:\MyTempDirectory\A\alice.txt";
   static void Main() {
     try {
       using (FileStream fs = new FileStream(path, FileMode.Open,
                FileAccess.Read, FileShare.Read))
       using (StreamReader r = new StreamReader(fs)) {
         Console.WriteLine("It worked! Here's the file:");
         Console.WriteLine(r.ReadToEnd());
       }
     }
     catch (Exception x) {
       Console.WriteLine("It failed! ({0})", x.Message);
     }
   }
 }

Were you surprised by the results? Think about what the ACL must look like on alice.txt. It inherited some ACEs, one of which denies access to Alice. It also has a direct ACE that permits Alice to read the file. Don't negative ACEs take precedence over positive ones, as I described in WhatIsAnAccessControlList? Generally this is true, but inheritance muddies the waters a bit. Here's the full story on DACL order. Negative ACEs do take precedence over positive ACEs, but, even more important, direct ACEs take precedence over inherited ACEs. Even within inherited ACEs, entries from parents are ordered before those from grandparents (Figure 45.6). So the closer in the directory structure you are to the object you're trying to control the more say you have, until you're at the object itself, where the owner of the object has ultimate control. It's all about discretionary access control, after all (WhatIsDiscretionaryAccessControl).

Figure 45.6 DACL order in the presence of inherited ACEs

Thus our direct positive ACE that grants Alice access is ordered before the negative inherited ACE from the parent (or grandparent) directory. This allows an administrator to set an overall security policy for a directory, but the object's owner ultimately can micromanage her own individual files without disrupting the flow of inherited permissions. Verify that the DACL is ordered this way by looking at it in the advanced ACL editor (Figure 45.7 shows what mine looks like).

Figure 45.7 DACL order with inheritance

For a final demonstration, I created six temporary user accounts and used them to place both positive and negative ACEs through our little hierarchy of folders and files. As you can see, the resulting order is direct entries first, inherited entries from the parent second, and entries inherited from grandparents last. In each of those three categories, negative entries precede positive ones (Figure 45.8).

Figure 45.8 When parents and grandparents contribute

Even given the extreme flexibility of ACL inheritance, you might find a directory that really needs to diverge from the parent's access control policy. An example of this can be found in the operating system itself. If you look at the DACL on the Documents and Settings folder, you'll see that by default anyone in the local Users group is allowed to read from it. But underneath that folder lie home directories, also known as profiles (WhatIsAUserProfile), and the contents of these subfolders must be protected more carefully. For example, the Keith subdirectory should grant Keith and perhaps Administrators full control. To ensure that this subfolder is protected from inadvertant changes to parent directory DACLs, a flag is set in the folder's security descriptor (WhatIsASecurityDescriptor). Two of these flags are defined, and when they're set in a security descriptor the system places a break in the flow of ACL inheritance at that point.

 // excerpt from winnt.h
 #define SE_DACL_PROTECTED     (0x1000)
 #define SE_SACL_PROTECTED     (0x2000)

Look at the first checkbox in the "Advanced…" ACL editor (way back in Figure 45.1). Its wording has changed a bit between operating systems, but the idea has always been the same: The box is checked by default, and by unchecking it you're setting the SE_DACL_PROTECTED flag, thus blocking the flow of inherited ACEs into the DACL. At the very beginning of this item, I asked you to uncheck this box for the temporary directory you created. This was the only way to get rid of any inherited ACEs from the parent directory or drive. You may remember being prompted to either "Copy" or "Remove" the existing inherited ACEs. I asked you to remove them for our experiment, which left you with an empty DACL. But had you chosen to copy them instead, the ACL editor would have simply changed the inherited ACEs into direct ACEs and reordered the DACL appropriately.

After blocking the flow, realize that any ACL changes that occur upstream won't be seen at or below the point of the protected object. Just for kicks, imagine blocking the flow for every single directory on your file system. Oh the chaos that would ensue! You would have to manually apply access controls to every individual directory instead of letting the operating system flow permissions throughout branches of the file system automatically. Use this feature sparingly.

One last thing I should point out. Note the bottom checkbox in Figure 45.1. That's the sledgehammer checkbox. If you check it, when you commit your change you'll be warned to make sure you really want to swing the sledgehammer (Figure 45.9 shows an example where I checked this box for a directory called Y).

Figure 45.9 You're about to clobber all child DACLs!

If it's not obvious from the wording of the warning, the sledgehammer walks to each child object, unblocking the flow (if it was blocked), removes any direct ACEs, and repropagates inheritable ACEs all the way down the tree, so that the only ACEs on child objects are those inherited from the node from which you swung the sledgehammer. This is really useful for synchronizing the access control policy of an entire branch of a tree, but it's really drastic and should be done only with the utmost care.

If you’re going to use the sledge, realize that the recursive change you’re applying will silently fail for any folders that don’t grant you the right to change their permissions. If you’re the owner of the entire tree under that node, then this isn’t a problem. So as an administrator, if you want this sledgehammer feature to be effective, you should seriously consider recursively taking ownership of that node before using the sledge. You can recursively take ownership via the Owner tab in the Advanced security settings dialog (for folders there is a checkbox allowing you to make the change recursive).

1 It wasn't until Windows 2000 that the chaos problem was addressed by tracking inherited ACEs in an ACL. Without this, the Windows NT 4 inheritance model wasn't as robust as it is now. See Brown (2000) if you're curious how the Windows NT 4 inheritance model worked.

2 If you're running Windows 2000 you won't have the "Full Control" checkbox, so you have to check the entire row of Allow checkboxes manually. Sorry.

3 Take this information as a "best guess." It's inaccurate in some cases and there simply to help you find out how far up the directory hierarchy you need to go before you can edit the inheritable ACE that's been propagated down to you.

4 A newer version of this tool that comes with the resource kit is called xcacls.exe, but it suffers the same problem.

PortedBy DonMoxley

PluralsightTraining

Keith's first book-in-a-wiki. If you would like to read the book online or order a physical copy to throw at annoying coworkers, surf to the HomePage. Please note that due to overwhelming wikispam, this particular wiki is no longer editable.

About FlexWiki.

Recent Topics