Contents
Managing the Active Directory Schema
Active Directory and its Schema
Schema Master Role
Schema Administrator
Transferring the Schema Master Role
Enabling Schema Administration
Creating an Attribute
Creating an Attribute using ldifde.exe
Adding an Attribute using the MMC
Adding an Attribute Programmatically
Adding an Attribute to a Container Class
Adding an Attribute to a Container Class using ldifde.exe
Adding an Attribute to a Container Class using the MMC
Adding an Attribute to a Container Class Programmatically
Setting the New Attributes Permissions using Control Access Rights
Control Access Rights
Property Sets
The Relation between Attributes and Property Sets
Adding the New Attribute to a Property Set
Conclusion
The Microsoft Windows 2000 Active Directory is
the central repository that stores all enterprise objects such as
user accounts, computer accounts, or other network devices data.
By design, Active Directory can store hundreds of million of like
objects.
Objects are organized in a tree-like manner,
similar in many respects to folders on a drive. Each object is
uniquely defined by its position in the tree, a.k.a. its
“distinguished name”. For example, the path to the
default Guest account of a directory
called MyDomain would be:
CN=Guest,CN=Users,DC=MyDomain
This string is also known as the Lightweight Directory
Access Protocol
(LDAP) path.
LDAP is a standard way of connecting to enterprise directory services
from the Internet Engineering Task Force (IETF).
Active Directory is an LDAP-compliant application.
The distinguished name is also defined by an IETF standard
.
Each object is made of a collection of attributes.
Each object's nature is defined by its Object-Class
attribute.
Each class groups attributes together, defining which ones are mandatory and
which others are optional.
Well-known classes include user,
computer,
group and
contact.
Classes can be derived from other classes using inheritance.
For example, the user class is derived
from the organizationalPerson
class, which is in turn derived from the person
class, whose definition derives from the top
class.
All these classes' definitions constitute the
“schema”.
The schema contains formal definitions of every object class that can be created
in an Active Directory forest.
The schema also contains formal definitions of every attribute that can exist in
an Active Directory object.
The classes and attributes simply are objects (whose type or
Object-Class attribute is equal to
classSchema and
attributeSchema respectively.)
The schema is thus made of a collection of directory objects
and it is stored in the directory itself.
For e.g., a directory called MyDomain has a
configuration located at
CN=Configuration,DC=MyDomain
The configuration contains the schema:
CN=Schema,CN=Configuration,DC=MyDomain
Active Directory is a hierarchical database, which relies on
a customized high-performance version of the Jet database engine.
It is capable of storing millions of objects across several servers, called
domain controllers (DC).
It supports multi-master changes, which means that changes to one of the databases
can be processed at any given DC in the enterprise regardless of whether the DC
is connected or disconnected from the network.
This advantage introduces the possibility of conflicts that
can potentially lead to problems once the altered data is replicated to the
other domain controllers.
This is the case when two administrators modify the same user's telephone number
while connected to two different DC servers.
In Windows 2000, the conflict resolution algorithm handles discrepancies in values
by keeping the latest written data.
In other words, the “last writer wins”.
An exception to this conflict resolution rule was made for
changes that are less frequent but have far reaching consequences like schema
changes.
Obviously, if the definition of the user
objects were changed one way on one server and another way on a second DC,
the “last writer wins” approach would potentially lead to
nightmares.
Windows 2000 prevents conflicting Active Directory updates
from occurring in some cases.
It thus defines five roles that are granted only once per domain.
These roles operate under the single-master model, in a manner similar to
the primary domain controller (PDC) role in the earlier versions of Windows.
In Windows NT 3.51 and 4.0, the PDC was responsible for processing all updates
in a given domain.
Because Active Directory roles are not bound to a single DC,
they are referred to as the Flexible Single Master Operation (FSMO) roles.
Windows 2000 has five FSMO roles:
- Schema master
- Domain naming master
- RID master
- PDC emulator
- Infrastructure master
The first Active Directory domain controller in a forest is
granted all five FSMO roles.
The Schema Master FSMO role holder is the DC responsible for performing updates
to the directory schema.
This schema master sends updates to all other DC in the directory.
Like the domain master role, the schema master role is forest-wide and granted
once per forest.
This is a consequence of the fact that child domains share their parent's schema
in Windows 2000.
Before being permitted to administer the schema, a user account
must be added to the “Schema Admins” group.
On Windows 2000, a security measure also requires a special value called
“Schema Updated Allowed” in the Registry of the machine that runs
the Schema console
.
Before modifying the schema, it is necessary to be able to
connect to the schema master DC.
It may be necessary to change what computer holds this role.
It may even be necessary to force the assignment of this role if the original
domain controller is no longer online: it sometimes happens to have Active
Directory forests without a schema master because the first server was
decommissioned and the administrators only transferred the RID, PDC and
infrastructure FSMO roles as only those show in the “Active Directory
Users and Computers” MMC snap-in dialog box.
Two different methods allow moving the schema FSMO role from
one computer to another:
- The
recommended method is a transfer.
It can only be used if both DC are running.
- The
second method is to seize the role and can be used if the FSMO roles
holder is offline.
It can be used if the DC previously holding the role has been lost
.
The first method can be performed through the “Active
Directory Schema” MMC snap-in.
This snap-in can be installed by running the adminpak.msi
program
located in the \I386 folder on the Windows 2000
Installation CD.
After installation, simply run the schmmgmt.msc
file from the Start menu.
The file is located in the %System32% folder.

