s4-drs: fixed the NC in the getncchanges RID alloc reply
[ira/wip.git] / source4 / rpc_server / drsuapi / getncchanges.c
index 8ff5de5dc030f12dec66632c1143e636f224dcfc..d0ce8198b8d2df61f21e82215054a18f1894689f 100644 (file)
@@ -246,10 +246,7 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
        struct drsuapi_DsReplicaLinkedAttribute *la;
        bool active;
        NTSTATUS status;
-       struct ldb_message_element val_el;
-       struct ldb_val v;
        WERROR werr;
-       struct drsuapi_DsReplicaAttribute drs;
 
        (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
        W_ERROR_HAVE_NO_MEMORY(*la_list);
@@ -259,7 +256,7 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
        la->identifier = get_object_identifier(*la_list, msg);
        W_ERROR_HAVE_NO_MEMORY(la->identifier);
 
-       active = ldb_dn_get_extended_component(dsdb_dn->dn, "DELETED")?false:true;
+       active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
 
        la->attid = sa->attributeID_id;
        la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
@@ -285,24 +282,9 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
                return ntstatus_to_werror(status);
        }
 
-       /* we need a message_element with just one value in it */
-       v = data_blob_string_const(dsdb_dn_get_extended_linearized(*la_list, dsdb_dn, 1));
-
-       val_el.name = sa->lDAPDisplayName;
-       val_el.values = &v;
-       val_el.num_values = 1;
-
-       werr = sa->syntax->ldb_to_drsuapi(sam_ctx, schema, sa, &val_el, *la_list, &drs);
+       werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
        W_ERROR_NOT_OK_RETURN(werr);
 
-       if (drs.value_ctr.num_values != 1) {
-               DEBUG(1,(__location__ ": Failed to build DRS blob for linked attribute %s in %s\n",
-                        sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
-               return WERR_DS_DRA_INTERNAL_ERROR;
-       }
-
-       la->value.blob = drs.value_ctr.values[0].blob;
-
        (*la_count)++;
        return WERR_OK;
 }
@@ -485,6 +467,74 @@ static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
        return WERR_OK;
 }
 
+
+/* comparison function for linked attributes - see CompareLinks() in
+ * MS-DRSR section 4.1.10.5.17 */
+static int linked_attribute_compare(const struct drsuapi_DsReplicaLinkedAttribute *la1,
+                                   const struct drsuapi_DsReplicaLinkedAttribute *la2,
+                                   struct ldb_context *sam_ctx)
+{
+       int c;
+       WERROR werr;
+       TALLOC_CTX *tmp_ctx;
+       const struct dsdb_schema *schema;
+       const struct dsdb_attribute *schema_attrib;
+       struct dsdb_dn *dn1, *dn2;
+       struct GUID guid1, guid2;
+       NTSTATUS status;
+
+       c = GUID_compare(&la1->identifier->guid,
+                        &la2->identifier->guid);
+       if (c != 0) return c;
+
+       if (la1->attid != la2->attid) {
+               return la1->attid < la2->attid? -1:1;
+       }
+
+       if ((la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
+           (la2->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
+               return (la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
+       }
+
+       /* we need to get the target GUIDs to compare */
+       tmp_ctx = talloc_new(sam_ctx);
+
+       schema = dsdb_get_schema(sam_ctx);
+       schema_attrib = dsdb_attribute_by_attributeID_id(schema, la1->attid);
+
+       werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la1->value.blob, &dn1);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0,(__location__ ": Bad la1 blob in sort\n"));
+               talloc_free(tmp_ctx);
+               return 0;
+       }
+
+       werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la2->value.blob, &dn2);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0,(__location__ ": Bad la2 blob in sort\n"));
+               talloc_free(tmp_ctx);
+               return 0;
+       }
+
+       status = dsdb_get_extended_dn_guid(dn1->dn, &guid1, "GUID");
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,(__location__ ": Bad la1 guid in sort\n"));
+               talloc_free(tmp_ctx);
+               return 0;
+       }
+       status = dsdb_get_extended_dn_guid(dn2->dn, &guid2, "GUID");
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,(__location__ ": Bad la2 guid in sort\n"));
+               talloc_free(tmp_ctx);
+               return 0;
+       }
+
+       talloc_free(tmp_ctx);
+
+       return GUID_compare(&guid1, &guid2);
+}
+
+
 /*
   sort the objects we send by tree order
  */
