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