Figure 1 - Change Schema Master Dialog (Windows 2000)

Figure 2 - Change Schema Master Dialog (Windows 2003)
The second method requires you to use the
ntdsutil.exe tool to
seize the roles
.
The tool can be run from the command line of any DC although it is recommended to
do so on the domain controller that is taking the schema master role.
The FSMO role seizure procedure runs as follows from the command line:
- After
starting ntdsutil.exe,
type roles and
press enter.
This instruction switches to the fsmo maintenance
mode.
- At
the fsmo maintenance:
prompt, type connections,
and press enter.
This instruction switches to the server connections
mode.
- At
the server connections:
mode, type connect to server <servername>,
where servername is the name of the server you
want to use as the new schema master, and then press
enter.
- At
the server connections:
prompt, type quit,
and then press enter
again.
This returns you to the fsmo maintenance
mode.
- At
the fsmo maintenance:
prompt, type seize schema master
.
A dialog box appears and prompts the user for confirmation.
Upon clicking on the Yes
option, the ntdsutil.exe
displays the following message:
Attempting safe transfer of schema FSMO before seizure.
FSMO transferred successfully - seizure not required.
Server "WEST" knows about 5 roles
Schema - CN=NTDS Settings,CN=WEST,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=MyDomain
Domain - CN="NTDS Settings
DEL:fde9d42c-78a0-40a8-b71f-c0694b712abd",CN="WEST
DEL:dbcc4f5c-c8ae-4771-ba7d-0ff5442f593b",CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=MyDomain
PDC - CN=NTDS Settings,CN=WEST,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=MyDomain
RID - CN=NTDS Settings,CN=WEST,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=MyDomain
Infrastructure - CN=NTDS Settings,CN=WEST,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=MyDomain
- After
you seize the schema master role, type quit,
and then press enter (twice)
to exit the ntdsutil.exe tool.
Before any schema extension is performed, schema updates must
be enabled.
There are two ways of performing this operation:
- Using
the “Active Directory Schema” MMC console, open the “Operations
Masters” dialog
.
Click on the checkbox reading “The Schema may be modified on this Domain
Controller”
.
- Using
the registry editor, add the following value:
Key: HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters
Value: “Schema Update Allowed” = 0x1
Microsoft provides a small utility named LDIFDE that allows
the import and export of data in or out of Active Directory.
The name stands for LDIF Data Exchange.
The LDAP Data Interchange Format (LDIF) is a technical specification of the IETF
.
It defines a file format to store LDAP-compliant data.
Although LDIFDE is mostly useful for doing batch operations
that require accessing numerous Active Directory objects, it can also be used
efficiently for schema modifications: it ensures that schema modifications are
scripted and can be reproduced.
It is very important since they cannot be rolled back.
It is strongly recommended to perform schema changes in a test environment before
attempting to implement them in production.
An LDIF file is made of sections separated by hyphens.
The file is hyphen-terminated.
Each section starts with the dn: tag followed
by the distinguished name of the object to which the section applies.
On the next line, the changetype: tag informs
the utility of which type of change needs to be performed.
The changes can be either add or
modify.
The following lines follow this syntax:
operation: attribute_on_which_the_operation_applies
attribute_on_which_the_operation_applies: the_old_or_the_new_value
-
The operation can be replace,
add or delete.
Let us create an LDIFDE file containing instructions to add a
personal identification number attribute to Active Directory.
The file will be saved as PIN.LDIF.
# Create single-valued PIN attribute
dn: CN=Personal-ID-Number,CN=Schema,CN=Configuration,DC=MyDomain
changetype: add
objectClass: attributeSchema
cn: Personal-ID-Number
distinguishedName: CN=Personal-ID-Number,CN=Schema,CN=Configuration,DC=MyDomain
instanceType: 4
attributeID: 1.2.840.113556.1.4.7000.233.28688.28684.8.51404.45879.8978.98567.7
attributeSyntax: 2.5.5.12
isSingleValued: TRUE
showInAdvancedViewOnly: TRUE
adminDisplayName: Personal-ID-Number
adminDescription: Personal-ID-Number
oMSyntax: 64
lDAPDisplayName: pIN
name: pIN
objectCategory: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=MyDomain
rangeLower: 1
rangeUpper: 4
# Instruct Active Directory to reload the schema cache. This must
# be done or the creation of the class fails. The schemaUpdateNow
# is an operational attribute of the RootDSE.
DN:
changeType: modify
add: schemaUpdateNow
schemaUpdateNow: 1
-
A notable point in the above script is the attributeID of the
new pIN attribute.
This number is an “object identifier” (OID).
The OID is an assigned number from the ANSI standards board to make sure that no two schemas
additions ever collide.
The collision can be avoided by following one of two methods:
- You
can go to http://www.iana.org/ to claim
a root OID number to assign to your schema extension.
This step is an absolute must for any software publisher.
- For
in house applications, however, it is simpler to run OIDGEN, a tool
available in the Windows 2000 Resource Kit, which circumvents the previous
step.
OIDGEN creates a random unique identifier such as the one in the above code.
The command to run the script is
ldifde -i -f pin.ldif -v -j
The new attribute can be seen in the “Active Directory
Schema” MMC snap-in:

