diff options
Diffstat (limited to 'source4/utils/net')
-rw-r--r-- | source4/utils/net/config.mk | 3 | ||||
-rw-r--r-- | source4/utils/net/drs/net_drs.c | 9 | ||||
-rw-r--r-- | source4/utils/net/drs/net_drs_replicate.c | 246 |
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; +} |