repl: Avoid use-after-free when working with the working_schema
[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
246 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
247
248 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
249                                                 struct tevent_context *ev,
250                                                 struct dreplsrv_out_operation *op)
251 {
252         struct tevent_req *req;
253         struct dreplsrv_op_pull_source_state *state;
254         struct tevent_req *subreq;
255
256         req = tevent_req_create(mem_ctx, &state,
257                                 struct dreplsrv_op_pull_source_state);
258         if (req == NULL) {
259                 return NULL;
260         }
261         state->ev = ev;
262         state->op = op;
263
264         subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
265         if (tevent_req_nomem(subreq, req)) {
266                 return tevent_req_post(req, ev);
267         }
268         tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
269
270         return req;
271 }
272
273 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
274
275 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
276 {
277         struct tevent_req *req = tevent_req_callback_data(subreq,
278                                  struct tevent_req);
279         NTSTATUS status;
280
281         status = dreplsrv_out_drsuapi_recv(subreq);
282         TALLOC_FREE(subreq);
283         if (tevent_req_nterror(req, status)) {
284                 return;
285         }
286
287         dreplsrv_op_pull_source_get_changes_trigger(req);
288 }
289
290 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
291
292 /*
293   get a RODC partial attribute set for a replication call
294  */
295 static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
296                                                         TALLOC_CTX *mem_ctx,
297                                                         struct drsuapi_DsPartialAttributeSet **_pas,
298                                                         bool for_schema)
299 {
300         struct drsuapi_DsPartialAttributeSet *pas;
301         struct dsdb_schema *schema;
302         uint32_t i;
303
304         pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
305         NT_STATUS_HAVE_NO_MEMORY(pas);
306
307         schema = dsdb_get_schema(service->samdb, NULL);
308
309         pas->version = 1;
310         pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
311         if (pas->attids == NULL) {
312                 TALLOC_FREE(pas);
313                 return NT_STATUS_NO_MEMORY;
314         }
315
316         for (i=0; i<schema->num_attributes; i++) {
317                 struct dsdb_attribute *a;
318                 a = schema->attributes_by_attributeID_id[i];
319                 if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
320                         continue;
321                 }
322                 if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
323                         continue;
324                 }
325                 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
326                 pas->num_attids++;
327         }
328
329         pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
330         if (pas->attids == NULL) {
331                 TALLOC_FREE(pas);
332                 return NT_STATUS_NO_MEMORY;
333         }
334
335         *_pas = pas;
336         return NT_STATUS_OK;
337 }
338
339
340 /*
341   get a GC partial attribute set for a replication call
342  */
343 static NTSTATUS dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service *service,
344                                                       TALLOC_CTX *mem_ctx,
345                                                       struct drsuapi_DsPartialAttributeSet **_pas)
346 {
347         struct drsuapi_DsPartialAttributeSet *pas;
348         struct dsdb_schema *schema;
349         uint32_t i;
350
351         pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
352         NT_STATUS_HAVE_NO_MEMORY(pas);
353
354         schema = dsdb_get_schema(service->samdb, NULL);
355
356         pas->version = 1;
357         pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
358         if (pas->attids == NULL) {
359                 TALLOC_FREE(pas);
360                 return NT_STATUS_NO_MEMORY;
361         }
362
363         for (i=0; i<schema->num_attributes; i++) {
364                 struct dsdb_attribute *a;
365                 a = schema->attributes_by_attributeID_id[i];
366                 if (a->isMemberOfPartialAttributeSet) {
367                         pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, false);
368                         pas->num_attids++;
369                 }
370         }
371
372         pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
373         if (pas->attids == NULL) {
374                 TALLOC_FREE(pas);
375                 return NT_STATUS_NO_MEMORY;
376         }
377
378         *_pas = pas;
379         return NT_STATUS_OK;
380 }
381
382 /*
383   convert from one udv format to the other
384  */
385 static WERROR udv_convert(TALLOC_CTX *mem_ctx,
386                           const struct replUpToDateVectorCtr2 *udv,
387                           struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
388 {
389         uint32_t i;
390
391         udv_ex->version = 2;
392         udv_ex->reserved1 = 0;
393         udv_ex->reserved2 = 0;
394         udv_ex->count = udv->count;
395         udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
396         W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
397
398         for (i=0; i<udv->count; i++) {
399                 udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
400                 udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
401         }
402
403         return WERR_OK;
404 }
405
406
407 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
408 {
409         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
410                                                       struct dreplsrv_op_pull_source_state);
411         struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
412         struct dreplsrv_service *service = state->op->service;
413         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
414         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
415         struct drsuapi_DsGetNCChanges *r;
416         struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
417         struct tevent_req *subreq;
418         struct drsuapi_DsPartialAttributeSet *pas = NULL;
419         NTSTATUS status;
420         uint32_t replica_flags;
421         struct drsuapi_DsReplicaHighWaterMark highwatermark;
422         struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
423
424         r = talloc(state, struct drsuapi_DsGetNCChanges);
425         if (tevent_req_nomem(r, req)) {
426                 return;
427         }
428
429         r->out.level_out = talloc(r, uint32_t);
430         if (tevent_req_nomem(r->out.level_out, req)) {
431                 return;
432         }
433         r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
434         if (tevent_req_nomem(r->in.req, req)) {
435                 return;
436         }
437         r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
438         if (tevent_req_nomem(r->out.ctr, req)) {
439                 return;
440         }
441
442         if (partition->uptodatevector.count != 0 &&
443             partition->uptodatevector_ex.count == 0) {
444                 WERROR werr;
445                 werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
446                 if (!W_ERROR_IS_OK(werr)) {
447                         DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
448                                  ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
449                 }
450         }
451
452         if (partition->uptodatevector_ex.count == 0) {
453                 uptodateness_vector = NULL;
454         } else {
455                 uptodateness_vector = &partition->uptodatevector_ex;
456         }
457
458         replica_flags = rf1->replica_flags;
459         highwatermark = rf1->highwatermark;
460
461         if (state->op->options & DRSUAPI_DRS_GET_ANC) {
462                 replica_flags |= DRSUAPI_DRS_GET_ANC;
463         }
464
465         if (partition->partial_replica) {
466                 status = dreplsrv_get_gc_partial_attribute_set(service, r, &pas);
467                 if (!NT_STATUS_IS_OK(status)) {
468                         DEBUG(0,(__location__ ": Failed to construct GC partial attribute set : %s\n", nt_errstr(status)));
469                         return;
470                 }
471                 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
472         } else if (partition->rodc_replica) {
473                 bool for_schema = false;
474                 if (ldb_dn_compare_base(schema_dn, partition->dn) == 0) {
475                         for_schema = true;
476                 }
477
478                 status = dreplsrv_get_rodc_partial_attribute_set(service, r, &pas, for_schema);
479                 if (!NT_STATUS_IS_OK(status)) {
480                         DEBUG(0,(__location__ ": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status)));
481                         return;
482                 }
483                 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
484                 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
485                         replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
486                 } else {
487                         replica_flags |= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
488                 }
489         }
490         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
491                 /*
492                  * If it's an exop never set the ADD_REF even if it's in
493                  * repsFrom flags.
494                  */
495                 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
496         }
497
498         /* is this a full resync of all objects? */
499         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
500                 ZERO_STRUCT(highwatermark);
501                 /* clear the FULL_SYNC_NOW option for subsequent
502                    stages of the replication cycle */
503                 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
504                 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
505                 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
506         }
507         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
508                 uptodateness_vector = NULL;
509         }
510
511         r->in.bind_handle       = &drsuapi->bind_handle;
512         if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
513                 r->in.level                             = 8;
514                 r->in.req->req8.destination_dsa_guid    = service->ntds_guid;
515                 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
516                 r->in.req->req8.naming_context          = &partition->nc;
517                 r->in.req->req8.highwatermark           = highwatermark;
518                 r->in.req->req8.uptodateness_vector     = uptodateness_vector;
519                 r->in.req->req8.replica_flags           = replica_flags;
520                 r->in.req->req8.max_object_count        = 133;
521                 r->in.req->req8.max_ndr_size            = 1336811;
522                 r->in.req->req8.extended_op             = state->op->extended_op;
523                 r->in.req->req8.fsmo_info               = state->op->fsmo_info;
524                 r->in.req->req8.partial_attribute_set   = pas;
525                 r->in.req->req8.partial_attribute_set_ex= NULL;
526                 r->in.req->req8.mapping_ctr.num_mappings= 0;
527                 r->in.req->req8.mapping_ctr.mappings    = NULL;
528         } else {
529                 r->in.level                             = 5;
530                 r->in.req->req5.destination_dsa_guid    = service->ntds_guid;
531                 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
532                 r->in.req->req5.naming_context          = &partition->nc;
533                 r->in.req->req5.highwatermark           = highwatermark;
534                 r->in.req->req5.uptodateness_vector     = uptodateness_vector;
535                 r->in.req->req5.replica_flags           = replica_flags;
536                 r->in.req->req5.max_object_count        = 133;
537                 r->in.req->req5.max_ndr_size            = 1336770;
538                 r->in.req->req5.extended_op             = state->op->extended_op;
539                 r->in.req->req5.fsmo_info               = state->op->fsmo_info;
540         }
541
542 #if 0
543         NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
544 #endif
545
546         state->ndr_struct_ptr = r;
547         subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
548                                                       state->ev,
549                                                       drsuapi->drsuapi_handle,
550                                                       r);
551         if (tevent_req_nomem(subreq, req)) {
552                 return;
553         }
554         tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
555 }
556
557 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
558                                                           struct drsuapi_DsGetNCChanges *r,
559                                                           uint32_t ctr_level,
560                                                           struct drsuapi_DsGetNCChangesCtr1 *ctr1,
561                                                           struct drsuapi_DsGetNCChangesCtr6 *ctr6);
562
563 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
564 {
565         struct tevent_req *req = tevent_req_callback_data(subreq,
566                                  struct tevent_req);
567         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
568                                                       struct dreplsrv_op_pull_source_state);
569         NTSTATUS status;
570         struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
571                                            struct drsuapi_DsGetNCChanges);
572         uint32_t ctr_level = 0;
573         struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
574         struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
575         enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
576         state->ndr_struct_ptr = NULL;
577
578         status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
579         TALLOC_FREE(subreq);
580         if (tevent_req_nterror(req, status)) {
581                 return;
582         }
583
584         if (!W_ERROR_IS_OK(r->out.result)) {
585                 status = werror_to_ntstatus(r->out.result);
586                 tevent_req_nterror(req, status);
587                 return;
588         }
589
590         if (*r->out.level_out == 1) {
591                 ctr_level = 1;
592                 ctr1 = &r->out.ctr->ctr1;
593         } else if (*r->out.level_out == 2 &&
594                    r->out.ctr->ctr2.mszip1.ts) {
595                 ctr_level = 1;
596                 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
597         } else if (*r->out.level_out == 6) {
598                 ctr_level = 6;
599                 ctr6 = &r->out.ctr->ctr6;
600         } else if (*r->out.level_out == 7 &&
601                    r->out.ctr->ctr7.level == 6 &&
602                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
603                    r->out.ctr->ctr7.ctr.mszip6.ts) {
604                 ctr_level = 6;
605                 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
606         } else if (*r->out.level_out == 7 &&
607                    r->out.ctr->ctr7.level == 6 &&
608                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
609                    r->out.ctr->ctr7.ctr.xpress6.ts) {
610                 ctr_level = 6;
611                 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
612         } else {
613                 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
614                 tevent_req_nterror(req, status);
615                 return;
616         }
617
618         if (!ctr1 && !ctr6) {
619                 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
620                 tevent_req_nterror(req, status);
621                 return;
622         }
623
624         if (ctr_level == 6) {
625                 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
626                         status = werror_to_ntstatus(ctr6->drs_error);
627                         tevent_req_nterror(req, status);
628                         return;
629                 }
630                 extended_ret = ctr6->extended_ret;
631         }
632
633         if (ctr_level == 1) {
634                 extended_ret = ctr1->extended_ret;
635         }
636
637         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
638                 state->op->extended_ret = extended_ret;
639
640                 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
641                         status = NT_STATUS_UNSUCCESSFUL;
642                         tevent_req_nterror(req, status);
643                         return;
644                 }
645         }
646
647         dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
648 }
649
650 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
651
652 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
653                                                           struct drsuapi_DsGetNCChanges *r,
654                                                           uint32_t ctr_level,
655                                                           struct drsuapi_DsGetNCChangesCtr1 *ctr1,
656                                                            struct drsuapi_DsGetNCChangesCtr6 *ctr6)
657 {
658         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
659                                                       struct dreplsrv_op_pull_source_state);
660         struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
661         struct dreplsrv_service *service = state->op->service;
662         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
663         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
664         struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
665         struct dsdb_schema *schema;
666         struct dsdb_schema *working_schema = NULL;
667         const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
668         uint32_t object_count;
669         struct drsuapi_DsReplicaObjectListItemEx *first_object;
670         uint32_t linked_attributes_count;
671         struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
672         const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
673         struct dsdb_extended_replicated_objects *objects;
674         bool more_data = false;
675         WERROR status;
676         NTSTATUS nt_status;
677         uint32_t dsdb_repl_flags = 0;
678         struct ldb_dn *nc_root = NULL;
679         int ret;
680
681         switch (ctr_level) {
682         case 1:
683                 mapping_ctr                     = &ctr1->mapping_ctr;
684                 object_count                    = ctr1->object_count;
685                 first_object                    = ctr1->first_object;
686                 linked_attributes_count         = 0;
687                 linked_attributes               = NULL;
688                 rf1.source_dsa_obj_guid         = ctr1->source_dsa_guid;
689                 rf1.source_dsa_invocation_id    = ctr1->source_dsa_invocation_id;
690                 rf1.highwatermark               = ctr1->new_highwatermark;
691                 uptodateness_vector             = NULL; /* TODO: map it */
692                 more_data                       = ctr1->more_data;
693                 break;
694         case 6:
695                 mapping_ctr                     = &ctr6->mapping_ctr;
696                 object_count                    = ctr6->object_count;
697                 first_object                    = ctr6->first_object;
698                 linked_attributes_count         = ctr6->linked_attributes_count;
699                 linked_attributes               = ctr6->linked_attributes;
700                 rf1.source_dsa_obj_guid         = ctr6->source_dsa_guid;
701                 rf1.source_dsa_invocation_id    = ctr6->source_dsa_invocation_id;
702                 rf1.highwatermark               = ctr6->new_highwatermark;
703                 uptodateness_vector             = ctr6->uptodateness_vector;
704                 more_data                       = ctr6->more_data;
705                 break;
706         default:
707                 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
708                 tevent_req_nterror(req, nt_status);
709                 return;
710         }
711
712         schema = dsdb_get_schema(service->samdb, state);
713         if (!schema) {
714                 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
715                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
716                 return;
717         }
718
719         /*
720          * Decide what working schema to use for object conversion.
721          * We won't need a working schema for empty replicas sent.
722          */
723         if (first_object) {
724                 bool is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
725                 if (is_schema) {
726                         /* create working schema to convert objects with */
727                         status = dsdb_repl_make_working_schema(service->samdb,
728                                                                schema,
729                                                                mapping_ctr,
730                                                                object_count,
731                                                                first_object,
732                                                                &drsuapi->gensec_skey,
733                                                                state, &working_schema);
734                         if (!W_ERROR_IS_OK(status)) {
735                                 DEBUG(0,("Failed to create working schema: %s\n",
736                                          win_errstr(status)));
737                                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
738                                 return;
739                         }
740                 }
741         }
742
743         if (partition->partial_replica || partition->rodc_replica) {
744                 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
745         }
746         if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
747                 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
748         }
749         if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
750                 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
751         }
752
753         if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
754                 ret = dsdb_find_nc_root(service->samdb, partition,
755                                         partition->dn, &nc_root);
756                 if (ret != LDB_SUCCESS) {
757                         DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
758                                  ldb_dn_get_linearized(partition->dn)));
759                         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
760                         return;
761                 }
762         } else {
763                 nc_root = partition->dn;
764         }
765
766         status = dsdb_replicated_objects_convert(service->samdb,
767                                                  working_schema ? working_schema : schema,
768                                                  nc_root,
769                                                  mapping_ctr,
770                                                  object_count,
771                                                  first_object,
772                                                  linked_attributes_count,
773                                                  linked_attributes,
774                                                  &rf1,
775                                                  uptodateness_vector,
776                                                  &drsuapi->gensec_skey,
777                                                  dsdb_repl_flags,
778                                                  state, &objects);
779
780         if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)
781             && state->op->source_dsa_retry == NULL) {
782                 struct dreplsrv_partition *p;
783
784                 /*
785                  * Change info sync or extended operation into a fetch
786                  * of the schema partition, so we get all the schema
787                  * objects we need.
788                  *
789                  * We don't want to re-do the remote exop,
790                  * unless it was REPL_SECRET so we set the
791                  * fallback operation to just be a fetch of
792                  * the relevent partition.
793                  */
794
795
796                 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
797                         state->op->extended_op_retry = state->op->extended_op;
798                 } else {
799                         state->op->extended_op_retry = DRSUAPI_EXOP_NONE;
800                 }
801                 state->op->extended_op = DRSUAPI_EXOP_NONE;
802
803                 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
804                         state->op->source_dsa_retry = state->op->source_dsa;
805                 } else {
806                         status = dreplsrv_partition_find_for_nc(service,
807                                                                 NULL, NULL,
808                                                                 ldb_dn_get_linearized(nc_root),
809                                                                 &p);
810                         if (!W_ERROR_IS_OK(status)) {
811                                 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
812                                           ldb_dn_get_linearized(nc_root),
813                                           win_errstr(status)));
814                                 nt_status = werror_to_ntstatus(status);
815                                 tevent_req_nterror(req, nt_status);
816                                 return;
817                         }
818                         status = dreplsrv_partition_source_dsa_by_guid(p,
819                                                                        &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
820                                                                        &state->op->source_dsa_retry);
821
822                         if (!W_ERROR_IS_OK(status)) {
823                                 struct GUID_txt_buf str;
824                                 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
825                                           ldb_dn_get_linearized(nc_root),
826                                           GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
827                                           win_errstr(status)));
828                                 nt_status = werror_to_ntstatus(status);
829                                 tevent_req_nterror(req, nt_status);
830                                 return;
831                         }
832                 }
833
834                 /* Find schmea naming context to be synchronized first */
835                 status = dreplsrv_partition_find_for_nc(service,
836                                                         NULL, NULL,
837                                                         ldb_dn_get_linearized(schema_dn),
838                                                         &p);
839                 if (!W_ERROR_IS_OK(status)) {
840                         DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
841                                   win_errstr(status)));
842                         nt_status = werror_to_ntstatus(status);
843                         tevent_req_nterror(req, nt_status);
844                         return;
845                 }
846
847                 status = dreplsrv_partition_source_dsa_by_guid(p,
848                                                                &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
849                                                                &state->op->source_dsa);
850                 if (!W_ERROR_IS_OK(status)) {
851                         struct GUID_txt_buf str;
852                         DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
853                                   ldb_dn_get_linearized(schema_dn),
854                                   GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
855                                   win_errstr(status)));
856                         nt_status = werror_to_ntstatus(status);
857                         tevent_req_nterror(req, nt_status);
858                         return;
859                 }
860                 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
861
862                 dreplsrv_op_pull_source_get_changes_trigger(req);
863                 return;
864
865         } else if (!W_ERROR_IS_OK(status)) {
866                 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
867                 DEBUG(0,("Failed to convert objects: %s/%s\n",
868                           win_errstr(status), nt_errstr(nt_status)));
869                 tevent_req_nterror(req, nt_status);
870                 return;
871         }
872
873         status = dsdb_replicated_objects_commit(service->samdb,
874                                                 working_schema,
875                                                 objects,
876                                                 &state->op->source_dsa->notify_uSN);
877         talloc_free(objects);
878
879         if (!W_ERROR_IS_OK(status)) {
880
881                 /*
882                  * If we failed to apply the records due to a missing
883                  * parent, try again after asking for the parent
884                  * records first.  Because we don't update the
885                  * highwatermark, we start this part of the cycle
886                  * again.
887                  */
888                 if (((state->op->options & DRSUAPI_DRS_GET_ANC) == 0)
889                     && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
890                         state->op->options |= DRSUAPI_DRS_GET_ANC;
891                         DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
892                         dreplsrv_op_pull_source_get_changes_trigger(req);
893                         return;
894                 } else if (((state->op->options & DRSUAPI_DRS_GET_ANC))
895                            && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
896                         DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
897                         nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
898                 } else {
899                         nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
900                 }
901                 DEBUG(0,("Failed to commit objects: %s/%s\n",
902                           win_errstr(status), nt_errstr(nt_status)));
903                 tevent_req_nterror(req, nt_status);
904                 return;
905         }
906
907         if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
908                 /* if it applied fine, we need to update the highwatermark */
909                 *state->op->source_dsa->repsFrom1 = rf1;
910         }
911
912         /* we don't need this maybe very large structure anymore */
913         TALLOC_FREE(r);
914
915         if (more_data) {
916                 dreplsrv_op_pull_source_get_changes_trigger(req);
917                 return;
918         }
919
920         /*
921          * If we had to divert via doing some other thing, such as
922          * pulling the schema, then go back and do the original
923          * operation once we are done.
924          */
925         if (state->op->source_dsa_retry != NULL) {
926                 state->op->source_dsa = state->op->source_dsa_retry;
927                 state->op->extended_op = state->op->extended_op_retry;
928                 state->op->source_dsa_retry = NULL;
929                 dreplsrv_op_pull_source_get_changes_trigger(req);
930                 return;
931         }
932
933         if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
934             state->op->service->am_rodc) {
935                 /*
936                   we don't do the UpdateRefs for extended ops or if we
937                   are a RODC
938                  */
939                 tevent_req_done(req);
940                 return;
941         }
942
943         /* now we need to update the repsTo record for this partition
944            on the server. These records are initially established when
945            we join the domain, but they quickly expire.  We do it here
946            so we can use the already established DRSUAPI pipe
947         */
948         dreplsrv_update_refs_trigger(req);
949 }
950
951 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
952
953 /*
954   send a UpdateRefs request to refresh our repsTo record on the server
955  */
956 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
957 {
958         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
959                                                       struct dreplsrv_op_pull_source_state);
960         struct dreplsrv_service *service = state->op->service;
961         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
962         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
963         struct drsuapi_DsReplicaUpdateRefs *r;
964         char *ntds_dns_name;
965         struct tevent_req *subreq;
966
967         r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
968         if (tevent_req_nomem(r, req)) {
969                 return;
970         }
971
972         ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
973         if (tevent_req_nomem(ntds_dns_name, req)) {
974                 talloc_free(r);
975                 return;
976         }
977
978         r->in.bind_handle       = &drsuapi->bind_handle;
979         r->in.level             = 1;
980         r->in.req.req1.naming_context     = &partition->nc;
981         r->in.req.req1.dest_dsa_dns_name  = ntds_dns_name;
982         r->in.req.req1.dest_dsa_guid      = service->ntds_guid;
983         r->in.req.req1.options            = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
984         if (!service->am_rodc) {
985                 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
986         }
987
988         state->ndr_struct_ptr = r;
989         subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
990                                                            state->ev,
991                                                            drsuapi->drsuapi_handle,
992                                                            r);
993         if (tevent_req_nomem(subreq, req)) {
994                 talloc_free(r);
995                 return;
996         }
997         tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
998 }
999
1000 /*
1001   receive a UpdateRefs reply
1002  */
1003 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1004 {
1005         struct tevent_req *req = tevent_req_callback_data(subreq,
1006                                  struct tevent_req);
1007         struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1008                                                       struct dreplsrv_op_pull_source_state);
1009         struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1010                                                                 struct drsuapi_DsReplicaUpdateRefs);
1011         NTSTATUS status;
1012
1013         state->ndr_struct_ptr = NULL;
1014
1015         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1016         TALLOC_FREE(subreq);
1017         if (!NT_STATUS_IS_OK(status)) {
1018                 DEBUG(0,("UpdateRefs failed with %s\n", 
1019                          nt_errstr(status)));
1020                 tevent_req_nterror(req, status);
1021                 return;
1022         }
1023
1024         if (!W_ERROR_IS_OK(r->out.result)) {
1025                 status = werror_to_ntstatus(r->out.result);
1026                 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1027                          win_errstr(r->out.result),
1028                          nt_errstr(status),
1029                          r->in.req.req1.dest_dsa_dns_name,
1030                          r->in.req.req1.naming_context->dn));
1031                 /*
1032                  * TODO we are currently not sending the
1033                  * DsReplicaUpdateRefs at the correct moment,
1034                  * we do it just after a GetNcChanges which is
1035                  * not always correct.
1036                  * Especially when another DC is trying to demote
1037                  * it will sends us a DsReplicaSync that will trigger a getNcChanges
1038                  * this call will succeed but the DsRecplicaUpdateRefs that we send
1039                  * just after will not because the DC is in a demote state and
1040                  * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1041                  * answer to the DsReplicaSync with a non OK status, the other DC
1042                  * will stop the demote due to this error.
1043                  * In order to cope with this we will for the moment concider
1044                  * a DS_DRA_BUSY not as an error.
1045                  * It's not ideal but it should not have a too huge impact for
1046                  * running production as this error otherwise never happen and
1047                  * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1048                  */
1049                 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1050                         tevent_req_nterror(req, status);
1051                         return;
1052                 }
1053         }
1054
1055         DEBUG(4,("UpdateRefs OK for %s %s\n", 
1056                  r->in.req.req1.dest_dsa_dns_name,
1057                  r->in.req.req1.naming_context->dn));
1058
1059         tevent_req_done(req);
1060 }
1061
1062 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1063 {
1064         NTSTATUS status;
1065
1066         if (tevent_req_is_nterror(req, &status)) {
1067                 tevent_req_received(req);
1068                 return ntstatus_to_werror(status);
1069         }
1070
1071         tevent_req_received(req);
1072         return WERR_OK;
1073 }
1074