Figure 3 - Personal Identification Number Attribute
It is also possible to add the attribute through the “Active
Directory Schema” MMC snap-in (located in the
%System32%
folder):

Figure 4 - Create New Attribute
This step does not have the advantage to be easily reproduced
when moving from a test environment to production.
However, it may be a smart short cut to generate the LDIF file: after creating the
attribute in a test environment, simply export the new attribute using the LDIFDE
utility to generate the LDIF code:
ldifde -f pin -d "CN=Personal-ID-Number,CN=Schema,CN=Configuration,DC=MyDomain"
The file is ready to import following the procedure described
in the previous section.
The following Visual Basic Script adds the attribute programmatically
using the ADSI library interfaces.
An advantage of this solution is that there is no need to specify
the domain name as it was done in the LDIF script.
By querying the root of the directory (rootDSE),
the script is able to locate a domain controller and its supporting schema.
This built-in capability helps migrating scripts from development to production.
Once the schema is found, the only task left is to add a new
container of type attributeSchema, populate
its attributes, and commit the changes (using the
SetInfo() method.)
Option Explicit
On Error Resume Next
Err.Clear()
' Get the root Directory Service Entry (rootDSE)
Dim adsRootDSE, adsSchema, adsAttribute
Set adsRootDSE = GetObject("LDAP://rootDSE")
' Access the schema
Set adsSchema = GetObject("LDAP://" & adsRootDSE.Get("SchemaNamingContext"))
' Add and populate attribute
Set adsAttribute = adsSchema.Create("attributeSchema", "CN=Personal-ID-Number")
' Naming
adsAttribute.Put "adminDescription", "Personal-ID-Number"
adsAttribute.Put "adminDisplayName", "Personal-ID-Number"
adsAttribute.Put "lDAPDisplayName", "pIN"
adsAttribute.Put "name", "Personal-ID-Number"
' Object ID (created by OIDGen.exe)
adsAttribute.Put "attributeID", _
"1.2.840.113556.1.4.7000.233.28688.28684.8.51404.45879.8978.98567.7"
' Unicode string
adsAttribute.Put "attributeSyntax", "2.5.5.12"
adsAttribute.Put "oMSyntax", 64
' Defaults to FALSE if not specified
adsAttribute.Put "isSingleValued", True
' Attribute size
adsAttribute.Put "rangeLower", 1
adsAttribute.Put "rangeUpper", 4
' Commit changes
adsAttribute.SetInfo
' Check for errors
Dim sMsg
If Err.number <> 0 Then
sMsg = "Unexpected error creating attribute." & vbNewLine & _
Err.Description & " (" & Hex(Err.number) & ")"
WScript.Echo sMsg
End If
The same operation can be carried out using the
DirectoryServices namespace classes in
Visual Basic .Net:
Imports System.DirectoryServices
Module Add_Attribute
Sub Main()
' Get the root Directory Service Entry (rootDSE)
Dim adsRootDSE As New DirectoryEntry("LDAP://rootDSE")
' Access the schema
Dim sSchema As String = _
adsRootDSE.Properties.Item("SchemaNamingContext").Value
Dim adsSchema As New DirectoryEntry("LDAP://" & sSchema)
' Add and populate attribute
Dim adsAttribute As DirectoryEntry = _
adsSchema.Children.Add("CN=Personal-ID-Number", "attributeSchema")
' Naming
adsAttribute.Properties("adminDescription").Value = "Personal-ID-Number"
adsAttribute.Properties("adminDisplayName").Value = "Personal-ID-Number"
adsAttribute.Properties("lDAPDisplayName").Value = "pIN"
adsAttribute.Properties("name").Value = "Personal-ID-Number"
' Object ID (created by OIDGen.exe)
adsAttribute.Properties("attributeID").Value = _
"1.2.840.113556.1.4.7000.233.28688.28684.8.51404.45879.8978.98567.7"
' Unicode string
adsAttribute.Properties("attributeSyntax").Value = "2.5.5.12"
adsAttribute.Properties("oMSyntax").Value = 64
' Defaults to FALSE if not specified
adsAttribute.Properties("isSingleValued").Value = True
' Attribute size
adsAttribute.Properties("rangeLower").Value = 1
adsAttribute.Properties("rangeUpper").Value = 4
' Commit changes
adsAttribute.CommitChanges()
End Sub
End Module
At this point, we need to add the attribute as one of the optional
ones on the user class.
This way, users will have the ability to store a 4-digit PIN in their Active
Directory profile.
To do so, we must add one more value (pIN) to
the multi-valued mayContain attribute of
the user class.
This can be done using the LDIFDE tool in a fashion similar to
the previous LDIF script.
We save the following script as user.ldif:
# Add the new attributes to the User class
dn: CN=User,CN=Schema,CN=Configuration,DC=MyDomain
changetype: modify
add: mayContain
mayContain: pIN
-
# Reload the schema
DN:
changeType: modify
add: schemaUpdateNow
schemaUpdateNow: 1
-
The above LDIF script is executed by running the following command:
ldifde -i -f user.ldif -v -j
This completes adding the new attribute to the
user class.
The operation can also be achieved via the “Active Directory
Schema” MMC snap-in.
To do so, simply open the user class properties
by double-clicking on the class.
This brings up the following dialog box:

