Line data Source code
1 : /*
2 : Unix SMB/CIFS Implementation.
3 :
4 : DSDB replication service periodic notification handling
5 :
6 : Copyright (C) Andrew Tridgell 2009
7 : based on drepl_periodic
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 :
22 : */
23 :
24 : #include "includes.h"
25 : #include "lib/events/events.h"
26 : #include "dsdb/samdb/samdb.h"
27 : #include "auth/auth.h"
28 : #include "samba/service.h"
29 : #include "dsdb/repl/drepl_service.h"
30 : #include <ldb_errors.h>
31 : #include "../lib/util/dlinklist.h"
32 : #include "librpc/gen_ndr/ndr_misc.h"
33 : #include "librpc/gen_ndr/ndr_drsuapi.h"
34 : #include "librpc/gen_ndr/ndr_drsblobs.h"
35 : #include "libcli/composite/composite.h"
36 : #include "../lib/util/tevent_ntstatus.h"
37 :
38 : #undef DBGC_CLASS
39 : #define DBGC_CLASS DBGC_DRS_REPL
40 :
41 :
42 : struct dreplsrv_op_notify_state {
43 : struct tevent_context *ev;
44 : struct dreplsrv_notify_operation *op;
45 : void *ndr_struct_ptr;
46 : };
47 :
48 : static void dreplsrv_op_notify_connect_done(struct tevent_req *subreq);
49 :
50 : /*
51 : start the ReplicaSync async call
52 : */
53 5611 : static struct tevent_req *dreplsrv_op_notify_send(TALLOC_CTX *mem_ctx,
54 : struct tevent_context *ev,
55 : struct dreplsrv_notify_operation *op)
56 : {
57 0 : struct tevent_req *req;
58 0 : struct dreplsrv_op_notify_state *state;
59 0 : struct tevent_req *subreq;
60 :
61 5611 : req = tevent_req_create(mem_ctx, &state,
62 : struct dreplsrv_op_notify_state);
63 5611 : if (req == NULL) {
64 0 : return NULL;
65 : }
66 5611 : state->ev = ev;
67 5611 : state->op = op;
68 :
69 5611 : subreq = dreplsrv_out_drsuapi_send(state,
70 : ev,
71 5611 : op->source_dsa->conn);
72 5611 : if (tevent_req_nomem(subreq, req)) {
73 0 : return tevent_req_post(req, ev);
74 : }
75 5611 : tevent_req_set_callback(subreq, dreplsrv_op_notify_connect_done, req);
76 :
77 5611 : return req;
78 : }
79 :
80 : static void dreplsrv_op_notify_replica_sync_trigger(struct tevent_req *req);
81 :
82 5610 : static void dreplsrv_op_notify_connect_done(struct tevent_req *subreq)
83 : {
84 5610 : struct tevent_req *req = tevent_req_callback_data(subreq,
85 : struct tevent_req);
86 0 : NTSTATUS status;
87 :
88 5610 : status = dreplsrv_out_drsuapi_recv(subreq);
89 5610 : TALLOC_FREE(subreq);
90 5610 : if (tevent_req_nterror(req, status)) {
91 4474 : return;
92 : }
93 :
94 1136 : dreplsrv_op_notify_replica_sync_trigger(req);
95 : }
96 :
97 : static void dreplsrv_op_notify_replica_sync_done(struct tevent_req *subreq);
98 :
99 1136 : static void dreplsrv_op_notify_replica_sync_trigger(struct tevent_req *req)
100 : {
101 0 : struct dreplsrv_op_notify_state *state =
102 1136 : tevent_req_data(req,
103 : struct dreplsrv_op_notify_state);
104 1136 : struct dreplsrv_partition *partition = state->op->source_dsa->partition;
105 1136 : struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
106 0 : struct drsuapi_DsReplicaSync *r;
107 0 : struct tevent_req *subreq;
108 :
109 1136 : r = talloc_zero(state, struct drsuapi_DsReplicaSync);
110 1136 : if (tevent_req_nomem(r, req)) {
111 0 : return;
112 : }
113 1136 : r->in.req = talloc_zero(r, union drsuapi_DsReplicaSyncRequest);
114 1136 : if (tevent_req_nomem(r, req)) {
115 0 : return;
116 : }
117 1136 : r->in.bind_handle = &drsuapi->bind_handle;
118 1136 : r->in.level = 1;
119 1136 : r->in.req->req1.naming_context = &partition->nc;
120 1136 : r->in.req->req1.source_dsa_guid = state->op->service->ntds_guid;
121 1136 : r->in.req->req1.options =
122 : DRSUAPI_DRS_ASYNC_OP |
123 : DRSUAPI_DRS_UPDATE_NOTIFICATION |
124 : DRSUAPI_DRS_WRIT_REP;
125 :
126 1136 : if (state->op->is_urgent) {
127 316 : r->in.req->req1.options |= DRSUAPI_DRS_SYNC_URGENT;
128 : }
129 :
130 1136 : state->ndr_struct_ptr = r;
131 :
132 1136 : if (DEBUGLVL(10)) {
133 0 : NDR_PRINT_IN_DEBUG(drsuapi_DsReplicaSync, r);
134 : }
135 :
136 1136 : subreq = dcerpc_drsuapi_DsReplicaSync_r_send(state,
137 : state->ev,
138 : drsuapi->drsuapi_handle,
139 : r);
140 1136 : if (tevent_req_nomem(subreq, req)) {
141 0 : return;
142 : }
143 1136 : tevent_req_set_callback(subreq, dreplsrv_op_notify_replica_sync_done, req);
144 : }
145 :
146 1136 : static void dreplsrv_op_notify_replica_sync_done(struct tevent_req *subreq)
147 : {
148 0 : struct tevent_req *req =
149 1136 : tevent_req_callback_data(subreq,
150 : struct tevent_req);
151 0 : struct dreplsrv_op_notify_state *state =
152 1136 : tevent_req_data(req,
153 : struct dreplsrv_op_notify_state);
154 1136 : struct drsuapi_DsReplicaSync *r = talloc_get_type(state->ndr_struct_ptr,
155 : struct drsuapi_DsReplicaSync);
156 0 : NTSTATUS status;
157 :
158 1136 : state->ndr_struct_ptr = NULL;
159 :
160 1136 : status = dcerpc_drsuapi_DsReplicaSync_r_recv(subreq, r);
161 1136 : TALLOC_FREE(subreq);
162 1136 : if (tevent_req_nterror(req, status)) {
163 0 : return;
164 : }
165 :
166 1136 : if (!W_ERROR_IS_OK(r->out.result)) {
167 0 : status = werror_to_ntstatus(r->out.result);
168 0 : tevent_req_nterror(req, status);
169 0 : return;
170 : }
171 :
172 1136 : tevent_req_done(req);
173 : }
174 :
175 5610 : static NTSTATUS dreplsrv_op_notify_recv(struct tevent_req *req)
176 : {
177 5610 : return tevent_req_simple_recv_ntstatus(req);
178 : }
179 :
180 : /*
181 : called when a notify operation has completed
182 : */
183 5610 : static void dreplsrv_notify_op_callback(struct tevent_req *subreq)
184 : {
185 0 : struct dreplsrv_notify_operation *op =
186 5610 : tevent_req_callback_data(subreq,
187 : struct dreplsrv_notify_operation);
188 0 : NTSTATUS status;
189 5610 : struct dreplsrv_service *s = op->service;
190 0 : WERROR werr;
191 :
192 5610 : status = dreplsrv_op_notify_recv(subreq);
193 5610 : werr = ntstatus_to_werror(status);
194 5610 : TALLOC_FREE(subreq);
195 5610 : if (!NT_STATUS_IS_OK(status)) {
196 4474 : DBG_INFO("dreplsrv_notify: Failed to send DsReplicaSync to %s for %s - %s : %s\n",
197 : op->source_dsa->repsFrom1->other_info->dns_name,
198 : ldb_dn_get_linearized(op->source_dsa->partition->dn),
199 : nt_errstr(status), win_errstr(werr));
200 : } else {
201 1136 : DBG_INFO("dreplsrv_notify: DsReplicaSync successfully sent to %s\n",
202 : op->source_dsa->repsFrom1->other_info->dns_name);
203 1136 : op->source_dsa->notify_uSN = op->uSN;
204 : }
205 :
206 5610 : drepl_reps_update(s, "repsTo", op->source_dsa->partition->dn,
207 5610 : &op->source_dsa->repsFrom1->source_dsa_obj_guid,
208 : werr);
209 :
210 5610 : talloc_free(op);
211 5610 : s->ops.n_current = NULL;
212 5610 : dreplsrv_run_pending_ops(s);
213 5610 : }
214 :
215 : /*
216 : run any pending replica sync calls
217 : */
218 5660 : void dreplsrv_notify_run_ops(struct dreplsrv_service *s)
219 : {
220 0 : struct dreplsrv_notify_operation *op;
221 0 : struct tevent_req *subreq;
222 :
223 5660 : if (s->ops.n_current || s->ops.current) {
224 : /* if there's still one running, we're done */
225 49 : return;
226 : }
227 :
228 5611 : if (!s->ops.notifies) {
229 : /* if there're no pending operations, we're done */
230 0 : return;
231 : }
232 :
233 5611 : op = s->ops.notifies;
234 5611 : s->ops.n_current = op;
235 5611 : DLIST_REMOVE(s->ops.notifies, op);
236 :
237 5611 : subreq = dreplsrv_op_notify_send(op, s->task->event_ctx, op);
238 5611 : if (!subreq) {
239 0 : DBG_ERR("dreplsrv_notify_run_ops: dreplsrv_op_notify_send[%s][%s] - no memory\n",
240 : op->source_dsa->repsFrom1->other_info->dns_name,
241 : ldb_dn_get_linearized(op->source_dsa->partition->dn));
242 0 : return;
243 : }
244 5611 : tevent_req_set_callback(subreq, dreplsrv_notify_op_callback, op);
245 5611 : DBG_INFO("started DsReplicaSync for %s to %s\n",
246 : ldb_dn_get_linearized(op->source_dsa->partition->dn),
247 : op->source_dsa->repsFrom1->other_info->dns_name);
248 : }
249 :
250 :
251 : /*
252 : find a source_dsa for a given guid
253 : */
254 17548 : static struct dreplsrv_partition_source_dsa *dreplsrv_find_notify_dsa(struct dreplsrv_partition *p,
255 : struct GUID *guid)
256 : {
257 0 : struct dreplsrv_partition_source_dsa *s;
258 :
259 : /* first check the sources list */
260 23497 : for (s=p->sources; s; s=s->next) {
261 12845 : if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
262 6896 : return s;
263 : }
264 : }
265 :
266 : /* then the notifies list */
267 21471 : for (s=p->notifies; s; s=s->next) {
268 21471 : if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
269 10652 : return s;
270 : }
271 : }
272 0 : return NULL;
273 : }
274 :
275 :
276 : /*
277 : schedule a replicaSync message
278 : */
279 5743 : static WERROR dreplsrv_schedule_notify_sync(struct dreplsrv_service *service,
280 : struct dreplsrv_partition *p,
281 : struct repsFromToBlob *reps,
282 : TALLOC_CTX *mem_ctx,
283 : uint64_t uSN,
284 : bool is_urgent,
285 : uint32_t replica_flags)
286 : {
287 0 : struct dreplsrv_notify_operation *op;
288 0 : struct dreplsrv_partition_source_dsa *s;
289 :
290 5743 : s = dreplsrv_find_notify_dsa(p, &reps->ctr.ctr1.source_dsa_obj_guid);
291 5743 : if (s == NULL) {
292 0 : DBG_ERR("Unable to find source_dsa for %s\n",
293 : GUID_string(mem_ctx, &reps->ctr.ctr1.source_dsa_obj_guid));
294 0 : return WERR_DS_UNAVAILABLE;
295 : }
296 :
297 : /* first try to find an existing notify operation */
298 34865 : for (op = service->ops.notifies; op; op = op->next) {
299 29250 : if (op->source_dsa != s) {
300 29122 : continue;
301 : }
302 :
303 128 : if (op->is_urgent != is_urgent) {
304 0 : continue;
305 : }
306 :
307 128 : if (op->replica_flags != replica_flags) {
308 0 : continue;
309 : }
310 :
311 128 : if (op->uSN < uSN) {
312 36 : op->uSN = uSN;
313 : }
314 :
315 : /* reuse the notify operation, as it's not yet started */
316 128 : return WERR_OK;
317 : }
318 :
319 5615 : op = talloc_zero(mem_ctx, struct dreplsrv_notify_operation);
320 5615 : W_ERROR_HAVE_NO_MEMORY(op);
321 :
322 5615 : op->service = service;
323 5615 : op->source_dsa = s;
324 5615 : op->uSN = uSN;
325 5615 : op->is_urgent = is_urgent;
326 5615 : op->replica_flags = replica_flags;
327 5615 : op->schedule_time = time(NULL);
328 :
329 5615 : DLIST_ADD_END(service->ops.notifies, op);
330 5615 : talloc_steal(service, op);
331 5615 : return WERR_OK;
332 : }
333 :
334 : /*
335 : see if a partition has a hugher uSN than what is in the repsTo and
336 : if so then send a DsReplicaSync
337 : */
338 49911 : static WERROR dreplsrv_notify_check(struct dreplsrv_service *s,
339 : struct dreplsrv_partition *p,
340 : TALLOC_CTX *mem_ctx)
341 : {
342 49911 : uint32_t count=0;
343 440 : struct repsFromToBlob *reps;
344 440 : WERROR werr;
345 440 : uint64_t uSNHighest;
346 440 : uint64_t uSNUrgent;
347 440 : uint32_t i;
348 440 : int ret;
349 :
350 49911 : werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsTo", &reps, &count);
351 49911 : if (!W_ERROR_IS_OK(werr)) {
352 0 : DBG_ERR("Failed to load repsTo for %s\n",
353 : ldb_dn_get_linearized(p->dn));
354 0 : return werr;
355 : }
356 :
357 : /* loads the partition uSNHighest and uSNUrgent */
358 49911 : ret = dsdb_load_partition_usn(s->samdb, p->dn, &uSNHighest, &uSNUrgent);
359 49911 : if (ret != LDB_SUCCESS || uSNHighest == 0) {
360 : /* nothing to do */
361 0 : return WERR_OK;
362 : }
363 :
364 : /* see if any of our partners need some of our objects */
365 61716 : for (i=0; i<count; i++) {
366 0 : struct dreplsrv_partition_source_dsa *sdsa;
367 0 : uint32_t replica_flags;
368 11805 : sdsa = dreplsrv_find_notify_dsa(p, &reps[i].ctr.ctr1.source_dsa_obj_guid);
369 11805 : replica_flags = reps[i].ctr.ctr1.replica_flags;
370 11805 : if (sdsa == NULL) continue;
371 11805 : if (sdsa->notify_uSN < uSNHighest) {
372 : /* we need to tell this partner to replicate
373 : with us */
374 5743 : bool is_urgent = sdsa->notify_uSN < uSNUrgent;
375 :
376 : /* check if urgent replication is needed */
377 5743 : werr = dreplsrv_schedule_notify_sync(s, p, &reps[i], mem_ctx,
378 : uSNHighest, is_urgent, replica_flags);
379 5743 : if (!W_ERROR_IS_OK(werr)) {
380 0 : DBG_ERR("Failed to setup notify to %s for %s\n",
381 : reps[i].ctr.ctr1.other_info->dns_name,
382 : ldb_dn_get_linearized(p->dn));
383 0 : return werr;
384 : }
385 5743 : DBG_DEBUG("queued DsReplicaSync for %s to %s "
386 : "(urgent=%s) uSN=%llu:%llu\n",
387 : ldb_dn_get_linearized(p->dn),
388 : reps[i].ctr.ctr1.other_info->dns_name,
389 : is_urgent?"true":"false",
390 : (unsigned long long)sdsa->notify_uSN,
391 : (unsigned long long)uSNHighest);
392 : }
393 : }
394 :
395 49911 : return WERR_OK;
396 : }
397 :
398 : /*
399 : see if any of the partitions have changed, and if so then send a
400 : DsReplicaSync to all the replica partners in the repsTo object
401 : */
402 10247 : static WERROR dreplsrv_notify_check_all(struct dreplsrv_service *s, TALLOC_CTX *mem_ctx)
403 : {
404 88 : WERROR status;
405 88 : struct dreplsrv_partition *p;
406 :
407 60158 : for (p = s->partitions; p; p = p->next) {
408 49911 : status = dreplsrv_notify_check(s, p, mem_ctx);
409 49911 : W_ERROR_NOT_OK_RETURN(status);
410 : }
411 :
412 10247 : return WERR_OK;
413 : }
414 :
415 : static void dreplsrv_notify_run(struct dreplsrv_service *service);
416 :
417 10247 : static void dreplsrv_notify_handler_te(struct tevent_context *ev, struct tevent_timer *te,
418 : struct timeval t, void *ptr)
419 : {
420 10247 : struct dreplsrv_service *service = talloc_get_type(ptr, struct dreplsrv_service);
421 88 : WERROR status;
422 :
423 10247 : service->notify.te = NULL;
424 :
425 10247 : dreplsrv_notify_run(service);
426 :
427 10247 : status = dreplsrv_notify_schedule(service, service->notify.interval);
428 10247 : if (!W_ERROR_IS_OK(status)) {
429 0 : task_server_terminate(service->task, win_errstr(status), false);
430 0 : return;
431 : }
432 : }
433 :
434 10305 : WERROR dreplsrv_notify_schedule(struct dreplsrv_service *service, uint32_t next_interval)
435 : {
436 90 : TALLOC_CTX *tmp_mem;
437 90 : struct tevent_timer *new_te;
438 90 : struct timeval next_time;
439 :
440 : /* prevent looping */
441 10305 : if (next_interval == 0) next_interval = 1;
442 :
443 10305 : next_time = timeval_current_ofs(next_interval, 50);
444 :
445 10305 : if (service->notify.te) {
446 : /*
447 : * if the timestamp of the new event is higher,
448 : * as current next we don't need to reschedule
449 : */
450 0 : if (timeval_compare(&next_time, &service->notify.next_event) > 0) {
451 0 : return WERR_OK;
452 : }
453 : }
454 :
455 : /* reset the next scheduled timestamp */
456 10305 : service->notify.next_event = next_time;
457 :
458 10305 : new_te = tevent_add_timer(service->task->event_ctx, service,
459 : service->notify.next_event,
460 : dreplsrv_notify_handler_te, service);
461 10305 : W_ERROR_HAVE_NO_MEMORY(new_te);
462 :
463 10305 : tmp_mem = talloc_new(service);
464 10305 : DBG_DEBUG("dreplsrv_notify_schedule(%u) %sscheduled for: %s\n",
465 : next_interval,
466 : (service->notify.te?"re":""),
467 : nt_time_string(tmp_mem, timeval_to_nttime(&next_time)));
468 10305 : talloc_free(tmp_mem);
469 :
470 10305 : talloc_free(service->notify.te);
471 10305 : service->notify.te = new_te;
472 :
473 10305 : return WERR_OK;
474 : }
475 :
476 10247 : static void dreplsrv_notify_run(struct dreplsrv_service *service)
477 : {
478 88 : TALLOC_CTX *mem_ctx;
479 :
480 10247 : mem_ctx = talloc_new(service);
481 10247 : dreplsrv_notify_check_all(service, mem_ctx);
482 10247 : talloc_free(mem_ctx);
483 :
484 10247 : dreplsrv_run_pending_ops(service);
485 10247 : }
|