How Domain Controllers Are Located (DCLocator)

 

This article describes how Windows client locates a domain controller. On the client, the NetLogon service verifies logon requests, and it registers, authenticates, and locates domain controllers by using the DsGetDcName API call (known as the domain controller locator function).

The following sequence describes how the Locator finds a domain controller during the logon process:

      1. Client does a DNS query to get a list of DC of the current domain in the form: _LDAP._TCP.dc._msdcs.domainname. (all domain controllers register this SRV record).
      2. Client receives a list of DC IP addresses from DNS (they are ordered following priority & weight).
      3. The client begins querying the DCs in turn to find out which DC is available. It sends a datagram LDAP UDP search which contains the IP address of the client (IP address ONLY without the subnet).
      4. The DC looks up the client IP address in its subnet-to-site mapping table and returns:
        • the subnet object that most closely matches the client IP address
        • The name of the site in which the current domain controller is located
        • A flag that indicates if the current DC is in the site closest to the client.
      5. The client decides whether to try to find a better domain controller. The decision is made as follows:
        • If the returned DC is in the closest site (the returned bit is set), the client uses that DC.
        • If the client has already tried to find a DC in the site in which the DC claims the client is located, the client uses that DC.
        • If the DC is not in the closest site, the client updates its site information and sends a site specific DNS query (_LDAP._TCP.sitename._sites.domainname) to find a new DC in the site. If the second query is successful, the client uses the new DC. If the second query fails, the client uses the original DC.

Automatic Site Coverage
There is not necessarily a domain controller in every site. If a site contains no DCs, then DCs in the sites closest to that site (calculated by site-link costs) will register site-specific records for that site as well, to help clients find a DC as close as possible.

This process known as automatic site coverage ensures that every site has a domain controller, even if a site does not contain a domain controller for that domain.

By default, each domain controller checks all sites in the forest and then checks the replication cost matrix. A domain controller advertises itself (registers a site-related SRV record in DNS) in any site that does not have a domain controller and for which its site has the lowest-cost connections.

Clients with No Apparent Site
Sometimes the client pings a domain controller and the client IP address cannot be found in the subnet-to-site mapping table. In this case, the domain controller returns a NULL site name, and the client uses the returned domain controller.

How to determine the fsmo role holder (fsmoRoleOwner attribute)

This article describes how to find the servers that hold the Flexible Single Master Operation (FSMO) roles in a forest.

Active Directory defines five FSMO roles:

Per-forest roles, one per forest:

  • Schema master
  • Domain naming master

Per-domain roles, one per domain:

  • RID master
  • PDC master
  • Infrastructure master

 

To determine the master for a partition, query the fSMORoleOwner attribute on the corresponding object under the naming context root in question:

Schema Master:
LDAP://cn=Schema,cn=Configuration,dc=contoso,dc=com

Domain Naming Master:
LDAP://cn=Partitions,cn=Configuration,dc=contoso,dc=com

PDC Role Owner:
LDAP://dc=concorp,dc=contoso,dc=com

Infrastructure Master:
LDAP://cn=Infrastructure, dc=concorp,dc=contoso,dc=com

RID Master:
LDAP://cn=RID Manager$,cn=System, dc=concorp,dc=contoso,dc=com

You can use tools such as ldifde to perform queries to get FSMO role holders:

ldifde -f Infrafsmo.ldf -d "CN=Infrastructure,DC=concorp,DC=contoso,DC=com" -l fSMORoleOwner

This query returns the infrastructure master role owner for the DC=concorp,DC=contoso,DC=com partition to the Infrafsmo.ldf file.

The information in the attribute is stored as a DN, representing the NTDS Settings object of the DC that is the role owner. Example:

CN=NTDS Settings,CN=DC1,CN=Servers,CN=SITE1,CN=Sites,CN=Configuration,DC=contoso,DC=com

The following c# code returns the PDC role owner:

static void Main(string[] args)
{
  DirectoryEntry DomDn = new DirectoryEntry("LDAP://dc=concorp,dc=contoso,dc=com");
  DirectoryEntry PDCfsmo = new DirectoryEntry("LDAP://" + DomDn.Properties["fsmoRoleOwner"].Value.ToString());

  Console.WriteLine (PDCfsmo.Parent.Properties["dnsHostName"].Value.ToString());

  PDCfsmo.Close();
  DomDn.Close();
}

Same as previoulsy, the following VBscript code returns the PDC role owner:

Set objDomDn = GetObject("LDAP://dc=concorp,dc=contoso,dc=com")
strfsmoRoleOwner = objDomDn.Get("fsmoRoleOwner")

Set objPDCfsmo = GetObject("LDAP://" &  strfsmoRoleOwner)
Set objPDCfsmoParent = GetObject(objPDCfsmo.Parent)
 
