2 Unix SMB/CIFS implementation.
4 implement the DRSUpdateRefs call
6 Copyright (C) Andrew Tridgell 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "rpc_server/dcerpc_server.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "libcli/security/security.h"
26 #include "libcli/security/session.h"
27 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
28 #include "auth/session.h"
29 #include "librpc/gen_ndr/ndr_drsuapi.h"
30 #include "librpc/gen_ndr/ndr_irpc_c.h"
31 #include "lib/messaging/irpc.h"
34 #define DBGC_CLASS DBGC_DRS_REPL
38 struct repsFromToBlob *r;
41 static WERROR uref_check_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
42 struct ldb_dn *dn, struct GUID *dest_guid,
50 werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
51 if (!W_ERROR_IS_OK(werr)) {
55 for (i=0; i<reps.count; i++) {
56 if (GUID_equal(dest_guid,
57 &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
63 if (options & DRSUAPI_DRS_ADD_REF) {
64 if (found && !(options & DRSUAPI_DRS_DEL_REF)) {
65 return WERR_DS_DRA_REF_ALREADY_EXISTS;
69 if (options & DRSUAPI_DRS_DEL_REF) {
70 if (!found && !(options & DRSUAPI_DRS_ADD_REF)) {
71 return WERR_DS_DRA_REF_NOT_FOUND;
79 add a replication destination for a given partition GUID
81 static WERROR uref_add_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
82 struct ldb_dn *dn, struct repsFromTo1 *dest,
89 werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
90 if (!W_ERROR_IS_OK(werr)) {
94 for (i=0; i<reps.count; i++) {
95 if (GUID_equal(&dest->source_dsa_obj_guid,
96 &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
97 if (options & DRSUAPI_DRS_GETCHG_CHECK) {
100 return WERR_DS_DRA_REF_ALREADY_EXISTS;
105 reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
106 if (reps.r == NULL) {
107 return WERR_DS_DRA_INTERNAL_ERROR;
109 ZERO_STRUCT(reps.r[reps.count]);
110 reps.r[reps.count].version = 1;
111 reps.r[reps.count].ctr.ctr1 = *dest;
112 /* add the GCSPN flag if the client asked for it */
113 reps.r[reps.count].ctr.ctr1.replica_flags |= (options & DRSUAPI_DRS_REF_GCSPN);
116 werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
117 if (!W_ERROR_IS_OK(werr)) {
125 delete a replication destination for a given partition GUID
127 static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
128 struct ldb_dn *dn, struct GUID *dest_guid,
136 werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
137 if (!W_ERROR_IS_OK(werr)) {
141 for (i=0; i<reps.count; i++) {
142 if (GUID_equal(dest_guid,
143 &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
144 if (i+1 < reps.count) {
145 memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
152 werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
153 if (!W_ERROR_IS_OK(werr)) {
158 !(options & DRSUAPI_DRS_GETCHG_CHECK) &&
159 !(options & DRSUAPI_DRS_ADD_REF)) {
160 return WERR_DS_DRA_REF_NOT_FOUND;
166 struct drepl_refresh_state {
167 struct dreplsrv_refresh r;
171 * @brief Update the references for the given NC and the destination DSA object
173 * This function is callable from non RPC functions (ie. getncchanges), it
174 * will validate the request to update reference and then will add/del a repsTo
175 * to the specified server referenced by its DSA GUID in the request.
177 * @param[in] msg_ctx Messaging context for sending partition
178 * refresh in dreplsrv
180 * @param[in] b_state A bind_state object
182 * @param[in] mem_ctx A talloc context for memory allocation
184 * @param[in] req A drsuapi_DsReplicaUpdateRefsRequest1
185 * object which NC, which server and which
186 * action (add/delete) should be performed
188 * @return WERR_OK is success, different error
191 WERROR drsuapi_UpdateRefs(struct imessaging_context *msg_ctx,
192 struct tevent_context *event_ctx,
193 struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
194 struct drsuapi_DsReplicaUpdateRefsRequest1 *req)
198 struct ldb_dn *dn_normalised;
199 struct ldb_dn *nc_root;
200 struct ldb_context *sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
201 struct dcerpc_binding_handle *irpc_handle;
202 struct tevent_req *subreq;
203 struct drepl_refresh_state *state;
206 DEBUG(4,("DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
207 req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
209 drs_ObjectIdentifier_to_string(mem_ctx, req->naming_context)));
212 * 4.1.26.2 Server Behavior of the IDL_DRSUpdateRefs Method
213 * Implements the input validation checks
215 if (GUID_all_zero(&req->dest_dsa_guid)) {
216 return WERR_DS_DRA_INVALID_PARAMETER;
219 /* FIXME it seems that we should check the length of the stuff too*/
220 if (req->dest_dsa_dns_name == NULL) {
221 return WERR_DS_DRA_INVALID_PARAMETER;
224 if (!(req->options & (DRSUAPI_DRS_DEL_REF|DRSUAPI_DRS_ADD_REF))) {
225 return WERR_DS_DRA_INVALID_PARAMETER;
228 ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, sam_ctx, req->naming_context,
229 &dn_normalised, &nc_root);
230 if (ret != LDB_SUCCESS) {
231 DBG_WARNING("Didn't find a nc for %s\n",
232 ldb_dn_get_linearized(dn_normalised));
233 return WERR_DS_DRA_BAD_NC;
235 if (ldb_dn_compare(dn_normalised, nc_root) != 0) {
236 DBG_NOTICE("dn %s is not equal to %s\n",
237 ldb_dn_get_linearized(dn_normalised),
238 ldb_dn_get_linearized(nc_root));
239 return WERR_DS_DRA_BAD_NC;
243 * First check without a transaction open.
245 * This means that in the usual case, it will never open it and never
246 * bother to refresh the dreplsrv.
248 werr = uref_check_dest(sam_ctx,
253 if (W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_ALREADY_EXISTS) ||
254 W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_NOT_FOUND)) {
255 if (req->options & DRSUAPI_DRS_GETCHG_CHECK) {
261 if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
262 DEBUG(0,(__location__ ": Failed to start transaction on samdb: %s\n",
263 ldb_errstring(sam_ctx)));
264 return WERR_DS_DRA_INTERNAL_ERROR;
267 if (req->options & DRSUAPI_DRS_DEL_REF) {
268 werr = uref_del_dest(sam_ctx,
273 if (!W_ERROR_IS_OK(werr)) {
274 DEBUG(0,("Failed to delete repsTo for %s: %s\n",
275 GUID_string(mem_ctx, &req->dest_dsa_guid),
281 if (req->options & DRSUAPI_DRS_ADD_REF) {
282 struct repsFromTo1 dest;
283 struct repsFromTo1OtherInfo oi;
288 oi.dns_name = req->dest_dsa_dns_name;
289 dest.other_info = &oi;
290 dest.source_dsa_obj_guid = req->dest_dsa_guid;
291 dest.replica_flags = req->options;
293 werr = uref_add_dest(sam_ctx,
298 if (!W_ERROR_IS_OK(werr)) {
299 DEBUG(0,("Failed to add repsTo for %s: %s\n",
300 GUID_string(mem_ctx, &dest.source_dsa_obj_guid),
306 if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
307 DEBUG(0,(__location__ ": Failed to commit transaction on samdb: %s\n",
308 ldb_errstring(sam_ctx)));
309 return WERR_DS_DRA_INTERNAL_ERROR;
312 state = talloc_zero(mem_ctx, struct drepl_refresh_state);
317 irpc_handle = irpc_binding_handle_by_name(mem_ctx, msg_ctx,
318 "dreplsrv", &ndr_table_irpc);
319 if (irpc_handle == NULL) {
320 /* dreplsrv is not running yet */
326 * [Taken from auth_sam_trigger_repl_secret in auth_sam.c]
328 * This seem to rely on the current IRPC implementation,
329 * which delivers the message in the _send function.
331 * TODO: we need a ONE_WAY IRPC handle and register
332 * a callback and wait for it to be triggered!
334 subreq = dcerpc_dreplsrv_refresh_r_send(state, event_ctx,
335 irpc_handle, &state->r);
342 ldb_transaction_cancel(sam_ctx);
347 drsuapi_DsReplicaUpdateRefs
349 WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
350 struct drsuapi_DsReplicaUpdateRefs *r)
352 struct auth_session_info *session_info =
353 dcesrv_call_session_info(dce_call);
354 struct imessaging_context *imsg_ctx =
355 dcesrv_imessaging_context(dce_call->conn);
356 struct dcesrv_handle *h;
357 struct drsuapi_bind_state *b_state;
358 struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
361 enum security_user_level security_level;
363 DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
366 if (r->in.level != 1) {
367 DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
368 return WERR_DS_DRA_INVALID_PARAMETER;
370 req = &r->in.req.req1;
371 werr = drs_security_access_check(b_state->sam_ctx,
373 session_info->security_token,
375 GUID_DRS_MANAGE_TOPOLOGY);
377 if (!W_ERROR_IS_OK(werr)) {
381 security_level = security_session_user_level(session_info, NULL);
382 if (security_level < SECURITY_ADMINISTRATOR) {
383 /* check that they are using an DSA objectGUID that they own */
384 ret = dsdb_validate_dsa_guid(b_state->sam_ctx,
386 &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]);
387 if (ret != LDB_SUCCESS) {
388 DEBUG(0,(__location__ ": Refusing DsReplicaUpdateRefs for sid %s with GUID %s\n",
389 dom_sid_string(mem_ctx,
390 &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
391 GUID_string(mem_ctx, &req->dest_dsa_guid)));
392 return WERR_DS_DRA_ACCESS_DENIED;
396 werr = drsuapi_UpdateRefs(imsg_ctx,
403 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsReplicaUpdateRefs, NDR_BOTH, r);