Figure 5 - User Class Properties
The third tab, “Attributes”, displays an “Add” button
allowing an Administrator to add an attribute to the existing list.

Figure 6 - Adding Attributes to the User Class
Before we can modify or assign a PIN to a user, we must alter the
user's class.
Specifically, we need to add the new pIN
attribute to the list of attributes that a user instance
“mayContain.”
The Visual Basic Script used to perform this task is very similar to the one
above:
Option Explicit
On Error Resume Next
Err.Clear()
' Get the root Directory Service Entry (rootDSE)
Dim adsRootDSE, adsUserClass
Set adsRootDSE = GetObject("LDAP://rootDSE")
' Access the user class in the schema
Set adsUserClass = GetObject("LDAP://CN=User," & _
adsRootDSE.Get("SchemaNamingContext"))
' Add the new attribute to the user class
adsUserClass.Put "mayContain", "pIN"
' Commit changes
adsUserClass.SetInfo
' Check for errors
Dim sMsg
If Err.number <> 0 Then
sMsg = "Unexpected error adding the attribute." & _
vbNewLine & Err.Description & " (" & Hex(Err.Number) & ")"
WScript.Echo sMsg
End If
The same operation can be carried out using the
DirectoryServices namespace
classes in Visual Basic .Net:
Imports System.DirectoryServices
Module Add_Attribute
Sub Main()
' Get the root Directory Service Entry (rootDSE)
Dim adsRootDSE As New DirectoryEntry("LDAP://rootDSE")
' Access the user class in the schema
Dim sSchema As String = _
adsRootDSE.Properties.Item("SchemaNamingContext").Value
Dim adsUserClass As New DirectoryEntry("LDAP://CN=User," & sSchema)
' Add the new attribute to the user class
adsUserClass.Properties("mayContain").Add("pIN")
' Commit changes
adsUserClass.CommitChanges()
End Sub
End Module
After adding the pIN attribute
as a potential one for the user class, it is
now possible to add that field to any instance of the user
class (using LDIFDE, ADSI Edit, or a custom Visual Basic program.)
However, only Domain Administrators have the permission to do so.
A user cannot update the pIN attribute of his
own container.
On the contrary, a user can update some of the other
user class attributes, such as
telephoneNumber or
street.
The difference between the new pIN
and the native telephoneNumber attributes is that
the latter is part of a group of attributes called
Personal-Information.
This object is called a control access right.
Active Directory control-Access-Right
objects define the Control Access Rights.
These objects are located in the CN=Configuration,DC=<domain>
container and come in three flavors:
- Extended Rights
- Validated Writes
- Property Sets
Extended rights cover special permissions beyond the standard set
of rights.
They are used by external applications, such as Exchange Server, that rely heavily
of Active Directory and its advanced security.
Validated Writes rights are validation logic applied to Active
Directory and set conditions on data prior to writing to the disk.
The last ones, property sets, are used to control access to a subset
of an object's attributes, skipping the necessity to define individual attributes
permissions.
The Personal-Information falls in this later
category.
Property sets are used to group schema attributes in a business
logic manner.
Once a property set is defined, it is possible to set the access right on the entire
property set at once rather than on each of the properties of the set.
Active Directory pre-defines the following property sets:
- DNS-Host-Name-Attributes
- Domain-Other-Parameters
- Domain-Password
- Email-Information
- General-Information
- Membership
- Personal-Information
- Public-Information
- RAS-Information
- User-Account-Restrictions
- User-Logon
- Web-Information
The Email-Information
property set contains user attributes that describe user email related information.
The Public-Information property set contains
attributes that describe public information for the user,
computer or inetOrgPerson
classes.
The Personal-Information
property set contains attributes that describe personal information for the
user, contact,
computer or
inetOrgPerson classes.
Users can change the content of all the attributes that are part
of the Personal-Information property set because
that set's permissions contains read/write for the self
account.
The self is a surrogate for the entry
itself and is used to give access to an entry to itself.
Like other objects, control-Access-Right
have attributes, including the following two:
-
rightsGUID that uniquely identifies
a control access right
-
validAccesses that sets the access to be
granted by the extended right
All the attributes that belong to a property set have the same
attribute-Security-GUID property value.
This GUID matches the rightsGUID of the
control-Access-Right object that represents
their property set.