Wscript.Echo  objPDCfsmoParent.Get("dnsHostName")

Flexible Single Master Operation (FSMO)

To prevent conflicting updates in Windows, Active Directory performs updates to certain objects in a single-master fashion. In a single-master model, only one DC in the entire directory is allowed to process updates, it is referred to as a Flexible Single Master Operation (FSMO) role. Currently in Windows there are five FSMO roles:

Schema master: responsible for performing updates to the directory schema (that is, the schema naming context or LDAP://cn=schema,cn=configuration,dc=<domain>).

Domain naming master: responsible for making changes to the forest-wide domain name space of the directory (that is, the Partitions\Configuration naming context or LDAP://CN=Partitions, CN=Configuration, DC=<domain>).

RID master: each DC is assigned a pool of RIDs from the global RID pool by the domain controller that holds the RID master role. The RID master (also known as the RID pool manager, RID manager, or RID operations master) is responsible for issuing a unique RID pool to each domain controller in its domain.

Each security principals (Users, computers, and groups) is assigned a unique alphanumeric string called a SID. The SID includes a domain prefix identifier that uniquely identifies the domain and a relative identifier (RID) that uniquely identifies the security principal within the domain.

PDC emulator: it is also responsible for time synchronizing within a domain. It is also the password master for a domain. Any password change is replicated to the PDC emulator as soon as is practical.

Infrastructure master: When an object in one domain is referenced by another object in another domain, it represents the reference by the GUID, the SID (for references to security principals), and the DN of the object being referenced. The infrastructure FSMO role holder is the DC responsible for updating an object’s SID and distinguished name in a cross-domain object reference. There is one Infrastructure FSMO role per domain and application NC in a directory.

Active Directory naming contexts (directory partitions)

Active Directory is the central repository in which all objects in an enterprise and their respective attributes are stored. It is a hierarchical, multi-master enabled database, capable of storing millions of objects. Those objects areorganized as a treeand stored into sections, called naming contexts or directory partition.

Each directory partition contains a hierarchy (subtree) of directory objects. At minimum, each domain controller stores three NCs in its local copy of the Active Directory database.
Active Directory naming contexts

Configuration NC contains objects about sites, services, and directory partitions. Every domain controller in the forest has a replica of the same configuration partition.

Configuration NC

Schema NC contains the Schema container, which stores class and attribute definitions for the forest. Every domain controller in the forest has a replica of the same schema partition

Domain NC : The domain partition contains the directory objects, such as users and computers, associated with the local domain. Each domain controller stores a full replica of the domain partition for its local domain, but does not store replicas of the domain partitions for other domains

 

How to Get all Group Policy links applied to an OU (gPLink attribute)

In GPO terms, domains, OUs, and sites are called Scopes of Management (SOMs).
SOM container object, such as OU has an attribute called gPLink that lists all of the GPOs applied to the object.
This attribute contains a list of GPO distinguished names and a Boolean to indicate whether the GPO DN is enforced. It looks like this :

 
[<GPO DN_1>;<GPLinkOptions_1>][<GPO DN_2>;<GPLinkOptions_2>]... [<GPODN_n>;<GPLinkOptions_n>]

Ex: 
[LDAP://cn={8BE35F55-E3DF-4D1C-8C3A-39F81F451D86},cn=policies,cn=system,DC=ad,DC=foo,DC=local;2]
[LDAP://cn={946584E4-F1CD-458E-8366-8A549FF2E4B2},cn=policies,cn=system,DC=ad,DC=foo,DC=local;0]
[LDAP://cn={92845926-AE1B-49C4-A33A-92441D29DDB7},cn=policies,cn=system,DC=ad,DC=foo,DC=local;1]

GPLinkOptions meaning:
0: The GPO Link is not ignored and is not an enforced GPO. This is the default value
1: The GPO Link MUST be ignored.
2: The GPO Link is an enforced GPO.

The order in which GPO paths appear in this attribute specifies the link order for the associated GPOs. In the GPMC console, you will see :
GPO DN_n
..
GPO DN_2
GPO DN_1

The “GPO DN_n” will have precedence over “GPO DN_n-1” .. “GPO DN_2”, “GPO DN_1”

GetGPO("LDAP://OU=sales,OU=people,DC=ad,DC=foo,DC=local")

Function GetGPO (objldap)

 Set objContainer = GetObject (objldap)
 strGpLink = objContainer.Get("gPLink")
	
 If Len(strGpLink) > 0 Then
		
   arrGpLinkItems = Split(strGpLink,"]")
   For each el in arrGpLinkItems
	WScript.Echo Mid(el,2, Len(el)-3)
	Set objgpo = GetObject (Mid(el,2, Len(el)-3))
	WScript.Echo objgpo.DisplayName
   Next
	
 End If	     
End Function   

How Can I Get a List of All the Domains in a Forest

Here’s a sample C# code that searches for all the domain objects.
Because we want to search the global catalog we need to use the global catalog provider. Thus instead of specifying LDAP: in our binding string we specify GC.

static void Main(string[] args)
{
	DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
	String gcPath = rootDSE.Properties["rootDomainNamingContext"].Value.ToString();
	DirectoryEntry gcRoot = new DirectoryEntry("GC://" + gcPath);

	DirectorySearcher ds = new DirectorySearcher();
	ds.SearchRoot = gcRoot;
	ds.SearchScope = SearchScope.OneLevel;
	ds.PageSize = 200;
	ds.CacheResults = false;
	ds.ClientTimeout = TimeSpan.FromSeconds(60);
	ds.Filter = "(&(objectClass=domain))";
	ds.PropertiesToLoad.Add("DC");
	ds.PropertiesToLoad.Add("distinguishedName");

	SearchResultCollection src = ds.FindAll();
	foreach (SearchResult result in src)
	{
		if (result.Properties.Contains("DC"))
		{
			Console.WriteLine((string)result.Properties["distinguishedName"][0]);
		}
	}
}

With .NET Framework 4.5 there is a very simple way to get all domains in the forest by using the class System.DirectoryServices.ActiveDirectory.Forest

static void Main(string[] args)
{
	using (Forest forest = Forest.GetCurrentForest())
	{
		Console.WriteLine("Forest:  {0}", forest.Name);
		foreach (Domain domain in forest.Domains)
		{
			Console.WriteLine(String.Format("Domain:  {0}", domain.Name));
			domain.Dispose();
		}
	}
}

System.DirectoryServices.Protocols

System.DirectoryServices.Protocols is a namespace designed for LDAP programming. It provides capabilities that were previously unavailable to managed code programmers such as running an attribute scoped query against an LDAP directory or binding to a directory server using X.509 client and server certificates. Therefore, if you plan to use .NET managed code against other LDAP directories, a great place to focus is on S.DS.P

Common Patterns:

 
LdapConnection connection = new LdapConnection("fabrikam.com");

DirectoryRequestType request = new DirectoryRequestType(parameters…);

DirectoryResponseType response = (DirectoryResponseType)connection.SendRequest(request);

System.DirectoryServices.ActiveDirectory

The System.DirectoryServices.ActiveDirectory namespace provides a high level abstraction object model for forest, domain, site, subnet, partition, and schema. It is used to automate Active Directory management tasks. It is not used to access data that resides within Active Directory or any other directory service. The System.DirectoryServices namespace should be used for this purpose.

The .NET Framework System.DirectoryServices namespace

The System.DirectoryServices namespace contains two component classes, DirectoryEntry and DirectorySearcher, which use the ADSI technology.
The DirectoryEntry class encapsulates a single entry in the Active Directory database hierarchy. Use this class for binding to objects, reading properties, updating attributes and enumerating children. You can delete objects, create child objects, modify the attributes on objects or read attributes off of objects.
The DirectorySearcher Class allows us to perform queries against the Active Directory Domain Services hierarchy. Executing a search through DirectorySearcher returns a SearchResult, which are contained in an instance of the SearchResultCollection class. A SearchResult or a SearchResultCollection is a read-only representation of these objects. If you want to do any read/write operations with them, you need to convert it to a DirectoryEntry object.

Active directory programming with .NET

When programming with Active Directory you can use several technologies. Microsoft has created 4 namespaces for directory services programming in managed code:

  • System.DirectoryServices: a simple managed interop layer over Active Directory Service Interfaces (ADSI) COM component. This namespace provides simple programming access to LDAP directories, such as Active Directory and any type of LDAP Server (ex: open LDAP Server). While the programming model is reasonably powerful, there’s no strongly typed objects, you’re responsible for a lot of things
  • System.DirectoryServices.ActiveDirectory: introduced in .NET Framework 2.0, it is a wealth of new classes for strongly typed management of directory infrastructure-level components, such as servers, domains, forests, schema, and replication
  • System.DirectoryServices.Protocolsintroduced in .NET Framework 2.0, provides raw access to underlying LDAP-based directories, such as Active Directory and Active Directory Lightweight Directory Services (AD LDS). This skips ADSI, so you get better performance, but it’s a lot harder to use.
  • System.DirectoryServices.AccountManagement: it is built on System.DirectoryServices and was introduced with .NET 3.5. This namespace is only for Active Directory or AD LDS. It works against User, Group, and Computer objects and they are strongly typed objects.
Microsoft Directory Services Programming Architecture

Microsoft Directory Services Programming Architecture