d526f4558a522c92ef7e96ae920f0597ed6f46c5
[nivanova/samba-autobuild/.git] / source4 / dsdb / repl / drepl_out_helpers.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    DSDB replication service helper function for outgoing traffic
4    
5    Copyright (C) Stefan Metzmacher 2007
6     
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19    
20 */
21
22 #include "includes.h"
23 #include "dsdb/samdb/samdb.h"
24 #include "auth/auth.h"
25 #include "smbd/service.h"
26 #include "lib/events/events.h"
27 #include "dsdb/repl/drepl_service.h"
28 #include <ldb_errors.h>
29 #include "../lib/util/dlinklist.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "librpc/gen_ndr/ndr_drsuapi.h"
32 #include "librpc/gen_ndr/ndr_drsblobs.h"
33 #include "libcli/composite/composite.h"
34 #include "auth/gensec/gensec.h"
35 #include "param/param.h"
36 #include "../lib/util/tevent_ntstatus.h"
37 #include "libcli/security/security.h"
38
39 struct dreplsrv_out_drsuapi_state {
40         struct tevent_context *ev;
41
42         struct dreplsrv_out_connection *conn;
43
44         struct dreplsrv_drsuapi_connection *drsuapi;
45
46         struct drsuapi_DsBindInfoCtr bind_info_ctr;
47         struct drsuapi_DsBind bind_r;
48 };
49
50 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq);
51
52 struct tevent_req *dreplsrv_out_drsuapi_send(TALLOC_CTX *mem_ctx,
53                                              struct tevent_context *ev,
54                                              struct dreplsrv_out_connection *conn)
55 {
56         struct tevent_req *req;
57         struct dreplsrv_out_drsuapi_state *state;
58         struct composite_context *creq;
59
60         req = tevent_req_create(mem_ctx, &state,
61                                 struct dreplsrv_out_drsuapi_state);
62         if (req == NULL) {
63                 return NULL;
64         }
65
66         state->ev       = ev;
67         state->conn     = conn;
68         state->drsuapi  = conn->drsuapi;
69
70         if (state->drsuapi != NULL) {
71                 struct dcerpc_binding_handle *b =
72                         state->drsuapi->pipe->binding_handle;
73                 bool is_connected = dcerpc_binding_handle_is_connected(b);
74
75                 if (is_connected) {
76                         tevent_req_done(req);
77                         return tevent_req_post(req, ev);
78                 }
79
80                 TALLOC_FREE(conn->drsuapi);
81         }
82
83         state->drsuapi = talloc_zero(state, struct dreplsrv_drsuapi_connection);
84         if (tevent_req_nomem(state->drsuapi, req)) {
85                 return tevent_req_post(req, ev);
86         }
87
88         creq = dcerpc_pipe_connect_b_send(state, conn->binding, &ndr_table_drsuapi,
89                                           conn->service->system_session_info->credentials,
90                                           ev, conn->service->task->lp_ctx);
91         if (tevent_req_nomem(creq, req)) {
92                 return tevent_req_post(req, ev);
93         }
94         composite_continue(NULL, creq, dreplsrv_out_drsuapi_connect_done, req);
95
96         return req;
97 }
98
99 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req *subreq);
100
101 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq)
102 {
103         struct tevent_req *req = talloc_get_type(creq->async.private_data,
104                                                  struct tevent_req);
105         struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
106                                                    struct dreplsrv_out_drsuapi_state);
107         NTSTATUS status;
108         struct tevent_req *subreq;
109
110         status = dcerpc_pipe_connect_b_recv(creq,
111                                             state->drsuapi,
112                                             &state->drsuapi->pipe);
113         if (tevent_req_nterror(req, status)) {
114                 return;
115         }
116
117         state->drsuapi->drsuapi_handle = state->drsuapi->pipe->binding_handle;
118
119         status = gensec_session_key(state->drsuapi->pipe->conn->security_state.generic_state,
120                                     state->drsuapi,
121                                     &state->drsuapi->gensec_skey);
122         if (tevent_req_nterror(req, status)) {
123                 return;
124         }
125
126         state->bind_info_ctr.length             = 28;
127         state->bind_info_ctr.info.info28        = state->conn->service->bind_info28;
128
129         state->bind_r.in.bind_guid = &state->conn->service->ntds_guid;
130         state->bind_r.in.bind_info = &state->bind_info_ctr;
131         state->bind_r.out.bind_handle = &state->drsuapi->bind_handle;
132
133         subreq = dcerpc_drsuapi_DsBind_r_send(state,
134                                               state->ev,
135                                               state->drsuapi->drsuapi_handle,
136                                               &state->bind_r);
137         if (tevent_req_nomem(subreq, req)) {
138                 return;
139         }
140         tevent_req_set_callback(subreq, dreplsrv_out_drsuapi_bind_done, req);
141 }
142
143 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req *subreq)
144 {
145         struct tevent_req *req = tevent_req_callback_data(subreq,
146                                  struct tevent_req);
147         struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
148                                                    struct dreplsrv_out_drsuapi_state);
149         NTSTATUS status;
150
151         status = dcerpc_drsuapi_DsBind_r_recv(subreq, state);
152         TALLOC_FREE(subreq);
153         if (tevent_req_nterror(req, status)) {
154                 return;
155         }
156
157         if (!W_ERROR_IS_OK(state->bind_r.out.result)) {
158                 status = werror_to_ntstatus(state->bind_r.out.result);
159                 tevent_req_nterror(req, status);
160                 return;
161         }
162
163         ZERO_STRUCT(state->drsuapi->remote_info28);
164         if (state->bind_r.out.bind_info) {
165                 struct drsuapi_DsBindInfo28 *info28;
166                 info28 = &state->drsuapi->remote_info28;
167
168                 switch (state->bind_r.out.bind_info->length) {
169                 case 24: {
170                         struct drsuapi_DsBindInfo24 *info24;
171                         info24 = &state->bind_r.out.bind_info->info.info24;
172
173                         info28->supported_extensions    = info24->supported_extensions;
174                         info28->site_guid               = info24->site_guid;
175                         info28->pid                     = info24->pid;
176                         info28->repl_epoch              = 0;
177                         break;
178                 }
179                 case 28: {
180                         *info28 = state->bind_r.out.bind_info->info.info28;
181                         break;
182                 }
183                 case 32: {
184                         struct drsuapi_DsBindInfo32 *info32;
185                         info32 = &state->bind_r.out.bind_info->info.info32;
186
187                         info28->supported_extensions    = info32->supported_extensions;
188                         info28->site_guid               = info32->site_guid;
189                         info28->pid                     = info32->pid;
190                         info28->repl_epoch              = info32->repl_epoch;
191                         break;
192                 }
193                 case 48: {
194                         struct drsuapi_DsBindInfo48 *info48;
195                         info48 = &state->bind_r.out.bind_info->info.info48;
196
197                         info28->supported_extensions    = info48->supported_extensions;
198                         info28->site_guid               = info48->site_guid;
199                         info28->pid                     = info48->pid;
200                         info28->repl_epoch              = info48->repl_epoch;
201                         break;
202                 }
203                 case 52: {
204                         struct drsuapi_DsBindInfo52 *info52;
205                         info52 = &state->bind_r.out.bind_info->info.info52;
206
207                         info28->supported_extensions    = info52->supported_extensions;
208                         info28->site_guid               = info52->site_guid;
209                         info28->pid                     = info52->pid;
210                         info28->repl_epoch              = info52->repl_epoch;
211                         break;
212                 }
213                 default:
214                         DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
215                                 state->bind_r.out.bind_info->length));
216                         break;
217                 }
218         }
219
220         tevent_req_done(req);
221 }
222
223 NTSTATUS dreplsrv_out_drsuapi_recv(struct tevent_req *req)
224 {
225         struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
226                                                    struct dreplsrv_out_drsuapi_state);
227         NTSTATUS status;
228
229         if (tevent_req_is_nterror(req, &status)) {
230                 tevent_req_received(req);
231                 return status;
232         }
233
234         state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
235
236         tevent_req_received(req);
237         return NT_STATUS_OK;
238 }
239
240 struct dreplsrv_op_pull_source_state {
241         struct tevent_context *ev;
242         struct dreplsrv_out_operation *op;
243         void *ndr_struct_ptr;
244         /*
245          * Used when we have to re-try with a different NC, eg for
246          * EXOP retry or to get a current schema first
247          */
248         struct dreplsrv_partition_source_dsa *source_dsa_retry;
249         enum drsuapi_DsExtendedOperation extended_op_retry;
250         bool retry_started;
251 };
252
253 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
254
255 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
256                                                 struct tevent_context *ev,
257                                                 struct dreplsrv_out_operation *op)
258 {
259         struct tevent_req *req;
260         struct dreplsrv_op_pull_source_state *state;
261         struct tevent_req *subreq;
262
263         req = tevent_req_create(mem_ctx, &state,
264                                 struct dreplsrv_op_pull_source_state);
265         if (req == NULL) {
266                 return NULL;
267         }
268         state->ev = ev;
269         state->op = op;
270
271         subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
272         if (tevent_req_nomem(subreq, req)) {
273                 return tevent_req_post(req, ev);
274         }
275         tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
276
277         return req;
278 }
279
280 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
281
282 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
283 {
284         struct tevent_req *req = tevent_req_callback_data(subreq,
285                                  struct tevent_req);
286         NTSTATUS status;
287
288         status = dreplsrv_out_drsuapi_recv(subreq);
289         TALLOC_FREE(subreq);
290         if (tevent_req_nterror(req, status)) {
291                 return;
292         }
293
294         dreplsrv_op_pull_source_get_changes_trigger(req);
295 }
296
297 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
298
299 /*
300   get a RODC partial attribute set for a replication call
301  */
302 static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
303                                                         TALLOC_CTX *mem_ctx,
304                                                         struct drsuapi_DsPartialAttributeSet **_pas,
305                                                         struct drsuapi_DsReplicaOIDMapping_Ctr **pfm,
306                                                         bool for_schema)
307 {
308         struct drsuapi_DsPartialAttributeSet *pas;
309         struct dsdb_schema *schema;
310         uint32_t i;
311
312         pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
313         NT_STATUS_HAVE_NO_MEMORY(pas);
314
315         schema = dsdb_get_schema(service->samdb, NULL);
316
317         pas->version = 1;
318         pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
319         if (pas->attids == NULL) {
320                 TALLOC_FREE(pas);
321                 return NT_STATUS_NO_MEMORY;
322         }
323
324         for (i=0; i<schema->num_attributes; i++) {
325                 struct dsdb_attribute *a;
326                 a = schema->attributes_by_attributeID_id[i];
327                 if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
328                         continue;
329                 }
330                 if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
331                         continue;
332                 }
333                 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
334                 pas->num_attids++;
335         }
336
337         pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
338         if (pas->attids == NULL) {
339                 TALLOC_FREE(pas);
340                 return NT_STATUS_NO_MEMORY;
341         }
342
343         *_pas = pas;
344
345         if (pfm != NULL) {
346                 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
347         }
348
349         return NT_STATUS_OK;
350 }
351
352
353 /*
354   get a GC partial attribute set for a replication call
355  */
356 static NTSTATUS dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service *service,
357                                                       TALLOC_CTX *mem_ctx,
358                                                       struct drsuapi_DsPartialAttributeSet **_pas,
359                                                       struct drsuapi_DsReplicaOIDMapping_Ctr **pfm)
360 {
361         struct drsuapi_DsPartialAttributeSet *pas;
362         struct dsdb_schema *schema;
363         uint32_t i;
364
365         pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
366         NT_STATUS_HAVE_NO_MEMORY(pas);
367
368         schema = dsdb_get_schema(service->samdb, NULL);
369
370         pas->version = 1;
371         pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
372         if (pas->attids == NULL) {
373                 TALLOC_FREE(pas);
374                 return NT_STATUS_NO_MEMORY;
375         }
376
377         for (i=0; i<schema->num_attributes; i++) {
378                 struct dsdb_attribute *a;
379                 a = schema->attributes_by_attributeID_id[i];
380                 if (a->isMemberOfPartialAttributeSet) {
381                         pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, false);
382                         pas->num_attids++;
383                 }
384         }
385
386         pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
387         if (pas->attids == NULL) {
388                 TALLOC_FREE(pas);
389                 return NT_STATUS_NO_MEMORY;
390         }
391
392         *_pas = pas;
393
394         if (pfm != NULL) {
395                 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
396         }
397
398         return NT_STATUS_OK;
399 }
400
401 /*
402   convert from one udv format to the other
403  */
404 static WERROR udv_convert(TALLOC_CTX *mem_ctx,
405                           const struct replUpToDateVectorCtr2 *udv,
406                           struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
407 {
408         uint32_t i;
409
410         udv_ex->version = 2;
411         udv_ex->reserved1 = 0;
412         udv_ex->reserved2 = 0;
413         udv_ex->count = udv->count;
414         udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
415         W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
416
417         for (i=0; i<udv->count; i++) {
418                 udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
419                 udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
420         }
421
422         return WERR_OK;
423 }
424
425
426 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
427 {
428         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
429                                                       struct dreplsrv_op_pull_source_state);
430         struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
431         struct dreplsrv_service *service = state->op->service;
432         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
433         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
434         struct drsuapi_DsGetNCChanges *r;
435         struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
436         struct tevent_req *subreq;
437         struct drsuapi_DsPartialAttributeSet *pas = NULL;
438         NTSTATUS status;
439         uint32_t replica_flags;
440         struct drsuapi_DsReplicaHighWaterMark highwatermark;
441         struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
442         struct drsuapi_DsReplicaOIDMapping_Ctr *mappings = NULL;
443
444         r = talloc(state, struct drsuapi_DsGetNCChanges);
445         if (tevent_req_nomem(r, req)) {
446                 return;
447         }
448
449         r->out.level_out = talloc(r, uint32_t);
450         if (tevent_req_nomem(r->out.level_out, req)) {
451                 return;
452         }
453         r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
454         if (tevent_req_nomem(r->in.req, req)) {
455                 return;
456         }
457         r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
458         if (tevent_req_nomem(r->out.ctr, req)) {
459                 return;
460         }
461
462         if (partition->uptodatevector.count != 0 &&
463             partition->uptodatevector_ex.count == 0) {
464                 WERROR werr;
465                 werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
466                 if (!W_ERROR_IS_OK(werr)) {
467                         DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
468                                  ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
469                         tevent_req_nterror(req, werror_to_ntstatus(werr));
470                         return;
471                 }
472         }
473
474         if (partition->uptodatevector_ex.count == 0) {
475                 uptodateness_vector = NULL;
476         } else {
477                 uptodateness_vector = &partition->uptodatevector_ex;
478         }
479
480         replica_flags = rf1->replica_flags;
481         highwatermark = rf1->highwatermark;
482
483         if (state->op->options & DRSUAPI_DRS_GET_ANC) {
484                 replica_flags |= DRSUAPI_DRS_GET_ANC;
485         }
486
487         if (state->op->options & DRSUAPI_DRS_SYNC_FORCED) {
488                 replica_flags |= DRSUAPI_DRS_SYNC_FORCED;
489         }
490
491         if (partition->partial_replica) {
492                 status = dreplsrv_get_gc_partial_attribute_set(service, r,
493                                                                &pas,
494                                                                &mappings);
495                 if (!NT_STATUS_IS_OK(status)) {
496                         DEBUG(0,(__location__ ": Failed to construct GC partial attribute set : %s\n", nt_errstr(status)));
497                         tevent_req_nterror(req, status);
498                         return;
499                 }
500                 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
501         } else if (partition->rodc_replica || state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
502                 bool for_schema = false;
503                 if (ldb_dn_compare_base(schema_dn, partition->dn) == 0) {
504                         for_schema = true;
505                 }
506                 status = dreplsrv_get_rodc_partial_attribute_set(service, r,
507                                                                  &pas,
508                                                                  &mappings,
509                                                                  for_schema);
510                 if (!NT_STATUS_IS_OK(status)) {
511                         DEBUG(0,(__location__ ": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status)));
512                         tevent_req_nterror(req, status);
513                         return;
514                 }
515                 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
516                 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
517                         replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
518                 } else {
519                         replica_flags |= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
520                 }
521         }
522         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
523                 /*
524                  * If it's an exop never set the ADD_REF even if it's in
525                  * repsFrom flags.
526                  */
527                 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
528         }
529
530         /* is this a full resync of all objects? */
531         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
532                 ZERO_STRUCT(highwatermark);
533                 /* clear the FULL_SYNC_NOW option for subsequent
534                    stages of the replication cycle */
535                 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
536                 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
537                 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
538         }
539         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
540                 uptodateness_vector = NULL;
541         }
542
543         r->in.bind_handle       = &drsuapi->bind_handle;
544         if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
545                 r->in.level                             = 8;
546                 r->in.req->req8.destination_dsa_guid    = service->ntds_guid;
547                 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
548                 r->in.req->req8.naming_context          = &partition->nc;
549                 r->in.req->req8.highwatermark           = highwatermark;
550                 r->in.req->req8.uptodateness_vector     = uptodateness_vector;
551                 r->in.req->req8.replica_flags           = replica_flags;
552                 r->in.req->req8.max_object_count        = 133;
553                 r->in.req->req8.max_ndr_size            = 1336811;
554                 r->in.req->req8.extended_op             = state->op->extended_op;
555                 r->in.req->req8.fsmo_info               = state->op->fsmo_info;
556                 r->in.req->req8.partial_attribute_set   = pas;
557                 r->in.req->req8.partial_attribute_set_ex= NULL;
558                 r->in.req->req8.mapping_ctr.num_mappings= mappings == NULL ? 0 : mappings->num_mappings;
559                 r->in.req->req8.mapping_ctr.mappings    = mappings == NULL ? NULL : mappings->mappings;
560         } else {
561                 r->in.level                             = 5;
562                 r->in.req->req5.destination_dsa_guid    = service->ntds_guid;
563                 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
564                 r->in.req->req5.naming_context          = &partition->nc;
565                 r->in.req->req5.highwatermark           = highwatermark;
566                 r->in.req->req5.uptodateness_vector     = uptodateness_vector;
567                 r->in.req->req5.replica_flags           = replica_flags;
568                 r->in.req->req5.max_object_count        = 133;
569                 r->in.req->req5.max_ndr_size            = 1336770;
570                 r->in.req->req5.extended_op             = state->op->extended_op;
571                 r->in.req->req5.fsmo_info               = state->op->fsmo_info;
572         }
573
574 #if 0
575         NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
576 #endif
577
578         state->ndr_struct_ptr = r;
579         subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
580                                                       state->ev,
581                                                       drsuapi->drsuapi_handle,
582                                                       r);
583         if (tevent_req_nomem(subreq, req)) {
584                 return;
585         }
586         tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
587 }
588
589 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
590                                                           struct drsuapi_DsGetNCChanges *r,
591                                                           uint32_t ctr_level,
592                                                           struct drsuapi_DsGetNCChangesCtr1 *ctr1,
593                                                           struct drsuapi_DsGetNCChangesCtr6 *ctr6);
594
595 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
596 {
597         struct tevent_req *req = tevent_req_callback_data(subreq,
598                                  struct tevent_req);
599         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
600                                                       struct dreplsrv_op_pull_source_state);
601         NTSTATUS status;
602         struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
603                                            struct drsuapi_DsGetNCChanges);
604         uint32_t ctr_level = 0;
605         struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
606         struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
607         enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
608         state->ndr_struct_ptr = NULL;
609
610         status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
611         TALLOC_FREE(subreq);
612         if (tevent_req_nterror(req, status)) {
613                 return;
614         }
615
616         if (!W_ERROR_IS_OK(r->out.result)) {
617                 status = werror_to_ntstatus(r->out.result);
618                 tevent_req_nterror(req, status);
619                 return;
620         }
621
622         if (*r->out.level_out == 1) {
623                 ctr_level = 1;
624                 ctr1 = &r->out.ctr->ctr1;
625         } else if (*r->out.level_out == 2 &&
626                    r->out.ctr->ctr2.mszip1.ts) {
627                 ctr_level = 1;
628                 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
629         } else if (*r->out.level_out == 6) {
630                 ctr_level = 6;
631                 ctr6 = &r->out.ctr->ctr6;
632         } else if (*r->out.level_out == 7 &&
633                    r->out.ctr->ctr7.level == 6 &&
634                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
635                    r->out.ctr->ctr7.ctr.mszip6.ts) {
636                 ctr_level = 6;
637                 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
638         } else if (*r->out.level_out == 7 &&
639                    r->out.ctr->ctr7.level == 6 &&
640                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
641                    r->out.ctr->ctr7.ctr.xpress6.ts) {
642                 ctr_level = 6;
643                 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
644         } else {
645                 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
646                 tevent_req_nterror(req, status);
647                 return;
648         }
649
650         if (!ctr1 && !ctr6) {
651                 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
652                 tevent_req_nterror(req, status);
653                 return;
654         }
655
656         if (ctr_level == 6) {
657                 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
658                         status = werror_to_ntstatus(ctr6->drs_error);
659                         tevent_req_nterror(req, status);
660                         return;
661                 }
662                 extended_ret = ctr6->extended_ret;
663         }
664
665         if (ctr_level == 1) {
666                 extended_ret = ctr1->extended_ret;
667         }
668
669         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
670                 state->op->extended_ret = extended_ret;
671
672                 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
673                         status = NT_STATUS_UNSUCCESSFUL;
674                         tevent_req_nterror(req, status);
675                         return;
676                 }
677         }
678
679         dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
680 }
681
682 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
683
684 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
685                                                           struct drsuapi_DsGetNCChanges *r,
686                                                           uint32_t ctr_level,
687                                                           struct drsuapi_DsGetNCChangesCtr1 *ctr1,
688                                                            struct drsuapi_DsGetNCChangesCtr6 *ctr6)
689 {
690         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
691                                                       struct dreplsrv_op_pull_source_state);
692         struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
693         struct dreplsrv_service *service = state->op->service;
694         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
695         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
696         struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
697         struct dsdb_schema *schema;
698         struct dsdb_schema *working_schema = NULL;
699         const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
700         uint32_t object_count;
701         struct drsuapi_DsReplicaObjectListItemEx *first_object;
702         uint32_t linked_attributes_count;
703         struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
704         const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
705         struct dsdb_extended_replicated_objects *objects;
706         bool more_data = false;
707         WERROR status;
708         NTSTATUS nt_status;
709         uint32_t dsdb_repl_flags = 0;
710         struct ldb_dn *nc_root = NULL;
711         int ret;
712
713         switch (ctr_level) {
714         case 1:
715                 mapping_ctr                     = &ctr1->mapping_ctr;
716                 object_count                    = ctr1->object_count;
717                 first_object                    = ctr1->first_object;
718                 linked_attributes_count         = 0;
719                 linked_attributes               = NULL;
720                 rf1.source_dsa_obj_guid         = ctr1->source_dsa_guid;
721                 rf1.source_dsa_invocation_id    = ctr1->source_dsa_invocation_id;
722                 rf1.highwatermark               = ctr1->new_highwatermark;
723                 uptodateness_vector             = NULL; /* TODO: map it */
724                 more_data                       = ctr1->more_data;
725                 break;
726         case 6:
727                 mapping_ctr                     = &ctr6->mapping_ctr;
728                 object_count                    = ctr6->object_count;
729                 first_object                    = ctr6->first_object;
730                 linked_attributes_count         = ctr6->linked_attributes_count;
731                 linked_attributes               = ctr6->linked_attributes;
732                 rf1.source_dsa_obj_guid         = ctr6->source_dsa_guid;
733                 rf1.source_dsa_invocation_id    = ctr6->source_dsa_invocation_id;
734                 rf1.highwatermark               = ctr6->new_highwatermark;
735                 uptodateness_vector             = ctr6->uptodateness_vector;
736                 more_data                       = ctr6->more_data;
737                 break;
738         default:
739                 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
740                 tevent_req_nterror(req, nt_status);
741                 return;
742         }
743
744         schema = dsdb_get_schema(service->samdb, state);
745         if (!schema) {
746                 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
747                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
748                 return;
749         }
750
751         /*
752          * Decide what working schema to use for object conversion.
753          * We won't need a working schema for empty replicas sent.
754          */
755         if (first_object) {
756                 bool is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
757                 if (is_schema) {
758                         /* create working schema to convert objects with */
759                         status = dsdb_repl_make_working_schema(service->samdb,
760                                                                schema,
761                                                                mapping_ctr,
762                                                                object_count,
763                                                                first_object,
764                                                                &drsuapi->gensec_skey,
765                                                                state, &working_schema);
766                         if (!W_ERROR_IS_OK(status)) {
767                                 DEBUG(0,("Failed to create working schema: %s\n",
768                                          win_errstr(status)));
769                                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
770                                 return;
771                         }
772                 }
773         }
774
775         if (partition->partial_replica || partition->rodc_replica) {
776                 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
777         }
778         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
779                 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
780         }
781         if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
782                 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
783         }
784
785         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
786                 ret = dsdb_find_nc_root(service->samdb, partition,
787                                         partition->dn, &nc_root);
788                 if (ret != LDB_SUCCESS) {
789                         DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
790                                  ldb_dn_get_linearized(partition->dn)));
791                         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
792                         return;
793                 }
794         } else {
795                 nc_root = partition->dn;
796         }
797
798         status = dsdb_replicated_objects_convert(service->samdb,
799                                                  working_schema ? working_schema : schema,
800                                                  nc_root,
801                                                  mapping_ctr,
802                                                  object_count,
803                                                  first_object,
804                                                  linked_attributes_count,
805                                                  linked_attributes,
806                                                  &rf1,
807                                                  uptodateness_vector,
808                                                  &drsuapi->gensec_skey,
809                                                  dsdb_repl_flags,
810                                                  state, &objects);
811
812         if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)) {
813                 struct dreplsrv_partition *p;
814
815                 if (state->retry_started) {
816                         nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
817                         DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
818                                   win_errstr(status), nt_errstr(nt_status)));
819                         tevent_req_nterror(req, nt_status);
820                         return;
821                 }
822
823                 /*
824                  * Change info sync or extended operation into a fetch
825                  * of the schema partition, so we get all the schema
826                  * objects we need.
827                  *
828                  * We don't want to re-do the remote exop,
829                  * unless it was REPL_SECRET so we set the
830                  * fallback operation to just be a fetch of
831                  * the relevent partition.
832                  */
833
834
835                 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
836                         state->extended_op_retry = state->op->extended_op;
837                 } else {
838                         state->extended_op_retry = DRSUAPI_EXOP_NONE;
839                 }
840                 state->op->extended_op = DRSUAPI_EXOP_NONE;
841
842                 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
843                         state->source_dsa_retry = state->op->source_dsa;
844                 } else {
845                         status = dreplsrv_partition_find_for_nc(service,
846                                                                 NULL, NULL,
847                                                                 ldb_dn_get_linearized(nc_root),
848                                                                 &p);
849                         if (!W_ERROR_IS_OK(status)) {
850                                 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
851                                           ldb_dn_get_linearized(nc_root),
852                                           win_errstr(status)));
853                                 nt_status = werror_to_ntstatus(status);
854                                 tevent_req_nterror(req, nt_status);
855                                 return;
856                         }
857                         status = dreplsrv_partition_source_dsa_by_guid(p,
858                                                                        &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
859                                                                        &state->source_dsa_retry);
860
861                         if (!W_ERROR_IS_OK(status)) {
862                                 struct GUID_txt_buf str;
863                                 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
864                                           ldb_dn_get_linearized(nc_root),
865                                           GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
866                                           win_errstr(status)));
867                                 nt_status = werror_to_ntstatus(status);
868                                 tevent_req_nterror(req, nt_status);
869                                 return;
870                         }
871                 }
872
873                 /* Find schema naming context to be synchronized first */
874                 status = dreplsrv_partition_find_for_nc(service,
875                                                         NULL, NULL,
876                                                         ldb_dn_get_linearized(schema_dn),
877                                                         &p);
878                 if (!W_ERROR_IS_OK(status)) {
879                         DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
880                                   win_errstr(status)));
881                         nt_status = werror_to_ntstatus(status);
882                         tevent_req_nterror(req, nt_status);
883                         return;
884                 }
885
886                 status = dreplsrv_partition_source_dsa_by_guid(p,
887                                                                &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
888                                                                &state->op->source_dsa);
889                 if (!W_ERROR_IS_OK(status)) {
890                         struct GUID_txt_buf str;
891                         DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
892                                   ldb_dn_get_linearized(schema_dn),
893                                   GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
894                                   win_errstr(status)));
895                         nt_status = werror_to_ntstatus(status);
896                         tevent_req_nterror(req, nt_status);
897                         return;
898                 }
899                 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
900
901                 state->retry_started = true;
902                 dreplsrv_op_pull_source_get_changes_trigger(req);
903                 return;
904
905         } else if (!W_ERROR_IS_OK(status)) {
906                 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
907                 DEBUG(0,("Failed to convert objects: %s/%s\n",
908                           win_errstr(status), nt_errstr(nt_status)));
909                 tevent_req_nterror(req, nt_status);
910                 return;
911         }
912
913         status = dsdb_replicated_objects_commit(service->samdb,
914                                                 working_schema,
915                                                 objects,
916                                                 &state->op->source_dsa->notify_uSN);
917         talloc_free(objects);
918
919         if (!W_ERROR_IS_OK(status)) {
920
921                 /*
922                  * If we failed to apply the records due to a missing
923                  * parent, try again after asking for the parent
924                  * records first.  Because we don't update the
925                  * highwatermark, we start this part of the cycle
926                  * again.
927                  */
928                 if (((state->op->options & DRSUAPI_DRS_GET_ANC) == 0)
929                     && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
930                         state->op->options |= DRSUAPI_DRS_GET_ANC;
931                         DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
932                         dreplsrv_op_pull_source_get_changes_trigger(req);
933                         return;
934                 } else if (((state->op->options & DRSUAPI_DRS_GET_ANC))
935                            && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
936                         DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
937                         nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
938                 } else {
939                         nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
940                 }
941                 DEBUG(0,("Failed to commit objects: %s/%s\n",
942                           win_errstr(status), nt_errstr(nt_status)));
943                 tevent_req_nterror(req, nt_status);
944                 return;
945         }
946
947         if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
948                 /* if it applied fine, we need to update the highwatermark */
949                 *state->op->source_dsa->repsFrom1 = rf1;
950         }
951
952         /* we don't need this maybe very large structure anymore */
953         TALLOC_FREE(r);
954
955         if (more_data) {
956                 dreplsrv_op_pull_source_get_changes_trigger(req);
957                 return;
958         }
959
960         /*
961          * If we had to divert via doing some other thing, such as
962          * pulling the schema, then go back and do the original
963          * operation once we are done.
964          */
965         if (state->source_dsa_retry != NULL) {
966                 state->op->source_dsa = state->source_dsa_retry;
967                 state->op->extended_op = state->extended_op_retry;
968                 state->source_dsa_retry = NULL;
969                 dreplsrv_op_pull_source_get_changes_trigger(req);
970                 return;
971         }
972
973         if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
974             state->op->service->am_rodc) {
975                 /*
976                   we don't do the UpdateRefs for extended ops or if we
977                   are a RODC
978                  */
979                 tevent_req_done(req);
980                 return;
981         }
982
983         /* now we need to update the repsTo record for this partition
984            on the server. These records are initially established when
985            we join the domain, but they quickly expire.  We do it here
986            so we can use the already established DRSUAPI pipe
987         */
988         dreplsrv_update_refs_trigger(req);
989 }
990
991 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
992
993 /*
994   send a UpdateRefs request to refresh our repsTo record on the server
995  */
996 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
997 {
998         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
999                                                       struct dreplsrv_op_pull_source_state);
1000         struct dreplsrv_service *service = state->op->service;
1001         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
1002         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
1003         struct drsuapi_DsReplicaUpdateRefs *r;
1004         char *ntds_dns_name;
1005         struct tevent_req *subreq;
1006
1007         r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
1008         if (tevent_req_nomem(r, req)) {
1009                 return;
1010         }
1011
1012         ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
1013         if (tevent_req_nomem(ntds_dns_name, req)) {
1014                 talloc_free(r);
1015                 return;
1016         }
1017
1018         r->in.bind_handle       = &drsuapi->bind_handle;
1019         r->in.level             = 1;
1020         r->in.req.req1.naming_context     = &partition->nc;
1021         r->in.req.req1.dest_dsa_dns_name  = ntds_dns_name;
1022         r->in.req.req1.dest_dsa_guid      = service->ntds_guid;
1023         r->in.req.req1.options            = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
1024         if (!service->am_rodc) {
1025                 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
1026         }
1027
1028         state->ndr_struct_ptr = r;
1029         subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
1030                                                            state->ev,
1031                                                            drsuapi->drsuapi_handle,
1032                                                            r);
1033         if (tevent_req_nomem(subreq, req)) {
1034                 talloc_free(r);
1035                 return;
1036         }
1037         tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
1038 }
1039
1040 /*
1041   receive a UpdateRefs reply
1042  */
1043 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1044 {
1045         struct tevent_req *req = tevent_req_callback_data(subreq,
1046                                  struct tevent_req);
1047         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1048                                                       struct dreplsrv_op_pull_source_state);
1049         struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1050                                                                 struct drsuapi_DsReplicaUpdateRefs);
1051         NTSTATUS status;
1052
1053         state->ndr_struct_ptr = NULL;
1054
1055         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1056         TALLOC_FREE(subreq);
1057         if (!NT_STATUS_IS_OK(status)) {
1058                 DEBUG(0,("UpdateRefs failed with %s\n", 
1059                          nt_errstr(status)));
1060                 tevent_req_nterror(req, status);
1061                 return;
1062         }
1063
1064         if (!W_ERROR_IS_OK(r->out.result)) {
1065                 status = werror_to_ntstatus(r->out.result);
1066                 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1067                          win_errstr(r->out.result),
1068                          nt_errstr(status),
1069                          r->in.req.req1.dest_dsa_dns_name,
1070                          r->in.req.req1.naming_context->dn));
1071                 /*
1072                  * TODO we are currently not sending the
1073                  * DsReplicaUpdateRefs at the correct moment,
1074                  * we do it just after a GetNcChanges which is
1075                  * not always correct.
1076                  * Especially when another DC is trying to demote
1077                  * it will sends us a DsReplicaSync that will trigger a getNcChanges
1078                  * this call will succeed but the DsRecplicaUpdateRefs that we send
1079                  * just after will not because the DC is in a demote state and
1080                  * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1081                  * answer to the DsReplicaSync with a non OK status, the other DC
1082                  * will stop the demote due to this error.
1083                  * In order to cope with this we will for the moment concider
1084                  * a DS_DRA_BUSY not as an error.
1085                  * It's not ideal but it should not have a too huge impact for
1086                  * running production as this error otherwise never happen and
1087                  * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1088                  */
1089                 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1090                         tevent_req_nterror(req, status);
1091                         return;
1092                 }
1093         }
1094
1095         DEBUG(4,("UpdateRefs OK for %s %s\n", 
1096                  r->in.req.req1.dest_dsa_dns_name,
1097                  r->in.req.req1.naming_context->dn));
1098
1099         tevent_req_done(req);
1100 }
1101
1102 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1103 {
1104         NTSTATUS status;
1105
1106         if (tevent_req_is_nterror(req, &status)) {
1107                 tevent_req_received(req);
1108                 return ntstatus_to_werror(status);
1109         }
1110
1111         tevent_req_received(req);
1112         return WERR_OK;
1113 }
1114