@@ -514,6 +564,100 @@ static int site_res_cmp_usn_order(const struct ldb_message **m1, const struct ld
 }
 
 
+/*
+  handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
+ */
+static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct drsuapi_DsGetNCChangesRequest8 *req8,
+                                    struct drsuapi_DsGetNCChangesCtr6 *ctr6)
+{
+       struct ldb_dn *rid_manager_dn, *fsmo_role_dn, *req_dn;
+       int ret;
+       struct ldb_context *ldb = b_state->sam_ctx;
+       struct ldb_result *ext_res;
+       struct ldb_dn *base_dn;
+       struct dsdb_fsmo_extended_op *exop;
+
+       /*
+         steps:
+           - verify that the DN being asked for is the RID Manager DN
+           - verify that we are the RID Manager
+        */
+
+       /* work out who is the RID Manager */
+       ret = samdb_rid_manager_dn(ldb, mem_ctx, &rid_manager_dn);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       req_dn = ldb_dn_new(mem_ctx, ldb, req8->naming_context->dn);
+       if (!req_dn ||
+           !ldb_dn_validate(req_dn) ||
+           ldb_dn_compare(req_dn, rid_manager_dn) != 0) {
+               /* that isn't the RID Manager DN */
+               DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n",
+                        req8->naming_context->dn));
+               ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
+               return WERR_OK;
+       }
+
+       /* find the DN of the RID Manager */
+       ret = samdb_reference_dn(ldb, mem_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed to find fSMORoleOwner in RID Manager object - %s\n",
+                        ldb_errstring(ldb)));
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
+               /* we're not the RID Manager - go away */
+               DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
+               ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
+               return WERR_OK;
+       }
+
+       exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
+       W_ERROR_HAVE_NO_MEMORY(exop);
+
+       exop->fsmo_info = req8->fsmo_info;
+       exop->destination_dsa_guid = req8->destination_dsa_guid;
+
+       ret = ldb_transaction_start(ldb);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed transaction start - %s\n",
+                        ldb_errstring(ldb)));
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
+                        ldb_errstring(ldb)));
+               ldb_transaction_cancel(ldb);
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       ret = ldb_transaction_commit(ldb);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
+                        ldb_errstring(ldb)));
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       talloc_free(ext_res);
+
+       base_dn = samdb_base_dn(ldb);
+
+       DEBUG(2,("Allocated RID pool for server %s\n",
+                GUID_string(mem_ctx, &req8->destination_dsa_guid)));
+
+       return WERR_OK;
+}
+
+
+
 /* state of a partially completed getncchanges call */
 struct drsuapi_getncchanges_state {
        struct ldb_result *site_res;
@@ -559,6 +703,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        struct drsuapi_DsGetNCChangesRequest8 *req8;
        uint32_t options;
        uint32_t max_objects;
+       struct ldb_dn *search_dn = NULL;
 
        DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
        b_state = h->data;
@@ -619,6 +764,28 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                return werr;
        }
 
