079edc8ba46bb1dea83483011926d336d47901e1
[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                 /*
523                  * As per MS-DRSR:
524                  *
525                  * 4.1.10.4
526                  * Client Behavior When Sending the IDL_DRSGetNCChanges Request
527                  *
528                  * 4.1.10.4.1
529                  * ReplicateNCRequestMsg
530                  */
531                 replica_flags |= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP;
532         } else {
533                 replica_flags |= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP;
534         }
535
536         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
537                 /*
538                  * If it's an exop never set the ADD_REF even if it's in
539                  * repsFrom flags.
540                  */
541                 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
542         }
543
544         /* is this a full resync of all objects? */
545         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
546                 ZERO_STRUCT(highwatermark);
547                 /* clear the FULL_SYNC_NOW option for subsequent
548                    stages of the replication cycle */
549                 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
550                 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
551                 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
552         }
553         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
554                 uptodateness_vector = NULL;
555         }
556
557         r->in.bind_handle       = &drsuapi->bind_handle;
558         if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
559                 r->in.level                             = 8;
560                 r->in.req->req8.destination_dsa_guid    = service->ntds_guid;
561                 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
562                 r->in.req->req8.naming_context          = &partition->nc;
563                 r->in.req->req8.highwatermark           = highwatermark;
564                 r->in.req->req8.uptodateness_vector     = uptodateness_vector;
565                 r->in.req->req8.replica_flags           = replica_flags;
566                 r->in.req->req8.max_object_count        = 133;
567                 r->in.req->req8.max_ndr_size            = 1336811;
568                 r->in.req->req8.extended_op             = state->op->extended_op;
569                 r->in.req->req8.fsmo_info               = state->op->fsmo_info;
570                 r->in.req->req8.partial_attribute_set   = pas;
571                 r->in.req->req8.partial_attribute_set_ex= NULL;
572                 r->in.req->req8.mapping_ctr.num_mappings= mappings == NULL ? 0 : mappings->num_mappings;
573                 r->in.req->req8.mapping_ctr.mappings    = mappings == NULL ? NULL : mappings->mappings;
574         } else {
575                 r->in.level                             = 5;
576                 r->in.req->req5.destination_dsa_guid    = service->ntds_guid;
577                 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
578                 r->in.req->req5.naming_context          = &partition->nc;
579                 r->in.req->req5.highwatermark           = highwatermark;
580                 r->in.req->req5.uptodateness_vector     = uptodateness_vector;
581                 r->in.req->req5.replica_flags           = replica_flags;
582                 r->in.req->req5.max_object_count        = 133;
583                 r->in.req->req5.max_ndr_size            = 1336770;
584                 r->in.req->req5.extended_op             = state->op->extended_op;
585                 r->in.req->req5.fsmo_info               = state->op->fsmo_info;
586         }
587
588 #if 0
589         NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
590 #endif
591
592         state->ndr_struct_ptr = r;
593         subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
594                                                       state->ev,
595                                                       drsuapi->drsuapi_handle,
596                                                       r);
597         if (tevent_req_nomem(subreq, req)) {
598                 return;
599         }
600         tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
601 }
602
603 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
604                                                           struct drsuapi_DsGetNCChanges *r,
605                                                           uint32_t ctr_level,
606                                                           struct drsuapi_DsGetNCChangesCtr1 *ctr1,
607                                                           struct drsuapi_DsGetNCChangesCtr6 *ctr6);
608
609 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
610 {
611         struct tevent_req *req = tevent_req_callback_data(subreq,
612                                  struct tevent_req);
613         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
614                                                       struct dreplsrv_op_pull_source_state);
615         NTSTATUS status;
616         struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
617                                            struct drsuapi_DsGetNCChanges);
618         uint32_t ctr_level = 0;
619         struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
620         struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
621         enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
622         state->ndr_struct_ptr = NULL;
623
624         status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
625         TALLOC_FREE(subreq);
626         if (tevent_req_nterror(req, status)) {
627                 return;
628         }
629
630         if (!W_ERROR_IS_OK(r->out.result)) {
631                 status = werror_to_ntstatus(r->out.result);
632                 tevent_req_nterror(req, status);
633                 return;
634         }
635
636         if (*r->out.level_out == 1) {
637                 ctr_level = 1;
638                 ctr1 = &r->out.ctr->ctr1;
639         } else if (*r->out.level_out == 2 &&
640                    r->out.ctr->ctr2.mszip1.ts) {
641                 ctr_level = 1;
642                 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
643         } else if (*r->out.level_out == 6) {
644                 ctr_level = 6;
645                 ctr6 = &r->out.ctr->ctr6;
646         } else if (*r->out.level_out == 7 &&
647                    r->out.ctr->ctr7.level == 6 &&
648                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
649                    r->out.ctr->ctr7.ctr.mszip6.ts) {
650                 ctr_level = 6;
651                 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
652         } else if (*r->out.level_out == 7 &&
653                    r->out.ctr->ctr7.level == 6 &&
654                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
655                    r->out.ctr->ctr7.ctr.xpress6.ts) {
656                 ctr_level = 6;
657                 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
658         } else {
659                 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
660                 tevent_req_nterror(req, status);
661                 return;
662         }
663
664         if (!ctr1 && !ctr6) {
665                 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
666                 tevent_req_nterror(req, status);
667                 return;
668         }
669
670         if (ctr_level == 6) {
671                 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
672                         status = werror_to_ntstatus(ctr6->drs_error);
673                         tevent_req_nterror(req, status);
674                         return;
675                 }
676                 extended_ret = ctr6->extended_ret;
677         }
678
679         if (ctr_level == 1) {
680                 extended_ret = ctr1->extended_ret;
681         }
682
683         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
684                 state->op->extended_ret = extended_ret;
685
686                 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
687                         status = NT_STATUS_UNSUCCESSFUL;
688                         tevent_req_nterror(req, status);
689                         return;
690                 }
691         }
692
693         dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
694 }
695
696 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
697
698 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
699                                                           struct drsuapi_DsGetNCChanges *r,
700                                                           uint32_t ctr_level,
701                                                           struct drsuapi_DsGetNCChangesCtr1 *ctr1,
702                                                            struct drsuapi_DsGetNCChangesCtr6 *ctr6)
703 {
704         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
705                                                       struct dreplsrv_op_pull_source_state);
706         struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
707         struct dreplsrv_service *service = state->op->service;
708         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
709         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
710         struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
711         struct dsdb_schema *schema;
712         struct dsdb_schema *working_schema = NULL;
713         const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
714         uint32_t object_count;
715         struct drsuapi_DsReplicaObjectListItemEx *first_object;
716         uint32_t linked_attributes_count;
717         struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
718         const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
719         struct dsdb_extended_replicated_objects *objects;
720         bool more_data = false;
721         WERROR status;
722         NTSTATUS nt_status;
723         uint32_t dsdb_repl_flags = 0;
724         struct ldb_dn *nc_root = NULL;
725         int ret;
726
727         switch (ctr_level) {
728         case 1:
729                 mapping_ctr                     = &ctr1->mapping_ctr;
730                 object_count                    = ctr1->object_count;
731                 first_object                    = ctr1->first_object;
732                 linked_attributes_count         = 0;
733                 linked_attributes               = NULL;
734                 rf1.source_dsa_obj_guid         = ctr1->source_dsa_guid;
735                 rf1.source_dsa_invocation_id    = ctr1->source_dsa_invocation_id;
736                 rf1.highwatermark               = ctr1->new_highwatermark;
737                 uptodateness_vector             = NULL; /* TODO: map it */
738                 more_data                       = ctr1->more_data;
739                 break;
740         case 6:
741                 mapping_ctr                     = &ctr6->mapping_ctr;
742                 object_count                    = ctr6->object_count;
743                 first_object                    = ctr6->first_object;
744                 linked_attributes_count         = ctr6->linked_attributes_count;
745                 linked_attributes               = ctr6->linked_attributes;
746                 rf1.source_dsa_obj_guid         = ctr6->source_dsa_guid;
747                 rf1.source_dsa_invocation_id    = ctr6->source_dsa_invocation_id;
748                 rf1.highwatermark               = ctr6->new_highwatermark;
749                 uptodateness_vector             = ctr6->uptodateness_vector;
750                 more_data                       = ctr6->more_data;
751                 break;
752         default:
753                 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
754                 tevent_req_nterror(req, nt_status);
755                 return;
756         }
757
758         schema = dsdb_get_schema(service->samdb, state);
759         if (!schema) {
760                 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
761                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
762                 return;
763         }
764
765         /*
766          * Decide what working schema to use for object conversion.
767          * We won't need a working schema for empty replicas sent.
768          */
769         if (first_object) {
770                 bool is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
771                 if (is_schema) {
772                         /* create working schema to convert objects with */
773                         status = dsdb_repl_make_working_schema(service->samdb,
774                                                                schema,
775                                                                mapping_ctr,
776                                                                object_count,
777                                                                first_object,
778                                                                &drsuapi->gensec_skey,
779                                                                state, &working_schema);
780                         if (!W_ERROR_IS_OK(status)) {
781                                 DEBUG(0,("Failed to create working schema: %s\n",
782                                          win_errstr(status)));
783                                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
784                                 return;
785                         }
786                 }
787         }
788
789         if (partition->partial_replica || partition->rodc_replica) {
790                 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
791         }
792         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
793                 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
794         }
795         if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
796                 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
797         }
798
799         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
800                 ret = dsdb_find_nc_root(service->samdb, partition,
801                                         partition->dn, &nc_root);
802                 if (ret != LDB_SUCCESS) {
803                         DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
804                                  ldb_dn_get_linearized(partition->dn)));
805                         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
806                         return;
807                 }
808         } else {
809                 nc_root = partition->dn;
810         }
811
812         status = dsdb_replicated_objects_convert(service->samdb,
813                                                  working_schema ? working_schema : schema,
814                                                  nc_root,
815                                                  mapping_ctr,
816                                                  object_count,
817                                                  first_object,
818                                                  linked_attributes_count,
819                                                  linked_attributes,
820                                                  &rf1,
821                                                  uptodateness_vector,
822                                                  &drsuapi->gensec_skey,
823                                                  dsdb_repl_flags,
824                                                  state, &objects);
825
826         if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)) {
827                 struct dreplsrv_partition *p;
828
829                 if (state->retry_started) {
830                         nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
831                         DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
832                                   win_errstr(status), nt_errstr(nt_status)));
833                         tevent_req_nterror(req, nt_status);
834                         return;
835                 }
836
837                 /*
838                  * Change info sync or extended operation into a fetch
839                  * of the schema partition, so we get all the schema
840                  * objects we need.
841                  *
842                  * We don't want to re-do the remote exop,
843                  * unless it was REPL_SECRET so we set the
844                  * fallback operation to just be a fetch of
845                  * the relevent partition.
846                  */
847
848
849                 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
850                         state->extended_op_retry = state->op->extended_op;
851                 } else {
852                         state->extended_op_retry = DRSUAPI_EXOP_NONE;
853                 }
854                 state->op->extended_op = DRSUAPI_EXOP_NONE;
855
856                 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
857                         state->source_dsa_retry = state->op->source_dsa;
858                 } else {
859                         status = dreplsrv_partition_find_for_nc(service,
860                                                                 NULL, NULL,
861                                                                 ldb_dn_get_linearized(nc_root),
862                                                                 &p);
863                         if (!W_ERROR_IS_OK(status)) {
864                                 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
865                                           ldb_dn_get_linearized(nc_root),
866                                           win_errstr(status)));
867                                 nt_status = werror_to_ntstatus(status);
868                                 tevent_req_nterror(req, nt_status);
869                                 return;
870                         }
871                         status = dreplsrv_partition_source_dsa_by_guid(p,
872                                                                        &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
873                                                                        &state->source_dsa_retry);
874
875                         if (!W_ERROR_IS_OK(status)) {
876                                 struct GUID_txt_buf str;
877                                 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
878                                           ldb_dn_get_linearized(nc_root),
879                                           GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
880                                           win_errstr(status)));
881                                 nt_status = werror_to_ntstatus(status);
882                                 tevent_req_nterror(req, nt_status);
883                                 return;
884                         }
885                 }
886
887                 /* Find schema naming context to be synchronized first */
888                 status = dreplsrv_partition_find_for_nc(service,
889                                                         NULL, NULL,
890                                                         ldb_dn_get_linearized(schema_dn),
891                                                         &p);
892                 if (!W_ERROR_IS_OK(status)) {
893                         DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
894                                   win_errstr(status)));
895                         nt_status = werror_to_ntstatus(status);
896                         tevent_req_nterror(req, nt_status);
897                         return;
898                 }
899
900                 status = dreplsrv_partition_source_dsa_by_guid(p,
901                                                                &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
902                                                                &state->op->source_dsa);
903                 if (!W_ERROR_IS_OK(status)) {
904                         struct GUID_txt_buf str;
905                         DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
906                                   ldb_dn_get_linearized(schema_dn),
907                                   GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
908                                   win_errstr(status)));
909                         nt_status = werror_to_ntstatus(status);
910                         tevent_req_nterror(req, nt_status);
911                         return;
912                 }
913                 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
914
915                 state->retry_started = true;
916                 dreplsrv_op_pull_source_get_changes_trigger(req);
917                 return;
918
919         } else if (!W_ERROR_IS_OK(status)) {
920                 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
921                 DEBUG(0,("Failed to convert objects: %s/%s\n",
922                           win_errstr(status), nt_errstr(nt_status)));
923                 tevent_req_nterror(req, nt_status);
924                 return;
925         }
926
927         status = dsdb_replicated_objects_commit(service->samdb,
928                                                 working_schema,
929                                                 objects,
930                                                 &state->op->source_dsa->notify_uSN);
931         talloc_free(objects);
932
933         if (!W_ERROR_IS_OK(status)) {
934
935                 /*
936                  * If we failed to apply the records due to a missing
937                  * parent, try again after asking for the parent
938                  * records first.  Because we don't update the
939                  * highwatermark, we start this part of the cycle
940                  * again.
941                  */
942                 if (((state->op->options & DRSUAPI_DRS_GET_ANC) == 0)
943                     && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
944                         state->op->options |= DRSUAPI_DRS_GET_ANC;
945                         DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
946                         dreplsrv_op_pull_source_get_changes_trigger(req);
947                         return;
948                 } else if (((state->op->options & DRSUAPI_DRS_GET_ANC))
949                            && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
950                         DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
951                         nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
952                 } else {
953                         nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
954                 }
955                 DEBUG(0,("Failed to commit objects: %s/%s\n",
956                           win_errstr(status), nt_errstr(nt_status)));
957                 tevent_req_nterror(req, nt_status);
958                 return;
959         }
960
961         if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
962                 /* if it applied fine, we need to update the highwatermark */
963                 *state->op->source_dsa->repsFrom1 = rf1;
964         }
965
966         /* we don't need this maybe very large structure anymore */
967         TALLOC_FREE(r);
968
969         if (more_data) {
970                 dreplsrv_op_pull_source_get_changes_trigger(req);
971                 return;
972         }
973
974         /*
975          * If we had to divert via doing some other thing, such as
976          * pulling the schema, then go back and do the original
977          * operation once we are done.
978          */
979         if (state->source_dsa_retry != NULL) {
980                 state->op->source_dsa = state->source_dsa_retry;
981                 state->op->extended_op = state->extended_op_retry;
982                 state->source_dsa_retry = NULL;
983                 dreplsrv_op_pull_source_get_changes_trigger(req);
984                 return;
985         }
986
987         if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
988             state->op->service->am_rodc) {
989                 /*
990                   we don't do the UpdateRefs for extended ops or if we
991                   are a RODC
992                  */
993                 tevent_req_done(req);
994                 return;
995         }
996
997         /* now we need to update the repsTo record for this partition
998            on the server. These records are initially established when
999            we join the domain, but they quickly expire.  We do it here
1000            so we can use the already established DRSUAPI pipe
1001         */
1002         dreplsrv_update_refs_trigger(req);
1003 }
1004
1005 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
1006
1007 /*
1008   send a UpdateRefs request to refresh our repsTo record on the server
1009  */
1010 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
1011 {
1012         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1013                                                       struct dreplsrv_op_pull_source_state);
1014         struct dreplsrv_service *service = state->op->service;
1015         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
1016         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
1017         struct drsuapi_DsReplicaUpdateRefs *r;
1018         char *ntds_dns_name;
1019         struct tevent_req *subreq;
1020
1021         r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
1022         if (tevent_req_nomem(r, req)) {
1023                 return;
1024         }
1025
1026         ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
1027         if (tevent_req_nomem(ntds_dns_name, req)) {
1028                 talloc_free(r);
1029                 return;
1030         }
1031
1032         r->in.bind_handle       = &drsuapi->bind_handle;
1033         r->in.level             = 1;
1034         r->in.req.req1.naming_context     = &partition->nc;
1035         r->in.req.req1.dest_dsa_dns_name  = ntds_dns_name;
1036         r->in.req.req1.dest_dsa_guid      = service->ntds_guid;
1037         r->in.req.req1.options            = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
1038         if (!service->am_rodc) {
1039                 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
1040         }
1041
1042         state->ndr_struct_ptr = r;
1043         subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
1044                                                            state->ev,
1045                                                            drsuapi->drsuapi_handle,
1046                                                            r);
1047         if (tevent_req_nomem(subreq, req)) {
1048                 talloc_free(r);
1049                 return;
1050         }
1051         tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
1052 }
1053
1054 /*
1055   receive a UpdateRefs reply
1056  */
1057 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1058 {
1059         struct tevent_req *req = tevent_req_callback_data(subreq,
1060                                  struct tevent_req);
1061         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1062                                                       struct dreplsrv_op_pull_source_state);
1063         struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1064                                                                 struct drsuapi_DsReplicaUpdateRefs);
1065         NTSTATUS status;
1066
1067         state->ndr_struct_ptr = NULL;
1068
1069         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1070         TALLOC_FREE(subreq);
1071         if (!NT_STATUS_IS_OK(status)) {
1072                 DEBUG(0,("UpdateRefs failed with %s\n", 
1073                          nt_errstr(status)));
1074                 tevent_req_nterror(req, status);
1075                 return;
1076         }
1077
1078         if (!W_ERROR_IS_OK(r->out.result)) {
1079                 status = werror_to_ntstatus(r->out.result);
1080                 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1081                          win_errstr(r->out.result),
1082                          nt_errstr(status),
1083                          r->in.req.req1.dest_dsa_dns_name,
1084                          r->in.req.req1.naming_context->dn));
1085                 /*
1086                  * TODO we are currently not sending the
1087                  * DsReplicaUpdateRefs at the correct moment,
1088                  * we do it just after a GetNcChanges which is
1089                  * not always correct.
1090                  * Especially when another DC is trying to demote
1091                  * it will sends us a DsReplicaSync that will trigger a getNcChanges
1092                  * this call will succeed but the DsRecplicaUpdateRefs that we send
1093                  * just after will not because the DC is in a demote state and
1094                  * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1095                  * answer to the DsReplicaSync with a non OK status, the other DC
1096                  * will stop the demote due to this error.
1097                  * In order to cope with this we will for the moment concider
1098                  * a DS_DRA_BUSY not as an error.
1099                  * It's not ideal but it should not have a too huge impact for
1100                  * running production as this error otherwise never happen and
1101                  * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1102                  */
1103                 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1104                         tevent_req_nterror(req, status);
1105                         return;
1106                 }
1107         }
1108
1109         DEBUG(4,("UpdateRefs OK for %s %s\n", 
1110                  r->in.req.req1.dest_dsa_dns_name,
1111                  r->in.req.req1.naming_context->dn));
1112
1113         tevent_req_done(req);
1114 }
1115
1116 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1117 {
1118         NTSTATUS status;
1119
1120         if (tevent_req_is_nterror(req, &status)) {
1121                 tevent_req_received(req);
1122                 return ntstatus_to_werror(status);
1123         }
1124
1125         tevent_req_received(req);
1126         return WERR_OK;
1127 }
1128