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