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