The schema is the blueprint for data storage in Active Directory. Each
object in Active Directory is an instance of a class in the schema. A user
object, for example, exists as an instance of the user
class. Attributes define the pieces of information that a class, and thus an
instance of that class, can hold. Syntaxes define the type of data that can
be placed into an attribute. As an example, if an attribute is defined with
a syntax of Boolean, it can store True
or
False
as its value, or it can be null. A null value has
an implementation-specific meaning; it could mean True
or
False
depending on the application using the value.
Active Directory contains many attributes and classes in the default schema, some of which are based on standards and some of which Microsoft needed for its own use. Each new release of Active Directory has included updates to the default schema. For background information on schema versions, see the sidebar Schema Versions, next. Additionally, the Active Directory schema was designed to be extensible, so that administrators could add classes or attributes they deemed necessary. In fact, extending the schema is not a difficult task; it is often more difficult to design the changes that you would like to incorporate. Schema design issues are covered in Chapter 17. In this chapter, we’re concerned only with the fundamentals of the schema.
The Schema container is located in Active Directory under the
Configuration container. For example, the distinguished name of the Schema
container in the contoso.com forest would be cn=schema,cn=Configuration,dc=contoso,dc=com
.
You can view the contents of the container directly by pointing an Active
Directory viewer such as ADSI Edit or LDP at it. You can also use the
Active Directory Schema MMC snap-in, which splits the classes and
attributes in separate containers for easy viewing (though in reality, all
the schema objects are stored directly in the Schema container).
The Active Directory Schema MMC snap-in is not fully enabled by default. In order to enable the schema management snap-in on a domain controller, you must first register the DLL it depends on. To do that, run this command: regsvr32 schmmgmt.dll.
The schema itself is made up of two types of Active
Directory objects: classes and attributes. In Active Directory, these are
known respectively as classSchema
(Class-Schema) and attributeSchema
(Attribute-Schema) objects. The two distinct forms of the same names
result from the fact that the cn
(Common-Name) attribute of a class contains the hyphenated easy-to-read
name of the class, and the lDAPDisplayName
(LDAP-Display-Name) attribute of
a class contains the concatenated string format that is used when querying
Active Directory with LDAP or ADSI. In the schema, the lDAPDisplayName
attribute of each object is
normally made by capitalizing the first letter of each word of the
Common-Name, and then removing the hyphens and concatenating all the words
together. Finally, the first letter is made lowercase.[1] This creates simple names like user
, as well as the more unusual sAMAccountName
and lDAPDisplayName
. We’ll specify the more commonly
used LDAP display name format from now on.
Whenever you need to create new types of objects in Active
Directory, you must first create a classSchema
object, defining the class of the
object and the attributes it contains. Once the class is properly designed
and added to the schema, you can then create objects in Active Directory
that use the class. If the class you are adding will have custom
attributes that are required to be populated when new instances of that
class are created, you must define the attributeSchema
objects first. If you just want
to add a new attribute to an existing class, you must create the attributeSchema
object and associate the
attribute with whatever classes you want to use it with.
Before we delve into what makes up an Active Directory class or attribute, we need to explain how each class that you create is unique, not just within your Active Directory infrastructure but also throughout the world.
Active Directory is based on LDAP, which was originally based on the X.500 standard created by the ISO (International Organization for Standardization) and ITU (International Telecommunication Union) organizations in 1988. To properly understand how the Active Directory schema works, you really need to understand some of the basics of X.500; we’ll run through them next.
The X.500 standard specifies that individual object classes in an organization can be uniquely defined using a special identifying process. The process has to be able to take into account the fact that classes can inherit from one another, as well as the potential need for any organization in the world to define and export a class of its own design.
To that end, the X.500 standard defined an object identifier (OID) to uniquely identify every schema object. This OID is composed of two parts:
The first part indicates the unique path to the branch holding the object in the X.500 tree-like structure.
The second part uniquely indicates the object in that branch.
OID notation uses integers for each branch and object, as in the following example OID for an object:
1.3.6.1.4.1.3385.12.497
This uniquely references object 497 in branch 1.3.6.1.4.1.3385.12. The branch 1.3.6.1.4.1.3385.12 is contained in a branch whose OID is 1.3.6.1.4.1.3385, and so on.
Each branch within an OID number also corresponds to a name. This means that the dotted notation 1.3.6.1.4.1, for example, is equivalent to iso.org.dod.internet.private.enterprise. As the names are of no relevance to us with Active Directory, we don’t cover them in this book.
This notation continues today and is used in the Active Directory schema. If you wish to create a schema object, you need to obtain a unique OID branch for your organization. Using this as your root, you can then create further branches and leaf nodes within the root, as your organization requires.
The Internet Assigned Numbers Authority (IANA) maintains the main set of root branches and defines itself as “the central coordinator for the assignment of unique parameter values for Internet protocols.” The IANA says of its mission:
The IANA is chartered by the Internet Society (ISOC) and the Federal Network Council (FNC) to act as the clearinghouse to assign and coordinate the use of numerous Internet protocol parameters. The Internet protocol suite, as defined by the Internet Engineering Task Force (IETF) and its steering group (the IESG), contains numerous parameters, such as Internet addresses, domain names, autonomous system numbers (used in some routing protocols), protocol numbers, port numbers, management information base object identifiers, including private enterprise numbers, and many others. The common use of the Internet protocols by the Internet community requires that the particular values used in these parameter fields be assigned uniquely. It is the task of the IANA to make those unique assignments as requested and to maintain a registry of the currently assigned values. The IANA is located at and operated by the Information Sciences Institute (ISI) of the University of Southern California (USC).
You can find the IANA website at this link.
You can request an OID namespace—i.e., a root OID number from which you can create your own branches—directly from the IANA if you like. These numbers are known as Enterprise Numbers. The entire list of Enterprise Numbers can be found at iana.org. This list of numbers is updated every time a new one is added.
At the top of the file, you can see that the root that the IANA uses is 1.3.6.1.4.1. If you look down the list, you will see that Microsoft has been allocated branch 311 of that part of the tree, so Microsoft’s OID namespace is 1.3.6.1.4.1.311. As each number also has a contact email address alongside it in the list, you can search through the file for any member of your organization that has already been allocated a number. It is likely that large organizations that already have an X.500 directory or that have developed SNMP management information bases (MIBs) will have obtained an OID.
In addition to Enterprise Numbers, country-specific OIDs can be purchased. An organization’s Enterprise Number registration has no bearing on whether it has obtained a country-based OID namespace to use. If you don’t see the company listed in the Enterprise Numbers list, don’t be fooled; the organization could still have a number.
For example, Microsoft has been issued the Enterprise Number 1.3.6.1.4.1.311, yet all of its new schema classes use a US-issued OID namespace of 1.2.840.113556 as their root. The 1.2.840 part is uniquely allotted to the United States. In other words, Microsoft has obtained two OID namespaces that it can use but is choosing to use only the US-issued namespace.
If you want to obtain an Enterprise Number, fill in the online form at http://pen.iana.org/pen/PenApplication.page. If this URL changes, you can navigate to it from the main IANA web page.
Microsoft used to issue unique OID namespaces to customers on request; however, it no longer does this. Instead, Microsoft provides a script that will generate a statistically unique OID branch each time it is run. This script is available from http://go.microsoft.com/fwlink/?LinkId=100725. While this script is a viable solution, best practice dictates that you should obtain a globally unique OID namespace and issue OIDs from there instead.
Once an organization has an OID namespace, it can add unique branches and leaves in any manner desired under the root. For example, you could decide to have no branches underneath and just give any new object an incrementing integer starting from 1 underneath the root. Alternatively, you could decide to make a series of numbered branches starting from 1, each corresponding to a certain set of classes or attributes. Thus, the fifth object under the third branch would have an OID ending in 3.5. Figure 5-1 shows one sample scenario for how an organization decided to structure its OID namespace.
The range of values in any part of an OID namespace for the Active Directory schema goes from 1 to 268,435,455—i.e., from 20 through 228 – 1.
This limitation has caused issues with schema extensions for some companies in Australia. Australia has the OID 1.2.36, and according to the Australia Standards document MP-75, companies may use their Australian Company Number, or ACN (excluding leading zeros), to formulate their OIDs without needing to request one. Unfortunately the ACN is nine digits, so it could easily exceed the limitation listed above.
To reinforce this point, let’s look at a couple of examples
directly from the Active Directory schema. If you open the Active
Directory Schema snap-in, you can look at the schema class OIDs very
easily. Navigating through the classes when we open the property page
for the printQueue
class, we get
Figure 5-2. You can see that
the unique OID is 1.2.840.113556.1.5.23. This tells us that the number
is a defined part of Microsoft’s object class hierarchy.
Figure 5-3 shows
the property page for the organizationalPerson
class. Here, you can see
that the unique OID 2.5.6.7 is very different, because within the
original X.500 standard, a set of original classes was defined. One was
organizationalPerson
, and this is a
copy of that class. Microsoft included the entire set of base X.500
classes within Active Directory.
The OID numbering notation has nothing to do with inheritance. Numbering a set of objects a certain way does nothing other than create a structure for you to reference the objects; it does not indicate how objects inherit from one another.
Let’s dissect an example attribute and class to see what they contain. With that information, you will be able to see what is required when you create a new schema object.
Just as class information is stored in Active Directory as instances
of the class called classSchema
,
attributes are represented by instances of the class called attributeSchema
. As with all objects, the
attributeSchema
class has a number of
attributes that can be set when specifying a new instance. The attributeSchema
class inherits attributes from
the class called top
. However, most of
the top
attributes are not relevant
here. All of the attributes of the attributeSchema
class are documented at this
site.
The userPrincipalName
(UPN)
attribute is used on user objects to provide a unique method of
identifying each user across a forest. Users can log =on to a
workstation in any domain in the forest using the UPN if they so desire.
The UPN attribute, in fact, accepts valid RFC 2822 (email) addresses, so
the UPN for user tpood in the
europe.contoso.com domain could be
[email protected] or
[email protected], or even
[email protected]. In fact, any UPN suffix, such as
@contoso.com, can be used in a forest. The only
requirement is that the UPN value for a user is unique across all users
in a forest.
Active Directory does not enforce the uniqueness of a UPN when it is set. If two different users in the same forest are assigned the same UPN, neither will be able to log on using the UPN. When duplicate UPNs are detected, domain controllers will log an event from source Key Distribution Center (KDC) with event ID 11. Many large organizations implement scripts or other tools to scan their directories on a regular basis to check for duplicate UPNs.
To dissect the attribute, we need to find out what values have
been set for it. Table 5-2 shows
a subset of the values of attributes that have been set for the userPrincipalName
attributeSchema
instance.
Attribute lDAPDisplayName | Attribute syntax | Attribute value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We can see that the name of the attribute is User-Principal-Name
(adminDescription, adminDisplayName, cn, name
),
that it is an instance of the attributeSchema
class (objectCategory
and
objectClass
), that it inherits
attributes from both top
and attributeSchema
(objectClass
), and that the UPN attribute is
not visible to casual browsing (showInAdvancedViewOnly
).
The userPrincipalName
attributes show the following:
It is to be stored in the GC (isMemberOfPartialAttributeSet
and systemFlags
).
It is to be indexed (searchFlags
).
It has an OID of 1.2.840.113556.1.4.656 (attributeID
).
We should use userPrincipalName
(lDAPDisplayName
) when binding to it with
ADSI.
Instances can be created by anyone (systemOnly
).
It stores single (isSingleValued
) Unicode strings (attributeSyntax
and oMSyntax
).
In Figure 5-4, you can see many of the values for the UPN attribute. We have indicated which attributes are changed by checking or unchecking each checkbox.
There are several properties of attributes that have a significant and varied impact on attribute use and functionality. Here we give a little more detailed information on a few attributes that you need to understand when modifying the schema.
The syntax of an attribute represents the kind of data it can hold; people with a programming background are probably more familiar with the term “data type.” Unlike attributes and classes, the supported syntaxes are not represented as objects in Active Directory. Instead, Microsoft has coded these syntaxes internally into Active Directory itself. Consequently, any new attributes you create in the schema must use one of the predefined syntaxes.
Whenever you create a new attribute, you must specify its syntax. To uniquely identify the syntax among the total set of 22 syntaxes, you must specify two pieces of information: the OID of the syntax and a so-called OM syntax. This pair of values must be set together and correctly correlate with Table 5-3. More than one syntax has the same OID, which may seem strange; to uniquely distinguish between different syntaxes, you thus need a second identifier. This is the result of Microsoft requiring some syntaxes that X.500 did not provide. Table 5-3 shows the 22 expanded syntaxes, including the name of each syntax with alternate names following in parentheses.
Syntax | OID | OM syntax | Description |
Address | 2.5.5.13 | 127 | Used internally by the system. |
Boolean | 2.5.5.8 | 1 | True or false. |
Case-insensitive string | 2.5.5.4 | 20 | A string that does not differentiate between uppercase and lowercase. |
Case-sensitive string | 2.5.5.3 | 27 | A string that differentiates between uppercase and lowercase. |
Distinguished name | 2.5.5.1 | 127 | The fully qualified domain name (FQDN) of an object in Active Directory. |
DN-Binary | 2.5.5.7 | 127 | Octet string with binary value and DN. Format: B:<char count>:<binary value>:<object DN>. |
DN-String | 2.5.5.14 | 127 | Octet string with string value and DN. Format: S:<char count>:<string value>:<object DN>. |
Generalized-Time | 2.5.5.11 | 24 | ASN1.1 time format, e.g., 20040625234417.0Z. |
Integer (enumeration) | 2.5.5.9 | 10 | A 32-bit number. |
Integer (integer) | 2.5.5.9 | 2 | A 32-bit number. |
Large integer | 2.5.5.16 | 65 | A 64-bit number. |
NT security descriptor | 2.5.5.15 | 66 | A security descriptor (SD). |
Numeric string | 2.5.5.6 | 18 | A string of digits. |
Object ID | 2.5.5.2 | 6 | An OID. |
Octet string (Octet-String) | 2.5.5.10 | 4 | A byte string. |
Print case string (IA5-String) | 2.5.5.5 | 22 | A normal printable string. |
Print case string (Printable-String) | 2.5.5.5 | 19 | A normal printable string. |
Replica-Link | 2.5.5.10 | 127 | Replication information. |
SID | 2.5.5.17 | 4 | A security identifier (SID). |
Undefined | 2.5.5.0 | N/A | Not a valid syntax. |
Unicode | 2.5.5.12 | 64 | A wide string. |
UTC-Time | 2.5.5.11 | 23 | The number of seconds elapsed since 1 January 1970. |
Most of these are standard programming types. If you’re not sure
which syntax to use, take a look at some preexisting attributes and see
if you can find an appropriate syntax for the attribute you wish to
create. For example, the userPrincipalName
attribute has an attributeSyntax
of 2.5.5.12 and an oMSyntax
of 64, so it must contain Unicode
strings.
The systemFlags
attribute is an
often overlooked but important attribute. It is a bit mask that
represents how the attribute should be handled. For more information on
bit masks, see the upcoming sidebar How to Work with Bit Masks. New bit values can be defined
any time that Microsoft updates the directory service binaries. The
systemFlags
attribute is configured
both on schema definitions of attributes and classes and on any
instantiated object throughout the forest. This can be confusing, but
the various bits in the attribute can mean various things depending on
the object to which the attribute applies. Table 5-4 lists only the
values for systemFlags
on attributeSchema
and classSchema
objects. A complete listing of
valid systemFlags
settings is
available at this
link.
Value | Description |
1 (0x0001) | Attribute is not replicated. |
2 (0x0002) | Attribute will be
replicated to the Global Catalog. This value should only be set
by Microsoft; do not use it. Instead, use the |
4 (0x0004) | Attribute is constructed, not stored in the database. This should only be set by Microsoft; do not use it. |
16 (0x0010) | Category 1 attribute or class. Category 1 objects are classes and attributes included in the base schema with the system. Note: not all classes and attributes in the base schema are marked Category 1. |
33554432 (0x02000000) | The object is not moved to the Deleted Objects container when it is deleted. Instead, the tombstone remains in the original container. |
134217728 (0x08000000) | The schema object cannot be renamed. |
268435456 (0x10000000) | For objects in the configuration partition, if this flag is set, the object can be moved with restrictions; otherwise, the object cannot be moved. By default, this flag is not set on new objects created under the configuration partition. This flag can only be set during object creation. |
536870912 (0x20000000) | For objects in the configuration partition, if this flag is set, the object can be moved; otherwise, the object cannot be moved. By default, this flag is not set on new objects created under the configuration partition. This flag can only be set during object creation. |
1073741824 (0x40000000) | For objects in the configuration partition, if this flag is set, the object can be renamed; otherwise, the object cannot be renamed. By default, this flag is not set on new objects created under the configuration partition. This flag can only be set during object creation. |
2147483648 (0x80000000) | The object cannot be deleted. |
Most attributes are directly stored in the Active Directory
database. Constructed attributes are the exception, and they are
handled by the directory service in order to offer special
functionality. This functionality can range from telling you
approximately how many objects are contained directly under a
container type object (msDS-Approx-Immed-Subordinates
) to telling
you the types of objects that can be instantiated under a given object
(possibleInferiors
) to telling you
which attributes you have write access to on a given object (allowedAttributesEffective
), and many other
things. These attributes, because they are special, have some rules
you should be aware of:
Constructed attributes are not replicated. They are constructed by each directory instance separately.
Constructed attributes cannot be used in server-side sorting.
Constructed attributes generally cannot be used for queries.
The attribute aNR
is an
exception here as it is used for constructing the special ANR
queries. ANR is covered in the section Ambiguous name resolution.
In some cases, a base scope query may be required to
retrieve certain constructed attributes due to the computational
cost of constructing the attribute; e.g., tokenGroups
can only be returned with a
base scope query.
The schemaFlagsEx
attribute is
an attribute that has existed since Windows 2000 but was not put into
use until Windows Server 2008. It is designed to hold flags that further
define the properties of an attribute. There is currently only one flag
implemented in this bit mask, as outlined in Table 5-5.
The searchFlags
attribute is
another bit mask that is best known as the attribute used to control
indexing, but it is a little more involved than that. As indicated by
the name, searchFlags
is similar to
systemFlags
in that it is a series of
bits representing how the attribute should be handled. Unlike systemFlags
, searchFlags
is only set on schema attribute
definitions. See Table 5-6 for all of the
values.
Bit number | Value | Description |
1 | 1 (0x0001) | Create an index for the attribute. All other index-based flags require this flag to be enabled as well. Marking linked attributes to be indexed has no effect. |
2 | 2 (0x0002) | Create an index for the attribute in each container. This is only useful for one-level LDAP queries. |
3 | 4 (0x0004) | Add an attribute to ambiguous name resolution (ANR) set. ANR queries are primarily used for Exchange and other address book tools. ANR attributes must be indexed and must be in either UNICODE or Teletex string attribute syntax. Adding attributes to this set can have performance implications in Microsoft Exchange. |
4 | 8 (0x0008) | Preserve this attribute in a tombstone object. This flag controls what attributes are kept when an object is deleted. |
5 | 16 (0x0010) | Copy this value when the object is copied. This flag doesn’t do anything in Active Directory; tools such as Active Directory Users and Computers that copy objects can look at this flag to determine what attributes should be copied. |
6 | 32 (0x0020) | Create a tuple index.
Tuple indexing is useful for medial searches. A medial search
has a wildcard at the beginning or in the middle of the search
string. For example, the medial search ( |
7 | 64 (0x0040) | Create a subtree index. This index is designed to increase the performance of virtual list view (VLV) queries. |
8 | 128 (0x0080) | Mark the attribute as confidential. Only users with both Read Property and Control Access rights to the attribute so marked can view it when it is so marked. |
9 | 256 (0x0100) | Never audit changes to this attribute. This flag was new in Windows Server 2008. Auditing is covered in Chapter 16. |
10 | 512 (0x0200) | Include this attribute in the RODC filtered attribute set. RODCs and the filtered attribute set are covered in Chapter 9. |
Attribute indexing is available to boost query performance. When
an attribute is indexed, the values are placed in a special table in a
sorted order so that a query using the attribute can be completed by
looking at a subset of all the information in the directory. The type
of index created can be modified by additional bit flags configured in
the searchFlags
attribute. There
are several points to know about indexes:
A query that contains bitwise operations on an indexed
attribute diminishes the usefulness of the index. A bitwise
operation can’t be directly looked up in the index table, and the
entire set of values in the index will have to be enumerated and
tested. Bitwise queries are queries that query a bit mask (for
example, the systemFlags
or
userAccountControl
attributes).
A query that contains a NOT of an indexed attribute negates the use of the index for that portion of the query. A NOT of an attribute requires enumerating all objects in the search scope to determine which objects don’t have the attribute or which objects have permissions applied that disallow the trustee to view the attribute value.
Linked attributes are implicitly indexed. If you modify the flag, it will have no effect due to the implicit indexing behavior.
It is often assumed that indexes cannot be built or do not
work well for attributes with multiple values or non-unique
values. This is incorrect. In the early days of the original
Active Directory beta, there was concern about multiple values and
non-unique values, but the issues surrounding them were addressed.
This topic is most often raised in regard to the objectClass
attribute and is stated as
the reason why Microsoft didn’t index the attribute by default
prior to Windows Server 2008.
If you have installed the Exchange Server 2007 schema
extensions in your forest, objectClass
is indexed as part of this
schema extension.
Windows Server 2008 and newer domain controllers implement a
special behavior that indexes objectClass
by default regardless of the
searchFlags
setting in Active
Directory. Note that this is considered a special behavior because
the attribute will not be indexed on Windows 2000 or Windows
Server 2003 domain controllers once you import the Windows Server
2008 or newer schema extensions; however, the attribute will be
indexed on any up-level domain controllers. This could cause
performance deltas in applications that randomly select domain
controllers for use. You may consider indexing objectClass
when you apply the Windows
Server 2008 or newer schema updates so that you will have a
consistent LDAP query experience across your enterprise once newer
domain controllers are introduced.
While indexing attributes can very frequently improve the performance of LDAP queries, it is important to realize that indexes also consume disk space. Adding an index to an attribute that is populated across a large percentage of directory objects may consume a substantial amount of disk space.
Domain controller performance will also be impacted while indexes are being generated. Index data is not replicated, so every domain controller in the forest must build its own copy of an index when it detects a new attribute index must be created. The speed at which an index is created is dependent on how much data must be indexed and also the hardware on which the domain controller is running.
Windows Server 2012 DCs support the notion of deferred
indexing. When deferred indexing is enabled, rather than
beginning the index generation process immediately, the DC will not
generate an index until it is either rebooted or an UpdateSchemaNow
LDAP request is made. In
order to enable deferred indexing, you must modify the forest’s
dsHeuristics
value such that the
19th byte is equal to 1
. For more information on editing the
dsHeuristics
attribute, refer to
Chapter 16.
Ambiguous name resolution (ANR) is used for
address book lookups. It allows a single small query to be expanded
into searching as many fields as the administrator would like
searched, so that users can enter a single piece of information and
hopefully find all possible “hits” on the value they are interested
in. When an ANR query such as (anr=Joe
Richards)
is submitted, the Active Directory Query Processor
expands the simple filter into a more complex OR wildcard filter that
contains all attributes marked as part of the ANR set. The specified
filter on a Windows Server 2012 Active Directory with Exchange Server
2010 installed would expand that simple query to:
( (| (displayName=joe richards*) (givenName=joe richards*) (legacyExchangeDN=joe richards) (msDS-AdditionalSamAccountName=joe richards*) (msDS-PhoneticCompanyName=joe richards*) (msDS-PhoneticDepartment=joe richards*) (msDS-PhoneticDisplayName=joe richards*) (msDS-PhoneticFirstName=joe richards*) (msDS-PhoneticLastName=joe richards*) (physicalDeliveryOfficeName=joe richards*) (proxyAddresses=joe richards*) (name=joe richards*) (sAMAccountName=joe richards*) (sn=joe richards*) (& (givenName=joe*) (sn=richards*) ) (& (givenName=richards*) (sn=joe*) ) ) )
As you can see, a very simple query can quickly be expanded into a very large query. For this reason, you should avoid adding additional ANR attributes.
The attributes that are retained when an object is deleted
(a.k.a. tombstoned) are configured through a
combination of the searchFlags
setting and some hardcoded internal functionality. The
preserve on tombstone searchFlags
setting is configurable by
administrators, so they can choose to add more attributes to what is
kept on a tombstoned object. The more attributes you allow the
directory to retain on the tombstoned object, the fewer attributes you
have to recover through other means after the object is reanimated. If
you have the Active Directory Recycle Bin enabled in your forest;
however, there is no value in preserving additional values on a
tombstone since tombstones cannot be reanimated once the Recycle Bin
is enabled.
Unfortunately, not all attributes can successfully be added to
the tombstone when the proper searchFlags
bit is set. The most obvious
examples are linked attributes such as group membership. Linked
attributes are handled differently by the directory, and thus there is
no way to force them to be retained. If you configure a linked
attribute to be preserved, Active Directory will simply ignore the
setting.
For more information on tombstones and the Active Directory Recycle Bin, refer to Chapter 18.
Creating this index allows any virtual list view (VLV)
operations using a particular attribute as the sort key to be handled
in a significantly more efficient manner. It can also prevent a common
failure that causes a VLV query to terminate with a “Critical
extension unavailable” error in larger directories. By default, when a
VLV query is processed, the directory executes the query and stores
the result set in a special internal table called the temp
table so it can be sorted. This table can vary in size, but
it can be no larger than the value specified in the MaxTempTableSize
setting of the Default
Query Policy. The value for that setting, by default, is only 10,000
records. If the query result set exceeds the size of the current temp
table, you will get the “Critical extension unavailable” error instead
of the results. Enabling the subtree container index allows AD to
avoid using the temp table, which significantly increases performance
and avoids the issue of exceeding the temp table size.
When you create an index, it is optimized for direct lookups
and, if the attribute syntax supports it, trailing wildcards—e.g.,
(name=joe*)
. If you use medial
queries—that is, queries with wildcards anywhere but the end of the
string, such as (name=*oe)
—performance tends to be rather less than
optimal. Generally, this is okay, but in the cases where an important
application is being significantly impacted due to poor medial query
performance, you may want to consider enabling a tuple index for the
attribute. This is just like enabling a normal index; you simply
enable another bit on the attribute’s searchFlags
mask to specify that a tuple
index should be created.
A tuple index is considered an expensive index, and it will increase the Active Directory database (ntds.dit) size more than a “normal” index. In addition, new attribute insertion performance will be impacted slightly. This performance hit will not be noticeable for single attribute insertions, but if you are updating a large number of attributes at once, the performance hit may be more noticeable.
A new bit for the searchFlags
attribute was defined for Windows Server 2003 Service Pack 1: the
confidential attribute flag. Any attribute that
has this flag enabled requires two permissions in order to be viewed
by a trustee (trustees are the security
principals who are granted permissions): the trustee needs Read
Property and Control Access permissions for the attribute. This
functionality was put into place primarily to protect sensitive user
attributes such as Social Security numbers and other personal
information. By default, only the administrators and account operators
have full control over all user objects, which means they will be able
to view any confidential attributes. Anyone else who has full control
over a user object will also be able to view the confidential data, so
this is yet another reason to not grant unnecessary rights in the
directory. If you have domain controllers in the domain (or global
catalogs in the forest, if you are dealing with an attribute in the
partial attribute set) that are not running Windows Server 2003
Service Pack 1 or newer, any attributes marked as confidential will
still be viewable without the special access rights on those domain
controllers or global catalogs.
The confidential attribute capability was added as a workaround to issues that exist in the current security model in Active Directory. Unfortunately, there are a large number of explicit Read Property grant permissions on objects in Active Directory that are terribly difficult to override. This new flag allows you to step in despite all the default grant permissions and quickly deny access to an attribute.
This new function was welcomed with open arms in the Active Directory community until administrators started to realize that Microsoft purposely limited the scope of the functionality by not allowing you to set Category 1 attributes as confidential. Category 1 attributes are many of the attributes defined in the default AD schema, and that list of attributes contains many of the attributes you probably want to make confidential, such as telephone numbers, addresses, employee IDs, and so on. It seems the intent is simply to give AD administrators a way to better secure custom attributes they have added to the directory with schema extensions. This limitation drastically reduces the usefulness of this capability for companies that stick to the default schema.
Modification of searchFlags
to enable confidentiality on Category 1 attributes is strictly
disallowed. If you try to change searchFlags
on one of these attributes so
that the confidential flag is set, you will get an error
message.
The default security descriptors on all AD LDS base schema objects are configured with no explicit access control entries (ACEs). The result is very few explicit Read Property grant permissions on objects when they are instantiated, which means you can more easily secure attributes with inherited deny permissions and will not need to depend on the confidential attribute functionality.
Next, we need to discuss the tools that Microsoft has made available starting with Windows Server 2003 Service Pack 1 to handle managing access to confidential attributes. The answer is easy: none. In order to grant a trustee the ability to view a specific confidential attribute on an object, a grant ACE with Control Access permission for the specific attribute needs to be added to the ACL of the object.
The GUI tools available for assigning permissions not only do not have the ability to assign this type of permission, but they can’t even display the permission if something else grants it. The command-line tool dsacls.exe is only marginally better; it can display the permission, but cannot grant the permission. The best that the GUI and the dsacls.exe tool can do is assign either full control to the object or all control access rights to the object, but neither of these is optimal if you prefer to give the minimum rights necessary to get the job done.
The version of LDP that comes with the Windows Server 2008 (and newer) RSAT tools is able to modify ACLs properly and is the best way to manage access to confidential attributes. Even if your forest is not running Windows Server 2008 or newer, you can use this version of LDP. For more information on modifying the ACL of an object, see Chapter 16.
Starting with Windows Server 2008, the auditing infrastructure has been substantially updated compared to its predecessors. In Windows 2000 and Windows Server 2003, there was a single domain-wide directory service auditing setting called “audit directory service access.” When this setting was enabled, all directory services auditing events were enabled. In a busy environment, the consequence of this was a substantial amount of security audit traffic in the event logs, to the point that it could easily become unmanageable and thus impractical to have enabled.
Windows Server 2008 and newer domain controllers separate directory services auditing into four subcategories:
Directory Service Access
Directory Service Changes
Directory Service Replication
Detailed Directory Service Replication
Of particular interest to us right now is the Directory Service Changes subcategory. We will discuss the new auditing infrastructure in much more detail in Chapter 16.
By default, all attribute changes will continue to be audited as
required by the system ACL (SACL). In order to
control noise, however, you can set bit 9 on an attribute’s searchFlags
mask to disable all change
audits for that attribute. The Windows Server 2008 GUI tools do not
expose an interactive method to easily set this bit. For directions on
setting this bit, reference the sidebar next.
The filtered attribute set is part of the new read-only domain controller (RODC) functionality in Windows Server 2008 Active Directory. RODCs can be configured to not replicate certain attributes in the Active Directory schema. There is an in-depth discussion of RODCs in Chapter 9; however, we will discuss RODCs briefly in the context of the filtered attribute set here.
RODCs were designed with the mentality that the server on which they are running is compromised by default. Consequentially, there are some attributes that we might not wish to have stored on an RODC, as they could contain sensitive information. Examples might be schema extensions that contain application-specific secrets, confidential data such as Social Security numbers, and so forth.
You can apply the process illustrated in the sidebar Controlling Attribute Change Auditing to control whether
or not attributes are included in the filtered attribute set. Instead
of toggling bit 0x100
, you should
toggle bit 0x200
.
Property sets are described in our Chapter 16 discussion on
Active Directory security. We mention them here because the creation,
modification, and identification of property sets involve the schema
partition. Part of the information for a property set is maintained in
the Configuration container in the cn=extended-rights
subcontainer, and the rest
is maintained in the schema.
The property sets are defined in the cn=extended-rights
subcontainer as controlAccessRight
objects. Two of the
attributes of the controlAccessRight
object link it to the schema. The first is the appliesTo
attribute; the second is the
rightsGuid
. The appliesTo
attribute is the string
representation of the schemaIDGUID
attribute of the classSchema
objects
that the property set applies to. The rightsGuid
is the string representation of the
binary GUID stamped on the attributeSecurityGUID
attribute of attributeSchema
objects to identify them as
members of the property set.
Microsoft allows distinguished name attributes with attributeSyntax
values of
2.5.5.1
, 2.5.5.7
, and
2.5.5.14
to be linked to attributes with an attributeSyntax
of 2.5.5.1
.
These are called linked attributes and consist of a
forward link and a back link. An example of a pair of linked attributes
is member
and memberOf
.
Attributes are linked by setting the linkID
attributes of two attributeSchema
objects to valid link values.
The values must be unique for all attributeSchema
objects. The value of the
forward link is a positive even value, and the value of the back link is
the forward linkID
value plus one, to
make it a positive odd value. Attributes must be linked when they are
first defined in the schema.
You can use any random linkID
values as long as they result in a unique linkID
pair; however, it is
highly recommended that you autogenerate link IDs.
You cannot use autogenerated link IDs in the case that you need your
schema extension to support Windows 2000. If you need to support Windows
2000, you should email [email protected] to request
unique link IDs.
In order to autogenerate link ID pairs, there are four steps you must follow:
Any attribute that is displayed in the Exchange global address
list (GAL) must have a MAPI ID defined in Active Directory. Once you
define a MAPI ID for an attribute (by storing it in the mAPIId
attribute of the attributeSchema
object), an Exchange
administrator can use the Details Template Editor (shown in Figure 5-5) to add the attribute to the
address book. The downfall of MAPI IDs is that there is no central
authority for issuing MAPI IDs, so, it is possible that there could be a
conflict. Fortunately, if your schema master is running Windows Server
2008 or newer, AD can generate a MAPI ID automatically when creating a
new attribute. To take advantage of the automatic MAPI ID generation
functionality, follow these steps:
Create a new attribute and populate the mAPIId
attribute with the value 1.2.840.113556.1.2.49
.
Reload the schema cache.
AD will assign the next available MAPI ID between 61,440
and 65,520
.
If you try to create a new attribute and explicitly specify a
MAPI ID in the reserved range of 61,440
to 65,520
, AD will return a “Will not perform”
error.
Schema classes are defined as instances of the classSchema
class. A complete listing of the
attributes on the classSchema
class is
available at this
link.
Classes are special in that they can inherit from one another. For
example, let’s say that we wanted to store two new types of objects in
the schema, representing a marketing user and a finance user,
respectively. These users both need all the attributes of the existing
user
class as a base. However, the
finance user needs three special attributes, while the marketing user
needs seven. The extra attributes required by both users do not match in
any way. In this example, we can create a marketing-user
class, a finance-user
class, and 10 distinctly new
attributes. However, rather than having to specify that the marketing-user
and finance-user
classes have each of the
attributes of the original user
class
individually, all we need to do is specify that the new classes inherit
from the user
class by setting the
subClassOf
attribute of each to
user
. When we do this, both of the
new classes inherit every single attribute that the user
class has. We can then add the extra
attributes to each class, and we have two new classes. This example is
outlined in Figure 5-6.
You can think of the Active Directory schema as a treelike
structure, with multiple classes branching down or inheriting from one
base class at the top that has the attributes all objects need to begin
with. This class, unsurprisingly enough called top
, was originally defined in the X.500
specification. Some classes inherit directly from top
, while others exist much lower down the
tree. While each class may have only one parent in this layout, each
class may also inherit attributes from other classes. This is possible
because you can create three categories of classSchema
object, also known as the objectClassCategory
:
If a class is structural, you can directly create objects of
its type in Active Directory. The user
and
group
classes are examples of structural
classes.
It is possible that you will want to create a class that inherits from other classes and has certain attributes, but that is not one you will ever need to create instances of directly. This type of class is known as abstract. Abstract classes can inherit from other classes and can have attributes defined on them directly. The only difference between abstract and structural classes is that an object that is an instance of an abstract class cannot be created in Active Directory. If you are familiar with an object-oriented programming language, abstract schema classes in Active Directory are analogous to abstract classes in the programming language.
An auxiliary class is used to store sets of attributes that other classes can inherit. Auxiliary classes are a way for structural and abstract classes to inherit collections of attributes that do not have to be defined directly within the classes themselves. Auxiliary classes are primarily a grouping mechanism.
The X.500 specifications indicate that an auxiliary class cannot inherit from a structural class, and an abstract class can inherit only from another abstract class.
To comply with the X.500 standards, there are actually four
types of objectClassCategory
. While
objects are required to be classified as one of structural, abstract,
or auxiliary by the 1993 X.500 specifications, objects defined before
1993 using the 1988 specifications are not required to comply with
these categories. Such objects have no corresponding 1993 category and
so are defined in the schema as having a special category known as the
88-Class.
Let’s take a look at the user
and computer
classes that are used to
create user and computer accounts within Active Directory. The computer
class and user
class are each structural, which means
that you can instantiate objects of those classes directly in Active
Directory. The computer
class
inherits from the user
class, so the
computer
class is a special type of
user in a way. The user
class
inherits from the organizationalPerson
Type-88 class. This means
that the total attributes available to objects of class computer
include not only
the attributes defined specifically on the computer
and user
classes themselves, but also all the
attributes that are inherited from the organizationalPerson
class. The organizationalPerson
class is a subclass of
the person
abstract class, which is a
subclass of the abstract top
class.
Recall that there are no classes above top
; it is the root class. This relationship
is outlined in Figure 5-7.
The user
class that Microsoft
needed to define in Active Directory had to be more than just the sum of
the X.500 standard parts. After all, Microsoft uses security identifiers
(SIDs) to identify users, and these were not contained in the original
X.500 standards. So, to extend the attributes that make up a user,
Microsoft defined some auxiliary classes and included these in the
user
class makeup. The auxiliary classes are mailRecipient
, securityPrincipal
, posixAccount
, and shadowAccount
. mailRecipient
is a collection of attributes
that allow a user object to hold information relating to the email
address and mail account associated with that user. Similarly, the
securityPrincipal
class holds the SID
and other user-related security attributes that Microsoft needed. The
shadowAccount
and posixAccount
auxiliary classes were added in
Windows Server 2003 R2 to support Microsoft Services for UNIX
(SFU).
If you were to use a tool such as ADSI Edit, you could see the
inheritance and class relationships quite clearly. For example, looking
at the objectClass
attribute of any
user object, you would see that at a minimum the values held in this
attribute were top
, person
, organizationalPerson
, and user
. In other words, this attribute indicates
that each user object inherits attributes from all these classes.
Similarly, for any computer object, the objectClass
attribute holds top
, person
, organizationalPerson
, user
, and computer
. If you were to look at the subclassOf
attribute on the computer
class object itself in the schema,
you would see the user
class. The
user
class has a subClassOf
attribute that indicates organizationalPerson
, and so on. You can see
this relationship in Figure 5-7.
Let’s now look at the user
class in a little more depth. Using a tool like ADSI Edit, we can see
the values of each attribute for the user classSchema
object. Table 5-7 contains a partial
listing of the attributes and their values.
User attribute’s LDAP-Display-Name | User attribute’s syntax | Value contained in user’s attribute |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| SDDL text-encoded representation of the default security descriptor |
|
|
|
|
|
|
|
|
|
|
| Binary representation of the security descriptor for the class |
|
|
|
|
|
|
|
|
|
|
|
|
|
| GUID that uniquely identifies this class |
|
|
|
|
|
|
|
|
|
|
| Various attributes (see discussion) |
|
|
|
You can learn the following about the user
class by inspecting these attribute values:
The name of the class is User
(adminDescription
, adminDisplayName
, cn
, name
).
It is an instance of the classSchema
class (objectCategory
and objectClass
).
It inherits attributes from both top
and classSchema
(objectClass
).
This object class has a security descriptor governing who can
access and manipulate it (nTSecurityDescriptor
).
A default security descriptor should be applied to new
instances of the user
class if one is not
specified on creation (defaultSecurityDescriptor
).
The instances of the user
class are
visible in normal browsing (defaultHiding
Value
).
The user
class itself is not hidden
from casual browsing (showInAdvancedView
Only
).
The user
class has an OID
of 1.2.840.113556.1.5.9 (governsID
).
It inherits attributes not only from top
and classSchema
but also from security-Principal
and mailRecipient
(objectClass
and systemAuxiliaryClass
).
The shadowAccount
and
posixAccount
auxiliary classes
can be associated with user objects.
The user
class is a direct subclass of the
organizationalPerson
class
(subClassOf
).
When connecting to instances of the class via LDAP, the
two-letter prefix used should be cn
(rDNAttID
).
This class can be created directly under only three different
parents in Active Directory
(systemPossSuperiors
).
The class is structural (objectClassCategory
).
There are a large number of attributes that instances of the
user
class can have values for (systemMayContain
).
Let’s look at mustContain
,
mayContain
, auxiliaryClass
, possSuperiors
, and their system attribute
pairs. You can see that the only values set are systemPossSuperiors
, systemMayContain
, and systemAuxiliaryClass
. These were the values
set on the initial creation of the user
class and
they cannot be changed. You can tell that there were no mandatory
attributes set at the creation of the original class because the
attribute systemMustContain
is not
listed. If you later wished to add an extra set of attributes or a new
optional attribute to the user
class, you could use
auxiliaryClass
or mayContain
and modify the base definition.
This might occur if, for example, you install the Exchange schema
updates in your forest. If you were to do this, the
user
class would be directly modified to include
three of these Exchange-related auxiliary classes in the auxiliaryClass
attribute:
msExchMailStorage
msExchCustomAttributes
msExchCertificateInformation
The attributes that are required when you create a new user are
not listed in the mustContain
attribute. That’s because objectSID
, sAMAccountName
, and the other attributes are
inherited from other classes that make up this one. The mustContain
attributes can be defined
directly in auxiliaryClass
or
systemAuxiliaryClass
, or they can
be defined on the classes from which it inherits further up the tree.
Both sAMAccountName
and objectSID
, for example, are defined on the
securityPrincipal
class.
Do not mistake attributes that a class must contain with the attributes that you must explicitly set on object instantiation. Unfortunately, there is no effective way to programmatically determine what attributes you need to set on an object when you create an instance of the class. Some of the attributes that an object must contain are system-owned and can only be set by the system; other attributes are optional and will be populated automatically; and finally, some attributes are actually required to be specified by the object’s creator. To confuse the situation even more, various versions of the OS or AD LDS change the requirements.
The same principle applies to the mayContain
attribute. The entire set of
attributes that the class may contain is available only when you
recurse back up the tree and identify all the inherited mayContain
attributes on all inherited
classes.
The possSuperiors
attribute,
on the other hand, can be made up of only those items defined directly
on the class, those defined on the class identified in the subClassOf
attribute, or those defined on
any other classes identified in the subClassOf
attributes continuing up the
subClassOf
tree. If that was too
confusing, try this: an instance of the user
class
can have possSuperiors
from itself,
from the organizationalPerson
class
defined in the subClassOf
attribute, from the person
class
(identified in the organizationalPerson
class’s subClassOf
attribute), and from top
(identified in the
person
class’s subClassOf
attribute).
Take a look at Figure 5-8. This shows the
user
class viewed with the Active
Directory Schema snap-in. You can see the relevant general user
data.
Notice that quite a bit of the user
class is
not configurable after the initial configuration, including:
governsID
subClassOf
schemaIDGUID
systemMustContain
rDNAttID
systemPossSuperiors
objectClassCategory
systemMayContain
systemOnly
systemAuxiliaryClass
objectClass
subClassOf
To see the so-called relationship settings (subClassOf
, auxiliaryClass
, possSuperiors
, systemAuxiliaryClass
, systemPossSuperiors
), look at Figure 5-9. In this screen,
you can see that the user
class in this schema is
inheriting attributes from the five auxiliary classes.
The third and final screen of interest to us here is the
Attributes tab for the user
class,
displayed in Figure 5-10. This shows the
mustContain
, systemMustContain
, mayContain
, and systemMayContain
attributes of the user
class.
Dynamically assigning auxiliary classes to individual objects instead of to an entire class of objects in the schema is a feature common in many LDAP directories. Having the dynamic auxiliary class mechanism provides much more flexibility for application developers who may want to utilize existing structural and auxiliary classes, but do not want to extend the schema to define such relationships.
Dynamic linking of auxiliary classes requires your forest to be running at the Windows Server 2003 forest functional level, or higher.
To dynamically link an auxiliary class to an object, you only need
to modify the objectClass
attribute
of the object to include the name of the auxiliary class. Any auxiliary
class can be used, provided that all mustContain
and systemMustContain
attributes contained within
the auxiliary class are set at the same time. You can also remove a
dynamically linked auxiliary class by clearing any values that have been
set for attributes defined by the auxiliary class and then removing the
auxiliary class name from the object’s objectClass
attribute.
Now let’s illustrate why dynamically linking auxiliary classes is
a good idea. Assume we have a forest with several domains, each
representing divisions within a company. Each division manages its own
user objects. One of the divisions, named Toasters, wants to assign
additional attributes to its user objects. These new attributes would
only apply to employees within the Toasters division. Under Windows
2000, the only way to accomplish this would be to create the new
attributes in the schema, create a new auxiliary class, and include the
new attributes in the auxiliary class. At that point, the new auxiliary
class could be added to the auxiliaryClass
attribute of the user classSchema
object. Every user object
contained within the forest would then have the new attributes available. If each division wanted to do
something similar, you can see how the number of attributes on all user
objects within the forest could grow very quickly and unnecessarily.
With Windows Server 2003, you would still create the new attributes and
auxiliary classes in the schema, but you would not modify the auxiliaryClass
attribute of the user object.
Instead, each division would dynamically link its auxiliary class to
its user objects. This provides for a cleaner and
much more efficient implementation than was possible under Windows
2000.
When you dynamically link an auxiliary class to an object, the
auxiliary class is listed in the objectClass
attribute for the object. When
an auxiliary class is statically linked, the auxiliary class is not
listed in the objectClass
attribute. This can cause issues with applications that need to
determine available attributes on an object and only look at the
schema definitions of an object class or at the objectClass
attribute of the object
itself.
In this chapter, we’ve shown you how the internal blueprint for all objects in Active Directory, known as the schema, was derived from the X.500 directory service. We explained the purpose of the OID numbering system and how it can be used, as well as the various elements that must be unique in an Active Directory schema extension (such as prefix names and link IDs).
We then detailed how an attribute and its syntax are structured in
the schema as attributeSchema
objects,
using the userPrincipalName
attribute
as an example. We showed how attributes are added to classes by detailing
how classes are stored in the schema as instances of classSchema
objects. To make this clearer, we
dug into the details of the user
class to see how it
was constructed. Finally, we covered how auxiliary classes can be
dynamically linked starting in Windows Server 2003 and why this is
significant.
Chapter 17 builds on what you’ve learned here to demonstrate how you can design and implement schema extensions.
[1] Names defined by the X.500 standard don’t tend to follow this
method. For example, the Common-Name attribute has an
LDAP-Display-Name of cn
, and the
Surname attribute has an LDAP-Display-Name of sn
.