#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,
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:
}
lfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
- if (fti == NULL) {
+ if (lfti == NULL) {
return NT_STATUS_NO_MEMORY;
}
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;
*_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);
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) {
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;
}
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;
}
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)
{
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;
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;
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)
{
struct dsdb_trust_routing_domain *prev, *next;
struct lsa_TrustDomainInfoInfoEx *tdo;
+
+ struct lsa_ForestTrustDomainInfo di;
+
struct lsa_ForestTrustInformation *fti;
};
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) {
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;
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;
+}