From: Stefan Metzmacher Date: Mon, 10 Feb 2025 13:56:15 +0000 (+0100) Subject: s4:dsdb: add forest trust scanner service X-Git-Tag: tevent-0.17.0~684 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f5b112b436667f6cedf5a4b62821dca36ed4471f;p=thirdparty%2Fsamba.git s4:dsdb: add forest trust scanner service See MS-ADTS 3.1.1.6.4 PDC Forest Trust Update It basically connects to all forest trusts and searches for crossRef objects with SYSTEM_FLAG_CR_NTDS_DOMAIN under CN=Partitions,CN=Configuration. With this information it add/removes FOREST_TRUST_SCANNER_INFO records into the msDS-TrustForestTrustInfo of the local trustedDomain object. Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- diff --git a/source4/dsdb/ft_scanner/ft_scanner_periodic.c b/source4/dsdb/ft_scanner/ft_scanner_periodic.c new file mode 100644 index 00000000000..7b35675d374 --- /dev/null +++ b/source4/dsdb/ft_scanner/ft_scanner_periodic.c @@ -0,0 +1,122 @@ +/* + Unix SMB/CIFS Implementation. + forest trust scanner service + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +#include "includes.h" +#include "dsdb/samdb/samdb.h" +#include "samba/service.h" +#include "dsdb/ft_scanner/ft_scanner_service.h" +#include "dsdb/ft_scanner/ft_scanner_service_proto.h" +#include + +static void ft_scanner_periodic_run(struct ft_scanner_service *service); + +static void ft_scanner_periodic_handler_te(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + struct ft_scanner_service *service = + talloc_get_type_abort(ptr, + struct ft_scanner_service); + NTSTATUS status; + + service->periodic.te = NULL; + + ft_scanner_periodic_run(service); + + status = ft_scanner_periodic_schedule(service, + service->periodic.interval); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(service->task, nt_errstr(status), false); + return; + } +} + +NTSTATUS ft_scanner_periodic_schedule(struct ft_scanner_service *service, + uint32_t next_interval) +{ + TALLOC_CTX *frame = NULL; + struct tevent_timer *new_te = NULL; + struct timeval next_time; + + /* prevent looping */ + if (next_interval == 0) next_interval = 1; + + next_time = timeval_current_ofs(next_interval, 50); + + if (service->periodic.te) { + /* + * if the timestamp of the new event is higher, + * as current next we don't need to reschedule + */ + if (timeval_compare(&next_time, &service->periodic.next_event) > 0) { + return NT_STATUS_OK; + } + } + + /* reset the next scheduled timestamp */ + service->periodic.next_event = next_time; + + new_te = tevent_add_timer(service->task->event_ctx, service, + service->periodic.next_event, + ft_scanner_periodic_handler_te, service); + if (new_te == NULL) { + return NT_STATUS_NO_MEMORY; + } + + frame = talloc_stackframe(); + D_DEBUG("ft_scanner_periodic_schedule(%u) %sscheduled for: %s\n", + next_interval, + (service->periodic.te?"re":""), + nt_time_string(frame, timeval_to_nttime(&next_time))); + TALLOC_FREE(frame); + + TALLOC_FREE(service->periodic.te); + service->periodic.te = new_te; + + return NT_STATUS_OK; +} + +static void ft_scanner_periodic_run(struct ft_scanner_service *service) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + bool is_pdc; + + is_pdc = samdb_is_pdc(service->l_samdb); + if (!is_pdc) { + DBG_DEBUG("NO-OP: we are not the current PDC\n"); + TALLOC_FREE(frame); + return; + } + + DBG_DEBUG("Running ft_scanner_check_trusts()\n"); + status = ft_scanner_check_trusts(service); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("ft_scanner_check_trusts() => %s\n", + nt_errstr(status)); + TALLOC_FREE(frame); + return; + } + DBG_DEBUG("ft_scanner_check_trusts() => %s\n", + nt_errstr(status)); + + TALLOC_FREE(frame); +} diff --git a/source4/dsdb/ft_scanner/ft_scanner_service.c b/source4/dsdb/ft_scanner/ft_scanner_service.c new file mode 100644 index 00000000000..0cb41af484d --- /dev/null +++ b/source4/dsdb/ft_scanner/ft_scanner_service.c @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS Implementation. + forest trust scanner service + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +#include "includes.h" +#include "dsdb/samdb/samdb.h" +#include "auth/auth.h" +#include "samba/service.h" +#include "dsdb/ft_scanner/ft_scanner_service.h" +#include "dsdb/ft_scanner/ft_scanner_service_proto.h" +#include +#include "lib/messaging/irpc.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "param/param.h" +#include "libds/common/roles.h" + +static NTSTATUS ft_scanner_connect_samdb(struct ft_scanner_service *service) +{ + struct auth_session_info *session_info = NULL; + + session_info = system_session(service->task->lp_ctx); + if (session_info == NULL) { + return NT_STATUS_DS_INIT_FAILURE; + } + + service->l_samdb = samdb_connect(service, + service->task->event_ctx, + service->task->lp_ctx, + session_info, + NULL, + 0); + if (service->l_samdb == NULL) { + return NT_STATUS_DS_UNAVAILABLE; + } + + return NT_STATUS_OK; +} + +/* + startup the forest trust scanner service task +*/ +static NTSTATUS ft_scanner_task_init(struct task_server *task) +{ + struct ft_scanner_service *service = NULL; + uint32_t periodic_startup_interval; + NTSTATUS status; + bool am_rodc; + int ret; + + switch (lpcfg_server_role(task->lp_ctx)) { + case ROLE_STANDALONE: + task_server_terminate(task, + "ft_scanner: no forest trust scanning " + "required in standalone configuration", + false); + return NT_STATUS_INVALID_DOMAIN_ROLE; + case ROLE_DOMAIN_MEMBER: + task_server_terminate(task, + "ft_scanner: no forest trust scanning " + "required in domain member configuration", + false); + return NT_STATUS_INVALID_DOMAIN_ROLE; + case ROLE_ACTIVE_DIRECTORY_DC: + /* Yes, we want forest trust scanning */ + break; + } + + task_server_set_title(task, "task[ft_scanner]"); + + service = talloc_zero(task, struct ft_scanner_service); + if (!service) { + task_server_terminate(task, "ft_scanner_task_init: out of memory", true); + return NT_STATUS_NO_MEMORY; + } + service->task = task; + service->startup_time = timeval_current(); + task->private_data = service; + + status = ft_scanner_connect_samdb(service); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, talloc_asprintf(task, + "ft_scanner: Failed to connect to local samdb: %s\n", + nt_errstr(status)), true); + return status; + } + + ret = samdb_rodc(service->l_samdb, &am_rodc); + if (ret != LDB_SUCCESS) { + status = NT_STATUS_LDAP(ret); + task_server_terminate(task, talloc_asprintf(task, + "ft_scanner: Failed to get rodc state: %s\n", + nt_errstr(status)), true); + return status; + } + + if (am_rodc) { + task_server_terminate(task, + "ft_scanner: no forest trust scanning " + "required on RODC configuration", + false); + return NT_STATUS_INVALID_DOMAIN_ROLE; + } + + periodic_startup_interval = lpcfg_parm_int(task->lp_ctx, + NULL, + "ft_scanner", + "periodic_startup_interval", + 15); /* in seconds */ + service->periodic.interval = lpcfg_parm_int(task->lp_ctx, + NULL, + "ft_scanner", + "periodic_interval", + 900); /* in seconds */ + service->periodic.interval = MAX(service->periodic.interval, 60); + + status = ft_scanner_periodic_schedule(service, periodic_startup_interval); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, talloc_asprintf(task, + "ft_scanner: Failed to periodic schedule: %s\n", + nt_errstr(status)), true); + return status; + } + + irpc_add_name(task->msg_ctx, "ft_scanner"); + + return NT_STATUS_OK; +} + +/* + register ourselves as a available server +*/ +NTSTATUS server_service_ft_scanner_init(TALLOC_CTX *ctx) +{ + static const struct service_details details = { + .inhibit_fork_on_accept = true, + .inhibit_pre_fork = true, + .task_init = ft_scanner_task_init, + .post_fork = NULL, + }; + return register_server_service(ctx, "ft_scanner", &details); +} diff --git a/source4/dsdb/ft_scanner/ft_scanner_service.h b/source4/dsdb/ft_scanner/ft_scanner_service.h new file mode 100644 index 00000000000..472d5b78554 --- /dev/null +++ b/source4/dsdb/ft_scanner/ft_scanner_service.h @@ -0,0 +1,57 @@ +/* + Unix SMB/CIFS Implementation. + forest trust scanner service + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +#ifndef _DSDB_FT_SCANNER_FT_SCANNER_SERVICE_H_ +#define _DSDB_FT_SCANNER_FT_SCANNER_SERVICE_H_ + +struct ft_scanner_service { + /* the whole ft_scanner service is in one task */ + struct task_server *task; + + /* the time the service was started */ + struct timeval startup_time; + + /* + * a connection to the local samdb + */ + struct ldb_context *l_samdb; + + /* some stuff for periodic processing */ + struct { + /* + * the interval between to periodic runs + */ + uint32_t interval; + + /* + * the timestamp for the next event, + * this is the timestamp passed to event_add_timed() + */ + struct timeval next_event; + + /* + * here we have a reference to the timed event the + * schedules the periodic stuff + */ + struct tevent_timer *te; + } periodic; +}; + +#endif /* _DSDB_FT_SCANNER_FT_SCANNER_SERVICE_H_ */ diff --git a/source4/dsdb/ft_scanner/ft_scanner_tdos.c b/source4/dsdb/ft_scanner/ft_scanner_tdos.c new file mode 100644 index 00000000000..80bb1725db1 --- /dev/null +++ b/source4/dsdb/ft_scanner/ft_scanner_tdos.c @@ -0,0 +1,1329 @@ +/* + Unix SMB/CIFS Implementation. + forest trust scanner service + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +#include "includes.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/common/util.h" +#include "samba/service.h" +#include "lib/param/param.h" +#include "libcli/resolve/resolve.h" +#include "libcli/finddc.h" +#include "librpc/gen_ndr/ads.h" +#include "auth/gensec/gensec.h" +#include "libcli/security/dom_sid.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "lib/tsocket/tsocket.h" +#include "lib/tls/tls.h" +#include "../source3/include/tldap.h" +#include "../source3/include/tldap_util.h" +#include "../source3/lib/tldap_tls_connect.h" +#include "../source3/lib/tldap_gensec_bind.h" +#include "dsdb/ft_scanner/ft_scanner_service.h" +#include "dsdb/ft_scanner/ft_scanner_service_proto.h" +#include "lib/util/tevent_ntstatus.h" +#include + +struct ft_scanner_scann_forest_state { + struct tevent_context *ev; + struct ft_scanner_service *service; + const struct ldb_message *msg; + const struct lsa_TrustDomainInfoInfoEx *tdo; + struct resolve_context *resolve_ctx; + struct finddcs finddcs_io; + const char *dc_dns_name; + const char *target_principal; + unsigned tcp_port; + bool use_tls; + bool use_starttls; + uint32_t gensec_features; + struct tsocket_address *local_addr; + struct tsocket_address *ldap_addr; + struct tldap_context *ld; + const char *partitions_dn; + struct ForestTrustDataDomainInfo *domains; +}; + +static void ft_scanner_scann_forest_found_dc(struct tevent_req *subreq); +static void ft_scanner_scann_forest_tcp_connected(struct tevent_req *subreq); +static void ft_scanner_scann_forest_starttls(struct tevent_req *req); +static void ft_scanner_scann_forest_starttls_done(struct tevent_req *subreq); +static void ft_scanner_scann_forest_tls_connect(struct tevent_req *req); +static void ft_scanner_scann_forest_tls_connected(struct tevent_req *subreq); +static void ft_scanner_scann_forest_gensec_bind(struct tevent_req *req); +static void ft_scanner_scann_forest_gensec_bound(struct tevent_req *subreq); +static void ft_scanner_scann_forest_config_dn_search(struct tevent_req *req); +static void ft_scanner_scann_forest_config_dn_done(struct tevent_req *subreq); +static void ft_scanner_scann_forest_partition_dn_search(struct tevent_req *req); +static void ft_scanner_scann_forest_partition_dn_done(struct tevent_req *subreq); +static void ft_scanner_scann_forest_partitions_search(struct tevent_req *req); +static void ft_scanner_scann_forest_partitions_done(struct tevent_req *subreq); + +static struct tevent_req *ft_scanner_scann_forest_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ft_scanner_service *service, + const struct ldb_message *msg, + const struct lsa_TrustDomainInfoInfoEx *tdo) +{ + struct loadparm_context *lp_ctx = service->task->lp_ctx; + struct tevent_req *req = NULL; + struct ft_scanner_scann_forest_state *state = NULL; + struct tevent_req *subreq = NULL; + int wrap_flags = -1; + + req = tevent_req_create(mem_ctx, &state, + struct ft_scanner_scann_forest_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->service = service; + state->msg = msg; + state->tdo = tdo; + + wrap_flags = lpcfg_client_ldap_sasl_wrapping(service->task->lp_ctx); + + state->tcp_port = 389; + if (wrap_flags & ADS_AUTH_SASL_LDAPS) { + state->use_tls = true; + state->tcp_port = 636; + } else if (wrap_flags & ADS_AUTH_SASL_STARTTLS) { + state->use_tls = true; + state->use_starttls = true; + } else { + state->gensec_features |= GENSEC_FEATURE_LDAP_STYLE; + state->gensec_features |= GENSEC_FEATURE_SIGN; + if (wrap_flags & ADS_AUTH_SASL_SEAL) { + state->gensec_features |= GENSEC_FEATURE_SEAL; + } + } + + state->resolve_ctx = lpcfg_resolve_context(lp_ctx); + + state->finddcs_io.in.domain_name = tdo->domain_name.string; + state->finddcs_io.in.minimum_dc_flags = NBT_SERVER_LDAP | + NBT_SERVER_DS | + NBT_SERVER_GC; + state->finddcs_io.in.proto = lpcfg_client_netlogon_ping_protocol(lp_ctx); + + subreq = finddcs_cldap_send(state, + &state->finddcs_io, + state->resolve_ctx, + state->ev); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_found_dc, + req); + + return req; +} + +static void ft_scanner_scann_forest_found_dc(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + NTSTATUS status; + const char *dupper = NULL; + int ret; + + status = finddcs_cldap_recv(subreq, state, &state->finddcs_io); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->finddcs_io.out.netlogon == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (state->finddcs_io.out.netlogon->data.nt5_ex.pdc_dns_name == NULL) { + tevent_req_nterror(req, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); + return; + } + state->dc_dns_name = state->finddcs_io.out.netlogon->data.nt5_ex.pdc_dns_name; + + dupper = talloc_strdup_upper(state, state->tdo->domain_name.string); + if (tevent_req_nomem(dupper, req)) { + return; + } + + state->target_principal = talloc_asprintf(state, + "ldap/%s/%s@%s", + state->dc_dns_name, + state->tdo->domain_name.string, + dupper); + if (tevent_req_nomem(state->target_principal, req)) { + return; + } + + /* parse the address of explicit kdc */ + ret = tsocket_address_inet_from_strings(state, + "ip", + state->finddcs_io.out.address, + state->tcp_port, + &state->ldap_addr); + if (ret != 0) { + status = map_nt_error_from_unix_common(errno); + tevent_req_nterror(req, status); + return; + } + + /* get an address for us to use locally */ + ret = tsocket_address_inet_from_strings(state, + "ip", + NULL, + 0, + &state->local_addr); + if (ret != 0) { + status = map_nt_error_from_unix_common(errno); + tevent_req_nterror(req, status); + return; + } + + subreq = tstream_inet_tcp_connect_send(state, + state->ev, + state->local_addr, + state->ldap_addr); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_tcp_connected, + req); +} + +static void PRINTF_ATTRIBUTE(3, 0) ft_scanner_scann_forest_tldap_debug( + void *log_private, + enum tldap_debug_level level, + const char *fmt, + va_list ap) +{ + int samba_level = -1; + + switch (level) { + case TLDAP_DEBUG_FATAL: + samba_level = DBGLVL_ERR; + break; + case TLDAP_DEBUG_ERROR: + samba_level = DBGLVL_ERR; + break; + case TLDAP_DEBUG_WARNING: + samba_level = DBGLVL_WARNING; + break; + case TLDAP_DEBUG_TRACE: + samba_level = DBGLVL_DEBUG; + break; + } + + if (CHECK_DEBUGLVL(samba_level)) { + char *s = NULL; + int ret; + + ret = vasprintf(&s, fmt, ap); + if (ret == -1) { + return; + } + DEBUG(samba_level, ("ft_scanner_scann_forest_tldap: %s", s)); + free(s); + } +} + +static void ft_scanner_scann_forest_tcp_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct tstream_context *plain_stream = NULL; + int ret, sys_errno; + + ret = tstream_inet_tcp_connect_recv(subreq, + &sys_errno, + state, + &plain_stream, + NULL); + TALLOC_FREE(subreq); + if (ret != 0) { + NTSTATUS status = map_nt_error_from_unix_common(sys_errno); + tevent_req_nterror(req, status); + return; + } + + state->ld = tldap_context_create_from_plain_stream(state, + &plain_stream); + if (tevent_req_nomem(state->ld, req)) { + return; + } + + tldap_set_debug(state->ld, ft_scanner_scann_forest_tldap_debug, NULL); + + if (state->use_tls && state->use_starttls) { + ft_scanner_scann_forest_starttls(req); + return; + } + + if (state->use_tls) { + ft_scanner_scann_forest_tls_connect(req); + return; + } + + ft_scanner_scann_forest_gensec_bind(req); + return; +} + +static void ft_scanner_scann_forest_starttls(struct tevent_req *req) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct tevent_req *subreq = NULL; + + subreq = tldap_extended_send(state, + state->ev, + state->ld, + LDB_EXTENDED_START_TLS_OID, + NULL, /* in_blob */ + NULL, /* sctrls */ + 0, /* num_sctrls */ + NULL, /* cctrls */ + 0); /* num_cctrls */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_starttls_done, + req); +} + +static void ft_scanner_scann_forest_starttls_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + TLDAPRC rc; + + rc = tldap_extended_recv(subreq, + NULL, /* mem_ctx */ + NULL, /* out_oid */ + NULL); /* out_blob */ + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_extended(%s) failed: %s\n", + LDB_EXTENDED_START_TLS_OID, + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + ft_scanner_scann_forest_tls_connect(req); +} + +static void ft_scanner_scann_forest_tls_connect(struct tevent_req *req) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct ft_scanner_service *service = state->service; + struct tstream_tls_params *tls_params = NULL; + struct tevent_req *subreq = NULL; + NTSTATUS status; + + status = tstream_tls_params_client_lpcfg(state, + service->task->lp_ctx, + state->dc_dns_name, + &tls_params); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("tstream_tls_params_client_lpcfg(%s) failed: %s\n", + state->dc_dns_name, nt_errstr(status)); + tevent_req_nterror(req, status); + return; + } + + subreq = tldap_tls_connect_send(state, + state->ev, + state->ld, + tls_params); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_tls_connected, + req); +} + +static void ft_scanner_scann_forest_tls_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + TLDAPRC rc; + + rc = tldap_tls_connect_recv(subreq); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_tls_connect(%s) failed: %s\n", + state->dc_dns_name, + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + ft_scanner_scann_forest_gensec_bind(req); +} + +static void ft_scanner_scann_forest_gensec_bind(struct tevent_req *req) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct ft_scanner_service *service = state->service; + struct auth_session_info *system_session_info = NULL; + struct tevent_req *subreq = NULL; + + system_session_info = system_session(service->task->lp_ctx); + if (system_session_info == NULL) { + tevent_req_nterror(req, NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + return; + } + + subreq = tldap_gensec_bind_send(state, + state->ev, + state->ld, + system_session_info->credentials, + "ldap", + state->dc_dns_name, + state->target_principal, + service->task->lp_ctx, + state->gensec_features); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_gensec_bound, + req); +} + +static void ft_scanner_scann_forest_gensec_bound(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + TLDAPRC rc; + + rc = tldap_gensec_bind_recv(subreq); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_gensec_bind(%s) failed: %s\n", + state->dc_dns_name, + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + ft_scanner_scann_forest_config_dn_search(req); +} + +static void ft_scanner_scann_forest_config_dn_search(struct tevent_req *req) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + static const char * const attrs[] = { "configurationNamingContext" }; + struct tevent_req *subreq = NULL; + + /* + * Do a rootdse search for configurationNamingContext + */ + subreq = tldap_search_all_send(state, + state->ev, + state->ld, + "", + TLDAP_SCOPE_BASE, + "(objectclass=*)", + attrs, + ARRAY_SIZE(attrs), + 0, /* attrsonly */ + NULL, /* sctrls */ + 0, /* num_sctrls */ + NULL, /* cctrls */ + 0, /* num_cctrls */ + 0, /* timelimit */ + 0, /* sizelimit */ + 0); /* deref */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_config_dn_done, + req); +} + +static void ft_scanner_scann_forest_config_dn_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct tldap_message **msgs = NULL; + size_t num_msgs; + struct tldap_message *res = NULL; + const char *config_dn = NULL; + TLDAPRC rc; + + rc = tldap_search_all_recv(subreq, state, &msgs, &res); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_search_all() failed: %s\n", + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + rc = tldap_msg_rc(res); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_search_all() res failed: %s\n", + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + num_msgs = talloc_array_length(msgs); + if (num_msgs != 1) { + NTSTATUS status = NT_STATUS_INVALID_NETWORK_RESPONSE; + DBG_NOTICE("tldap_search_all() num_msgs=%zu: %s\n", + num_msgs, nt_errstr(status)); + tevent_req_nterror(req, status); + return; + } + + config_dn = tldap_talloc_single_attribute(msgs[0], + "configurationNamingContext", + state); + if (tevent_req_nomem(config_dn, req)) { + return; + } + + state->partitions_dn = talloc_asprintf(state, + "CN=Partitions,%s", + config_dn); + if (tevent_req_nomem(state->partitions_dn, req)) { + return; + } + + ft_scanner_scann_forest_partition_dn_search(req); +} + +static void ft_scanner_scann_forest_partition_dn_search(struct tevent_req *req) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + static const char * const attrs[] = { "msDS-Behavior-Version" }; + struct tevent_req *subreq = NULL; + + /* + * Do a search CN=Partitions,CN=Configuration,... + * Filter: (objectClass=crossRefContainer) + * Attrs: msDS-Behavior-Version + */ + subreq = tldap_search_all_send(state, + state->ev, + state->ld, + state->partitions_dn, + TLDAP_SCOPE_BASE, + "(objectclass=crossRefContainer)", + attrs, + ARRAY_SIZE(attrs), + 0, /* attrsonly */ + NULL, /* sctrls */ + 0, /* num_sctrls */ + NULL, /* cctrls */ + 0, /* num_cctrls */ + 0, /* timelimit */ + 0, /* sizelimit */ + 0); /* deref */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_partition_dn_done, + req); +} + +static void ft_scanner_scann_forest_partition_dn_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct tldap_message **msgs = NULL; + size_t num_msgs; + struct tldap_message *res = NULL; + TLDAPRC rc; + + rc = tldap_search_all_recv(subreq, state, &msgs, &res); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_search_all() failed: %s\n", + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + rc = tldap_msg_rc(res); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_search_all() res failed: %s\n", + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + num_msgs = talloc_array_length(msgs); + if (num_msgs != 1) { + NTSTATUS status = NT_STATUS_INVALID_NETWORK_RESPONSE; + DBG_NOTICE("tldap_search_all() num_msgs=%zu: %s\n", + num_msgs, nt_errstr(status)); + tevent_req_nterror(req, status); + return; + } + + /* + * TODO: does Windows check the msDS-Behavior-Version value ? + * + * Currently I don't see a reason to check it... + */ + + ft_scanner_scann_forest_partitions_search(req); +} + +static void ft_scanner_scann_forest_partitions_search(struct tevent_req *req) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + static const char * const attrs[] = { + "dnsRoot", + "msDS-DnsRootAlias", + "nETBIOSName", + "systemFlags", + "msDS-Behavior-Version", + "trustParent", + }; + struct tevent_req *subreq = NULL; + const char *filter = NULL; + + /* + * Do a search CN=Partitions,CN=Configuration,... + * Filter: + * (&(objectClass=crossRef)(systemFlags:1.2.840.113556.1.4.803:=3)) + * Attrs: + * dnsRoot + * msDS-DnsRootAlias + * nETBIOSName + * systemFlags + * msDS-Behavior-Version + * trustParent + */ + + filter = talloc_asprintf(state, + "(&(objectClass=crossRef)(systemFlags:%s:=%u))", + LDB_OID_COMPARATOR_AND, + SYSTEM_FLAG_CR_NTDS_NC| + SYSTEM_FLAG_CR_NTDS_DOMAIN); + if (tevent_req_nomem(filter, req)) { + return; + } + + subreq = tldap_search_all_send(state, + state->ev, + state->ld, + state->partitions_dn, + TLDAP_SCOPE_ONE, + filter, + attrs, + ARRAY_SIZE(attrs), + 0, /* attrsonly */ + NULL, /* sctrls */ + 0, /* num_sctrls */ + NULL, /* cctrls */ + 0, /* num_cctrls */ + 0, /* timelimit */ + 0, /* sizelimit */ + 0); /* deref */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + ft_scanner_scann_forest_partitions_done, + req); +} + +static void ft_scanner_scann_forest_partitions_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + struct tldap_message **msgs = NULL; + size_t num_msgs; + struct tldap_message *res = NULL; + size_t i; + TLDAPRC rc; + + rc = tldap_search_all_recv(subreq, state, &msgs, &res); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_search_all() failed: %s\n", + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + rc = tldap_msg_rc(res); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + NTSTATUS status = NT_STATUS_LDAP(TLDAP_RC_V(rc)); + DBG_ERR("tldap_search_all() res failed: %s\n", + tldap_errstr(state, state->ld, rc)); + tevent_req_nterror(req, status); + return; + } + + /* + * At least the forest root should be there! + */ + num_msgs = talloc_array_length(msgs); + if (num_msgs == 0) { + NTSTATUS status = NT_STATUS_INVALID_NETWORK_RESPONSE; + DBG_NOTICE("tldap_search_all() num_msgs=%zu: %s\n", + num_msgs, nt_errstr(status)); + tevent_req_nterror(req, status); + return; + } + + state->domains = talloc_zero_array(state, + struct ForestTrustDataDomainInfo, + num_msgs); + if (tevent_req_nomem(state->domains, req)) { + return; + } + + for (i = 0; i < num_msgs; i++) { + struct tldap_message *m = msgs[i]; + struct ForestTrustDataDomainInfo *d = &state->domains[i]; + + d->dns_name.string = tldap_talloc_single_attribute(m, + "dnsRoot", + state->domains); + if (tevent_req_nomem(d->dns_name.string, req)) { + return; + } + + d->netbios_name.string = tldap_talloc_single_attribute(m, + "nETBIOSName", + state->domains); + if (tevent_req_nomem(d->netbios_name.string, req)) { + return; + } + } + + /* + * disconnect + */ + TALLOC_FREE(state->ld); + + tevent_req_done(req); +} + +static NTSTATUS ft_scanner_scann_forest_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ForestTrustDataDomainInfo **_domains) +{ + struct ft_scanner_scann_forest_state *state = + tevent_req_data(req, + struct ft_scanner_scann_forest_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + *_domains = talloc_move(mem_ctx, &state->domains); + tevent_req_received(req); + return NT_STATUS_OK; +} + +struct ft_scanner_check_trusts_state; + +struct ft_scanner_check_trusts_domain { + struct ft_scanner_check_trusts_state *state; + + struct GUID tdo_guid; + struct lsa_TrustDomainInfoInfoEx *tdo; + struct ldb_message *msg; +}; + +struct ft_scanner_check_trusts_state { + struct ft_scanner_service *service; + + struct ft_scanner_check_trusts_domain *domains; +}; + +static void ft_scanner_check_trusts_scanned(struct tevent_req *subreq); + +NTSTATUS ft_scanner_check_trusts(struct ft_scanner_service *service) +{ + static const char * const trust_attrs[] = { + "objectGUID", + "securityIdentifier", + "flatName", + "trustPartner", + "trustType", + "trustAttributes", + "trustDirection", + "msDS-TrustForestTrustInfo", + NULL + }; + struct ldb_result *trusts_res = NULL; + unsigned int i; + NTSTATUS status; + struct ft_scanner_check_trusts_state *state = NULL; + size_t num_ft = 0; + uint32_t timeout_secs; + struct timeval endtime; + + timeout_secs = service->periodic.interval; + if (timeout_secs > 75) { + timeout_secs -= 15; + } + + endtime = timeval_current_ofs(timeout_secs, 0); + + state = talloc_zero(service, struct ft_scanner_check_trusts_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + state->service = service; + + /* fetch all trusted domain objects */ + status = dsdb_trust_search_tdos(service->l_samdb, + NULL, + trust_attrs, + state, + &trusts_res); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("dsdb_trust_search_tdos() failed %s\n", + nt_errstr(status)); + TALLOC_FREE(state); + return status; + } + + if (trusts_res->count == 0) { + /* + * No trusts => nothing to do... + */ + DBG_DEBUG("dsdb_trust_search_tdos() gave 0 results\n"); + TALLOC_FREE(state); + return NT_STATUS_OK; + } + + state->domains = talloc_zero_array(state, + struct ft_scanner_check_trusts_domain, + trusts_res->count); + if (state->domains == NULL) { + /* + * We ignore errors and + * retry later + */ + TALLOC_FREE(state); + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; i < trusts_res->count; i++) { + struct ft_scanner_check_trusts_domain *d = + &state->domains[i]; + struct tevent_req *subreq = NULL; + bool ok; + + d->msg = trusts_res->msgs[i]; + + d->tdo_guid = samdb_result_guid(d->msg, "objectGUID"); + + status = dsdb_trust_parse_tdo_info(state->domains, + d->msg, + &d->tdo); + if (!NT_STATUS_IS_OK(status)) { + /* + * We ignore error and continue with the + * next domain + * + * In the hope it will work better later. + */ + d->tdo = NULL; + continue; + } + + if (!(d->tdo->trust_direction & LSA_TRUST_DIRECTION_INBOUND)) { + /* + * Only inbound trusts should be scanned + */ + TALLOC_FREE(d->tdo); + continue; + } + + if (!(d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) { + /* + * Only forest trusts should be scanned + */ + TALLOC_FREE(d->tdo); + continue; + } + + subreq = ft_scanner_scann_forest_send(state, + service->task->event_ctx, + service, + d->msg, + d->tdo); + if (subreq == NULL) { + /* + * Ignore errors... + * + * We'll retry later + */ + TALLOC_FREE(d->tdo); + continue; + } + tevent_req_set_callback(subreq, + ft_scanner_check_trusts_scanned, + d); + + ok = tevent_req_set_endtime(subreq, + service->task->event_ctx, + endtime); + if (!ok) { + /* + * Ignore errors... + * + * We'll retry later + */ + TALLOC_FREE(subreq); + TALLOC_FREE(d->tdo); + continue; + } + + d->state = state; + + num_ft += 1; + } + + if (num_ft == 0) { + /* + * No inbound forest trusts => nothing to do... + */ + DBG_DEBUG("no forest trusts found\n"); + TALLOC_FREE(state); + return NT_STATUS_OK; + } + + /* + * Keep state for the callbacks + */ + DBG_DEBUG("Waiting for %zu forest trusts\n", num_ft); + return NT_STATUS_OK; +} + +static bool ft_scanner_check_trusts_di_equal(const struct ForestTrustDataDomainInfo *s1, + const struct ForestTrustDataDomainInfo *s2) +{ + if (!dom_sid_equal(&s1->sid, &s2->sid)) { + return false; + } + + if (!strequal(s1->dns_name.string, s2->dns_name.string)) { + return false; + } + + if (!strequal(s1->netbios_name.string, s2->netbios_name.string)) { + return false; + } + + return true; +} + +static void ft_scanner_check_trusts_scanned(struct tevent_req *subreq) +{ + struct ft_scanner_check_trusts_domain *d = + (struct ft_scanner_check_trusts_domain *) + tevent_req_callback_data_void(subreq); + struct ft_scanner_check_trusts_state *state = + talloc_get_type_abort(d->state, + struct ft_scanner_check_trusts_state); + struct ft_scanner_service *service = state->service; + static const char * const trust_attrs[] = { + "objectGUID", + "securityIdentifier", + "flatName", + "trustPartner", + "trustType", + "trustAttributes", + "trustDirection", + "msDS-TrustForestTrustInfo", + NULL + }; + struct ForestTrustDataDomainInfo *sdis = NULL; + size_t num_sdis; + size_t si; + struct ldb_result *res = NULL; + struct lsa_TrustDomainInfoInfoEx *tdo = NULL; + struct ForestTrustInfo *fti = NULL; + size_t new_count; + size_t ri; + bool modified = false; + DATA_BLOB ft_blob = {}; + enum ndr_err_code ndr_err; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + struct ldb_message *mod_msg = NULL; + size_t num_fts; + size_t num_pending; + NTSTATUS status; + size_t fi; + int ret; + + d->state = NULL; + + status = ft_scanner_scann_forest_recv(subreq, + state->domains, + &sdis); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DBG_NOTICE("Forest[%s][%s] scann failed: %s\n", + d->tdo->domain_name.string, + d->tdo->netbios_name.string, + nt_errstr(status)); + goto cleanup_state; + } + + num_sdis = talloc_array_length(sdis); + DBG_DEBUG("Forest[%s][%s] num_domains[%zu]\n", + d->tdo->domain_name.string, + d->tdo->netbios_name.string, + num_sdis); + for (si = 0; si < num_sdis; si++) { + DBG_DEBUG("domain[%zu][%s][%s]\n", + si, + sdis[si].dns_name.string, + sdis[si].netbios_name.string); + } + + ret = ldb_transaction_start(service->l_samdb); + if (ret != LDB_SUCCESS) { + goto cleanup_state; + } + + ret = dsdb_search_by_dn_guid(service->l_samdb, + state->domains, + &res, + &d->tdo_guid, + trust_attrs, + DSDB_SEARCH_ONE_ONLY); + if (ret != LDB_SUCCESS) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + status = dsdb_trust_parse_tdo_info(state->domains, + res->msgs[0], + &tdo); + if (!NT_STATUS_IS_OK(status)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + if (!dom_sid_equal(d->tdo->sid, tdo->sid)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + if (!strequal(d->tdo->domain_name.string, tdo->domain_name.string)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + if (!strequal(d->tdo->netbios_name.string, tdo->netbios_name.string)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + if (!(tdo->trust_direction & LSA_TRUST_DIRECTION_INBOUND)) { + /* + * Only inbound trusts should be scanned + */ + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + if (!(tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) { + /* + * Only forest trusts should be scanned + */ + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + status = dsdb_trust_parse_forest_info(state->domains, + res->msgs[0], + &fti); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + /* + * If there's no msDS-TrustForestTrustInfo + * create the defeault one + */ + status = dsdb_trust_default_forest_info(state->domains, + tdo->sid, + tdo->domain_name.string, + tdo->netbios_name.string, + now, + &fti); + if (!NT_STATUS_IS_OK(status)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + } + if (!NT_STATUS_IS_OK(status)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + TALLOC_FREE(tdo); + if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(ForestTrustInfo, fti); + } + + /* + * First remove stale scanner info records + */ + new_count = 0; + for (ri = 0; ri < fti->count; ri++) { + struct ForestTrustInfoRecord *dst = &fti->records[new_count].record; + const struct ForestTrustInfoRecord *r = &fti->records[ri].record; + const struct ForestTrustDataDomainInfo *es = NULL; + bool keep = false; + + if (r->type != FOREST_TRUST_SCANNER_INFO) { + if (dst != r) { + *dst = *r; + } + new_count += 1; + continue; + } + + es = &r->data.scanner_info.info; + + for (si = 0; si < num_sdis; si++) { + const struct ForestTrustDataDomainInfo *cs = &sdis[si]; + bool match; + + match = ft_scanner_check_trusts_di_equal(es, cs); + if (!match) { + continue; + } + + keep = true; + break; + } + + if (keep) { + if (dst != r) { + *dst = *r; + } + new_count += 1; + continue; + } + + modified = true; + } + fti->count = new_count; + + /* + * Now add the missing scanner infos + */ + for (si = 0; si < num_sdis; si++) { + struct ForestTrustDataDomainInfo *cs = &sdis[si]; + struct ForestTrustInfoRecord *dst = NULL; + bool found = false; + + for (ri = 0; ri < fti->count; ri++) { + const struct ForestTrustInfoRecord *r = &fti->records[ri].record; + const struct ForestTrustDataDomainInfo *es = NULL; + bool match; + + if (r->type != FOREST_TRUST_SCANNER_INFO) { + continue; + } + + es = &r->data.scanner_info.info; + + match = ft_scanner_check_trusts_di_equal(es, cs); + if (!match) { + continue; + } + + found = true; + break; + } + + if (found) { + continue; + } + + fti->records = talloc_realloc(fti, + fti->records, + struct ForestTrustInfoRecordArmor, + fti->count + 1); + if (fti->records == NULL) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + dst = &fti->records[fti->count].record; + + dst->flags = 0; + dst->timestamp = now; + dst->type = FOREST_TRUST_SCANNER_INFO; + dst->data.scanner_info.sub_type = FOREST_TRUST_SCANNER_INFO; + dst->data.scanner_info.info = *cs; + + fti->count += 1; + modified = true; + } + + if (!modified) { + DBG_DEBUG("Forest[%s][%s] no updates\n", + d->tdo->domain_name.string, + d->tdo->netbios_name.string); + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(ForestTrustInfo, fti); + } + + ndr_err = ndr_push_struct_blob(&ft_blob, state->domains, fti, + (ndr_push_flags_fn_t)ndr_push_ForestTrustInfo); + TALLOC_FREE(fti); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + mod_msg = ldb_msg_new(state->domains); + if (mod_msg == NULL) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + mod_msg->dn = ldb_dn_copy(state->domains, res->msgs[0]->dn); + if (mod_msg->dn == NULL) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + ret = ldb_msg_append_value(mod_msg, + "msDS-TrustForestTrustInfo", + &ft_blob, + LDB_FLAG_MOD_REPLACE); + if (ret != LDB_SUCCESS) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + ret = dsdb_modify(service->l_samdb, mod_msg, 0); + if (ret != LDB_SUCCESS) { + ldb_transaction_cancel(service->l_samdb); + goto cleanup_state; + } + + ret = ldb_transaction_commit(service->l_samdb); + if (ret != LDB_SUCCESS) { + goto cleanup_state; + } + + DBG_DEBUG("Forest[%s][%s] updated\n", + d->tdo->domain_name.string, + d->tdo->netbios_name.string); + +cleanup_state: + d = NULL; + num_fts = 0; + num_pending = 0; + for (fi = 0; fi < talloc_array_length(state->domains); fi++) { + if (state->domains[fi].tdo != NULL) { + num_fts += 1; + } + + if (state->domains[fi].state != NULL) { + num_pending += 1; + } + } + + if (num_pending != 0) { + /* still pending */ + DBG_DEBUG("Pending %zu for %zu forest trusts in %zu domains\n", + num_pending, num_fts, + talloc_array_length(state->domains)); + return; + } + + DBG_DEBUG("Done for %zu forest trusts in %zu domains\n", + num_fts, talloc_array_length(state->domains)); + talloc_free(state); +} diff --git a/source4/dsdb/wscript_build b/source4/dsdb/wscript_build index 196cf66dfab..1b03dc9f077 100644 --- a/source4/dsdb/wscript_build +++ b/source4/dsdb/wscript_build @@ -69,6 +69,20 @@ bld.SAMBA_MODULE('service_dns_update', enabled=bld.AD_DC_BUILD_IS_ENABLED() ) +bld.SAMBA_MODULE('service_ft_scanner', + source=''' + ft_scanner/ft_scanner_service.c + ft_scanner/ft_scanner_periodic.c + ft_scanner/ft_scanner_tdos.c + ''', + autoproto='ft_scanner/ft_scanner_service_proto.h', + subsystem='service', + init_function='server_service_ft_scanner_init', + deps='samdb process_model', + internal_module=False, + enabled=bld.AD_DC_BUILD_IS_ENABLED() +) + pyldb_util = bld.pyembed_libname('pyldb-util') pyrpc_util = bld.pyembed_libname('pyrpc_util') pyparam_util = bld.pyembed_libname('pyparam_util')