94b7d1d5294c3838d16df32b329dc0b2c9017c0a
[nivanova/samba-autobuild/.git] / source4 / rpc_server / drsuapi / getncchanges.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    implement the DSGetNCChanges call
5
6    Copyright (C) Anatoliy Atanasov 2009
7    Copyright (C) Andrew Tridgell 2009-2010
8    Copyright (C) Andrew Bartlett 2010-2016
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "rpc_server/dcerpc_server.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "param/param.h"
28 #include "librpc/gen_ndr/ndr_drsblobs.h"
29 #include "librpc/gen_ndr/ndr_drsuapi.h"
30 #include "librpc/gen_ndr/ndr_security.h"
31 #include "libcli/security/security.h"
32 #include "libcli/security/session.h"
33 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
34 #include "rpc_server/dcerpc_server_proto.h"
35 #include "rpc_server/common/sid_helper.h"
36 #include "../libcli/drsuapi/drsuapi.h"
37 #include "lib/util/binsearch.h"
38 #include "lib/util/tsort.h"
39 #include "auth/session.h"
40 #include "dsdb/common/util.h"
41 #include "lib/dbwrap/dbwrap.h"
42 #include "lib/dbwrap/dbwrap_rbt.h"
43 #include "librpc/gen_ndr/ndr_misc.h"
44
45 #undef DBGC_CLASS
46 #define DBGC_CLASS            DBGC_DRS_REPL
47
48 #define DRS_GUID_SIZE       16
49
50 /*
51  * state of a partially-completed replication cycle. This state persists
52  * over multiple calls to dcesrv_drsuapi_DsGetNCChanges()
53  */
54 struct drsuapi_getncchanges_state {
55         struct db_context *obj_cache;
56         struct GUID *guids;
57         uint32_t num_records;
58         uint32_t num_processed;
59         struct ldb_dn *ncRoot_dn;
60         struct GUID ncRoot_guid;
61         bool is_schema_nc;
62         bool is_get_anc;
63         uint64_t min_usn;
64         uint64_t max_usn;
65         struct drsuapi_DsReplicaHighWaterMark last_hwm;
66         struct ldb_dn *last_dn;
67         struct drsuapi_DsReplicaHighWaterMark final_hwm;
68         struct drsuapi_DsReplicaCursor2CtrEx *final_udv;
69         struct drsuapi_DsReplicaLinkedAttribute *la_list;
70         uint32_t la_count;
71         struct la_for_sorting *la_sorted;
72         uint32_t la_idx;
73 };
74
75 /* We must keep the GUIDs in NDR form for sorting */
76 struct la_for_sorting {
77         const struct drsuapi_DsReplicaLinkedAttribute *link;
78         uint8_t target_guid[DRS_GUID_SIZE];
79         uint8_t source_guid[DRS_GUID_SIZE];
80 };
81
82 /*
83  * stores the state for a chunk of replication data. This state information
84  * only exists for a single call to dcesrv_drsuapi_DsGetNCChanges()
85  */
86 struct getncchanges_repl_chunk {
87         struct drsuapi_DsGetNCChangesCtr6 *ctr6;
88
89         /* the last object written to the response */
90         struct drsuapi_DsReplicaObjectListItemEx *last_object;
91 };
92
93 static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1,
94                                               const struct drsuapi_DsReplicaHighWaterMark *h2)
95 {
96         if (h1->highest_usn < h2->highest_usn) {
97                 return -1;
98         } else if (h1->highest_usn > h2->highest_usn) {
99                 return 1;
100         } else if (h1->tmp_highest_usn < h2->tmp_highest_usn) {
101                 return -1;
102         } else if (h1->tmp_highest_usn > h2->tmp_highest_usn) {
103                 return 1;
104         } else if (h1->reserved_usn < h2->reserved_usn) {
105                 return -1;
106         } else if (h1->reserved_usn > h2->reserved_usn) {
107                 return 1;
108         }
109
110         return 0;
111 }
112
113 /*
114   build a DsReplicaObjectIdentifier from a ldb msg
115  */
116 static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
117                                                                        const struct ldb_message *msg)
118 {
119         struct drsuapi_DsReplicaObjectIdentifier *identifier;
120         struct dom_sid *sid;
121
122         identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
123         if (identifier == NULL) {
124                 return NULL;
125         }
126
127         identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
128         identifier->guid = samdb_result_guid(msg, "objectGUID");
129
130         sid = samdb_result_dom_sid(identifier, msg, "objectSid");
131         if (sid) {
132                 identifier->sid = *sid;
133         } else {
134                 ZERO_STRUCT(identifier->sid);
135         }
136         return identifier;
137 }
138
139 static int udv_compare(const struct GUID *guid1, struct GUID guid2)
140 {
141         return GUID_compare(guid1, &guid2);
142 }
143
144 /*
145   see if we can filter an attribute using the uptodateness_vector
146  */
147 static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
148                        const struct GUID *originating_invocation_id,
149                        uint64_t originating_usn)
150 {
151         const struct drsuapi_DsReplicaCursor *c;
152         if (udv == NULL) return false;
153         BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id,
154                             originating_invocation_id, udv_compare, c);
155         if (c && originating_usn <= c->highest_usn) {
156                 return true;
157         }
158         return false;
159 }
160
161 static int uint32_t_cmp(uint32_t a1, uint32_t a2)
162 {
163         if (a1 == a2) return 0;
164         return a1 > a2 ? 1 : -1;
165 }
166
167 static int uint32_t_ptr_cmp(uint32_t *a1, uint32_t *a2, void *unused)
168 {
169         if (*a1 == *a2) return 0;
170         return *a1 > *a2 ? 1 : -1;
171 }
172
173 static WERROR getncchanges_attid_remote_to_local(const struct dsdb_schema *schema,
174                                                  const struct dsdb_syntax_ctx *ctx,
175                                                  enum drsuapi_DsAttributeId remote_attid_as_enum,
176                                                  enum drsuapi_DsAttributeId *local_attid_as_enum,
177                                                  const struct dsdb_attribute **_sa)
178 {
179         WERROR werr;
180         const struct dsdb_attribute *sa = NULL;
181
182         if (ctx->pfm_remote == NULL) {
183                 DEBUG(7, ("No prefixMap supplied, falling back to local prefixMap.\n"));
184                 goto fail;
185         }
186
187         werr = dsdb_attribute_drsuapi_remote_to_local(ctx,
188                                                       remote_attid_as_enum,
189                                                       local_attid_as_enum,
190                                                       _sa);
191         if (!W_ERROR_IS_OK(werr)) {
192                 DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n"));
193                 goto fail;
194         }
195
196         return werr;
197 fail:
198
199         sa = dsdb_attribute_by_attributeID_id(schema, remote_attid_as_enum);
200         if (sa == NULL) {
201                 return WERR_DS_DRA_SCHEMA_MISMATCH;
202         } else {
203                 if (local_attid_as_enum != NULL) {
204                         *local_attid_as_enum = sa->attributeID_id;
205                 }
206                 if (_sa != NULL) {
207                         *_sa = sa;
208                 }
209                 return WERR_OK;
210         }
211 }
212
213 /*
214  * Similar to function in repl_meta_data without the extra
215  * dependencies.
216  */
217 static WERROR get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el,
218                                   struct parsed_dn **pdn)
219 {
220         /* Here we get a list of 'struct parsed_dns' without the parsing */
221         int i;
222         *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
223                                  el->num_values);
224         if (!*pdn) {
225                 return WERR_DS_DRA_INTERNAL_ERROR;
226         }
227
228         for (i = 0; i < el->num_values; i++) {
229                 (*pdn)[i].v = &el->values[i];
230         }
231
232         return WERR_OK;
233 }
234
235 static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
236                                                 TALLOC_CTX *mem_ctx,
237                                                 struct ldb_message **msg,
238                                                 struct ldb_dn *object_dn,
239                                                 const struct GUID *object_guid,
240                                                 const struct dsdb_attribute *sa,
241                                                 struct replPropertyMetaData1 *meta_data,
242                                                 struct ldb_message *revealed_users)
243 {
244         enum ndr_err_code ndr_err;
245         int ldb_err;
246         char *attr_str = NULL;
247         char *attr_hex = NULL;
248         DATA_BLOB attr_blob;
249         struct ldb_message_element *existing = NULL, *el_add = NULL, *el_del = NULL;
250         const char * const * secret_attributes = ldb_get_opaque(sam_ctx, "LDB_SECRET_ATTRIBUTE_LIST");
251
252         if (!ldb_attr_in_list(secret_attributes,
253                               sa->lDAPDisplayName)) {
254                 return WERR_OK;
255         }
256
257
258         ndr_err = ndr_push_struct_blob(&attr_blob, mem_ctx, meta_data, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaData1);
259         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
260                 return WERR_DS_DRA_INTERNAL_ERROR;
261         }
262
263         attr_hex = hex_encode_talloc(mem_ctx, attr_blob.data, attr_blob.length);
264         if (attr_hex == NULL) {
265                 return WERR_NOT_ENOUGH_MEMORY;
266         }
267
268         attr_str = talloc_asprintf(mem_ctx, "B:%zd:%s:%s", attr_blob.length*2, attr_hex, ldb_dn_get_linearized(object_dn));
269         if (attr_str == NULL) {
270                 return WERR_NOT_ENOUGH_MEMORY;
271         }
272
273         existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
274         if (existing != NULL) {
275                 /* Replace the old value (if one exists) with the current one */
276                 struct parsed_dn *link_dns;
277                 struct parsed_dn *exact = NULL, *unused = NULL;
278                 WERROR werr;
279                 uint8_t attid[4];
280                 DATA_BLOB partial_meta;
281
282                 werr = get_parsed_dns_trusted(mem_ctx, existing, &link_dns);
283                 if (!W_ERROR_IS_OK(werr)) {
284                         return werr;
285                 }
286
287                 /* Construct a partial metadata blob to match on in the DB */
288                 SIVAL(attid, 0, sa->attributeID_id);
289                 partial_meta.length = 4;
290                 partial_meta.data = attid;
291
292                 /* Binary search using GUID and attribute id for uniqueness */
293                 ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values,
294                                          object_guid, object_dn,
295                                          partial_meta, 4,
296                                          &exact, &unused,
297                                          DSDB_SYNTAX_BINARY_DN, true);
298
299                 if (ldb_err != LDB_SUCCESS) {
300                         DEBUG(0,(__location__ ": Failed parsed DN find - %s\n",
301                                  ldb_errstring(sam_ctx)));
302                         return WERR_DS_DRA_INTERNAL_ERROR;
303                 }
304
305                 if (exact != NULL) {
306                         /* Perform some verification of the blob */
307                         struct replPropertyMetaData1 existing_meta_data;
308                         ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_dn->extra_part,
309                                                                    &existing_meta_data,
310                                                                    (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
311                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
312                                 return WERR_DS_DRA_INTERNAL_ERROR;
313                         }
314
315                         if (existing_meta_data.attid == sa->attributeID_id) {
316                                 ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
317                                 if (ldb_err != LDB_SUCCESS) {
318                                         return WERR_DS_DRA_INTERNAL_ERROR;
319                                 }
320
321                                 el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
322                                 if (el_del->values == NULL) {
323                                         return WERR_NOT_ENOUGH_MEMORY;
324                                 }
325                                 el_del->values[0] = *exact->v;
326                                 el_del->num_values = 1;
327                         } else {
328                                 return WERR_DS_DRA_INTERNAL_ERROR;
329                         }
330                 }
331         }
332
333         ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_ADD, &el_add);
334         if (ldb_err != LDB_SUCCESS) {
335                 return WERR_DS_DRA_INTERNAL_ERROR;
336         }
337
338         el_add->values = talloc_array((*msg)->elements, struct ldb_val, 1);
339         if (el_add->values == NULL) {
340                 return WERR_NOT_ENOUGH_MEMORY;
341
342         }
343
344         el_add->values[0] = data_blob_string_const(attr_str);
345         el_add->num_values = 1;
346
347         return WERR_OK;
348 }
349
350 /*
351  * This function filter attributes for build_object based on the
352  * uptodatenessvector and partial attribute set.
353  *
354  * Any secret attributes are forced here for REPL_SECRET, and audited at this
355  * point with msDS-RevealedUsers.
356  */
357 static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItemEx *obj,
358                                           struct replPropertyMetaDataBlob md,
359                                           struct ldb_context *sam_ctx,
360                                           const struct ldb_message *msg,
361                                           const struct GUID *guid,
362                                           uint32_t *count,
363                                           uint64_t highest_usn,
364                                           const struct dsdb_attribute *rdn_sa,
365                                           struct dsdb_schema *schema,
366                                           struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
367                                           struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
368                                           uint32_t *local_pas,
369                                           uint32_t *attids,
370                                           bool exop_secret,
371                                           struct ldb_message **revealed_list_msg,
372                                           struct ldb_message *existing_revealed_list_msg)
373 {
374         uint32_t i, n;
375         WERROR werr;
376         for (n=i=0; i<md.ctr.ctr1.count; i++) {
377                 const struct dsdb_attribute *sa;
378                 bool force_attribute = false;
379
380                 /* if the attribute has not changed, and it is not the
381                    instanceType then don't include it */
382                 if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
383                     !exop_secret &&
384                     md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) continue;
385
386                 /* don't include the rDN */
387                 if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
388
389                 sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
390                 if (!sa) {
391                         DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n",
392                                  (unsigned int)md.ctr.ctr1.array[i].attid,
393                                  ldb_dn_get_linearized(msg->dn)));
394                         return WERR_DS_DRA_INTERNAL_ERROR;
395                 }
396
397                 if (sa->linkID) {
398                         struct ldb_message_element *el;
399                         el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
400                         if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
401                                 /* don't send upgraded links inline */
402                                 continue;
403                         }
404                 }
405
406                 if (exop_secret &&
407                     !dsdb_attr_in_rodc_fas(sa)) {
408                         force_attribute = true;
409                         DEBUG(4,("Forcing attribute %s in %s\n",
410                                  sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
411                         werr = getncchanges_update_revealed_list(sam_ctx, obj,
412                                                                  revealed_list_msg,
413                                                                  msg->dn, guid, sa,
414                                                                  &md.ctr.ctr1.array[i],
415                                                                  existing_revealed_list_msg);
416                         if (!W_ERROR_IS_OK(werr)) {
417                                 return werr;
418                         }
419                 }
420
421                 /* filter by uptodateness_vector */
422                 if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType &&
423                     !force_attribute &&
424                     udv_filter(uptodateness_vector,
425                                &md.ctr.ctr1.array[i].originating_invocation_id,
426                                md.ctr.ctr1.array[i].originating_usn)) {
427                         continue;
428                 }
429
430                 /* filter by partial_attribute_set */
431                 if (partial_attribute_set && !force_attribute) {
432                         uint32_t *result = NULL;
433                         BINARY_ARRAY_SEARCH_V(local_pas, partial_attribute_set->num_attids, sa->attributeID_id,
434                                               uint32_t_cmp, result);
435                         if (result == NULL) {
436                                 continue;
437                         }
438                 }
439
440                 obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
441                 obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
442                 obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
443                 obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
444                 attids[n] = md.ctr.ctr1.array[i].attid;
445
446                 n++;
447         }
448
449         *count = n;
450
451         return WERR_OK;
452 }
453
454 /* 
455   drsuapi_DsGetNCChanges for one object
456 */
457 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
458                                           const struct ldb_message *msg,
459                                           struct ldb_context *sam_ctx,
460                                           struct drsuapi_getncchanges_state *getnc_state,
461                                           struct dsdb_schema *schema,
462                                           DATA_BLOB *session_key,
463                                           struct drsuapi_DsGetNCChangesRequest10 *req10,
464                                           bool force_object_return,
465                                           uint32_t *local_pas,
466                                           struct ldb_dn *machine_dn,
467                                           const struct GUID *guid)
468 {
469         const struct ldb_val *md_value;
470         uint32_t i, n;
471         struct replPropertyMetaDataBlob md;
472         uint32_t rid = 0;
473         int ldb_err;
474         enum ndr_err_code ndr_err;
475         uint32_t *attids;
476         const char *rdn;
477         const struct dsdb_attribute *rdn_sa;
478         uint64_t uSNChanged;
479         unsigned int instanceType;
480         struct dsdb_syntax_ctx syntax_ctx;
481         struct ldb_result *res = NULL;
482         WERROR werr;
483         int ret;
484         uint32_t replica_flags = req10->replica_flags;
485         struct drsuapi_DsPartialAttributeSet *partial_attribute_set =
486                         req10->partial_attribute_set;
487         struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector =
488                         req10->uptodateness_vector;
489         enum drsuapi_DsExtendedOperation extended_op = req10->extended_op;
490         bool is_schema_nc = getnc_state->is_schema_nc;
491         uint64_t highest_usn = getnc_state->min_usn;
492
493         /* make dsdb sytanx context for conversions */
494         dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
495         syntax_ctx.is_schema_nc = is_schema_nc;
496
497         uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
498         instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
499         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
500                 obj->is_nc_prefix = true;
501                 obj->parent_object_guid = NULL;
502         } else {
503                 obj->is_nc_prefix = false;
504                 obj->parent_object_guid = talloc(obj, struct GUID);
505                 if (obj->parent_object_guid == NULL) {
506                         return WERR_DS_DRA_INTERNAL_ERROR;
507                 }
508                 *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
509                 if (GUID_all_zero(obj->parent_object_guid)) {
510                         DEBUG(0,(__location__ ": missing parentGUID for %s\n",
511                                  ldb_dn_get_linearized(msg->dn)));
512                         return WERR_DS_DRA_INTERNAL_ERROR;
513                 }
514         }
515         obj->next_object = NULL;
516
517         md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
518         if (!md_value) {
519                 /* nothing to send */
520                 return WERR_OK;
521         }
522
523         if (instanceType & INSTANCE_TYPE_UNINSTANT) {
524                 /* don't send uninstantiated objects */
525                 return WERR_OK;
526         }
527
528         if (uSNChanged <= highest_usn) {
529                 /* nothing to send */
530                 return WERR_OK;
531         }
532
533         ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
534                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
535         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
536                 return WERR_DS_DRA_INTERNAL_ERROR;
537         }
538
539         if (md.version != 1) {
540                 return WERR_DS_DRA_INTERNAL_ERROR;
541         }
542
543         rdn = ldb_dn_get_rdn_name(msg->dn);
544         if (rdn == NULL) {
545                 DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
546                 return WERR_DS_DRA_INTERNAL_ERROR;
547         }
548
549         rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
550         if (rdn_sa == NULL) {
551                 DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n",
552                          rdn, ldb_dn_get_linearized(msg->dn)));
553                 return WERR_DS_DRA_INTERNAL_ERROR;
554         }
555
556         obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
557         attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
558
559         obj->object.identifier = get_object_identifier(obj, msg);
560         if (obj->object.identifier == NULL) {
561                 return WERR_NOT_ENOUGH_MEMORY;
562         }
563         dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
564
565         obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
566
567         if (extended_op == DRSUAPI_EXOP_REPL_SECRET) {
568                 /* Get the existing revealed users for the destination */
569                 struct ldb_message *revealed_list_msg = NULL;
570                 struct ldb_message *existing_revealed_list_msg = NULL;
571                 const char *machine_attrs[] = {
572                         "msDS-RevealedUsers",
573                         NULL
574                 };
575
576                 revealed_list_msg = ldb_msg_new(sam_ctx);
577                 if (revealed_list_msg == NULL) {
578                         return WERR_NOT_ENOUGH_MEMORY;
579                 }
580                 revealed_list_msg->dn = machine_dn;
581
582                 ret = ldb_transaction_start(sam_ctx);
583                 if (ret != LDB_SUCCESS) {
584                         DEBUG(0,(__location__ ": Failed transaction start - %s\n",
585                                  ldb_errstring(sam_ctx)));
586                         return WERR_DS_DRA_INTERNAL_ERROR;
587                 }
588
589                 ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
590                 if (ldb_err != LDB_SUCCESS || res->count != 1) {
591                         ldb_transaction_cancel(sam_ctx);
592                         return WERR_DS_DRA_INTERNAL_ERROR;
593                 }
594
595                 existing_revealed_list_msg = res->msgs[0];
596
597                 werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg,
598                                                    guid, &n, highest_usn,
599                                                    rdn_sa, schema,
600                                                    uptodateness_vector,
601                                                    partial_attribute_set, local_pas,
602                                                    attids,
603                                                    true,
604                                                    &revealed_list_msg,
605                                                    existing_revealed_list_msg);
606                 if (!W_ERROR_IS_OK(werr)) {
607                         ldb_transaction_cancel(sam_ctx);
608                         return werr;
609                 }
610
611                 if (revealed_list_msg != NULL) {
612                         ret = ldb_modify(sam_ctx, revealed_list_msg);
613                         if (ret != LDB_SUCCESS) {
614                                 DEBUG(0,(__location__ ": Failed to alter revealed links - %s\n",
615                                          ldb_errstring(sam_ctx)));
616                                 ldb_transaction_cancel(sam_ctx);
617                                 return WERR_DS_DRA_INTERNAL_ERROR;
618                         }
619                 }
620
621                 ret = ldb_transaction_commit(sam_ctx);
622                 if (ret != LDB_SUCCESS) {
623                         DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
624                                  ldb_errstring(sam_ctx)));
625                         return WERR_DS_DRA_INTERNAL_ERROR;
626                 }
627         } else {
628                 werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid,
629                                                    &n, highest_usn, rdn_sa,
630                                                    schema, uptodateness_vector,
631                                                    partial_attribute_set, local_pas,
632                                                    attids,
633                                                    false,
634                                                    NULL,
635                                                    NULL);
636                 if (!W_ERROR_IS_OK(werr)) {
637                         return werr;
638                 }
639         }
640
641         /* ignore it if its an empty change. Note that renames always
642          * change the 'name' attribute, so they won't be ignored by
643          * this
644
645          * the force_object_return check is used to force an empty
646          * object return when we timeout in the getncchanges loop.
647          * This allows us to return an empty object, which keeps the
648          * client happy while preventing timeouts
649          */
650         if (n == 0 ||
651             (n == 1 &&
652              attids[0] == DRSUAPI_ATTID_instanceType &&
653              !force_object_return)) {
654                 talloc_free(obj->meta_data_ctr);
655                 obj->meta_data_ctr = NULL;
656                 return WERR_OK;
657         }
658
659         obj->meta_data_ctr->count = n;
660
661         obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
662         obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
663         obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
664                                                             obj->object.attribute_ctr.num_attributes);
665         if (obj->object.attribute_ctr.attributes == NULL) {
666                 return WERR_NOT_ENOUGH_MEMORY;
667         }
668
669         /*
670          * Note that the meta_data array and the attributes array must
671          * be the same size and in the same order
672          */
673         for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
674                 struct ldb_message_element *el;
675                 const struct dsdb_attribute *sa;
676
677                 sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
678                 if (!sa) {
679                         DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
680                         return WERR_DS_DRA_INTERNAL_ERROR;
681                 }
682
683                 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
684                 if (el == NULL) {
685                         /* this happens for attributes that have been removed */
686                         DEBUG(5,("No element '%s' for attributeID %u in message\n",
687                                  sa->lDAPDisplayName, attids[i]));
688                         ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
689                         obj->object.attribute_ctr.attributes[i].attid =
690                                         dsdb_attribute_get_attid(sa, syntax_ctx.is_schema_nc);
691                 } else {
692                         werr = sa->syntax->ldb_to_drsuapi(&syntax_ctx, sa, el, obj,
693                                                           &obj->object.attribute_ctr.attributes[i]);
694                         if (!W_ERROR_IS_OK(werr)) {
695                                 DEBUG(0,("Unable to convert %s on %s to DRS object - %s\n",
696                                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
697                                          win_errstr(werr)));
698                                 return werr;
699                         }
700                         /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
701                          * check if attribute is secret and send a null value
702                          */
703                         if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
704                                 drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
705                                                                  &obj->meta_data_ctr->meta_data[i]);
706                         }
707                         /* some attributes needs to be encrypted
708                            before being sent */
709                         werr = drsuapi_encrypt_attribute(obj, session_key, rid, 
710                                                          &obj->object.attribute_ctr.attributes[i]);
711                         if (!W_ERROR_IS_OK(werr)) {
712                                 DEBUG(0,("Unable to encrypt %s on %s in DRS object - %s\n",
713                                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
714                                          win_errstr(werr)));
715                                 return werr;
716                         }
717                 }
718                 if (attids[i] != obj->object.attribute_ctr.attributes[i].attid) {
719                         DEBUG(0, ("Unable to replicate attribute %s on %s via DRS, incorrect attributeID:  "
720                                   "0x%08x vs 0x%08x "
721                                   "Run dbcheck!\n",
722                                   sa->lDAPDisplayName,
723                                   ldb_dn_get_linearized(msg->dn),
724                                   attids[i],
725                                   obj->object.attribute_ctr.attributes[i].attid));
726                         return WERR_DS_DATABASE_ERROR;
727                 }
728         }
729
730         return WERR_OK;
731 }
732
733 /*
734   add one linked attribute from an object to the list of linked
735   attributes in a getncchanges request
736  */
737 static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
738                                     struct ldb_context *sam_ctx,
739                                     const struct dsdb_schema *schema,
740                                     const struct dsdb_attribute *sa,
741                                     const struct ldb_message *msg,
742                                     struct dsdb_dn *dsdb_dn,
743                                     struct drsuapi_DsReplicaLinkedAttribute **la_list,
744                                     uint32_t *la_count,
745                                     bool is_schema_nc)
746 {
747         struct drsuapi_DsReplicaLinkedAttribute *la;
748         bool active;
749         NTSTATUS status;
750         WERROR werr;
751
752         (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
753         W_ERROR_HAVE_NO_MEMORY(*la_list);
754
755         la = &(*la_list)[*la_count];
756
757         la->identifier = get_object_identifier(*la_list, msg);
758         W_ERROR_HAVE_NO_MEMORY(la->identifier);
759
760         active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
761
762         if (!active) {
763                 /* We have to check that the inactive link still point to an existing object */
764                 struct GUID guid;
765                 struct ldb_dn *tdn;
766                 int ret;
767                 const char *v;
768
769                 v = ldb_msg_find_attr_as_string(msg, "isDeleted", "FALSE");
770                 if (strncmp(v, "TRUE", 4) == 0) {
771                         /*
772                           * Note: we skip the transmition of the deleted link even if the other part used to
773                           * know about it because when we transmit the deletion of the object, the link will
774                           * be deleted too due to deletion of object where link points and Windows do so.
775                           */
776                         if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008_R2) {
777                                 v = ldb_msg_find_attr_as_string(msg, "isRecycled", "FALSE");
778                                 /*
779                                  * On Windows 2008R2 isRecycled is always present even if FL or DL are < FL 2K8R2
780                                  * if it join an existing domain with deleted objets, it firsts impose to have a
781                                  * schema with the is-Recycled object and for all deleted objects it adds the isRecycled
782                                  * either during initial replication or after the getNCChanges.
783                                  * Behavior of samba has been changed to always have this attribute if it's present in the schema.
784                                  *
785                                  * So if FL <2K8R2 isRecycled might be here or not but we don't care, it's meaning less.
786                                  * If FL >=2K8R2 we are sure that this attribute will be here.
787                                  * For this kind of forest level we do not return the link if the object is recycled
788                                  * (isRecycled = true).
789                                  */
790                                 if (strncmp(v, "TRUE", 4) == 0) {
791                                         DEBUG(2, (" object %s is recycled, not returning linked attribute !\n",
792                                                                 ldb_dn_get_linearized(msg->dn)));
793                                         return WERR_OK;
794                                 }
795                         } else {
796                                 return WERR_OK;
797                         }
798                 }
799                 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
800                 if (!NT_STATUS_IS_OK(status)) {
801                         DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
802                                 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
803                         return ntstatus_to_werror(status);
804                 }
805                 ret = dsdb_find_dn_by_guid(sam_ctx, mem_ctx, &guid, 0, &tdn);
806                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
807                         DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
808                                                 GUID_string(mem_ctx, &guid)));
809                         return WERR_OK;
810                 } else if (ret != LDB_SUCCESS) {
811                         DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
812                                                 GUID_string(mem_ctx, &guid),
813                                                 ret));
814                         return WERR_OK;
815                 }
816         }
817         la->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
818         la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
819
820         status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
821         if (!NT_STATUS_IS_OK(status)) {
822                 DEBUG(0,(__location__ " No RMD_VERSION in linked attribute '%s' in '%s'\n",
823                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
824                 return ntstatus_to_werror(status);
825         }
826         status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
827         if (!NT_STATUS_IS_OK(status)) {
828                 DEBUG(0,(__location__ " No RMD_CHANGETIME in linked attribute '%s' in '%s'\n",
829                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
830                 return ntstatus_to_werror(status);
831         }
832         status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
833         if (!NT_STATUS_IS_OK(status)) {
834                 DEBUG(0,(__location__ " No RMD_INVOCID in linked attribute '%s' in '%s'\n",
835                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
836                 return ntstatus_to_werror(status);
837         }
838         status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
839         if (!NT_STATUS_IS_OK(status)) {
840                 DEBUG(0,(__location__ " No RMD_ORIGINATING_USN in linked attribute '%s' in '%s'\n",
841                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
842                 return ntstatus_to_werror(status);
843         }
844
845         status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
846         if (!NT_STATUS_IS_OK(status)) {
847                 /* this is possible for upgraded links */
848                 la->originating_add_time = la->meta_data.originating_change_time;
849         }
850
851         werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
852         W_ERROR_NOT_OK_RETURN(werr);
853
854         (*la_count)++;
855         return WERR_OK;
856 }
857
858
859 /*
860   add linked attributes from an object to the list of linked
861   attributes in a getncchanges request
862  */
863 static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
864                                        TALLOC_CTX *mem_ctx,
865                                        bool is_schema_nc,
866                                        struct dsdb_schema *schema,
867                                        uint64_t highest_usn,
868                                        uint32_t replica_flags,
869                                        const struct ldb_message *msg,
870                                        struct drsuapi_DsReplicaLinkedAttribute **la_list,
871                                        uint32_t *la_count,
872                                        struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
873 {
874         unsigned int i;
875         TALLOC_CTX *tmp_ctx = NULL;
876         uint64_t uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
877         bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
878
879         if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
880                 if (!is_critical) {
881                         return WERR_OK;
882                 }
883         }
884
885         if (uSNChanged <= highest_usn) {
886                 return WERR_OK;
887         }
888
889         tmp_ctx = talloc_new(mem_ctx);
890         if (tmp_ctx == NULL) {
891                 return WERR_NOT_ENOUGH_MEMORY;
892         }
893
894         for (i=0; i<msg->num_elements; i++) {
895                 struct ldb_message_element *el = &msg->elements[i];
896                 const struct dsdb_attribute *sa;
897                 unsigned int j;
898
899                 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
900
901                 if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
902                         /* we only want forward links */
903                         continue;
904                 }
905
906                 if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
907                         /* its an old style link, it will have been
908                          * sent in the main replication data */
909                         continue;
910                 }
911
912                 for (j=0; j<el->num_values; j++) {
913                         struct dsdb_dn *dsdb_dn;
914                         uint64_t local_usn;
915                         uint64_t originating_usn;
916                         NTSTATUS status, status2;
917                         WERROR werr;
918                         struct GUID originating_invocation_id;
919
920                         dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
921                         if (dsdb_dn == NULL) {
922                                 DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
923                                          el->name, ldb_dn_get_linearized(msg->dn)));
924                                 talloc_free(tmp_ctx);
925                                 return WERR_DS_DRA_INTERNAL_ERROR;
926                         }
927
928                         status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
929                         if (!NT_STATUS_IS_OK(status)) {
930                                 /* this can happen for attributes
931                                    given to us with old style meta
932                                    data */
933                                 continue;
934                         }
935
936                         if (local_usn > uSNChanged) {
937                                 DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
938                                          el->name, ldb_dn_get_linearized(msg->dn)));
939                                 talloc_free(tmp_ctx);
940                                 return WERR_DS_DRA_INTERNAL_ERROR;
941                         }
942
943                         if (local_usn <= highest_usn) {
944                                 continue;
945                         }
946
947                         status = dsdb_get_extended_dn_guid(dsdb_dn->dn,
948                                                            &originating_invocation_id,
949                                                            "RMD_INVOCID");
950                         status2 = dsdb_get_extended_dn_uint64(dsdb_dn->dn,
951                                                               &originating_usn,
952                                                               "RMD_ORIGINATING_USN");
953
954                         if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2)) {
955                                 if (udv_filter(uptodateness_vector,
956                                                &originating_invocation_id,
957                                                originating_usn)) {
958                                         continue;
959                                 }
960                         }
961
962                         werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema,
963                                                      sa, msg, dsdb_dn, la_list,
964                                                      la_count, is_schema_nc);
965                         if (!W_ERROR_IS_OK(werr)) {
966                                 talloc_free(tmp_ctx);
967                                 return werr;
968                         }
969                 }
970         }
971
972         talloc_free(tmp_ctx);
973         return WERR_OK;
974 }
975
976 /*
977   fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
978  */
979 static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
980                                  struct ldb_dn *ncRoot_dn,
981                                  struct drsuapi_DsReplicaCursor2CtrEx *udv)
982 {
983         int ret;
984
985         udv->version = 2;
986         udv->reserved1 = 0;
987         udv->reserved2 = 0;
988
989         ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
990         if (ret != LDB_SUCCESS) {
991                 DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
992                          ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
993                 return WERR_DS_DRA_INTERNAL_ERROR;
994         }
995
996         return WERR_OK;
997 }
998
999
1000 /* comparison function for linked attributes - see CompareLinks() in
1001  * MS-DRSR section 4.1.10.5.17 */
1002 static int linked_attribute_compare(const struct la_for_sorting *la1,
1003                                     const struct la_for_sorting *la2,
1004                                     void *opaque)
1005 {
1006         int c;
1007         c = memcmp(la1->source_guid,
1008                    la2->source_guid, sizeof(la2->source_guid));
1009         if (c != 0) {
1010                 return c;
1011         }
1012
1013         if (la1->link->attid != la2->link->attid) {
1014                 return la1->link->attid < la2->link->attid? -1:1;
1015         }
1016
1017         if ((la1->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
1018             (la2->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
1019                 return (la1->link->flags &
1020                         DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
1021         }
1022
1023         return memcmp(la1->target_guid,
1024                       la2->target_guid, sizeof(la2->target_guid));
1025 }
1026
1027 struct drsuapi_changed_objects {
1028         struct ldb_dn *dn;
1029         struct GUID guid;
1030         uint64_t usn;
1031 };
1032
1033 /*
1034   sort the objects we send first by uSNChanged
1035  */
1036 static int site_res_cmp_usn_order(struct drsuapi_changed_objects *m1,
1037                                   struct drsuapi_changed_objects *m2,
1038                                   struct drsuapi_getncchanges_state *getnc_state)
1039 {
1040         int ret;
1041
1042         ret = ldb_dn_compare(getnc_state->ncRoot_dn, m1->dn);
1043         if (ret == 0) {
1044                 return -1;
1045         }
1046
1047         ret = ldb_dn_compare(getnc_state->ncRoot_dn, m2->dn);
1048         if (ret == 0) {
1049                 return 1;
1050         }
1051
1052         if (m1->usn == m2->usn) {
1053                 return ldb_dn_compare(m2->dn, m1->dn);
1054         }
1055
1056         if (m1->usn < m2->usn) {
1057                 return -1;
1058         }
1059
1060         return 1;
1061 }
1062
1063
1064 /*
1065   handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
1066  */
1067 static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
1068                                      TALLOC_CTX *mem_ctx,
1069                                      struct drsuapi_DsGetNCChangesRequest10 *req10,
1070                                      struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1071                                      struct ldb_dn **rid_manager_dn)
1072 {
1073         struct ldb_dn *req_dn, *ntds_dn = NULL;
1074         int ret;
1075         struct ldb_context *ldb = b_state->sam_ctx;
1076         struct ldb_result *ext_res;
1077         struct dsdb_fsmo_extended_op *exop;
1078         bool is_us;
1079
1080         /*
1081           steps:
1082             - verify that the DN being asked for is the RID Manager DN
1083             - verify that we are the RID Manager
1084          */
1085
1086         /* work out who is the RID Manager, also return to caller */
1087         ret = samdb_rid_manager_dn(ldb, mem_ctx, rid_manager_dn);
1088         if (ret != LDB_SUCCESS) {
1089                 DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
1090                 return WERR_DS_DRA_INTERNAL_ERROR;
1091         }
1092
1093         req_dn = drs_ObjectIdentifier_to_dn(mem_ctx, ldb, req10->naming_context);
1094         if (!ldb_dn_validate(req_dn) ||
1095             ldb_dn_compare(req_dn, *rid_manager_dn) != 0) {
1096                 /* that isn't the RID Manager DN */
1097                 DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n",
1098                          drs_ObjectIdentifier_to_string(mem_ctx, req10->naming_context)));
1099                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1100                 return WERR_OK;
1101         }
1102
1103         /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1104         ret = dsdb_find_dn_by_guid(ldb, mem_ctx, &req10->destination_dsa_guid, 0, &ntds_dn);
1105         if (ret != LDB_SUCCESS) {
1106                 DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1107                           GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1108                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1109                 return WERR_OK;
1110         }
1111
1112         /* find the DN of the RID Manager */
1113         ret = samdb_reference_dn_is_our_ntdsa(ldb, *rid_manager_dn, "fSMORoleOwner", &is_us);
1114         if (ret != LDB_SUCCESS) {
1115                 DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1116                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1117                 return WERR_DS_DRA_INTERNAL_ERROR;
1118         }
1119
1120         if (!is_us) {
1121                 /* we're not the RID Manager - go away */
1122                 DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
1123                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1124                 return WERR_OK;
1125         }
1126
1127         exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
1128         W_ERROR_HAVE_NO_MEMORY(exop);
1129
1130         exop->fsmo_info = req10->fsmo_info;
1131         exop->destination_dsa_guid = req10->destination_dsa_guid;
1132
1133         ret = ldb_transaction_start(ldb);
1134         if (ret != LDB_SUCCESS) {
1135                 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1136                          ldb_errstring(ldb)));
1137                 return WERR_DS_DRA_INTERNAL_ERROR;
1138         }
1139
1140         ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
1141         if (ret != LDB_SUCCESS) {
1142                 DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
1143                          ldb_errstring(ldb)));
1144                 ldb_transaction_cancel(ldb);
1145                 return WERR_DS_DRA_INTERNAL_ERROR;
1146         }
1147
1148         ret = ldb_transaction_commit(ldb);
1149         if (ret != LDB_SUCCESS) {
1150                 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1151                          ldb_errstring(ldb)));
1152                 return WERR_DS_DRA_INTERNAL_ERROR;
1153         }
1154
1155         talloc_free(ext_res);
1156
1157         DEBUG(2,("Allocated RID pool for server %s\n",
1158                  GUID_string(mem_ctx, &req10->destination_dsa_guid)));
1159
1160         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1161
1162         return WERR_OK;
1163 }
1164
1165 /*
1166   handle a DRSUAPI_EXOP_REPL_SECRET call
1167  */
1168 static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
1169                                        TALLOC_CTX *mem_ctx,
1170                                        struct drsuapi_DsGetNCChangesRequest10 *req10,
1171                                        struct dom_sid *user_sid,
1172                                        struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1173                                        bool has_get_all_changes,
1174                                        struct ldb_dn **machine_dn)
1175 {
1176         struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1177         struct ldb_dn *obj_dn = NULL;
1178         struct ldb_dn *ntds_dn = NULL, *server_dn = NULL;
1179         struct ldb_dn *rodc_dn, *krbtgt_link_dn;
1180         int ret;
1181         const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", "objectGUID", NULL };
1182         const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
1183         struct ldb_result *rodc_res = NULL, *obj_res = NULL;
1184         const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids;
1185         const struct dom_sid *object_sid = NULL;
1186         WERROR werr;
1187         const struct dom_sid *additional_sids[] = { NULL, NULL };
1188
1189         DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n",
1190                  drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
1191
1192         /*
1193          * we need to work out if we will allow this DC to
1194          * replicate the secrets for this object
1195          *
1196          * see 4.1.10.5.14 GetRevealSecretsPolicyForUser for details
1197          * of this function
1198          */
1199
1200         if (b_state->sam_ctx_system == NULL) {
1201                 /* this operation needs system level access */
1202                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_ACCESS_DENIED;
1203                 return WERR_DS_DRA_ACCESS_DENIED;
1204         }
1205
1206         /*
1207          * Before we accept or deny, fetch the machine DN for the destination
1208          * DSA GUID.
1209          *
1210          * If we are the RODC, we will check that this matches the SID.
1211          */
1212         ret = dsdb_find_dn_by_guid(b_state->sam_ctx_system, mem_ctx,
1213                                    &req10->destination_dsa_guid, 0,
1214                                    &ntds_dn);
1215         if (ret != LDB_SUCCESS) {
1216                 goto failed;
1217         }
1218
1219         server_dn = ldb_dn_get_parent(mem_ctx, ntds_dn);
1220         if (server_dn == NULL) {
1221                 goto failed;
1222         }
1223
1224         ret = samdb_reference_dn(b_state->sam_ctx_system, mem_ctx, server_dn,
1225                                  "serverReference", machine_dn);
1226
1227         if (ret != LDB_SUCCESS) {
1228                 goto failed;
1229         }
1230
1231         /*
1232          * In MS-DRSR.pdf 5.99 IsGetNCChangesPermissionGranted
1233          *
1234          * The pseudo code indicate
1235          * revealsecrets = true
1236          * if IsRevealSecretRequest(msgIn) then
1237          *   if AccessCheckCAR(ncRoot, Ds-Replication-Get-Changes-All) = false
1238          *   then
1239          *     if (msgIn.ulExtendedOp = EXOP_REPL_SECRETS) then
1240          *     <... check if this account is ok to be replicated on this DC ...>
1241          *     <... and if not reveal secrets = no ...>
1242          *     else
1243          *       reveal secrets = false
1244          *     endif
1245          *   endif
1246          * endif
1247          *
1248          * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC)
1249          * then you can do EXOP_REPL_SECRETS
1250          */
1251         obj_dn = drs_ObjectIdentifier_to_dn(mem_ctx, b_state->sam_ctx_system, ncRoot);
1252         if (!ldb_dn_validate(obj_dn)) goto failed;
1253
1254         if (has_get_all_changes) {
1255                 goto allowed;
1256         }
1257
1258         rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "<SID=%s>",
1259                                  dom_sid_string(mem_ctx, user_sid));
1260         if (!ldb_dn_validate(rodc_dn)) goto failed;
1261
1262         /* do the two searches we need */
1263         ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
1264                              DSDB_SEARCH_SHOW_EXTENDED_DN);
1265         if (ret != LDB_SUCCESS || rodc_res->count != 1) goto failed;
1266
1267         ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
1268         if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed;
1269
1270         /* if the object SID is equal to the user_sid, allow */
1271         object_sid = samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid");
1272         if (dom_sid_equal(user_sid, object_sid)) {
1273                 goto allowed;
1274         }
1275
1276         additional_sids[0] = object_sid;
1277
1278         /*
1279          * Must be an RODC account at this point, verify machine DN matches the
1280          * SID account
1281          */
1282         if (ldb_dn_compare(rodc_res->msgs[0]->dn, *machine_dn) != 0) {
1283                 goto denied;
1284         }
1285
1286         /* an RODC is allowed to get its own krbtgt account secrets */
1287         krbtgt_link_dn = samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
1288                                          rodc_res->msgs[0], "msDS-KrbTgtLink", NULL);
1289         if (krbtgt_link_dn != NULL &&
1290             ldb_dn_compare(obj_dn, krbtgt_link_dn) == 0) {
1291                 goto allowed;
1292         }
1293
1294         /* but it isn't allowed to get anyone elses krbtgt secrets */
1295         if (samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
1296                             obj_res->msgs[0], "msDS-KrbTgtLinkBL", NULL)) {
1297                 goto denied;
1298         }
1299
1300         if (ldb_msg_find_attr_as_uint(obj_res->msgs[0],
1301                                       "userAccountControl", 0) &
1302             UF_INTERDOMAIN_TRUST_ACCOUNT) {
1303                 goto denied;
1304         }
1305
1306         werr = samdb_result_sid_array_dn(b_state->sam_ctx_system, rodc_res->msgs[0],
1307                                          mem_ctx, "msDS-NeverRevealGroup", &never_reveal_sids);
1308         if (!W_ERROR_IS_OK(werr)) {
1309                 goto denied;
1310         }
1311
1312         werr = samdb_result_sid_array_dn(b_state->sam_ctx_system, rodc_res->msgs[0],
1313                                          mem_ctx, "msDS-RevealOnDemandGroup", &reveal_sids);
1314         if (!W_ERROR_IS_OK(werr)) {
1315                 goto denied;
1316         }
1317
1318         /*
1319          * The SID list needs to include itself as well as the tokenGroups.
1320          *
1321          * TODO determine if sIDHistory is required for this check
1322          */
1323         werr = samdb_result_sid_array_ndr(b_state->sam_ctx_system, obj_res->msgs[0],
1324                                           mem_ctx, "tokenGroups", &token_sids,
1325                                           additional_sids, 1);
1326         if (!W_ERROR_IS_OK(werr) || token_sids==NULL) {
1327                 goto denied;
1328         }
1329
1330         if (never_reveal_sids &&
1331             sid_list_match(token_sids, never_reveal_sids)) {
1332                 goto denied;
1333         }
1334
1335         if (reveal_sids &&
1336             sid_list_match(token_sids, reveal_sids)) {
1337                 goto allowed;
1338         }
1339
1340         /* default deny */
1341 denied:
1342         DEBUG(2,(__location__ ": Denied single object with secret replication for %s by RODC %s\n",
1343                  ldb_dn_get_linearized(obj_dn), ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
1344         ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1345         return WERR_DS_DRA_SECRETS_DENIED;
1346
1347 allowed:
1348         DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
1349                  ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC",
1350                  ldb_dn_get_linearized(*machine_dn)));
1351         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1352         req10->highwatermark.highest_usn = 0;
1353         return WERR_OK;
1354
1355 failed:
1356         DEBUG(2,(__location__ ": Failed single secret replication for %s by RODC %s\n",
1357                  ldb_dn_get_linearized(obj_dn), dom_sid_string(mem_ctx, user_sid)));
1358         ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1359         return WERR_DS_DRA_BAD_DN;
1360 }
1361
1362 /*
1363   handle a DRSUAPI_EXOP_REPL_OBJ call
1364  */
1365 static WERROR getncchanges_repl_obj(struct drsuapi_bind_state *b_state,
1366                                     TALLOC_CTX *mem_ctx,
1367                                     struct drsuapi_DsGetNCChangesRequest10 *req10,
1368                                     struct dom_sid *user_sid,
1369                                     struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1370 {
1371         struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1372
1373         DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_OBJ extended op on %s\n",
1374                  drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
1375
1376         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1377         return WERR_OK;
1378 }
1379
1380
1381 /*
1382   handle DRSUAPI_EXOP_FSMO_REQ_ROLE,
1383   DRSUAPI_EXOP_FSMO_RID_REQ_ROLE,
1384   and DRSUAPI_EXOP_FSMO_REQ_PDC calls
1385  */
1386 static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state,
1387                                          TALLOC_CTX *mem_ctx,
1388                                          struct drsuapi_DsGetNCChangesRequest10 *req10,
1389                                          struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1390 {
1391         struct ldb_dn *req_dn, *ntds_dn;
1392         int ret;
1393         unsigned int i;
1394         struct ldb_context *ldb = b_state->sam_ctx;
1395         struct ldb_message *msg;
1396         bool is_us;
1397
1398         /*
1399           steps:
1400             - verify that the client dn exists
1401             - verify that we are the current master
1402          */
1403
1404         req_dn = drs_ObjectIdentifier_to_dn(mem_ctx, ldb, req10->naming_context);
1405         if (!ldb_dn_validate(req_dn)) {
1406                 /* that is not a valid dn */
1407                 DEBUG(0,(__location__ ": FSMO role transfer request for invalid DN %s\n",
1408                          drs_ObjectIdentifier_to_string(mem_ctx, req10->naming_context)));
1409                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1410                 return WERR_OK;
1411         }
1412
1413         /* find the DN of the current role owner */
1414         ret = samdb_reference_dn_is_our_ntdsa(ldb, req_dn, "fSMORoleOwner", &is_us);
1415         if (ret != LDB_SUCCESS) {
1416                 DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1417                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1418                 return WERR_DS_DRA_INTERNAL_ERROR;
1419         }
1420
1421         if (!is_us) {
1422                 /* we're not the RID Manager or role owner - go away */
1423                 DEBUG(0,(__location__ ": FSMO role or RID manager transfer owner request when not role owner\n"));
1424                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1425                 return WERR_OK;
1426         }
1427
1428         /* change the current master */
1429         msg = ldb_msg_new(ldb);
1430         W_ERROR_HAVE_NO_MEMORY(msg);
1431         msg->dn = drs_ObjectIdentifier_to_dn(msg, ldb, req10->naming_context);
1432         W_ERROR_HAVE_NO_MEMORY(msg->dn);
1433
1434         /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1435         ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, 0, &ntds_dn);
1436         if (ret != LDB_SUCCESS) {
1437                 DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1438                           GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1439                 talloc_free(msg);
1440                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1441                 return WERR_OK;
1442         }
1443
1444         ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn));
1445         if (ret != 0) {
1446                 talloc_free(msg);
1447                 return WERR_DS_DRA_INTERNAL_ERROR;
1448         }
1449
1450         for (i=0;i<msg->num_elements;i++) {
1451                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1452         }
1453
1454         ret = ldb_transaction_start(ldb);
1455         if (ret != LDB_SUCCESS) {
1456                 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1457                          ldb_errstring(ldb)));
1458                 return WERR_DS_DRA_INTERNAL_ERROR;
1459         }
1460
1461         ret = ldb_modify(ldb, msg);
1462         if (ret != LDB_SUCCESS) {
1463                 DEBUG(0,(__location__ ": Failed to change current owner - %s\n",
1464                          ldb_errstring(ldb)));
1465                 ldb_transaction_cancel(ldb);
1466                 return WERR_DS_DRA_INTERNAL_ERROR;
1467         }
1468
1469         ret = ldb_transaction_commit(ldb);
1470         if (ret != LDB_SUCCESS) {
1471                 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1472                          ldb_errstring(ldb)));
1473                 return WERR_DS_DRA_INTERNAL_ERROR;
1474         }
1475
1476         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1477
1478         return WERR_OK;
1479 }
1480
1481 /*
1482   see if this getncchanges request includes a request to reveal secret information
1483  */
1484 static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state *b_state,
1485                                                        struct drsuapi_DsGetNCChangesRequest10 *req10,
1486                                                        struct dsdb_schema_prefixmap *pfm_remote,
1487                                                        bool *is_secret_request)
1488 {
1489         enum drsuapi_DsExtendedOperation exop;
1490         uint32_t i;
1491         struct dsdb_schema *schema;
1492         struct dsdb_syntax_ctx syntax_ctx;
1493
1494         *is_secret_request = true;
1495
1496         exop = req10->extended_op;
1497
1498         switch (exop) {
1499         case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1500         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1501         case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1502         case DRSUAPI_EXOP_FSMO_REQ_PDC:
1503         case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1504                 /* FSMO exops can reveal secrets */
1505                 *is_secret_request = true;
1506                 return WERR_OK;
1507         case DRSUAPI_EXOP_REPL_SECRET:
1508         case DRSUAPI_EXOP_REPL_OBJ:
1509         case DRSUAPI_EXOP_NONE:
1510                 break;
1511         }
1512
1513         if (req10->replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
1514                 *is_secret_request = false;
1515                 return WERR_OK;
1516         }
1517
1518         if (exop == DRSUAPI_EXOP_REPL_SECRET ||
1519             req10->partial_attribute_set == NULL) {
1520                 /* they want secrets */
1521                 *is_secret_request = true;
1522                 return WERR_OK;
1523         }
1524
1525         schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1526         dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1527         syntax_ctx.pfm_remote = pfm_remote;
1528
1529         /* check the attributes they asked for */
1530         for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1531                 const struct dsdb_attribute *sa;
1532                 WERROR werr = getncchanges_attid_remote_to_local(schema,
1533                                                                  &syntax_ctx,
1534                                                                  req10->partial_attribute_set->attids[i],
1535                                                                  NULL,
1536                                                                  &sa);
1537
1538                 if (!W_ERROR_IS_OK(werr)) {
1539                         DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1540                                  req10->partial_attribute_set->attids[i], win_errstr(werr)));
1541                         return werr;
1542                 }
1543
1544                 if (!dsdb_attr_in_rodc_fas(sa)) {
1545                         *is_secret_request = true;
1546                         return WERR_OK;
1547                 }
1548         }
1549
1550         if (req10->partial_attribute_set_ex) {
1551                 /* check the extended attributes they asked for */
1552                 for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1553                         const struct dsdb_attribute *sa;
1554                         WERROR werr = getncchanges_attid_remote_to_local(schema,
1555                                                                          &syntax_ctx,
1556                                                                          req10->partial_attribute_set_ex->attids[i],
1557                                                                          NULL,
1558                                                                          &sa);
1559
1560                         if (!W_ERROR_IS_OK(werr)) {
1561                                 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1562                                          req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1563                                 return werr;
1564                         }
1565
1566                         if (!dsdb_attr_in_rodc_fas(sa)) {
1567                                 *is_secret_request = true;
1568                                 return WERR_OK;
1569                         }
1570                 }
1571         }
1572
1573         *is_secret_request = false;
1574         return WERR_OK;
1575 }
1576
1577 /*
1578   see if this getncchanges request is only for attributes in the GC
1579   partial attribute set
1580  */
1581 static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state,
1582                                                struct drsuapi_DsGetNCChangesRequest10 *req10,
1583                                                struct dsdb_schema_prefixmap *pfm_remote,
1584                                                bool *is_gc_pas_request)
1585 {
1586         enum drsuapi_DsExtendedOperation exop;
1587         uint32_t i;
1588         struct dsdb_schema *schema;
1589         struct dsdb_syntax_ctx syntax_ctx;
1590
1591         exop = req10->extended_op;
1592
1593         switch (exop) {
1594         case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1595         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1596         case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1597         case DRSUAPI_EXOP_FSMO_REQ_PDC:
1598         case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1599         case DRSUAPI_EXOP_REPL_SECRET:
1600                 *is_gc_pas_request = false;
1601                 return WERR_OK;
1602         case DRSUAPI_EXOP_REPL_OBJ:
1603         case DRSUAPI_EXOP_NONE:
1604                 break;
1605         }
1606
1607         if (req10->partial_attribute_set == NULL) {
1608                 /* they want it all */
1609                 *is_gc_pas_request = false;
1610                 return WERR_OK;
1611         }
1612
1613         schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1614         dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1615         syntax_ctx.pfm_remote = pfm_remote;
1616
1617         /* check the attributes they asked for */
1618         for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1619                 const struct dsdb_attribute *sa;
1620                 WERROR werr = getncchanges_attid_remote_to_local(schema,
1621                                                                  &syntax_ctx,
1622                                                                  req10->partial_attribute_set->attids[i],
1623                                                                  NULL,
1624                                                                  &sa);
1625
1626                 if (!W_ERROR_IS_OK(werr)) {
1627                         DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1628                                  req10->partial_attribute_set->attids[i], win_errstr(werr)));
1629                         return werr;
1630                 }
1631
1632                 if (!sa->isMemberOfPartialAttributeSet) {
1633                         *is_gc_pas_request = false;
1634                         return WERR_OK;
1635                 }
1636         }
1637
1638         if (req10->partial_attribute_set_ex) {
1639                 /* check the extended attributes they asked for */
1640                 for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1641                         const struct dsdb_attribute *sa;
1642                         WERROR werr = getncchanges_attid_remote_to_local(schema,
1643                                                                          &syntax_ctx,
1644                                                                          req10->partial_attribute_set_ex->attids[i],
1645                                                                          NULL,
1646                                                                          &sa);
1647
1648                         if (!W_ERROR_IS_OK(werr)) {
1649                                 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1650                                          req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1651                                 return werr;
1652                         }
1653
1654                         if (!sa->isMemberOfPartialAttributeSet) {
1655                                 *is_gc_pas_request = false;
1656                                 return WERR_OK;
1657                         }
1658                 }
1659         }
1660
1661         *is_gc_pas_request = true;
1662         return WERR_OK;
1663 }
1664
1665
1666 /*
1667   map from req8 to req10
1668  */
1669 static struct drsuapi_DsGetNCChangesRequest10 *
1670 getncchanges_map_req8(TALLOC_CTX *mem_ctx,
1671                       struct drsuapi_DsGetNCChangesRequest8 *req8)
1672 {
1673         struct drsuapi_DsGetNCChangesRequest10 *req10 = talloc_zero(mem_ctx,
1674                                                                     struct drsuapi_DsGetNCChangesRequest10);
1675         if (req10 == NULL) {
1676                 return NULL;
1677         }
1678
1679         req10->destination_dsa_guid = req8->destination_dsa_guid;
1680         req10->source_dsa_invocation_id = req8->source_dsa_invocation_id;
1681         req10->naming_context = req8->naming_context;
1682         req10->highwatermark = req8->highwatermark;
1683         req10->uptodateness_vector = req8->uptodateness_vector;
1684         req10->replica_flags = req8->replica_flags;
1685         req10->max_object_count = req8->max_object_count;
1686         req10->max_ndr_size = req8->max_ndr_size;
1687         req10->extended_op = req8->extended_op;
1688         req10->fsmo_info = req8->fsmo_info;
1689         req10->partial_attribute_set = req8->partial_attribute_set;
1690         req10->partial_attribute_set_ex = req8->partial_attribute_set_ex;
1691         req10->mapping_ctr = req8->mapping_ctr;
1692
1693         return req10;
1694 }
1695
1696 static const char *collect_objects_attrs[] = { "uSNChanged",
1697                                                "objectGUID" ,
1698                                                NULL };
1699
1700 /**
1701  * Collects object for normal replication cycle.
1702  */
1703 static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
1704                                            TALLOC_CTX *mem_ctx,
1705                                            struct drsuapi_DsGetNCChangesRequest10 *req10,
1706                                            struct ldb_dn *search_dn,
1707                                            const char *extra_filter,
1708                                            struct ldb_result **search_res)
1709 {
1710         int ret;
1711         char* search_filter;
1712         enum ldb_scope scope = LDB_SCOPE_SUBTREE;
1713         struct drsuapi_getncchanges_state *getnc_state = b_state->getncchanges_state;
1714         bool critical_only = false;
1715
1716         if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
1717                 critical_only = true;
1718         }
1719
1720         if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ ||
1721             req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
1722                 scope = LDB_SCOPE_BASE;
1723                 critical_only = false;
1724         }
1725
1726         /* Construct response. */
1727         search_filter = talloc_asprintf(mem_ctx,
1728                                         "(uSNChanged>=%llu)",
1729                                         (unsigned long long)(getnc_state->min_usn+1));
1730
1731         if (extra_filter) {
1732                 search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
1733         }
1734
1735         if (critical_only) {
1736                 search_filter = talloc_asprintf(mem_ctx,
1737                                                 "(&%s(isCriticalSystemObject=TRUE))",
1738                                                 search_filter);
1739         }
1740
1741         if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
1742                 scope = LDB_SCOPE_BASE;
1743         }
1744
1745         if (!search_dn) {
1746                 search_dn = getnc_state->ncRoot_dn;
1747         }
1748
1749         DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n",
1750                  ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
1751         ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, search_res,
1752                                               search_dn, scope,
1753                                               collect_objects_attrs,
1754                                               search_filter);
1755         if (ret != LDB_SUCCESS) {
1756                 return WERR_DS_DRA_INTERNAL_ERROR;
1757         }
1758
1759         return WERR_OK;
1760 }
1761
1762 /**
1763  * Collects object for normal replication cycle.
1764  */
1765 static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state,
1766                                                 TALLOC_CTX *mem_ctx,
1767                                                 struct drsuapi_DsGetNCChangesRequest10 *req10,
1768                                                 struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1769                                                 struct ldb_dn *search_dn,
1770                                                 const char *extra_filter,
1771                                                 struct ldb_result **search_res)
1772 {
1773         /* we have nothing to do in case of ex-op failure */
1774         if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
1775                 return WERR_OK;
1776         }
1777
1778         switch (req10->extended_op) {
1779         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1780         {
1781                 int ret;
1782                 struct ldb_dn *ntds_dn = NULL;
1783                 struct ldb_dn *server_dn = NULL;
1784                 struct ldb_dn *machine_dn = NULL;
1785                 struct ldb_dn *rid_set_dn = NULL;
1786                 struct ldb_result *search_res2 = NULL;
1787                 struct ldb_result *search_res3 = NULL;
1788                 TALLOC_CTX *frame = talloc_stackframe();
1789                 /* get RID manager, RID set and server DN (in that order) */
1790
1791                 /* This first search will get the RID Manager */
1792                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1793                                                       search_res,
1794                                                       search_dn, LDB_SCOPE_BASE,
1795                                                       collect_objects_attrs,
1796                                                       NULL);
1797                 if (ret != LDB_SUCCESS) {
1798                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %s",
1799                                   ldb_dn_get_linearized(search_dn),
1800                                   ldb_errstring(b_state->sam_ctx)));
1801                         TALLOC_FREE(frame);
1802                         return WERR_DS_DRA_INTERNAL_ERROR;
1803                 }
1804
1805                 if ((*search_res)->count != 1) {
1806                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %u objects returned",
1807                                   ldb_dn_get_linearized(search_dn),
1808                                   (*search_res)->count));
1809                         TALLOC_FREE(frame);
1810                         return WERR_DS_DRA_INTERNAL_ERROR;
1811                 }
1812
1813                 /* Now extend it to the RID set */
1814
1815                 /* Find the computer account DN for the destination
1816                  * dsa GUID specified */
1817
1818                 ret = dsdb_find_dn_by_guid(b_state->sam_ctx, frame,
1819                                            &req10->destination_dsa_guid, 0,
1820                                            &ntds_dn);
1821                 if (ret != LDB_SUCCESS) {
1822                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Unable to find NTDS object for guid %s - %s\n",
1823                                   GUID_string(frame,
1824                                               &req10->destination_dsa_guid),
1825                                   ldb_errstring(b_state->sam_ctx)));
1826                         TALLOC_FREE(frame);
1827                         return WERR_DS_DRA_INTERNAL_ERROR;
1828                 }
1829
1830                 server_dn = ldb_dn_get_parent(frame, ntds_dn);
1831                 if (!server_dn) {
1832                         TALLOC_FREE(frame);
1833                         return WERR_DS_DRA_INTERNAL_ERROR;
1834                 }
1835
1836                 ret = samdb_reference_dn(b_state->sam_ctx, frame, server_dn,
1837                                          "serverReference", &machine_dn);
1838                 if (ret != LDB_SUCCESS) {
1839                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find serverReference in %s - %s",
1840                                   ldb_dn_get_linearized(server_dn),
1841                                   ldb_errstring(b_state->sam_ctx)));
1842                         TALLOC_FREE(frame);
1843                         return WERR_DS_DRA_INTERNAL_ERROR;
1844                 }
1845
1846                 ret = samdb_reference_dn(b_state->sam_ctx, frame, machine_dn,
1847                                          "rIDSetReferences", &rid_set_dn);
1848                 if (ret != LDB_SUCCESS) {
1849                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find rIDSetReferences in %s - %s",
1850                                   ldb_dn_get_linearized(server_dn),
1851                                   ldb_errstring(b_state->sam_ctx)));
1852                         TALLOC_FREE(frame);
1853                         return WERR_DS_DRA_INTERNAL_ERROR;
1854                 }
1855
1856
1857                 /* This first search will get the RID Manager, now get the RID set */
1858                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1859                                                       &search_res2,
1860                                                       rid_set_dn, LDB_SCOPE_BASE,
1861                                                       collect_objects_attrs,
1862                                                       NULL);
1863                 if (ret != LDB_SUCCESS) {
1864                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %s",
1865                                   ldb_dn_get_linearized(rid_set_dn),
1866                                   ldb_errstring(b_state->sam_ctx)));
1867                         TALLOC_FREE(frame);
1868                         return WERR_DS_DRA_INTERNAL_ERROR;
1869                 }
1870
1871                 if (search_res2->count != 1) {
1872                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %u objects returned",
1873                                   ldb_dn_get_linearized(rid_set_dn),
1874                                   search_res2->count));
1875                         TALLOC_FREE(frame);
1876                         return WERR_DS_DRA_INTERNAL_ERROR;
1877                 }
1878
1879                 /* Finally get the server DN */
1880                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1881                                                       &search_res3,
1882                                                       machine_dn, LDB_SCOPE_BASE,
1883                                                       collect_objects_attrs,
1884                                                       NULL);
1885                 if (ret != LDB_SUCCESS) {
1886                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %s",
1887                                   ldb_dn_get_linearized(server_dn),
1888                                   ldb_errstring(b_state->sam_ctx)));
1889                         TALLOC_FREE(frame);
1890                         return WERR_DS_DRA_INTERNAL_ERROR;
1891                 }
1892
1893                 if (search_res3->count != 1) {
1894                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %u objects returned",
1895                                   ldb_dn_get_linearized(server_dn),
1896                                   search_res3->count));
1897                         TALLOC_FREE(frame);
1898                         return WERR_DS_DRA_INTERNAL_ERROR;
1899                 }
1900
1901                 /* Now extend the original search_res with these answers */
1902                 (*search_res)->count = 3;
1903
1904                 (*search_res)->msgs = talloc_realloc(frame, (*search_res)->msgs,
1905                                                      struct ldb_message *,
1906                                                      (*search_res)->count);
1907                 if ((*search_res)->msgs == NULL) {
1908                         TALLOC_FREE(frame);
1909                         return WERR_NOT_ENOUGH_MEMORY;
1910                 }
1911
1912
1913                 talloc_steal(mem_ctx, *search_res);
1914                 (*search_res)->msgs[1] =
1915                         talloc_steal((*search_res)->msgs, search_res2->msgs[0]);
1916                 (*search_res)->msgs[2] =
1917                         talloc_steal((*search_res)->msgs, search_res3->msgs[0]);
1918
1919                 TALLOC_FREE(frame);
1920                 return WERR_OK;
1921         }
1922         default:
1923                 /* TODO: implement extended op specific collection
1924                  * of objects. Right now we just normal procedure
1925                  * for collecting objects */
1926                 return getncchanges_collect_objects(b_state, mem_ctx, req10, search_dn, extra_filter, search_res);
1927         }
1928 }
1929
1930 static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
1931                                                 uint64_t max_usn,
1932                                                 struct drsuapi_DsReplicaHighWaterMark *hwm)
1933 {
1934         uint64_t uSN = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
1935
1936         if (uSN > max_usn) {
1937                 /*
1938                  * Only report the max_usn we had at the start
1939                  * of the replication cycle.
1940                  *
1941                  * If this object has changed lately we better
1942                  * let the destination dsa refetch the change.
1943                  * This is better than the risk of loosing some
1944                  * objects or linked attributes.
1945                  */
1946                 return;
1947         }
1948
1949         if (uSN <= hwm->tmp_highest_usn) {
1950                 return;
1951         }
1952
1953         hwm->tmp_highest_usn = uSN;
1954         hwm->reserved_usn = 0;
1955 }
1956
1957 /**
1958  * Adds an object's GUID to the cache of objects already sent.
1959  * This avoids us sending the same object multiple times when
1960  * the GetNCChanges request uses a flag like GET_ANC.
1961  */
1962 static WERROR dcesrv_drsuapi_obj_cache_add(struct db_context *obj_cache,
1963                                            const struct GUID *guid)
1964 {
1965         enum ndr_err_code ndr_err;
1966         uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
1967         DATA_BLOB b = {
1968                 .data = guid_buf,
1969                 .length = sizeof(guid_buf),
1970         };
1971         TDB_DATA key = {
1972                 .dptr = b.data,
1973                 .dsize = b.length,
1974         };
1975         TDB_DATA val = {
1976                 .dptr = NULL,
1977                 .dsize = 0,
1978         };
1979         NTSTATUS status;
1980
1981         ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
1982                         (ndr_push_flags_fn_t)ndr_push_GUID);
1983         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1984                 return WERR_DS_DRA_INTERNAL_ERROR;
1985         }
1986
1987         status = dbwrap_store(obj_cache, key, val, TDB_REPLACE);
1988         if (!NT_STATUS_IS_OK(status)) {
1989                 return WERR_DS_DRA_INTERNAL_ERROR;
1990         }
1991
1992         return WERR_OK;
1993 }
1994
1995 /**
1996  * Checks if the object with the GUID specified already exists in the
1997  * object cache, i.e. it's already been sent in a GetNCChanges response.
1998  */
1999 static WERROR dcesrv_drsuapi_obj_cache_exists(struct db_context *obj_cache,
2000                                               const struct GUID *guid)
2001 {
2002         enum ndr_err_code ndr_err;
2003         uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
2004         DATA_BLOB b = {
2005                 .data = guid_buf,
2006                 .length = sizeof(guid_buf),
2007         };
2008         TDB_DATA key = {
2009                 .dptr = b.data,
2010                 .dsize = b.length,
2011         };
2012         bool exists;
2013
2014         ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
2015                         (ndr_push_flags_fn_t)ndr_push_GUID);
2016         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2017                 return WERR_DS_DRA_INTERNAL_ERROR;
2018         }
2019
2020         exists = dbwrap_exists(obj_cache, key);
2021         if (!exists) {
2022                 return WERR_OBJECT_NOT_FOUND;
2023         }
2024
2025         return WERR_OBJECT_NAME_EXISTS;
2026 }
2027
2028 /**
2029  * Copies the la_list specified into a sorted array, ready to be sent in a
2030  * GetNCChanges response.
2031  */
2032 static WERROR getncchanges_get_sorted_array(const struct drsuapi_DsReplicaLinkedAttribute *la_list,
2033                                             const uint32_t link_count,
2034                                             struct ldb_context *sam_ctx,
2035                                             TALLOC_CTX *mem_ctx,
2036                                             const struct dsdb_schema *schema,
2037                                             struct la_for_sorting **ret_array)
2038 {
2039         int j;
2040         struct la_for_sorting *guid_array;
2041         WERROR werr = WERR_OK;
2042
2043         *ret_array = NULL;
2044         guid_array = talloc_array(mem_ctx, struct la_for_sorting, link_count);
2045         if (guid_array == NULL) {
2046                 DEBUG(0, ("Out of memory allocating %u linked attributes for sorting", link_count));
2047                 return WERR_NOT_ENOUGH_MEMORY;
2048         }
2049
2050         for (j = 0; j < link_count; j++) {
2051
2052                 /* we need to get the target GUIDs to compare */
2053                 struct dsdb_dn *dn;
2054                 const struct drsuapi_DsReplicaLinkedAttribute *la = &la_list[j];
2055                 const struct dsdb_attribute *schema_attrib;
2056                 const struct ldb_val *target_guid;
2057                 DATA_BLOB source_guid;
2058                 TALLOC_CTX *frame = talloc_stackframe();
2059                 NTSTATUS status;
2060
2061                 schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid);
2062
2063                 werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn);
2064                 if (!W_ERROR_IS_OK(werr)) {
2065                         DEBUG(0,(__location__ ": Bad la blob in sort\n"));
2066                         TALLOC_FREE(frame);
2067                         return werr;
2068                 }
2069
2070                 /* Extract the target GUID in NDR form */
2071                 target_guid = ldb_dn_get_extended_component(dn->dn, "GUID");
2072                 if (target_guid == NULL
2073                                 || target_guid->length != sizeof(guid_array[0].target_guid)) {
2074                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2075                 } else {
2076                         /* Repack the source GUID as NDR for sorting */
2077                         status = GUID_to_ndr_blob(&la->identifier->guid,
2078                                                   frame,
2079                                                   &source_guid);
2080                 }
2081
2082                 if (!NT_STATUS_IS_OK(status)
2083                                 || source_guid.length != sizeof(guid_array[0].source_guid)) {
2084                         DEBUG(0,(__location__ ": Bad la guid in sort\n"));
2085                         TALLOC_FREE(frame);
2086                         return ntstatus_to_werror(status);
2087                 }
2088
2089                 guid_array[j].link = &la_list[j];
2090                 memcpy(guid_array[j].target_guid, target_guid->data,
2091                        sizeof(guid_array[j].target_guid));
2092                 memcpy(guid_array[j].source_guid, source_guid.data,
2093                        sizeof(guid_array[j].source_guid));
2094                 TALLOC_FREE(frame);
2095         }
2096
2097         LDB_TYPESAFE_QSORT(guid_array, link_count, NULL, linked_attribute_compare);
2098
2099         *ret_array = guid_array;
2100
2101         return werr;
2102 }
2103
2104
2105 /**
2106  * Adds any ancestor/parent objects of the child_obj specified.
2107  * This is needed when the GET_ANC flag is specified in the request.
2108  * @param new_objs if parents are added, this gets updated to point to a chain
2109  * of parent objects (with the parents first and the child last)
2110  */
2111 static WERROR getncchanges_add_ancestors(struct drsuapi_DsReplicaObjectListItemEx *child_obj,
2112                                          struct ldb_dn *child_dn,
2113                                          TALLOC_CTX *mem_ctx,
2114                                          struct ldb_context *sam_ctx,
2115                                          struct drsuapi_getncchanges_state *getnc_state,
2116                                          struct dsdb_schema *schema,
2117                                          DATA_BLOB *session_key,
2118                                          struct drsuapi_DsGetNCChangesRequest10 *req10,
2119                                          uint32_t *local_pas,
2120                                          struct ldb_dn *machine_dn,
2121                                          struct drsuapi_DsReplicaObjectListItemEx **new_objs)
2122 {
2123         int ret;
2124         const struct GUID *next_anc_guid = NULL;
2125         WERROR werr = WERR_OK;
2126         static const char * const msg_attrs[] = {
2127                                             "*",
2128                                             "nTSecurityDescriptor",
2129                                             "parentGUID",
2130                                             "replPropertyMetaData",
2131                                             DSDB_SECRET_ATTRIBUTES,
2132                                             NULL };
2133
2134         next_anc_guid = child_obj->parent_object_guid;
2135
2136         while (next_anc_guid != NULL) {
2137                 struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
2138                 struct ldb_message *anc_msg = NULL;
2139                 struct ldb_result *anc_res = NULL;
2140                 struct ldb_dn *anc_dn = NULL;
2141
2142                 /*
2143                  * Don't send an object twice. (If we've sent the object, then
2144                  * we've also sent all its parents as well)
2145                  */
2146                 werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
2147                                                        next_anc_guid);
2148                 if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2149                         return WERR_OK;
2150                 }
2151                 if (W_ERROR_IS_OK(werr)) {
2152                         return WERR_INTERNAL_ERROR;
2153                 }
2154                 if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
2155                         return werr;
2156                 }
2157
2158                 anc_obj = talloc_zero(mem_ctx,
2159                                       struct drsuapi_DsReplicaObjectListItemEx);
2160                 if (anc_obj == NULL) {
2161                         return WERR_NOT_ENOUGH_MEMORY;
2162                 }
2163
2164                 anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
2165                                         GUID_string(anc_obj, next_anc_guid));
2166                 if (anc_dn == NULL) {
2167                         return WERR_NOT_ENOUGH_MEMORY;
2168                 }
2169
2170                 ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
2171                                                       &anc_res, anc_dn,
2172                                                       LDB_SCOPE_BASE,
2173                                                       msg_attrs, NULL);
2174                 if (ret != LDB_SUCCESS) {
2175                         const char *anc_str = NULL;
2176                         const char *obj_str = NULL;
2177
2178                         anc_str = ldb_dn_get_extended_linearized(anc_obj,
2179                                                                  anc_dn,
2180                                                                  1);
2181                         obj_str = ldb_dn_get_extended_linearized(anc_obj,
2182                                                                  child_dn,
2183                                                                  1);
2184
2185                         DBG_ERR("getncchanges: failed to fetch ANC "
2186                                 "DN %s for DN %s - %s\n",
2187                                 anc_str, obj_str, ldb_errstring(sam_ctx));
2188                         return WERR_DS_DRA_INCONSISTENT_DIT;
2189                 }
2190
2191                 anc_msg = anc_res->msgs[0];
2192
2193                 werr = get_nc_changes_build_object(anc_obj, anc_msg,
2194                                                    sam_ctx,
2195                                                    getnc_state,
2196                                                    schema, session_key,
2197                                                    req10,
2198                                                    false, /* force_object_return */
2199                                                    local_pas,
2200                                                    machine_dn,
2201                                                    next_anc_guid);
2202                 if (!W_ERROR_IS_OK(werr)) {
2203                         return werr;
2204                 }
2205
2206                 /*
2207                  * Regardless of whether we actually use it or not,
2208                  * we add it to the cache so we don't look at it again
2209                  */
2210                 werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
2211                                                     next_anc_guid);
2212                 if (!W_ERROR_IS_OK(werr)) {
2213                         return werr;
2214                 }
2215
2216                 /*
2217                  * Any ancestors which are below the highwatermark
2218                  * or uptodateness_vector shouldn't be added,
2219                  * but we still look further up the
2220                  * tree for ones which have been changed recently.
2221                  */
2222                 if (anc_obj->meta_data_ctr != NULL) {
2223
2224                         /*
2225                          * prepend the parent to the list so that the client-side
2226                          * adds the parent object before it adds the children
2227                          */
2228                         anc_obj->next_object = *new_objs;
2229                         *new_objs = anc_obj;
2230                 }
2231
2232                 anc_msg = NULL;
2233                 TALLOC_FREE(anc_res);
2234                 TALLOC_FREE(anc_dn);
2235
2236                 /*
2237                  * We may need to resolve more parents...
2238                  */
2239                 next_anc_guid = anc_obj->parent_object_guid;
2240         }
2241         return werr;
2242 }
2243
2244 /**
2245  * Adds a list of new objects into the getNCChanges response message
2246  */
2247 static void getncchanges_add_objs_to_resp(struct getncchanges_repl_chunk *repl_chunk,
2248                                           struct drsuapi_DsReplicaObjectListItemEx *obj_list)
2249 {
2250         struct drsuapi_DsReplicaObjectListItemEx *obj;
2251
2252         /*
2253          * We track the last object added to the response message, so just add
2254          * the new object-list onto the end
2255          */
2256         if (repl_chunk->last_object == NULL) {
2257                 repl_chunk->ctr6->first_object = obj_list;
2258         } else {
2259                 repl_chunk->last_object->next_object = obj_list;
2260         }
2261
2262         for (obj = obj_list; obj != NULL; obj = obj->next_object) {
2263                 repl_chunk->ctr6->object_count += 1;
2264
2265                 /*
2266                  * Remember the last object in the response - we'll use this to
2267                  * link the next object(s) processed onto the existing list
2268                  */
2269                 if (obj->next_object == NULL) {
2270                         repl_chunk->last_object = obj;
2271                 }
2272         }
2273 }
2274
2275 /**
2276  * Gets the object to send, packed into an RPC struct ready to send. This also
2277  * adds the object to the object cache, and adds any ancestors (if needed).
2278  * @param msg - DB search result for the object to add
2279  * @param guid - GUID of the object to add
2280  * @param ret_obj_list - returns the object ready to be sent (in a list, along
2281  * with any ancestors that might be needed). NULL if nothing to send.
2282  */
2283 static WERROR getncchanges_get_obj_to_send(const struct ldb_message *msg,
2284                                            TALLOC_CTX *mem_ctx,
2285                                            struct ldb_context *sam_ctx,
2286                                            struct drsuapi_getncchanges_state *getnc_state,
2287                                            struct dsdb_schema *schema,
2288                                            DATA_BLOB *session_key,
2289                                            struct drsuapi_DsGetNCChangesRequest10 *req10,
2290                                            bool force_object_return,
2291                                            uint32_t *local_pas,
2292                                            struct ldb_dn *machine_dn,
2293                                            const struct GUID *guid,
2294                                            struct drsuapi_DsReplicaObjectListItemEx **ret_obj_list)
2295 {
2296         struct drsuapi_DsReplicaObjectListItemEx *obj;
2297         WERROR werr;
2298
2299         *ret_obj_list = NULL;
2300
2301         obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
2302         W_ERROR_HAVE_NO_MEMORY(obj);
2303
2304         werr = get_nc_changes_build_object(obj, msg, sam_ctx, getnc_state,
2305                                            schema, session_key, req10,
2306                                            force_object_return,
2307                                            local_pas, machine_dn, guid);
2308         if (!W_ERROR_IS_OK(werr)) {
2309                 return werr;
2310         }
2311
2312         /*
2313          * The object may get filtered out by the UTDV's USN and not actually
2314          * sent, in which case there's nothing more to do here
2315          */
2316         if (obj->meta_data_ctr == NULL) {
2317                 TALLOC_FREE(obj);
2318                 return WERR_OK;
2319         }
2320
2321         if (getnc_state->obj_cache != NULL) {
2322                 werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
2323                                                     guid);
2324                 if (!W_ERROR_IS_OK(werr)) {
2325                         return werr;
2326                 }
2327         }
2328
2329         *ret_obj_list = obj;
2330
2331         /*
2332          * If required, also add any ancestors that the client may need to know
2333          * about before it can resolve this object. These get prepended to the
2334          * ret_obj_list so the client adds them first.
2335          */
2336         if (getnc_state->is_get_anc) {
2337                 werr = getncchanges_add_ancestors(obj, msg->dn, mem_ctx,
2338                                                   sam_ctx, getnc_state,
2339                                                   schema, session_key,
2340                                                   req10, local_pas,
2341                                                   machine_dn, ret_obj_list);
2342         }
2343
2344         return werr;
2345 }
2346
2347 /*
2348   drsuapi_DsGetNCChanges
2349
2350   see MS-DRSR 4.1.10.5.2 for basic logic of this function
2351 */
2352 WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2353                                      struct drsuapi_DsGetNCChanges *r)
2354 {
2355         struct drsuapi_DsReplicaObjectIdentifier *ncRoot;
2356         int ret;
2357         uint32_t i, k;
2358         struct dsdb_schema *schema;
2359         struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
2360         struct getncchanges_repl_chunk repl_chunk = { 0 };
2361         NTSTATUS status;
2362         DATA_BLOB session_key;
2363         WERROR werr;
2364         struct dcesrv_handle *h;
2365         struct drsuapi_bind_state *b_state;
2366         struct drsuapi_getncchanges_state *getnc_state;
2367         struct drsuapi_DsGetNCChangesRequest10 *req10;
2368         uint32_t options;
2369         uint32_t max_objects;
2370         uint32_t max_links;
2371         uint32_t link_count = 0;
2372         uint32_t link_total = 0;
2373         uint32_t link_given = 0;
2374         struct ldb_dn *search_dn = NULL;
2375         bool am_rodc;
2376         enum security_user_level security_level;
2377         struct ldb_context *sam_ctx;
2378         struct dom_sid *user_sid;
2379         bool is_secret_request;
2380         bool is_gc_pas_request;
2381         struct drsuapi_changed_objects *changes;
2382         time_t max_wait;
2383         time_t start = time(NULL);
2384         bool max_wait_reached = false;
2385         bool has_get_all_changes = false;
2386         struct GUID invocation_id;
2387         static const struct drsuapi_DsReplicaLinkedAttribute no_linked_attr;
2388         struct dsdb_schema_prefixmap *pfm_remote = NULL;
2389         bool full = true;
2390         uint32_t *local_pas = NULL;
2391         struct ldb_dn *machine_dn = NULL; /* Only used for REPL SECRET EXOP */
2392
2393         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
2394         b_state = h->data;
2395
2396         /* sam_ctx_system is not present for non-administrator users */
2397         sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
2398
2399         invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2400
2401         *r->out.level_out = 6;
2402
2403         r->out.ctr->ctr6.linked_attributes_count = 0;
2404         r->out.ctr->ctr6.linked_attributes = discard_const_p(struct drsuapi_DsReplicaLinkedAttribute, &no_linked_attr);
2405
2406         r->out.ctr->ctr6.object_count = 0;
2407         r->out.ctr->ctr6.nc_object_count = 0;
2408         r->out.ctr->ctr6.more_data = false;
2409         r->out.ctr->ctr6.uptodateness_vector = NULL;
2410         r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
2411         r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2412         r->out.ctr->ctr6.first_object = NULL;
2413
2414         /* a RODC doesn't allow for any replication */
2415         ret = samdb_rodc(sam_ctx, &am_rodc);
2416         if (ret == LDB_SUCCESS && am_rodc) {
2417                 DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
2418                 return WERR_DS_DRA_SOURCE_DISABLED;
2419         }
2420
2421         /* Check request revision. 
2422          */
2423         switch (r->in.level) {
2424         case 8:
2425                 req10 = getncchanges_map_req8(mem_ctx, &r->in.req->req8);
2426                 if (req10 == NULL) {
2427                         return WERR_NOT_ENOUGH_MEMORY;
2428                 }
2429                 break;
2430         case 10:
2431                 req10 = &r->in.req->req10;
2432                 break;
2433         default:
2434                 DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
2435                          r->in.level));
2436                 return WERR_REVISION_MISMATCH;
2437         }
2438
2439
2440         /* Perform access checks. */
2441         /* TODO: we need to support a sync on a specific non-root
2442          * DN. We'll need to find the real partition root here */
2443         ncRoot = req10->naming_context;
2444         if (ncRoot == NULL) {
2445                 DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
2446                 return WERR_DS_DRA_INVALID_PARAMETER;
2447         }
2448
2449         if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) {
2450                 return WERR_DS_DRA_INTERNAL_ERROR;
2451         }
2452
2453         if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
2454             !(req10->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
2455                 return WERR_DS_DRA_SOURCE_DISABLED;
2456         }
2457
2458         user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
2459
2460         /* all clients must have GUID_DRS_GET_CHANGES */
2461         werr = drs_security_access_check_nc_root(sam_ctx,
2462                                                  mem_ctx,
2463                                                  dce_call->conn->auth_state.session_info->security_token,
2464                                                  req10->naming_context,
2465                                                  GUID_DRS_GET_CHANGES);
2466         if (!W_ERROR_IS_OK(werr)) {
2467                 return werr;
2468         }
2469
2470         if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) {
2471                 full = req10->partial_attribute_set == NULL &&
2472                        req10->partial_attribute_set_ex == NULL;
2473         } else {
2474                 full = (options & DRSUAPI_DRS_WRIT_REP) != 0;
2475         }
2476
2477         werr = dsdb_schema_pfm_from_drsuapi_pfm(&req10->mapping_ctr, true,
2478                                                 mem_ctx, &pfm_remote, NULL);
2479
2480         /* We were supplied a partial attribute set, without the prefix map! */
2481         if (!full && !W_ERROR_IS_OK(werr)) {
2482                 if (req10->mapping_ctr.num_mappings == 0) {
2483                         /*
2484                          * Despite the fact MS-DRSR specifies that this shouldn't
2485                          * happen, Windows RODCs will in fact not provide a prefixMap.
2486                          */
2487                         DEBUG(5,(__location__ ": Failed to provide a remote prefixMap,"
2488                                  " falling back to local prefixMap\n"));
2489                 } else {
2490                         DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
2491                                  win_errstr(werr)));
2492                         return werr;
2493                 }
2494         }
2495
2496         /* allowed if the GC PAS and client has
2497            GUID_DRS_GET_FILTERED_ATTRIBUTES */
2498         werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, pfm_remote, &is_gc_pas_request);
2499         if (!W_ERROR_IS_OK(werr)) {
2500                 return werr;
2501         }
2502         if (is_gc_pas_request) {
2503                 werr = drs_security_access_check_nc_root(sam_ctx,
2504                                                          mem_ctx,
2505                                                          dce_call->conn->auth_state.session_info->security_token,
2506                                                          req10->naming_context,
2507                                                          GUID_DRS_GET_FILTERED_ATTRIBUTES);
2508                 if (W_ERROR_IS_OK(werr)) {
2509                         goto allowed;
2510                 }
2511         }
2512
2513         werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10,
2514                                                         pfm_remote,
2515                                                         &is_secret_request);
2516         if (!W_ERROR_IS_OK(werr)) {
2517                 return werr;
2518         }
2519         if (is_secret_request) {
2520                 werr = drs_security_access_check_nc_root(sam_ctx,
2521                                                          mem_ctx,
2522                                                          dce_call->conn->auth_state.session_info->security_token,
2523                                                          req10->naming_context,
2524                                                          GUID_DRS_GET_ALL_CHANGES);
2525                 if (!W_ERROR_IS_OK(werr)) {
2526                         /* Only bail if this is not a EXOP_REPL_SECRET */
2527                         if (req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
2528                                 return werr;
2529                         }
2530                 } else {
2531                         has_get_all_changes = true;
2532                 }
2533         }
2534
2535 allowed:
2536         /* for non-administrator replications, check that they have
2537            given the correct source_dsa_invocation_id */
2538         security_level = security_session_user_level(dce_call->conn->auth_state.session_info,
2539                                                      samdb_domain_sid(sam_ctx));
2540         if (security_level == SECURITY_RO_DOMAIN_CONTROLLER) {
2541                 if (req10->replica_flags & DRSUAPI_DRS_WRIT_REP) {
2542                         /* we rely on this flag being unset for RODC requests */
2543                         req10->replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
2544                 }
2545         }
2546
2547         if (req10->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
2548                 /* Ignore the _in_ uptpdateness vector*/
2549                 req10->uptodateness_vector = NULL;
2550         }
2551
2552         if (GUID_all_zero(&req10->source_dsa_invocation_id)) {
2553                 req10->source_dsa_invocation_id = invocation_id;
2554         }
2555
2556         if (!GUID_equal(&req10->source_dsa_invocation_id, &invocation_id)) {
2557                 /*
2558                  * The given highwatermark is only valid relative to the
2559                  * specified source_dsa_invocation_id.
2560                  */
2561                 ZERO_STRUCT(req10->highwatermark);
2562         }
2563
2564         getnc_state = b_state->getncchanges_state;
2565
2566         /* see if a previous replication has been abandoned */
2567         if (getnc_state) {
2568                 struct ldb_dn *new_dn = drs_ObjectIdentifier_to_dn(getnc_state, sam_ctx, ncRoot);
2569                 if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
2570                         DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
2571                                  ldb_dn_get_linearized(new_dn),
2572                                  ldb_dn_get_linearized(getnc_state->ncRoot_dn),
2573                                  ldb_dn_get_linearized(getnc_state->last_dn)));
2574                         TALLOC_FREE(getnc_state);
2575                         b_state->getncchanges_state = NULL;
2576                 }
2577         }
2578
2579         if (getnc_state) {
2580                 ret = drsuapi_DsReplicaHighWaterMark_cmp(&getnc_state->last_hwm,
2581                                                          &req10->highwatermark);
2582                 if (ret != 0) {
2583                         DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication "
2584                                  "on DN %s %s highwatermark (last_dn %s)\n",
2585                                  ldb_dn_get_linearized(getnc_state->ncRoot_dn),
2586                                  (ret > 0) ? "older" : "newer",
2587                                  ldb_dn_get_linearized(getnc_state->last_dn)));
2588                         TALLOC_FREE(getnc_state);
2589                         b_state->getncchanges_state = NULL;
2590                 }
2591         }
2592
2593         if (getnc_state == NULL) {
2594                 struct ldb_result *res = NULL;
2595                 const char *attrs[] = {
2596                         "instanceType",
2597                         "objectGuID",
2598                         NULL
2599                 };
2600                 uint32_t nc_instanceType;
2601                 struct ldb_dn *ncRoot_dn;
2602
2603                 ncRoot_dn = drs_ObjectIdentifier_to_dn(mem_ctx, sam_ctx, ncRoot);
2604                 if (ncRoot_dn == NULL) {
2605                         return WERR_NOT_ENOUGH_MEMORY;
2606                 }
2607
2608                 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res,
2609                                      ncRoot_dn, attrs,
2610                                      DSDB_SEARCH_SHOW_DELETED |
2611                                      DSDB_SEARCH_SHOW_RECYCLED);
2612                 if (ret != LDB_SUCCESS) {
2613                         DBG_WARNING("Failed to find ncRoot_dn %s\n",
2614                                     ldb_dn_get_linearized(ncRoot_dn));
2615                         return WERR_DS_DRA_BAD_DN;
2616                 }
2617                 nc_instanceType = ldb_msg_find_attr_as_int(res->msgs[0],
2618                                                            "instanceType",
2619                                                            0);
2620
2621                 if (req10->extended_op != DRSUAPI_EXOP_NONE) {
2622                         r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
2623                 }
2624
2625                 /*
2626                  * This is the first replication cycle and it is
2627                  * a good place to handle extended operations
2628                  *
2629                  * FIXME: we don't fully support extended operations yet
2630                  */
2631                 switch (req10->extended_op) {
2632                 case DRSUAPI_EXOP_NONE:
2633                         if ((nc_instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) {
2634                                 const char *dn_str
2635                                         = ldb_dn_get_linearized(ncRoot_dn);
2636
2637                                 DBG_NOTICE("Rejecting full replication on "
2638                                            "not NC %s", dn_str);
2639
2640                                 return WERR_DS_CANT_FIND_EXPECTED_NC;
2641                         }
2642
2643                         break;
2644                 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
2645                         werr = getncchanges_rid_alloc(b_state, mem_ctx, req10, &r->out.ctr->ctr6, &search_dn);
2646                         W_ERROR_NOT_OK_RETURN(werr);
2647                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2648                                 return WERR_OK;
2649                         }
2650                         break;
2651                 case DRSUAPI_EXOP_REPL_SECRET:
2652                         werr = getncchanges_repl_secret(b_state, mem_ctx, req10,
2653                                                         user_sid,
2654                                                         &r->out.ctr->ctr6,
2655                                                         has_get_all_changes,
2656                                                         &machine_dn);
2657                         r->out.result = werr;
2658                         W_ERROR_NOT_OK_RETURN(werr);
2659                         break;
2660                 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
2661                         werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
2662                         W_ERROR_NOT_OK_RETURN(werr);
2663                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2664                                 return WERR_OK;
2665                         }
2666                         break;
2667                 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
2668                         werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
2669                         W_ERROR_NOT_OK_RETURN(werr);
2670                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2671                                 return WERR_OK;
2672                         }
2673                         break;
2674                 case DRSUAPI_EXOP_FSMO_REQ_PDC:
2675                         werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
2676                         W_ERROR_NOT_OK_RETURN(werr);
2677                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
2678                                 return WERR_OK;
2679                         }
2680                         break;
2681                 case DRSUAPI_EXOP_REPL_OBJ:
2682                         werr = getncchanges_repl_obj(b_state, mem_ctx, req10, user_sid, &r->out.ctr->ctr6);
2683                         r->out.result = werr;
2684                         W_ERROR_NOT_OK_RETURN(werr);
2685                         break;
2686
2687                 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
2688
2689                         DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
2690                                  (unsigned)req10->extended_op));
2691                         return WERR_DS_DRA_NOT_SUPPORTED;
2692                 }
2693
2694                 /* Initialize the state we'll store over the replication cycle */
2695                 getnc_state = talloc_zero(b_state, struct drsuapi_getncchanges_state);
2696                 if (getnc_state == NULL) {
2697                         return WERR_NOT_ENOUGH_MEMORY;
2698                 }
2699                 b_state->getncchanges_state = getnc_state;
2700
2701                 getnc_state->ncRoot_dn = ncRoot_dn;
2702                 talloc_steal(getnc_state, ncRoot_dn);
2703
2704                 getnc_state->ncRoot_guid = samdb_result_guid(res->msgs[0],
2705                                                              "objectGUID");
2706                 ncRoot->guid = getnc_state->ncRoot_guid;
2707
2708                 /* find out if we are to replicate Schema NC */
2709                 ret = ldb_dn_compare_base(ldb_get_schema_basedn(sam_ctx),
2710                                           ncRoot_dn);
2711                 getnc_state->is_schema_nc = (0 == ret);
2712
2713                 TALLOC_FREE(res);
2714         }
2715
2716         if (!ldb_dn_validate(getnc_state->ncRoot_dn) ||
2717             ldb_dn_is_null(getnc_state->ncRoot_dn)) {
2718                 DEBUG(0,(__location__ ": Bad DN '%s'\n",
2719                          drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
2720                 return WERR_DS_DRA_INVALID_PARAMETER;
2721         }
2722
2723         ncRoot->guid = getnc_state->ncRoot_guid;
2724
2725         /* we need the session key for encrypting password attributes */
2726         status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
2727         if (!NT_STATUS_IS_OK(status)) {
2728                 DEBUG(0,(__location__ ": Failed to get session key\n"));
2729                 return WERR_DS_DRA_INTERNAL_ERROR;
2730         }
2731
2732         /* 
2733            TODO: MS-DRSR section 4.1.10.1.1
2734            Work out if this is the start of a new cycle */
2735
2736         if (getnc_state->guids == NULL) {
2737                 const char *extra_filter;
2738                 struct ldb_result *search_res = NULL;
2739                 static const struct drsuapi_DsReplicaCursorCtrEx empty_udv;
2740                 const struct drsuapi_DsReplicaCursorCtrEx *udv = NULL;
2741
2742                 extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
2743
2744                 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2745                         if (req10->uptodateness_vector != NULL) {
2746                                 udv = req10->uptodateness_vector;
2747                         } else {
2748                                 udv = &empty_udv;
2749                         }
2750
2751                         getnc_state->min_usn = req10->highwatermark.highest_usn;
2752                         for (i = 0; i < udv->count; i++) {
2753                                 bool match;
2754                                 const struct drsuapi_DsReplicaCursor *cur =
2755                                         &udv->cursors[i];
2756
2757                                 match = GUID_equal(&invocation_id,
2758                                                    &cur->source_dsa_invocation_id);
2759                                 if (!match) {
2760                                         continue;
2761                                 }
2762                                 if (cur->highest_usn > getnc_state->min_usn) {
2763                                         getnc_state->min_usn = cur->highest_usn;
2764                                 }
2765                                 break;
2766                         }
2767                 } else {
2768                         /* We do not want REPL_SECRETS or REPL_SINGLE to return empty-handed */
2769                         udv = &empty_udv;
2770                         getnc_state->min_usn = 0;
2771                 }
2772
2773                 getnc_state->max_usn = getnc_state->min_usn;
2774
2775                 getnc_state->final_udv = talloc_zero(getnc_state,
2776                                         struct drsuapi_DsReplicaCursor2CtrEx);
2777                 if (getnc_state->final_udv == NULL) {
2778                         return WERR_NOT_ENOUGH_MEMORY;
2779                 }
2780                 werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn,
2781                                           getnc_state->final_udv);
2782                 if (!W_ERROR_IS_OK(werr)) {
2783                         return werr;
2784                 }
2785
2786                 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2787                         werr = getncchanges_collect_objects(b_state, mem_ctx, req10,
2788                                                             search_dn, extra_filter,
2789                                                             &search_res);
2790                 } else {
2791                         werr = getncchanges_collect_objects_exop(b_state, mem_ctx, req10,
2792                                                                  &r->out.ctr->ctr6,
2793                                                                  search_dn, extra_filter,
2794                                                                  &search_res);
2795                 }
2796                 W_ERROR_NOT_OK_RETURN(werr);
2797
2798                 /* extract out the GUIDs list */
2799                 getnc_state->num_records = search_res ? search_res->count : 0;
2800                 getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records);
2801                 W_ERROR_HAVE_NO_MEMORY(getnc_state->guids);
2802
2803                 changes = talloc_array(getnc_state,
2804                                        struct drsuapi_changed_objects,
2805                                        getnc_state->num_records);
2806                 W_ERROR_HAVE_NO_MEMORY(changes);
2807
2808                 for (i=0; i<getnc_state->num_records; i++) {
2809                         changes[i].dn = search_res->msgs[i]->dn;
2810                         changes[i].guid = samdb_result_guid(search_res->msgs[i], "objectGUID");
2811                         changes[i].usn = ldb_msg_find_attr_as_uint64(search_res->msgs[i], "uSNChanged", 0);
2812
2813                         if (changes[i].usn > getnc_state->max_usn) {
2814                                 getnc_state->max_usn = changes[i].usn;
2815                         }
2816                 }
2817
2818                 /* RID_ALLOC returns 3 objects in a fixed order */
2819                 if (req10->extended_op == DRSUAPI_EXOP_FSMO_RID_ALLOC) {
2820                         /* Do nothing */
2821                 } else {
2822                         LDB_TYPESAFE_QSORT(changes,
2823                                            getnc_state->num_records,
2824                                            getnc_state,
2825                                            site_res_cmp_usn_order);
2826                 }
2827
2828                 for (i=0; i < getnc_state->num_records; i++) {
2829                         getnc_state->guids[i] = changes[i].guid;
2830                         if (GUID_all_zero(&getnc_state->guids[i])) {
2831                                 DEBUG(2,("getncchanges: bad objectGUID from %s\n",
2832                                          ldb_dn_get_linearized(search_res->msgs[i]->dn)));
2833                                 return WERR_DS_DRA_INTERNAL_ERROR;
2834                         }
2835                 }
2836
2837                 getnc_state->final_hwm.tmp_highest_usn = getnc_state->max_usn;
2838                 getnc_state->final_hwm.reserved_usn = 0;
2839                 getnc_state->final_hwm.highest_usn = getnc_state->max_usn;
2840
2841                 talloc_free(search_res);
2842                 talloc_free(changes);
2843
2844                 if (req10->extended_op != DRSUAPI_EXOP_NONE) {
2845                         /* Do nothing */
2846                 } else if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) {
2847                         getnc_state->obj_cache = db_open_rbt(getnc_state);
2848                         if (getnc_state->obj_cache == NULL) {
2849                                 return WERR_NOT_ENOUGH_MEMORY;
2850                         }
2851                         getnc_state->is_get_anc = true;
2852                 }
2853         }
2854
2855         if (req10->uptodateness_vector) {
2856                 /* make sure its sorted */
2857                 TYPESAFE_QSORT(req10->uptodateness_vector->cursors,
2858                                req10->uptodateness_vector->count,
2859                                drsuapi_DsReplicaCursor_compare);
2860         }
2861
2862         /* Prefix mapping */
2863         schema = dsdb_get_schema(sam_ctx, mem_ctx);
2864         if (!schema) {
2865                 DEBUG(0,("No schema in sam_ctx\n"));
2866                 return WERR_DS_DRA_INTERNAL_ERROR;
2867         }
2868
2869         r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
2870         if (r->out.ctr->ctr6.naming_context == NULL) {
2871                 return WERR_NOT_ENOUGH_MEMORY;
2872         }
2873         *r->out.ctr->ctr6.naming_context = *ncRoot;
2874
2875         /* find the SID if there is one */
2876         dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
2877
2878         dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
2879         r->out.ctr->ctr6.mapping_ctr = *ctr;
2880
2881         r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
2882         r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2883
2884         r->out.ctr->ctr6.old_highwatermark = req10->highwatermark;
2885         r->out.ctr->ctr6.new_highwatermark = req10->highwatermark;
2886
2887         repl_chunk.ctr6 = &r->out.ctr->ctr6;
2888         repl_chunk.last_object = NULL;
2889
2890         max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
2891         /*
2892          * The client control here only applies in normal replication, not extended
2893          * operations, which return a fixed set, even if the caller
2894          * sets max_object_count == 0
2895          */
2896         if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2897                 /* use this to force single objects at a time, which is useful
2898                  * for working out what object is giving problems
2899                  */
2900                 if (req10->max_object_count < max_objects) {
2901                         max_objects = req10->max_object_count;
2902                 }
2903         }
2904         /*
2905          * TODO: work out how the maximum should be calculated
2906          */
2907         max_links = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max link sync", 1500);
2908
2909         /*
2910          * Maximum time that we can spend in a getncchanges
2911          * in order to avoid timeout of the other part.
2912          * 10 seconds by default.
2913          */
2914         max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max work time", 10);
2915
2916         if (req10->partial_attribute_set != NULL) {
2917                 struct dsdb_syntax_ctx syntax_ctx;
2918                 uint32_t j = 0;
2919
2920                 dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
2921                 syntax_ctx.pfm_remote = pfm_remote;
2922
2923                 local_pas = talloc_array(b_state, uint32_t, req10->partial_attribute_set->num_attids);
2924
2925                 for (j = 0; j < req10->partial_attribute_set->num_attids; j++) {
2926                         getncchanges_attid_remote_to_local(schema,
2927                                                            &syntax_ctx,
2928                                                            req10->partial_attribute_set->attids[j],
2929                                                            (enum drsuapi_DsAttributeId *)&local_pas[j],
2930                                                            NULL);
2931                 }
2932
2933                 LDB_TYPESAFE_QSORT(local_pas,
2934                                    req10->partial_attribute_set->num_attids,
2935                                    NULL,
2936                                    uint32_t_ptr_cmp);
2937         }
2938
2939         for (i=getnc_state->num_processed;
2940              i<getnc_state->num_records &&
2941                      (r->out.ctr->ctr6.object_count < max_objects)
2942                      && !max_wait_reached;
2943             i++) {
2944                 struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
2945                 struct ldb_message *msg;
2946                 static const char * const msg_attrs[] = {
2947                                             "*",
2948                                             "nTSecurityDescriptor",
2949                                             "parentGUID",
2950                                             "replPropertyMetaData",
2951                                             DSDB_SECRET_ATTRIBUTES,
2952                                             NULL };
2953                 struct ldb_result *msg_res;
2954                 struct ldb_dn *msg_dn;
2955                 bool obj_already_sent = false;
2956                 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2957
2958                 msg_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
2959                                         GUID_string(tmp_ctx, &getnc_state->guids[i]));
2960                 W_ERROR_HAVE_NO_MEMORY(msg_dn);
2961
2962                 /*
2963                  * by re-searching here we avoid having a lot of full
2964                  * records in memory between calls to getncchanges.
2965                  *
2966                  * We expect that we may get some objects that vanish
2967                  * (tombstone expunge) between the first and second
2968                  * check.
2969                  */
2970                 ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx, &msg_res,
2971                                                       msg_dn,
2972                                                       LDB_SCOPE_BASE, msg_attrs, NULL);
2973                 if (ret != LDB_SUCCESS) {
2974                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
2975                                 DEBUG(1,("getncchanges: failed to fetch DN %s - %s\n",
2976                                          ldb_dn_get_extended_linearized(tmp_ctx, msg_dn, 1),
2977                                          ldb_errstring(sam_ctx)));
2978                         }
2979                         TALLOC_FREE(tmp_ctx);
2980                         continue;
2981                 }
2982
2983                 if (msg_res->count == 0) {
2984                         DEBUG(1,("getncchanges: got LDB_SUCCESS but failed"
2985                                  "to get any results in fetch of DN "
2986                                  "%s (race with tombstone expunge?)\n",
2987                                  ldb_dn_get_extended_linearized(tmp_ctx,
2988                                                                 msg_dn, 1)));
2989                         TALLOC_FREE(tmp_ctx);
2990                         continue;
2991                 }
2992
2993                 msg = msg_res->msgs[0];
2994
2995                 /*
2996                  * Check if we've already sent the object as an ancestor of
2997                  * another object. If so, we don't need to send it again
2998                  */
2999                 if (getnc_state->obj_cache != NULL) {
3000                         werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
3001                                                                &getnc_state->guids[i]);
3002                         if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
3003                                 obj_already_sent = true;
3004                         }
3005                 }
3006
3007                 if (!obj_already_sent) {
3008                         max_wait_reached = (time(NULL) - start > max_wait);
3009
3010                         /*
3011                          * Construct an object, ready to send (this will include
3012                          * the object's ancestors as well, if needed)
3013                          */
3014                         werr = getncchanges_get_obj_to_send(msg, mem_ctx, sam_ctx,
3015                                                             getnc_state, schema,
3016                                                             &session_key, req10,
3017                                                             max_wait_reached,
3018                                                             local_pas, machine_dn,