--- /dev/null
+/*
+ Unix SMB/CIFS mplementation.
+ DFSR replication service
+
+ Copyright (C) Matthieu Patou <mat@matws.net> 2013-2014
+
+ 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 "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/events/events.h"
+#include <ldb_errors.h>
+#include <ldb_module.h>
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_frstrans.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_frstrans_c.h"
+#include "param/param.h"
+#include "dfsr/dfsr_service.h"
+#include "util/tevent_ntstatus.h"
+#include "util/dlinklist.h"
+#include "ldb_wrap.h"
+#include "libcli/composite/composite.h"
+
+#define ALLYOUCANEATLOG 11
+#define INFOLOG 1
+#define ERRORLOG 0
+#define WARNLOG 1
+#define DEBUGLOG 1
+
+const char NULLSHA1[20] = {0};
+static WERROR dfsrsrv_init_creds(struct dfsrsrv_service *service)
+{
+ service->system_session_info = system_session(service->task->lp_ctx);
+ if (service->system_session_info == NULL) {
+ return WERR_NOMEM;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dfsrsrv_connect_dbs(struct dfsrsrv_service *service, struct loadparm_context *lp_ctx)
+{
+ service->samdb = samdb_connect(service, service->task->event_ctx, lp_ctx,
+ service->system_session_info, 0);
+ if (!service->samdb) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ service->dfsrdb = ldb_wrap_connect(service, NULL, lp_ctx, "dfsr.ldb",
+ NULL, NULL, 0);
+ if (!service->dfsrdb) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ return WERR_OK;
+}
+
+static void dorun(struct dfsrsrv_service *service);
+
+WERROR dfsrsrv_periodic_schedule(struct dfsrsrv_service *service, uint32_t next_interval);
+
+/* Callback called when the timer expire for the DFSR service
+ * it will call dorun and then rearm a new time
+ */
+static void dfsrsrv_periodic_handler_te(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct dfsrsrv_service *service = talloc_get_type(ptr, struct dfsrsrv_service);
+ WERROR status;
+
+ service->periodic.te = NULL;
+
+ service->tevent_ctx = ev;
+
+ dorun(service);
+
+ status = dfsrsrv_periodic_schedule(service, service->periodic.interval);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(service->task, win_errstr(status), false);
+ return;
+ }
+}
+
+/* Schedule the DFSR service for a run in @next_interval delay */
+WERROR dfsrsrv_periodic_schedule(struct dfsrsrv_service *service, uint32_t next_interval)
+{
+ struct tevent_timer *new_te;
+ 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.
+ * This can happen if the next event is scheduled in 5 seconds,
+ * but we ask to schedule in 10 seconds.
+ */
+ if (timeval_compare(&next_time, &service->periodic.next_event) > 0) {
+ return WERR_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,
+ dfsrsrv_periodic_handler_te, service);
+ W_ERROR_HAVE_NO_MEMORY(new_te);
+
+ talloc_free(service->periodic.te);
+ service->periodic.te = new_te;
+
+ return WERR_OK;
+}
+
+/* For a given replicagroup specified by it's DN (@rep_group_dn)
+ * search for DFSR sets in it.
+ */
+static WERROR get_dfsr_sets_ingroup(struct dfsrsrv_service *service,
+ struct ldb_dn *rep_group_dn,
+ bool issysvol)
+{
+ int ret, i;
+ const char *attrs[] = { "name", "objectGUID", NULL };
+ struct ldb_result *res, *res2;
+ struct dfsrsrv_replica_group *group;
+
+ if (service->sysvol) {
+ /* Already exists */
+ return WERR_OK;
+ }
+ if (!issysvol) {
+ /*
+ * FIXME do also the other replica group one day
+ */
+ return WERR_NOT_SUPPORTED;
+ }
+ /*
+ * FIXME
+ * Check the DL > 2008
+ */
+
+ /* Search the specified msDFSR-ReplicationGroup in order to validate it*/
+ ret = ldb_search(service->samdb, service, &res, rep_group_dn,
+ LDB_SCOPE_BASE, attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ /*
+ * No entry or a problem ?
+ * Hum we might have this group
+ * TODO if we are alone, that's normal we just need to create
+ * the structure if it's sysvol.
+ */
+ DEBUG(ERRORLOG, ("Unable to find DFSR replica group for DN %s",
+ ldb_dn_get_linearized(rep_group_dn)));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ group = talloc_zero(service, struct dfsrsrv_replica_group);
+ if (issysvol) {
+ service->sysvol = group;
+ } else {
+ /* FIXME add the service to the list of replica */
+ return WERR_NOT_SUPPORTED;
+ }
+ /*
+ * Ok we've found the replica group, so now look for the replica sets
+ * in this group
+ */
+ group->is_sysvol = issysvol;
+ group->dn = talloc_steal(group, res->msgs[0]->dn);
+ group->name = talloc_strdup(group, ldb_msg_find_attr_as_string(res->msgs[0],
+ "name", NULL));
+ group->guid = samdb_result_guid(res->msgs[0], "objectGUID");
+
+ ret = ldb_search(service->samdb, service, &res2, rep_group_dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ "(objectClass=msDFSR-ContentSet)");
+ if (ret != LDB_SUCCESS || res2->count == 0) {
+ /*
+ * No entry or a problem ?
+ * Hum we might have this replica
+ * TODO if we are alone, that's normal we just need to create
+ * the structure if it's sysvol.
+ */
+ DEBUG(ERRORLOG, ("Unable to find DFSR contentSet for group %s",
+ ldb_dn_get_linearized(rep_group_dn)));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ group->set_names = talloc_array(group, char *, res2->count);
+ W_ERROR_HAVE_NO_MEMORY(group->set_names);
+
+ group->numsets = res2->count;
+
+ group->set_guids = talloc_array(group, struct GUID, res2->count);
+ W_ERROR_HAVE_NO_MEMORY(group->set_guids);
+
+ for (i = 0; i < res2->count; i++) {
+ group->dn = talloc_steal(group, res->msgs[0]->dn);
+ group->set_names[i] = talloc_strdup(group->set_names,
+ ldb_msg_find_attr_as_string(res->msgs[i],
+ "name", NULL));
+ group->set_guids[i] = samdb_result_guid(res2->msgs[i], "objectGUID");
+ }
+
+ service->groups = talloc_realloc(service, service->groups,
+ struct dfsrsrv_replica_group*,
+ service->numgroups + 1);
+ service->groups[service->numgroups] = group;
+ service->numgroups++;
+
+ group->me = talloc_zero(group, struct dfsrsrv_replica_member);
+
+ talloc_free(res);
+ talloc_free(res2);
+ return WERR_OK;
+}
+
+/* Subscribe current DC to the sysvol contentset, creating needed entries in the DB,
+ * the @me object is also populated so that it can be used for replication
+ */
+static int subscribe_sysvol_contentset(TALLOC_CTX *mem_ctx, struct dfsrsrv_service *service, struct dfsrsrv_replica_member *me)
+{
+ /*
+ * Ok we do not have registered ourself as
+ * a member of this dfsr replica, let's do it !
+ */
+ struct ldb_message *msg, *msg2;
+ struct ldb_dn *dn, *dn2;
+ struct ldb_result *res;
+ const char *attrs2[] = { "objectGUID", NULL };
+ const char *string_ntds_dn = ldb_dn_get_linearized(samdb_ntds_settings_dn(service->samdb, mem_ctx));
+ int ret = ldb_transaction_start(service->samdb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* CN=DFSR-LocalSettings,CN=<NETBIOS>,OU=Domain Controllers,<DOMAIN> */
+ msg = ldb_msg_new(mem_ctx);
+
+ if (msg == NULL) {
+ return ldb_operr(service->samdb);
+ }
+ dn = ldb_dn_new_fmt(msg, service->samdb, "CN=DFSR-LocalSettings,%s",
+ ldb_dn_get_linearized(service->my_account_dn));
+ DEBUG(0, ("DN = %s\n", ldb_dn_get_linearized(dn)));
+
+ if (dn == NULL) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ msg->dn = dn;
+ ret = ldb_msg_add_string(msg, "objectClass", "msDFSR-LocalSettings");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg, "msDFSR-Version", "1.0.0.0");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg, "msDFSR-Flags", "48");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_add(service->samdb, msg);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ return ret;
+ }
+ /*
+ * End
+ * CN=DFSR-LocalSettings,CN=<NETBIOS>,OU=Domain Controllers,<DOMAIN>
+ */
+
+ /* CN=<NETBIOS>,CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,<DOMAIN>*/
+ msg2 = ldb_msg_new(mem_ctx);
+
+ if (msg2 == NULL) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ dn2 = ldb_dn_new_fmt(msg2, service->samdb,
+ "CN=%s, CN=Topology, %s",
+ lpcfg_netbios_name(service->task->lp_ctx),
+ ldb_dn_get_linearized(service->sysvol->dn));
+
+ if (dn2 == NULL) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+ msg2->dn = talloc_steal(msg2, dn2);
+
+ ret = ldb_msg_add_string(msg2, "objectClass", "msDFSR-Member");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg2, "serverReference", string_ntds_dn);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg2, "msDFSR-ComputerReference",
+ ldb_dn_get_linearized(service->my_account_dn));
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_add(service->samdb, msg2);
+ /*
+ * END
+ * CN=<NETBIOS>,CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,<DOMAIN>
+ */
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ me->memberdn = talloc_steal(me, msg2->dn);
+
+ /* We will need this for the memberReference */
+ dn2 = msg2->dn;
+ talloc_free(msg2);
+
+ /* CN=Domain System Volume,CN=DFSR-LocalSettings,CN=<NETBIOS>,OU=Domain Controllers,<DOMAIN> */
+ msg2 = ldb_msg_new(mem_ctx);
+
+ if (msg2 == NULL) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_error(service->samdb, LDB_ERR_OPERATIONS_ERROR,
+ "Unable to allocate a ldb_msg object");
+ }
+
+ ldb_dn_add_child_fmt(dn, "CN=Domain System Volume");
+ msg2->dn = talloc_steal(msg2, dn);
+ if (msg2->dn == NULL) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_error(service->samdb, LDB_ERR_OPERATIONS_ERROR,
+ "Unable to allocate dn for DFSR");
+ }
+ /* dn is on the msg context */
+ talloc_free(msg);
+
+ ret = ldb_msg_add_string(msg2, "objectClass", "msDFSR-Subscriber");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = dsdb_msg_add_guid(msg2,
+ &service->sysvol->guid,
+ "msDFSR-ReplicationGroupGuid");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg2, "msDFSR-MemberReference",
+ ldb_dn_get_linearized(dn2));
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_add(service->samdb, msg2);
+ talloc_free(dn2);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ /*
+ * END
+ * CN=Domain System Volume,CN=DFSR-LocalSettings,CN=<NETBIOS>,OU=Domain Controllers,<DOMAIN>
+ */
+
+ /* CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=<NETBIOS>,OU=Domain Controllers,<DOMAIN>*/
+ msg = ldb_msg_new(mem_ctx);
+
+ if (msg == NULL) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_error(service->samdb, LDB_ERR_OPERATIONS_ERROR,
+ "Unable to allocate a ldb_msg object");
+ }
+
+ ldb_dn_add_child_fmt(dn, "CN=SYSVOL Subscription");
+ msg->dn = talloc_steal(msg, dn);
+ if (!msg->dn) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_error(service->samdb, LDB_ERR_OPERATIONS_ERROR,
+ "Unable to add NTdfsr Subscriptions object for sysvol");
+ }
+ /* dn is on the msg2 context */
+ talloc_free(msg2);
+
+ ret = ldb_msg_add_string(msg, "objectClass", "msDFSR-Subscription");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg, "msDFSR-RootPath", me->path);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg, "msDFSR-Enabled", "TRUE");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg, "msDFSR-Options", "0");
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = dsdb_msg_add_guid(msg,
+ &service->sysvol->set_guids[0],
+ "msDFSR-ContentSetGuid");
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = dsdb_msg_add_guid(msg,
+ &service->sysvol->guid,
+ "msDFSR-ReplicationGroupGuid");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_msg_add_string(msg, "msDFSR-ReadOnly", "FALSE");
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ldb_operr(service->samdb);
+ }
+
+ ret = ldb_add(service->samdb, msg);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ /*
+ * END
+ * CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=<NETBIOS>,OU=Domain Controllers,<DOMAIN>
+ */
+ ret = ldb_search(service->samdb, mem_ctx, &res, msg->dn,
+ LDB_SCOPE_BASE, attrs2,
+ "(objectClass=msDFSR-Subscription)");
+
+ DEBUG(0, ("DN = %s count = %d\n", ldb_dn_get_linearized(service->sysvol->dn), res->count));
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ ldb_transaction_cancel(service->samdb);
+ talloc_free(mem_ctx);
+ TALLOC_FREE(service->sysvol->me);
+ return ret;
+ }
+ DEBUG(0, ("Finished initializing our DFSR settings\n"));
+
+ me->guid = samdb_result_guid(res->msgs[0], "objectGUID");
+ me->dn = talloc_steal(me, res->msgs[0]->dn);
+ me->name = talloc_strdup(me, ldb_msg_find_attr_as_string(res->msgs[0], "cn", NULL));
+ talloc_free(mem_ctx);
+
+ service->sysvol->initizialized = true;
+ return ldb_transaction_commit(service->samdb);
+}
+
+static int register_or_get_sysvol_subscription(struct dfsrsrv_service *service)
+{
+ int ret;
+ TALLOC_CTX *mem_ctx = talloc_new(service);
+ struct ldb_result *res, *res2;
+ struct dfsrsrv_replica_member *me;
+ const char* sysvol_path;
+ const char *string_ntds_dn = ldb_dn_get_linearized(samdb_ntds_settings_dn(service->samdb, mem_ctx));
+ const char *attrs[] = { "objectGUID",
+ "msDFSR-MemberReferenceBL", NULL };
+ const char *attrs2[] = { "objectGUID", NULL };
+ struct loadparm_service *lp_sysvol;
+
+ ret = ldb_search(service->samdb, service, &res, service->sysvol->dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ "(objectClass=msDFSR-Member)");
+
+ DEBUG(ALLYOUCANEATLOG, (__FILE__": start\n"));
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ /* TODO if we are alone in the domain just take the job */
+ DEBUG(ERRORLOG, ("Unable to find any the dfsr member for group %s",
+ ldb_dn_get_linearized(service->sysvol->dn)));
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ lp_sysvol = lpcfg_service(service->task->lp_ctx , "sysvol");
+ sysvol_path = lpcfg_path(lp_sysvol, lpcfg_default_service(service->task->lp_ctx), mem_ctx);
+ if (sysvol_path == NULL) {
+ DEBUG(0, ("sysvol path is null\n"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = samdb_server_reference_dn(service->samdb, service, &service->my_account_dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_free(res);
+
+ /* Check if we have already subscribed to the SYSVOL DFSR ContentSet */
+ ret = ldb_search(service->samdb, service, &res, service->sysvol->dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectClass=msDFSR-Member)(serverReference=%s))",
+ string_ntds_dn);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ me = service->sysvol->me;
+ me->path = talloc_strdup(me, sysvol_path);
+
+ if (res->count == 1) {
+ /* We did */
+ struct ldb_dn *refbl = ldb_msg_find_attr_as_dn(service->samdb, mem_ctx,
+ res->msgs[0], "msDFSR-MemberReferenceBL");
+ if (refbl == NULL) {
+ talloc_free(mem_ctx);
+ TALLOC_FREE(service->sysvol->me);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ ret = ldb_search(service->samdb, mem_ctx, &res2, refbl,
+ LDB_SCOPE_ONELEVEL, attrs2,
+ "(objectClass=msDFSR-Subscription)");
+
+ if (ret != LDB_SUCCESS || res2->count == 0) {
+ talloc_free(mem_ctx);
+ TALLOC_FREE(service->sysvol->me);
+ return ret;
+ }
+
+ me->guid = samdb_result_guid(res2->msgs[0], "objectGUID");
+ me->dn = talloc_steal(me, res2->msgs[0]->dn);
+ me->name = talloc_strdup(me, ldb_msg_find_attr_as_string(res2->msgs[0], "cn", NULL));
+ /* to be checked */
+ me->memberdn = talloc_steal(me, res->msgs[0]->dn);
+
+ return LDB_SUCCESS;
+ } else if (res->count == 0) {
+ return subscribe_sysvol_contentset(mem_ctx, service, me);
+ } else {
+ char *txt = talloc_asprintf(service->samdb,
+ "There is more than 1 object with serverReference"
+ " ,bellow %s,pointing to our NTDS value"
+ " database is corrupted",
+ ldb_dn_get_linearized(service->sysvol->dn));
+ ldb_error(service->samdb, LDB_ERR_OPERATIONS_ERROR, txt);
+ }
+ /* Never reached */
+ return LDB_SUCCESS;
+}
+
+static bool init_sysvol_replicaset(struct dfsrsrv_service *service);
+
+
+static int populate_sysvol_cnxs(struct dfsrsrv_replica_member *member,
+ struct ldb_context *samdb)
+{
+ uint32_t old_num = member->num_cnx;
+ struct ldb_result *res;
+ TALLOC_CTX *mem_ctx = talloc_new(member);
+ const char* attrs[] = {"objectGUID", "fromServer", NULL};
+ const char* attrs2[] = {"dNSHostName", NULL};
+ struct ldb_dn *settings_dn = samdb_ntds_settings_dn(samdb, mem_ctx);
+ int i, j;
+ struct dfsrsrv_connection **cnxs;
+ int ret = ldb_search(samdb, mem_ctx, &res, settings_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(objectclass=nTDSConnection)");
+
+ DEBUG(ALLYOUCANEATLOG, ("%s called, # cnx = %d\n", __func__, member->num_cnx));
+ if (old_num != 0) {
+ /* FIXME we need to adapt to the change of number of replica in the group*/
+ return LDB_SUCCESS;
+ }
+ member->num_cnx = 0;
+
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ DEBUG(0, ("No NTDS connection for our server (%s)\n",
+ ldb_dn_get_linearized(settings_dn)));
+ talloc_free(mem_ctx);
+ return LDB_SUCCESS;
+ }
+
+ cnxs = talloc_zero_array(member, struct dfsrsrv_connection *, res->count);
+ for (i = 0; i < res->count; i++) {
+ struct GUID guid = samdb_result_guid(res->msgs[i], "objectGUID");
+ struct ldb_dn *from_server, *from_server_ntds;
+
+ for (j = 0; j < old_num; j++) {
+ if (GUID_equal(&member->cnxs[j]->guid, &guid)) {
+ cnxs[i] = talloc_move(cnxs, &member->cnxs[j]);
+ }
+ }
+ if (cnxs[i] != NULL) {
+ continue;
+ }
+
+ from_server_ntds = ldb_msg_find_attr_as_dn(samdb, member,
+ res->msgs[i],
+ "fromServer");
+ if (!from_server_ntds) {
+ DEBUG(0, ("Unable to find fromServer from %s\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ from_server = ldb_dn_get_parent(mem_ctx, from_server_ntds);
+ talloc_free(res);
+ ret = ldb_search(samdb, mem_ctx, &res, from_server,
+ LDB_SCOPE_BASE, attrs2,
+ "(&(objectclass=server)(dnsHostName=*))");
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ DEBUG(0, ("Unable to get the dnsHostname from %s\n",
+ ldb_dn_get_linearized(from_server)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ cnxs[i] = talloc_zero(member, struct dfsrsrv_connection);
+ cnxs[i]->guid = guid;
+ cnxs[i]->hostname = ldb_msg_find_attr_as_string(res->msgs[0],
+ "dNSHostName",
+ NULL);
+ talloc_steal(cnxs[i], cnxs[i]->hostname);
+ cnxs[i]->established = false;
+ }
+ if (member->cnxs) {
+ talloc_free(member->cnxs);
+ }
+ member->cnxs = cnxs;
+ member->num_cnx = res->count;
+ talloc_free(mem_ctx);
+ return LDB_SUCCESS;
+}
+static int populate_connections(struct dfsrsrv_replica_member *member,
+ struct ldb_context *samdb,
+ bool is_sysvol)
+{
+ if (is_sysvol) {
+ return populate_sysvol_cnxs(member, samdb);
+ }
+ return LDB_SUCCESS;
+}
+
+static void dfsrsrv_session_loop_start(struct dfsrsrv_service *service,
+ struct dfsrsrv_replica_group *group,
+ struct dfsrsrv_replica_member *member,
+ struct dfsrsrv_connection *conn);
+
+static void establish_connection(struct dfsrsrv_service *service,
+ struct dfsrsrv_replica_group *group,
+ struct dfsrsrv_replica_member *member,
+ struct dfsrsrv_connection *conn)
+{
+ enum frstrans_ProtocolVersion version;
+ uint32_t flags;
+ NTSTATUS status;
+ WERROR result;
+ struct dcerpc_pipe *p;
+ struct GUID_txt_buf tmp_buf;
+
+ DEBUG(INFOLOG, ("Trying to connect to %s (connection guid %s)\n",
+ conn->hostname,
+ GUID_string(conn, &conn->guid)));
+
+ /* Use print to print the NDR */
+ conn->binding_string = talloc_asprintf(conn,
+ "%s@ncacn_ip_tcp:%s[krb5,seal]",
+ "5bc1ed07-f5f5-485f-9dfd-6fd0acf9a23c",
+ conn->hostname);
+
+ status = dcerpc_parse_binding(conn, conn->binding_string,
+ &conn->binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to parse the binding string %s\n",
+ conn->binding_string));
+ return;
+ }
+ dcerpc_binding_set_flags(conn->binding, DCERPC_CONCURRENT_MULTIPLEX, 0);
+
+ status = dcerpc_pipe_connect_b(conn, &p, conn->binding,
+ &ndr_table_frstrans,
+ service->system_session_info->credentials,
+ service->tevent_ctx,
+ service->task->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to connect to %s - %s\n",
+ conn->binding_string, nt_errstr(status)));
+ return;
+ }
+ conn->dcerpc_pipe = p;
+ if (conn->dcerpc_pipe == NULL) {
+ return;
+ }
+ DEBUG(INFOLOG, ("Creating a DFSR connection to %s\n",
+ GUID_buf_string(&conn->guid, &tmp_buf)));
+ status = dcerpc_frstrans_EstablishConnection(p->binding_handle,
+ conn,
+ group->guid,
+ conn->guid,
+ FRSTRANS_PROTOCOL_VERSION_LONGHORN_SERVER,
+ FRSTRANS_TRANSPORT_SUPPORTS_RDC_SIMILARITY,
+ &version,
+ &flags,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(ERRORLOG, ("Unable to established a connection"));
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(ERRORLOG, ("Unable to established a connection"));
+ return;
+ }
+
+ status = dcerpc_frstrans_EstablishSession(p->binding_handle,
+ conn,
+ conn->guid,
+ group->set_guids[0],
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to establishe a session"));
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Unable to establishe a session"));
+ return;
+ }
+ conn->established = true;
+
+ dfsrsrv_session_loop_start(service, group, member, conn);
+ return;
+}
+
+struct dfsrsrv_session_loop_state {
+ struct dfsrsrv_service *service;
+ struct dfsrsrv_replica_group *group;
+ struct dfsrsrv_replica_member *member;
+ struct dfsrsrv_connection *conn;
+
+ struct frstrans_AsyncResponseContext poll_response;
+ struct tevent_req *subreq;
+
+ enum frstrans_VersionChangeType change_type;
+ uint32_t sequence;
+ uint64_t generation;
+ uint64_t known_generation;
+ bool use_new_generation;
+
+ /* Version Vectors that we want to have */
+ uint32_t vector_diff_count;
+ struct frstrans_VersionVector *vector_diff;
+
+ uint32_t max_updates;
+ uint32_t num_updates;
+ uint32_t remaining_downloads;
+ uint32_t downloaded_updates;
+ uint32_t skipped_updates;
+ struct frstrans_Update *updates;
+ enum frstrans_UpdateStatus update_status;
+ struct GUID gvsn_db_guid;
+ uint64_t gvsn_version;
+};
+
+static void dfsrsrv_session_loop_next(struct dfsrsrv_session_loop_state *state);
+static void dfsrsrv_session_loop_poll_done(struct tevent_req *subreq);
+static void dfsrsrv_session_loop_vector_done(struct tevent_req *subreq);
+static void dfsrsrv_session_loop_update_done(struct tevent_req *subreq);
+
+static void dfsrsrv_download_loop_start(struct dfsrsrv_session_loop_state *session,
+ uint32_t update_idx);
+
+/* Will start looping for DFSR events on a connection related to our membership in a
+ * given replicaset for a given partner
+ */
+static void dfsrsrv_session_loop_start(struct dfsrsrv_service *service,
+ struct dfsrsrv_replica_group *group,
+ struct dfsrsrv_replica_member *member,
+ struct dfsrsrv_connection *conn)
+{
+ struct dfsrsrv_session_loop_state *state;
+ struct GUID_txt_buf tmp_buf;
+
+ DEBUG(DEBUGLOG, ("Starting looping for DFSR member group '%s' with %s (our set = %s)\n",
+ group->name,
+ conn->hostname,
+ GUID_buf_string(&member->guid, &tmp_buf)));
+ state = talloc_zero(conn, struct dfsrsrv_session_loop_state);
+ if (state == NULL) {
+ //???
+ return;
+ }
+ state->service = service;
+ state->group = group;
+ state->member = member;
+ state->conn = conn;
+ state->generation = 0;
+ state->known_generation = 0;
+ state->change_type = FRSTRANS_VERSION_CHANGE_NOTIFY;
+
+ dfsrsrv_session_loop_next(state);
+}
+
+static void dfsrsrv_session_loop_next(struct dfsrsrv_session_loop_state *state)
+{
+ struct tevent_req *subreq;
+ uint32_t old_timeout;
+ uint32_t hash_requested = 0;
+ struct GUID_txt_buf tmp_buf;
+
+ if (state->subreq != NULL) {
+ goto request_updates;
+ }
+
+ old_timeout = dcerpc_binding_handle_set_timeout(state->conn->dcerpc_pipe->binding_handle, UINT32_MAX);
+ state->subreq = dcerpc_frstrans_AsyncPoll_send(state,
+ state->service->task->event_ctx,
+ state->conn->dcerpc_pipe->binding_handle,
+ state->conn->guid,
+ &state->poll_response);
+ dcerpc_binding_handle_set_timeout(state->conn->dcerpc_pipe->binding_handle, old_timeout);
+ if (state->subreq == NULL) {
+ return;
+ }
+ tevent_req_set_callback(state->subreq, dfsrsrv_session_loop_poll_done, state);
+ DEBUG(DEBUGLOG, ("Sending Request Version Vector with flag %s and generation %ld to %s\n",
+ state->change_type == FRSTRANS_VERSION_CHANGE_NOTIFY?"CHANGE_NOTIFY":"CHANGE_ALL",
+ state->known_generation,
+ GUID_buf_string(&state->conn->guid, &tmp_buf)));
+ state->sequence += 1;
+ subreq = dcerpc_frstrans_RequestVersionVector_send(state,
+ state->service->task->event_ctx,
+ state->conn->dcerpc_pipe->binding_handle,
+ state->sequence,
+ state->conn->guid,
+ state->group->set_guids[0],
+ FRSTRANS_VERSION_REQUEST_NORNAL_SYNC,
+ state->change_type,
+ state->known_generation);
+ if (subreq == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dfsrsrv_session_loop_vector_done, state);
+
+ if (state->vector_diff_count != 0) {
+ goto request_updates;
+ }
+ state->change_type = FRSTRANS_VERSION_CHANGE_NOTIFY;
+ return;
+
+request_updates:
+ /*
+ * We have at this moment calculated a delta between what we know
+ * and what the partner told us as new stuff
+ */
+ state->num_updates = 0;
+ state->max_updates = 32;
+ state->updates = talloc_zero_array(state, struct frstrans_Update, state->max_updates);
+ if (state->updates == NULL) {
+ return;
+ }
+ subreq = dcerpc_frstrans_RequestUpdates_send(state,
+ state->service->task->event_ctx,
+ state->conn->dcerpc_pipe->binding_handle,
+ state->conn->guid,
+ state->group->set_guids[0],
+ state->max_updates,
+ hash_requested,
+ FRSTRANS_UPDATE_REQUEST_ALL,
+ state->vector_diff_count,
+ state->vector_diff,
+ state->updates,
+ &state->num_updates,
+ &state->update_status,
+ &state->gvsn_db_guid,
+ &state->gvsn_version);
+ if (subreq == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dfsrsrv_session_loop_update_done, state);
+}
+
+/*
+ * Calculate the delta between what we know and what was sent by the partner
+ * Result is stored in state->vector_diff, previous vector is freed is non NULL
+ */
+static void calculate_delta_vectors(struct dfsrsrv_session_loop_state *state,
+ uint32_t vector_diff_count,
+ struct frstrans_VersionVector *vector_diff)
+{
+ uint32_t i, j;
+ if (state->vector_diff) {
+ TALLOC_FREE(state->vector_diff);
+ state->vector_diff_count = 0;
+ }
+
+ for (i = 0; i < vector_diff_count; i++) {
+ bool found = false;
+ for (j = 0; j < state->group->known_vector_diff_count; j++) {
+ if (!GUID_compare(&vector_diff[i].db_guid,
+ &state->group->known_vector_diff[j].db_guid) == 0)
+ {
+ continue;
+ }
+ /* Same guid */
+ found = true;
+ if (state->group->known_vector_diff[j].high >= vector_diff[i].high) {
+ /* We know already every thing */
+ break;
+ } else {
+ state->vector_diff = talloc_realloc(state, state->vector_diff, struct frstrans_VersionVector, state->vector_diff_count + 1);
+ state->vector_diff[state->vector_diff_count] = vector_diff[i];
+ state->vector_diff[state->vector_diff_count].low = state->group->known_vector_diff[i].high;
+ state->vector_diff_count++;
+ break;
+ }
+ }
+ if (!found) {
+ state->vector_diff = talloc_realloc(state, state->vector_diff, struct frstrans_VersionVector, state->vector_diff_count + 1);
+ state->vector_diff[state->vector_diff_count] = vector_diff[i];
+ state->vector_diff_count++;
+ }
+ }
+ DEBUG(DEBUGLOG, ("%s %d newer vectors\n", __FILE__, state->vector_diff_count));
+}
+
+static void dfsrsrv_session_loop_poll_done(struct tevent_req *subreq)
+{
+ struct dfsrsrv_session_loop_state *state =
+ tevent_req_callback_data(subreq,
+ struct dfsrsrv_session_loop_state);
+ NTSTATUS error;
+ WERROR result;
+
+ if (state->change_type == FRSTRANS_VERSION_CHANGE_NOTIFY) {
+ state->change_type = FRSTRANS_VERSION_CHANGE_ALL ;
+ }
+ error = dcerpc_frstrans_AsyncPoll_recv(subreq, state, &result);
+ if (!NT_STATUS_IS_OK(error)) {
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ return;
+ }
+
+ if (!W_ERROR_IS_OK(state->poll_response.status)) {
+ return;
+ }
+ if (state->poll_response.sequence_number != state->sequence) {
+ return;
+ }
+ state->generation = state->poll_response.response.vv_generation;
+ if (state->poll_response.response.version_vector_count == 0) {
+ /* So that we restart a request version_vector */
+ state->subreq = NULL;
+ } else {
+ calculate_delta_vectors(state,
+ state->poll_response.response.version_vector_count,
+ state->poll_response.response.version_vector);
+ }
+
+ dfsrsrv_session_loop_next(state);
+}
+
+static void dfsrsrv_session_loop_vector_done(struct tevent_req *subreq)
+{
+ struct dfsrsrv_session_loop_state *state =
+ tevent_req_callback_data(subreq,
+ struct dfsrsrv_session_loop_state);
+ NTSTATUS error;
+ WERROR result;
+
+ error = dcerpc_frstrans_RequestVersionVector_recv(subreq, state,
+ &result);
+ if (!NT_STATUS_IS_OK(error)) {
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ return;
+ }
+}
+
+static void lexicofilter_vector(struct GUID dbguid, uint64_t version,
+ struct dfsrsrv_session_loop_state *state)
+{
+ unsigned i;
+ struct frstrans_VersionVector *vector_diff = NULL;
+ uint32_t vector_diff_count = 0;
+
+ for (i = 0; i < state->vector_diff_count; i++) {
+ int compare_res = GUID_compare(&state->vector_diff[i].db_guid, &dbguid);
+ switch (compare_res) {
+ case -1:
+ /* Lexically less so we ignore it */
+ break;
+ case 1:
+ /* More so we keep it */
+ vector_diff = talloc_realloc(state, vector_diff,
+ struct frstrans_VersionVector,
+ vector_diff_count + 1);
+ vector_diff[vector_diff_count] = state->vector_diff[i];
+ vector_diff_count++;
+ break;
+ case 0:
+ /* Equal, let's see if the high is > version */
+ if (state->vector_diff[i].high > version) {
+ vector_diff = talloc_realloc(state, vector_diff,
+ struct frstrans_VersionVector,
+ vector_diff_count + 1);
+ vector_diff[vector_diff_count] = state->vector_diff[i];
+ vector_diff[vector_diff_count].low = version;
+ vector_diff_count++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ talloc_free(state->vector_diff);
+ state->vector_diff = vector_diff;
+ state->vector_diff_count = vector_diff_count;
+ return;
+}
+
+static void xor_guid(struct GUID *tgt, const struct GUID *guid1, const struct GUID *guid2)
+{
+ tgt->time_low = guid1->time_low ^ guid2->time_low;
+ tgt->time_mid = guid1->time_mid ^ guid2->time_mid;
+ tgt->time_hi_and_version = guid1->time_hi_and_version ^ guid2->time_hi_and_version;
+ tgt->clock_seq[0] = guid1->clock_seq[0] ^ guid2->clock_seq[0];
+ tgt->clock_seq[1] = guid1->clock_seq[1] ^ guid2->clock_seq[1];
+ tgt->node[0] = guid1->node[0] ^ guid2->node[0];
+ tgt->node[1] = guid1->node[1] ^ guid2->node[1];
+ tgt->node[2] = guid1->node[2] ^ guid2->node[2];
+ tgt->node[3] = guid1->node[3] ^ guid2->node[3];
+ tgt->node[4] = guid1->node[4] ^ guid2->node[4];
+ tgt->node[5] = guid1->node[5] ^ guid2->node[5];
+}
+
+/* Called once we have requested all the updates (but not got them necessarly */
+static void dfsrsrv_session_loop_update_done(struct tevent_req *subreq)
+{
+ struct dfsrsrv_session_loop_state *state =
+ tevent_req_callback_data(subreq,
+ struct dfsrsrv_session_loop_state);
+ NTSTATUS error;
+ WERROR result;
+ uint32_t i;
+ struct GUID_txt_buf tmp_buf;
+
+ error = dcerpc_frstrans_RequestUpdates_recv(subreq, state,
+ &result);
+ if (!NT_STATUS_IS_OK(error)) {
+ TALLOC_FREE(subreq);
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(subreq);
+ return;
+ }
+
+ TALLOC_FREE(subreq);
+ state->remaining_downloads += state->num_updates;
+ if (state->update_status != FRSTRANS_UPDATE_STATUS_MORE) {
+ /*
+ * state->vector_diff will be freed next time we do the calculation
+ * of the delta
+ */
+ state->vector_diff_count = 0;
+ state->known_generation = state->generation;
+ state->known_vector_diff = talloc_steal(state, state->poll_response.response.version_vector);
+ state->known_vector_diff_count = state->poll_response.response.version_vector_count;
+ state->change_type = FRSTRANS_VERSION_CHANGE_NOTIFY;
+ ZERO_STRUCT(state->poll_response);
+ TALLOC_FREE(state->subreq);
+ } else {
+ /* MS-FRS2 3.3.4.6.1: Requesting Updates (State Transitions)
+ * If thet status is not FRSTRANS_UPDATE_STATUS_DONE
+ * We need to compare the guid and not blindly assume that it's always the first elment*/
+ lexicofilter_vector(state->gvsn_db_guid, state->gvsn_version, state);
+ if (state->vector_diff == NULL) {
+ DEBUG(ERRORLOG, ("FRSTRANS_UPDATE_STATUS_MORE but 0 delta\n"));
+ state->vector_diff_count = 0;
+ state->known_generation = state->generation;
+ state->known_vector_diff = talloc_steal(state, state->poll_response.response.version_vector);
+ state->known_vector_diff_count = state->poll_response.response.version_vector_count;
+ state->change_type = FRSTRANS_VERSION_CHANGE_NOTIFY;
+ ZERO_STRUCT(state->poll_response);
+ TALLOC_FREE(state->subreq);
+ }
+ /* We don't do the free of the subreq if the lexicofilter thiny
+ * retruns a non empty vector so that session_loop_next
+ * goes back to requesting more updates
+ */
+ }
+ for (i = 0; i < state->num_updates; i++) {
+ struct GUID xored;
+ if (!state->member->isinitialized && !state->updates[i].present) {
+ state->downloaded_updates += 1;
+ DEBUG(0, ("Skipping update %d as we are not initialized and the file is not present\n",
+ i));
+ continue;
+ }
+ if (state->updates[i].uid_version == 2) {
+ xor_guid(&xored, &state->updates[i].uid_db_guid,
+ &state->updates[i].content_set_guid);
+ state->skipped_updates += 1;
+ DEBUG(0, ("VersionVector Tombstone at %d for GUID %s\n",
+ i,
+ GUID_buf_string(&xored, &tmp_buf)));
+ continue;
+ }
+ dfsrsrv_download_loop_start(state, i);
+ }
+ dfsrsrv_session_loop_next(state);
+}
+
+struct dfsrsrv_download_loop_state {
+ struct dfsrsrv_session_loop_state *session;
+ uint32_t update_idx;
+ struct frstrans_Update *update;
+
+ enum frstrans_RequestedStagingPolicy staging_policy;
+ struct policy_handle server_context;
+ struct frstrans_RdcFileInfo *rdc_file_info;
+ uint8_t *data_buffer;
+ uint32_t buffer_size;
+ uint32_t size_read;
+ uint32_t is_end_of_file;
+ uint64_t offset;
+
+ struct dcerpc_pipe *p;
+ struct frstrans_BytePipe *byte_pipe;
+};
+
+static void dfsrsrv_download_loop_open_done(struct tevent_req *subreq);
+
+static void dfsrsrv_download_loop_start(struct dfsrsrv_session_loop_state *session,
+ uint32_t update_idx)
+{
+ struct frstrans_Update *update = &session->updates[update_idx];
+ struct dfsrsrv_download_loop_state *state;
+ struct tevent_req *subreq;
+
+ state = talloc_zero(session->updates, struct dfsrsrv_download_loop_state);
+ if (state == NULL) {
+ //???
+ return;
+ }
+ state->session = session;
+ state->update_idx = update_idx;
+ state->update = update;
+
+ state->staging_policy = FRSTRANS_STAGING_POLICY_SERVER_DEFAULTY;
+ state->buffer_size = 262144;
+ state->data_buffer = talloc_zero_array(state, uint8_t, state->buffer_size);
+ if (state->data_buffer == NULL) {
+ return;
+ }
+ state->size_read = 0;
+ state->is_end_of_file = 0;
+
+ DEBUG(0, ("Requesting file %s\n",state->update->name));
+ subreq = dcerpc_frstrans_InitializeFileTransferAsync_send(state,
+ session->service->task->event_ctx,
+ session->conn->dcerpc_pipe->binding_handle,
+ session->conn->guid,
+ state->update,
+ 1, /* rdc_desired */
+ &state->staging_policy,
+ &state->server_context,
+ &state->rdc_file_info,
+ state->data_buffer,
+ state->buffer_size,
+ &state->size_read,
+ &state->is_end_of_file);
+ if (subreq == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dfsrsrv_download_loop_open_done, state);
+}
+
+static void dfsrsrv_download_loop_connect_done(struct composite_context *c_req);
+
+/* Will write in the database the information that we had from the download */
+static void checkpoint_download(struct dfsrsrv_session_loop_state *session)
+{
+ DEBUG(INFOLOG, ("Downloaded %d files, skipped %d files\n",
+ session->downloaded_updates,
+ session->skipped_updates));
+ session->max_updates = 0;
+ session->num_updates = 0;
+ session->downloaded_updates = 0;
+ session->skipped_updates = 0;
+ session->remaining_downloads = 0;
+ if (!session->member->isinitialized) {
+ DEBUG(0, ("Finish initializing !\n"));
+ session->member->isinitialized = true;
+ /*
+ * FIXME check with ASAN if we need this or not
+ * session->conn->established = false;
+ * TALLOC_FREE(state->p);
+ * TALLOC_FREE(state);
+ * TALLOC_FREE(session);
+ */
+ }
+ TALLOC_FREE(session->updates);
+ return;
+}
+
+static void dfsrsrv_download_loop_open_done(struct tevent_req *subreq)
+{
+ struct dfsrsrv_download_loop_state *state =
+ tevent_req_callback_data(subreq,
+ struct dfsrsrv_download_loop_state);
+ struct dfsrsrv_session_loop_state *session = state->session;
+ struct composite_context *c_req;
+ NTSTATUS error;
+ WERROR result;
+
+ error = dcerpc_frstrans_InitializeFileTransferAsync_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(ERRORLOG, ("dcerpc_frstrans_InitializeFileTransferAsync_recv returned error: %s",
+ nt_errstr(error)));
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(ERRORLOG, ("RPC call InitializeFileTranferAsync return an error: %s",
+ get_friendly_werror_msg(result)));
+ return;
+ }
+
+ if (state->is_end_of_file != 0) {
+ session->downloaded_updates += 1;
+ DEBUG(INFOLOG, ("Finished downloading file %s (%d out of %d)\n",
+ state->update->name,
+ session->downloaded_updates,
+ (session->remaining_downloads - session->skipped_updates)));
+ TALLOC_FREE(state);
+ if (session->downloaded_updates < (session->remaining_downloads - session->skipped_updates)) {
+ return;
+ }
+ /*
+ * TODO we want to checkpoint when 1 batch is finished not when
+ * everything is downloaded otherwise if there is a lot of updates
+ * and the download speed is low we can takes minutes/hours/days...
+ * to store our state
+ */
+ checkpoint_download(session);
+ return;
+ }
+
+ /*
+ * If the file couldn't be downloaded in just one rpc call then
+ * we have to go async with the byte pipe stuff
+ */
+ c_req = dcerpc_pipe_connect_b_send(state, session->conn->binding,
+ &ndr_table_frstrans,
+ session->service->system_session_info->credentials,
+ session->service->tevent_ctx,
+ session->service->task->lp_ctx);
+ if (c_req == NULL) {
+ return;
+ }
+ c_req->async.fn = dfsrsrv_download_loop_connect_done;
+ c_req->async.private_data = state;
+}
+
+static void dfsrsrv_download_loop_read_done(struct tevent_req *subreq);
+static void dfsrsrv_download_loop_chunk_done(struct tevent_req *subreq2);
+
+static void dfsrsrv_download_loop_connect_done(struct composite_context *c_req)
+{
+ struct dfsrsrv_download_loop_state *state =
+ talloc_get_type_abort(c_req->async.private_data,
+ struct dfsrsrv_download_loop_state);
+ struct dfsrsrv_session_loop_state *session = state->session;
+ struct tevent_req *subreq;
+ struct tevent_req *subreq2;
+ NTSTATUS error;
+
+ error = dcerpc_pipe_connect_b_recv(c_req, state, &state->p);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(ERRORLOG, ("dcerpc_pipe_connect_b_recv returned error: %s",
+ nt_errstr(error)));
+ return;
+ }
+
+ state->byte_pipe = dcerpc_frstrans_BytePipe_create(state);
+ if (state->byte_pipe == NULL) {
+ DEBUG(ERRORLOG, ("dcerpc_frstrans_BytePipe_create returned NULL"));
+ return;
+ }
+
+ subreq = dcerpc_frstrans_RawGetFileDataAsync_send(state,
+ session->service->task->event_ctx,
+ session->conn->dcerpc_pipe->binding_handle,
+ &state->server_context,
+ state->byte_pipe);
+ if (subreq == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dfsrsrv_download_loop_read_done, state);
+
+ subreq2 = dcerpc_frstrans_BytePipe_chunk_pull_send(state,
+ session->service->task->event_ctx,
+ state->byte_pipe);
+ if (subreq2 == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq2, dfsrsrv_download_loop_chunk_done, state);
+}
+
+static void dfsrsrv_download_loop_chunk_done(struct tevent_req *subreq2)
+{
+ struct dfsrsrv_download_loop_state *state =
+ tevent_req_callback_data(subreq2,
+ struct dfsrsrv_download_loop_state);
+ struct dfsrsrv_session_loop_state *session = state->session;
+ NTSTATUS error;
+ struct frstrans_BytePipe_chunk *chunk;
+
+ error = dcerpc_frstrans_BytePipe_chunk_pull_recv(subreq2, state, &chunk);
+ TALLOC_FREE(subreq2);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(ERRORLOG, ("dcerpc_frstrans_InitializeFileTransferAsync_recv returned error: %s",
+ nt_errstr(error)));
+ return;
+ }
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(frstrans_BytePipe_chunk, chunk);
+ } else {
+ if (chunk->count > 0) {
+ DEBUG(DEBUGLOG,("file %s frstrans_BytePipe_chunk count[%u]\n",
+ state->update->name,
+ chunk->count));
+ }
+ }
+
+ if (chunk->count == 0) {
+ session->downloaded_updates += 1;
+ DEBUG(INFOLOG, ("Finished downloading file %s (%d out of %d) (using async)\n",
+ state->update->name,
+ session->downloaded_updates,
+ (session->remaining_downloads - session->skipped_updates)));
+ TALLOC_FREE(chunk);
+ return;
+ }
+ TALLOC_FREE(chunk);
+
+ subreq2 = dcerpc_frstrans_BytePipe_chunk_pull_send(state,
+ session->service->task->event_ctx,
+ state->byte_pipe);
+ if (subreq2 == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq2, dfsrsrv_download_loop_chunk_done, state);
+}
+
+static void dfsrsrv_download_loop_close_done(struct tevent_req *subreq);
+
+static void dfsrsrv_download_loop_read_done(struct tevent_req *subreq)
+{
+ struct dfsrsrv_download_loop_state *state =
+ tevent_req_callback_data(subreq,
+ struct dfsrsrv_download_loop_state);
+ struct dfsrsrv_session_loop_state *session = state->session;
+ NTSTATUS error;
+ WERROR result;
+
+ error = dcerpc_frstrans_RawGetFileDataAsync_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(ERRORLOG, ("dcerpc_frstrans_RawGetFileDataAsync_recv returned error: %s",
+ nt_errstr(error)));
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(ERRORLOG, ("RPC call RawGetFileDataAsync return an error: %s",
+ get_friendly_werror_msg(result)));
+ return;
+ }
+
+ subreq = dcerpc_frstrans_RdcClose_send(state,
+ session->service->task->event_ctx,
+ session->conn->dcerpc_pipe->binding_handle,
+ &state->server_context);
+ if (subreq == NULL) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dfsrsrv_download_loop_close_done, state);
+}
+
+/* End of async download */
+static void dfsrsrv_download_loop_close_done(struct tevent_req *subreq)
+{
+ struct dfsrsrv_download_loop_state *state =
+ tevent_req_callback_data(subreq,
+ struct dfsrsrv_download_loop_state);
+ struct dfsrsrv_session_loop_state *session = state->session;
+ NTSTATUS error;
+ WERROR result;
+
+ error = dcerpc_frstrans_RdcClose_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(error)) {
+ return;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ return;
+ }
+
+ TALLOC_FREE(state);
+ if (session->downloaded_updates < (session->remaining_downloads - session->skipped_updates)) {
+ return;
+ }
+
+ checkpoint_download(session);
+}
+
+static void dorun(struct dfsrsrv_service *service)
+{
+ struct ldb_dn *dn_sysvol;
+ TALLOC_CTX *mem_ctx = talloc_new(service);
+ const struct GUID *ntds_guid;
+ unsigned int i;
+ static bool just_booted = true;
+
+ /*
+ * Every time we run if we don't know the sysvol dfsr
+ * replication group has already been created
+ *
+ * Normaly the dfsr sysvol is now created at provision time, but
+ * in order to cope with existing provsion that do not have it
+ * we wait for one to pop !
+ */
+ if (!init_sysvol_replicaset(service)) {
+ DEBUG(0, ("init_sysvol_replicaset returned %s",
+ service->error));
+ talloc_free(service->error);
+ return;
+ }
+
+ ntds_guid = samdb_ntds_objectGUID(service->samdb);
+ dn_sysvol = ldb_dn_new_fmt(mem_ctx, service->dfsrdb, "CN=%s, CN=%s",
+ GUID_string(mem_ctx, &service->sysvol->guid),
+ GUID_string(mem_ctx, ntds_guid));
+ if (dn_sysvol == NULL || service->sysvol->me == NULL) {
+ talloc_free(mem_ctx);
+ return;
+ }
+ if (just_booted) {
+ DEBUG(INFOLOG, ("DFSR SYSVOL subscription for this server is %s\n",
+ ldb_dn_get_linearized(service->sysvol->me->dn)));
+ }
+
+ /* TODO
+ * We need to scan the groups from time to time in order to find new groups
+ * and also new member
+ */
+
+ for (i = 0; i < service->numgroups; i++) {
+ /* Most probably FIXME because we make the assumption that there
+ * is one replicaset in a given group
+ * It's the case for sysvol but not for the rest I think
+ * Matthieu Patou Sep 2014
+ */
+ struct dfsrsrv_replica_member *me = service->groups[i]->me;
+ if (me) {
+ int j, loglevel;
+ populate_connections(me, service->samdb,
+ service->groups[i]->is_sysvol);
+
+ if (!just_booted) {
+ loglevel = ALLYOUCANEATLOG;
+ } else {
+ loglevel = 1;
+ }
+ DEBUG(loglevel, ("There is %d connection for the subscription %s\n",
+ me->num_cnx, ldb_dn_get_linearized(me->dn)));
+
+ for (j=0; j < me->num_cnx; j++) {
+ if (!me->cnxs[j]->established) {
+ establish_connection(service,
+ service->groups[i],
+ me,
+ me->cnxs[j]);
+ } else {
+ continue;
+ }
+ //send_asyncpoll();
+ }
+ }
+ }
+ just_booted = false;
+}
+
+static bool init_sysvol_replicaset(struct dfsrsrv_service *service)
+{
+ struct ldb_dn *sysvol_replicagrp_dn;
+ WERROR status;
+ if (service->sysvol != NULL && service->sysvol->initizialized) {
+ return true;
+ }
+ sysvol_replicagrp_dn = ldb_dn_new_fmt(service, service->samdb,
+ "CN=Domain System Volume, %s",
+ ldb_dn_get_linearized(service->dfsr_basedn));
+ status = get_dfsr_sets_ingroup(service, sysvol_replicagrp_dn, true);
+ if (!W_ERROR_IS_OK(status)) {
+ service->error = talloc_asprintf(service,
+ "dfssrv: Failed to get sysvol replica infos %s",
+ win_errstr(status));
+ return false;
+ }
+ talloc_free(sysvol_replicagrp_dn);
+ /*
+ * There is a dfsr group for sysvol, let's see if we should
+ * register ourselves
+ */
+ if (service->sysvol) {
+ int ret = register_or_get_sysvol_subscription(service);
+ if ( ret != LDB_SUCCESS) {
+ DEBUG(ERRORLOG, ("register_or_get_sysvol_subscription "
+ "failed with the following ldb error: %s\n",
+ ldb_errstring(service->samdb)));
+ service->error = talloc_asprintf(service,
+ "dfsrsrv: Failed to register_or_get_sysvol_subscription: %d\n",
+ ret);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ startup the dfsr service.
+*/
+static void dfsrsrv_task_init(struct task_server *task)
+{
+ WERROR status;
+ struct dfsrsrv_service *service;
+ uint32_t periodic_startup_interval;
+
+ switch (lpcfg_server_role(task->lp_ctx)) {
+ case ROLE_STANDALONE:
+ task_server_terminate(task, "dfsrsrv: no dfsr required in standalone configuration",
+ false);
+ return;
+ case ROLE_DOMAIN_MEMBER:
+ /*
+ * FIXME
+ * Needs to be fixed in the future
+ */
+ task_server_terminate(task, "dfsrsrv: no dfsr required in domain member configuration",
+ false);
+ return;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ break;
+ }
+
+ task_server_set_title(task, "task[dfsrsrv]");
+
+ service = talloc_zero(task, struct dfsrsrv_service);
+ if (!service) {
+ task_server_terminate(task, "dfsrsrv_task_init: out of memory", true);
+ return;
+ }
+ service->task = task;
+ service->startup_time = timeval_current();
+ status = init_service_paths(service);
+
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dfsrsrv: Failed to init files path: %s\n",
+ win_errstr(status)), true);
+ return;
+ }
+
+ task->private_data = service;
+
+ status = dfsrsrv_init_creds(service);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dfsrsrv: Failed to obtain server credentials: %s\n",
+ win_errstr(status)), true);
+ return;
+ }
+ status = dfsrsrv_connect_dbs(service, task->lp_ctx);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dfsrsrv: Failed to connect to databases: %s\n",
+ win_errstr(status)), true);
+ return;
+ }
+
+ service->dfsr_basedn = ldb_dn_new_fmt(service, service->samdb,
+ "CN=DFSR-GlobalSettings,CN=System,%s",
+ ldb_dn_get_linearized(ldb_get_root_basedn(service->samdb)));
+ /*
+ * FIXME do all the DFSR Replication groups one day
+ * For the moment do only sysvol
+ */
+ if (!init_sysvol_replicaset(service)) {
+ task_server_terminate(task, service->error, true);
+ return;
+ }
+ periodic_startup_interval = lpcfg_parm_int(task->lp_ctx, NULL,
+ "dfsrsrv",
+ "periodic_startup_interval",
+ 1); /* in seconds */
+ service->periodic.interval = lpcfg_parm_int(task->lp_ctx, NULL,
+ "dfsrsrv",
+ "periodic_interval",
+ 10); /* in seconds */
+
+ status = dfsrsrv_periodic_schedule(service, periodic_startup_interval);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task,
+ talloc_asprintf(task,
+ "dfsrsrv: Failed to periodic schedule: %s\n",
+ win_errstr(status)),
+ true);
+ return;
+ }
+
+#if 0
+ irpc_add_name(task->msg_ctx, "dfsrsrv");
+
+ IRPC_REGISTER(task->msg_ctx, irpc, dfsrSRV_CHECK_VALID_VOLATILE,
+ dfsrsrv_is_valid_temporary_cnx, service);
+
+ IRPC_REGISTER(task->msg_ctx, irpc, dfsrSRV_PROCESS_COMMAND,
+ dfsrsrv_process_command, service);
+
+ IRPC_REGISTER(task->msg_ctx, irpc, dfsrSRV_PROCESS_START_PROMOTION,
+ dfsrsrv_process_start_promotion, service);
+#endif
+}
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_dfsr_init(void)
+{
+ return register_server_service("dfsr", dfsrsrv_task_init);
+}