s4-rpc_server/drsuapi: Ensure logs show DN for replicated objects, not (null)
[samba.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 "../libcli/drsuapi/drsuapi.h"
35 #include "lib/util/binsearch.h"
36 #include "lib/util/tsort.h"
37 #include "auth/session.h"
38 #include "dsdb/common/util.h"
39 #include "lib/dbwrap/dbwrap.h"
40 #include "lib/dbwrap/dbwrap_rbt.h"
41 #include "librpc/gen_ndr/ndr_misc.h"
42
43 #undef DBGC_CLASS
44 #define DBGC_CLASS            DBGC_DRS_REPL
45
46 #define DRS_GUID_SIZE       16
47 #define DEFAULT_MAX_OBJECTS 1000
48 #define DEFAULT_MAX_LINKS   1500
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 broken_samba_4_5_get_anc_emulation;
64         bool is_get_tgt;
65         uint64_t min_usn;
66         uint64_t max_usn;
67         struct drsuapi_DsReplicaHighWaterMark last_hwm;
68         struct ldb_dn *last_dn;
69         struct drsuapi_DsReplicaHighWaterMark final_hwm;
70         struct drsuapi_DsReplicaCursor2CtrEx *final_udv;
71         struct drsuapi_DsReplicaLinkedAttribute *la_list;
72         uint32_t la_count;
73         uint32_t la_idx;
74
75         /* these are just used for debugging the replication's progress */
76         uint32_t links_given;
77         uint32_t total_links;
78 };
79
80 /* We must keep the GUIDs in NDR form for sorting */
81 struct la_for_sorting {
82         const struct drsuapi_DsReplicaLinkedAttribute *link;
83         uint8_t target_guid[DRS_GUID_SIZE];
84         uint8_t source_guid[DRS_GUID_SIZE];
85 };
86
87 /*
88  * stores the state for a chunk of replication data. This state information
89  * only exists for a single call to dcesrv_drsuapi_DsGetNCChanges()
90  */
91 struct getncchanges_repl_chunk {
92         uint32_t max_objects;
93         uint32_t max_links;
94         uint32_t tgt_la_count;
95         bool immediate_link_sync;
96         time_t max_wait;
97         time_t start;
98
99         /* stores the objects to be sent in this chunk */
100         uint32_t object_count;
101         struct drsuapi_DsReplicaObjectListItemEx *object_list;
102
103         /* the last object added to this replication chunk */
104         struct drsuapi_DsReplicaObjectListItemEx *last_object;
105 };
106
107 static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1,
108                                               const struct drsuapi_DsReplicaHighWaterMark *h2)
109 {
110         if (h1->highest_usn < h2->highest_usn) {
111                 return -1;
112         } else if (h1->highest_usn > h2->highest_usn) {
113                 return 1;
114         } else if (h1->tmp_highest_usn < h2->tmp_highest_usn) {
115                 return -1;
116         } else if (h1->tmp_highest_usn > h2->tmp_highest_usn) {
117                 return 1;
118         } else if (h1->reserved_usn < h2->reserved_usn) {
119                 return -1;
120         } else if (h1->reserved_usn > h2->reserved_usn) {
121                 return 1;
122         }
123
124         return 0;
125 }
126
127 /*
128   build a DsReplicaObjectIdentifier from a ldb msg
129  */
130 static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
131                                                                        const struct ldb_message *msg)
132 {
133         struct drsuapi_DsReplicaObjectIdentifier *identifier;
134         struct dom_sid *sid;
135
136         identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
137         if (identifier == NULL) {
138                 return NULL;
139         }
140
141         identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
142         identifier->guid = samdb_result_guid(msg, "objectGUID");
143
144         sid = samdb_result_dom_sid(identifier, msg, "objectSid");
145         if (sid) {
146                 identifier->sid = *sid;
147         } else {
148                 ZERO_STRUCT(identifier->sid);
149         }
150         return identifier;
151 }
152
153 static int udv_compare(const struct GUID *guid1, struct GUID guid2)
154 {
155         return GUID_compare(guid1, &guid2);
156 }
157
158 /*
159   see if we can filter an attribute using the uptodateness_vector
160  */
161 static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
162                        const struct GUID *originating_invocation_id,
163                        uint64_t originating_usn)
164 {
165         const struct drsuapi_DsReplicaCursor *c;
166         if (udv == NULL) return false;
167         BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id,
168                             originating_invocation_id, udv_compare, c);
169         if (c && originating_usn <= c->highest_usn) {
170                 return true;
171         }
172         return false;
173 }
174
175 static int uint32_t_cmp(uint32_t a1, uint32_t a2)
176 {
177         if (a1 == a2) return 0;
178         return a1 > a2 ? 1 : -1;
179 }
180
181 static int uint32_t_ptr_cmp(uint32_t *a1, uint32_t *a2)
182 {
183         if (*a1 == *a2) return 0;
184         return *a1 > *a2 ? 1 : -1;
185 }
186
187 static WERROR getncchanges_attid_remote_to_local(const struct dsdb_schema *schema,
188                                                  const struct dsdb_syntax_ctx *ctx,
189                                                  enum drsuapi_DsAttributeId remote_attid_as_enum,
190                                                  enum drsuapi_DsAttributeId *local_attid_as_enum,
191                                                  const struct dsdb_attribute **_sa)
192 {
193         WERROR werr;
194         const struct dsdb_attribute *sa = NULL;
195
196         if (ctx->pfm_remote == NULL) {
197                 DEBUG(7, ("No prefixMap supplied, falling back to local prefixMap.\n"));
198                 goto fail;
199         }
200
201         werr = dsdb_attribute_drsuapi_remote_to_local(ctx,
202                                                       remote_attid_as_enum,
203                                                       local_attid_as_enum,
204                                                       _sa);
205         if (!W_ERROR_IS_OK(werr)) {
206                 DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n"));
207                 goto fail;
208         }
209
210         return werr;
211 fail:
212
213         sa = dsdb_attribute_by_attributeID_id(schema, remote_attid_as_enum);
214         if (sa == NULL) {
215                 return WERR_DS_DRA_SCHEMA_MISMATCH;
216         } else {
217                 if (local_attid_as_enum != NULL) {
218                         *local_attid_as_enum = sa->attributeID_id;
219                 }
220                 if (_sa != NULL) {
221                         *_sa = sa;
222                 }
223                 return WERR_OK;
224         }
225 }
226
227 static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
228                                                 TALLOC_CTX *mem_ctx,
229                                                 struct ldb_message **msg,
230                                                 struct ldb_dn *object_dn,
231                                                 const struct GUID *object_guid,
232                                                 const struct dsdb_attribute *sa,
233                                                 struct replPropertyMetaData1 *meta_data,
234                                                 struct ldb_message *revealed_users)
235 {
236         enum ndr_err_code ndr_err;
237         int ldb_err;
238         char *attr_str = NULL;
239         char *attr_hex = NULL;
240         DATA_BLOB attr_blob;
241         struct ldb_message_element *existing = NULL, *el_add = NULL, *el_del = NULL;
242         const char * const * secret_attributes = ldb_get_opaque(sam_ctx, "LDB_SECRET_ATTRIBUTE_LIST");
243
244         if (!ldb_attr_in_list(secret_attributes,
245                               sa->lDAPDisplayName)) {
246                 return WERR_OK;
247         }
248
249
250         ndr_err = ndr_push_struct_blob(&attr_blob, mem_ctx, meta_data, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaData1);
251         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
252                 return WERR_DS_DRA_INTERNAL_ERROR;
253         }
254
255         attr_hex = hex_encode_talloc(mem_ctx, attr_blob.data, attr_blob.length);
256         if (attr_hex == NULL) {
257                 return WERR_NOT_ENOUGH_MEMORY;
258         }
259
260         attr_str = talloc_asprintf(mem_ctx, "B:%zd:%s:%s", attr_blob.length*2, attr_hex, ldb_dn_get_linearized(object_dn));
261         if (attr_str == NULL) {
262                 return WERR_NOT_ENOUGH_MEMORY;
263         }
264
265         existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
266         if (existing != NULL) {
267                 /* Replace the old value (if one exists) with the current one */
268                 struct parsed_dn *link_dns;
269                 struct parsed_dn *exact = NULL, *unused = NULL;
270                 uint8_t attid[4];
271                 DATA_BLOB partial_meta;
272
273                 ldb_err = get_parsed_dns_trusted(mem_ctx, existing, &link_dns);
274                 if (ldb_err != LDB_SUCCESS) {
275                         return WERR_DS_DRA_INTERNAL_ERROR;
276                 }
277
278                 /* Construct a partial metadata blob to match on in the DB */
279                 SIVAL(attid, 0, sa->attributeID_id);
280                 partial_meta.length = 4;
281                 partial_meta.data = attid;
282
283                 /* Binary search using GUID and attribute id for uniqueness */
284                 ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values,
285                                          object_guid, object_dn,
286                                          partial_meta, 4,
287                                          &exact, &unused,
288                                          DSDB_SYNTAX_BINARY_DN, true);
289
290                 if (ldb_err != LDB_SUCCESS) {
291                         DEBUG(0,(__location__ ": Failed parsed DN find - %s\n",
292                                  ldb_errstring(sam_ctx)));
293                         return WERR_DS_DRA_INTERNAL_ERROR;
294                 }
295
296                 if (exact != NULL) {
297                         /* Perform some verification of the blob */
298                         struct replPropertyMetaData1 existing_meta_data;
299                         ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_dn->extra_part,
300                                                                    &existing_meta_data,
301                                                                    (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
302                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
303                                 return WERR_DS_DRA_INTERNAL_ERROR;
304                         }
305
306                         if (existing_meta_data.attid == sa->attributeID_id) {
307                                 ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
308                                 if (ldb_err != LDB_SUCCESS) {
309                                         return WERR_DS_DRA_INTERNAL_ERROR;
310                                 }
311
312                                 el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
313                                 if (el_del->values == NULL) {
314                                         return WERR_NOT_ENOUGH_MEMORY;
315                                 }
316                                 el_del->values[0] = *exact->v;
317                                 el_del->num_values = 1;
318                         } else {
319                                 return WERR_DS_DRA_INTERNAL_ERROR;
320                         }
321                 }
322         }
323
324         ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_ADD, &el_add);
325         if (ldb_err != LDB_SUCCESS) {
326                 return WERR_DS_DRA_INTERNAL_ERROR;
327         }
328
329         el_add->values = talloc_array((*msg)->elements, struct ldb_val, 1);
330         if (el_add->values == NULL) {
331                 return WERR_NOT_ENOUGH_MEMORY;
332
333         }
334
335         el_add->values[0] = data_blob_string_const(attr_str);
336         el_add->num_values = 1;
337
338         return WERR_OK;
339 }
340
341 /*
342  * This function filter attributes for build_object based on the
343  * uptodatenessvector and partial attribute set.
344  *
345  * Any secret attributes are forced here for REPL_SECRET, and audited at this
346  * point with msDS-RevealedUsers.
347  */
348 static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItemEx *obj,
349                                           struct replPropertyMetaDataBlob md,
350                                           struct ldb_context *sam_ctx,
351                                           const struct ldb_message *msg,
352                                           const struct GUID *guid,
353                                           uint32_t *count,
354                                           uint64_t highest_usn,
355                                           const struct dsdb_attribute *rdn_sa,
356                                           struct dsdb_schema *schema,
357                                           struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
358                                           struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
359                                           uint32_t *local_pas,
360                                           uint32_t *attids,
361                                           bool exop_secret,
362                                           struct ldb_message **revealed_list_msg,
363                                           struct ldb_message *existing_revealed_list_msg)
364 {
365         uint32_t i, n;
366         WERROR werr;
367         for (n=i=0; i<md.ctr.ctr1.count; i++) {
368                 const struct dsdb_attribute *sa;
369                 bool force_attribute = false;
370
371                 /* if the attribute has not changed, and it is not the
372                    instanceType then don't include it */
373                 if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
374                     !exop_secret &&
375                     md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) continue;
376
377                 /* don't include the rDN */
378                 if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
379
380                 sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
381                 if (!sa) {
382                         DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n",
383                                  (unsigned int)md.ctr.ctr1.array[i].attid,
384                                  ldb_dn_get_linearized(msg->dn)));
385                         return WERR_DS_DRA_INTERNAL_ERROR;
386                 }
387
388                 if (sa->linkID) {
389                         struct ldb_message_element *el;
390                         el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
391                         if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
392                                 /* don't send upgraded links inline */
393                                 continue;
394                         }
395                 }
396
397                 if (exop_secret &&
398                     !dsdb_attr_in_rodc_fas(sa)) {
399                         force_attribute = true;
400                         DEBUG(4,("Forcing attribute %s in %s\n",
401                                  sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
402                         werr = getncchanges_update_revealed_list(sam_ctx, obj,
403                                                                  revealed_list_msg,
404                                                                  msg->dn, guid, sa,
405                                                                  &md.ctr.ctr1.array[i],
406                                                                  existing_revealed_list_msg);
407                         if (!W_ERROR_IS_OK(werr)) {
408                                 return werr;
409                         }
410                 }
411
412                 /* filter by uptodateness_vector */
413                 if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType &&
414                     !force_attribute &&
415                     udv_filter(uptodateness_vector,
416                                &md.ctr.ctr1.array[i].originating_invocation_id,
417                                md.ctr.ctr1.array[i].originating_usn)) {
418                         continue;
419                 }
420
421                 /* filter by partial_attribute_set */
422                 if (partial_attribute_set && !force_attribute) {
423                         uint32_t *result = NULL;
424                         BINARY_ARRAY_SEARCH_V(local_pas, partial_attribute_set->num_attids, sa->attributeID_id,
425                                               uint32_t_cmp, result);
426                         if (result == NULL) {
427                                 continue;
428                         }
429                 }
430
431                 obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
432                 obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
433                 obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
434                 obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
435                 attids[n] = md.ctr.ctr1.array[i].attid;
436
437                 n++;
438         }
439
440         *count = n;
441
442         return WERR_OK;
443 }
444
445 /* 
446   drsuapi_DsGetNCChanges for one object
447 */
448 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
449                                           const struct ldb_message *msg,
450                                           struct ldb_context *sam_ctx,
451                                           struct drsuapi_getncchanges_state *getnc_state,
452                                           struct dsdb_schema *schema,
453                                           DATA_BLOB *session_key,
454                                           struct drsuapi_DsGetNCChangesRequest10 *req10,
455                                           bool force_object_return,
456                                           uint32_t *local_pas,
457                                           struct ldb_dn *machine_dn,
458                                           const struct GUID *guid)
459 {
460         const struct ldb_val *md_value;
461         uint32_t i, n;
462         struct replPropertyMetaDataBlob md;
463         uint32_t rid = 0;
464         int ldb_err;
465         enum ndr_err_code ndr_err;
466         uint32_t *attids;
467         const char *rdn;
468         const struct dsdb_attribute *rdn_sa;
469         uint64_t uSNChanged;
470         unsigned int instanceType;
471         struct dsdb_syntax_ctx syntax_ctx;
472         struct ldb_result *res = NULL;
473         WERROR werr;
474         int ret;
475         uint32_t replica_flags = req10->replica_flags;
476         struct drsuapi_DsPartialAttributeSet *partial_attribute_set =
477                         req10->partial_attribute_set;
478         struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector =
479                         req10->uptodateness_vector;
480         enum drsuapi_DsExtendedOperation extended_op = req10->extended_op;
481         bool is_schema_nc = getnc_state->is_schema_nc;
482         uint64_t highest_usn = getnc_state->min_usn;
483
484         /* make dsdb sytanx context for conversions */
485         dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
486         syntax_ctx.is_schema_nc = is_schema_nc;
487
488         uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
489         instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
490         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
491                 obj->is_nc_prefix = true;
492                 obj->parent_object_guid = NULL;
493         } else {
494                 obj->is_nc_prefix = false;
495                 obj->parent_object_guid = talloc(obj, struct GUID);
496                 if (obj->parent_object_guid == NULL) {
497                         return WERR_DS_DRA_INTERNAL_ERROR;
498                 }
499                 *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
500                 if (GUID_all_zero(obj->parent_object_guid)) {
501                         DEBUG(0,(__location__ ": missing parentGUID for %s\n",
502                                  ldb_dn_get_linearized(msg->dn)));
503                         return WERR_DS_DRA_INTERNAL_ERROR;
504                 }
505         }
506         obj->next_object = NULL;
507
508         md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
509         if (!md_value) {
510                 /* nothing to send */
511                 return WERR_OK;
512         }
513
514         if (instanceType & INSTANCE_TYPE_UNINSTANT) {
515                 /* don't send uninstantiated objects */
516                 return WERR_OK;
517         }
518
519         if (uSNChanged <= highest_usn) {
520                 /* nothing to send */
521                 return WERR_OK;
522         }
523
524         ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
525                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
526         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
527                 return WERR_DS_DRA_INTERNAL_ERROR;
528         }
529
530         if (md.version != 1) {
531                 return WERR_DS_DRA_INTERNAL_ERROR;
532         }
533
534         rdn = ldb_dn_get_rdn_name(msg->dn);
535         if (rdn == NULL) {
536                 DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
537                 return WERR_DS_DRA_INTERNAL_ERROR;
538         }
539
540         rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
541         if (rdn_sa == NULL) {
542                 DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n",
543                          rdn, ldb_dn_get_linearized(msg->dn)));
544                 return WERR_DS_DRA_INTERNAL_ERROR;
545         }
546
547         obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
548         attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
549
550         obj->object.identifier = get_object_identifier(obj, msg);
551         if (obj->object.identifier == NULL) {
552                 return WERR_NOT_ENOUGH_MEMORY;
553         }
554         dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
555
556         obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
557
558         if (extended_op == DRSUAPI_EXOP_REPL_SECRET) {
559                 /* Get the existing revealed users for the destination */
560                 struct ldb_message *revealed_list_msg = NULL;
561                 struct ldb_message *existing_revealed_list_msg = NULL;
562                 const char *machine_attrs[] = {
563                         "msDS-RevealedUsers",
564                         NULL
565                 };
566
567                 revealed_list_msg = ldb_msg_new(sam_ctx);
568                 if (revealed_list_msg == NULL) {
569                         return WERR_NOT_ENOUGH_MEMORY;
570                 }
571                 revealed_list_msg->dn = machine_dn;
572
573                 ret = ldb_transaction_start(sam_ctx);
574                 if (ret != LDB_SUCCESS) {
575                         DEBUG(0,(__location__ ": Failed transaction start - %s\n",
576                                  ldb_errstring(sam_ctx)));
577                         return WERR_DS_DRA_INTERNAL_ERROR;
578                 }
579
580                 ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
581                 if (ldb_err != LDB_SUCCESS || res->count != 1) {
582                         ldb_transaction_cancel(sam_ctx);
583                         return WERR_DS_DRA_INTERNAL_ERROR;
584                 }
585
586                 existing_revealed_list_msg = res->msgs[0];
587
588                 werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg,
589                                                    guid, &n, highest_usn,
590                                                    rdn_sa, schema,
591                                                    uptodateness_vector,
592                                                    partial_attribute_set, local_pas,
593                                                    attids,
594                                                    true,
595                                                    &revealed_list_msg,
596                                                    existing_revealed_list_msg);
597                 if (!W_ERROR_IS_OK(werr)) {
598                         ldb_transaction_cancel(sam_ctx);
599                         return werr;
600                 }
601
602                 if (revealed_list_msg != NULL) {
603                         ret = ldb_modify(sam_ctx, revealed_list_msg);
604                         if (ret != LDB_SUCCESS) {
605                                 DEBUG(0,(__location__ ": Failed to alter revealed links - %s\n",
606                                          ldb_errstring(sam_ctx)));
607                                 ldb_transaction_cancel(sam_ctx);
608                                 return WERR_DS_DRA_INTERNAL_ERROR;
609                         }
610                 }
611
612                 ret = ldb_transaction_commit(sam_ctx);
613                 if (ret != LDB_SUCCESS) {
614                         DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
615                                  ldb_errstring(sam_ctx)));
616                         return WERR_DS_DRA_INTERNAL_ERROR;
617                 }
618         } else {
619                 werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid,
620                                                    &n, highest_usn, rdn_sa,
621                                                    schema, uptodateness_vector,
622                                                    partial_attribute_set, local_pas,
623                                                    attids,
624                                                    false,
625                                                    NULL,
626                                                    NULL);
627                 if (!W_ERROR_IS_OK(werr)) {
628                         return werr;
629                 }
630         }
631
632         /* ignore it if its an empty change. Note that renames always
633          * change the 'name' attribute, so they won't be ignored by
634          * this
635
636          * the force_object_return check is used to force an empty
637          * object return when we timeout in the getncchanges loop.
638          * This allows us to return an empty object, which keeps the
639          * client happy while preventing timeouts
640          */
641         if (n == 0 ||
642             (n == 1 &&
643              attids[0] == DRSUAPI_ATTID_instanceType &&
644              !force_object_return)) {
645                 talloc_free(obj->meta_data_ctr);
646                 obj->meta_data_ctr = NULL;
647                 return WERR_OK;
648         }
649
650         obj->meta_data_ctr->count = n;
651
652         obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
653         obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
654         obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
655                                                             obj->object.attribute_ctr.num_attributes);
656         if (obj->object.attribute_ctr.attributes == NULL) {
657                 return WERR_NOT_ENOUGH_MEMORY;
658         }
659
660         /*
661          * Note that the meta_data array and the attributes array must
662          * be the same size and in the same order
663          */
664         for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
665                 struct ldb_message_element *el;
666                 const struct dsdb_attribute *sa;
667
668                 sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
669                 if (!sa) {
670                         DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
671                         return WERR_DS_DRA_INTERNAL_ERROR;
672                 }
673
674                 el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
675                 if (el == NULL) {
676                         /* this happens for attributes that have been removed */
677                         DEBUG(5,("No element '%s' for attributeID %u in message\n",
678                                  sa->lDAPDisplayName, attids[i]));
679                         ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
680                         obj->object.attribute_ctr.attributes[i].attid =
681                                         dsdb_attribute_get_attid(sa, syntax_ctx.is_schema_nc);
682                 } else {
683                         werr = sa->syntax->ldb_to_drsuapi(&syntax_ctx, sa, el, obj,
684                                                           &obj->object.attribute_ctr.attributes[i]);
685                         if (!W_ERROR_IS_OK(werr)) {
686                                 DEBUG(0,("Unable to convert %s on %s to DRS object - %s\n",
687                                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
688                                          win_errstr(werr)));
689                                 return werr;
690                         }
691                         /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
692                          * check if attribute is secret and send a null value
693                          */
694                         if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
695                                 drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
696                                                                  &obj->meta_data_ctr->meta_data[i]);
697                         }
698                         /* some attributes needs to be encrypted
699                            before being sent */
700                         werr = drsuapi_encrypt_attribute(obj, session_key, rid, 
701                                                          &obj->object.attribute_ctr.attributes[i]);
702                         if (!W_ERROR_IS_OK(werr)) {
703                                 DEBUG(0,("Unable to encrypt %s on %s in DRS object - %s\n",
704                                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
705                                          win_errstr(werr)));
706                                 return werr;
707                         }
708                 }
709                 if (attids[i] != obj->object.attribute_ctr.attributes[i].attid) {
710                         DEBUG(0, ("Unable to replicate attribute %s on %s via DRS, incorrect attributeID:  "
711                                   "0x%08x vs 0x%08x "
712                                   "Run dbcheck!\n",
713                                   sa->lDAPDisplayName,
714                                   ldb_dn_get_linearized(msg->dn),
715                                   attids[i],
716                                   obj->object.attribute_ctr.attributes[i].attid));
717                         return WERR_DS_DATABASE_ERROR;
718                 }
719         }
720
721         return WERR_OK;
722 }
723
724 /*
725   add one linked attribute from an object to the list of linked
726   attributes in a getncchanges request
727  */
728 static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
729                                     struct ldb_context *sam_ctx,
730                                     const struct dsdb_schema *schema,
731                                     const struct dsdb_attribute *sa,
732                                     const struct ldb_message *msg,
733                                     struct dsdb_dn *dsdb_dn,
734                                     struct drsuapi_DsReplicaLinkedAttribute **la_list,
735                                     uint32_t *la_count,
736                                     bool is_schema_nc)
737 {
738         struct drsuapi_DsReplicaLinkedAttribute *la;
739         bool active;
740         NTSTATUS status;
741         WERROR werr;
742
743         (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
744         W_ERROR_HAVE_NO_MEMORY(*la_list);
745
746         la = &(*la_list)[*la_count];
747
748         la->identifier = get_object_identifier(*la_list, msg);
749         W_ERROR_HAVE_NO_MEMORY(la->identifier);
750
751         active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
752
753         if (!active) {
754                 /* We have to check that the inactive link still point to an existing object */
755                 struct GUID guid;
756                 struct ldb_dn *tdn;
757                 int ret;
758                 const char *v;
759
760                 v = ldb_msg_find_attr_as_string(msg, "isDeleted", "FALSE");
761                 if (strncmp(v, "TRUE", 4) == 0) {
762                         /*
763                           * Note: we skip the transmition of the deleted link even if the other part used to
764                           * know about it because when we transmit the deletion of the object, the link will
765                           * be deleted too due to deletion of object where link points and Windows do so.
766                           */
767                         if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008_R2) {
768                                 v = ldb_msg_find_attr_as_string(msg, "isRecycled", "FALSE");
769                                 /*
770                                  * On Windows 2008R2 isRecycled is always present even if FL or DL are < FL 2K8R2
771                                  * if it join an existing domain with deleted objets, it firsts impose to have a
772                                  * schema with the is-Recycled object and for all deleted objects it adds the isRecycled
773                                  * either during initial replication or after the getNCChanges.
774                                  * Behavior of samba has been changed to always have this attribute if it's present in the schema.
775                                  *
776                                  * So if FL <2K8R2 isRecycled might be here or not but we don't care, it's meaning less.
777                                  * If FL >=2K8R2 we are sure that this attribute will be here.
778                                  * For this kind of forest level we do not return the link if the object is recycled
779                                  * (isRecycled = true).
780                                  */
781                                 if (strncmp(v, "TRUE", 4) == 0) {
782                                         DEBUG(2, (" object %s is recycled, not returning linked attribute !\n",
783                                                                 ldb_dn_get_linearized(msg->dn)));
784                                         return WERR_OK;
785                                 }
786                         } else {
787                                 return WERR_OK;
788                         }
789                 }
790                 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
791                 if (!NT_STATUS_IS_OK(status)) {
792                         DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
793                                 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
794                         return ntstatus_to_werror(status);
795                 }
796                 ret = dsdb_find_dn_by_guid(sam_ctx, mem_ctx, &guid, 0, &tdn);
797                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
798                         DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
799                                                 GUID_string(mem_ctx, &guid)));
800                         return WERR_OK;
801                 } else if (ret != LDB_SUCCESS) {
802                         DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
803                                                 GUID_string(mem_ctx, &guid),
804                                                 ret));
805                         return WERR_OK;
806                 }
807         }
808         la->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
809         la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
810
811         status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
812         if (!NT_STATUS_IS_OK(status)) {
813                 DEBUG(0,(__location__ " No RMD_VERSION in linked attribute '%s' in '%s'\n",
814                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
815                 return ntstatus_to_werror(status);
816         }
817         status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
818         if (!NT_STATUS_IS_OK(status)) {
819                 DEBUG(0,(__location__ " No RMD_CHANGETIME in linked attribute '%s' in '%s'\n",
820                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
821                 return ntstatus_to_werror(status);
822         }
823         status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
824         if (!NT_STATUS_IS_OK(status)) {
825                 DEBUG(0,(__location__ " No RMD_INVOCID in linked attribute '%s' in '%s'\n",
826                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
827                 return ntstatus_to_werror(status);
828         }
829         status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
830         if (!NT_STATUS_IS_OK(status)) {
831                 DEBUG(0,(__location__ " No RMD_ORIGINATING_USN in linked attribute '%s' in '%s'\n",
832                          sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
833                 return ntstatus_to_werror(status);
834         }
835
836         status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
837         if (!NT_STATUS_IS_OK(status)) {
838                 /* this is possible for upgraded links */
839                 la->originating_add_time = la->meta_data.originating_change_time;
840         }
841
842         werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
843         W_ERROR_NOT_OK_RETURN(werr);
844
845         (*la_count)++;
846         return WERR_OK;
847 }
848
849
850 /*
851   add linked attributes from an object to the list of linked
852   attributes in a getncchanges request
853  */
854 static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
855                                        TALLOC_CTX *mem_ctx,
856                                        bool is_schema_nc,
857                                        struct dsdb_schema *schema,
858                                        uint64_t highest_usn,
859                                        uint32_t replica_flags,
860                                        const struct ldb_message *msg,
861                                        struct drsuapi_DsReplicaLinkedAttribute **la_list,
862                                        uint32_t *la_count,
863                                        struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
864 {
865         unsigned int i;
866         TALLOC_CTX *tmp_ctx = NULL;
867         uint64_t uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
868         bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
869
870         if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
871                 if (!is_critical) {
872                         return WERR_OK;
873                 }
874         }
875
876         if (uSNChanged <= highest_usn) {
877                 return WERR_OK;
878         }
879
880         tmp_ctx = talloc_new(mem_ctx);
881         if (tmp_ctx == NULL) {
882                 return WERR_NOT_ENOUGH_MEMORY;
883         }
884
885         for (i=0; i<msg->num_elements; i++) {
886                 struct ldb_message_element *el = &msg->elements[i];
887                 const struct dsdb_attribute *sa;
888                 unsigned int j;
889
890                 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
891
892                 if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
893                         /* we only want forward links */
894                         continue;
895                 }
896
897                 if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
898                         /* its an old style link, it will have been
899                          * sent in the main replication data */
900                         continue;
901                 }
902
903                 for (j=0; j<el->num_values; j++) {
904                         struct dsdb_dn *dsdb_dn;
905                         uint64_t local_usn;
906                         uint64_t originating_usn;
907                         NTSTATUS status, status2;
908                         WERROR werr;
909                         struct GUID originating_invocation_id;
910
911                         dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
912                         if (dsdb_dn == NULL) {
913                                 DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
914                                          el->name, ldb_dn_get_linearized(msg->dn)));
915                                 talloc_free(tmp_ctx);
916                                 return WERR_DS_DRA_INTERNAL_ERROR;
917                         }
918
919                         status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
920                         if (!NT_STATUS_IS_OK(status)) {
921                                 /* this can happen for attributes
922                                    given to us with old style meta
923                                    data */
924                                 continue;
925                         }
926
927                         if (local_usn > uSNChanged) {
928                                 DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
929                                          el->name, ldb_dn_get_linearized(msg->dn)));
930                                 talloc_free(tmp_ctx);
931                                 return WERR_DS_DRA_INTERNAL_ERROR;
932                         }
933
934                         if (local_usn <= highest_usn) {
935                                 continue;
936                         }
937
938                         status = dsdb_get_extended_dn_guid(dsdb_dn->dn,
939                                                            &originating_invocation_id,
940                                                            "RMD_INVOCID");
941                         status2 = dsdb_get_extended_dn_uint64(dsdb_dn->dn,
942                                                               &originating_usn,
943                                                               "RMD_ORIGINATING_USN");
944
945                         if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2)) {
946                                 if (udv_filter(uptodateness_vector,
947                                                &originating_invocation_id,
948                                                originating_usn)) {
949                                         continue;
950                                 }
951                         }
952
953                         werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema,
954                                                      sa, msg, dsdb_dn, la_list,
955                                                      la_count, is_schema_nc);
956                         if (!W_ERROR_IS_OK(werr)) {
957                                 talloc_free(tmp_ctx);
958                                 return werr;
959                         }
960                 }
961         }
962
963         talloc_free(tmp_ctx);
964         return WERR_OK;
965 }
966
967 /*
968   fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
969  */
970 static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
971                                  struct ldb_dn *ncRoot_dn,
972                                  struct drsuapi_DsReplicaCursor2CtrEx *udv)
973 {
974         int ret;
975
976         udv->version = 2;
977         udv->reserved1 = 0;
978         udv->reserved2 = 0;
979
980         ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
981         if (ret != LDB_SUCCESS) {
982                 DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
983                          ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
984                 return WERR_DS_DRA_INTERNAL_ERROR;
985         }
986
987         return WERR_OK;
988 }
989
990
991 /* comparison function for linked attributes - see CompareLinks() in
992  * MS-DRSR section 4.1.10.5.17 */
993 static int linked_attribute_compare(const struct la_for_sorting *la1,
994                                     const struct la_for_sorting *la2)
995 {
996         int c;
997         c = memcmp(la1->source_guid,
998                    la2->source_guid, sizeof(la2->source_guid));
999         if (c != 0) {
1000                 return c;
1001         }
1002
1003         if (la1->link->attid != la2->link->attid) {
1004                 return la1->link->attid < la2->link->attid? -1:1;
1005         }
1006
1007         if ((la1->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
1008             (la2->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
1009                 return (la1->link->flags &
1010                         DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
1011         }
1012
1013         return memcmp(la1->target_guid,
1014                       la2->target_guid, sizeof(la2->target_guid));
1015 }
1016
1017 struct drsuapi_changed_objects {
1018         struct ldb_dn *dn;
1019         struct GUID guid;
1020         uint64_t usn;
1021 };
1022
1023
1024 /*
1025   sort the objects we send by tree order (Samba 4.5 emulation)
1026  */
1027 static int site_res_cmp_anc_order(struct drsuapi_changed_objects *m1,
1028                                   struct drsuapi_changed_objects *m2,
1029                                   struct drsuapi_getncchanges_state *getnc_state)
1030 {
1031         return ldb_dn_compare(m2->dn, m1->dn);
1032 }
1033
1034 /*
1035   sort the objects we send first by uSNChanged
1036  */
1037 static int site_res_cmp_usn_order(struct drsuapi_changed_objects *m1,
1038                                   struct drsuapi_changed_objects *m2,
1039                                   struct drsuapi_getncchanges_state *getnc_state)
1040 {
1041         int ret;
1042
1043         ret = ldb_dn_compare(getnc_state->ncRoot_dn, m1->dn);
1044         if (ret == 0) {
1045                 return -1;
1046         }
1047
1048         ret = ldb_dn_compare(getnc_state->ncRoot_dn, m2->dn);
1049         if (ret == 0) {
1050                 return 1;
1051         }
1052
1053         if (m1->usn == m2->usn) {
1054                 return ldb_dn_compare(m2->dn, m1->dn);
1055         }
1056
1057         if (m1->usn < m2->usn) {
1058                 return -1;
1059         }
1060
1061         return 1;
1062 }
1063
1064
1065 /*
1066   handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
1067  */
1068 static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
1069                                      TALLOC_CTX *mem_ctx,
1070                                      struct drsuapi_DsGetNCChangesRequest10 *req10,
1071                                      struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1072                                      struct ldb_dn **rid_manager_dn)
1073 {
1074         struct ldb_dn *req_dn, *ntds_dn = NULL;
1075         int ret;
1076         struct ldb_context *ldb = b_state->sam_ctx;
1077         struct ldb_result *ext_res;
1078         struct dsdb_fsmo_extended_op *exop;
1079         bool is_us;
1080
1081         /*
1082           steps:
1083             - verify that the DN being asked for is the RID Manager DN
1084             - verify that we are the RID Manager
1085          */
1086
1087         /* work out who is the RID Manager, also return to caller */
1088         ret = samdb_rid_manager_dn(ldb, mem_ctx, rid_manager_dn);
1089         if (ret != LDB_SUCCESS) {
1090                 DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
1091                 return WERR_DS_DRA_INTERNAL_ERROR;
1092         }
1093
1094         ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
1095                                                      ldb,
1096                                                      req10->naming_context,
1097                                                      &req_dn,
1098                                                      NULL);
1099         if (ret != LDB_SUCCESS) {
1100                 DBG_ERR("RID Alloc request for invalid DN %s: %s\n",
1101                         drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
1102                         ldb_strerror(ret));
1103                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1104                 return WERR_OK;
1105         }
1106
1107         if (ldb_dn_compare(req_dn, *rid_manager_dn) != 0) {
1108                 /* that isn't the RID Manager DN */
1109                 DBG_ERR("RID Alloc request for wrong DN %s\n",
1110                         drs_ObjectIdentifier_to_debug_string(mem_ctx,
1111                                                              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_message *ntds_msg = NULL;
1192         struct ldb_dn *ntds_dn = NULL, *server_dn = NULL;
1193         struct ldb_dn *rodc_dn, *krbtgt_link_dn;
1194         int ret;
1195         const char *ntds_attrs[] = { NULL };
1196         const char *rodc_attrs[] = { "msDS-KrbTgtLink",
1197                                      "msDS-NeverRevealGroup",
1198                                      "msDS-RevealOnDemandGroup",
1199                                      "userAccountControl",
1200                                      NULL };
1201         const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
1202         struct ldb_result *rodc_res = NULL, *obj_res = NULL;
1203         WERROR werr;
1204         struct GUID_txt_buf guid_buf;
1205
1206         DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n",
1207                  drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot)));
1208
1209         /*
1210          * we need to work out if we will allow this DC to
1211          * replicate the secrets for this object
1212          *
1213          * see 4.1.10.5.14 GetRevealSecretsPolicyForUser for details
1214          * of this function
1215          */
1216
1217         if (b_state->sam_ctx_system == NULL) {
1218                 /* this operation needs system level access */
1219                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_ACCESS_DENIED;
1220                 return WERR_DS_DRA_ACCESS_DENIED;
1221         }
1222
1223         /*
1224          * Before we accept or deny, fetch the machine DN for the destination
1225          * DSA GUID.
1226          *
1227          * If we are the RODC, we will check that this matches the SID.
1228          */
1229         ret = samdb_get_ntds_obj_by_guid(mem_ctx,
1230                                          b_state->sam_ctx_system,
1231                                          &req10->destination_dsa_guid,
1232                                          ntds_attrs,
1233                                          &ntds_msg);
1234         if (ret != LDB_SUCCESS) {
1235                 goto dest_dsa_error;
1236         }
1237
1238         ntds_dn = ntds_msg->dn;
1239
1240         server_dn = ldb_dn_get_parent(mem_ctx, ntds_dn);
1241         if (server_dn == NULL) {
1242                 goto failed;
1243         }
1244
1245         ret = samdb_reference_dn(b_state->sam_ctx_system, mem_ctx, server_dn,
1246                                  "serverReference", machine_dn);
1247
1248         if (ret != LDB_SUCCESS) {
1249                 goto dest_dsa_error;
1250         }
1251
1252         /*
1253          * In MS-DRSR.pdf 5.99 IsGetNCChangesPermissionGranted
1254          *
1255          * The pseudo code indicate
1256          * revealsecrets = true
1257          * if IsRevealSecretRequest(msgIn) then
1258          *   if AccessCheckCAR(ncRoot, Ds-Replication-Get-Changes-All) = false
1259          *   then
1260          *     if (msgIn.ulExtendedOp = EXOP_REPL_SECRETS) then
1261          *     <... check if this account is ok to be replicated on this DC ...>
1262          *     <... and if not reveal secrets = no ...>
1263          *     else
1264          *       reveal secrets = false
1265          *     endif
1266          *   endif
1267          * endif
1268          *
1269          * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC)
1270          * then you can do EXOP_REPL_SECRETS
1271          */
1272         ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
1273                                                         b_state->sam_ctx_system,
1274                                                         ncRoot,
1275                                                         &obj_dn,
1276                                                         NULL);
1277         if (ret != LDB_SUCCESS) {
1278                 DBG_ERR("RevealSecretRequest for invalid DN %s\n",
1279                          drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot));
1280                 goto failed;
1281         }
1282
1283         if (!ldb_dn_validate(obj_dn)) goto failed;
1284
1285         if (has_get_all_changes) {
1286                 goto allowed;
1287         }
1288
1289         rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "<SID=%s>",
1290                                  dom_sid_string(mem_ctx, user_sid));
1291         if (!ldb_dn_validate(rodc_dn)) goto failed;
1292
1293         /*
1294          * do the two searches we need
1295          * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID lists
1296          * out of the extended DNs
1297          */
1298         ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
1299                              DSDB_SEARCH_SHOW_EXTENDED_DN);
1300         if (ret != LDB_SUCCESS || rodc_res->count != 1) goto failed;
1301
1302         ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
1303         if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed;
1304
1305         /*
1306          * Must be an RODC account at this point, verify machine DN matches the
1307          * SID account
1308          */
1309         if (ldb_dn_compare(rodc_res->msgs[0]->dn, *machine_dn) != 0) {
1310                 goto denied;
1311         }
1312
1313         /* an RODC is allowed to get its own krbtgt account secrets */
1314         krbtgt_link_dn = samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
1315                                          rodc_res->msgs[0], "msDS-KrbTgtLink", NULL);
1316         if (krbtgt_link_dn != NULL &&
1317             ldb_dn_compare(obj_dn, krbtgt_link_dn) == 0) {
1318                 goto allowed;
1319         }
1320
1321         werr = samdb_confirm_rodc_allowed_to_repl_to(b_state->sam_ctx_system,
1322                                                      user_sid,
1323                                                      rodc_res->msgs[0],
1324                                                      obj_res->msgs[0]);
1325
1326         if (W_ERROR_IS_OK(werr)) {
1327                 goto allowed;
1328         }
1329
1330         /* default deny */
1331 denied:
1332         DEBUG(2,(__location__ ": Denied single object with secret replication for %s by RODC %s\n",
1333                  ldb_dn_get_linearized(obj_dn), ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
1334         ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1335         return WERR_DS_DRA_SECRETS_DENIED;
1336
1337 allowed:
1338         DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
1339                  ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC",
1340                  ldb_dn_get_linearized(*machine_dn)));
1341         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1342         req10->highwatermark.highest_usn = 0;
1343         return WERR_OK;
1344
1345 failed:
1346         DEBUG(2,(__location__ ": Failed single secret replication for %s by RODC %s\n",
1347                  ldb_dn_get_linearized(obj_dn), dom_sid_string(mem_ctx, user_sid)));
1348         ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1349         return WERR_DS_DRA_BAD_DN;
1350
1351 dest_dsa_error:
1352         DBG_WARNING("Failed secret replication for %s by RODC %s as dest_dsa_guid %s is invalid\n",
1353                     ldb_dn_get_linearized(obj_dn),
1354                     dom_sid_string(mem_ctx, user_sid),
1355                     GUID_buf_string(&req10->destination_dsa_guid,
1356                                     &guid_buf));
1357         ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
1358         return WERR_DS_DRA_DB_ERROR;
1359 }
1360
1361 /*
1362   handle a DRSUAPI_EXOP_REPL_OBJ call
1363  */
1364 static WERROR getncchanges_repl_obj(struct drsuapi_bind_state *b_state,
1365                                     TALLOC_CTX *mem_ctx,
1366                                     struct drsuapi_DsGetNCChangesRequest10 *req10,
1367                                     struct dom_sid *user_sid,
1368                                     struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1369 {
1370         struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
1371
1372         DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_OBJ extended op on %s\n",
1373                  drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot)));
1374
1375         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1376         return WERR_OK;
1377 }
1378
1379
1380 /*
1381   handle DRSUAPI_EXOP_FSMO_REQ_ROLE,
1382   DRSUAPI_EXOP_FSMO_RID_REQ_ROLE,
1383   and DRSUAPI_EXOP_FSMO_REQ_PDC calls
1384  */
1385 static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state,
1386                                          TALLOC_CTX *mem_ctx,
1387                                          struct drsuapi_DsGetNCChangesRequest10 *req10,
1388                                          struct drsuapi_DsGetNCChangesCtr6 *ctr6)
1389 {
1390         struct ldb_dn *req_dn, *ntds_dn;
1391         int ret;
1392         unsigned int i;
1393         struct ldb_context *ldb = b_state->sam_ctx;
1394         struct ldb_message *msg;
1395         bool is_us;
1396
1397         /*
1398           steps:
1399             - verify that the client dn exists
1400             - verify that we are the current master
1401          */
1402
1403         ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, ldb, req10->naming_context,
1404                                                      &req_dn, NULL);
1405         if (ret != LDB_SUCCESS) {
1406                 /* that is not a valid dn */
1407                 DBG_ERR("FSMO role transfer request for invalid DN %s: %s\n",
1408                         drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
1409                         ldb_strerror(ret));
1410                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1411                 return WERR_OK;
1412         }
1413
1414         /* find the DN of the current role owner */
1415         ret = samdb_reference_dn_is_our_ntdsa(ldb, req_dn, "fSMORoleOwner", &is_us);
1416         if (ret != LDB_SUCCESS) {
1417                 DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
1418                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1419                 return WERR_DS_DRA_INTERNAL_ERROR;
1420         }
1421
1422         if (!is_us) {
1423                 /* we're not the RID Manager or role owner - go away */
1424                 DEBUG(0,(__location__ ": FSMO role or RID manager transfer owner request when not role owner\n"));
1425                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
1426                 return WERR_OK;
1427         }
1428
1429         /* change the current master */
1430         msg = ldb_msg_new(ldb);
1431         W_ERROR_HAVE_NO_MEMORY(msg);
1432         ret = drs_ObjectIdentifier_to_dn_and_nc_root(msg, ldb, req10->naming_context,
1433                                                      &msg->dn, NULL);
1434         if (ret != LDB_SUCCESS) {
1435                 /* that is not a valid dn */
1436                 DBG_ERR("FSMO role transfer request for invalid DN %s: %s\n",
1437                         drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
1438                         ldb_strerror(ret));
1439                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
1440                 return WERR_OK;
1441         }
1442
1443         /* TODO: make sure ntds_dn is a valid nTDSDSA object */
1444         ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, 0, &ntds_dn);
1445         if (ret != LDB_SUCCESS) {
1446                 DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
1447                           GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
1448                 talloc_free(msg);
1449                 ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
1450                 return WERR_OK;
1451         }
1452
1453         ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn));
1454         if (ret != 0) {
1455                 talloc_free(msg);
1456                 return WERR_DS_DRA_INTERNAL_ERROR;
1457         }
1458
1459         for (i=0;i<msg->num_elements;i++) {
1460                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1461         }
1462
1463         ret = ldb_transaction_start(ldb);
1464         if (ret != LDB_SUCCESS) {
1465                 DEBUG(0,(__location__ ": Failed transaction start - %s\n",
1466                          ldb_errstring(ldb)));
1467                 return WERR_DS_DRA_INTERNAL_ERROR;
1468         }
1469
1470         ret = ldb_modify(ldb, msg);
1471         if (ret != LDB_SUCCESS) {
1472                 DEBUG(0,(__location__ ": Failed to change current owner - %s\n",
1473                          ldb_errstring(ldb)));
1474                 ldb_transaction_cancel(ldb);
1475                 return WERR_DS_DRA_INTERNAL_ERROR;
1476         }
1477
1478         ret = ldb_transaction_commit(ldb);
1479         if (ret != LDB_SUCCESS) {
1480                 DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
1481                          ldb_errstring(ldb)));
1482                 return WERR_DS_DRA_INTERNAL_ERROR;
1483         }
1484
1485         ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
1486
1487         return WERR_OK;
1488 }
1489
1490 /*
1491   see if this getncchanges request includes a request to reveal secret information
1492  */
1493 static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state *b_state,
1494                                                        struct drsuapi_DsGetNCChangesRequest10 *req10,
1495                                                        struct dsdb_schema_prefixmap *pfm_remote,
1496                                                        bool *is_secret_request)
1497 {
1498         enum drsuapi_DsExtendedOperation exop;
1499         uint32_t i;
1500         struct dsdb_schema *schema;
1501         struct dsdb_syntax_ctx syntax_ctx;
1502
1503         *is_secret_request = true;
1504
1505         exop = req10->extended_op;
1506
1507         switch (exop) {
1508         case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1509         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1510         case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1511         case DRSUAPI_EXOP_FSMO_REQ_PDC:
1512         case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1513                 /* FSMO exops can reveal secrets */
1514                 *is_secret_request = true;
1515                 return WERR_OK;
1516         case DRSUAPI_EXOP_REPL_SECRET:
1517         case DRSUAPI_EXOP_REPL_OBJ:
1518         case DRSUAPI_EXOP_NONE:
1519                 break;
1520         }
1521
1522         if (req10->replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
1523                 *is_secret_request = false;
1524                 return WERR_OK;
1525         }
1526
1527         if (exop == DRSUAPI_EXOP_REPL_SECRET ||
1528             req10->partial_attribute_set == NULL) {
1529                 /* they want secrets */
1530                 *is_secret_request = true;
1531                 return WERR_OK;
1532         }
1533
1534         schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1535         dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1536         syntax_ctx.pfm_remote = pfm_remote;
1537
1538         /* check the attributes they asked for */
1539         for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1540                 const struct dsdb_attribute *sa;
1541                 WERROR werr = getncchanges_attid_remote_to_local(schema,
1542                                                                  &syntax_ctx,
1543                                                                  req10->partial_attribute_set->attids[i],
1544                                                                  NULL,
1545                                                                  &sa);
1546
1547                 if (!W_ERROR_IS_OK(werr)) {
1548                         DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1549                                  req10->partial_attribute_set->attids[i], win_errstr(werr)));
1550                         return werr;
1551                 }
1552
1553                 if (!dsdb_attr_in_rodc_fas(sa)) {
1554                         *is_secret_request = true;
1555                         return WERR_OK;
1556                 }
1557         }
1558
1559         if (req10->partial_attribute_set_ex) {
1560                 /* check the extended attributes they asked for */
1561                 for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1562                         const struct dsdb_attribute *sa;
1563                         WERROR werr = getncchanges_attid_remote_to_local(schema,
1564                                                                          &syntax_ctx,
1565                                                                          req10->partial_attribute_set_ex->attids[i],
1566                                                                          NULL,
1567                                                                          &sa);
1568
1569                         if (!W_ERROR_IS_OK(werr)) {
1570                                 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1571                                          req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1572                                 return werr;
1573                         }
1574
1575                         if (!dsdb_attr_in_rodc_fas(sa)) {
1576                                 *is_secret_request = true;
1577                                 return WERR_OK;
1578                         }
1579                 }
1580         }
1581
1582         *is_secret_request = false;
1583         return WERR_OK;
1584 }
1585
1586 /*
1587   see if this getncchanges request is only for attributes in the GC
1588   partial attribute set
1589  */
1590 static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state,
1591                                                struct drsuapi_DsGetNCChangesRequest10 *req10,
1592                                                struct dsdb_schema_prefixmap *pfm_remote,
1593                                                bool *is_gc_pas_request)
1594 {
1595         enum drsuapi_DsExtendedOperation exop;
1596         uint32_t i;
1597         struct dsdb_schema *schema;
1598         struct dsdb_syntax_ctx syntax_ctx;
1599
1600         exop = req10->extended_op;
1601
1602         switch (exop) {
1603         case DRSUAPI_EXOP_FSMO_REQ_ROLE:
1604         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1605         case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
1606         case DRSUAPI_EXOP_FSMO_REQ_PDC:
1607         case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
1608         case DRSUAPI_EXOP_REPL_SECRET:
1609                 *is_gc_pas_request = false;
1610                 return WERR_OK;
1611         case DRSUAPI_EXOP_REPL_OBJ:
1612         case DRSUAPI_EXOP_NONE:
1613                 break;
1614         }
1615
1616         if (req10->partial_attribute_set == NULL) {
1617                 /* they want it all */
1618                 *is_gc_pas_request = false;
1619                 return WERR_OK;
1620         }
1621
1622         schema = dsdb_get_schema(b_state->sam_ctx, NULL);
1623         dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
1624         syntax_ctx.pfm_remote = pfm_remote;
1625
1626         /* check the attributes they asked for */
1627         for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
1628                 const struct dsdb_attribute *sa;
1629                 WERROR werr = getncchanges_attid_remote_to_local(schema,
1630                                                                  &syntax_ctx,
1631                                                                  req10->partial_attribute_set->attids[i],
1632                                                                  NULL,
1633                                                                  &sa);
1634
1635                 if (!W_ERROR_IS_OK(werr)) {
1636                         DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1637                                  req10->partial_attribute_set->attids[i], win_errstr(werr)));
1638                         return werr;
1639                 }
1640
1641                 if (!sa->isMemberOfPartialAttributeSet) {
1642                         *is_gc_pas_request = false;
1643                         return WERR_OK;
1644                 }
1645         }
1646
1647         if (req10->partial_attribute_set_ex) {
1648                 /* check the extended attributes they asked for */
1649                 for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
1650                         const struct dsdb_attribute *sa;
1651                         WERROR werr = getncchanges_attid_remote_to_local(schema,
1652                                                                          &syntax_ctx,
1653                                                                          req10->partial_attribute_set_ex->attids[i],
1654                                                                          NULL,
1655                                                                          &sa);
1656
1657                         if (!W_ERROR_IS_OK(werr)) {
1658                                 DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
1659                                          req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
1660                                 return werr;
1661                         }
1662
1663                         if (!sa->isMemberOfPartialAttributeSet) {
1664                                 *is_gc_pas_request = false;
1665                                 return WERR_OK;
1666                         }
1667                 }
1668         }
1669
1670         *is_gc_pas_request = true;
1671         return WERR_OK;
1672 }
1673
1674
1675 /*
1676   map from req8 to req10
1677  */
1678 static struct drsuapi_DsGetNCChangesRequest10 *
1679 getncchanges_map_req8(TALLOC_CTX *mem_ctx,
1680                       struct drsuapi_DsGetNCChangesRequest8 *req8)
1681 {
1682         struct drsuapi_DsGetNCChangesRequest10 *req10 = talloc_zero(mem_ctx,
1683                                                                     struct drsuapi_DsGetNCChangesRequest10);
1684         if (req10 == NULL) {
1685                 return NULL;
1686         }
1687
1688         req10->destination_dsa_guid = req8->destination_dsa_guid;
1689         req10->source_dsa_invocation_id = req8->source_dsa_invocation_id;
1690         req10->naming_context = req8->naming_context;
1691         req10->highwatermark = req8->highwatermark;
1692         req10->uptodateness_vector = req8->uptodateness_vector;
1693         req10->replica_flags = req8->replica_flags;
1694         req10->max_object_count = req8->max_object_count;
1695         req10->max_ndr_size = req8->max_ndr_size;
1696         req10->extended_op = req8->extended_op;
1697         req10->fsmo_info = req8->fsmo_info;
1698         req10->partial_attribute_set = req8->partial_attribute_set;
1699         req10->partial_attribute_set_ex = req8->partial_attribute_set_ex;
1700         req10->mapping_ctr = req8->mapping_ctr;
1701
1702         return req10;
1703 }
1704
1705 static const char *collect_objects_attrs[] = { "uSNChanged",
1706                                                "objectGUID" ,
1707                                                NULL };
1708
1709 /**
1710  * Collects object for normal replication cycle.
1711  */
1712 static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
1713                                            TALLOC_CTX *mem_ctx,
1714                                            struct drsuapi_getncchanges_state *getnc_state,
1715                                            struct drsuapi_DsGetNCChangesRequest10 *req10,
1716                                            struct ldb_dn *search_dn,
1717                                            const char *extra_filter,
1718                                            struct ldb_result **search_res)
1719 {
1720         int ret;
1721         char* search_filter;
1722         enum ldb_scope scope = LDB_SCOPE_SUBTREE;
1723         bool critical_only = false;
1724
1725         if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
1726                 critical_only = true;
1727         }
1728
1729         if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ ||
1730             req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
1731                 scope = LDB_SCOPE_BASE;
1732                 critical_only = false;
1733         }
1734
1735         /* Construct response. */
1736         search_filter = talloc_asprintf(mem_ctx,
1737                                         "(uSNChanged>=%llu)",
1738                                         (unsigned long long)(getnc_state->min_usn+1));
1739
1740         if (extra_filter) {
1741                 search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
1742         }
1743
1744         if (critical_only) {
1745                 search_filter = talloc_asprintf(mem_ctx,
1746                                                 "(&%s(isCriticalSystemObject=TRUE))",
1747                                                 search_filter);
1748         }
1749
1750         if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
1751                 scope = LDB_SCOPE_BASE;
1752         }
1753
1754         if (!search_dn) {
1755                 search_dn = getnc_state->ncRoot_dn;
1756         }
1757
1758         DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n",
1759                  ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
1760         ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, search_res,
1761                                               search_dn, scope,
1762                                               collect_objects_attrs,
1763                                               search_filter);
1764         if (ret != LDB_SUCCESS) {
1765                 return WERR_DS_DRA_INTERNAL_ERROR;
1766         }
1767
1768         return WERR_OK;
1769 }
1770
1771 /**
1772  * Collects object for normal replication cycle.
1773  */
1774 static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state,
1775                                                 TALLOC_CTX *mem_ctx,
1776                                                 struct drsuapi_getncchanges_state *getnc_state,
1777                                                 struct drsuapi_DsGetNCChangesRequest10 *req10,
1778                                                 struct drsuapi_DsGetNCChangesCtr6 *ctr6,
1779                                                 struct ldb_dn *search_dn,
1780                                                 const char *extra_filter,
1781                                                 struct ldb_result **search_res)
1782 {
1783         /* we have nothing to do in case of ex-op failure */
1784         if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
1785                 return WERR_OK;
1786         }
1787
1788         switch (req10->extended_op) {
1789         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
1790         {
1791                 int ret;
1792                 struct ldb_dn *ntds_dn = NULL;
1793                 struct ldb_dn *server_dn = NULL;
1794                 struct ldb_dn *machine_dn = NULL;
1795                 struct ldb_dn *rid_set_dn = NULL;
1796                 struct ldb_result *search_res2 = NULL;
1797                 struct ldb_result *search_res3 = NULL;
1798                 TALLOC_CTX *frame = talloc_stackframe();
1799                 /* get RID manager, RID set and server DN (in that order) */
1800
1801                 /* This first search will get the RID Manager */
1802                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1803                                                       search_res,
1804                                                       search_dn, LDB_SCOPE_BASE,
1805                                                       collect_objects_attrs,
1806                                                       NULL);
1807                 if (ret != LDB_SUCCESS) {
1808                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %s\n",
1809                                   ldb_dn_get_linearized(search_dn),
1810                                   ldb_errstring(b_state->sam_ctx)));
1811                         TALLOC_FREE(frame);
1812                         return WERR_DS_DRA_INTERNAL_ERROR;
1813                 }
1814
1815                 if ((*search_res)->count != 1) {
1816                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %u objects returned\n",
1817                                   ldb_dn_get_linearized(search_dn),
1818                                   (*search_res)->count));
1819                         TALLOC_FREE(frame);
1820                         return WERR_DS_DRA_INTERNAL_ERROR;
1821                 }
1822
1823                 /* Now extend it to the RID set */
1824
1825                 /* Find the computer account DN for the destination
1826                  * dsa GUID specified */
1827
1828                 ret = dsdb_find_dn_by_guid(b_state->sam_ctx, frame,
1829                                            &req10->destination_dsa_guid, 0,
1830                                            &ntds_dn);
1831                 if (ret != LDB_SUCCESS) {
1832                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Unable to find NTDS object for guid %s - %s\n",
1833                                   GUID_string(frame,
1834                                               &req10->destination_dsa_guid),
1835                                   ldb_errstring(b_state->sam_ctx)));
1836                         TALLOC_FREE(frame);
1837                         return WERR_DS_DRA_INTERNAL_ERROR;
1838                 }
1839
1840                 server_dn = ldb_dn_get_parent(frame, ntds_dn);
1841                 if (!server_dn) {
1842                         TALLOC_FREE(frame);
1843                         return WERR_DS_DRA_INTERNAL_ERROR;
1844                 }
1845
1846                 ret = samdb_reference_dn(b_state->sam_ctx, frame, server_dn,
1847                                          "serverReference", &machine_dn);
1848                 if (ret != LDB_SUCCESS) {
1849                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find serverReference in %s - %s\n",
1850                                   ldb_dn_get_linearized(server_dn),
1851                                   ldb_errstring(b_state->sam_ctx)));
1852                         TALLOC_FREE(frame);
1853                         return WERR_DS_DRA_INTERNAL_ERROR;
1854                 }
1855
1856                 ret = samdb_reference_dn(b_state->sam_ctx, frame, machine_dn,
1857                                          "rIDSetReferences", &rid_set_dn);
1858                 if (ret != LDB_SUCCESS) {
1859                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find rIDSetReferences in %s - %s\n",
1860                                   ldb_dn_get_linearized(server_dn),
1861                                   ldb_errstring(b_state->sam_ctx)));
1862                         TALLOC_FREE(frame);
1863                         return WERR_DS_DRA_INTERNAL_ERROR;
1864                 }
1865
1866
1867                 /* This first search will get the RID Manager, now get the RID set */
1868                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1869                                                       &search_res2,
1870                                                       rid_set_dn, LDB_SCOPE_BASE,
1871                                                       collect_objects_attrs,
1872                                                       NULL);
1873                 if (ret != LDB_SUCCESS) {
1874                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %s\n",
1875                                   ldb_dn_get_linearized(rid_set_dn),
1876                                   ldb_errstring(b_state->sam_ctx)));
1877                         TALLOC_FREE(frame);
1878                         return WERR_DS_DRA_INTERNAL_ERROR;
1879                 }
1880
1881                 if (search_res2->count != 1) {
1882                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %u objects returned\n",
1883                                   ldb_dn_get_linearized(rid_set_dn),
1884                                   search_res2->count));
1885                         TALLOC_FREE(frame);
1886                         return WERR_DS_DRA_INTERNAL_ERROR;
1887                 }
1888
1889                 /* Finally get the server DN */
1890                 ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
1891                                                       &search_res3,
1892                                                       machine_dn, LDB_SCOPE_BASE,
1893                                                       collect_objects_attrs,
1894                                                       NULL);
1895                 if (ret != LDB_SUCCESS) {
1896                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %s\n",
1897                                   ldb_dn_get_linearized(server_dn),
1898                                   ldb_errstring(b_state->sam_ctx)));
1899                         TALLOC_FREE(frame);
1900                         return WERR_DS_DRA_INTERNAL_ERROR;
1901                 }
1902
1903                 if (search_res3->count != 1) {
1904                         DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %u objects returned\n",
1905                                   ldb_dn_get_linearized(server_dn),
1906                                   search_res3->count));
1907                         TALLOC_FREE(frame);
1908                         return WERR_DS_DRA_INTERNAL_ERROR;
1909                 }
1910
1911                 /* Now extend the original search_res with these answers */
1912                 (*search_res)->count = 3;
1913
1914                 (*search_res)->msgs = talloc_realloc(frame, (*search_res)->msgs,
1915                                                      struct ldb_message *,
1916                                                      (*search_res)->count);
1917                 if ((*search_res)->msgs == NULL) {
1918                         TALLOC_FREE(frame);
1919                         return WERR_NOT_ENOUGH_MEMORY;
1920                 }
1921
1922
1923                 talloc_steal(mem_ctx, *search_res);
1924                 (*search_res)->msgs[1] =
1925                         talloc_steal((*search_res)->msgs, search_res2->msgs[0]);
1926                 (*search_res)->msgs[2] =
1927                         talloc_steal((*search_res)->msgs, search_res3->msgs[0]);
1928
1929                 TALLOC_FREE(frame);
1930                 return WERR_OK;
1931         }
1932         default:
1933                 /* TODO: implement extended op specific collection
1934                  * of objects. Right now we just normal procedure
1935                  * for collecting objects */
1936                 return getncchanges_collect_objects(b_state,
1937                                                     mem_ctx,
1938                                                     getnc_state,
1939                                                     req10,
1940                                                     search_dn,
1941                                                     extra_filter,
1942                                                     search_res);
1943         }
1944 }
1945
1946 static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
1947                                                 uint64_t max_usn,
1948                                                 struct drsuapi_DsReplicaHighWaterMark *hwm)
1949 {
1950         uint64_t uSN = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
1951
1952         if (uSN > max_usn) {
1953                 /*
1954                  * Only report the max_usn we had at the start
1955                  * of the replication cycle.
1956                  *
1957                  * If this object has changed lately we better
1958                  * let the destination dsa refetch the change.
1959                  * This is better than the risk of loosing some
1960                  * objects or linked attributes.
1961                  */
1962                 return;
1963         }
1964
1965         if (uSN <= hwm->tmp_highest_usn) {
1966                 return;
1967         }
1968
1969         hwm->tmp_highest_usn = uSN;
1970         hwm->reserved_usn = 0;
1971 }
1972
1973 /**
1974  * Adds an object's GUID to the cache of objects already sent.
1975  * This avoids us sending the same object multiple times when
1976  * the GetNCChanges request uses a flag like GET_ANC.
1977  */
1978 static WERROR dcesrv_drsuapi_obj_cache_add(struct db_context *obj_cache,
1979                                            const struct GUID *guid)
1980 {
1981         enum ndr_err_code ndr_err;
1982         uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
1983         DATA_BLOB b = {
1984                 .data = guid_buf,
1985                 .length = sizeof(guid_buf),
1986         };
1987         TDB_DATA key = {
1988                 .dptr = b.data,
1989                 .dsize = b.length,
1990         };
1991         TDB_DATA val = {
1992                 .dptr = NULL,
1993                 .dsize = 0,
1994         };
1995         NTSTATUS status;
1996
1997         ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
1998                         (ndr_push_flags_fn_t)ndr_push_GUID);
1999         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2000                 return WERR_DS_DRA_INTERNAL_ERROR;
2001         }
2002
2003         status = dbwrap_store(obj_cache, key, val, TDB_REPLACE);
2004         if (!NT_STATUS_IS_OK(status)) {
2005                 return WERR_DS_DRA_INTERNAL_ERROR;
2006         }
2007
2008         return WERR_OK;
2009 }
2010
2011 /**
2012  * Checks if the object with the GUID specified already exists in the
2013  * object cache, i.e. it's already been sent in a GetNCChanges response.
2014  */
2015 static WERROR dcesrv_drsuapi_obj_cache_exists(struct db_context *obj_cache,
2016                                               const struct GUID *guid)
2017 {
2018         enum ndr_err_code ndr_err;
2019         uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
2020         DATA_BLOB b = {
2021                 .data = guid_buf,
2022                 .length = sizeof(guid_buf),
2023         };
2024         TDB_DATA key = {
2025                 .dptr = b.data,
2026                 .dsize = b.length,
2027         };
2028         bool exists;
2029
2030         ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
2031                         (ndr_push_flags_fn_t)ndr_push_GUID);
2032         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2033                 return WERR_DS_DRA_INTERNAL_ERROR;
2034         }
2035
2036         exists = dbwrap_exists(obj_cache, key);
2037         if (!exists) {
2038                 return WERR_OBJECT_NOT_FOUND;
2039         }
2040
2041         return WERR_OBJECT_NAME_EXISTS;
2042 }
2043
2044 /**
2045  * Copies the la_list specified into a sorted array, ready to be sent in a
2046  * GetNCChanges response.
2047  */
2048 static WERROR getncchanges_get_sorted_array(const struct drsuapi_DsReplicaLinkedAttribute *la_list,
2049                                             const uint32_t link_count,
2050                                             struct ldb_context *sam_ctx,
2051                                             TALLOC_CTX *mem_ctx,
2052                                             const struct dsdb_schema *schema,
2053                                             struct la_for_sorting **ret_array)
2054 {
2055         int j;
2056         struct la_for_sorting *guid_array;
2057         WERROR werr = WERR_OK;
2058
2059         *ret_array = NULL;
2060         guid_array = talloc_array(mem_ctx, struct la_for_sorting, link_count);
2061         if (guid_array == NULL) {
2062                 DEBUG(0, ("Out of memory allocating %u linked attributes for sorting\n", link_count));
2063                 return WERR_NOT_ENOUGH_MEMORY;
2064         }
2065
2066         for (j = 0; j < link_count; j++) {
2067
2068                 /* we need to get the target GUIDs to compare */
2069                 struct dsdb_dn *dn;
2070                 const struct drsuapi_DsReplicaLinkedAttribute *la = &la_list[j];
2071                 const struct dsdb_attribute *schema_attrib;
2072                 const struct ldb_val *target_guid;
2073                 DATA_BLOB source_guid;
2074                 TALLOC_CTX *frame = talloc_stackframe();
2075                 NTSTATUS status;
2076
2077                 schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid);
2078
2079                 werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn);
2080                 if (!W_ERROR_IS_OK(werr)) {
2081                         DEBUG(0,(__location__ ": Bad la blob in sort\n"));
2082                         TALLOC_FREE(frame);
2083                         return werr;
2084                 }
2085
2086                 /* Extract the target GUID in NDR form */
2087                 target_guid = ldb_dn_get_extended_component(dn->dn, "GUID");
2088                 if (target_guid == NULL
2089                                 || target_guid->length != sizeof(guid_array[0].target_guid)) {
2090                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2091                 } else {
2092                         /* Repack the source GUID as NDR for sorting */
2093                         status = GUID_to_ndr_blob(&la->identifier->guid,
2094                                                   frame,
2095                                                   &source_guid);
2096                 }
2097
2098                 if (!NT_STATUS_IS_OK(status)
2099                                 || source_guid.length != sizeof(guid_array[0].source_guid)) {
2100                         DEBUG(0,(__location__ ": Bad la guid in sort\n"));
2101                         TALLOC_FREE(frame);
2102                         return ntstatus_to_werror(status);
2103                 }
2104
2105                 guid_array[j].link = &la_list[j];
2106                 memcpy(guid_array[j].target_guid, target_guid->data,
2107                        sizeof(guid_array[j].target_guid));
2108                 memcpy(guid_array[j].source_guid, source_guid.data,
2109                        sizeof(guid_array[j].source_guid));
2110                 TALLOC_FREE(frame);
2111         }
2112
2113         TYPESAFE_QSORT(guid_array, link_count, linked_attribute_compare);
2114
2115         *ret_array = guid_array;
2116
2117         return werr;
2118 }
2119
2120
2121 /**
2122  * Adds any ancestor/parent objects of the child_obj specified.
2123  * This is needed when the GET_ANC flag is specified in the request.
2124  * @param new_objs if parents are added, this gets updated to point to a chain
2125  * of parent objects (with the parents first and the child last)
2126  */
2127 static WERROR getncchanges_add_ancestors(struct drsuapi_DsReplicaObjectListItemEx *child_obj,
2128                                          struct ldb_dn *child_dn,
2129                                          TALLOC_CTX *mem_ctx,
2130                                          struct ldb_context *sam_ctx,
2131                                          struct drsuapi_getncchanges_state *getnc_state,
2132                                          struct dsdb_schema *schema,
2133                                          DATA_BLOB *session_key,
2134                                          struct drsuapi_DsGetNCChangesRequest10 *req10,
2135                                          uint32_t *local_pas,
2136                                          struct ldb_dn *machine_dn,
2137                                          struct drsuapi_DsReplicaObjectListItemEx **new_objs)
2138 {
2139         int ret;
2140         const struct GUID *next_anc_guid = NULL;
2141         WERROR werr = WERR_OK;
2142         static const char * const msg_attrs[] = {
2143                                             "*",
2144                                             "nTSecurityDescriptor",
2145                                             "parentGUID",
2146                                             "replPropertyMetaData",
2147                                             DSDB_SECRET_ATTRIBUTES,
2148                                             NULL };
2149
2150         next_anc_guid = child_obj->parent_object_guid;
2151
2152         while (next_anc_guid != NULL) {
2153                 struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
2154                 struct ldb_message *anc_msg = NULL;
2155                 struct ldb_result *anc_res = NULL;
2156                 struct ldb_dn *anc_dn = NULL;
2157
2158                 /*
2159                  * Don't send an object twice. (If we've sent the object, then
2160                  * we've also sent all its parents as well)
2161                  */
2162                 werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
2163                                                        next_anc_guid);
2164                 if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2165                         return WERR_OK;
2166                 }
2167                 if (W_ERROR_IS_OK(werr)) {
2168                         return WERR_INTERNAL_ERROR;
2169                 }
2170                 if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
2171                         return werr;
2172                 }
2173
2174                 anc_obj = talloc_zero(mem_ctx,
2175                                       struct drsuapi_DsReplicaObjectListItemEx);
2176                 if (anc_obj == NULL) {
2177                         return WERR_NOT_ENOUGH_MEMORY;
2178                 }
2179
2180                 anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
2181                                         GUID_string(anc_obj, next_anc_guid));
2182                 if (anc_dn == NULL) {
2183                         return WERR_NOT_ENOUGH_MEMORY;
2184                 }
2185
2186                 ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
2187                                                       &anc_res, anc_dn,
2188                                                       LDB_SCOPE_BASE,
2189                                                       msg_attrs, NULL);
2190                 if (ret != LDB_SUCCESS) {
2191                         const char *anc_str = NULL;
2192                         const char *obj_str = NULL;
2193
2194                         anc_str = ldb_dn_get_extended_linearized(anc_obj,
2195                                                                  anc_dn,
2196                                                                  1);
2197                         obj_str = ldb_dn_get_extended_linearized(anc_obj,
2198                                                                  child_dn,
2199                                                                  1);
2200
2201                         DBG_ERR("getncchanges: failed to fetch ANC "
2202                                 "DN %s for DN %s - %s\n",
2203                                 anc_str, obj_str, ldb_errstring(sam_ctx));
2204                         return WERR_DS_DRA_INCONSISTENT_DIT;
2205                 }
2206
2207                 anc_msg = anc_res->msgs[0];
2208
2209                 werr = get_nc_changes_build_object(anc_obj, anc_msg,
2210                                                    sam_ctx,
2211                                                    getnc_state,
2212                                                    schema, session_key,
2213                                                    req10,
2214                                                    false, /* force_object_return */
2215                                                    local_pas,
2216                                                    machine_dn,
2217                                                    next_anc_guid);
2218                 if (!W_ERROR_IS_OK(werr)) {
2219                         return werr;
2220                 }
2221
2222                 /*
2223                  * Regardless of whether we actually use it or not,
2224                  * we add it to the cache so we don't look at it again
2225                  */
2226                 werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
2227                                                     next_anc_guid);
2228                 if (!W_ERROR_IS_OK(werr)) {
2229                         return werr;
2230                 }
2231
2232                 /*
2233                  * Any ancestors which are below the highwatermark
2234                  * or uptodateness_vector shouldn't be added,
2235                  * but we still look further up the
2236                  * tree for ones which have been changed recently.
2237                  */
2238                 if (anc_obj->meta_data_ctr != NULL) {
2239
2240                         /*
2241                          * prepend the parent to the list so that the client-side
2242                          * adds the parent object before it adds the children
2243                          */
2244                         anc_obj->next_object = *new_objs;
2245                         *new_objs = anc_obj;
2246                 }
2247
2248                 anc_msg = NULL;
2249                 TALLOC_FREE(anc_res);
2250                 TALLOC_FREE(anc_dn);
2251
2252                 /*
2253                  * We may need to resolve more parents...
2254                  */
2255                 next_anc_guid = anc_obj->parent_object_guid;
2256         }
2257         return werr;
2258 }
2259
2260 /**
2261  * Adds a list of new objects into the current chunk of replication data to send
2262  */
2263 static void getncchanges_chunk_add_objects(struct getncchanges_repl_chunk *repl_chunk,
2264                                            struct drsuapi_DsReplicaObjectListItemEx *obj_list)
2265 {
2266         struct drsuapi_DsReplicaObjectListItemEx *obj;
2267
2268         /*
2269          * We track the last object added to the replication chunk, so just add
2270          * the new object-list onto the end
2271          */
2272         if (repl_chunk->object_list == NULL) {
2273                 repl_chunk->object_list = obj_list;
2274         } else {
2275                 repl_chunk->last_object->next_object = obj_list;
2276         }
2277
2278         for (obj = obj_list; obj != NULL; obj = obj->next_object) {
2279                 repl_chunk->object_count += 1;
2280
2281                 /*
2282                  * Remember the last object in the response - we'll use this to
2283                  * link the next object(s) processed onto the existing list
2284                  */
2285                 if (obj->next_object == NULL) {
2286                         repl_chunk->last_object = obj;
2287                 }
2288         }
2289 }
2290
2291 /**
2292  * Gets the object to send, packed into an RPC struct ready to send. This also
2293  * adds the object to the object cache, and adds any ancestors (if needed).
2294  * @param msg - DB search result for the object to add
2295  * @param guid - GUID of the object to add
2296  * @param ret_obj_list - returns the object ready to be sent (in a list, along
2297  * with any ancestors that might be needed). NULL if nothing to send.
2298  */
2299 static WERROR getncchanges_get_obj_to_send(const struct ldb_message *msg,
2300                                            TALLOC_CTX *mem_ctx,
2301                                            struct ldb_context *sam_ctx,
2302                                            struct drsuapi_getncchanges_state *getnc_state,
2303                                            struct dsdb_schema *schema,
2304                                            DATA_BLOB *session_key,
2305                                            struct drsuapi_DsGetNCChangesRequest10 *req10,
2306                                            bool force_object_return,
2307                                            uint32_t *local_pas,
2308                                            struct ldb_dn *machine_dn,
2309                                            const struct GUID *guid,
2310                                            struct drsuapi_DsReplicaObjectListItemEx **ret_obj_list)
2311 {
2312         struct drsuapi_DsReplicaObjectListItemEx *obj;
2313         WERROR werr;
2314
2315         *ret_obj_list = NULL;
2316
2317         obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
2318         W_ERROR_HAVE_NO_MEMORY(obj);
2319
2320         werr = get_nc_changes_build_object(obj, msg, sam_ctx, getnc_state,
2321                                            schema, session_key, req10,
2322                                            force_object_return,
2323                                            local_pas, machine_dn, guid);
2324         if (!W_ERROR_IS_OK(werr)) {
2325                 return werr;
2326         }
2327
2328         /*
2329          * The object may get filtered out by the UTDV's USN and not actually
2330          * sent, in which case there's nothing more to do here
2331          */
2332         if (obj->meta_data_ctr == NULL) {
2333                 TALLOC_FREE(obj);
2334                 return WERR_OK;
2335         }
2336
2337         if (getnc_state->obj_cache != NULL) {
2338                 werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
2339                                                     guid);
2340                 if (!W_ERROR_IS_OK(werr)) {
2341                         return werr;
2342                 }
2343         }
2344
2345         *ret_obj_list = obj;
2346
2347         /*
2348          * If required, also add any ancestors that the client may need to know
2349          * about before it can resolve this object. These get prepended to the
2350          * ret_obj_list so the client adds them first.
2351          *
2352          * We allow this to be disabled to permit testing of a
2353          * client-side fallback for the broken behaviour in Samba 4.5
2354          * and earlier.
2355          */
2356         if (getnc_state->is_get_anc
2357             && !getnc_state->broken_samba_4_5_get_anc_emulation) {
2358                 werr = getncchanges_add_ancestors(obj, msg->dn, mem_ctx,
2359                                                   sam_ctx, getnc_state,
2360                                                   schema, session_key,
2361                                                   req10, local_pas,
2362                                                   machine_dn, ret_obj_list);
2363         }
2364
2365         return werr;
2366 }
2367
2368 /**
2369  * Returns the number of links that are waiting to be sent
2370  */
2371 static uint32_t getncchanges_chunk_links_pending(struct getncchanges_repl_chunk *repl_chunk,
2372                                                  struct drsuapi_getncchanges_state *getnc_state)
2373 {
2374         uint32_t links_to_send = 0;
2375
2376         if (getnc_state->is_get_tgt) {
2377
2378                 /*
2379                  * when the GET_TGT flag is set, only include the linked
2380                  * attributes whose target object has already been checked
2381                  * (i.e. they're ready to send).
2382                  */
2383                 if (repl_chunk->tgt_la_count > getnc_state->la_idx) {
2384                         links_to_send = (repl_chunk->tgt_la_count -
2385                                          getnc_state->la_idx);
2386                 }
2387         } else {
2388                 links_to_send = getnc_state->la_count - getnc_state->la_idx;
2389         }
2390
2391         return links_to_send;
2392 }
2393
2394 /**
2395  * Returns the max number of links that will fit in the current replication chunk
2396  */
2397 static uint32_t getncchanges_chunk_max_links(struct getncchanges_repl_chunk *repl_chunk)
2398 {
2399         uint32_t max_links = 0;
2400
2401         if (repl_chunk->max_links != DEFAULT_MAX_LINKS ||
2402             repl_chunk->max_objects != DEFAULT_MAX_OBJECTS) {
2403
2404                 /*
2405                  * We're using non-default settings, so don't try to adjust
2406                  * them, just trust the user has configured decent values
2407                  */
2408                 max_links = repl_chunk->max_links;
2409
2410         } else if (repl_chunk->max_links > repl_chunk->object_count) {
2411
2412                 /*
2413                  * This is just an approximate guess to avoid overfilling the
2414                  * replication chunk. It's the logic we've used historically.
2415                  * E.g. if we've already sent 1000 objects, then send 1000 fewer
2416                  * links. For comparison, the max that Windows seems to send is
2417                  * ~2700 links and ~250 objects (although this may vary based
2418                  * on timeouts)
2419                  */
2420                 max_links = repl_chunk->max_links - repl_chunk->object_count;
2421         }
2422
2423         return max_links;
2424 }
2425
2426 /**
2427  * Returns true if the current GetNCChanges() call has taken longer than its
2428  * allotted time. This prevents the client from timing out.
2429  */
2430 static bool getncchanges_chunk_timed_out(struct getncchanges_repl_chunk *repl_chunk)
2431 {
2432         return (time(NULL) - repl_chunk->start > repl_chunk->max_wait);
2433 }
2434
2435 /**
2436  * Returns true if the current chunk of replication data has reached the
2437  * max_objects and/or max_links thresholds.
2438  */
2439 static bool getncchanges_chunk_is_full(struct getncchanges_repl_chunk *repl_chunk,
2440                                        struct drsuapi_getncchanges_state *getnc_state)
2441 {
2442         bool chunk_full = false;
2443         uint32_t links_to_send;
2444         uint32_t chunk_limit;
2445
2446         /* check if the current chunk is already full with objects */
2447         if (repl_chunk->object_count >= repl_chunk->max_objects) {
2448                 chunk_full = true;
2449
2450         } else if (repl_chunk->object_count > 0 &&
2451                    getncchanges_chunk_timed_out(repl_chunk)) {
2452
2453                 /*
2454                  * We've exceeded our allotted time building this chunk,
2455                  * and we have at least one object to send back to the client
2456                  */
2457                 chunk_full = true;
2458
2459         } else if (repl_chunk->immediate_link_sync) {
2460
2461                 /* check if the chunk is already full with links */
2462                 links_to_send = getncchanges_chunk_links_pending(repl_chunk,
2463                                                                  getnc_state);
2464
2465                 chunk_limit = getncchanges_chunk_max_links(repl_chunk);
2466
2467                 /*
2468                  * The chunk is full if we've got more links to send than will
2469                  * fit in one chunk
2470                  */
2471                 if (links_to_send > 0 && chunk_limit <= links_to_send) {
2472                         chunk_full = true;
2473                 }
2474         }
2475
2476         return chunk_full;
2477 }
2478
2479 /**
2480  * Goes through any new linked attributes and checks that the target object
2481  * will be known to the client, i.e. we've already sent it in an replication
2482  * chunk. If not, then it adds the target object to the current replication
2483  * chunk. This is only done when the client specifies DRS_GET_TGT.
2484  */
2485 static WERROR getncchanges_chunk_add_la_targets(struct getncchanges_repl_chunk *repl_chunk,
2486                                                 struct drsuapi_getncchanges_state *getnc_state,
2487                                                 uint32_t start_la_index,
2488                                                 TALLOC_CTX *mem_ctx,
2489                                                 struct ldb_context *sam_ctx,
2490                                                 struct dsdb_schema *schema,
2491                                                 DATA_BLOB *session_key,
2492                                                 struct drsuapi_DsGetNCChangesRequest10 *req10,
2493                                                 uint32_t *local_pas,
2494                                                 struct ldb_dn *machine_dn)
2495 {
2496         int ret;
2497         uint32_t i;
2498         uint32_t max_la_index;
2499         uint32_t max_links;
2500         uint32_t target_count = 0;
2501         WERROR werr = WERR_OK;
2502         static const char * const msg_attrs[] = {
2503                                             "*",
2504                                             "nTSecurityDescriptor",
2505                                             "parentGUID",
2506                                             "replPropertyMetaData",
2507                                             DSDB_SECRET_ATTRIBUTES,
2508                                             NULL };
2509
2510         /*
2511          * A object can potentially link to thousands of targets. Only bother
2512          * checking as many targets as will fit into the current response
2513          */
2514         max_links = getncchanges_chunk_max_links(repl_chunk);
2515         max_la_index = MIN(getnc_state->la_count,
2516                            start_la_index + max_links);
2517
2518         /* loop through any linked attributes to check */
2519         for (i = start_la_index;
2520              (i < max_la_index &&
2521               !getncchanges_chunk_is_full(repl_chunk, getnc_state));
2522              i++) {
2523
2524                 struct GUID target_guid;
2525                 struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
2526                 const struct drsuapi_DsReplicaLinkedAttribute *la;
2527                 struct ldb_result *msg_res;
2528                 struct ldb_dn *search_dn;
2529                 TALLOC_CTX *tmp_ctx;
2530                 struct dsdb_dn *dn;
2531                 const struct dsdb_attribute *schema_attrib;
2532                 NTSTATUS status;
2533                 bool same_nc;
2534
2535                 la = &getnc_state->la_list[i];
2536                 tmp_ctx = talloc_new(mem_ctx);
2537
2538                 /*
2539                  * Track what linked attribute targets we've checked. We might
2540                  * not have time to check them all, so we should only send back
2541                  * the ones we've actually checked.
2542                  */
2543                 repl_chunk->tgt_la_count = i + 1;
2544
2545                 /* get the GUID of the linked attribute's target object */
2546                 schema_attrib = dsdb_attribute_by_attributeID_id(schema,
2547                                                                  la->attid);
2548
2549                 werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema,
2550                                             tmp_ctx, la->value.blob, &dn);
2551
2552                 if (!W_ERROR_IS_OK(werr)) {
2553                         DEBUG(0,(__location__ ": Bad la blob\n"));
2554                         return werr;
2555                 }
2556
2557                 status = dsdb_get_extended_dn_guid(dn->dn, &target_guid, "GUID");
2558
2559                 if (!NT_STATUS_IS_OK(status)) {
2560                         return ntstatus_to_werror(status);
2561                 }
2562
2563                 /*
2564                  * if the target isn't in the cache, then the client
2565                  * might not know about it, so send the target now
2566                  */
2567                 werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
2568                                                        &target_guid);
2569
2570                 if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
2571
2572                         /* target already sent, nothing to do */
2573                         TALLOC_FREE(tmp_ctx);
2574                         continue;
2575                 }
2576
2577                 same_nc = dsdb_objects_have_same_nc(sam_ctx, tmp_ctx, dn->dn,
2578                                                     getnc_state->ncRoot_dn);
2579
2580                 /* don't try to fetch target objects from another partition */
2581                 if (!same_nc) {
2582                         TALLOC_FREE(tmp_ctx);
2583                         continue;
2584                 }
2585
2586                 search_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
2587                                            GUID_string(tmp_ctx, &target_guid));
2588                 W_ERROR_HAVE_NO_MEMORY(search_dn);
2589
2590                 ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx,
2591                                                       &msg_res, search_dn,
2592                                                       LDB_SCOPE_BASE,
2593                                                       msg_attrs, NULL);
2594
2595                 /*
2596                  * Don't fail the replication if we can't find the target.
2597                  * This could happen for a one-way linked attribute, if the
2598                  * target is deleted and then later expunged (thus, the source
2599                  * object can be left with a hanging link). Continue to send
2600                  * the the link (the client-side has already tried once with
2601                  * GET_TGT, so it should just end up ignoring it).
2602                  */
2603                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
2604                         DBG_WARNING("Encountered unknown link target DN %s\n",
2605                                     ldb_dn_get_extended_linearized(tmp_ctx, dn->dn, 1));
2606                         TALLOC_FREE(tmp_ctx);
2607                         continue;
2608
2609                 } else if (ret != LDB_SUCCESS) {
2610                         DBG_ERR("Failed to fetch link target DN %s - %s\n",
2611                                 ldb_dn_get_extended_linearized(tmp_ctx, dn->dn, 1),
2612                                 ldb_errstring(sam_ctx));
2613                         return WERR_DS_DRA_INCONSISTENT_DIT;
2614                 }
2615
2616                 /*
2617                  * Construct an object, ready to send (this will include
2618                  * the object's ancestors as well, if GET_ANC is set)
2619                  */
2620                 werr = getncchanges_get_obj_to_send(msg_res->msgs[0], mem_ctx,
2621                                                     sam_ctx, getnc_state,
2622                                                     schema, session_key, req10,
2623                                                     false, local_pas,
2624                                                     machine_dn, &target_guid,
2625                                                     &new_objs);
2626                 if (!W_ERROR_IS_OK(werr)) {
2627                         return werr;
2628                 }
2629
2630                 if (new_objs != NULL) {
2631                         target_count++;
2632                         getncchanges_chunk_add_objects(repl_chunk, new_objs);
2633                 }
2634                 TALLOC_FREE(tmp_ctx);
2635         }
2636
2637         if (target_count > 0) {
2638                 DEBUG(3, ("GET_TGT: checked %u link-attrs, added %u target objs\n",
2639                           i - start_la_index, target_count));
2640         }
2641
2642         return WERR_OK;
2643 }
2644
2645 /**
2646  * Creates a helper struct used for building a chunk of replication data,
2647  * i.e. used over a single call to dcesrv_drsuapi_DsGetNCChanges().
2648  */
2649 static struct getncchanges_repl_chunk * getncchanges_chunk_new(TALLOC_CTX *mem_ctx,
2650                                                                struct dcesrv_call_state *dce_call,
2651                                                                struct drsuapi_DsGetNCChangesRequest10 *req10)
2652 {
2653         struct getncchanges_repl_chunk *repl_chunk;
2654
2655         repl_chunk = talloc_zero(mem_ctx, struct getncchanges_repl_chunk);
2656
2657         repl_chunk->start = time(NULL);
2658
2659         repl_chunk->max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL,
2660                                                  "drs", "max object sync",
2661                                                  DEFAULT_MAX_OBJECTS);
2662
2663         /*
2664          * The client control here only applies in normal replication, not extended
2665          * operations, which return a fixed set, even if the caller
2666          * sets max_object_count == 0
2667          */
2668         if (req10->extended_op == DRSUAPI_EXOP_NONE) {
2669
2670                 /*
2671                  * use this to force single objects at a time, which is useful
2672                  * for working out what object is giving problems
2673                  */
2674                 if (req10->max_object_count < repl_chunk->max_objects) {
2675                         repl_chunk->max_objects = req10->max_object_count;
2676                 }
2677         }
2678
2679         repl_chunk->max_links =
2680                         lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL,
2681                                        "drs", "max link sync",
2682                                         DEFAULT_MAX_LINKS);
2683
2684         repl_chunk->immediate_link_sync =
2685                         lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL,
2686                                         "drs", "immediate link sync", false);
2687
2688         /*
2689          * Maximum time that we can spend in a getncchanges
2690          * in order to avoid timeout of the other part.
2691          * 10 seconds by default.
2692          */
2693         repl_chunk->max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx,
2694                                               NULL, "drs", "max work time", 10);
2695
2696         return repl_chunk;
2697 }
2698
2699 /*
2700   drsuapi_DsGetNCChanges
2701
2702   see MS-DRSR 4.1.10.5.2 for basic logic of this function
2703 */
2704 WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2705                                      struct drsuapi_DsGetNCChanges *r)
2706 {
2707         struct auth_session_info *session_info =
2708                 dcesrv_call_session_info(dce_call);
2709         struct imessaging_context *imsg_ctx =
2710                 dcesrv_imessaging_context(dce_call->conn);
2711         struct drsuapi_DsReplicaObjectIdentifier *untrusted_ncRoot;
2712         int ret;
2713         uint32_t i, k;
2714         struct dsdb_schema *schema;
2715         struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
2716         struct getncchanges_repl_chunk *repl_chunk;
2717         NTSTATUS status;
2718         DATA_BLOB session_key;
2719         WERROR werr;
2720         struct dcesrv_handle *h;
2721         struct drsuapi_bind_state *b_state;
2722         struct drsuapi_getncchanges_state *getnc_state = NULL;
2723         struct drsuapi_DsGetNCChangesRequest10 *req10;
2724         uint32_t options;
2725         uint32_t link_count = 0;
2726         struct ldb_dn *search_dn = NULL;
2727         bool am_rodc;
2728         enum security_user_level security_level;
2729         struct ldb_context *sam_ctx;
2730         struct dom_sid *user_sid;
2731         bool is_secret_request;
2732         bool is_gc_pas_request;
2733         struct drsuapi_changed_objects *changes;
2734         bool has_get_all_changes = false;
2735         struct GUID invocation_id;
2736         static const struct drsuapi_DsReplicaLinkedAttribute no_linked_attr;
2737         struct dsdb_schema_prefixmap *pfm_remote = NULL;
2738         bool full = true;
2739         uint32_t *local_pas = NULL;
2740         struct ldb_dn *machine_dn = NULL; /* Only used for REPL SECRET EXOP */
2741
2742         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
2743         b_state = h->data;
2744
2745         /* sam_ctx_system is not present for non-administrator users */
2746         sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
2747
2748         invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2749
2750         *r->out.level_out = 6;
2751
2752         r->out.ctr->ctr6.linked_attributes_count = 0;
2753         r->out.ctr->ctr6.linked_attributes = discard_const_p(struct drsuapi_DsReplicaLinkedAttribute, &no_linked_attr);
2754
2755         r->out.ctr->ctr6.object_count = 0;
2756         r->out.ctr->ctr6.nc_object_count = 0;
2757         r->out.ctr->ctr6.more_data = false;
2758         r->out.ctr->ctr6.uptodateness_vector = NULL;
2759         r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
2760         r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
2761         r->out.ctr->ctr6.first_object = NULL;
2762
2763         /* Check request revision. 
2764          */
2765         switch (r->in.level) {
2766         case 8:
2767                 req10 = getncchanges_map_req8(mem_ctx, &r->in.req->req8);
2768                 if (req10 == NULL) {
2769                         return WERR_NOT_ENOUGH_MEMORY;
2770                 }
2771                 break;
2772         case 10:
2773                 req10 = &r->in.req->req10;
2774                 break;
2775         default:
2776                 DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
2777                          r->in.level));
2778                 return WERR_REVISION_MISMATCH;
2779         }
2780
2781         repl_chunk = getncchanges_chunk_new(mem_ctx, dce_call, req10);
2782
2783         if (repl_chunk == NULL) {
2784                 return WERR_NOT_ENOUGH_MEMORY;
2785         }
2786
2787         /* a RODC doesn't allow for any replication */
2788         ret = samdb_rodc(sam_ctx, &am_rodc);
2789         if (ret == LDB_SUCCESS && am_rodc) {
2790                 DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
2791                 return WERR_DS_DRA_SOURCE_DISABLED;
2792         }
2793
2794         /*
2795          * Help our tests pass by pre-checking the
2796          * destination_dsa_guid before the NC permissions.  Info on
2797          * valid DSA GUIDs is not sensitive so this isn't a leak
2798          */
2799         switch (req10->extended_op) {
2800         case DRSUAPI_EXOP_FSMO_REQ_ROLE:
2801         case DRSUAPI_EXOP_FSMO_RID_ALLOC:
2802         case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
2803         case DRSUAPI_EXOP_FSMO_REQ_PDC:
2804         case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
2805         {
2806                 const char *attrs[] = { NULL };
2807
2808                 ret = samdb_get_ntds_obj_by_guid(mem_ctx,
2809                                                  sam_ctx,
2810                                                  &req10->destination_dsa_guid,
2811                                                  attrs,
2812                                                  NULL);
2813                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
2814                         /*
2815                          * Error out with an EXOP error but success at
2816                          * the top level return value
2817                          */
2818                         r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
2819                         return WERR_OK;
2820                 } else if (ret != LDB_SUCCESS) {
2821                         return WERR_DS_DRA_INTERNAL_ERROR;
2822                 }
2823
2824                 break;
2825         }
2826         case DRSUAPI_EXOP_REPL_SECRET:
2827         case DRSUAPI_EXOP_REPL_OBJ:
2828         case DRSUAPI_EXOP_NONE:
2829                 break;
2830         }
2831
2832         /* Perform access checks. */
2833         untrusted_ncRoot = req10->naming_context;
2834         if (untrusted_ncRoot == NULL) {
2835                 DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
2836                 return WERR_DS_DRA_INVALID_PARAMETER;
2837         }
2838
2839         if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) {
2840                 return WERR_DS_DRA_INTERNAL_ERROR;
2841         }
2842
2843         if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
2844             !(req10->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
2845                 return WERR_DS_DRA_SOURCE_DISABLED;
2846         }
2847
2848         user_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
2849
2850         /*
2851          * all clients must have GUID_DRS_GET_CHANGES.  This finds the
2852          * actual NC root of the given value and checks that, allowing
2853          * REPL_OBJ to work safely
2854          */
2855         werr = drs_security_access_check_nc_root(sam_ctx,
2856                                                  mem_ctx,
2857                                                  session_info->security_token,
2858                                                  req10->naming_context,
2859                                                  GUID_DRS_GET_CHANGES);
2860
2861         if (W_ERROR_EQUAL(werr, WERR_DS_DRA_BAD_NC)) {
2862                 /*
2863                  * These extended operations need a different error if
2864                  * the supplied DN can't be found
2865                  */
2866                 switch (req10->extended_op) {
2867                 case DRSUAPI_EXOP_REPL_OBJ:
2868                 case DRSUAPI_EXOP_REPL_SECRET:
2869                         return WERR_DS_DRA_BAD_DN;
2870                 default:
2871                         return werr;
2872                 }
2873         }
2874         if (!W_ERROR_IS_OK(werr)) {
2875                 return werr;
2876         }
2877
2878         if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) {
2879                 full = req10->partial_attribute_set == NULL &&
2880                        req10->partial_attribute_set_ex == NULL;
2881         } else {
2882                 full = (options & DRSUAPI_DRS_WRIT_REP) != 0;
2883         }
2884
2885         werr = dsdb_schema_pfm_from_drsuapi_pfm(&req10->mapping_ctr, true,
2886                                                 mem_ctx, &pfm_remote, NULL);
2887
2888         /* We were supplied a partial attribute set, without the prefix map! */
2889         if (!full && !W_ERROR_IS_OK(werr)) {
2890                 if (req10->mapping_ctr.num_mappings == 0) {
2891                         /*
2892                          * Despite the fact MS-DRSR specifies that this shouldn't
2893                          * happen, Windows RODCs will in fact not provide a prefixMap.
2894                          */
2895                         DEBUG(5,(__location__ ": Failed to provide a remote prefixMap,"
2896                                  " falling back to local prefixMap\n"));
2897                 } else {
2898                         DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
2899                                  win_errstr(werr)));
2900                         return werr;
2901                 }
2902         }
2903
2904         /* allowed if the GC PAS and client has
2905            GUID_DRS_GET_FILTERED_ATTRIBUTES */
2906         werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, pfm_remote, &is_gc_pas_request);
2907         if (!W_ERROR_IS_OK(werr)) {
2908                 return werr;
2909         }
2910         if (is_gc_pas_request) {
2911                 werr = drs_security_access_check_nc_root(sam_ctx,
2912                                                          mem_ctx,
2913                                                          session_info->security_token,
2914                                                          req10->naming_context,
2915                                                          GUID_DRS_GET_FILTERED_ATTRIBUTES);
2916                 if (W_ERROR_IS_OK(werr)) {
2917                         goto allowed;
2918                 }
2919         }
2920
2921         werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10,
2922                                                         pfm_remote,
2923                                                         &is_secret_request);
2924         if (!W_ERROR_IS_OK(werr)) {
2925                 return werr;
2926         }
2927         if (is_secret_request) {
2928                 werr = drs_security_access_check_nc_root(sam_ctx,
2929                                                          mem_ctx,
2930                                                          session_info->security_token,
2931                                                          req10->naming_context,
2932                                                          GUID_DRS_GET_ALL_CHANGES);
2933                 if (!W_ERROR_IS_OK(werr)) {
2934                         /* Only bail if this is not a EXOP_REPL_SECRET */
2935                         if (req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
2936                                 return werr;
2937                         }
2938                 } else {
2939                         has_get_all_changes = true;
2940                 }
2941         }
2942
2943 allowed:
2944         /* for non-administrator replications, check that they have
2945            given the correct source_dsa_invocation_id */
2946         security_level = security_session_user_level(session_info,
2947                                                      samdb_domain_sid(sam_ctx));
2948         if (security_level == SECURITY_RO_DOMAIN_CONTROLLER) {
2949                 if (req10->replica_flags & DRSUAPI_DRS_WRIT_REP) {
2950                         /* we rely on this flag being unset for RODC requests */
2951                         req10->replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
2952                 }
2953         }
2954
2955         if (req10->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
2956                 /* Ignore the _in_ uptpdateness vector*/
2957                 req10->uptodateness_vector = NULL;
2958         }
2959
2960         if (GUID_all_zero(&req10->source_dsa_invocation_id)) {
2961                 req10->source_dsa_invocation_id = invocation_id;
2962         }
2963
2964         if (!GUID_equal(&req10->source_dsa_invocation_id, &invocation_id)) {
2965                 /*
2966                  * The given highwatermark is only valid relative to the
2967                  * specified source_dsa_invocation_id.
2968                  */
2969                 ZERO_STRUCT(req10->highwatermark);
2970         }
2971
2972         /*
2973          * An extended operation is "special single-response cycle"
2974          * per MS-DRSR 4.1.10.1.1 "Start and Finish" so we don't need
2975          * to guess if this is a continuation of any long-term
2976          * state.
2977          *
2978          * Otherwise, maintain (including marking as stale, which is
2979          * what the below is for) the replication state.
2980          *
2981          * Note that point 5 "The server implementation MAY declare
2982          * the supplied values ... as too stale to use."  would allow
2983          * resetting the state at almost any point, Microsoft Azure AD
2984          * Connect will switch back and forth between a REPL_OBJ and a
2985          * full replication, so we must not reset the statue during
2986          * extended operations.
2987          */
2988         if (req10->extended_op == DRSUAPI_EXOP_NONE &&
2989             b_state->getncchanges_full_repl_state != NULL) {
2990                 /*
2991                  * Knowing that this is not an extended operation, we
2992                  * can access (and validate) the full replication
2993                  * state
2994                  */
2995                 getnc_state = b_state->getncchanges_full_repl_state;
2996         }
2997
2998         /* see if a previous replication has been abandoned */
2999         if (getnc_state != NULL) {
3000                 struct ldb_dn *new_dn;
3001                 ret = drs_ObjectIdentifier_to_dn_and_nc_root(getnc_state,
3002                                                              sam_ctx,
3003                                                              untrusted_ncRoot,
3004                                                              &new_dn,
3005                                                              NULL);
3006                 if (ret != LDB_SUCCESS) {
3007                         /*
3008                          * This can't fail as we have done this above
3009                          * implicitly but not got the DN out, but
3010                          * print a good error message regardless just
3011                          * in case.
3012                          */
3013                         DBG_ERR("Bad DN '%s' as Naming Context for GetNCChanges: %s\n",
3014                                 drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3015                                 ldb_strerror(ret));
3016                         return WERR_DS_DRA_INVALID_PARAMETER;
3017                 }
3018                 if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
3019                         DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
3020                                  ldb_dn_get_linearized(new_dn),
3021                                  ldb_dn_get_linearized(getnc_state->ncRoot_dn),
3022                                  ldb_dn_get_linearized(getnc_state->last_dn)));
3023                         TALLOC_FREE(getnc_state);
3024                         b_state->getncchanges_full_repl_state = NULL;
3025                 }
3026         }
3027
3028         if (getnc_state != NULL) {
3029                 ret = drsuapi_DsReplicaHighWaterMark_cmp(&getnc_state->last_hwm,
3030                                                          &req10->highwatermark);
3031                 if (ret != 0) {
3032                         DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication "
3033                                  "on DN %s %s highwatermark (last_dn %s)\n",
3034                                  ldb_dn_get_linearized(getnc_state->ncRoot_dn),
3035                                  (ret > 0) ? "older" : "newer",
3036                                  ldb_dn_get_linearized(getnc_state->last_dn)));
3037                         TALLOC_FREE(getnc_state);
3038                         b_state->getncchanges_full_repl_state = NULL;
3039                 }
3040         }
3041
3042          /*
3043           * This is either a new replication cycle, or an extended
3044           * operation.  A new cycle is triggered above by the
3045           * TALLOC_FREE() which sets getnc_state to NULL.
3046           */
3047         if (getnc_state == NULL) {
3048                 struct ldb_result *res = NULL;
3049                 const char *attrs[] = {
3050                         "instanceType",
3051                         "objectGuID",
3052                         NULL
3053                 };
3054                 uint32_t nc_instanceType;
3055                 struct ldb_dn *ncRoot_dn;
3056
3057                 ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
3058                                                              sam_ctx,
3059                                                              untrusted_ncRoot,
3060                                                              &ncRoot_dn,
3061                                                              NULL);
3062                 if (ret != LDB_SUCCESS) {
3063                         DBG_ERR("Bad DN '%s' as Naming Context or EXOP DN for GetNCChanges: %s\n",
3064                                 drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3065                                 ldb_strerror(ret));
3066                         return WERR_DS_DRA_BAD_DN;
3067                 }
3068
3069                 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res,
3070                                      ncRoot_dn, attrs,
3071                                      DSDB_SEARCH_SHOW_DELETED |
3072                                      DSDB_SEARCH_SHOW_RECYCLED);
3073                 if (ret != LDB_SUCCESS) {
3074                         DBG_WARNING("Failed to find ncRoot_dn %s\n",
3075                                     ldb_dn_get_linearized(ncRoot_dn));
3076                         return WERR_DS_DRA_BAD_DN;
3077                 }
3078                 nc_instanceType = ldb_msg_find_attr_as_int(res->msgs[0],
3079                                                            "instanceType",
3080                                                            0);
3081
3082                 if (req10->extended_op != DRSUAPI_EXOP_NONE) {
3083                         r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
3084                 }
3085
3086                 /*
3087                  * This is the first replication cycle and it is
3088                  * a good place to handle extended operations
3089                  *
3090                  * FIXME: we don't fully support extended operations yet
3091                  */
3092                 switch (req10->extended_op) {
3093                 case DRSUAPI_EXOP_NONE:
3094                         if ((nc_instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) {
3095                                 const char *dn_str
3096                                         = ldb_dn_get_linearized(ncRoot_dn);
3097
3098                                 DBG_NOTICE("Rejecting full replication on "
3099                                            "not NC %s\n", dn_str);
3100
3101                                 return WERR_DS_CANT_FIND_EXPECTED_NC;
3102                         }
3103
3104                         break;
3105                 case DRSUAPI_EXOP_FSMO_RID_ALLOC:
3106                         werr = getncchanges_rid_alloc(b_state, mem_ctx, req10, &r->out.ctr->ctr6, &search_dn);
3107                         W_ERROR_NOT_OK_RETURN(werr);
3108                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3109                                 return WERR_OK;
3110                         }
3111                         break;
3112                 case DRSUAPI_EXOP_REPL_SECRET:
3113                         werr = getncchanges_repl_secret(b_state, mem_ctx, req10,
3114                                                         user_sid,
3115                                                         &r->out.ctr->ctr6,
3116                                                         has_get_all_changes,
3117                                                         &machine_dn);
3118                         r->out.result = werr;
3119                         W_ERROR_NOT_OK_RETURN(werr);
3120                         break;
3121                 case DRSUAPI_EXOP_FSMO_REQ_ROLE:
3122                         werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
3123                         W_ERROR_NOT_OK_RETURN(werr);
3124                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3125                                 return WERR_OK;
3126                         }
3127                         break;
3128                 case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
3129                         werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
3130                         W_ERROR_NOT_OK_RETURN(werr);
3131                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3132                                 return WERR_OK;
3133                         }
3134                         break;
3135                 case DRSUAPI_EXOP_FSMO_REQ_PDC:
3136                         werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
3137                         W_ERROR_NOT_OK_RETURN(werr);
3138                         if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
3139                                 return WERR_OK;
3140                         }
3141                         break;
3142                 case DRSUAPI_EXOP_REPL_OBJ:
3143                         werr = getncchanges_repl_obj(b_state, mem_ctx, req10, user_sid, &r->out.ctr->ctr6);
3144                         r->out.result = werr;
3145                         W_ERROR_NOT_OK_RETURN(werr);
3146                         break;
3147
3148                 case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
3149
3150                         DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
3151                                  (unsigned)req10->extended_op));
3152                         return WERR_DS_DRA_NOT_SUPPORTED;
3153                 }
3154
3155                 /*
3156                  * Initialize the state, initially for the remainder
3157                  * of this call (EXOPs)
3158                  *
3159                  * An extended operation is a "special single-response
3160                  * cycle" per MS-DRSR 4.1.10.1.1 "Start and Finish"
3161                  *
3162                  */
3163                 getnc_state = talloc_zero(mem_ctx, struct drsuapi_getncchanges_state);
3164                 if (getnc_state == NULL) {
3165                         return WERR_NOT_ENOUGH_MEMORY;
3166                 }
3167
3168                 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3169                         /*
3170                          * Promote the memory to being a store of
3171                          * long-term state that we will use over the
3172                          * replication cycle for full replication
3173                          * requests
3174                          *
3175                          * Store the state in a clearly named location
3176                          * for pulling back only during full
3177                          * replications
3178                          */
3179                         b_state->getncchanges_full_repl_state
3180                                 = talloc_steal(b_state, getnc_state);
3181                 }
3182
3183                 getnc_state->ncRoot_dn = ncRoot_dn;
3184                 talloc_steal(getnc_state, ncRoot_dn);
3185
3186                 getnc_state->ncRoot_guid = samdb_result_guid(res->msgs[0],
3187                                                              "objectGUID");
3188
3189                 /* find out if we are to replicate Schema NC */
3190                 ret = ldb_dn_compare_base(ldb_get_schema_basedn(sam_ctx),
3191                                           ncRoot_dn);
3192                 getnc_state->is_schema_nc = (0 == ret);
3193
3194                 TALLOC_FREE(res);
3195         }
3196
3197         /* we need the session key for encrypting password attributes */
3198         status = dcesrv_auth_session_key(dce_call, &session_key);
3199         if (!NT_STATUS_IS_OK(status)) {
3200                 DEBUG(0,(__location__ ": Failed to get session key\n"));
3201                 return WERR_DS_DRA_INTERNAL_ERROR;
3202         }
3203
3204         /* 
3205            TODO: MS-DRSR section 4.1.10.1.1
3206            Work out if this is the start of a new cycle */
3207
3208         if (getnc_state->guids == NULL) {
3209                 const char *extra_filter;
3210                 struct ldb_result *search_res = NULL;
3211                 static const struct drsuapi_DsReplicaCursorCtrEx empty_udv;
3212                 const struct drsuapi_DsReplicaCursorCtrEx *udv = NULL;
3213
3214                 extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
3215
3216                 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3217                         if (req10->uptodateness_vector != NULL) {
3218                                 udv = req10->uptodateness_vector;
3219                         } else {
3220                                 udv = &empty_udv;
3221                         }
3222
3223                         getnc_state->min_usn = req10->highwatermark.highest_usn;
3224                         for (i = 0; i < udv->count; i++) {
3225                                 bool match;
3226                                 const struct drsuapi_DsReplicaCursor *cur =
3227                                         &udv->cursors[i];
3228
3229                                 match = GUID_equal(&invocation_id,
3230                                                    &cur->source_dsa_invocation_id);
3231                                 if (!match) {
3232                                         continue;
3233                                 }
3234                                 if (cur->highest_usn > getnc_state->min_usn) {
3235                                         getnc_state->min_usn = cur->highest_usn;
3236                                 }
3237                                 break;
3238                         }
3239                 } else {
3240                         /* We do not want REPL_SECRETS or REPL_SINGLE to return empty-handed */
3241                         udv = &empty_udv;
3242                         getnc_state->min_usn = 0;
3243                 }
3244
3245                 getnc_state->max_usn = getnc_state->min_usn;
3246
3247                 getnc_state->final_udv = talloc_zero(getnc_state,
3248                                         struct drsuapi_DsReplicaCursor2CtrEx);
3249                 if (getnc_state->final_udv == NULL) {
3250                         return WERR_NOT_ENOUGH_MEMORY;
3251                 }
3252                 werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn,
3253                                           getnc_state->final_udv);
3254                 if (!W_ERROR_IS_OK(werr)) {
3255                         return werr;
3256                 }
3257
3258                 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3259                         werr = getncchanges_collect_objects(b_state, mem_ctx,
3260                                                             getnc_state, req10,
3261                                                             search_dn, extra_filter,
3262                                                             &search_res);
3263                 } else {
3264                         werr = getncchanges_collect_objects_exop(b_state, mem_ctx,
3265                                                                  getnc_state, req10,
3266                                                                  &r->out.ctr->ctr6,
3267                                                                  search_dn, extra_filter,
3268                                                                  &search_res);
3269                 }
3270                 W_ERROR_NOT_OK_RETURN(werr);
3271
3272                 /* extract out the GUIDs list */
3273                 getnc_state->num_records = search_res ? search_res->count : 0;
3274                 getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records);
3275                 W_ERROR_HAVE_NO_MEMORY(getnc_state->guids);
3276
3277                 changes = talloc_array(getnc_state,
3278                                        struct drsuapi_changed_objects,
3279                                        getnc_state->num_records);
3280                 W_ERROR_HAVE_NO_MEMORY(changes);
3281
3282                 for (i=0; i<getnc_state->num_records; i++) {
3283                         changes[i].dn = search_res->msgs[i]->dn;
3284                         changes[i].guid = samdb_result_guid(search_res->msgs[i], "objectGUID");
3285                         changes[i].usn = ldb_msg_find_attr_as_uint64(search_res->msgs[i], "uSNChanged", 0);
3286
3287                         if (changes[i].usn > getnc_state->max_usn) {
3288                                 getnc_state->max_usn = changes[i].usn;
3289                         }
3290                 }
3291
3292                 if (req10->extended_op == DRSUAPI_EXOP_NONE) {
3293                         getnc_state->is_get_anc =
3294                                 ((req10->replica_flags & DRSUAPI_DRS_GET_ANC) != 0);
3295                         if (getnc_state->is_get_anc
3296                                 && lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
3297                                                     NULL,
3298                                                     "drs",
3299                                                     "broken_samba_4.5_get_anc_emulation",
3300                                                    false)) {
3301                                 getnc_state->broken_samba_4_5_get_anc_emulation = true;
3302                         }
3303                         if (lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
3304                                              NULL,
3305                                              "drs",
3306                                              "get_tgt_support",
3307                                              true)) {
3308                                 getnc_state->is_get_tgt =
3309                                         ((req10->more_flags & DRSUAPI_DRS_GET_TGT) != 0);
3310                         }
3311                 }
3312
3313                 /* RID_ALLOC returns 3 objects in a fixed order */
3314                 if (req10->extended_op == DRSUAPI_EXOP_FSMO_RID_ALLOC) {
3315                         /* Do nothing */
3316                 } else if (getnc_state->broken_samba_4_5_get_anc_emulation) {
3317                         LDB_TYPESAFE_QSORT(changes,
3318                                            getnc_state->num_records,
3319                                            getnc_state,
3320                                            site_res_cmp_anc_order);
3321                 } else {
3322                         LDB_TYPESAFE_QSORT(changes,
3323                                            getnc_state->num_records,
3324                                            getnc_state,
3325                                            site_res_cmp_usn_order);
3326                 }
3327
3328                 for (i=0; i < getnc_state->num_records; i++) {
3329                         getnc_state->guids[i] = changes[i].guid;
3330                         if (GUID_all_zero(&getnc_state->guids[i])) {
3331                                 DEBUG(2,("getncchanges: bad objectGUID from %s\n",
3332                                          ldb_dn_get_linearized(search_res->msgs[i]->dn)));
3333                                 return WERR_DS_DRA_INTERNAL_ERROR;
3334                         }
3335                 }
3336
3337                 getnc_state->final_hwm.tmp_highest_usn = getnc_state->max_usn;
3338                 getnc_state->final_hwm.reserved_usn = 0;
3339                 getnc_state->final_hwm.highest_usn = getnc_state->max_usn;
3340
3341                 talloc_free(search_res);
3342                 talloc_free(changes);
3343
3344                 /*
3345                  * when using GET_ANC or GET_TGT, cache the objects that have
3346                  * been already sent, to avoid sending them multiple times
3347                  */
3348                 if (getnc_state->is_get_anc || getnc_state->is_get_tgt) {
3349                         DEBUG(3,("Using object cache, GET_ANC %u, GET_TGT %u\n",
3350                                  getnc_state->is_get_anc,
3351                                  getnc_state->is_get_tgt));
3352
3353                         getnc_state->obj_cache = db_open_rbt(getnc_state);
3354                         if (getnc_state->obj_cache == NULL) {
3355                                 return WERR_NOT_ENOUGH_MEMORY;
3356                         }
3357                 }
3358         }
3359
3360         if (req10->uptodateness_vector) {
3361                 /* make sure its sorted */
3362                 TYPESAFE_QSORT(req10->uptodateness_vector->cursors,
3363                                req10->uptodateness_vector->count,
3364                                drsuapi_DsReplicaCursor_compare);
3365         }
3366
3367         /* Prefix mapping */
3368         schema = dsdb_get_schema(sam_ctx, mem_ctx);
3369         if (!schema) {
3370                 DEBUG(0,("No schema in sam_ctx\n"));
3371                 return WERR_DS_DRA_INTERNAL_ERROR;
3372         }
3373
3374         r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
3375         if (r->out.ctr->ctr6.naming_context == NULL) {
3376                 return WERR_NOT_ENOUGH_MEMORY;
3377         }
3378
3379         /*
3380          * Match Windows and echo back the original values from the request, even if
3381          * they say DummyDN for the string NC
3382          */
3383         *r->out.ctr->ctr6.naming_context = *untrusted_ncRoot;
3384
3385         /* find the SID if there is one */
3386         dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
3387
3388         /* Set GUID */
3389         r->out.ctr->ctr6.naming_context->guid = getnc_state->ncRoot_guid;
3390
3391         dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
3392         r->out.ctr->ctr6.mapping_ctr = *ctr;
3393
3394         r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
3395         r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
3396
3397         r->out.ctr->ctr6.old_highwatermark = req10->highwatermark;
3398         r->out.ctr->ctr6.new_highwatermark = req10->highwatermark;
3399
3400         /*
3401          * If the client has already set GET_TGT then we know they can handle
3402          * receiving the linked attributes interleaved with the source objects
3403          */
3404         if (getnc_state->is_get_tgt) {
3405                 repl_chunk->immediate_link_sync = true;
3406         }
3407
3408         if (req10->partial_attribute_set != NULL) {
3409                 struct dsdb_syntax_ctx syntax_ctx;
3410                 uint32_t j = 0;
3411
3412                 dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
3413                 syntax_ctx.pfm_remote = pfm_remote;
3414
3415                 local_pas = talloc_array(b_state, uint32_t, req10->partial_attribute_set->num_attids);
3416
3417                 for (j = 0; j < req10->partial_attribute_set->num_attids; j++) {
3418                         getncchanges_attid_remote_to_local(schema,
3419                                                            &syntax_ctx,
3420                                                            req10->partial_attribute_set->attids[j],
3421                                                            (enum drsuapi_DsAttributeId *)&local_pas[j],
3422                                                            NULL);
3423                 }
3424
3425                 TYPESAFE_QSORT(local_pas,
3426                                req10->partial_attribute_set->num_attids,
3427                                uint32_t_ptr_cmp);
3428         }
3429
3430         /*
3431          * Check in case we're still processing the links from an object in the
3432          * previous chunk. We want to send the links (and any targets needed)
3433          * before moving on to the next object.
3434          */
3435         if (getnc_state->is_get_tgt) {
3436                 werr = getncchanges_chunk_add_la_targets(repl_chunk,
3437                                                          getnc_state,
3438                                                          getnc_state->la_idx,
3439                                                          mem_ctx, sam_ctx,
3440                                                          schema, &session_key,
3441                                                          req10, local_pas,
3442                                                          machine_dn);
3443
3444                 if (!W_ERROR_IS_OK(werr)) {
3445                         return werr;
3446                 }
3447         }
3448
3449         for (i=getnc_state->num_processed;
3450              i<getnc_state->num_records &&
3451                      !getncchanges_chunk_is_full(repl_chunk, getnc_state);
3452             i++) {
3453                 struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
3454                 struct ldb_message *msg;
3455                 static const char * const msg_attrs[] = {
3456                                             "*",
3457                                             "nTSecurityDescriptor",
3458                                             "parentGUID",
3459                                             "replPropertyMetaData",
3460                                             DSDB_SECRET_ATTRIBUTES,
3461                                             NULL };
3462                 struct ldb_result *msg_res;
3463                 struct ldb_dn *msg_dn;
3464                 bool obj_already_sent = false;
3465                 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3466                 uint32_t old_la_index;
3467
3468                 msg_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
3469                                         GUID_string(tmp_ctx, &getnc_state->guids[i]));
3470                 W_ERROR_HAVE_NO_MEMORY(msg_dn);
3471
3472                 /*
3473                  * by re-searching here we avoid having a lot of full
3474                  * records in memory between calls to getncchanges.
3475                  *
3476                  * We expect that we may get some objects that vanish
3477                  * (tombstone expunge) between the first and second
3478                  * check.
3479                  */
3480                 ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx, &msg_res,
3481                                                       msg_dn,
3482                                                       LDB_SCOPE_BASE, msg_attrs, NULL);
3483                 if (ret != LDB_SUCCESS) {
3484                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
3485                                 DEBUG(1,("getncchanges: failed to fetch DN %s - %s\n",
3486                                          ldb_dn_get_extended_linearized(tmp_ctx, msg_dn, 1),
3487                                          ldb_errstring(sam_ctx)));
3488                         }
3489                         TALLOC_FREE(tmp_ctx);
3490                         continue;
3491                 }
3492
3493                 if (msg_res->count == 0) {
3494                         DEBUG(1,("getncchanges: got LDB_SUCCESS but failed"
3495                                  "to get any results in fetch of DN "
3496                                  "%s (race with tombstone expunge?)\n",
3497                                  ldb_dn_get_extended_linearized(tmp_ctx,
3498                                                                 msg_dn, 1)));
3499                         TALLOC_FREE(tmp_ctx);
3500                         continue;
3501                 }
3502
3503                 msg = msg_res->msgs[0];
3504
3505                 /*
3506                  * Check if we've already sent the object as an ancestor of
3507                  * another object. If so, we don't need to send it again
3508                  */
3509                 if (getnc_state->obj_cache != NULL) {
3510                         werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
3511                                                                &getnc_state->guids[i]);
3512                         if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
3513                                 obj_already_sent = true;
3514                         }
3515                 }
3516
3517                 if (!obj_already_sent) {
3518                         bool max_wait_reached;
3519
3520                         max_wait_reached = getncchanges_chunk_timed_out(repl_chunk);
3521
3522                         /*
3523                          * Construct an object, ready to send (this will include
3524                          * the object's ancestors as well, if needed)
3525                          */
3526                         werr = getncchanges_get_obj_to_send(msg, mem_ctx, sam_ctx,
3527                                                             getnc_state, schema,
3528                                                             &session_key, req10,
3529                                                             max_wait_reached,
3530                                                             local_pas, machine_dn,
3531                                                             &getnc_state->guids[i],
3532                                                             &new_objs);
3533                         if (!W_ERROR_IS_OK(werr)) {
3534                                 return werr;
3535                         }
3536                 }
3537
3538                 old_la_index = getnc_state->la_count;
3539
3540                 /*
3541                  * We've reached the USN where this object naturally occurs.
3542                  * Regardless of whether we've already sent the object (as an
3543                  * ancestor), we add its links and update the HWM at this point
3544                  */
3545                 werr = get_nc_changes_add_links(sam_ctx, getnc_state,
3546                                                 getnc_state->is_schema_nc,
3547                                                 schema, getnc_state->min_usn,
3548                                                 req10->replica_flags,
3549                                                 msg,
3550                                                 &getnc_state->la_list,
3551                                                 &getnc_state->la_count,
3552                                                 req10->uptodateness_vector);
3553                 if (!W_ERROR_IS_OK(werr)) {
3554                         return werr;
3555                 }
3556
3557                 dcesrv_drsuapi_update_highwatermark(msg,
3558                                         getnc_state->max_usn,
3559                                         &r->out.ctr->ctr6.new_highwatermark);
3560
3561                 if (new_objs != NULL) {
3562
3563                         /*
3564                          * Add the object (and, if GET_ANC, any parents it may
3565                          * have) into the current chunk of replication data
3566                          */
3567                         getncchanges_chunk_add_objects(repl_chunk, new_objs);
3568
3569                         talloc_free(getnc_state->last_dn);
3570                         /*
3571                          * talloc_steal() as we still need msg->dn to
3572                          * be a valid pointer for the log on the next
3573                          * line.
3574                          *
3575                          * msg only remains in scope for the next 25
3576                          * lines or so anyway.
3577                          */
3578                         getnc_state->last_dn = talloc_steal(getnc_state, msg->dn);
3579                 }
3580
3581                 DEBUG(8,(__location__ ": %s object %s new tmp_highest_usn=%" PRIu64 "\n",
3582                          new_objs ? "replicating" : "skipping send of",
3583                          ldb_dn_get_linearized(msg->dn),
3584                          r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn));
3585
3586                 getnc_state->total_links += (getnc_state->la_count - old_la_index);
3587
3588                 /*
3589                  * If the GET_TGT flag was set, check any new links added to
3590                  * make sure the client knows about the link target object
3591                  */
3592                 if (getnc_state->is_get_tgt) {
3593                         werr = getncchanges_chunk_add_la_targets(repl_chunk,
3594                                                                  getnc_state,
3595                                                                  old_la_index,
3596                                                                  mem_ctx, sam_ctx,
3597                                                                  schema, &session_key,
3598                                                                  req10, local_pas,
3599                                                                  machine_dn);
3600
3601                         if (!W_ERROR_IS_OK(werr)) {
3602                                 return werr;
3603                         }
3604                 }
3605
3606                 TALLOC_FREE(tmp_ctx);
3607         }
3608
3609         /* copy the constructed object list into the response message */
3610         r->out.ctr->ctr6.object_count = repl_chunk->object_count;
3611         r->out.ctr->ctr6.first_object = repl_chunk->object_list;
3612
3613         getnc_state->num_processed = i;
3614
3615         if (i < getnc_state->num_records) {
3616                 r->out.ctr->ctr6.more_data = true;
3617         }
3618
3619         /* the client can us to call UpdateRefs on its behalf to
3620            re-establish monitoring of the NC */
3621         if ((req10->replica_flags & (DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_REF_GCSPN)) &&
3622             !GUID_all_zero(&req10->destination_dsa_guid)) {
3623                 struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
3624                 DEBUG(3,("UpdateRefs on getncchanges for %s\n",
3625                          GUID_string(mem_ctx, &req10->destination_dsa_guid)));
3626
3627                 /*
3628                  * We pass the pre-validation NC root here as
3629                  * drsuapi_UpdateRefs() has to check its own input
3630                  * values due to being called from
3631                  * dcesrv_drsuapi_DsReplicaUpdateRefs()
3632                  */
3633
3634                 ureq.naming_context = untrusted_ncRoot;
3635                 ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(sam_ctx, mem_ctx,
3636                                                                    &req10->destination_dsa_guid);
3637                 if (!ureq.dest_dsa_dns_name) {
3638                         return WERR_NOT_ENOUGH_MEMORY;
3639                 }
3640                 ureq.dest_dsa_guid = req10->destination_dsa_guid;
3641                 ureq.options = DRSUAPI_DRS_ADD_REF |
3642                         DRSUAPI_DRS_ASYNC_OP |
3643                         DRSUAPI_DRS_GETCHG_CHECK;
3644
3645                 /* we also need to pass through the
3646                    DRSUAPI_DRS_REF_GCSPN bit so that repsTo gets flagged
3647                    to send notifies using the GC SPN */
3648                 ureq.options |= (req10->replica_flags & DRSUAPI_DRS_REF_GCSPN);
3649
3650                 werr = drsuapi_UpdateRefs(imsg_ctx,
3651                                           dce_call->event_ctx,
3652                                           b_state,
3653                                           mem_ctx,
3654                                           &ureq);
3655                 if (!W_ERROR_IS_OK(werr)) {
3656                         DEBUG(0,(__location__ ": Failed UpdateRefs on %s for %s in DsGetNCChanges - %s\n",
3657                                  drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3658                                  ureq.dest_dsa_dns_name,
3659                                  win_errstr(werr)));
3660                 }
3661         }
3662
3663         /*
3664          * Work out how many links we can send in this chunk. The default is to
3665          * send all the links last, but there is a config option to send them
3666          * immediately, in the same chunk as their source object
3667          */
3668         if (!r->out.ctr->ctr6.more_data || repl_chunk->immediate_link_sync) {
3669                 link_count = getncchanges_chunk_links_pending(repl_chunk,
3670                                                               getnc_state);
3671                 link_count = MIN(link_count,
3672                                  getncchanges_chunk_max_links(repl_chunk));
3673         }
3674
3675         /* If we've got linked attributes to send, add them now */
3676         if (link_count > 0) {
3677                 struct la_for_sorting *la_sorted;
3678
3679                 /*
3680                  * Grab a chunk of linked attributes off the list and put them
3681                  * in sorted array, ready to send
3682                  */
3683                 werr = getncchanges_get_sorted_array(&getnc_state->la_list[getnc_state->la_idx],
3684                                                      link_count,
3685                                                      sam_ctx, getnc_state,
3686                                                      schema,
3687                                                      &la_sorted);
3688                 if (!W_ERROR_IS_OK(werr)) {
3689                         return werr;
3690                 }
3691
3692                 r->out.ctr->ctr6.linked_attributes_count = link_count;
3693                 r->out.ctr->ctr6.linked_attributes = talloc_array(r->out.ctr, struct drsuapi_DsReplicaLinkedAttribute, link_count);
3694                 if (r->out.ctr->ctr6.linked_attributes == NULL) {
3695                         DEBUG(0, ("Out of memory allocating %u linked attributes for output\n", link_count));
3696                         return WERR_NOT_ENOUGH_MEMORY;
3697                 }
3698
3699                 for (k = 0; k < link_count; k++) {
3700                         r->out.ctr->ctr6.linked_attributes[k] = *la_sorted[k].link;
3701                 }
3702
3703                 getnc_state->la_idx += link_count;
3704                 getnc_state->links_given += link_count;
3705
3706                 if (getnc_state->la_idx < getnc_state->la_count) {
3707                         r->out.ctr->ctr6.more_data = true;
3708                 } else {
3709
3710                         /*
3711                          * We've now sent all the links seen so far, so we can
3712                          * reset la_list back to an empty list again. Note that
3713                          * the steal means the linked attribute memory gets
3714                          * freed after this RPC message is sent on the wire.
3715                          */
3716                         talloc_steal(mem_ctx, getnc_state->la_list);
3717                         getnc_state->la_list = NULL;
3718                         getnc_state->la_idx = 0;
3719                         getnc_state->la_count = 0;
3720                 }
3721
3722                 TALLOC_FREE(la_sorted);
3723         }
3724
3725         if (req10->replica_flags & DRSUAPI_DRS_GET_NC_SIZE) {
3726                 /*
3727                  * TODO: This implementation is wrong
3728                  * we should find out the total number of
3729                  * objects and links in the whole naming context
3730                  * at the start of the cycle and return these
3731                  * values in each message.
3732                  *
3733                  * For now we keep our current strategy and return
3734                  * the number of objects for this cycle and the number
3735                  * of links we found so far during the cycle.
3736                  */
3737                 r->out.ctr->ctr6.nc_object_count = getnc_state->num_records;
3738                 r->out.ctr->ctr6.nc_linked_attributes_count = getnc_state->total_links;
3739         }
3740
3741         if (req10->extended_op != DRSUAPI_EXOP_NONE) {
3742                 r->out.ctr->ctr6.uptodateness_vector = NULL;
3743                 r->out.ctr->ctr6.nc_object_count = 0;
3744                 ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
3745         } else if (!r->out.ctr->ctr6.more_data) {
3746
3747                 /* this is the last response in the replication cycle */
3748                 r->out.ctr->ctr6.new_highwatermark = getnc_state->final_hwm;
3749                 r->out.ctr->ctr6.uptodateness_vector = talloc_move(mem_ctx,
3750                                                                    &getnc_state->final_udv);
3751
3752                 /*
3753                  * Free the state info stored for the replication cycle. Note
3754                  * that the RPC message we're sending contains links stored in
3755                  * getnc_state. mem_ctx is local to this RPC call, so the memory
3756                  * will get freed after the RPC message is sent on the wire.
3757                  *
3758                  * We must not do this for an EXOP, as that should not
3759                  * end the replication state, which is why that is
3760                  * checked first above.
3761                  */
3762                 talloc_steal(mem_ctx, getnc_state);
3763                 b_state->getncchanges_full_repl_state = NULL;
3764         } else {
3765                 ret = drsuapi_DsReplicaHighWaterMark_cmp(&r->out.ctr->ctr6.old_highwatermark,
3766                                                          &r->out.ctr->ctr6.new_highwatermark);
3767                 if (ret == 0) {
3768                         /*
3769                          * We need to make sure that we never return the
3770                          * same highwatermark within the same replication
3771                          * cycle more than once. Otherwise we cannot detect
3772                          * when the client uses an unexptected highwatermark.
3773                          *
3774                          * This is a HACK which is needed because our
3775                          * object ordering is wrong and set tmp_highest_usn
3776                          * to a value that is higher than what we already
3777                          * sent to the client (destination dsa).
3778                          */
3779                         r->out.ctr->ctr6.new_highwatermark.reserved_usn += 1;
3780                 }
3781
3782                 getnc_state->last_hwm = r->out.ctr->ctr6.new_highwatermark;
3783         }
3784
3785         TALLOC_FREE(repl_chunk);
3786
3787         DEBUG(r->out.ctr->ctr6.more_data?4:2,
3788               ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %u/%u) %u links (done %u/%u (as %s))\n",
3789                (unsigned long long)(req10->highwatermark.highest_usn+1),
3790                req10->replica_flags,
3791                drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
3792                r->out.ctr->ctr6.object_count,
3793                i, r->out.ctr->ctr6.more_data?getnc_state->num_records:i,
3794                r->out.ctr->ctr6.linked_attributes_count,
3795                getnc_state->links_given, getnc_state->total_links,
3796                dom_sid_string(mem_ctx, user_sid)));
3797
3798 #if 0
3799         if (!r->out.ctr->ctr6.more_data && req10->extended_op != DRSUAPI_EXOP_NONE) {
3800                 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);
3801         }
3802 #endif
3803
3804         return WERR_OK;
3805 }
3806