4aa0e86b480a6a34c957ac01109a19a7af5d54f6
[metze/samba/wip.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 "lib/messaging/irpc.h"
28 #include "dsdb/repl/drepl_service.h"
29 #include "lib/ldb/include/ldb_errors.h"
30 #include "../lib/util/dlinklist.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "librpc/gen_ndr/ndr_drsuapi.h"
33 #include "librpc/gen_ndr/ndr_drsblobs.h"
34 #include "libcli/composite/composite.h"
35 #include "auth/gensec/gensec.h"
36 #include "param/param.h"
37 #include "../lib/util/tevent_ntstatus.h"
38
39 struct dreplsrv_out_drsuapi_state {
40         struct dreplsrv_out_connection *conn;
41
42         struct dreplsrv_drsuapi_connection *drsuapi;
43
44         struct drsuapi_DsBindInfoCtr bind_info_ctr;
45         struct drsuapi_DsBind bind_r;
46 };
47
48 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq);
49
50 struct tevent_req *dreplsrv_out_drsuapi_send(TALLOC_CTX *mem_ctx,
51                                              struct tevent_context *ev,
52                                              struct dreplsrv_out_connection *conn)
53 {
54         struct tevent_req *req;
55         struct dreplsrv_out_drsuapi_state *state;
56         struct composite_context *creq;
57
58         req = tevent_req_create(mem_ctx, &state,
59                                 struct dreplsrv_out_drsuapi_state);
60         if (req == NULL) {
61                 return NULL;
62         }
63
64         state->conn     = conn;
65         state->drsuapi  = conn->drsuapi;
66
67         if (state->drsuapi && !state->drsuapi->pipe->conn->dead) {
68                 tevent_req_done(req);
69                 return tevent_req_post(req, ev);
70         }
71
72         if (state->drsuapi && state->drsuapi->pipe->conn->dead) {
73                 talloc_free(state->drsuapi);
74                 conn->drsuapi = NULL;
75         }
76
77         state->drsuapi = talloc_zero(state, struct dreplsrv_drsuapi_connection);
78         if (tevent_req_nomem(state->drsuapi, req)) {
79                 return tevent_req_post(req, ev);
80         }
81
82         creq = dcerpc_pipe_connect_b_send(state, conn->binding, &ndr_table_drsuapi,
83                                           conn->service->system_session_info->credentials,
84                                           ev, conn->service->task->lp_ctx);
85         if (tevent_req_nomem(creq, req)) {
86                 return tevent_req_post(req, ev);
87         }
88         composite_continue(NULL, creq, dreplsrv_out_drsuapi_connect_done, req);
89
90         return req;
91 }
92
93 static void dreplsrv_out_drsuapi_bind_done(struct rpc_request *rreq);
94
95 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq)
96 {
97         struct tevent_req *req = talloc_get_type(creq->async.private_data,
98                                                  struct tevent_req);
99         struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
100                                                    struct dreplsrv_out_drsuapi_state);
101         NTSTATUS status;
102         struct rpc_request *rreq;
103
104         status = dcerpc_pipe_connect_b_recv(creq,
105                                             state->drsuapi,
106                                             &state->drsuapi->pipe);
107         if (tevent_req_nterror(req, status)) {
108                 return;
109         }
110
111         status = gensec_session_key(state->drsuapi->pipe->conn->security_state.generic_state,
112                                     &state->drsuapi->gensec_skey);
113         if (tevent_req_nterror(req, status)) {
114                 return;
115         }
116
117         state->bind_info_ctr.length             = 28;
118         state->bind_info_ctr.info.info28        = state->conn->service->bind_info28;
119
120         state->bind_r.in.bind_guid = &state->conn->service->ntds_guid;
121         state->bind_r.in.bind_info = &state->bind_info_ctr;
122         state->bind_r.out.bind_handle = &state->drsuapi->bind_handle;
123
124         rreq = dcerpc_drsuapi_DsBind_send(state->drsuapi->pipe,
125                                           state,
126                                           &state->bind_r);
127         if (tevent_req_nomem(rreq, req)) {
128                 return;
129         }
130         composite_continue_rpc(NULL, rreq, dreplsrv_out_drsuapi_bind_done, req);
131 }
132
133 static void dreplsrv_out_drsuapi_bind_done(struct rpc_request *rreq)
134 {
135         struct tevent_req *req = talloc_get_type(rreq->async.private_data,
136                                                  struct tevent_req);
137         struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
138                                                    struct dreplsrv_out_drsuapi_state);
139         NTSTATUS status;
140
141         status = dcerpc_ndr_request_recv(rreq);
142         if (tevent_req_nterror(req, status)) {
143                 return;
144         }
145
146         if (!W_ERROR_IS_OK(state->bind_r.out.result)) {
147                 status = werror_to_ntstatus(state->bind_r.out.result);
148                 tevent_req_nterror(req, status);
149                 return;
150         }
151
152         ZERO_STRUCT(state->drsuapi->remote_info28);
153         if (state->bind_r.out.bind_info) {
154                 struct drsuapi_DsBindInfo28 *info28;
155                 info28 = &state->drsuapi->remote_info28;
156
157                 switch (state->bind_r.out.bind_info->length) {
158                 case 24: {
159                         struct drsuapi_DsBindInfo24 *info24;
160                         info24 = &state->bind_r.out.bind_info->info.info24;
161
162                         info28->supported_extensions    = info24->supported_extensions;
163                         info28->site_guid               = info24->site_guid;
164                         info28->pid                     = info24->pid;
165                         info28->repl_epoch              = 0;
166                         break;
167                 }
168                 case 48: {
169                         struct drsuapi_DsBindInfo48 *info48;
170                         info48 = &state->bind_r.out.bind_info->info.info48;
171
172                         info28->supported_extensions    = info48->supported_extensions;
173                         info28->site_guid               = info48->site_guid;
174                         info28->pid                     = info48->pid;
175                         info28->repl_epoch              = info48->repl_epoch;
176                         break;
177                 }
178                 case 28:
179                         *info28 = state->bind_r.out.bind_info->info.info28;
180                         break;
181                 }
182         }
183
184         tevent_req_done(req);
185 }
186
187 NTSTATUS dreplsrv_out_drsuapi_recv(struct tevent_req *req)
188 {
189         struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
190                                                    struct dreplsrv_out_drsuapi_state);
191         NTSTATUS status;
192
193         if (tevent_req_is_nterror(req, &status)) {
194                 tevent_req_received(req);
195                 return status;
196         }
197
198         state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
199
200         tevent_req_received(req);
201         return NT_STATUS_OK;
202 }
203
204 struct dreplsrv_op_pull_source_state {
205         struct composite_context *creq;
206
207         struct dreplsrv_out_operation *op;
208
209         struct dreplsrv_drsuapi_connection *drsuapi;
210
211         bool have_all;
212
213         uint32_t ctr_level;
214         struct drsuapi_DsGetNCChangesCtr1 *ctr1;
215         struct drsuapi_DsGetNCChangesCtr6 *ctr6;
216 };
217
218 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
219
220 struct composite_context *dreplsrv_op_pull_source_send(struct dreplsrv_out_operation *op)
221 {
222         struct composite_context *c;
223         struct dreplsrv_op_pull_source_state *st;
224         struct tevent_req *subreq;
225
226         c = composite_create(op, op->service->task->event_ctx);
227         if (c == NULL) return NULL;
228
229         st = talloc_zero(c, struct dreplsrv_op_pull_source_state);
230         if (composite_nomem(st, c)) return c;
231
232         st->creq        = c;
233         st->op          = op;
234
235         subreq = dreplsrv_out_drsuapi_send(st,
236                                            op->service->task->event_ctx,
237                                            op->source_dsa->conn);
238         if (composite_nomem(subreq, c)) return c;
239         tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, st);
240
241         return c;
242 }
243
244 static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st);
245
246 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
247 {
248         struct dreplsrv_op_pull_source_state *st = tevent_req_callback_data(subreq,
249                                                    struct dreplsrv_op_pull_source_state);
250         struct composite_context *c = st->creq;
251
252         c->status = dreplsrv_out_drsuapi_recv(subreq);
253         TALLOC_FREE(subreq);
254         if (!composite_is_ok(c)) return;
255
256         dreplsrv_op_pull_source_get_changes_send(st);
257 }
258
259 static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req);
260
261 static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st)
262 {
263         struct composite_context *c = st->creq;
264         struct repsFromTo1 *rf1 = st->op->source_dsa->repsFrom1;
265         struct dreplsrv_service *service = st->op->service;
266         struct dreplsrv_partition *partition = st->op->source_dsa->partition;
267         struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
268         struct rpc_request *req;
269         struct drsuapi_DsGetNCChanges *r;
270
271         r = talloc(st, struct drsuapi_DsGetNCChanges);
272         if (composite_nomem(r, c)) return;
273
274         r->out.level_out = talloc(r, int32_t);
275         if (composite_nomem(r->out.level_out, c)) return;
276         r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
277         if (composite_nomem(r->in.req, c)) return;
278         r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
279         if (composite_nomem(r->out.ctr, c)) return;
280
281         r->in.bind_handle       = &drsuapi->bind_handle;
282         if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
283                 r->in.level                             = 8;
284                 r->in.req->req8.destination_dsa_guid    = service->ntds_guid;
285                 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
286                 r->in.req->req8.naming_context          = &partition->nc;
287                 r->in.req->req8.highwatermark           = rf1->highwatermark;
288                 r->in.req->req8.uptodateness_vector     = NULL;/*&partition->uptodatevector_ex;*/
289                 r->in.req->req8.replica_flags           = rf1->replica_flags;
290                 r->in.req->req8.max_object_count        = 133;
291                 r->in.req->req8.max_ndr_size            = 1336811;
292                 r->in.req->req8.extended_op             = st->op->extended_op;
293                 r->in.req->req8.fsmo_info               = st->op->fsmo_info;
294                 r->in.req->req8.partial_attribute_set   = NULL;
295                 r->in.req->req8.partial_attribute_set_ex= NULL;
296                 r->in.req->req8.mapping_ctr.num_mappings= 0;
297                 r->in.req->req8.mapping_ctr.mappings    = NULL;
298         } else {
299                 r->in.level                             = 5;
300                 r->in.req->req5.destination_dsa_guid    = service->ntds_guid;
301                 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
302                 r->in.req->req5.naming_context          = &partition->nc;
303                 r->in.req->req5.highwatermark           = rf1->highwatermark;
304                 r->in.req->req5.uptodateness_vector     = NULL;/*&partition->uptodatevector_ex;*/
305                 r->in.req->req5.replica_flags           = rf1->replica_flags;
306                 r->in.req->req5.max_object_count        = 133;
307                 r->in.req->req5.max_ndr_size            = 1336770;
308                 r->in.req->req5.extended_op             = st->op->extended_op;
309                 r->in.req->req5.fsmo_info               = st->op->fsmo_info;
310         }
311
312         req = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi->pipe, r, r);
313         composite_continue_rpc(c, req, dreplsrv_op_pull_source_get_changes_recv, st);
314 }
315
316 static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st,
317                                                        struct drsuapi_DsGetNCChanges *r,
318                                                        uint32_t ctr_level,
319                                                        struct drsuapi_DsGetNCChangesCtr1 *ctr1,
320                                                        struct drsuapi_DsGetNCChangesCtr6 *ctr6);
321
322 static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req)
323 {
324         struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data,
325                                                    struct dreplsrv_op_pull_source_state);
326         struct composite_context *c = st->creq;
327         struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
328                                            struct drsuapi_DsGetNCChanges);
329         uint32_t ctr_level = 0;
330         struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
331         struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
332
333         c->status = dcerpc_ndr_request_recv(req);
334         if (!composite_is_ok(c)) return;
335
336         if (!W_ERROR_IS_OK(r->out.result)) {
337                 composite_error(c, werror_to_ntstatus(r->out.result));
338                 return;
339         }
340
341         if (*r->out.level_out == 1) {
342                 ctr_level = 1;
343                 ctr1 = &r->out.ctr->ctr1;
344         } else if (*r->out.level_out == 2 &&
345                    r->out.ctr->ctr2.mszip1.ts) {
346                 ctr_level = 1;
347                 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
348         } else if (*r->out.level_out == 6) {
349                 ctr_level = 6;
350                 ctr6 = &r->out.ctr->ctr6;
351         } else if (*r->out.level_out == 7 &&
352                    r->out.ctr->ctr7.level == 6 &&
353                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
354                    r->out.ctr->ctr7.ctr.mszip6.ts) {
355                 ctr_level = 6;
356                 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
357         } else if (*r->out.level_out == 7 &&
358                    r->out.ctr->ctr7.level == 6 &&
359                    r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
360                    r->out.ctr->ctr7.ctr.xpress6.ts) {
361                 ctr_level = 6;
362                 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
363         } else {
364                 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
365                 return;
366         }
367
368         if (!ctr1 && !ctr6) {
369                 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
370                 return;
371         }
372
373         if (ctr_level == 6) {
374                 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
375                         composite_error(c, werror_to_ntstatus(ctr6->drs_error));
376                         return;
377                 }
378         }
379
380         dreplsrv_op_pull_source_apply_changes_send(st, r, ctr_level, ctr1, ctr6);
381 }
382
383 static void dreplsrv_update_refs_send(struct dreplsrv_op_pull_source_state *st);
384
385 static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st,
386                                                        struct drsuapi_DsGetNCChanges *r,
387                                                        uint32_t ctr_level,
388                                                        struct drsuapi_DsGetNCChangesCtr1 *ctr1,
389                                                        struct drsuapi_DsGetNCChangesCtr6 *ctr6)
390 {
391         struct composite_context *c = st->creq;
392         struct repsFromTo1 rf1 = *st->op->source_dsa->repsFrom1;
393         struct dreplsrv_service *service = st->op->service;
394         struct dreplsrv_partition *partition = st->op->source_dsa->partition;
395         struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
396         const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
397         uint32_t object_count;
398         struct drsuapi_DsReplicaObjectListItemEx *first_object;
399         uint32_t linked_attributes_count;
400         struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
401         const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
402         struct dsdb_extended_replicated_objects *objects;
403         bool more_data = false;
404         WERROR status;
405
406         switch (ctr_level) {
407         case 1:
408                 mapping_ctr                     = &ctr1->mapping_ctr;
409                 object_count                    = ctr1->object_count;
410                 first_object                    = ctr1->first_object;
411                 linked_attributes_count         = 0;
412                 linked_attributes               = NULL;
413                 rf1.highwatermark               = ctr1->new_highwatermark;
414                 uptodateness_vector             = NULL; /* TODO: map it */
415                 more_data                       = ctr1->more_data;
416                 break;
417         case 6:
418                 mapping_ctr                     = &ctr6->mapping_ctr;
419                 object_count                    = ctr6->object_count;
420                 first_object                    = ctr6->first_object;
421                 linked_attributes_count         = ctr6->linked_attributes_count;
422                 linked_attributes               = ctr6->linked_attributes;
423                 rf1.highwatermark               = ctr6->new_highwatermark;
424                 uptodateness_vector             = ctr6->uptodateness_vector;
425                 more_data                       = ctr6->more_data;
426                 break;
427         default:
428                 composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
429                 return;
430         }
431
432         status = dsdb_extended_replicated_objects_convert(service->samdb,
433                                                           partition->nc.dn,
434                                                           mapping_ctr,
435                                                           object_count,
436                                                           first_object,
437                                                           linked_attributes_count,
438                                                           linked_attributes,
439                                                           &rf1,
440                                                           uptodateness_vector,
441                                                           &drsuapi->gensec_skey,
442                                                           st, &objects);
443         if (!W_ERROR_IS_OK(status)) {
444                 DEBUG(0,("Failed to convert objects: %s\n", win_errstr(status)));
445                 composite_error(c, werror_to_ntstatus(status));
446                 return;
447         }
448
449         status = dsdb_extended_replicated_objects_commit(service->samdb,
450                                                          objects, 
451                                                          &st->op->source_dsa->notify_uSN);
452         talloc_free(objects);
453         if (!W_ERROR_IS_OK(status)) {
454                 DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
455                 composite_error(c, werror_to_ntstatus(status));
456                 return;
457         }
458
459         /* if it applied fine, we need to update the highwatermark */
460         *st->op->source_dsa->repsFrom1 = rf1;
461
462         /*
463          * TODO: update our uptodatevector!
464          */
465
466         if (more_data) {
467                 dreplsrv_op_pull_source_get_changes_send(st);
468                 return;
469         }
470
471         /* now we need to update the repsTo record for this partition
472            on the server. These records are initially established when
473            we join the domain, but they quickly expire.  We do it here
474            so we can use the already established DRSUAPI pipe
475         */
476         dreplsrv_update_refs_send(st);
477 }
478
479 WERROR dreplsrv_op_pull_source_recv(struct composite_context *c)
480 {
481         NTSTATUS status;
482
483         status = composite_wait(c);
484
485         talloc_free(c);
486         return ntstatus_to_werror(status);
487 }
488
489 /*
490   receive a UpdateRefs reply
491  */
492 static void dreplsrv_update_refs_recv(struct rpc_request *req)
493 {
494         struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data,
495                                                    struct dreplsrv_op_pull_source_state);
496         struct composite_context *c = st->creq;
497         struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
498                                                                 struct drsuapi_DsReplicaUpdateRefs);
499
500         c->status = dcerpc_ndr_request_recv(req);
501         if (!composite_is_ok(c)) {
502                 DEBUG(0,("UpdateRefs failed with %s\n", 
503                          nt_errstr(c->status)));
504                 return;
505         }
506
507         if (!W_ERROR_IS_OK(r->out.result)) {
508                 DEBUG(0,("UpdateRefs failed with %s for %s %s\n", 
509                          win_errstr(r->out.result),
510                          r->in.req.req1.dest_dsa_dns_name,
511                          r->in.req.req1.naming_context->dn));
512                 composite_error(c, werror_to_ntstatus(r->out.result));
513                 return;
514         }
515
516         DEBUG(4,("UpdateRefs OK for %s %s\n", 
517                  r->in.req.req1.dest_dsa_dns_name,
518                  r->in.req.req1.naming_context->dn));
519
520         composite_done(c);
521 }
522
523 /*
524   send a UpdateRefs request to refresh our repsTo record on the server
525  */
526 static void dreplsrv_update_refs_send(struct dreplsrv_op_pull_source_state *st)
527 {
528         struct composite_context *c = st->creq;
529         struct dreplsrv_service *service = st->op->service;
530         struct dreplsrv_partition *partition = st->op->source_dsa->partition;
531         struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
532         struct rpc_request *req;
533         struct drsuapi_DsReplicaUpdateRefs *r;
534         char *ntds_guid_str;
535         char *ntds_dns_name;
536
537         r = talloc(st, struct drsuapi_DsReplicaUpdateRefs);
538         if (composite_nomem(r, c)) return;
539
540         ntds_guid_str = GUID_string(r, &service->ntds_guid);
541         if (composite_nomem(ntds_guid_str, c)) return;
542
543         ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
544                                         ntds_guid_str,
545                                         lp_dnsdomain(service->task->lp_ctx));
546         if (composite_nomem(ntds_dns_name, c)) return;
547
548         r->in.bind_handle       = &drsuapi->bind_handle;
549         r->in.level             = 1;
550         r->in.req.req1.naming_context     = &partition->nc;
551         r->in.req.req1.dest_dsa_dns_name  = ntds_dns_name;
552         r->in.req.req1.dest_dsa_guid      = service->ntds_guid;
553         r->in.req.req1.options            = 
554                 DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE |
555                 DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE;
556         if (!samdb_rodc(service->task->lp_ctx)) {
557                 r->in.req.req1.options |= DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE;
558         }
559
560         req = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r);
561         composite_continue_rpc(c, req, dreplsrv_update_refs_recv, st);
562 }