1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00
samba-mirror/source4/dsdb/common/util_trusts.c
Douglas Bagnall b37186cf91 s4:dsdb:util_trusts: simplify the NULL case in dns_cmp
In this comparison function a NULL string is treated as the ancestor
of all names, but you need to look hard to see that.

By pulling the logic for NULLs to the front, hopefully we have to look
less hard.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2024-05-07 23:25:35 +00:00

3466 lines
76 KiB
C

/*
Unix SMB/CIFS implementation.
Copyright (C) Stefan Metzmacher 2015
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "ldb.h"
#include "../lib/util/util_ldb.h"
#include "dsdb/samdb/samdb.h"
#include "libcli/security/security.h"
#include "librpc/gen_ndr/ndr_security.h"
#include "librpc/gen_ndr/ndr_misc.h"
#include "../libds/common/flags.h"
#include "dsdb/common/proto.h"
#include "param/param.h"
#include "librpc/gen_ndr/ndr_drsblobs.h"
#include "lib/util/tsort.h"
#include "dsdb/common/util.h"
#include "libds/common/flag_mapping.h"
#include "../lib/util/dlinklist.h"
#include "lib/crypto/md4.h"
#include "libcli/ldap/ldap_ndr.h"
#undef strcasecmp
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,
struct lsa_ForestTrustRecord **_lftr)
{
struct lsa_ForestTrustRecord *lftr = NULL;
const struct ForestTrustString *str = NULL;
struct lsa_StringLarge *lstr = NULL;
const struct ForestTrustDataDomainInfo *info = NULL;
struct lsa_ForestTrustDomainInfo *linfo = NULL;
*_lftr = NULL;
lftr = talloc_zero(mem_ctx, struct lsa_ForestTrustRecord);
if (lftr == NULL) {
return NT_STATUS_NO_MEMORY;
}
lftr->flags = ftr->flags;
lftr->time = ftr->timestamp;
lftr->type = (enum lsa_ForestTrustRecordType)ftr->type;
switch (lftr->type) {
case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
lstr = &lftr->forest_trust_data.top_level_name;
str = &ftr->data.name;
lstr->string = talloc_strdup(mem_ctx, str->string);
if (lstr->string == NULL) {
TALLOC_FREE(lftr);
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;
lstr->string = talloc_strdup(mem_ctx, str->string);
if (lstr->string == NULL) {
TALLOC_FREE(lftr);
return NT_STATUS_NO_MEMORY;
}
break;
case LSA_FOREST_TRUST_DOMAIN_INFO:
linfo = &lftr->forest_trust_data.domain_info;
info = &ftr->data.info;
linfo->domain_sid = dom_sid_dup(lftr, &info->sid);
if (linfo->domain_sid == NULL) {
TALLOC_FREE(lftr);
return NT_STATUS_NO_MEMORY;
}
lstr = &linfo->dns_domain_name;
str = &info->dns_name;
lstr->string = talloc_strdup(mem_ctx, str->string);
if (lstr->string == NULL) {
TALLOC_FREE(lftr);
return NT_STATUS_NO_MEMORY;
}
lstr = &linfo->netbios_domain_name;
str = &info->netbios_name;
lstr->string = talloc_strdup(mem_ctx, str->string);
if (lstr->string == NULL) {
TALLOC_FREE(lftr);
return NT_STATUS_NO_MEMORY;
}
break;
default:
return NT_STATUS_NOT_SUPPORTED;
}
*_lftr = lftr;
return NT_STATUS_OK;
}
NTSTATUS dsdb_trust_forest_info_to_lsa(TALLOC_CTX *mem_ctx,
const struct ForestTrustInfo *fti,
struct lsa_ForestTrustInformation **_lfti)
{
struct lsa_ForestTrustInformation *lfti;
uint32_t i;
*_lfti = NULL;
if (fti->version != 1) {
return NT_STATUS_INVALID_PARAMETER;
}
lfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
if (lfti == NULL) {
return NT_STATUS_NO_MEMORY;
}
lfti->count = fti->count;
lfti->entries = talloc_zero_array(mem_ctx,
struct lsa_ForestTrustRecord *,
lfti->count);
if (lfti->entries == NULL) {
TALLOC_FREE(lfti);
return NT_STATUS_NO_MEMORY;
}
for (i = 0; i < fti->count; i++) {
struct ForestTrustInfoRecord *ftr = &fti->records[i].record;
struct lsa_ForestTrustRecord *lftr = NULL;
NTSTATUS status;
status = dsdb_trust_forest_record_to_lsa(lfti->entries, ftr,
&lftr);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(lfti);
return NT_STATUS_NO_MEMORY;
}
lfti->entries[i] = lftr;
}
*_lfti = lfti;
return NT_STATUS_OK;
}
static NTSTATUS dsdb_trust_forest_info_add_record(struct lsa_ForestTrustInformation *fti,
const struct lsa_ForestTrustRecord *ftr)
{
struct lsa_ForestTrustRecord **es = NULL;
struct lsa_ForestTrustRecord *e = NULL;
const struct lsa_StringLarge *dns1 = NULL;
struct lsa_StringLarge *dns2 = NULL;
const struct lsa_ForestTrustDomainInfo *d1 = NULL;
struct lsa_ForestTrustDomainInfo *d2 = NULL;
size_t len = 0;
es = talloc_realloc(fti, fti->entries,
struct lsa_ForestTrustRecord *,
fti->count + 1);
if (!es) {
return NT_STATUS_NO_MEMORY;
}
fti->entries = es;
e = talloc_zero(es, struct lsa_ForestTrustRecord);
if (e == NULL) {
return NT_STATUS_NO_MEMORY;
}
e->type = ftr->type;
e->flags = ftr->flags;
e->time = ftr->time;
switch (ftr->type) {
case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
dns1 = &ftr->forest_trust_data.top_level_name;
dns2 = &e->forest_trust_data.top_level_name;
break;
case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
dns1 = &ftr->forest_trust_data.top_level_name_ex;
dns2 = &e->forest_trust_data.top_level_name_ex;
break;
case LSA_FOREST_TRUST_DOMAIN_INFO:
dns1 = &ftr->forest_trust_data.domain_info.dns_domain_name;
dns2 = &e->forest_trust_data.domain_info.dns_domain_name;
d1 = &ftr->forest_trust_data.domain_info;
d2 = &e->forest_trust_data.domain_info;
break;
default:
return NT_STATUS_INVALID_PARAMETER;
}
if (dns1->string == NULL) {
TALLOC_FREE(e);
return NT_STATUS_INVALID_PARAMETER;
}
len = strlen(dns1->string);
if (len == 0) {
TALLOC_FREE(e);
return NT_STATUS_INVALID_PARAMETER;
}
dns2->string = talloc_strdup(e, dns1->string);
if (dns2->string == NULL) {
TALLOC_FREE(e);
return NT_STATUS_NO_MEMORY;
}
if (d1 != NULL) {
const struct lsa_StringLarge *nb1 = &d1->netbios_domain_name;
struct lsa_StringLarge *nb2 = &d2->netbios_domain_name;
if (nb1->string == NULL) {
TALLOC_FREE(e);
return NT_STATUS_INVALID_PARAMETER;
}
len = strlen(nb1->string);
if (len == 0) {
TALLOC_FREE(e);
return NT_STATUS_INVALID_PARAMETER;
}
if (len > 15) {
TALLOC_FREE(e);
return NT_STATUS_INVALID_PARAMETER;
}
nb2->string = talloc_strdup(e, nb1->string);
if (nb2->string == NULL) {
TALLOC_FREE(e);
return NT_STATUS_NO_MEMORY;
}
if (d1->domain_sid == NULL) {
TALLOC_FREE(e);
return NT_STATUS_INVALID_PARAMETER;
}
d2->domain_sid = dom_sid_dup(e, d1->domain_sid);
if (d2->domain_sid == NULL) {
TALLOC_FREE(e);
return NT_STATUS_NO_MEMORY;
}
}
fti->entries[fti->count++] = e;
return NT_STATUS_OK;
}
static NTSTATUS dsdb_trust_parse_crossref_info(TALLOC_CTX *mem_ctx,
struct ldb_context *sam_ctx,
const struct ldb_message *msg,
struct lsa_TrustDomainInfoInfoEx **_tdo)
{
TALLOC_CTX *frame = talloc_stackframe();
struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
const char *dns = NULL;
const char *netbios = NULL;
struct ldb_dn *nc_dn = NULL;
struct dom_sid sid = {
.num_auths = 0,
};
NTSTATUS status;
*_tdo = NULL;
tdo = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
if (tdo == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
talloc_steal(frame, tdo);
dns = ldb_msg_find_attr_as_string(msg, "dnsRoot", NULL);
if (dns == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
tdo->domain_name.string = talloc_strdup(tdo, dns);
if (tdo->domain_name.string == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
netbios = ldb_msg_find_attr_as_string(msg, "nETBIOSName", NULL);
if (netbios == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
tdo->netbios_name.string = talloc_strdup(tdo, netbios);
if (tdo->netbios_name.string == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
nc_dn = samdb_result_dn(sam_ctx, frame, msg, "ncName", NULL);
if (nc_dn == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = dsdb_get_extended_dn_sid(nc_dn, &sid, "SID");
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
tdo->sid = dom_sid_dup(tdo, &sid);
if (tdo->sid == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
tdo->trust_type = LSA_TRUST_TYPE_UPLEVEL;
tdo->trust_direction = LSA_TRUST_DIRECTION_INBOUND |
LSA_TRUST_DIRECTION_OUTBOUND;
tdo->trust_attributes = LSA_TRUST_ATTRIBUTE_WITHIN_FOREST;
*_tdo = talloc_move(mem_ctx, &tdo);
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
static NTSTATUS dsdb_trust_crossref_tdo_info(TALLOC_CTX *mem_ctx,
struct ldb_context *sam_ctx,
struct ldb_dn *domain_dn,
const char *extra_filter,
struct lsa_TrustDomainInfoInfoEx **_tdo,
struct lsa_TrustDomainInfoInfoEx **_root_trust_tdo,
struct lsa_TrustDomainInfoInfoEx **_trust_parent_tdo)
{
TALLOC_CTX *frame = talloc_stackframe();
struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
struct lsa_TrustDomainInfoInfoEx *root_trust_tdo = NULL;
struct lsa_TrustDomainInfoInfoEx *trust_parent_tdo = NULL;
struct ldb_dn *partitions_dn = NULL;
const char * const cross_attrs[] = {
"dnsRoot",
"nETBIOSName",
"nCName",
"rootTrust",
"trustParent",
NULL,
};
struct ldb_result *cross_res = NULL;
struct ldb_message *msg = NULL;
struct ldb_dn *root_trust_dn = NULL;
struct ldb_dn *trust_parent_dn = NULL;
NTSTATUS status;
int ret;
if (extra_filter == NULL) {
extra_filter = "";
}
*_tdo = NULL;
if (_root_trust_tdo != NULL) {
*_root_trust_tdo = NULL;
}
if (_trust_parent_tdo != NULL) {
*_trust_parent_tdo = NULL;
}
partitions_dn = samdb_partitions_dn(sam_ctx, frame);
if (partitions_dn == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
ret = dsdb_search(sam_ctx, partitions_dn, &cross_res,
partitions_dn, LDB_SCOPE_ONELEVEL,
cross_attrs,
DSDB_SEARCH_ONE_ONLY |
DSDB_SEARCH_SHOW_EXTENDED_DN,
"(&"
"(ncName=%s)"
"(objectClass=crossRef)"
"(systemFlags:%s:=%u)"
"%s"
")",
ldb_dn_get_linearized(domain_dn),
LDB_OID_COMPARATOR_AND,
SYSTEM_FLAG_CR_NTDS_DOMAIN,
extra_filter);
if (ret != LDB_SUCCESS) {
TALLOC_FREE(frame);
return dsdb_ldb_err_to_ntstatus(ret);
}
msg = cross_res->msgs[0];
status = dsdb_trust_parse_crossref_info(mem_ctx, sam_ctx, msg, &tdo);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
talloc_steal(frame, tdo);
if (_root_trust_tdo != NULL) {
root_trust_dn = samdb_result_dn(sam_ctx, frame, msg,
"rootTrust", NULL);
}
if (_trust_parent_tdo != NULL) {
trust_parent_dn = samdb_result_dn(sam_ctx, frame, msg,
"trustParent", NULL);
}
if (root_trust_dn != NULL) {
struct ldb_message *root_trust_msg = NULL;
ret = dsdb_search_one(sam_ctx, frame,
&root_trust_msg,
root_trust_dn,
LDB_SCOPE_BASE,
cross_attrs,
DSDB_SEARCH_NO_GLOBAL_CATALOG,
"(objectClass=crossRef)");
if (ret != LDB_SUCCESS) {
status = dsdb_ldb_err_to_ntstatus(ret);
DEBUG(3, ("Failed to search for %s: %s - %s\n",
ldb_dn_get_linearized(root_trust_dn),
nt_errstr(status), ldb_errstring(sam_ctx)));
TALLOC_FREE(frame);
return status;
}
status = dsdb_trust_parse_crossref_info(mem_ctx, sam_ctx,
root_trust_msg,
&root_trust_tdo);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
talloc_steal(frame, root_trust_tdo);
}
if (trust_parent_dn != NULL) {
struct ldb_message *trust_parent_msg = NULL;
ret = dsdb_search_one(sam_ctx, frame,
&trust_parent_msg,
trust_parent_dn,
LDB_SCOPE_BASE,
cross_attrs,
DSDB_SEARCH_NO_GLOBAL_CATALOG,
"(objectClass=crossRef)");
if (ret != LDB_SUCCESS) {
status = dsdb_ldb_err_to_ntstatus(ret);
DEBUG(3, ("Failed to search for %s: %s - %s\n",
ldb_dn_get_linearized(trust_parent_dn),
nt_errstr(status), ldb_errstring(sam_ctx)));
TALLOC_FREE(frame);
return status;
}
status = dsdb_trust_parse_crossref_info(mem_ctx, sam_ctx,
trust_parent_msg,
&trust_parent_tdo);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
talloc_steal(frame, trust_parent_tdo);
}
*_tdo = talloc_move(mem_ctx, &tdo);
if (_root_trust_tdo != NULL) {
*_root_trust_tdo = talloc_move(mem_ctx, &root_trust_tdo);
}
if (_trust_parent_tdo != NULL) {
*_trust_parent_tdo = talloc_move(mem_ctx, &trust_parent_tdo);
}
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
#define DNS_CMP_FIRST_IS_CHILD -2
#define DNS_CMP_FIRST_IS_LESS -1
#define DNS_CMP_MATCH 0
#define DNS_CMP_SECOND_IS_LESS 1
#define DNS_CMP_SECOND_IS_CHILD 2
#define DNS_CMP_IS_NO_MATCH(__cmp) \
((__cmp == DNS_CMP_FIRST_IS_LESS) || (__cmp == DNS_CMP_SECOND_IS_LESS))
/*
* this function assumes names are well formed DNS names.
* it doesn't validate them
*
* It allows strings up to a length of UINT16_MAX - 1
* with up to UINT8_MAX components. On overflow this
* just returns the result of strcasecmp_m().
*
* Trailing dots (only one) are ignored.
*
* The DNS names are compared per component, starting from
* the last one.
*
* The function is usable in a sort, but the return value contains more
* information than a simple comparison. There are 5 return values, defined
* above.
*
* DNS_CMP_FIRST_IS_CHILD (-2) means the first argument is a sub-domain of the
* second. e.g. dns_cmp("foo.example.org", "example.org")
*
* DNS_CMP_FIRST_IS_LESS (-1) means the first argument sorts before the
* second, but is not a sub-domain. e.g. dns_cmp("eggsample.org", "example.org").
*
* DNS_CMP_SECOND_IS_CHILD (+2) and DNS_CMP_SECOND_IS_LESS (+1) have the
* similar expected meanings. DNS_CMP_MATCH (0) means equality.
*
* NULL values are the parent of all addresses, which means comparisons
* between a string and NULL will return +2 or -2.
*/
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] = {0};
size_t l2 = 0;
const char *p2 = NULL;
size_t num_comp2 = 0;
uint16_t comp2[UINT8_MAX] = {0};
size_t i;
if (s1 == s2) {
/* this includes the both NULL case */
return DNS_CMP_MATCH;
}
if (s1 == NULL) {
return DNS_CMP_SECOND_IS_CHILD;
}
if (s2 == NULL) {
return DNS_CMP_FIRST_IS_CHILD;
}
l1 = strlen(s1);
l2 = strlen(s2);
/*
* trailing '.' are ignored.
*/
if (l1 > 1 && s1[l1 - 1] == '.') {
l1--;
}
if (l2 > 1 && s2[l2 - 1] == '.') {
l2--;
}
for (i = 0; i < ARRAY_SIZE(comp1); i++) {
char *p;
if (i == 0) {
p1 = s1;
if (l1 == 0 || l1 >= UINT16_MAX) {
/* just use one single component on overflow */
break;
}
}
comp1[num_comp1++] = PTR_DIFF(p1, s1);
p = strchr_m(p1, '.');
if (p == NULL) {
p1 = NULL;
break;
}
p1 = p + 1;
}
if (p1 != NULL) {
/* just use one single component on overflow */
num_comp1 = 0;
comp1[num_comp1++] = 0;
p1 = NULL;
}
for (i = 0; i < ARRAY_SIZE(comp2); i++) {
char *p;
if (i == 0) {
p2 = s2;
if (l2 == 0 || l2 >= UINT16_MAX) {
/* just use one single component on overflow */
break;
}
}
comp2[num_comp2++] = PTR_DIFF(p2, s2);
p = strchr_m(p2, '.');
if (p == NULL) {
p2 = NULL;
break;
}
p2 = p + 1;
}
if (p2 != NULL) {
/* just use one single component on overflow */
num_comp2 = 0;
comp2[num_comp2++] = 0;
p2 = NULL;
}
for (i = 0; i < UINT8_MAX; i++) {
int cmp;
if (i < num_comp1) {
size_t idx = num_comp1 - (i + 1);
p1 = s1 + comp1[idx];
} else {
p1 = NULL;
}
if (i < num_comp2) {
size_t idx = num_comp2 - (i + 1);
p2 = s2 + comp2[idx];
} else {
p2 = NULL;
}
if (p1 == NULL && p2 == NULL) {
return DNS_CMP_MATCH;
}
if (p1 != NULL && p2 == NULL) {
return DNS_CMP_FIRST_IS_CHILD;
}
if (p1 == NULL && p2 != NULL) {
return DNS_CMP_SECOND_IS_CHILD;
}
cmp = strcasecmp_m(p1, p2);
if (cmp < 0) {
return DNS_CMP_FIRST_IS_LESS;
}
if (cmp > 0) {
return DNS_CMP_SECOND_IS_LESS;
}
}
smb_panic(__location__);
return -1;
}
static int dsdb_trust_find_tln_match_internal(const struct lsa_ForestTrustInformation *info,
enum lsa_ForestTrustRecordType type,
uint32_t disable_mask,
const char *tln)
{
uint32_t i;
for (i = 0; i < info->count; i++) {
struct lsa_ForestTrustRecord *e = info->entries[i];
struct lsa_StringLarge *t = NULL;
int cmp;
if (e == NULL) {
continue;
}
if (e->type != type) {
continue;
}
if (e->flags & disable_mask) {
continue;
}
switch (type) {
case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
t = &e->forest_trust_data.top_level_name;
break;
case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
t = &e->forest_trust_data.top_level_name_ex;
break;
default:
break;
}
if (t == NULL) {
continue;
}
cmp = dns_cmp(tln, t->string);
switch (cmp) {
case DNS_CMP_MATCH:
case DNS_CMP_FIRST_IS_CHILD:
return i;
}
}
return -1;
}
static bool dsdb_trust_find_tln_match(const struct lsa_ForestTrustInformation *info,
const char *tln)
{
int m;
m = dsdb_trust_find_tln_match_internal(info,
LSA_FOREST_TRUST_TOP_LEVEL_NAME,
LSA_TLN_DISABLED_MASK,
tln);
if (m != -1) {
return true;
}
return false;
}
static bool dsdb_trust_find_tln_ex_match(const struct lsa_ForestTrustInformation *info,
const char *tln)
{
int m;
m = dsdb_trust_find_tln_match_internal(info,
LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX,
0,
tln);
if (m != -1) {
return true;
}
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)
{
struct ldb_message *m1 = *_m1;
struct ldb_message *m2 = *_m2;
const char *dns1 = NULL;
const char *dns2 = NULL;
int cmp;
struct ldb_message_element *rootTrust1 = NULL;
struct ldb_message_element *trustParent1 = NULL;
struct ldb_message_element *rootTrust2 = NULL;
struct ldb_message_element *trustParent2 = NULL;
dns1 = ldb_msg_find_attr_as_string(m1, "dnsRoot", NULL);
dns2 = ldb_msg_find_attr_as_string(m2, "dnsRoot", NULL);
cmp = dns_cmp(dns1, dns2);
switch (cmp) {
case DNS_CMP_FIRST_IS_CHILD:
return -1;
case DNS_CMP_SECOND_IS_CHILD:
return 1;
}
rootTrust1 = ldb_msg_find_element(m1, "rootTrust");
trustParent1 = ldb_msg_find_element(m1, "trustParent");
rootTrust2 = ldb_msg_find_element(m2, "rootTrust");
trustParent2 = ldb_msg_find_element(m2, "trustParent");
if (rootTrust1 == NULL && trustParent1 == NULL) {
/* m1 is the forest root */
return -1;
}
if (rootTrust2 == NULL && trustParent2 == NULL) {
/* m2 is the forest root */
return 1;
}
return cmp;
}
static int dsdb_trust_xref_sort_vals(struct ldb_val *v1,
struct ldb_val *v2)
{
const char *dns1 = (const char *)v1->data;
const char *dns2 = (const char *)v2->data;
return dns_cmp(dns1, dns2);
}
NTSTATUS dsdb_trust_xref_forest_info(TALLOC_CTX *mem_ctx,
struct ldb_context *sam_ctx,
struct lsa_ForestTrustInformation **_info)
{
TALLOC_CTX *frame = talloc_stackframe();
struct lsa_ForestTrustInformation *info = NULL;
struct ldb_dn *partitions_dn = NULL;
const char * const cross_attrs1[] = {
"uPNSuffixes",
"msDS-SPNSuffixes",
NULL,
};
struct ldb_result *cross_res1 = NULL;
struct ldb_message_element *upn_el = NULL;
struct ldb_message_element *spn_el = NULL;
struct ldb_message *tln_msg = NULL;
struct ldb_message_element *tln_el = NULL;
const char * const cross_attrs2[] = {
"dnsRoot",
"nETBIOSName",
"nCName",
"rootTrust",
"trustParent",
NULL,
};
struct ldb_result *cross_res2 = NULL;
int ret;
unsigned int i;
bool restart = false;
*_info = NULL;
info = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
if (info == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
talloc_steal(frame, info);
partitions_dn = samdb_partitions_dn(sam_ctx, frame);
if (partitions_dn == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
ret = dsdb_search_dn(sam_ctx, partitions_dn, &cross_res1,
partitions_dn, cross_attrs1, 0);
if (ret != LDB_SUCCESS) {
TALLOC_FREE(frame);
return dsdb_ldb_err_to_ntstatus(ret);
}
ret = dsdb_search(sam_ctx, partitions_dn, &cross_res2,
partitions_dn, LDB_SCOPE_ONELEVEL,
cross_attrs2,
DSDB_SEARCH_SHOW_EXTENDED_DN,
"(&(objectClass=crossRef)"
"(systemFlags:%s:=%u))",
LDB_OID_COMPARATOR_AND,
SYSTEM_FLAG_CR_NTDS_DOMAIN);
if (ret != LDB_SUCCESS) {
TALLOC_FREE(frame);
return dsdb_ldb_err_to_ntstatus(ret);
}
/*
* Sort the domains as trees, starting with the forest root
*/
TYPESAFE_QSORT(cross_res2->msgs, cross_res2->count,
dsdb_trust_xref_sort_msgs);
upn_el = ldb_msg_find_element(cross_res1->msgs[0], "uPNSuffixes");
if (upn_el != NULL) {
upn_el->name = "__tln__";
}
spn_el = ldb_msg_find_element(cross_res1->msgs[0], "msDS-SPNSuffixes");
if (spn_el != NULL) {
spn_el->name = "__tln__";
}
ret = ldb_msg_normalize(sam_ctx, frame, cross_res1->msgs[0], &tln_msg);
if (ret != LDB_SUCCESS) {
TALLOC_FREE(frame);
return dsdb_ldb_err_to_ntstatus(ret);
}
tln_el = ldb_msg_find_element(tln_msg, "__tln__");
if (tln_el != NULL) {
/*
* Sort the domains as trees
*/
TYPESAFE_QSORT(tln_el->values, tln_el->num_values,
dsdb_trust_xref_sort_vals);
}
for (i=0; i < cross_res2->count; i++) {
struct ldb_message *m = cross_res2->msgs[i];
const char *dns = NULL;
const char *netbios = NULL;
struct ldb_dn *nc_dn = NULL;
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;
NTSTATUS status;
dns = ldb_msg_find_attr_as_string(m, "dnsRoot", NULL);
if (dns == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
netbios = ldb_msg_find_attr_as_string(m, "nETBIOSName", NULL);
if (netbios == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
nc_dn = samdb_result_dn(sam_ctx, m, m, "ncName", NULL);
if (nc_dn == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = dsdb_get_extended_dn_sid(nc_dn, &sid, "SID");
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
match = dsdb_trust_find_tln_match(info, dns);
if (!match) {
/*
* First the TOP_LEVEL_NAME, if required
*/
e = (struct lsa_ForestTrustRecord) {
.flags = 0,
.type = LSA_FOREST_TRUST_TOP_LEVEL_NAME,
.time = 0, /* so far always 0 in traces. */
};
t = &e.forest_trust_data.top_level_name;
t->string = dns;
status = dsdb_trust_forest_info_add_record(info, &e);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
}
/*
* Then the DOMAIN_INFO
*/
e = (struct lsa_ForestTrustRecord) {
.flags = 0,
.type = LSA_FOREST_TRUST_DOMAIN_INFO,
.time = 0, /* so far always 0 in traces. */
};
d = &e.forest_trust_data.domain_info;
d->domain_sid = &sid;
d->dns_domain_name.string = dns;
d->netbios_domain_name.string = netbios;
status = dsdb_trust_forest_info_add_record(info, &e);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
}
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 = {
.flags = 0,
};
struct lsa_StringLarge *t = NULL;
bool match = false;
NTSTATUS status;
if (dns == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
match = dsdb_trust_find_tln_match(info, dns);
if (match) {
continue;
}
/*
* an additional the TOP_LEVEL_NAME
*/
e = (struct lsa_ForestTrustRecord) {
.flags = 0,
.type = LSA_FOREST_TRUST_TOP_LEVEL_NAME,
.time = 0, /* so far always 0 in traces. */
};
t = &e.forest_trust_data.top_level_name;
t->string = dns;
status = dsdb_trust_forest_info_add_record(info, &e);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
}
for (i=0; i < info->count; restart ? i=0 : i++) {
struct lsa_ForestTrustRecord *tr = info->entries[i];
const struct lsa_StringLarge *ts = NULL;
uint32_t c;
restart = false;
if (tr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
continue;
}
ts = &tr->forest_trust_data.top_level_name;
for (c = i + 1; c < info->count; c++) {
struct lsa_ForestTrustRecord *cr = info->entries[c];
const struct lsa_StringLarge *cs = NULL;
uint32_t j;
int cmp;
if (cr->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
continue;
}
cs = &cr->forest_trust_data.top_level_name;
cmp = dns_cmp(ts->string, cs->string);
if (DNS_CMP_IS_NO_MATCH(cmp)) {
continue;
}
if (cmp != DNS_CMP_FIRST_IS_CHILD) {
/* can't happen ... */
continue;
}
ts = NULL;
tr = NULL;
TALLOC_FREE(info->entries[i]);
info->entries[i] = info->entries[c];
for (j = c + 1; j < info->count; j++) {
info->entries[j-1] = info->entries[j];
}
info->count -= 1;
restart = true;
break;
}
}
*_info = talloc_move(mem_ctx, &info);
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
NTSTATUS dsdb_trust_parse_tdo_info(TALLOC_CTX *mem_ctx,
struct ldb_message *m,
struct lsa_TrustDomainInfoInfoEx **_tdo)
{
struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
const char *dns = NULL;
const char *netbios = NULL;
*_tdo = NULL;
tdo = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
if (tdo == NULL) {
return NT_STATUS_NO_MEMORY;
}
dns = ldb_msg_find_attr_as_string(m, "trustPartner", NULL);
if (dns == NULL) {
TALLOC_FREE(tdo);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
tdo->domain_name.string = talloc_strdup(tdo, dns);
if (tdo->domain_name.string == NULL) {
TALLOC_FREE(tdo);
return NT_STATUS_NO_MEMORY;
}
netbios = ldb_msg_find_attr_as_string(m, "flatName", NULL);
if (netbios == NULL) {
TALLOC_FREE(tdo);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
tdo->netbios_name.string = talloc_strdup(tdo, netbios);
if (tdo->netbios_name.string == NULL) {
TALLOC_FREE(tdo);
return NT_STATUS_NO_MEMORY;
}
tdo->sid = samdb_result_dom_sid(tdo, m, "securityIdentifier");
if (tdo->sid == NULL) {
TALLOC_FREE(tdo);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
tdo->trust_type = ldb_msg_find_attr_as_uint(m, "trustType", 0);
tdo->trust_direction = ldb_msg_find_attr_as_uint(m, "trustDirection", 0);
tdo->trust_attributes = ldb_msg_find_attr_as_uint(m, "trustAttributes", 0);
*_tdo = tdo;
return NT_STATUS_OK;
}
NTSTATUS dsdb_trust_parse_forest_info(TALLOC_CTX *mem_ctx,
struct ldb_message *m,
struct ForestTrustInfo **_fti)
{
const struct ldb_val *ft_blob = NULL;
struct ForestTrustInfo *fti = NULL;
enum ndr_err_code ndr_err;
*_fti = NULL;
ft_blob = ldb_msg_find_ldb_val(m, "msDS-TrustForestTrustInfo");
if (ft_blob == NULL) {
return NT_STATUS_NOT_FOUND;
}
fti = talloc_zero(mem_ctx, struct ForestTrustInfo);
if (fti == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* ldb_val is equivalent to DATA_BLOB */
ndr_err = ndr_pull_struct_blob_all(ft_blob, fti, fti,
(ndr_pull_flags_fn_t)ndr_pull_ForestTrustInfo);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
TALLOC_FREE(fti);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
*_fti = fti;
return NT_STATUS_OK;
}
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();
struct lsa_ForestTrustInformation *nfti;
uint32_t n;
*_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);
/*
* First we copy every record and remove possible trailing dots
* from dns names.
*
* We also NULL out duplicates. 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 (gftr == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INVALID_PARAMETER;
}
status = dsdb_trust_forest_info_add_record(nfti, gftr);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
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_INVALID_PARAMETER;
}
/*
* 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_INVALID_PARAMETER;
}
p = strstr_m(ntln->string, "..");
if (p != NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INVALID_PARAMETER;
}
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;
if (cftr == NULL) {
continue;
}
if (cftr->type != nftr->type) {
continue;
}
switch (cftr->type) {
case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
ctln = &cftr->forest_trust_data.top_level_name;
break;
case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
ctln = &cftr->forest_trust_data.top_level_name_ex;
break;
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;
default:
TALLOC_FREE(frame);
return NT_STATUS_INVALID_PARAMETER;
}
cmp = dns_cmp(ntln->string, ctln->string);
if (cmp == DNS_CMP_MATCH) {
nftr = NULL;
TALLOC_FREE(nfti->entries[n]);
break;
}
if (cinfo == NULL) {
continue;
}
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;
}
}
}
/*
* 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;
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 = samdb_system_container_dn(sam_ctx, frame);
if (system_dn == NULL) {
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 = samdb_system_container_dn(sam_ctx, frame);
if (system_dn == NULL) {
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_memdup(mem_ctx, current, sizeof(*current));
if (*_current == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
}
if (_previous != NULL) {
*_previous =
talloc_memdup(mem_ctx, previous, sizeof(*previous));
if (*_previous == NULL) {
if (_current != NULL) {
TALLOC_FREE(*_current);
}
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
}
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)
{
TALLOC_CTX *frame = talloc_stackframe();
int ret;
struct ldb_dn *system_dn = NULL;
const char *filter = NULL;
char *exclude_encoded = NULL;
*res = NULL;
system_dn = samdb_system_container_dn(sam_ctx, frame);
if (system_dn == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
if (exclude != NULL) {
exclude_encoded = ldb_binary_encode_string(frame, exclude);
if (exclude_encoded == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
filter = talloc_asprintf(frame,
"(&(objectClass=trustedDomain)"
"(!(|(trustPartner=%s)(flatName=%s)))"
")",
exclude_encoded, exclude_encoded);
if (filter == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
} else {
filter = "(objectClass=trustedDomain)";
}
ret = dsdb_search(sam_ctx, mem_ctx, res,
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;
}
struct dsdb_trust_routing_domain;
struct dsdb_trust_routing_table {
struct dsdb_trust_routing_domain *domains;
};
struct dsdb_trust_routing_domain {
struct dsdb_trust_routing_domain *prev, *next;
struct lsa_TrustDomainInfoInfoEx *tdo;
struct lsa_ForestTrustDomainInfo di;
struct lsa_ForestTrustInformation *fti;
};
NTSTATUS dsdb_trust_routing_table_load(struct ldb_context *sam_ctx,
TALLOC_CTX *mem_ctx,
struct dsdb_trust_routing_table **_table)
{
TALLOC_CTX *frame = talloc_stackframe();
struct dsdb_trust_routing_table *table;
struct dsdb_trust_routing_domain *d = NULL;
struct ldb_dn *domain_dn = NULL;
struct lsa_TrustDomainInfoInfoEx *root_trust_tdo = NULL;
struct lsa_TrustDomainInfoInfoEx *trust_parent_tdo = NULL;
struct lsa_TrustDomainInfoInfoEx *root_direction_tdo = NULL;
const char * const trusts_attrs[] = {
"securityIdentifier",
"flatName",
"trustPartner",
"trustAttributes",
"trustDirection",
"trustType",
"msDS-TrustForestTrustInfo",
NULL
};
struct ldb_result *trusts_res = NULL;
unsigned int i;
NTSTATUS status;
*_table = NULL;
domain_dn = ldb_get_default_basedn(sam_ctx);
if (domain_dn == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_INTERNAL_ERROR;
}
table = talloc_zero(mem_ctx, struct dsdb_trust_routing_table);
if (table == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
talloc_steal(frame, table);
d = talloc_zero(table, struct dsdb_trust_routing_domain);
if (d == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
status = dsdb_trust_crossref_tdo_info(d, sam_ctx,
domain_dn, NULL,
&d->tdo,
&root_trust_tdo,
&trust_parent_tdo);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
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) {
root_direction_tdo = trust_parent_tdo;
}
if (root_direction_tdo == NULL) {
/* we're the forest root */
status = dsdb_trust_xref_forest_info(d, sam_ctx, &d->fti);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
}
DLIST_ADD(table->domains, d);
status = dsdb_trust_search_tdos(sam_ctx, NULL, trusts_attrs,
frame, &trusts_res);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
for (i = 0; i < trusts_res->count; i++) {
bool ok;
int cmp;
d = talloc_zero(table, struct dsdb_trust_routing_domain);
if (d == NULL) {
TALLOC_FREE(frame);
return NT_STATUS_NO_MEMORY;
}
status = dsdb_trust_parse_tdo_info(d,
trusts_res->msgs[i],
&d->tdo);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
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;
status = dsdb_trust_parse_forest_info(frame,
trusts_res->msgs[i],
&fti);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
fti = NULL;
status = NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
if (fti == NULL) {
continue;
}
status = dsdb_trust_forest_info_to_lsa(d, fti, &d->fti);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
continue;
}
if (!(d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
continue;
}
if (root_direction_tdo == NULL) {
continue;
}
ok = dom_sid_equal(root_direction_tdo->sid, d->tdo->sid);
if (!ok) {
continue;
}
cmp = strcasecmp_m(root_direction_tdo->netbios_name.string,
d->tdo->netbios_name.string);
if (cmp != 0) {
continue;
}
cmp = strcasecmp_m(root_direction_tdo->domain_name.string,
d->tdo->domain_name.string);
if (cmp != 0) {
continue;
}
/* this our route to the forest root */
status = dsdb_trust_xref_forest_info(d, sam_ctx, &d->fti);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(frame);
return status;
}
}
*_table = talloc_move(mem_ctx, &table);
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
static void dsdb_trust_update_best_tln(
const struct dsdb_trust_routing_domain **best_d,
const char **best_tln,
const struct dsdb_trust_routing_domain *d,
const char *tln)
{
int cmp;
if (*best_tln == NULL) {
*best_tln = tln;
*best_d = d;
return;
}
cmp = dns_cmp(*best_tln, tln);
if (cmp != DNS_CMP_FIRST_IS_CHILD) {
return;
}
*best_tln = tln;
*best_d = d;
}
const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_routing_by_name(
const struct dsdb_trust_routing_table *table,
const char *name)
{
const struct dsdb_trust_routing_domain *best_d = NULL;
const char *best_tln = NULL;
const struct dsdb_trust_routing_domain *d = NULL;
if (name == NULL) {
return NULL;
}
for (d = table->domains; d != NULL; d = d->next) {
bool transitive = false;
bool allow_netbios = false;
bool exclude = false;
uint32_t i;
if (d->tdo->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
/*
* Only uplevel trusts have top level names
*/
continue;
}
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;
}
switch (d->tdo->trust_type) {
case LSA_TRUST_TYPE_UPLEVEL:
if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY) {
break;
}
allow_netbios = true;
break;
case LSA_TRUST_TYPE_DOWNLEVEL:
allow_netbios = true;
break;
default:
allow_netbios = false;
break;
}
if (!transitive || d->fti == NULL) {
int cmp;
if (allow_netbios) {
cmp = dns_cmp(name, d->tdo->netbios_name.string);
if (cmp == DNS_CMP_MATCH) {
/*
* exact match
*/
return d->tdo;
}
}
cmp = dns_cmp(name, d->tdo->domain_name.string);
if (cmp == DNS_CMP_MATCH) {
/*
* exact match
*/
return d->tdo;
}
if (cmp != DNS_CMP_FIRST_IS_CHILD) {
continue;
}
if (!transitive) {
continue;
}
dsdb_trust_update_best_tln(&best_d, &best_tln, d,
d->tdo->domain_name.string);
continue;
}
exclude = dsdb_trust_find_tln_ex_match(d->fti, name);
if (exclude) {
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 char *fti_nbt = NULL;
int cmp;
if (!allow_netbios) {
break;
}
if (f == NULL) {
/* broken record */
continue;
}
if (f->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
continue;
}
if (f->flags & LSA_NB_DISABLED_MASK) {
/*
* any flag disables the entry.
*/
continue;
}
di = &f->forest_trust_data.domain_info;
fti_nbt = di->netbios_domain_name.string;
if (fti_nbt == NULL) {
/* broken record */
continue;
}
cmp = dns_cmp(name, fti_nbt);
if (cmp == DNS_CMP_MATCH) {
/*
* exact match
*/
return d->tdo;
}
}
for (i = 0; i < d->fti->count; i++ ) {
const struct lsa_ForestTrustRecord *f = d->fti->entries[i];
const union lsa_ForestTrustData *u = NULL;
const char *fti_tln = NULL;
int cmp;
if (f == NULL) {
/* broken record */
continue;
}
if (f->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
continue;
}
if (f->flags & LSA_TLN_DISABLED_MASK) {
/*
* any flag disables the entry.
*/
continue;
}
u = &f->forest_trust_data;
fti_tln = u->top_level_name.string;
if (fti_tln == NULL) {
continue;
}
cmp = dns_cmp(name, fti_tln);
switch (cmp) {
case DNS_CMP_MATCH:
case DNS_CMP_FIRST_IS_CHILD:
dsdb_trust_update_best_tln(&best_d, &best_tln,
d, fti_tln);
break;
default:
break;
}
}
}
if (best_d != NULL) {
return best_d->tdo;
}
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;
}