summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/utils/net/config.mk3
-rw-r--r--source4/utils/net/drs/net_drs.c9
-rw-r--r--source4/utils/net/drs/net_drs_replicate.c246
3 files changed, 254 insertions, 4 deletions
diff --git a/source4/utils/net/config.mk b/source4/utils/net/config.mk
index e3dfb35ce0..ee1638cbdc 100644
--- a/source4/utils/net/config.mk
+++ b/source4/utils/net/config.mk
@@ -15,7 +15,8 @@ PRIVATE_DEPENDENCIES = \
net_drs_OBJ_FILES = $(addprefix $(utilssrcdir)/net/drs/, \
net_drs.o \
net_drs_bind.o \
- net_drs_kcc.o)
+ net_drs_kcc.o \
+ net_drs_replicate.o)
$(eval $(call proto_header_template,$(utilssrcdir)/net/drs/net_drs_proto.h,$(net_drs_OBJ_FILES:.o=.c)))
diff --git a/source4/utils/net/drs/net_drs.c b/source4/utils/net/drs/net_drs.c
index 87ebe0890a..1ba6eea576 100644
--- a/source4/utils/net/drs/net_drs.c
+++ b/source4/utils/net/drs/net_drs.c
@@ -34,7 +34,9 @@
static const struct net_functable net_drs_functable[] = {
{ "bind", "Display replication features for a domain controller\n", net_drs_bind_cmd, net_drs_bind_usage },
{ "kcc", "Forces the KCC to recalculate replication topology for a specified domain controller\n",
- net_drs_kcc_cmd, net_drs_kcc_usage },
+ net_drs_kcc_cmd, net_drs_kcc_usage },
+ { "replicate", "Triggers replication event for the specified naming context between the source and destination domain controllers.\n",
+ net_drs_replicate_cmd, net_drs_replicate_usage },
{ NULL, NULL }
};
@@ -54,8 +56,9 @@ int net_drs_usage(struct net_context *ctx, int argc, const char **argv)
d_printf("net drs <command> [options]\n");
d_printf("\n");
d_printf("Currently implemented commands:\n");
- d_printf(" bind - Display DC replication features\n");
- d_printf(" kcc - Forces the KCC to recalculate replication topology for a specified domain controller\n");
+ d_printf(" bind - Display DC replication features\n");
+ d_printf(" kcc - Forces the KCC to recalculate replication topology for a specified domain controller\n");
+ d_printf(" replicate - Triggers replication event for the specified naming context between the source and destination domain controllers.\n");
return 0;
}
diff --git a/source4/utils/net/drs/net_drs_replicate.c b/source4/utils/net/drs/net_drs_replicate.c
new file mode 100644
index 0000000000..6c6cbfaf97
--- /dev/null
+++ b/source4/utils/net/drs/net_drs_replicate.c
@@ -0,0 +1,246 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Implements functions offered by repadmin.exe tool under Windows
+
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2010
+
+ 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 "utils/net/net.h"
+#include "net_drs.h"
+#include "lib/ldb/include/ldb.h"
+#include "dsdb/samdb/samdb.h"
+
+
+/**
+ * Figure out what is the NDTS Settings objectGUID
+ * when DC_NAME is given
+ */
+static struct ldb_dn *
+net_drs_server_dn_from_dc_name(struct net_drs_context *drs_ctx,
+ const char *dc_name)
+{
+ int ldb_err;
+ struct ldb_dn *dn;
+ struct ldb_dn *server_dn = NULL;
+ struct ldb_result *ldb_res;
+ const char *filter;
+ static const char *attrs[] = {
+ "objectGUID",
+ "name",
+ "dNSHostName",
+ NULL
+ };
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(drs_ctx);
+
+ /* Make DN for Sites container */
+ dn = ldb_msg_find_attr_as_dn(drs_ctx->ldap.ldb, mem_ctx, drs_ctx->ldap.rootdse, "dsServiceName");
+ NET_DRS_CHECK_GOTO(dn != NULL, failed, "RootDSE doesn't have dsServiceName!?\n");
+ if (!ldb_dn_remove_child_components(dn, 4)) {
+ d_printf("Failed to make DN for Sites container.\n");
+ goto failed;
+ }
+
+ /* search for Server in Sites container */
+ ldb_err = ldb_search(drs_ctx->ldap.ldb, mem_ctx, &ldb_res,
+ dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectCategory=server)(|(name=%1$s)(dNSHostName=%1$s)))",
+ dc_name);
+ if (ldb_err != LDB_SUCCESS) {
+ d_printf("ldb_seach() failed with err: %d (%s); filter: (%s)",
+ ldb_err, ldb_errstring(drs_ctx->ldap.ldb), filter);
+ goto failed;
+ }
+ if (ldb_res->count != 1) {
+ d_printf("ldb_search() should return exactly one record!");
+ goto failed;
+ }
+
+ server_dn = talloc_steal(drs_ctx, ldb_res->msgs[0]->dn);
+
+failed:
+ talloc_free(mem_ctx);
+ return server_dn;
+}
+
+
+/**
+ * Figure out what is the NDTS Settings objectGUID
+ * when DC_NAME is given
+ */
+static bool net_drs_ntds_guid_from_dc_name(struct net_drs_context *drs_ctx,
+ const char *dc_name,
+ struct GUID *_ntds_guid)
+{
+ int ldb_err;
+ struct ldb_dn *server_dn;
+ struct ldb_result *ldb_res;
+ const char *filter;
+ static const char *attrs[] = {
+ "objectGUID",
+ "msDS-portLDAP",
+ "name",
+ "objectCategory",
+ NULL
+ };
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(drs_ctx);
+
+ /* resolve Server_DN for dc_name */
+ server_dn = net_drs_server_dn_from_dc_name(drs_ctx, dc_name);
+ if (!server_dn) {
+ d_printf("DSA object for %s could not be found.\n", dc_name);
+ goto failed;
+ }
+
+ /* move server_dn mem to local context */
+ server_dn = talloc_steal(mem_ctx, server_dn);
+
+ /* search ntdsDsa object under Server container */
+ ldb_err = ldb_search(drs_ctx->ldap.ldb, mem_ctx, &ldb_res,
+ server_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "%s", "(|(objectCategory=nTDSDSA)(objectCategory=nTDSDSARO))");
+ if (ldb_err != LDB_SUCCESS) {
+ d_printf("ldb_seach() failed with err: %d (%s); filter: (%s)",
+ ldb_err, ldb_errstring(drs_ctx->ldap.ldb), filter);
+ goto failed;
+ }
+ if (ldb_res->count != 1) {
+ d_printf("ldb_search() should return exactly one record!");
+ goto failed;
+ }
+
+ *_ntds_guid = samdb_result_guid(ldb_res->msgs[0], "objectGUID");
+
+ talloc_free(mem_ctx);
+ return true;
+
+failed:
+ talloc_free(mem_ctx);
+ return false;
+}
+
+/**
+ * Sends DsReplicaSync to dc_name_dest to
+ * replicate naming context nc_dn_str from
+ * server with ntds_guid_src GUID
+ */
+static bool net_drs_replicate_sync_nc(struct net_drs_context *drs_ctx,
+ struct GUID ntds_guid_src,
+ const char *nc_dn_str,
+ uint32_t options)
+{
+ NTSTATUS status;
+ struct net_drs_connection *drs_conn;
+ struct drsuapi_DsReplicaSync req;
+ union drsuapi_DsReplicaSyncRequest sync_req;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+
+ /* use already opened connection */
+ drs_conn = drs_ctx->drs_conn;
+
+ /* construct naming context object */
+ ZERO_STRUCT(nc);
+ nc.dn = nc_dn_str;
+
+ /* construct request object for DsReplicaSync */
+ req.in.bind_handle = &drs_conn->bind_handle;
+ req.in.level = 1;
+ req.in.req = &sync_req;
+ req.in.req->req1.naming_context = &nc;
+ req.in.req->req1.options = options;
+ req.in.req->req1.source_dsa_dns = NULL;
+ req.in.req->req1.source_dsa_guid = ntds_guid_src;
+
+ /* send DsReplicaSync request */
+ status = dcerpc_drsuapi_DsReplicaSync(drs_conn->drs_pipe, drs_ctx, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(drs_ctx, drs_conn->drs_pipe->last_fault_code);
+ }
+ d_printf("DsReplicaSync failed - %s.\n", errstr);
+ return false;
+ } else if (!W_ERROR_IS_OK(req.out.result)) {
+ d_printf("DsReplicaSync failed - %s.\n", win_errstr(req.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * 'net drs replicate' command entry point
+ */
+int net_drs_replicate_cmd(struct net_context *ctx, int argc, const char **argv)
+{
+ bool bret;
+ struct net_drs_context *drs_ctx;
+ struct GUID ntds_guid_src;
+ const char *dc_name_dest;
+ const char *dc_name_src;
+ const char *nc_dn_str;
+
+ /* only one arg expected */
+ if (argc != 3) {
+ return net_drs_replicate_usage(ctx, argc, argv);
+ }
+
+ dc_name_dest = argv[0];
+ dc_name_src = argv[1];
+ nc_dn_str = argv[2];
+
+ if (!net_drs_create_context(ctx, dc_name_dest, &drs_ctx)) {
+ return -1;
+ }
+
+ /* Resolve source DC_NAME to its NDTS Settings GUID */
+ if (!net_drs_ntds_guid_from_dc_name(drs_ctx, dc_name_src, &ntds_guid_src)) {
+ d_printf("Error: DSA object for %s could not be found.\n", dc_name_src);
+ goto failed;
+ }
+
+ /* Synchronize given Naming Context */
+ bret = net_drs_replicate_sync_nc(drs_ctx,
+ ntds_guid_src, nc_dn_str,
+ DRSUAPI_DRS_WRIT_REP);
+ if (!bret) {
+ goto failed;
+ }
+
+ d_printf("Replicate from %s to %s was successful.\n", dc_name_src, drs_ctx->dc_name);
+
+ talloc_free(drs_ctx);
+ return 0;
+
+failed:
+ d_printf("Replicate terminated with errors.\n");
+ talloc_free(drs_ctx);
+ return -1;
+}
+
+/**
+ * 'net drs replicate' usage
+ */
+int net_drs_replicate_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net drs replicate <Dest_DC_NAME> <Src_DC_NAME> <Naming Context>\n");
+ return 0;
+}