s4/dsdb/common: clang: Fix access results in null pointer deref.
[amitay/samba.git] / source4 / dsdb / common / util_trusts.c
index db087391026b4886527dd241bdb2218b2e1a41cf..b4571dca95700f04d5b46cc2e6006a2e6f1ddf58 100644 (file)
 #include "dsdb/common/util.h"
 #include "libds/common/flag_mapping.h"
 #include "../lib/util/dlinklist.h"
-#include "../lib/crypto/crypto.h"
+#include "lib/crypto/md4.h"
+#include "libcli/ldap/ldap_ndr.h"
+
+NTSTATUS dsdb_trust_forest_info_from_lsa(TALLOC_CTX *mem_ctx,
+                               const struct lsa_ForestTrustInformation *lfti,
+                               struct ForestTrustInfo **_fti)
+{
+       struct ForestTrustInfo *fti;
+       uint32_t i;
+
+       *_fti = NULL;
+
+       fti = talloc_zero(mem_ctx, struct ForestTrustInfo);
+       if (fti == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       fti->version = 1;
+       fti->count = lfti->count;
+       fti->records = talloc_zero_array(mem_ctx,
+                                        struct ForestTrustInfoRecordArmor,
+                                        fti->count);
+       if (fti->records == NULL) {
+               TALLOC_FREE(fti);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i = 0; i < fti->count; i++) {
+               const struct lsa_ForestTrustRecord *lftr = lfti->entries[i];
+               struct ForestTrustInfoRecord *ftr = &fti->records[i].record;
+               struct ForestTrustString *str = NULL;
+               const struct lsa_StringLarge *lstr = NULL;
+               const struct lsa_ForestTrustDomainInfo *linfo = NULL;
+               struct ForestTrustDataDomainInfo *info = NULL;
+
+               if (lftr == NULL) {
+                       TALLOC_FREE(fti);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               ftr->flags = lftr->flags;
+               ftr->timestamp = lftr->time;
+               ftr->type = (enum ForestTrustInfoRecordType)lftr->type;
+
+               switch (lftr->type) {
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+                       lstr = &lftr->forest_trust_data.top_level_name;
+                       str = &ftr->data.name;
+
+                       str->string = talloc_strdup(mem_ctx, lstr->string);
+                       if (str->string == NULL) {
+                               TALLOC_FREE(fti);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       break;
+
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+                       lstr = &lftr->forest_trust_data.top_level_name_ex;
+                       str = &ftr->data.name;
+
+                       str->string = talloc_strdup(mem_ctx, lstr->string);
+                       if (str->string == NULL) {
+                               TALLOC_FREE(fti);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       break;
+
+               case LSA_FOREST_TRUST_DOMAIN_INFO:
+                       linfo = &lftr->forest_trust_data.domain_info;
+                       info = &ftr->data.info;
+
+                       if (linfo->domain_sid == NULL) {
+                               TALLOC_FREE(fti);
+                               return NT_STATUS_INVALID_PARAMETER;
+                       }
+                       info->sid = *linfo->domain_sid;
+
+                       lstr = &linfo->dns_domain_name;
+                       str = &info->dns_name;
+                       str->string = talloc_strdup(mem_ctx, lstr->string);
+                       if (str->string == NULL) {
+                               TALLOC_FREE(fti);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       lstr = &linfo->netbios_domain_name;
+                       str = &info->netbios_name;
+                       str->string = talloc_strdup(mem_ctx, lstr->string);
+                       if (str->string == NULL) {
+                               TALLOC_FREE(fti);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       break;
+
+               default:
+                       return NT_STATUS_NOT_SUPPORTED;
+               }
+       }
+
+       *_fti = fti;
+       return NT_STATUS_OK;
+}
 
 static NTSTATUS dsdb_trust_forest_record_to_lsa(TALLOC_CTX *mem_ctx,
                                         const struct ForestTrustInfoRecord *ftr,
@@ -53,7 +157,7 @@ static NTSTATUS dsdb_trust_forest_record_to_lsa(TALLOC_CTX *mem_ctx,
 
        lftr->flags = ftr->flags;
        lftr->time = ftr->timestamp;
-       lftr->type = ftr->type;
+       lftr->type = (enum lsa_ForestTrustRecordType)ftr->type;
 
        switch (lftr->type) {
        case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
@@ -130,7 +234,7 @@ NTSTATUS dsdb_trust_forest_info_to_lsa(TALLOC_CTX *mem_ctx,
        }
 
        lfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
-       if (fti == NULL) {
+       if (lfti == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -278,7 +382,9 @@ static NTSTATUS dsdb_trust_parse_crossref_info(TALLOC_CTX *mem_ctx,
        const char *dns = NULL;
        const char *netbios = NULL;
        struct ldb_dn *nc_dn = NULL;
-       struct dom_sid sid = {};
+       struct dom_sid sid = {
+               .num_auths = 0,
+       };
        NTSTATUS status;
 
        *_tdo = NULL;
@@ -378,12 +484,6 @@ static NTSTATUS dsdb_trust_crossref_tdo_info(TALLOC_CTX *mem_ctx,
                *_trust_parent_tdo = NULL;
        }
 
-       domain_dn = ldb_get_default_basedn(sam_ctx);
-       if (domain_dn == NULL) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_INTERNAL_ERROR;
-       }
-
        partitions_dn = samdb_partitions_dn(sam_ctx, frame);
        if (partitions_dn == NULL) {
                TALLOC_FREE(frame);
@@ -523,11 +623,11 @@ static int dns_cmp(const char *s1, const char *s2)
        size_t l1 = 0;
        const char *p1 = NULL;
        size_t num_comp1 = 0;
-       uint16_t comp1[UINT8_MAX] = {};
+       uint16_t comp1[UINT8_MAX] = {0};
        size_t l2 = 0;
        const char *p2 = NULL;
        size_t num_comp2 = 0;
-       uint16_t comp2[UINT8_MAX] = {};
+       uint16_t comp2[UINT8_MAX] = {0};
        size_t i;
 
        if (s1 != NULL) {
@@ -554,7 +654,7 @@ static int dns_cmp(const char *s1, const char *s2)
                if (i == 0) {
                        p1 = s1;
 
-                       if (l1 == 0 && l1 >= UINT16_MAX) {
+                       if (l1 == 0 || l1 >= UINT16_MAX) {
                                /* just use one single component on overflow */
                                break;
                        }
@@ -584,7 +684,7 @@ static int dns_cmp(const char *s1, const char *s2)
                if (i == 0) {
                        p2 = s2;
 
-                       if (l2 == 0 && l2 >= UINT16_MAX) {
+                       if (l2 == 0 || l2 >= UINT16_MAX) {
                                /* just use one single component on overflow */
                                break;
                        }
@@ -730,6 +830,42 @@ static bool dsdb_trust_find_tln_ex_match(const struct lsa_ForestTrustInformation
        return false;
 }
 
+NTSTATUS dsdb_trust_local_tdo_info(TALLOC_CTX *mem_ctx,
+                                  struct ldb_context *sam_ctx,
+                                  struct lsa_TrustDomainInfoInfoEx **_tdo)
+{
+       struct ldb_dn *domain_dn = NULL;
+
+       domain_dn = ldb_get_default_basedn(sam_ctx);
+       if (domain_dn == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return dsdb_trust_crossref_tdo_info(mem_ctx, sam_ctx,
+                                           domain_dn, NULL,
+                                           _tdo, NULL, NULL);
+}
+
+NTSTATUS dsdb_trust_xref_tdo_info(TALLOC_CTX *mem_ctx,
+                                 struct ldb_context *sam_ctx,
+                                 struct lsa_TrustDomainInfoInfoEx **_tdo)
+{
+       /*
+        * The extra filter makes sure we only find the forest root domain
+        */
+       const char *extra_filter = "(!(|(rootTrust=*)(trustParent=*)))";
+       struct ldb_dn *domain_dn = NULL;
+
+       domain_dn = ldb_get_default_basedn(sam_ctx);
+       if (domain_dn == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return dsdb_trust_crossref_tdo_info(mem_ctx, sam_ctx,
+                                           domain_dn, extra_filter,
+                                           _tdo, NULL, NULL);
+}
+
 static int dsdb_trust_xref_sort_msgs(struct ldb_message **_m1,
                                     struct ldb_message **_m2)
 {
@@ -877,8 +1013,12 @@ NTSTATUS dsdb_trust_xref_forest_info(TALLOC_CTX *mem_ctx,
                const char *dns = NULL;
                const char *netbios = NULL;
                struct ldb_dn *nc_dn = NULL;
-               struct dom_sid sid = {};
-               struct lsa_ForestTrustRecord e = {};
+               struct dom_sid sid = {
+                       .num_auths = 0,
+               };
+               struct lsa_ForestTrustRecord e = {
+                       .flags = 0,
+               };
                struct lsa_ForestTrustDomainInfo *d = NULL;
                struct lsa_StringLarge *t = NULL;
                bool match = false;
@@ -952,7 +1092,9 @@ NTSTATUS dsdb_trust_xref_forest_info(TALLOC_CTX *mem_ctx,
        for (i=0; (tln_el != NULL) && i < tln_el->num_values; i++) {
                const struct ldb_val *v = &tln_el->values[i];
                const char *dns = (const char *)v->data;
-               struct lsa_ForestTrustRecord e = {};
+               struct lsa_ForestTrustRecord e = {
+                       .flags = 0,
+               };
                struct lsa_StringLarge *t = NULL;
                bool match = false;
                NTSTATUS status;
@@ -1121,162 +1263,1524 @@ NTSTATUS dsdb_trust_parse_forest_info(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
-NTSTATUS dsdb_trust_search_tdo(struct ldb_context *sam_ctx,
-                              const char *netbios, const char *dns,
-                              const char * const *attrs,
-                              TALLOC_CTX *mem_ctx,
-                              struct ldb_message **msg)
+NTSTATUS dsdb_trust_normalize_forest_info_step1(TALLOC_CTX *mem_ctx,
+                               const struct lsa_ForestTrustInformation *gfti,
+                               struct lsa_ForestTrustInformation **_nfti)
 {
        TALLOC_CTX *frame = talloc_stackframe();
-       int ret;
-       struct ldb_dn *system_dn = NULL;
-       char *netbios_encoded = NULL;
-       char *dns_encoded = NULL;
-       char *filter = NULL;
-
-       *msg = NULL;
+       struct lsa_ForestTrustInformation *nfti;
+       uint32_t n;
 
-       if (netbios == NULL && dns == NULL) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_INVALID_PARAMETER_MIX;
-       }
+       *_nfti = NULL;
 
-       system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
-       if (system_dn == NULL) {
+       nfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
+       if (nfti == NULL) {
                TALLOC_FREE(frame);
                return NT_STATUS_NO_MEMORY;
        }
+       talloc_steal(frame, nfti);
 
-       if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
+       /*
+        * First we copy every record and remove possible trailing dots
+        * from dns names.
+        *
+        * We also NULL out dublicates. The first one wins and
+        * we keep 'count' as is. This is required in order to
+        * provide the correct index for collision records.
+        */
+       for (n = 0; n < gfti->count; n++) {
+               const struct lsa_ForestTrustRecord *gftr = gfti->entries[n];
+               struct lsa_ForestTrustRecord *nftr = NULL;
+               struct lsa_ForestTrustDomainInfo *ninfo = NULL;
+               struct lsa_StringLarge *ntln = NULL;
+               struct lsa_StringLarge *nnb = NULL;
+               struct dom_sid *nsid = NULL;
+               NTSTATUS status;
+               size_t len = 0;
+               char *p = NULL;
+               uint32_t c;
 
-       if (netbios != NULL) {
-               netbios_encoded = ldb_binary_encode_string(frame, netbios);
-               if (netbios_encoded == NULL) {
+               if (gftr == NULL) {
                        TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
+                       return NT_STATUS_INVALID_PARAMETER;
                }
-       }
 
-       if (dns != NULL) {
-               dns_encoded = ldb_binary_encode_string(frame, dns);
-               if (dns_encoded == NULL) {
+               status = dsdb_trust_forest_info_add_record(nfti, gftr);
+               if (!NT_STATUS_IS_OK(status)) {
                        TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
+                       return status;
                }
-       }
 
-       if (netbios != NULL && dns != NULL) {
-               filter = talloc_asprintf(frame,
-                               "(&(objectClass=trustedDomain)"
-                                 "(|(trustPartner=%s)(flatName=%s))"
-                               ")",
-                               dns_encoded, netbios_encoded);
-               if (filter == NULL) {
+               nftr = nfti->entries[n];
+
+               switch (nftr->type) {
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+                       ntln = &nftr->forest_trust_data.top_level_name;
+                       break;
+
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+                       ntln = &nftr->forest_trust_data.top_level_name_ex;
+                       break;
+
+               case LSA_FOREST_TRUST_DOMAIN_INFO:
+                       ninfo = &nftr->forest_trust_data.domain_info;
+                       ntln = &ninfo->dns_domain_name;
+                       nnb = &ninfo->netbios_domain_name;
+                       nsid = ninfo->domain_sid;
+                       break;
+
+               default:
                        TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
+                       return NT_STATUS_INVALID_PARAMETER;
                }
-       } else if (netbios != NULL) {
-               filter = talloc_asprintf(frame,
-                               "(&(objectClass=trustedDomain)(flatName=%s))",
-                               netbios_encoded);
-               if (filter == NULL) {
+
+               /*
+                * We remove one trailing '.' before checking
+                * for invalid dots.
+                *
+                * domain.com.  becomes domain.com
+                * domain.com.. becomes domain.com.
+                *
+                * Then the following is invalid:
+                *
+                * domain..com
+                * .domain.com
+                * domain.com.
+                */
+               len = strlen(ntln->string);
+               if (len > 1 && ntln->string[len - 1] == '.') {
+                       const char *cp = &ntln->string[len - 1];
+                       p = discard_const_p(char, cp);
+                       *p= '\0';
+               }
+               if (ntln->string[0] == '.') {
                        TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
+                       return NT_STATUS_INVALID_PARAMETER;
                }
-       } else if (dns != NULL) {
-               filter = talloc_asprintf(frame,
-                               "(&(objectClass=trustedDomain)(trustPartner=%s))",
-                               dns_encoded);
-               if (filter == NULL) {
+               p = strstr_m(ntln->string, "..");
+               if (p != NULL) {
                        TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
+                       return NT_STATUS_INVALID_PARAMETER;
                }
-       }
 
-       ret = dsdb_search_one(sam_ctx, mem_ctx, msg,
-                             system_dn,
-                             LDB_SCOPE_ONELEVEL, attrs,
-                             DSDB_SEARCH_NO_GLOBAL_CATALOG,
-                             "%s", filter);
-       if (ret != LDB_SUCCESS) {
-               NTSTATUS status = dsdb_ldb_err_to_ntstatus(ret);
-               DEBUG(3, ("Failed to search for %s: %s - %s\n",
-                         filter, nt_errstr(status), ldb_errstring(sam_ctx)));
-               TALLOC_FREE(frame);
-               return status;
-       }
+               for (c = 0; c < n; c++) {
+                       const struct lsa_ForestTrustRecord *cftr = nfti->entries[c];
+                       const struct lsa_ForestTrustDomainInfo *cinfo = NULL;
+                       const struct lsa_StringLarge *ctln = NULL;
+                       const struct lsa_StringLarge *cnb = NULL;
+                       const struct dom_sid *csid = NULL;
+                       int cmp;
 
-       TALLOC_FREE(frame);
-       return NT_STATUS_OK;
-}
+                       if (cftr == NULL) {
+                               continue;
+                       }
 
-NTSTATUS dsdb_trust_search_tdo_by_type(struct ldb_context *sam_ctx,
-                                      enum netr_SchannelType type,
-                                      const char *name,
-                                      const char * const *attrs,
-                                      TALLOC_CTX *mem_ctx,
-                                      struct ldb_message **msg)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       NTSTATUS status;
-       size_t len;
-       char trailer = '$';
-       bool require_trailer = true;
-       char *encoded_name = NULL;
-       const char *netbios = NULL;
-       const char *dns = NULL;
+                       if (cftr->type != nftr->type) {
+                               continue;
+                       }
 
-       if (type != SEC_CHAN_DOMAIN && type != SEC_CHAN_DNS_DOMAIN) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_INVALID_PARAMETER;
-       }
+                       switch (cftr->type) {
+                       case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+                               ctln = &cftr->forest_trust_data.top_level_name;
+                               break;
 
-       if (type == SEC_CHAN_DNS_DOMAIN) {
-               trailer = '.';
-               require_trailer = false;
-       }
+                       case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+                               ctln = &cftr->forest_trust_data.top_level_name_ex;
+                               break;
 
-       encoded_name = ldb_binary_encode_string(frame, name);
-       if (encoded_name == NULL) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
+                       case LSA_FOREST_TRUST_DOMAIN_INFO:
+                               cinfo = &cftr->forest_trust_data.domain_info;
+                               ctln = &cinfo->dns_domain_name;
+                               cnb = &cinfo->netbios_domain_name;
+                               csid = cinfo->domain_sid;
+                               break;
 
-       len = strlen(encoded_name);
-       if (len < 2) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-       }
+                       default:
+                               TALLOC_FREE(frame);
+                               return NT_STATUS_INVALID_PARAMETER;
+                       }
 
-       if (require_trailer && encoded_name[len - 1] != trailer) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-       }
-       encoded_name[len - 1] = '\0';
+                       cmp = dns_cmp(ntln->string, ctln->string);
+                       if (cmp == DNS_CMP_MATCH) {
+                               nftr = NULL;
+                               TALLOC_FREE(nfti->entries[n]);
+                               break;
+                       }
 
-       if (type == SEC_CHAN_DNS_DOMAIN) {
-               dns = encoded_name;
-       } else {
-               netbios = encoded_name;
-       }
+                       if (cinfo == NULL) {
+                               continue;
+                       }
 
-       status = dsdb_trust_search_tdo(sam_ctx, netbios, dns,
-                                      attrs, mem_ctx, msg);
-       if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(frame);
-               return status;
+                       cmp = strcasecmp_m(nnb->string, cnb->string);
+                       if (cmp == 0) {
+                               nftr = NULL;
+                               TALLOC_FREE(nfti->entries[n]);
+                               break;
+                       }
+
+                       cmp = dom_sid_compare(nsid, csid);
+                       if (cmp == 0) {
+                               nftr = NULL;
+                               TALLOC_FREE(nfti->entries[n]);
+                               break;
+                       }
+               }
        }
 
-       TALLOC_FREE(frame);
-       return NT_STATUS_OK;
-}
+       /*
+        * Now we check that only true top level names are provided
+        */
+       for (n = 0; n < nfti->count; n++) {
+               const struct lsa_ForestTrustRecord *nftr = nfti->entries[n];
+               const struct lsa_StringLarge *ntln = NULL;
+               uint32_t c;
 
-NTSTATUS dsdb_trust_search_tdos(struct ldb_context *sam_ctx,
-                               const char *exclude,
-                               const char * const *attrs,
+               if (nftr == NULL) {
+                       continue;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                       continue;
+               }
+
+               ntln = &nftr->forest_trust_data.top_level_name;
+
+               for (c = 0; c < nfti->count; c++) {
+                       const struct lsa_ForestTrustRecord *cftr = nfti->entries[c];
+                       const struct lsa_StringLarge *ctln = NULL;
+                       int cmp;
+
+                       if (cftr == NULL) {
+                               continue;
+                       }
+
+                       if (cftr == nftr) {
+                               continue;
+                       }
+
+                       if (cftr->type != nftr->type) {
+                               continue;
+                       }
+
+                       ctln = &cftr->forest_trust_data.top_level_name;
+
+                       cmp = dns_cmp(ntln->string, ctln->string);
+                       if (DNS_CMP_IS_NO_MATCH(cmp)) {
+                               continue;
+                       }
+
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+       }
+
+       /*
+        * Now we check that only true sub level excludes are provided
+        */
+       for (n = 0; n < nfti->count; n++) {
+               const struct lsa_ForestTrustRecord *nftr = nfti->entries[n];
+               const struct lsa_StringLarge *ntln = NULL;
+               uint32_t c;
+               bool found_tln = false;
+
+               if (nftr == NULL) {
+                       continue;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX) {
+                       continue;
+               }
+
+               ntln = &nftr->forest_trust_data.top_level_name;
+
+               for (c = 0; c < nfti->count; c++) {
+                       const struct lsa_ForestTrustRecord *cftr = nfti->entries[c];
+                       const struct lsa_StringLarge *ctln = NULL;
+                       int cmp;
+
+                       if (cftr == NULL) {
+                               continue;
+                       }
+
+                       if (cftr == nftr) {
+                               continue;
+                       }
+
+                       if (cftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                               continue;
+                       }
+
+                       ctln = &cftr->forest_trust_data.top_level_name;
+
+                       cmp = dns_cmp(ntln->string, ctln->string);
+                       if (cmp == DNS_CMP_FIRST_IS_CHILD) {
+                               found_tln = true;
+                               break;
+                       }
+               }
+
+               if (found_tln) {
+                       continue;
+               }
+
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /*
+        * Now we check that there's a top level name for each domain
+        */
+       for (n = 0; n < nfti->count; n++) {
+               const struct lsa_ForestTrustRecord *nftr = nfti->entries[n];
+               const struct lsa_ForestTrustDomainInfo *ninfo = NULL;
+               const struct lsa_StringLarge *ntln = NULL;
+               uint32_t c;
+               bool found_tln = false;
+
+               if (nftr == NULL) {
+                       continue;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                       continue;
+               }
+
+               ninfo = &nftr->forest_trust_data.domain_info;
+               ntln = &ninfo->dns_domain_name;
+
+               for (c = 0; c < nfti->count; c++) {
+                       const struct lsa_ForestTrustRecord *cftr = nfti->entries[c];
+                       const struct lsa_StringLarge *ctln = NULL;
+                       int cmp;
+
+                       if (cftr == NULL) {
+                               continue;
+                       }
+
+                       if (cftr == nftr) {
+                               continue;
+                       }
+
+                       if (cftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                               continue;
+                       }
+
+                       ctln = &cftr->forest_trust_data.top_level_name;
+
+                       cmp = dns_cmp(ntln->string, ctln->string);
+                       if (cmp == DNS_CMP_MATCH) {
+                               found_tln = true;
+                               break;
+                       }
+                       if (cmp == DNS_CMP_FIRST_IS_CHILD) {
+                               found_tln = true;
+                               break;
+                       }
+               }
+
+               if (found_tln) {
+                       continue;
+               }
+
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       *_nfti = talloc_move(mem_ctx, &nfti);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_normalize_forest_info_step2(TALLOC_CTX *mem_ctx,
+                               const struct lsa_ForestTrustInformation *gfti,
+                               struct lsa_ForestTrustInformation **_nfti)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct timeval tv = timeval_current();
+       NTTIME now = timeval_to_nttime(&tv);
+       struct lsa_ForestTrustInformation *nfti;
+       uint32_t g;
+
+       *_nfti = NULL;
+
+       nfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
+       if (nfti == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_steal(frame, nfti);
+
+       /*
+        * Now we add TOP_LEVEL_NAME[_EX] in reverse order
+        * followed by LSA_FOREST_TRUST_DOMAIN_INFO in reverse order.
+        *
+        * This also removes the possible NULL entries generated in step1.
+        */
+
+       for (g = 0; g < gfti->count; g++) {
+               const struct lsa_ForestTrustRecord *gftr = gfti->entries[gfti->count - (g+1)];
+               struct lsa_ForestTrustRecord tftr;
+               bool skip = false;
+               NTSTATUS status;
+
+               if (gftr == NULL) {
+                       continue;
+               }
+
+               switch (gftr->type) {
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+                       break;
+
+               case LSA_FOREST_TRUST_DOMAIN_INFO:
+                       skip = true;
+                       break;
+
+               default:
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (skip) {
+                       continue;
+               }
+
+               /* make a copy in order to update the time. */
+               tftr = *gftr;
+               if (tftr.time == 0) {
+                       tftr.time = now;
+               }
+
+               status = dsdb_trust_forest_info_add_record(nfti, &tftr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       for (g = 0; g < gfti->count; g++) {
+               const struct lsa_ForestTrustRecord *gftr = gfti->entries[gfti->count - (g+1)];
+               struct lsa_ForestTrustRecord tftr;
+               bool skip = false;
+               NTSTATUS status;
+
+               if (gftr == NULL) {
+                       continue;
+               }
+
+               switch (gftr->type) {
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+               case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+                       skip = true;
+                       break;
+
+               case LSA_FOREST_TRUST_DOMAIN_INFO:
+                       break;
+
+               default:
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (skip) {
+                       continue;
+               }
+
+               /* make a copy in order to update the time. */
+               tftr = *gftr;
+               if (tftr.time == 0) {
+                       tftr.time = now;
+               }
+
+               status = dsdb_trust_forest_info_add_record(nfti, &tftr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       *_nfti = talloc_move(mem_ctx, &nfti);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS dsdb_trust_add_collision(
+                       struct lsa_ForestTrustCollisionInfo *c_info,
+                       enum lsa_ForestTrustCollisionRecordType type,
+                       uint32_t idx, uint32_t flags,
+                       const char *tdo_name)
+{
+       struct lsa_ForestTrustCollisionRecord **es;
+       uint32_t i = c_info->count;
+
+       es = talloc_realloc(c_info, c_info->entries,
+                           struct lsa_ForestTrustCollisionRecord *, i + 1);
+       if (es == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       c_info->entries = es;
+       c_info->count = i + 1;
+
+       es[i] = talloc_zero(es, struct lsa_ForestTrustCollisionRecord);
+       if (es[i] == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       es[i]->index = idx;
+       es[i]->type = type;
+       es[i]->flags = flags;
+       es[i]->name.string = talloc_strdup(es[i], tdo_name);
+       if (es[i]->name.string == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_verify_forest_info(const struct lsa_TrustDomainInfoInfoEx *ref_tdo,
+                               const struct lsa_ForestTrustInformation *ref_fti,
+                               enum lsa_ForestTrustCollisionRecordType collision_type,
+                               struct lsa_ForestTrustCollisionInfo *c_info,
+                               struct lsa_ForestTrustInformation *new_fti)
+{
+       uint32_t n;
+
+       for (n = 0; n < new_fti->count; n++) {
+               struct lsa_ForestTrustRecord *nftr = new_fti->entries[n];
+               struct lsa_StringLarge *ntln = NULL;
+               bool ntln_excluded = false;
+               uint32_t flags = 0;
+               uint32_t r;
+               NTSTATUS status;
+
+               if (nftr == NULL) {
+                       continue;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                       continue;
+               }
+
+               ntln = &nftr->forest_trust_data.top_level_name;
+               if (ntln->string == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               ntln_excluded = dsdb_trust_find_tln_ex_match(ref_fti,
+                                                            ntln->string);
+
+               /* check if this is already taken and not excluded */
+               for (r = 0; r < ref_fti->count; r++) {
+                       const struct lsa_ForestTrustRecord *rftr =
+                               ref_fti->entries[r];
+                       const struct lsa_StringLarge *rtln = NULL;
+                       int cmp;
+
+                       if (rftr == NULL) {
+                               continue;
+                       }
+
+                       if (rftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                               continue;
+                       }
+
+                       rtln = &rftr->forest_trust_data.top_level_name;
+                       if (rtln->string == NULL) {
+                               continue;
+                       }
+
+                       cmp = dns_cmp(ntln->string, rtln->string);
+                       if (DNS_CMP_IS_NO_MATCH(cmp)) {
+                               continue;
+                       }
+                       if (cmp == DNS_CMP_MATCH) {
+                               /* We need to normalize the string */
+                               ntln->string = talloc_strdup(nftr,
+                                                            rtln->string);
+                               if (ntln->string == NULL) {
+                                       return NT_STATUS_NO_MEMORY;
+                               }
+                       }
+
+                       if (ntln_excluded) {
+                               continue;
+                       }
+
+                       if (rftr->flags & LSA_TLN_DISABLED_MASK) {
+                               continue;
+                       }
+
+                       if (nftr->flags & LSA_TLN_DISABLED_MASK) {
+                               continue;
+                       }
+
+                       if (cmp == DNS_CMP_SECOND_IS_CHILD) {
+                               bool m;
+
+                               /*
+                                * If the conflicting tln is a child, check if
+                                * we have an exclusion record for it.
+                                */
+                               m = dsdb_trust_find_tln_ex_match(new_fti,
+                                                                rtln->string);
+                               if (m) {
+                                       continue;
+                               }
+                       }
+
+                       flags |= LSA_TLN_DISABLED_CONFLICT;
+               }
+
+               if (flags == 0) {
+                       continue;
+               }
+
+               nftr->flags |= flags;
+
+               status = dsdb_trust_add_collision(c_info,
+                                                 collision_type,
+                                                 n, nftr->flags,
+                                                 ref_tdo->domain_name.string);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
+       for (n = 0; n < new_fti->count; n++) {
+               struct lsa_ForestTrustRecord *nftr = new_fti->entries[n];
+               struct lsa_ForestTrustDomainInfo *ninfo = NULL;
+               struct lsa_StringLarge *ntln = NULL;
+               struct lsa_StringLarge *nnb = NULL;
+               struct dom_sid *nsid = NULL;
+               bool ntln_found = false;
+               uint32_t flags = 0;
+               uint32_t r;
+               NTSTATUS status;
+
+               if (nftr == NULL) {
+                       continue;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                       continue;
+               }
+
+               ninfo = &nftr->forest_trust_data.domain_info;
+               ntln = &ninfo->dns_domain_name;
+               if (ntln->string == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               nnb = &ninfo->netbios_domain_name;
+               if (nnb->string == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               nsid = ninfo->domain_sid;
+               if (nsid == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               ntln_found = dsdb_trust_find_tln_match(ref_fti, ntln->string);
+
+               /* check if this is already taken and not excluded */
+               for (r = 0; r < ref_fti->count; r++) {
+                       const struct lsa_ForestTrustRecord *rftr =
+                               ref_fti->entries[r];
+                       const struct lsa_ForestTrustDomainInfo *rinfo = NULL;
+                       const struct lsa_StringLarge *rtln = NULL;
+                       const struct lsa_StringLarge *rnb = NULL;
+                       const struct dom_sid *rsid = NULL;
+                       bool nb_possible = true;
+                       bool sid_possible = true;
+                       int cmp;
+
+                       if (rftr == NULL) {
+                               continue;
+                       }
+
+                       if (!ntln_found) {
+                               /*
+                                * If the dns name doesn't match any existing
+                                * tln any conflict is ignored, but name
+                                * normalization still happens.
+                                *
+                                * I guess that's a bug in Windows
+                                * (tested with Windows 2012r2).
+                                */
+                               nb_possible = false;
+                               sid_possible = false;
+                       }
+
+                       if (nftr->flags & LSA_SID_DISABLED_MASK) {
+                               sid_possible = false;
+                       }
+
+                       if (nftr->flags & LSA_NB_DISABLED_MASK) {
+                               nb_possible = false;
+                       }
+
+                       switch (rftr->type) {
+                       case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+                               rtln = &rftr->forest_trust_data.top_level_name;
+                               nb_possible = false;
+                               sid_possible = false;
+                               break;
+
+                       case LSA_FOREST_TRUST_DOMAIN_INFO:
+                               rinfo = &rftr->forest_trust_data.domain_info;
+                               rtln = &rinfo->dns_domain_name;
+                               rnb = &rinfo->netbios_domain_name;
+                               rsid = rinfo->domain_sid;
+
+                               if (rftr->flags & LSA_SID_DISABLED_MASK) {
+                                       sid_possible = false;
+                               }
+
+                               if (rftr->flags & LSA_NB_DISABLED_MASK) {
+                                       nb_possible = false;
+                               }
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       if (rtln == NULL) {
+                               continue;
+                       }
+
+                       if (rtln->string == NULL) {
+                               continue;
+                       }
+
+                       cmp = dns_cmp(ntln->string, rtln->string);
+                       if (DNS_CMP_IS_NO_MATCH(cmp)) {
+                               nb_possible = false;
+                               sid_possible = false;
+                       }
+                       if (cmp == DNS_CMP_MATCH) {
+                               /* We need to normalize the string */
+                               ntln->string = talloc_strdup(nftr,
+                                                            rtln->string);
+                               if (ntln->string == NULL) {
+                                       return NT_STATUS_NO_MEMORY;
+                               }
+                       }
+
+                       if (rinfo == NULL) {
+                               continue;
+                       }
+
+                       if (rsid != NULL) {
+                               cmp = dom_sid_compare(nsid, rsid);
+                       } else {
+                               cmp = -1;
+                       }
+                       if (cmp == 0) {
+                               if (sid_possible) {
+                                       flags |= LSA_SID_DISABLED_CONFLICT;
+                               }
+                       }
+
+                       if (rnb->string != NULL) {
+                               cmp = strcasecmp_m(nnb->string, rnb->string);
+                       } else {
+                               cmp = -1;
+                       }
+                       if (cmp == 0) {
+                               nnb->string = talloc_strdup(nftr, rnb->string);
+                               if (nnb->string == NULL) {
+                                       return NT_STATUS_NO_MEMORY;
+                               }
+                               if (nb_possible) {
+                                       flags |= LSA_NB_DISABLED_CONFLICT;
+                               }
+                       }
+               }
+
+               if (flags == 0) {
+                       continue;
+               }
+
+               nftr->flags |= flags;
+
+               status = dsdb_trust_add_collision(c_info,
+                                                 collision_type,
+                                                 n, nftr->flags,
+                                                 ref_tdo->domain_name.string);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_merge_forest_info(TALLOC_CTX *mem_ctx,
+                               const struct lsa_TrustDomainInfoInfoEx *tdo,
+                               const struct lsa_ForestTrustInformation *ofti,
+                               const struct lsa_ForestTrustInformation *nfti,
+                               struct lsa_ForestTrustInformation **_mfti)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct lsa_ForestTrustInformation *mfti = NULL;
+       uint32_t ni;
+       uint32_t oi;
+       NTSTATUS status;
+       int cmp;
+
+       *_mfti = NULL;
+       mfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
+       if (mfti == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_steal(frame, mfti);
+
+       /*
+        * First we add all top unique level names.
+        *
+        * The one matching the tdo dns name, will be
+        * added without further checking. All others
+        * may keep the flags and time values.
+        */
+       for (ni = 0; ni < nfti->count; ni++) {
+               const struct lsa_ForestTrustRecord *nftr = nfti->entries[ni];
+               struct lsa_ForestTrustRecord tftr = {
+                       .flags = 0,
+               };
+               const char *ndns = NULL;
+               bool ignore_new = false;
+               bool found_old = false;
+               uint32_t mi;
+
+               if (nftr == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                       continue;
+               }
+
+               ndns = nftr->forest_trust_data.top_level_name.string;
+               if (ndns == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               cmp = dns_cmp(tdo->domain_name.string, ndns);
+               if (cmp == DNS_CMP_MATCH) {
+                       status = dsdb_trust_forest_info_add_record(mfti, nftr);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               TALLOC_FREE(frame);
+                               return status;
+                       }
+               }
+
+               for (mi = 0; mi < mfti->count; mi++) {
+                       const struct lsa_ForestTrustRecord *mftr =
+                               mfti->entries[mi];
+                       const char *mdns = NULL;
+
+                       /*
+                        * we just added this above, so we're sure to have a
+                        * valid LSA_FOREST_TRUST_TOP_LEVEL_NAME record
+                        */
+                       mdns = mftr->forest_trust_data.top_level_name.string;
+
+                       cmp = dns_cmp(mdns, ndns);
+                       switch (cmp) {
+                       case DNS_CMP_MATCH:
+                       case DNS_CMP_SECOND_IS_CHILD:
+                               ignore_new = true;
+                               break;
+                       }
+
+                       if (ignore_new) {
+                               break;
+                       }
+               }
+
+               if (ignore_new) {
+                       continue;
+               }
+
+               /*
+                * make a temporary copy where we can change time and flags
+                */
+               tftr = *nftr;
+
+               for (oi = 0; oi < ofti->count; oi++) {
+                       const struct lsa_ForestTrustRecord *oftr =
+                               ofti->entries[oi];
+                       const char *odns = NULL;
+
+                       if (oftr == NULL) {
+                               /*
+                                * broken record => ignore...
+                                */
+                               continue;
+                       }
+
+                       if (oftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                               continue;
+                       }
+
+                       odns = oftr->forest_trust_data.top_level_name.string;
+                       if (odns == NULL) {
+                               /*
+                                * broken record => ignore...
+                                */
+                               continue;
+                       }
+
+                       cmp = dns_cmp(odns, ndns);
+                       if (cmp != DNS_CMP_MATCH) {
+                               continue;
+                       }
+
+                       found_old = true;
+                       tftr.flags = oftr->flags;
+                       tftr.time = oftr->time;
+               }
+
+               if (!found_old) {
+                       tftr.flags = LSA_TLN_DISABLED_NEW;
+                       tftr.time = 0;
+               }
+
+               status = dsdb_trust_forest_info_add_record(mfti, &tftr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       /*
+        * Now we add all unique (based on their SID) domains
+        * and may keep the flags and time values.
+        */
+       for (ni = 0; ni < nfti->count; ni++) {
+               const struct lsa_ForestTrustRecord *nftr = nfti->entries[ni];
+               struct lsa_ForestTrustRecord tftr = {
+                       .flags = 0,
+               };
+               const struct lsa_ForestTrustDomainInfo *nd = NULL;
+               const char *ndns = NULL;
+               const char *nnbt = NULL;
+               bool ignore_new = false;
+               bool found_old = false;
+               uint32_t mi;
+
+               if (nftr == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (nftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                       continue;
+               }
+
+               nd = &nftr->forest_trust_data.domain_info;
+               if (nd->domain_sid == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               ndns = nd->dns_domain_name.string;
+               if (ndns == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               nnbt = nd->netbios_domain_name.string;
+               if (nnbt == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               for (mi = 0; mi < mfti->count; mi++) {
+                       const struct lsa_ForestTrustRecord *mftr =
+                               mfti->entries[mi];
+                       const struct lsa_ForestTrustDomainInfo *md = NULL;
+
+                       if (mftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                               continue;
+                       }
+
+                       /*
+                        * we just added this above, so we're sure to have a
+                        * valid LSA_FOREST_TRUST_DOMAIN_INFO record
+                        */
+                       md = &mftr->forest_trust_data.domain_info;
+
+                       cmp = dom_sid_compare(nd->domain_sid, md->domain_sid);
+                       if (cmp == 0) {
+                               ignore_new = true;
+                               break;
+                       }
+               }
+
+               if (ignore_new) {
+                       continue;
+               }
+
+               /*
+                * make a temporary copy where we can change time and flags
+                */
+               tftr = *nftr;
+
+               for (oi = 0; oi < ofti->count; oi++) {
+                       const struct lsa_ForestTrustRecord *oftr =
+                               ofti->entries[oi];
+                       const struct lsa_ForestTrustDomainInfo *od = NULL;
+                       const char *onbt = NULL;
+
+                       if (oftr == NULL) {
+                               /*
+                                * broken record => ignore...
+                                */
+                               continue;
+                       }
+
+                       if (oftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                               continue;
+                       }
+
+                       od = &oftr->forest_trust_data.domain_info;
+                       onbt = od->netbios_domain_name.string;
+                       if (onbt == NULL) {
+                               /*
+                                * broken record => ignore...
+                                */
+                               continue;
+                       }
+
+                       cmp = strcasecmp(onbt, nnbt);
+                       if (cmp != 0) {
+                               continue;
+                       }
+
+                       found_old = true;
+                       tftr.flags = oftr->flags;
+                       tftr.time = oftr->time;
+               }
+
+               if (!found_old) {
+                       tftr.flags = 0;
+                       tftr.time = 0;
+               }
+
+               status = dsdb_trust_forest_info_add_record(mfti, &tftr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       /*
+        * We keep old domain records disabled by the admin
+        * if not already in the list.
+        */
+       for (oi = 0; oi < ofti->count; oi++) {
+               const struct lsa_ForestTrustRecord *oftr =
+                       ofti->entries[oi];
+               const struct lsa_ForestTrustDomainInfo *od = NULL;
+               const char *odns = NULL;
+               const char *onbt = NULL;
+               bool ignore_old = true;
+               uint32_t mi;
+
+               if (oftr == NULL) {
+                       /*
+                        * broken record => ignore...
+                        */
+                       continue;
+               }
+
+               if (oftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                       continue;
+               }
+
+               od = &oftr->forest_trust_data.domain_info;
+               odns = od->dns_domain_name.string;
+               if (odns == NULL) {
+                       /*
+                        * broken record => ignore...
+                        */
+                       continue;
+               }
+               onbt = od->netbios_domain_name.string;
+               if (onbt == NULL) {
+                       /*
+                        * broken record => ignore...
+                        */
+                       continue;
+               }
+               if (od->domain_sid == NULL) {
+                       /*
+                        * broken record => ignore...
+                        */
+                       continue;
+               }
+
+               if (oftr->flags & LSA_NB_DISABLED_ADMIN) {
+                       ignore_old = false;
+               } else if (oftr->flags & LSA_SID_DISABLED_ADMIN) {
+                       ignore_old = false;
+               }
+
+               for (mi = 0; mi < mfti->count; mi++) {
+                       const struct lsa_ForestTrustRecord *mftr =
+                               mfti->entries[mi];
+                       const struct lsa_ForestTrustDomainInfo *md = NULL;
+
+                       if (mftr->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                               continue;
+                       }
+
+                       /*
+                        * we just added this above, so we're sure to have a
+                        * valid LSA_FOREST_TRUST_DOMAIN_INFO record
+                        */
+                       md = &mftr->forest_trust_data.domain_info;
+
+                       cmp = dom_sid_compare(od->domain_sid, md->domain_sid);
+                       if (cmp == 0) {
+                               ignore_old = true;
+                               break;
+                       }
+               }
+
+               if (ignore_old) {
+                       continue;
+               }
+
+               status = dsdb_trust_forest_info_add_record(mfti, oftr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       /*
+        * Finally we readd top level exclusions,
+        * if they still match a top level name.
+        */
+       for (oi = 0; oi < ofti->count; oi++) {
+               const struct lsa_ForestTrustRecord *oftr =
+                       ofti->entries[oi];
+               const char *odns = NULL;
+               bool ignore_old = false;
+               uint32_t mi;
+
+               if (oftr == NULL) {
+                       /*
+                        * broken record => ignore...
+                        */
+                       continue;
+               }
+
+               if (oftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX) {
+                       continue;
+               }
+
+               odns = oftr->forest_trust_data.top_level_name_ex.string;
+               if (odns == NULL) {
+                       /*
+                        * broken record => ignore...
+                        */
+                       continue;
+               }
+
+               for (mi = 0; mi < mfti->count; mi++) {
+                       const struct lsa_ForestTrustRecord *mftr =
+                               mfti->entries[mi];
+                       const char *mdns = NULL;
+
+                       if (mftr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+                               continue;
+                       }
+
+                       /*
+                        * we just added this above, so we're sure to have a
+                        * valid LSA_FOREST_TRUST_TOP_LEVEL_NAME.
+                        */
+                       mdns = mftr->forest_trust_data.top_level_name.string;
+
+                       cmp = dns_cmp(mdns, odns);
+                       switch (cmp) {
+                       case DNS_CMP_MATCH:
+                       case DNS_CMP_SECOND_IS_CHILD:
+                               break;
+                       default:
+                               ignore_old = true;
+                               break;
+                       }
+
+                       if (ignore_old) {
+                               break;
+                       }
+               }
+
+               if (ignore_old) {
+                       continue;
+               }
+
+               status = dsdb_trust_forest_info_add_record(mfti, oftr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       *_mfti = talloc_move(mem_ctx, &mfti);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_search_tdo(struct ldb_context *sam_ctx,
+                              const char *netbios, const char *dns,
+                              const char * const *attrs,
+                              TALLOC_CTX *mem_ctx,
+                              struct ldb_message **msg)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       int ret;
+       struct ldb_dn *system_dn = NULL;
+       char *netbios_encoded = NULL;
+       char *dns_encoded = NULL;
+       char *filter = NULL;
+
+       *msg = NULL;
+
+       if (netbios == NULL && dns == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
+       if (system_dn == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (netbios != NULL) {
+               netbios_encoded = ldb_binary_encode_string(frame, netbios);
+               if (netbios_encoded == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (dns != NULL) {
+               dns_encoded = ldb_binary_encode_string(frame, dns);
+               if (dns_encoded == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (netbios != NULL && dns != NULL) {
+               filter = talloc_asprintf(frame,
+                               "(&(objectClass=trustedDomain)"
+                                 "(|(trustPartner=%s)(flatName=%s))"
+                               ")",
+                               dns_encoded, netbios_encoded);
+               if (filter == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else if (netbios != NULL) {
+               filter = talloc_asprintf(frame,
+                               "(&(objectClass=trustedDomain)(flatName=%s))",
+                               netbios_encoded);
+               if (filter == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else if (dns != NULL) {
+               filter = talloc_asprintf(frame,
+                               "(&(objectClass=trustedDomain)(trustPartner=%s))",
+                               dns_encoded);
+               if (filter == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       ret = dsdb_search_one(sam_ctx, mem_ctx, msg,
+                             system_dn,
+                             LDB_SCOPE_ONELEVEL, attrs,
+                             DSDB_SEARCH_NO_GLOBAL_CATALOG,
+                             "%s", filter);
+       if (ret != LDB_SUCCESS) {
+               NTSTATUS status = dsdb_ldb_err_to_ntstatus(ret);
+               DEBUG(3, ("Failed to search for %s: %s - %s\n",
+                         filter, nt_errstr(status), ldb_errstring(sam_ctx)));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_search_tdo_by_type(struct ldb_context *sam_ctx,
+                                      enum netr_SchannelType type,
+                                      const char *name,
+                                      const char * const *attrs,
+                                      TALLOC_CTX *mem_ctx,
+                                      struct ldb_message **msg)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
+       size_t len;
+       char trailer = '$';
+       bool require_trailer = true;
+       char *encoded_name = NULL;
+       const char *netbios = NULL;
+       const char *dns = NULL;
+
+       if (type != SEC_CHAN_DOMAIN && type != SEC_CHAN_DNS_DOMAIN) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (type == SEC_CHAN_DNS_DOMAIN) {
+               trailer = '.';
+               require_trailer = false;
+       }
+
+       encoded_name = ldb_binary_encode_string(frame, name);
+       if (encoded_name == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       len = strlen(encoded_name);
+       if (len < 2) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       if (require_trailer && encoded_name[len - 1] != trailer) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+       encoded_name[len - 1] = '\0';
+
+       if (type == SEC_CHAN_DNS_DOMAIN) {
+               dns = encoded_name;
+       } else {
+               netbios = encoded_name;
+       }
+
+       status = dsdb_trust_search_tdo(sam_ctx, netbios, dns,
+                                      attrs, mem_ctx, msg);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_search_tdo_by_sid(struct ldb_context *sam_ctx,
+                                     const struct dom_sid *sid,
+                                     const char * const *attrs,
+                                     TALLOC_CTX *mem_ctx,
+                                     struct ldb_message **msg)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       int ret;
+       struct ldb_dn *system_dn = NULL;
+       char *encoded_sid = NULL;
+       char *filter = NULL;
+
+       *msg = NULL;
+
+       if (sid == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       encoded_sid = ldap_encode_ndr_dom_sid(frame, sid);
+       if (encoded_sid == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
+       if (system_dn == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       filter = talloc_asprintf(frame,
+                               "(&"
+                                 "(objectClass=trustedDomain)"
+                                 "(securityIdentifier=%s)"
+                               ")",
+                               encoded_sid);
+       if (filter == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dsdb_search_one(sam_ctx, mem_ctx, msg,
+                             system_dn,
+                             LDB_SCOPE_ONELEVEL, attrs,
+                             DSDB_SEARCH_NO_GLOBAL_CATALOG,
+                             "%s", filter);
+       if (ret != LDB_SUCCESS) {
+               NTSTATUS status = dsdb_ldb_err_to_ntstatus(ret);
+               DEBUG(3, ("Failed to search for %s: %s - %s\n",
+                         filter, nt_errstr(status), ldb_errstring(sam_ctx)));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_get_incoming_passwords(struct ldb_message *msg,
+                                          TALLOC_CTX *mem_ctx,
+                                          struct samr_Password **_current,
+                                          struct samr_Password **_previous)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct samr_Password __current = {
+               .hash = {0},
+       };
+       struct samr_Password __previous = {
+               .hash = {0},
+       };
+       struct samr_Password *current = NULL;
+       struct samr_Password *previous = NULL;
+       const struct ldb_val *blob = NULL;
+       enum ndr_err_code ndr_err;
+       struct trustAuthInOutBlob incoming = {
+               .count = 0,
+       };
+       uint32_t i;
+
+       if (_current != NULL) {
+               *_current = NULL;
+       }
+       if (_previous != NULL) {
+               *_previous = NULL;
+       }
+
+       blob = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
+       if (blob == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_ACCOUNT_DISABLED;
+       }
+
+       /* ldb_val is equivalent to DATA_BLOB */
+       ndr_err = ndr_pull_struct_blob_all(blob, frame, &incoming,
+                               (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       for (i = 0; i < incoming.current.count; i++) {
+               struct AuthenticationInformation *a =
+                       &incoming.current.array[i];
+
+               if (current != NULL) {
+                       break;
+               }
+
+               switch (a->AuthType) {
+               case TRUST_AUTH_TYPE_NONE:
+               case TRUST_AUTH_TYPE_VERSION:
+                       break;
+               case TRUST_AUTH_TYPE_NT4OWF:
+                       current = &a->AuthInfo.nt4owf.password;
+                       break;
+               case TRUST_AUTH_TYPE_CLEAR:
+                       mdfour(__current.hash,
+                              a->AuthInfo.clear.password,
+                              a->AuthInfo.clear.size);
+                       current = &__current;
+                       break;
+               }
+       }
+
+       if (current == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       for (i = 0; i < incoming.previous.count; i++) {
+               struct AuthenticationInformation *a =
+                       &incoming.previous.array[i];
+
+               if (previous != NULL) {
+                       break;
+               }
+
+               switch (a->AuthType) {
+               case TRUST_AUTH_TYPE_NONE:
+               case TRUST_AUTH_TYPE_VERSION:
+                       break;
+               case TRUST_AUTH_TYPE_NT4OWF:
+                       previous = &a->AuthInfo.nt4owf.password;
+                       break;
+               case TRUST_AUTH_TYPE_CLEAR:
+                       mdfour(__previous.hash,
+                              a->AuthInfo.clear.password,
+                              a->AuthInfo.clear.size);
+                       previous = &__previous;
+                       break;
+               }
+       }
+
+       if (previous == NULL) {
+               previous = current;
+       }
+
+       if (_current != NULL) {
+               *_current = talloc(mem_ctx, struct samr_Password);
+               if (*_current == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               **_current = *current;
+       }
+       if (_previous != NULL) {
+               *_previous = talloc(mem_ctx, struct samr_Password);
+               if (*_previous == NULL) {
+                       if (_current != NULL) {
+                               TALLOC_FREE(*_current);
+                       }
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               **_previous = *previous;
+       }
+       ZERO_STRUCTP(current);
+       ZERO_STRUCTP(previous);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_search_tdos(struct ldb_context *sam_ctx,
+                               const char *exclude,
+                               const char * const *attrs,
                                TALLOC_CTX *mem_ctx,
                                struct ldb_result **res)
 {
@@ -1346,6 +2850,9 @@ struct dsdb_trust_routing_domain {
        struct dsdb_trust_routing_domain *prev, *next;
 
        struct lsa_TrustDomainInfoInfoEx *tdo;
+
+       struct lsa_ForestTrustDomainInfo di;
+
        struct lsa_ForestTrustInformation *fti;
 };
 
@@ -1405,6 +2912,18 @@ NTSTATUS dsdb_trust_routing_table_load(struct ldb_context *sam_ctx,
                return status;
        }
 
+       /*
+        * d->tdo should not be NULL of status above is 'NT_STATUS_OK'
+        * check is needed to satisfy clang static checker
+       */
+       if (d->tdo == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       d->di.domain_sid = d->tdo->sid;
+       d->di.netbios_domain_name.string = d->tdo->netbios_name.string;
+       d->di.dns_domain_name.string = d->tdo->domain_name.string;
+
        if (root_trust_tdo != NULL) {
                root_direction_tdo = root_trust_tdo;
        } else if (trust_parent_tdo != NULL) {
@@ -1447,7 +2966,11 @@ NTSTATUS dsdb_trust_routing_table_load(struct ldb_context *sam_ctx,
                        return status;
                }
 
-               DLIST_ADD_END(table->domains, d, NULL);
+               d->di.domain_sid = d->tdo->sid;
+               d->di.netbios_domain_name.string = d->tdo->netbios_name.string;
+               d->di.dns_domain_name.string = d->tdo->domain_name.string;
+
+               DLIST_ADD_END(table->domains, d);
 
                if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
                        struct ForestTrustInfo *fti = NULL;
@@ -1721,3 +3244,214 @@ const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_routing_by_name(
 
        return NULL;
 }
+
+const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_domain_by_sid(
+               const struct dsdb_trust_routing_table *table,
+               const struct dom_sid *sid,
+               const struct lsa_ForestTrustDomainInfo **pdi)
+{
+       const struct dsdb_trust_routing_domain *d = NULL;
+
+       if (pdi != NULL) {
+               *pdi = NULL;
+       }
+
+       if (sid == NULL) {
+               return NULL;
+       }
+
+       for (d = table->domains; d != NULL; d = d->next) {
+               bool transitive = false;
+               uint32_t i;
+
+               if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+                       transitive = true;
+               }
+
+               if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+                       transitive = true;
+               }
+
+               if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE) {
+                       transitive = false;
+               }
+
+               if (d->tdo->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
+                       transitive = false;
+               }
+
+               if (!transitive || d->fti == NULL) {
+                       bool match = false;
+
+                       match = dom_sid_equal(d->di.domain_sid, sid);
+                       if (match) {
+                               /*
+                                * exact match, it's the domain itself.
+                                */
+                               if (pdi != NULL) {
+                                       *pdi = &d->di;
+                               }
+                               return d->tdo;
+                       }
+                       continue;
+               }
+
+               for (i = 0; i < d->fti->count; i++ ) {
+                       const struct lsa_ForestTrustRecord *f = d->fti->entries[i];
+                       const struct lsa_ForestTrustDomainInfo *di = NULL;
+                       const struct dom_sid *fti_sid = NULL;
+                       bool match = false;
+
+                       if (f == NULL) {
+                               /* broken record */
+                               continue;
+                       }
+
+                       if (f->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                               continue;
+                       }
+
+                       if (f->flags & LSA_SID_DISABLED_MASK) {
+                               /*
+                                * any flag disables the entry.
+                                */
+                               continue;
+                       }
+
+                       di = &f->forest_trust_data.domain_info;
+                       fti_sid = di->domain_sid;
+                       if (fti_sid == NULL) {
+                               /* broken record */
+                               continue;
+                       }
+
+                       match = dom_sid_equal(fti_sid, sid);
+                       if (match) {
+                               /*
+                                * exact match, it's a domain in the forest.
+                                */
+                               if (pdi != NULL) {
+                                       *pdi = di;
+                               }
+                               return d->tdo;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_domain_by_name(
+               const struct dsdb_trust_routing_table *table,
+               const char *name,
+               const struct lsa_ForestTrustDomainInfo **pdi)
+{
+       const struct dsdb_trust_routing_domain *d = NULL;
+
+       if (pdi != NULL) {
+               *pdi = NULL;
+       }
+
+       if (name == NULL) {
+               return NULL;
+       }
+
+       for (d = table->domains; d != NULL; d = d->next) {
+               bool transitive = false;
+               uint32_t i;
+
+               if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+                       transitive = true;
+               }
+
+               if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+                       transitive = true;
+               }
+
+               if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE) {
+                       transitive = false;
+               }
+
+               if (d->tdo->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
+                       transitive = false;
+               }
+
+               if (!transitive || d->fti == NULL) {
+                       bool match = false;
+
+                       match = strequal_m(d->di.netbios_domain_name.string,
+                                          name);
+                       if (match) {
+                               /*
+                                * exact match for netbios name,
+                                * it's the domain itself.
+                                */
+                               if (pdi != NULL) {
+                                       *pdi = &d->di;
+                               }
+                               return d->tdo;
+                       }
+                       match = strequal_m(d->di.dns_domain_name.string,
+                                          name);
+                       if (match) {
+                               /*
+                                * exact match for dns name,
+                                * it's the domain itself.
+                                */
+                               if (pdi != NULL) {
+                                       *pdi = &d->di;
+                               }
+                               return d->tdo;
+                       }
+                       continue;
+               }
+
+               for (i = 0; i < d->fti->count; i++ ) {
+                       const struct lsa_ForestTrustRecord *f = d->fti->entries[i];
+                       const struct lsa_ForestTrustDomainInfo *di = NULL;
+                       bool match = false;
+
+                       if (f == NULL) {
+                               /* broken record */
+                               continue;
+                       }
+
+                       if (f->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+                               continue;
+                       }
+                       di = &f->forest_trust_data.domain_info;
+
+                       if (!(f->flags & LSA_NB_DISABLED_MASK)) {
+                               match = strequal_m(di->netbios_domain_name.string,
+                                                  name);
+                               if (match) {
+                                       /*
+                                        * exact match for netbios name,
+                                        * it's a domain in the forest.
+                                        */
+                                       if (pdi != NULL) {
+                                               *pdi = di;
+                                       }
+                                       return d->tdo;
+                               }
+                       }
+
+                       if (!(f->flags & LSA_TLN_DISABLED_MASK)) {
+                               match = strequal_m(di->dns_domain_name.string,
+                                                  name);
+                               if (match) {
+                                       /*
+                                        * exact match for dns name,
+                                        * it's a domain in the forest.
+                                        */
+                                       if (pdi != NULL) {
+                                               *pdi = di;
+                                       }
+                                       return d->tdo;
+                               }
+                       }
+               }
+       }
+
+       return NULL;
+}