mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
232054c09b
lib/util/safe_string.h is similar to source3/include/safe_string.h, but the former has fewer checks. It is missing bcopy, strcasecmp, and strncasecmp. Add the missing elements to lib/util/safe_string.h remove the other safe_string.h which is in the source3-specific path. To accomodate existing uses of str(n?)casecmp, add #undef lines to source files where they are used. Signed-off-by: Matthew DeVore <matvore@google.com> Reviewed-by: David Mulder <dmulder@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org> Autobuild-User(master): Jeremy Allison <jra@samba.org> Autobuild-Date(master): Fri Aug 28 02:18:40 UTC 2020 on sn-devel-184
3460 lines
76 KiB
C
3460 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.
|
|
*/
|
|
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 != NULL) {
|
|
l1 = strlen(s1);
|
|
}
|
|
|
|
if (s2 != NULL) {
|
|
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 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 (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 = 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)
|
|
{
|
|
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 = 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 (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;
|
|
}
|