Figure 7 - Key Relationships
Restrictions are imposed on modification of existing schema
objects.
The schema is divided into two categories.
The schema objects that ship with Microsoft Windows 2000 in the base schema belong
to Category 1.
All schema objects added later by other applications or users through dynamic schema
extension belong to Category 2.
The 0x10 bit set in the systemFlags attribute
determines the category of a schema object.
This bit is only set on Category 1 objects, and cannot be altered, nor can it be set
on any Category 2 object.
This leads us to our final step which is the setup of the security
of the new attribute.
In order to give it the permission set forth in the
Personal-Information
extended right, we can simply copy the
attributeSecurityGUID
field of another attribute (for e.g.
Street-Address)
that relies on this same extended right.
Using the ADSI Edit MMC snap-in, it is easy to open the new
attribute and copy the proper value to the
attributeSecurityGUID
field.
To perform the task with an LDIF script, a single line needs
to be added to the PIN.LDIF file:
attributeSecurityGUID: 0x86 0xB8 ............. 0x67 0xC1
In Visual Basic Scripting, the code is a follows:
' Get the root Directory Service Entry (rootDSE)
Dim rootdse
Set rootdse = GetObject("LDAP://rootDSE")
' Get the CN=Street-Address located in the schema
Dim sStreet, oStreet
sStreet = "LDAP://CN=Street-Address," & rootdse.Get("SchemaNamingContext")
Set oStreet = GetObject(sStreet)
' Get the CN=Personal-ID-Number located in the schema
Dim sPIN, oPIN
sPIN= "LDAP://CN=Personal-ID-Number," & rootdse.Get("SchemaNamingContext")
Set oPIN = GetObject(sPIN)
' Copy the attributeSecurityGUID value from the CN=Street-Address
' to the CN=Personal-ID-Number attribute
oPIN.Put "attributeSecurityGUID", oStreet.Get("attributeSecurityGUID")
oPIN.SetInfo
An alternative would be to copy the
rightsGUID field of the
Personal-Information
extended right.
However, we would have to perform a type conversion and the above code is thus simpler.
In Visual Basic .Net, the same code looks as follows:
Imports System.DirectoryServices
Module Copy_Security
Const sSource As String = "Street-Address"
Const sDestination As String = "Personal-ID-Number"
Sub Main()
' Get the rootDSE and open the configuration container
Dim rootdse As New DirectoryEntry("LDAP://rootDSE")
Dim sSourceDN As String, oSource As DirectoryEntry
sSourceDN = "LDAP://CN=" & sSource & "," & _
rootdse.Properties("SchemaNamingContext").Value
oSource = New DirectoryEntry(sSourceDN)
oSource.RefreshCache()
Dim sDestinationDN As String, oDestination As DirectoryEntry
sDestinationDN = "LDAP://CN=" & sDestination & "," & _
rootdse.Properties("SchemaNamingContext").Value
oDestination = New DirectoryEntry(sDestinationDN)
oDestination.RefreshCache()
Dim oGUID As Byte()
oGUID = CType(oSource.Properties("attributeSecurityGUID").Value, Byte())
oDestination.Properties("attributeSecurityGUID").Clear()
oDestination.Properties("attributeSecurityGUID").Add(oGUID)
oDestination.CommitChanges()
End Sub
End Module
The pIN attribute is now part
of the user class and can be self-edited
by any user logged on to the domain.
This would allow changes to that attribute by any application using integrated security
to connect to the directory.
By virtue of the Personal-Information control access
right, other users can read but not change each other's pIN
data.
Microsoft Active Directory offers a large range of pre-defined control
access rights that should generally cover the needs of solution architects.
In the rare cases when the requirements do not fall in this category, one can simply
add a new controlAccessRight container in the
domain's configuration using a script, the ADSI Edit MMC snap-in, LDIFDE, or Visual
Basic .Net.
So far we have learned, how to create a new Active Directory attribute,
add it to an existing container (user class), and configure its security using Active
Directory control access rights by Visual Basic script, programmatically using Visual
Basic .Net and manually using MMC.
|