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