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