s4/rodc: Implement samdb_rodc with ldb context
[rusty/samba.git] / source4 / rpc_server / drsuapi / getncchanges.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    implement the DRSUpdateRefs call
5
6    Copyright (C) Anatoliy Atanasov 2009
7    Copyright (C) Andrew Tridgell 2009
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "param/param.h"
27 #include "librpc/gen_ndr/ndr_drsblobs.h"
28 #include "librpc/gen_ndr/ndr_drsuapi.h"
29 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
30 #include "rpc_server/dcerpc_server_proto.h"
31 #include "../libcli/drsuapi/drsuapi.h"
32 #include "libcli/security/security.h"
33 #include "lib/util/binsearch.h"
34 #include "lib/util/tsort.h"
35
36 /*
37   build a DsReplicaObjectIdentifier from a ldb msg
38  */
39 static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
40                                                                        struct ldb_message *msg)
41 {
42         struct drsuapi_DsReplicaObjectIdentifier *identifier;
43         struct dom_sid *sid;
44
45         identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
46         if (identifier == NULL) {
47                 return NULL;
48         }
49
50         identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
51         identifier->guid = samdb_result_guid(msg, "objectGUID");
52
53         sid = samdb_result_dom_sid(identifier, msg, "objectSid");
54         if (sid) {
55                 identifier->sid = *sid;
56         } else {
57                 ZERO_STRUCT(identifier->sid);
58         }
59         return identifier;
60 }
61
62 static int udv_compare(const struct GUID *guid1, struct GUID guid2)
63 {
64         return GUID_compare(guid1, &guid2);
65 }
66
67 /*
68   see if we can filter an attribute using the uptodateness_vector
69  */
70 static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
71                        const struct GUID *originating_invocation_id,
72                        uint64_t originating_usn)
73 {
74         const struct drsuapi_DsReplicaCursor *c;
75         if (udv == NULL) return false;
76         BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id, 
77                             originating_invocation_id, udv_compare, c);
78         if (c && originating_usn <= c->highest_usn) {
79                 return true;
80         }
81         return false;
82         
83 }
84
85 /* 
86   drsuapi_DsGetNCChanges for one object
87 */
88 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
89                                           struct ldb_message *msg,
90                                           struct ldb_context *sam_ctx,
91                                           struct ldb_dn *ncRoot_dn,
92                                           struct dsdb_schema *schema,
93                                           DATA_BLOB *session_key,
94                                           uint64_t highest_usn,
95                                           uint32_t replica_flags,
96                                           struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
97 {
98         const struct ldb_val *md_value;
99         int i, n;
100         struct replPropertyMetaDataBlob md;
101         uint32_t rid = 0;
102         enum ndr_err_code ndr_err;
103         uint32_t *attids;
104         const char *rdn;
105         const struct dsdb_attribute *rdn_sa;
106         unsigned int instanceType;
107
108         instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
109         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
110                 obj->is_nc_prefix = true;
111                 obj->parent_object_guid = NULL;
112         } else {
113                 obj->is_nc_prefix = false;
114                 obj->parent_object_guid = talloc(obj, struct GUID);
115                 if (obj->parent_object_guid == NULL) {
116                         return WERR_DS_DRA_INTERNAL_ERROR;
117                 }
118                 *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
119                 if (GUID_all_zero(obj->parent_object_guid)) {
120                         DEBUG(0,(__location__ ": missing parentGUID for %s\n",
121                                  ldb_dn_get_linearized(msg->dn)));
122                         return WERR_DS_DRA_INTERNAL_ERROR;
123                 }
124         }
125         obj->next_object = NULL;
126         
127         md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
128         if (!md_value) {
129                 /* nothing to send */
130                 return WERR_OK;
131         }
132
133         ndr_err = ndr_pull_struct_blob(md_value, obj,
134                                        lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")), &md,
135                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
136         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
137                 return WERR_DS_DRA_INTERNAL_ERROR;
138         }
139         
140         if (md.version != 1) {
141                 return WERR_DS_DRA_INTERNAL_ERROR;
142         }
143
144         rdn = ldb_dn_get_rdn_name(msg->dn);
145         if (rdn == NULL) {
146                 DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
147                 return WERR_DS_DRA_INTERNAL_ERROR;
148         }
149
150         rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
151         if (rdn_sa == NULL) {
152                 DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n", 
153                          rdn, ldb_dn_get_linearized(msg->dn)));
154                 return WERR_DS_DRA_INTERNAL_ERROR;
155         }
156
157         obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
158         attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
159
160         obj->object.identifier = get_object_identifier(obj, msg);
161         if (obj->object.identifier == NULL) {
162                 return WERR_NOMEM;
163         }
164         dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
165         
166         obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
167         for (n=i=0; i<md.ctr.ctr1.count; i++) {
168                 const struct dsdb_attribute *sa;
169                 /* if the attribute has not changed, and it is not the
170                    instanceType then don't include it */
171                 if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
172                     md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) continue;
173
174                 /* don't include the rDN */
175                 if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
176
177                 sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
178                 if (sa->linkID) {
179                         struct ldb_message_element *el;
180                         el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
181                         if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
182                                 /* don't send upgraded links inline */
183                                 continue;
184                         }
185                 }
186
187                 /* filter by uptodateness_vector */
188                 if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType &&
189                     udv_filter(uptodateness_vector,
190                                &md.ctr.ctr1.array[i].originating_invocation_id, 
191                                md.ctr.ctr1.array[i].originating_usn)) {
192                         continue;
193                 }
194
195                 obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
196                 obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
197                 obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
198                 obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
199                 attids[n] = md.ctr.ctr1.array[i].attid;
200                 n++;
201         }
202
203         /* ignore it if its an empty change. Note that renames always
204          * change the 'name' attribute, so they won't be ignored by
205          * this */
206         if (n == 0 ||
207             (n == 1 && attids[0] == DRSUAPI_ATTRIBUTE_instanceType)) {
208                 talloc_free(obj->meta_data_ctr);
209                 obj->meta_data_ctr = NULL;
210                 return WERR_OK;
211         }
212
213         obj->meta_data_ctr->count = n;
214
215         obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
216         obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
217         obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
218                                                             obj->object.attribute_ctr.num_attributes);
219
220         /*
221          * Note that the meta_data array and the attributes array must
222          * be the same size and in the same order
223          */
224         for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
225                 struct ldb_message_element *el;
226                 WERROR werr;
227                 const struct dsdb_attribute *sa;
228         
229                 sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
230                 if (!sa) {
231                         DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
232                         return WERR_DS_DRA_INTERNAL_ERROR;
233                 }
234
235                 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
236                 if (el == NULL) {
237                         /* this happens for attributes that have been removed */
238                         DEBUG(5,("No element '%s' for attributeID %u in message\n",
239                                  sa->lDAPDisplayName, attids[i]));
240                         ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
241                         obj->object.attribute_ctr.attributes[i].attid = attids[i];
242                 } else {
243                         werr = dsdb_attribute_ldb_to_drsuapi(sam_ctx, schema, el, obj,
244                                                              &obj->object.attribute_ctr.attributes[i]);
245                         if (!W_ERROR_IS_OK(werr)) {
246                                 DEBUG(0,("Unable to convert %s to DRS object - %s\n", 
247                                          sa->lDAPDisplayName, win_errstr(werr)));
248                                 return werr;
249                         }
250                         /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
251                          * check if attribute is secret and send a null value
252                          */
253                         if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
254                                 drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
255                                                                  &obj->meta_data_ctr->meta_data[i]);
256                         }
257                         /* some attributes needs to be encrypted
258                            before being sent */
259                         werr = drsuapi_encrypt_attribute(obj, session_key, rid, 
260                                                          &obj->object.attribute_ctr.attributes[i]);
261                         if (!W_ERROR_IS_OK(werr)) {
262                                 DEBUG(0,("Unable to encrypt %s in DRS object - %s\n", 
263                                          sa->lDAPDisplayName, win_errstr(werr)));
264                                 return werr;
265                         }
266                 }
267         }
268
269         return WERR_OK;
270 }
271
272
273 /*
274   add one linked attribute from an object to the list of linked
275   attributes in a getncchanges request
276  */
277 static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
278                                     struct ldb_context *sam_ctx,
279                                     const struct dsdb_schema *schema,
280                                     const struct dsdb_attribute *sa,
281                                     struct ldb_message *msg,
282                                     struct dsdb_dn *dsdb_dn,
283                                     struct drsuapi_DsReplicaLinkedAttribute **la_list,
284                                     uint32_t *la_count)
285 {
286         struct drsuapi_DsReplicaLinkedAttribute *la;
287         bool active;
288         NTSTATUS status;
289         WERROR werr;
290
291         (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
292         W_ERROR_HAVE_NO_MEMORY(*la_list);
293
294         la = &(*la_list)[*la_count];
295
296         la->identifier = get_object_identifier(*la_list, msg);
297         W_ERROR_HAVE_NO_MEMORY(la->identifier);
298
299         active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
300
301         la->attid = sa->attributeID_id;
302         la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
303
304         status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
305         if (!NT_STATUS_IS_OK(status)) {
306                 return ntstatus_to_werror(status);
307         }
308         status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
309         if (!NT_STATUS_IS_OK(status)) {
310                 return ntstatus_to_werror(status);
311         }
312         status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
313         if (!NT_STATUS_IS_OK(status)) {
314                 return ntstatus_to_werror(status);
315         }
316         status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
317         if (!NT_STATUS_IS_OK(status)) {
318                 return ntstatus_to_werror(status);
319         }
320         status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
321         if (!NT_STATUS_IS_OK(status)) {
322                 return ntstatus_to_werror(status);
323         }
324
325         werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
326         W_ERROR_NOT_OK_RETURN(werr);
327
328         (*la_count)++;
329         return WERR_OK;
330 }
331
332
333 /*
334   add linked attributes from an object to the list of linked
335   attributes in a getncchanges request
336  */
337 static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
338                                        TALLOC_CTX *mem_ctx,
339                                        struct ldb_dn *ncRoot_dn,
340                                        struct dsdb_schema *schema,
341                                        uint64_t highest_usn,
342                                        uint32_t replica_flags,
343                                        struct ldb_message *msg,
344                                        struct drsuapi_DsReplicaLinkedAttribute **la_list,
345                                        uint32_t *la_count,
346                                        struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
347 {
348         int i;
349         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
350         uint64_t uSNChanged = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
351
352         for (i=0; i<msg->num_elements; i++) {
353                 struct ldb_message_element *el = &msg->elements[i];
354                 const struct dsdb_attribute *sa;
355                 int j;
356
357                 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
358
359                 if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
360                         /* we only want forward links */
361                         continue;
362                 }
363
364                 if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
365                         /* its an old style link, it will have been
366                          * sent in the main replication data */
367                         continue;
368                 }
369
370                 for (j=0; j<el->num_values; j++) {
371                         struct dsdb_dn *dsdb_dn;
372                         uint64_t local_usn;
373                         NTSTATUS status;
374                         WERROR werr;
375
376                         dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
377                         if (dsdb_dn == NULL) {
378                                 DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
379                                          el->name, ldb_dn_get_linearized(msg->dn)));
380                                 talloc_free(tmp_ctx);
381                                 return WERR_DS_DRA_INTERNAL_ERROR;
382                         }
383
384                         status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
385                         if (!NT_STATUS_IS_OK(status)) {
386                                 /* this can happen for attributes
387                                    given to us with old style meta
388                                    data */
389                                 continue;
390                         }
391
392                         if (local_usn > uSNChanged) {
393                                 DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
394                                          el->name, ldb_dn_get_linearized(msg->dn)));
395                                 talloc_free(tmp_ctx);
396                                 return WERR_DS_DRA_INTERNAL_ERROR;
397                         }
398
399                         if (local_usn < highest_usn) {
400                                 continue;
401                         }
402
403                         werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema, sa, msg,
404                                                      dsdb_dn, la_list, la_count);
405                         if (!W_ERROR_IS_OK(werr)) {
406                                 talloc_free(tmp_ctx);
407                                 return werr;
408                         }
409                 }
410         }
411
412         talloc_free(tmp_ctx);
413         return WERR_OK;
414 }
415
416 /*
417   fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
418  */
419 static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
420                                  struct ldb_dn *ncRoot_dn,
421                                  struct drsuapi_DsReplicaCursor2CtrEx *udv)
422 {
423         int ret;
424
425         udv->version = 2;
426         udv->reserved1 = 0;
427         udv->reserved2 = 0;
428
429         ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
430         if (ret != LDB_SUCCESS) {
431                 DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
432                          ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
433                 return WERR_DS_DRA_INTERNAL_ERROR;
434         }
435         
436         return WERR_OK;
437 }
438
439
440 /* comparison function for linked attributes - see CompareLinks() in
441  * MS-DRSR section 4.1.10.5.17 */
442 static int linked_attribute_compare(const struct drsuapi_DsReplicaLinkedAttribute *la1,
443                                     const struct drsuapi_DsReplicaLinkedAttribute *la2,
444                                     struct ldb_context *sam_ctx)
445 {
446         int c;
447         WERROR werr;
448         TALLOC_CTX *tmp_ctx;
449         const struct dsdb_schema *schema;
450         const struct dsdb_attribute *schema_attrib;
451         struct dsdb_dn *dn1, *dn2;
452         struct GUID guid1, guid2;
453         NTSTATUS status;
454
455         c = GUID_compare(&la1->identifier->guid,
456                          &la2->identifier->guid);
457         if (c != 0) return c;
458
459         if (la1->attid != la2->attid) {
460                 return la1->attid < la2->attid? -1:1;
461         }
462
463         if ((la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
464             (la2->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
465                 return (la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
466         }
467
468         /* we need to get the target GUIDs to compare */
469         tmp_ctx = talloc_new(sam_ctx);
470
471         schema = dsdb_get_schema(sam_ctx);
472         schema_attrib = dsdb_attribute_by_attributeID_id(schema, la1->attid);
473
474         werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la1->value.blob, &dn1);
475         if (!W_ERROR_IS_OK(werr)) {
476                 DEBUG(0,(__location__ ": Bad la1 blob in sort\n"));
477                 talloc_free(tmp_ctx);
478                 return 0;
479         }
480
481         werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la2->value.blob, &dn2);
482         if (!W_ERROR_IS_OK(werr)) {
483                 DEBUG(0,(__location__ ": Bad la2 blob in sort\n"));
484                 talloc_free(tmp_ctx);
485                 return 0;
486         }
487
488         status = dsdb_get_extended_dn_guid(dn1->dn, &guid1, "GUID");
489         if (!NT_STATUS_IS_OK(status)) {
490                 DEBUG(0,(__location__ ": Bad la1 guid in sort\n"));
491                 talloc_free(tmp_ctx);
492                 return 0;
493         }
494         status = dsdb_get_extended_dn_guid(dn2->dn, &guid2, "GUID");
495         if (!NT_STATUS_IS_OK(status)) {
496                 DEBUG(0,(__location__ ": Bad la2 guid in sort\n"));
497                 talloc_free(tmp_ctx);
498                 return 0;
499         }
500
501         talloc_free(tmp_ctx);
502
503         return GUID_compare(&guid1, &guid2);
504 }
505
506
507 /*
508   sort the objects we send by tree order
509  */
510 static int site_res_cmp_parent_order(struct ldb_message **m1, struct ldb_message **m2)
511 {
512         return ldb_dn_compare((*m2)->dn, (*m1)->dn);
513 }
514
515 /*
516   sort the objects we send first by uSNChanged
517  */
518 static int site_res_cmp_usn_order(struct ldb_message **m1, struct ldb_message **m2)
519 {
520         unsigned usnchanged1, usnchanged2;
521         unsigned cn1, cn2;
522         cn1 = ldb_dn_get_comp_num((*m1)->dn);
523         cn2 = ldb_dn_get_comp_num((*m2)->dn);
524         if (cn1 != cn2) {
525                 return cn1 > cn2 ? 1 : -1;
526         }
527         usnchanged1 = ldb_msg_find_attr_as_uint(*m1, "uSNChanged", 0);
528         usnchanged2 = ldb_msg_find_attr_as_uint(*m2, "uSNChanged", 0);
529         if (usnchanged1 == usnchanged2) {
530                 return 0;
531         }
532         return usnchanged1 > usnchanged2 ? 1 : -1;
533 }
534
535
536 /*
537   handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
538  */
539 static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
540                                      TALLOC_CTX *mem_ctx,
541                                      struct drsuapi_DsGetNCChangesRequest8 *req8,
542                                      struct drsuapi_DsGetNCChangesCtr6 *ctr6)
543 {
544         struct ldb_dn *rid_manager_dn, *fsmo_role_dn, *req_dn;
545         int ret;
546         struct ldb_context *ldb = b_state->sam_ctx;
547         struct ldb_result *ext_res;
548         struct ldb_dn *base_dn;
549         struct dsdb_fsmo_extended_op *exop;
550
551         /*
552           steps:
553             - verify that the DN being asked for is the RID Manager DN
554             - verify that we are the RID Manager
555          */
556
557         /* work out who is the RID Manager */
558         ret = samdb_rid_manager_dn(ldb, mem_ctx, &rid_manager_dn);
559         if (ret != LDB_SUCCESS) {
560                 DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
561                 return WERR_DS_DRA_INTERNAL_ERROR;
562         }
563
564         req_dn = ldb_dn_new(mem_ctx, ldb, req8->naming_context->dn);
565         if (!req_dn ||
566             !ldb_dn_validate(req_dn) ||
567             ldb_dn_compare(req_dn, rid_manager_dn) != 0) {
568                 /* that isn't the RID Manager DN */
569                 DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n",
570                          req8->naming_context->dn));
571                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
572                 return WERR_OK;
573         }
574
575         /* find the DN of the RID Manager */
576         ret = samdb_reference_dn(ldb, mem_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
577         if (ret != LDB_SUCCESS) {
578                 DEBUG(0,(__location__ ": Failed to find fSMORoleOwner in RID Manager object - %s\n",
579                          ldb_errstring(ldb)));
580                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
581                 return WERR_DS_DRA_INTERNAL_ERROR;
582         }
583
584         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
585                 /* we're not the RID Manager - go away */
586                 DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
587                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
588                 return WERR_OK;
589         }
590
591         exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
592         W_ERROR_HAVE_NO_MEMORY(exop);
593
594         exop->fsmo_info = req8->fsmo_info;
595         exop->destination_dsa_guid = req8->destination_dsa_guid;
596
597         ret = ldb_transaction_start(ldb);
598         if (ret != LDB_SUCCESS) {
599                 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
600                          ldb_errstring(ldb)));
601                 return WERR_DS_DRA_INTERNAL_ERROR;
602         }
603
604         ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
605         if (ret != LDB_SUCCESS) {
606                 DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
607                          ldb_errstring(ldb)));
608                 ldb_transaction_cancel(ldb);
609                 return WERR_DS_DRA_INTERNAL_ERROR;
610         }
611
612         ret = ldb_transaction_commit(ldb);
613         if (ret != LDB_SUCCESS) {
614                 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
615                          ldb_errstring(ldb)));
616                 return WERR_DS_DRA_INTERNAL_ERROR;
617         }
618
619         talloc_free(ext_res);
620
621         base_dn = samdb_base_dn(ldb);
622
623         DEBUG(2,("Allocated RID pool for server %s\n",
624                  GUID_string(mem_ctx, &req8->destination_dsa_guid)));
625
626         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
627
628         return WERR_OK;
629 }
630
631
632
633 /* state of a partially completed getncchanges call */
634 struct drsuapi_getncchanges_state {
635         struct ldb_result *site_res;
636         uint32_t num_sent;
637         struct ldb_dn *ncRoot_dn;
638         uint64_t min_usn;
639         uint64_t highest_usn;
640         struct ldb_dn *last_dn;
641         struct drsuapi_DsReplicaLinkedAttribute *la_list;
642         uint32_t la_count;
643         struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
644 };
645
646 /* 
647   drsuapi_DsGetNCChanges
648
649   see MS-DRSR 4.1.10.5.2 for basic logic of this function
650 */
651 WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
652                                      struct drsuapi_DsGetNCChanges *r)
653 {
654         struct drsuapi_DsReplicaObjectIdentifier *ncRoot;
655         int ret;
656         int i;
657         struct dsdb_schema *schema;
658         struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
659         struct drsuapi_DsReplicaObjectListItemEx **currentObject;
660         NTSTATUS status;
661         DATA_BLOB session_key;
662         const char *attrs[] = { "*", "distinguishedName",
663                                 "nTSecurityDescriptor",
664                                 "parentGUID",
665                                 "replPropertyMetaData",
666                                 "unicodePwd",
667                                 "dBCSPwd",
668                                 "ntPwdHistory",
669                                 "lmPwdHistory",
670                                 "supplementalCredentials",
671                                 NULL };
672         WERROR werr;
673         struct dcesrv_handle *h;
674         struct drsuapi_bind_state *b_state;     
675         struct drsuapi_getncchanges_state *getnc_state;
676         struct drsuapi_DsGetNCChangesRequest8 *req8;
677         uint32_t options;
678         uint32_t max_objects;
679         struct ldb_dn *search_dn = NULL;
680
681         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
682         b_state = h->data;
683
684         *r->out.level_out = 6;
685         /* TODO: linked attributes*/
686         r->out.ctr->ctr6.linked_attributes_count = 0;
687         r->out.ctr->ctr6.linked_attributes = NULL;
688
689         r->out.ctr->ctr6.object_count = 0;
690         r->out.ctr->ctr6.nc_object_count = 0;
691         r->out.ctr->ctr6.more_data = false;
692         r->out.ctr->ctr6.uptodateness_vector = NULL;
693
694         /* a RODC doesn't allow for any replication */
695         if (samdb_rodc(b_state->sam_ctx)) {
696                 DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
697                 return WERR_DS_DRA_SOURCE_DISABLED;
698         }
699
700         /* Check request revision. 
701            TODO: Adding mappings to req8 from the other levels
702          */
703         if (r->in.level != 8) {
704                 DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
705                          r->in.level));
706                 return WERR_REVISION_MISMATCH;
707         }
708
709         req8 = &r->in.req->req8;
710
711         /* Perform access checks. */
712         /* TODO: we need to support a sync on a specific non-root
713          * DN. We'll need to find the real partition root here */
714         ncRoot = req8->naming_context;
715         if (ncRoot == NULL) {
716                 DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
717                 return WERR_DS_DRA_INVALID_PARAMETER;
718         }
719
720         if (samdb_ntds_options(b_state->sam_ctx, &options) != LDB_SUCCESS) {
721                 return WERR_DS_DRA_INTERNAL_ERROR;
722         }
723         
724         if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
725             !(req8->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
726                 return WERR_DS_DRA_SOURCE_DISABLED;
727         }
728
729
730         if (req8->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
731                 /* Ignore the _in_ uptpdateness vector*/
732                 req8->uptodateness_vector = NULL;
733         } 
734
735         werr = drs_security_level_check(dce_call, "DsGetNCChanges");
736         if (!W_ERROR_IS_OK(werr)) {
737                 return werr;
738         }
739
740         /* we don't yet support extended operations */
741         switch (req8->extended_op) {
742         case DRSUAPI_EXOP_NONE:
743                 break;
744
745         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
746                 werr = getncchanges_rid_alloc(b_state, mem_ctx, req8, &r->out.ctr->ctr6);
747                 W_ERROR_NOT_OK_RETURN(werr);
748                 search_dn = samdb_base_dn(b_state->sam_ctx);
749                 break;
750
751         case DRSUAPI_EXOP_FSMO_REQ_ROLE:
752         case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
753         case DRSUAPI_EXOP_FSMO_REQ_PDC:
754         case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
755         case DRSUAPI_EXOP_REPL_OBJ:
756         case DRSUAPI_EXOP_REPL_SECRET:
757                 DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
758                          (unsigned)req8->extended_op));
759                 return WERR_DS_DRA_NOT_SUPPORTED;
760         }
761
762         getnc_state = b_state->getncchanges_state;
763
764         /* see if a previous replication has been abandoned */
765         if (getnc_state) {
766                 struct ldb_dn *new_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
767                 if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
768                         DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
769                                  ldb_dn_get_linearized(new_dn),
770                                  ldb_dn_get_linearized(getnc_state->ncRoot_dn),
771                                  ldb_dn_get_linearized(getnc_state->last_dn)));
772                         talloc_free(getnc_state);
773                         getnc_state = NULL;
774                 }
775         }
776
777         if (getnc_state == NULL) {
778                 getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state);
779                 if (getnc_state == NULL) {
780                         return WERR_NOMEM;
781                 }
782                 b_state->getncchanges_state = getnc_state;
783                 getnc_state->ncRoot_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
784         }
785
786         if (!ldb_dn_validate(getnc_state->ncRoot_dn) ||
787             ldb_dn_is_null(getnc_state->ncRoot_dn)) {
788                 DEBUG(0,(__location__ ": Bad DN '%s'\n", ncRoot->dn));
789                 return WERR_DS_DRA_INVALID_PARAMETER;
790         }
791
792         /* we need the session key for encrypting password attributes */
793         status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
794         if (!NT_STATUS_IS_OK(status)) {
795                 DEBUG(0,(__location__ ": Failed to get session key\n"));
796                 return WERR_DS_DRA_INTERNAL_ERROR;              
797         }
798
799         /* 
800            TODO: MS-DRSR section 4.1.10.1.1
801            Work out if this is the start of a new cycle */
802
803         if (getnc_state->site_res == NULL) {
804                 char* search_filter;
805                 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
806                 const char *extra_filter;
807
808                 extra_filter = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
809
810                 getnc_state->min_usn = req8->highwatermark.highest_usn;
811
812                 /* Construct response. */
813                 search_filter = talloc_asprintf(mem_ctx,
814                                                 "(uSNChanged>=%llu)",
815                                                 (unsigned long long)(getnc_state->min_usn+1));
816         
817                 if (extra_filter) {
818                         search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
819                 }
820
821                 if (req8->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
822                         search_filter = talloc_asprintf(mem_ctx,
823                                                         "(&%s(isCriticalSystemObject=TRUE))",
824                                                         search_filter);
825                 }
826                 
827                 if (req8->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
828                         scope = LDB_SCOPE_BASE;
829                 }
830                 
831                 if (!search_dn) {
832                         search_dn = getnc_state->ncRoot_dn;
833                 }
834
835                 DEBUG(1,(__location__ ": getncchanges on %s using filter %s\n",
836                          ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
837                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, &getnc_state->site_res,
838                                                       search_dn, scope, attrs,
839                                                       search_filter);
840                 if (ret != LDB_SUCCESS) {
841                         return WERR_DS_DRA_INTERNAL_ERROR;
842                 }
843
844                 if (req8->replica_flags & DRSUAPI_DRS_GET_ANC) {
845                         TYPESAFE_QSORT(getnc_state->site_res->msgs,
846                                        getnc_state->site_res->count,
847                                        site_res_cmp_parent_order);
848                 } else {
849                         TYPESAFE_QSORT(getnc_state->site_res->msgs,
850                                        getnc_state->site_res->count,
851                                        site_res_cmp_usn_order);
852                 }
853
854                 getnc_state->uptodateness_vector = talloc_steal(getnc_state, req8->uptodateness_vector);
855                 if (getnc_state->uptodateness_vector) {
856                         /* make sure its sorted */
857                         TYPESAFE_QSORT(getnc_state->uptodateness_vector->cursors,
858                                        getnc_state->uptodateness_vector->count,
859                                        drsuapi_DsReplicaCursor_compare);
860                 }
861         }
862
863         /* Prefix mapping */
864         schema = dsdb_get_schema(b_state->sam_ctx);
865         if (!schema) {
866                 DEBUG(0,("No schema in sam_ctx\n"));
867                 return WERR_DS_DRA_INTERNAL_ERROR;
868         }
869
870         r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
871         *r->out.ctr->ctr6.naming_context = *ncRoot;
872
873         if (dsdb_find_guid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn, 
874                                  &r->out.ctr->ctr6.naming_context->guid) != LDB_SUCCESS) {
875                 DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
876                          ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
877                 return WERR_DS_DRA_INTERNAL_ERROR;
878         }
879
880         /* find the SID if there is one */
881         dsdb_find_sid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
882
883         dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
884         r->out.ctr->ctr6.mapping_ctr = *ctr;
885
886         r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(b_state->sam_ctx));
887         r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(b_state->sam_ctx));
888
889         r->out.ctr->ctr6.old_highwatermark = req8->highwatermark;
890         r->out.ctr->ctr6.new_highwatermark = req8->highwatermark;
891
892         r->out.ctr->ctr6.first_object = NULL;
893         currentObject = &r->out.ctr->ctr6.first_object;
894
895         /* use this to force single objects at a time, which is useful
896          * for working out what object is giving problems
897          */
898         max_objects = lp_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
899         if (req8->max_object_count < max_objects) {
900                 max_objects = req8->max_object_count;
901         }
902
903         for(i=getnc_state->num_sent; 
904             i<getnc_state->site_res->count && 
905                     (r->out.ctr->ctr6.object_count < max_objects);
906             i++) {
907                 int uSN;
908                 struct drsuapi_DsReplicaObjectListItemEx *obj;
909                 struct ldb_message *msg = getnc_state->site_res->msgs[i];
910
911                 obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
912
913                 werr = get_nc_changes_build_object(obj, msg,
914                                                    b_state->sam_ctx, getnc_state->ncRoot_dn, 
915                                                    schema, &session_key, getnc_state->min_usn,
916                                                    req8->replica_flags, getnc_state->uptodateness_vector);
917                 if (!W_ERROR_IS_OK(werr)) {
918                         return werr;
919                 }
920
921                 werr = get_nc_changes_add_links(b_state->sam_ctx, getnc_state,
922                                                 getnc_state->ncRoot_dn,
923                                                 schema, getnc_state->min_usn,
924                                                 req8->replica_flags,
925                                                 msg,
926                                                 &getnc_state->la_list,
927                                                 &getnc_state->la_count,
928                                                 getnc_state->uptodateness_vector);
929                 if (!W_ERROR_IS_OK(werr)) {
930                         return werr;
931                 }
932
933                 uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
934                 if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
935                         r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
936                 }
937                 if (uSN > getnc_state->highest_usn) {
938                         getnc_state->highest_usn = uSN;
939                 }
940
941                 if (obj->meta_data_ctr == NULL) {
942                         DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
943                                  ldb_dn_get_linearized(msg->dn)));
944                         /* no attributes to send */
945                         talloc_free(obj);
946                         continue;
947                 }
948
949                 r->out.ctr->ctr6.object_count++;
950                 
951                 *currentObject = obj;
952                 currentObject = &obj->next_object;
953
954                 talloc_free(getnc_state->last_dn);
955                 getnc_state->last_dn = ldb_dn_copy(getnc_state, msg->dn);
956
957                 DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));
958         }
959
960         getnc_state->num_sent += r->out.ctr->ctr6.object_count;
961
962         r->out.ctr->ctr6.nc_object_count = getnc_state->site_res->count;
963
964         /* the client can us to call UpdateRefs on its behalf to
965            re-establish monitoring of the NC */
966         if ((req8->replica_flags & DRSUAPI_DRS_ADD_REF) && 
967             !GUID_all_zero(&req8->destination_dsa_guid)) {
968                 struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
969                 ureq.naming_context = ncRoot;
970                 ureq.dest_dsa_dns_name = talloc_asprintf(mem_ctx, "%s._msdcs.%s",
971                                                          GUID_string(mem_ctx, &req8->destination_dsa_guid),
972                                                          lp_realm(dce_call->conn->dce_ctx->lp_ctx));
973                 if (!ureq.dest_dsa_dns_name) {
974                         return WERR_NOMEM;
975                 }
976                 ureq.dest_dsa_guid = req8->destination_dsa_guid;
977                 ureq.options = DRSUAPI_DRS_ADD_REF |
978                         DRSUAPI_DRS_ASYNC_OP |
979                         DRSUAPI_DRS_GETCHG_CHECK;
980                 werr = drsuapi_UpdateRefs(b_state, mem_ctx, &ureq);
981                 if (!W_ERROR_IS_OK(werr)) {
982                         DEBUG(0,(__location__ ": Failed UpdateRefs in DsGetNCChanges - %s\n",
983                                  win_errstr(werr)));
984                 }
985         }
986
987         if (i < getnc_state->site_res->count) {
988                 r->out.ctr->ctr6.more_data = true;
989         } else {
990                 r->out.ctr->ctr6.linked_attributes_count = getnc_state->la_count;
991                 r->out.ctr->ctr6.linked_attributes = talloc_steal(mem_ctx, getnc_state->la_list);
992
993                 LDB_TYPESAFE_QSORT(r->out.ctr->ctr6.linked_attributes, r->out.ctr->ctr6.linked_attributes_count,
994                                    b_state->sam_ctx, linked_attribute_compare);
995
996                 r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
997                 r->out.ctr->ctr6.new_highwatermark.highest_usn = r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn;
998
999                 werr = get_nc_changes_udv(b_state->sam_ctx, getnc_state->ncRoot_dn, 
1000                                           r->out.ctr->ctr6.uptodateness_vector);
1001                 if (!W_ERROR_IS_OK(werr)) {
1002                         return werr;
1003                 }
1004
1005                 talloc_free(getnc_state);
1006                 b_state->getncchanges_state = NULL;
1007         }
1008
1009         if (req8->extended_op != DRSUAPI_EXOP_NONE) {
1010                 r->out.ctr->ctr6.uptodateness_vector = NULL;
1011                 r->out.ctr->ctr6.nc_object_count = 0;
1012                 ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
1013         }
1014
1015         DEBUG(r->out.ctr->ctr6.more_data?2:1,
1016               ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
1017                (unsigned long long)(req8->highwatermark.highest_usn+1),
1018                req8->replica_flags,
1019                ncRoot->dn, r->out.ctr->ctr6.object_count,
1020                i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i,
1021                r->out.ctr->ctr6.linked_attributes_count));
1022
1023 #if 0
1024         if (!r->out.ctr->ctr6.more_data) {
1025                 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);
1026         }
1027 #endif
1028
1029         return WERR_OK;
1030 }