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