After my post the other day I couldn’t help but think that perhaps I had an ordering problem. I was really outlining the solution to a problem which had not yet been scoped. Let’s take a step back.
We so often throw the term “ACL” around, like it’s some thing that we all implicitly know. But what’s going on there under the hood? What is an ACL in AD anyway?
Well, to start, the term ACL should really be clarified as it’s something of a catch-all that we use. What we’re really talking about is a security descriptor. A security descriptor (SD), according to MSDN, is:
A security descriptor contains the security information associated with a securable object.
There are a couple of interesting terms there. First, we mentioned the term security descriptor. If I could, I’d like to skip defining this term for now. I would like to define securable object though.
“Securable object” implies that some objects are securable, and some are not. This is a property I think many people tend to forget. There are a subset of objects in Windows that can not be secured via the mechanisms we are discussing. While this discussion is slightly out of scope for this post, if you are interested in more information around that, please do visit the following link: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/securable_objects.asp
I think it's time that we get back to the original term that started this, ACL. Again looking to MSDN for some wording:
An access control list (ACL) is a list of access control entries (ACE). Each ACE in an ACL identifies a trustee and specifies the access rights allowed, denied, or audited for that trustee. The security descriptor for a securable object can contain two types of ACLs: a DACL and a SACL.
Now while I’ll grant that most people are more interested in DACLs than SACLs, the SACL should not be forgotten. Discretionary Access Control Lists (DACLs) define the trustees that are denied access to or allowed access to a securable object, as well as the type of access they are denied/allowed. System Access Control Lists (SACLs) are used for defining the audit policy on the object.
If you recall, a few paragraphs ago we skipped the term security descriptor. With all of the other pieces that we’ve put together so far, let’s go ahead and revisit that term. Security descriptor is defined as:
A structure and associated data that contains the security information for a securable object. A security descriptor identifies the object's owner and primary group. It can also contain a DACL that controls access to the object, and a SACL that controls the logging of attempts to access the object.
It’s starting to come together. In reality, a security descriptor is nothing more than a hunk of data that contains a SACL, a DACL and some other elements (owner, etc.). Each DACL/SACL is made up of ACEs. Makes sense.
While this has all been entertaining I’m sure, let’s get to the point, shall we? Why did we need to do the single instance store thing for security descriptors in AD? What problem were we really trying to solve?
Well, security descriptors are written in a language called Security Descriptor Definition Language (SDDL). In reality, it’s nothing more than a well formatted string. Here’s a sample bit of SDDL: D:(A;;CCDC;;;PS)
While scary looking, that’s actually not so bad to translate. Let’s break it up…
The “D:” part tells me that the forthcoming elements are part of a DACL. Other possibilities for that are enumerated here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp
Next we have the ACE string in the ( )’s. This field is broken up as follows:
So if we look at this particular string, we find the following:
- Ace_type: The A tells me this is an allow. That is, this is granted access to a resource rather than denying it.
- Ace_flags: In this case, this particular field is blank.
- Rights: The GR tells me that we’re granting SDDL_GENERIC_READ, aka GENERIC_READ.
- Object_guid: Again in this case, this field is blank.
- Inherit_object_guid: Another empty field.
- Account_sid: The PS tells me this is granting access to SDDL_PERSONAL_SELF, aka the security principal himself/herself. This field doesn’t have to be a two letter abbreviation like this; it could just have a SID in it (such as when you grant access to a resource using a custom user/group). We provide many well-known SIDs which are defined in sddl.h for your convenience. Some information on them is available here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp
This is, of course, just one example. For the sake of brevity, I did not define every possible element. More information on this format can be found here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp
Anyway, these security descriptors can get quite large. For example, let's look at the default security descriptor for users created in AD:
Wow! That’s pretty large for just a single user. Remember, we place that on each object of this class created. That's a lot of space lost to duplication.
Enter Single Instance Store of Security Descriptors. In most environments, we’ve found that many objects tend to have the same security descriptors on many objects. This property makes sense; most ACLs are applied to containers and set to inherit so as to save administrative overhead, and ACLs on individual objects tend to be from the default and not custom for each object. Since SDs can be quite large, we felt that if we stored only unique copies of them in a single table and then kept pointers to the appropriate SD that table, we would save a substantial amount of space. So far, we think we’ve helped a lot of people with this one. Please do holler with your experiences, good or bad.