The branch, master has been updated via a8798d8 s4/drepl_fsmo: Add an CR so that message is visible in the logs via d4481be s4/getnc_exop: Initial implementation of a testsuite for GetNCChanges extended opeartion handling via 647827d s4/getncchanges: Pre-mark extended requests as success in case a sub-function "forget" to do this via 55916e2 s4/getncchanges: Implement placeholder for handling ex-op collection of objects via 88a9f79 s4/getncchanges: Move the code that collects objects into separate function via 41496e7 s4/getncchanges: Don't mask Extended operation result - callers need it via 232a8df s4/getncchanges: Fail extended request rather than failing whole request from f99d6f0 s3-printing: make cups_pull_comment_location() work again.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit a8798d8bce9a191ccd6e0692c75e38ffadba1011 Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed May 11 21:05:42 2011 +0300 s4/drepl_fsmo: Add an CR so that message is visible in the logs Autobuild-User: Kamen Mazdrashki <kame...@samba.org> Autobuild-Date: Wed May 11 21:03:59 CEST 2011 on sn-devel-104 commit d4481be95c0c22ab8dd66edfdd82d1e9312b137d Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed May 11 21:04:54 2011 +0300 s4/getnc_exop: Initial implementation of a testsuite for GetNCChanges extended opeartion handling commit 647827d09ccba7bcfa6454119afc22de96397128 Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed May 11 21:09:34 2011 +0300 s4/getncchanges: Pre-mark extended requests as success in case a sub-function "forget" to do this commit 55916e273f0853084f5bc2171eefc86af69e27dc Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed May 11 20:49:42 2011 +0300 s4/getncchanges: Implement placeholder for handling ex-op collection of objects Right now it is solely based on function that handles objects in normal DsGetNCChanges calls. commit 88a9f793aa3cc7d7be073eef618fded87420f0d3 Author: Kamen Mazdrashki <kame...@samba.org> Date: Wed May 11 20:48:53 2011 +0300 s4/getncchanges: Move the code that collects objects into separate function commit 41496e782df028a0fc8619aed6ca7a1173750677 Author: Kamen Mazdrashki <kame...@samba.org> Date: Mon May 9 20:11:22 2011 +0300 s4/getncchanges: Don't mask Extended operation result - callers need it commit 232a8dfb060dcac27801b51b9576d76fb2f93655 Author: Kamen Mazdrashki <kame...@samba.org> Date: Mon May 9 20:09:49 2011 +0300 s4/getncchanges: Fail extended request rather than failing whole request in case that destination_dsa_guid is not valid ----------------------------------------------------------------------- Summary of changes: source4/dsdb/repl/drepl_fsmo.c | 2 +- source4/rpc_server/drsuapi/getncchanges.c | 168 ++++++++++++++++++++--------- source4/torture/drs/python/getnc_exop.py | 136 +++++++++++++++++++++++ 3 files changed, 252 insertions(+), 54 deletions(-) create mode 100644 source4/torture/drs/python/getnc_exop.py Changeset truncated at 500 lines: diff --git a/source4/dsdb/repl/drepl_fsmo.c b/source4/dsdb/repl/drepl_fsmo.c index f8f4769..db63853 100644 --- a/source4/dsdb/repl/drepl_fsmo.c +++ b/source4/dsdb/repl/drepl_fsmo.c @@ -111,7 +111,7 @@ NTSTATUS drepl_take_FSMO_role(struct irpc_message *msg, if (fsmo_master_equal(ntds_dn, role_owner_dn) || (extended_op == DRSUAPI_EXOP_NONE)) { - DEBUG(0,("FSMO role check failed for DN %s and owner %s ", + DEBUG(0,("FSMO role check failed for DN %s and owner %s \n", ldb_dn_get_linearized(fsmo_role_dn), ldb_dn_get_linearized(role_owner_dn))); r->out.result = WERR_OK; diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c index 8ae5368..0c196aa 100644 --- a/source4/rpc_server/drsuapi/getncchanges.c +++ b/source4/rpc_server/drsuapi/getncchanges.c @@ -993,12 +993,14 @@ static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state, msg->dn = drs_ObjectIdentifier_to_dn(msg, ldb, req10->naming_context); W_ERROR_HAVE_NO_MEMORY(msg->dn); + /* TODO: make sure ntds_dn is a valid nTDSDSA object */ ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, &ntds_dn); if (ret != LDB_SUCCESS) { DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n", GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb))); talloc_free(msg); - return WERR_DS_DRA_INTERNAL_ERROR; + ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER; + return WERR_OK; } ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn)); @@ -1161,6 +1163,91 @@ getncchanges_map_req8(TALLOC_CTX *mem_ctx, } +/** + * Collects object for normal replication cycle. + */ +static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state, + TALLOC_CTX *mem_ctx, + struct drsuapi_DsGetNCChangesRequest10 *req10, + struct ldb_dn *search_dn, + const char *extra_filter, + struct ldb_result **search_res) +{ + int ret; + char* search_filter; + enum ldb_scope scope = LDB_SCOPE_SUBTREE; + //const char *extra_filter; + struct drsuapi_getncchanges_state *getnc_state = b_state->getncchanges_state; + const char *attrs[] = { "uSNChanged", + "objectGUID" , + NULL }; + + if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ || + req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) { + scope = LDB_SCOPE_BASE; + } + + //extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter"); + + //getnc_state->min_usn = req10->highwatermark.highest_usn; + + /* Construct response. */ + search_filter = talloc_asprintf(mem_ctx, + "(uSNChanged>=%llu)", + (unsigned long long)(getnc_state->min_usn+1)); + + if (extra_filter) { + search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter); + } + + if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) { + search_filter = talloc_asprintf(mem_ctx, + "(&%s(isCriticalSystemObject=TRUE))", + search_filter); + } + + if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) { + scope = LDB_SCOPE_BASE; + } + + if (!search_dn) { + search_dn = getnc_state->ncRoot_dn; + } + + DEBUG(2,(__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, search_res, + search_dn, scope, attrs, + search_filter); + if (ret != LDB_SUCCESS) { + return WERR_DS_DRA_INTERNAL_ERROR; + } + + return WERR_OK; +} + +/** + * Collects object for normal replication cycle. + */ +static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state, + TALLOC_CTX *mem_ctx, + struct drsuapi_DsGetNCChangesRequest10 *req10, + struct drsuapi_DsGetNCChangesCtr6 *ctr6, + struct ldb_dn *search_dn, + const char *extra_filter, + struct ldb_result **search_res) +{ + /* we have nothing to do in case of ex-op failure */ + if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) { + return WERR_OK; + } + + /* TODO: implement extended op specific collection + * of objects. Right now we just normal procedure + * for collecting objects */ + return getncchanges_collect_objects(b_state, mem_ctx, req10, search_dn, extra_filter, search_res); +} + /* drsuapi_DsGetNCChanges @@ -1177,9 +1264,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ struct drsuapi_DsReplicaObjectListItemEx **currentObject; NTSTATUS status; DATA_BLOB session_key; - const char *attrs[] = { "uSNChanged", - "objectGUID" , - NULL }; WERROR werr; struct dcesrv_handle *h; struct drsuapi_bind_state *b_state; @@ -1327,6 +1411,10 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ ldb_get_schema_basedn(b_state->sam_ctx)); getnc_state->is_schema_nc = (0 == ret); + if (req10->extended_op != DRSUAPI_EXOP_NONE) { + r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; + } + /* * This is the first replication cycle and it is * a good place to handle extended operations @@ -1391,64 +1479,39 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ Work out if this is the start of a new cycle */ if (getnc_state->guids == NULL) { - char* search_filter; - enum ldb_scope scope = LDB_SCOPE_SUBTREE; const char *extra_filter; - struct ldb_result *search_res; - - if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ || - req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) { - scope = LDB_SCOPE_BASE; - } + struct ldb_result *search_res = NULL; extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter"); getnc_state->min_usn = req10->highwatermark.highest_usn; - /* Construct response. */ - search_filter = talloc_asprintf(mem_ctx, - "(uSNChanged>=%llu)", - (unsigned long long)(getnc_state->min_usn+1)); - - if (extra_filter) { - search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter); - } - - if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) { - search_filter = talloc_asprintf(mem_ctx, - "(&%s(isCriticalSystemObject=TRUE))", - search_filter); - } - - if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) { - scope = LDB_SCOPE_BASE; - } - - if (!search_dn) { - search_dn = getnc_state->ncRoot_dn; - } - - DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n", - ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter)); - ret = drsuapi_search_with_extended_dn(sam_ctx, getnc_state, &search_res, - search_dn, scope, attrs, - search_filter); - if (ret != LDB_SUCCESS) { - return WERR_DS_DRA_INTERNAL_ERROR; - } - - if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) { - TYPESAFE_QSORT(search_res->msgs, - search_res->count, - site_res_cmp_parent_order); + if (req10->extended_op == DRSUAPI_EXOP_NONE) { + werr = getncchanges_collect_objects(b_state, mem_ctx, req10, + search_dn, extra_filter, + &search_res); } else { - TYPESAFE_QSORT(search_res->msgs, - search_res->count, - site_res_cmp_usn_order); + werr = getncchanges_collect_objects_exop(b_state, mem_ctx, req10, + &r->out.ctr->ctr6, + search_dn, extra_filter, + &search_res); + } + W_ERROR_NOT_OK_RETURN(werr); + + if (search_res) { + if (req10->replica_flags & DRSUAPI_DRS_GET_ANC) { + TYPESAFE_QSORT(search_res->msgs, + search_res->count, + site_res_cmp_parent_order); + } else { + TYPESAFE_QSORT(search_res->msgs, + search_res->count, + site_res_cmp_usn_order); + } } /* extract out the GUIDs list */ - getnc_state->num_records = search_res->count; + getnc_state->num_records = search_res ? search_res->count : 0; getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records); W_ERROR_HAVE_NO_MEMORY(getnc_state->guids); @@ -1705,7 +1768,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_ r->out.ctr->ctr6.uptodateness_vector = NULL; r->out.ctr->ctr6.nc_object_count = 0; ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark); - r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS; } DEBUG(r->out.ctr->ctr6.more_data?4:2, diff --git a/source4/torture/drs/python/getnc_exop.py b/source4/torture/drs/python/getnc_exop.py new file mode 100644 index 0000000..3aeb7e0 --- /dev/null +++ b/source4/torture/drs/python/getnc_exop.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Tests various schema replication scenarios +# +# Copyright (C) Kamen Mazdrashki <kame...@samba.org> 2011 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# +# Usage: +# export DC1=dc1_dns_name +# export DC2=dc2_dns_name +# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun +# PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN getnc_exop -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD" +# + +import drs_base +import samba.tests + +from ldb import SCOPE_BASE + +from samba.dcerpc import drsuapi, misc, drsblobs +from samba.drs_utils import drs_DsBind + + +class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase): + """Intended as a semi-black box test case for DsGetNCChanges + implementation for extended operations. It should be testing + how DsGetNCChanges handles different input params (mostly invalid). + Final goal is to make DsGetNCChanges as binary compatible to + Windows implementation as possible""" + + def setUp(self): + super(DrsReplicaSyncTestCase, self).setUp() + + def tearDown(self): + super(DrsReplicaSyncTestCase, self).tearDown() + + def _exop_req8(self, dest_dsa, invocation_id, nc_dn_str, exop): + req8 = drsuapi.DsGetNCChangesRequest8() + + req8.destination_dsa_guid = misc.GUID(dest_dsa) + req8.source_dsa_invocation_id = misc.GUID(invocation_id) + req8.naming_context = drsuapi.DsReplicaObjectIdentifier() + req8.naming_context.dn = unicode(nc_dn_str) + req8.highwatermark = drsuapi.DsReplicaHighWaterMark() + req8.highwatermark.tmp_highest_usn = 0 + req8.highwatermark.reserved_usn = 0 + req8.highwatermark.highest_usn = 0 + req8.uptodateness_vector = None + req8.replica_flags = 0 + req8.max_object_count = 0 + req8.max_ndr_size = 402116 + req8.extended_op = exop + req8.fsmo_info = 0 + req8.partial_attribute_set = None + req8.partial_attribute_set_ex = None + req8.mapping_ctr.num_mappings = 0 + req8.mapping_ctr.mappings = None + + return req8 + + def _ds_bind(self, server_name): + binding_str = "ncacn_ip_tcp:%s[print,seal]" % server_name + + drs = drsuapi.drsuapi(binding_str, self.get_loadparm(), self.get_credentials()) + (drs_handle, supported_extensions) = drs_DsBind(drs) + return (drs, drs_handle) + + def _determine_fSMORoleOwner(self, fsmo_obj_dn): + """Returns (owner, not_owner) pair where: + owner: dns name for FSMO owner + not_owner: dns name for DC not owning the FSMO""" + res = self.ldb_dc1.search(fsmo_obj_dn, + scope=SCOPE_BASE, attrs=["fSMORoleOwner"]) + assert len(res) == 1, "Only one fSMORoleOwner value expected for %s!"%fsmo_obj_dn + fsmo_owner = res[0]["fSMORoleOwner"][0] + if fsmo_owner == self.info_dc1["dsServiceName"][0]: + return (self.dnsname_dc1, self.dnsname_dc2) + return (self.dnsname_dc2, self.dnsname_dc1) + + def _check_exop_failed(self, ctr6, expected_failure): + c = drsuapi.DsGetNCChangesCtr6() + self.assertEqual(ctr6.extended_ret, expected_failure) + self.assertEqual(ctr6.object_count, 0) + self.assertEqual(ctr6.first_object, None) + self.aserrtEqual(ctr6.more_data, False) + self.assertEqual(ctr6.nc_object_count, 0) + self.assertEqual(ctr6.nc_linked_attributes_count, 0) + self.assertEqual(ctr6.linked_attributes_count, 0) + self.assertEqual(ctr6.linked_attributes, None) + self.assertEqual(ctr6.drs_error, 0) + + def test_FSMONotOwner(self): + """Test role transfer with against DC not owner of the role""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner_dc, fsmo_not_owner_dc) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=fsmo_dn, + exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE) + + (drs, drs_handle) = self._ds_bind(fsmo_not_owner_dc) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER) + + def test_InvalidDestDSA(self): + """Test role transfer with invalid destination DSA guid""" + fsmo_dn = self.ldb_dc1.get_schema_basedn() + (fsmo_owner_dc, fsmo_not_owner_dc) = self._determine_fSMORoleOwner(fsmo_dn) + + req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef", + invocation_id=self.ldb_dc1.get_invocation_id(), + nc_dn_str=self.ldb_dc1.get_schema_basedn(), + exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE) + + (drs, drs_handle) = self._ds_bind(fsmo_owner_dc) + (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) + self.assertEqual(level, 6, "Expected level 6 response!") + #ctr = drsuapi.DsGetNCChangesCtr6() + self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER) -- Samba Shared Repository