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