5.64.2 Determining If a Name Is in a Trusted Forest
This section describes procedures that use the forest trust information contained in the msDS-TrustForestTrustInfo attribute to determine if a given domain is in a trusted forest.
The procedures described in this section use the following data structures.
-
struct { ULONG RecordCount; PX_FOREST_TRUST_RECORD *Entries; } X_FOREST_TRUST_INFORMATION; struct { ULONG Flags; FOREST_TRUST_RECORD_TYPE ForestTrustType; LARGE_INTEGER Time; union { LPWSTR TopLevelName; X_FOREST_TRUST_DOMAIN_INFO DomainInfo; X_FOREST_TRUST_BINARY_DATA Data; } ForestTrustData; } X_FOREST_TRUST_RECORD, *PX_FOREST_TRUST_RECORD; struct { SID *Sid; LPWSTR DnsName; LPWSTR NetbiosName; } X_FOREST_TRUST_DOMAIN_INFO; struct { ULONG Length; BYTE *Buffer; } X_FOREST_TRUST_BINARY_DATA;
The X_FOREST_TRUST_INFORMATION structure previously defined is used by the procedure to determine if a given domain is in a trusted forest. To unmarshal the content of the msDS-TrustForestTrustInfo attribute into this structure, the UnmarshalForestTrustInfo procedure described below can be used.
-
procedure ExtractString( buffer: sequence of BYTE, index: DWORD, size: DWORD): unicodestring;
The sequence [index .. index + size] of bytes in buffer is interpreted as a UTF-8 string, and a corresponding unicodestring (section 3.4.3) is returned.
-
procedure ExtractSid( buffer: sequence of BYTE, index: DWORD, size: DWORD): SID;
The sequence [index .. index + size] of bytes in buffer is converted into a SID structure and returned.
-
procedure ExtractBinary( buffer: sequence of BYTE, index: DWORD, size: DWORD): sequence of BYTE;
The sequence [index .. index + size] of bytes in buffer is returned.
-
procedure UnmarshalForestTrustInfo (inputBuffer: sequence of BYTE, var forestTrustInfo: X_FOREST_TRUST_INFORMATION): boolean
Informative summary of behavior: The UnmarshalForestTrustInfo procedure unmarshals the byte stream inputBuffer, which holds the content of a msDS-TrustForestTrustInfo attribute that contains forest trust information, as described in FOREST_TRUST_INFORMATION, into the forestTrustInfo structure.
-
index: DWORD pdwVersion: ADDRESS OF DWORD pdwRecordCount: ADDRESS OF DWORD i: DWORD pwdRecordLength: ADDRESS OF DWORD pTrustRecord: ADDRESS OF X_FOREST_TRUST_RECORD pulTime: ADDRESS OF ULONGLONG pType: ADDRESS OF BYTE pSid: ADDRESS OF SID pString: ADDRESS OF unicodestring pdwSize: ADDRESS OF DWORD index := 0 pdwVersion := ADR(inputBuffer[index]) if pdwVersion^ ≠ 1 then return false endif index := index + 4 pdwRecordCount := ADR(inputBuffer[index]) forestTrustInfo.RecordCount := pdwRecordCount^ index := index + 4 /* Extract each record */ for i:= 0 to pdwRecordCount^ /* First 4 bytes of the record is the length */ pdwRecordLength := ADR(inputBuffer[index]) index := index + 4 pTrustRecord := forestTrustInfo.Entries[i] /* Next 4 bytes of the record are the flags */ pdwFlags := ADR(inputBuffer[index]) pTrustRecord^.Flags := pdwFlags^ index := index + 4 /* Next 8 bytes of the record represent the Time field */ pulTime := ADR(inputBuffer[index]) pTrustRecord^.Time := pulTime^ index := index + 8 /* Next byte represents trust type */ pType := ADR(inputBuffer[index]) pTrustRecord^.ForestTrustType := pType^ index := index + 1 if (pTrustRecord^.ForestTrustType = ForestTrustTopLevelName or pTrustRecord^.ForestTrustType = ForestTrustTopLevelNameEx) then /* Next 4 bytes represent the size of the top level name */ pdwSize := ADR(inputBuffer[index]) index := index + 4 /* Extract the top level name; index is at the start of name */ pTrustRecord^.TopLevelName := ExtractString(inputBuffer, index, pdwSize^) index := index + pdwSize^ else if (pTrustRecord^.ForestTrustType = ForestTrustDomainInfo) then /* Next 4 bytes represent the size of the sid */ pdwSize := ADR(inputBuffer[index]) index := index + 4 /* Extract the sid; index is at the start of sid */ pTrustRecord^.DomainInfo.Sid := ExtractSid(inputBuffer, index, pdwSize^) index := index + pdwSize^ /* Next 4 bytes represent the size of the dns domain name */ pdwSize := ADR(inputBuffer[index]) index := index + 4 /* Extract the dns domain name; index is at start of name */ pTrustRecord^.DomainInfo.DnsName := ExtractString(inputBuffer, index, pdwSize^) index := index + pdwSize^ /* Next 4 bytes represent the size of the netbios * domain name */ pdwSize := ADR(inputBuffer[index]) index := index + 4 /* Extract the netbios domain name; index is at the start * of name */ pTrustRecord^.DomainInfo.NetbiosName := ExtractString(inputBuffer, index, pdwSize^) index := index + pdwSize^ else /* Next 4 bytes represent the size of the binary data */ pdwSize := ADR(inputBuffer[index]) pTrustRecord^.Data.Length := pdwSize^ index := index + 4 /* Extract the binary data; index is at the start of data */ pTrustRecord^.Data.Buffer := ExtractBinary(inputbuffer, index, pdwSize^) index := index + pdwSize^ endif endif /* index is now at the beginning of the next record */ endfor return true
The following procedures are used to determine if a given domain name, SID, or UPN is in a trusted forest. Since they make use of forest trust information data stored in objects in the NC replica of the forest root domain (see FOREST_TRUST_INFORMATION), these functions only work on GC servers or DCs in the forest root domain.
-
procedure IsDomainNameInTrustedForest(name: unicodestring, referredDomain: unicodestring): boolean
Informative summary of behavior: The IsDomainNameInTrustedForest procedure determines if the domain with the name given by name is in a trusted forest. The input name can be a DNS or a NetBIOS name.
-
if IsDomainDnsNameInTrustedForest(name, referredDomain) then return true endIf if IsDomainNetbiosNameInTrustedForest(name, referredDomain) then return true endIf return false procedure IsDomainSidInTrustedForest(sid: SID): boolean
Informative summary of behavior: The IsDomainSidInTrustedForest procedure determines if the domain with the SID given by sid is in a trusted forest.
-
tdos: set of DSName f: X_FOREST_TRUST_INFORMATION b: boolean tdos := select all o in Children ForestRootDomainNC() where trustedDomain in o!objectClass and o!trustAttributes & 0x00000008 ≠ 0 and o!msDS-TrustForestTrustInfo ≠ null foreach o in tdos if not UnmarshalForestTrustInfo(o!msDS-TrustForestTrustInfo, f) then return false else foreach e in f.Entries if (e.ForestTrustType = ForestTrustDomainInfo and e.DomainInfo.Sid = sid and LSA_FTRECORD_DISABLED_REASONS not in e.Flags) then b := true foreach g in f.Entries if (g.ForestTrustType = ForestTrustTopLevelNameEx and LSA_FTRECORD_DISABLED_REASONS not in g.Flags and (g.TopLevelName = e.DomainInfo.DnsName or IsSubdomainOf(e.DomainInfo.DnsName, g.TopLevelName))) then b := false break endif endfor if b then return true endif endif endfor endif endfor return false procedure IsUPNInTrustedForest(upn: unicodestring): boolean
Informative summary of behavior: The IsUPNInTrustedForest procedure determines if the domain containing the account with the UPN given by upn is in a trusted forest.
-
interpret upn as being in the format "username@domainName" return IsNamespaceInTrustedDomain(domainName, trustedForestName)
The IsDomainNameInTrustedForest procedure uses the following helper procedures to determine if a domain is in a trusted forest.
-
procedure IsSubdomainOf(subdomainName: unicodestring, superiordomainName: unicodestring): boolean
The IsSubdomainOf procedure takes a pair of domain names and returns true if subdomainName is a subdomain of superiordomainName as described in [RFC1034] section 3.1, and false otherwise.
-
procedure ForestTrustOwnsName(f: X_FOREST_TRUST_INFORMATION, name: unicodestring): boolean /* if the name matches or is a subdomain of one in the exclusion list, the * forest does not own this name */ foreach e in f.Entries if (e.ForestTrustType = ForestTrustTopLevelNameEx and (e.TopLevelName = name or IsSubdomainOf(name, e.TopLevelName))) then return false endif endfor /* if a suffix of the name is in the inclusion list and is * not disabled, the forest owns this name */ foreach e in f.Entries if (e.ForestTrustType = ForestTrustTopLevelName and LSA_FTRECORD_DISABLED_REASONS not in e.Flags and (e.TopLevelName = name or IsSubdomainOf(name, e.TopLevelName))) then return true endif endfor return false procedure IsDomainDnsNameInTrustedForest(name: unicodestring, var referredDomain: unicodestring) : boolean tdos: set of DSName f: X_FOREST_TRUST_INFORMATION /* Get all the objects that represent trusted domains */ tdos := select all o in Children ForestRootDomainNC() where trustedDomain in o!objectClass and o!trustAttributes & 0x00000008 ≠ 0 and o!msDS-TrustForestTrustInfo ≠ null foreach o in tdos if not UnmarshalForestTrustInfo(o!msDS-TrustForestTrustInfo, f) then return false else foreach e in f.Entries if (e.ForestTrustType = ForestTrustDomainInfo and e.DomainInfo.DnsName = name and LSA_SID_DISABLED_ADMIN not in e.Flags and LSA_SID_DISABLED_CONFLICT not in e.Flags and ForestTrustOwnsName(f, e.DomainInfo.DnsName) then referredDomain := o!trustPartner return true endif endfor endif endfor return false procedure IsDomainNetbiosNameInTrustedForest (name: unicodestring, var referredDomain: unicodestring): boolean tdos: set of DSName f: X_FOREST_TRUST_INFORMATION /* Get all the objects that represent trusted domains */ tdos := select all o in Children ForestRootDomainNC() where trustedDomain in o!objectClass and o!trustAttributes & 0x00000008 ≠ 0 and o!msDS-TrustForestTrustInfo ≠ null foreach o in tdos if not UnmarshalForestTrustInfo(o!msDS-TrustForestTrustInfo, f) then return false else foreach e in f.Entries if (e.ForestTrustType = ForestTrustDomainInfo and e.DomainInfo.NetbiosName = name and NETBIOS_DISABLED_MASK not in e.Flags and ForestTrustOwnsName(f, e.DomainInfo.DnsName) then referredDomain := o!trustPartner return true endif endfor endif endfor return false
The IsUPNInTrustedForest procedure uses the following helper procedure to determine if a UPN is in a trusted forest.
-
procedure IsNamespaceInTrustedDomain (name: unicodestring, var trustedForestName: unicodestring): boolean tdos: set of DSName f: X_FOREST_TRUST_INFORMATION b: boolean dnsParent: unicodestring parents: set of unicodestring /* if name is A.B.C, parents has the values A.B.C, B.C, and C */ parents := DNS parents of name foreach dnsParent in parents /* Get all the objects that represent trusted domains */ tdos := select all o in Children ForestRootDomainNC() where trustedDomain in o!objectClass and o!trustAttributes & 0x00000008 ≠ 0 and o!msDS-TrustForestTrustInfo ≠ null foreach o in tdos if not UnmarshalForestTrustInfo(o!msDS-TrustForestTrustInfo, f) then return false else foreach e in f.Entries if (e.ForestTrustType = ForestTrustTopLevelName and e.TopLevelName = dnsParent and LSA_FTRECORD_DISABLED_REASONS not in e.Flags) then b := true foreach g in f.Entries if (g.ForestTrustType = ForestTrustTopLevelNameEx and LSA_FTRECORD_DISABLED_REASONS not in g.Flags and (g.TopLevelName = dnsParent or IsSubdomainOf(dnsParent, g.TopLevelName))) then b := false break endif endfor if (b) then trustedForestName := o!trustPartner return true endif endif endfor endif endfor endfor return false