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