+       /* we don't yet support extended operations */
+       switch (req8->extended_op) {
+       case DRSUAPI_EXOP_NONE:
+               break;
+
+       case DRSUAPI_EXOP_FSMO_RID_ALLOC:
+               werr = getncchanges_rid_alloc(b_state, mem_ctx, req8, &r->out.ctr->ctr6);
+               W_ERROR_NOT_OK_RETURN(werr);
+               search_dn = samdb_base_dn(b_state->sam_ctx);
+               break;
+
+       case DRSUAPI_EXOP_FSMO_REQ_ROLE:
+       case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
+       case DRSUAPI_EXOP_FSMO_REQ_PDC:
+       case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
+       case DRSUAPI_EXOP_REPL_OBJ:
+       case DRSUAPI_EXOP_REPL_SECRET:
+               DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
+                        (unsigned)req8->extended_op));
+               return WERR_DS_DRA_NOT_SUPPORTED;
+       }
+
        getnc_state = b_state->getncchanges_state;
 
        /* see if a previous replication has been abandoned */
@@ -656,13 +823,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                return WERR_DS_DRA_INTERNAL_ERROR;              
        }
 
-       /* we don't yet support extended operations */
-       if (req8->extended_op != DRSUAPI_EXOP_NONE) {
-               DEBUG(0,(__location__ ": Request for DsGetNCChanges extended op 0x%x\n",
-                        (unsigned)req8->extended_op));
-               return WERR_DS_DRA_NOT_SUPPORTED;
-       }
-
        /* 
           TODO: MS-DRSR section 4.1.10.1.1
           Work out if this is the start of a new cycle */
@@ -695,10 +855,14 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                        scope = LDB_SCOPE_BASE;
                }
                
+               if (!search_dn) {
+                       search_dn = getnc_state->ncRoot_dn;
+               }
+
                DEBUG(1,(__location__ ": getncchanges on %s using filter %s\n",
                         ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
                ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, &getnc_state->site_res,
-                                                     getnc_state->ncRoot_dn, scope, attrs,
+                                                     search_dn, scope, attrs,
                                                      search_filter);
                if (ret != LDB_SUCCESS) {
                        return WERR_DS_DRA_INTERNAL_ERROR;
@@ -768,14 +932,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
 
                obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
 
-               uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
-               if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
-                       r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
-               }
-               if (uSN > getnc_state->highest_usn) {
-                       getnc_state->highest_usn = uSN;
-               }
-
                werr = get_nc_changes_build_object(obj, msg,
                                                   b_state->sam_ctx, getnc_state->ncRoot_dn, 
                                                   schema, &session_key, getnc_state->min_usn,
@@ -795,6 +951,14 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                        return werr;
                }
 
+               uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
+               if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
+                       r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
+               }
+               if (uSN > getnc_state->highest_usn) {
+                       getnc_state->highest_usn = uSN;
+               }
+
                if (obj->meta_data_ctr == NULL) {
                        DEBUG(0,(__location__ ": getncchanges skipping send of object %s\n",
                                 ldb_dn_get_linearized(msg->dn)));
@@ -846,6 +1010,11 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        } else {
                r->out.ctr->ctr6.linked_attributes_count = getnc_state->la_count;
                r->out.ctr->ctr6.linked_attributes = talloc_steal(mem_ctx, getnc_state->la_list);
+
+               ldb_qsort(r->out.ctr->ctr6.linked_attributes, r->out.ctr->ctr6.linked_attributes_count,
+                         sizeof(r->out.ctr->ctr6.linked_attributes[0]),
+                         b_state->sam_ctx, (ldb_qsort_cmp_fn_t)linked_attribute_compare);
+
                r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
                r->out.ctr->ctr6.uptodateness_vector->version = 2;
                r->out.ctr->ctr6.uptodateness_vector->reserved1 = 0;
@@ -864,6 +1033,12 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                b_state->getncchanges_state = NULL;
        }
 
+       if (req8->extended_op != DRSUAPI_EXOP_NONE) {
+               r->out.ctr->ctr6.uptodateness_vector = NULL;
+               r->out.ctr->ctr6.nc_object_count = 0;
+               ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
+       }
+
        DEBUG(r->out.ctr->ctr6.more_data?2:1,
              ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
               (unsigned long long)(req8->highwatermark.highest_usn+1),