The branch, v4-18-stable has been updated via 71fa86a3fbb VERSION: Disable GIT_SNAPSHOT for the 4.18.0rc2 release. via 0679a07af31 WHATSNEW: Add release notes for Samba 4.18.0rc2. via 46e0575931e tmp via 65077cd390f WHATSNEW: Add note about Azure AD cloud connect sync support via c9b7fd177d4 s4-drsuapi: Give an error that matches windows on destination_dsa_guid lookup failure via 68fcea19bd0 s4-drsuapi: Clarify role of drs_security_access_check_nc_root() via 262fef5acbf s4-rpc_server: Pre-check destination_dsa_guid in GetNCChanges for validity via 68edd5c1c7f s4-drsuapi: Use samdb_get_ntds_obj_by_guid() to find RODC in REPL_SECRET via dc7497c3a46 s4-dsdb: Require that the NTDS object is an nTDSDSA objectclass via 29a89f07aa7 s4-dsdb: Split samdb_get_ntds_obj_by_guid() out of samdb_is_rodc() via 613d9b75499 s4-rpc_server/drsuapi: Return correct error code for an invalid DN to EXOP_REPL_OBJ/EXOP_REPL_OBJ via 64df0963f8c s4-drs: Make drs_ObjectIdentifier_to_dn() safer and able to cope with DummyDN values via 84a952b01ee s4-dsdb: rework drs_ObjectIdentifier_to_dn() into drs_ObjectIdentifier_to_dn_and_nc_root() via 1a97e897f86 s4-rpc_server/drsuapi: Use dsdb_normalise_dn_and_find_nc_root() via feffb9ec5df s4-dsdb: Add dsdb_normalise_dn_and_find_nc_root() around dsdb_find_nc_root() via ab282dba376 s4-dsdb: Add better debugging to dsdb_objects_have_same_nc() via 855c11c4146 s4-dsdb: Make dsdb_find_nc_root() first try and use DSDB_CONTROL_CURRENT_PARTITION_OID via 92f56081291 s4-dsdb: Schedule SD propegation only after successful rename via f70fd3385f3 s4-selftest/drs: Confirm GetNCChanges REPL_SECRET works with a DummyDN and real GUID via 87ed6e23061 s4-selftest/drs: Confirm GetNCChanges full replication works with a DummyDN and real GUID via a40d3697e1a s4-selftest/drs: Confirm GetNCChanges REPL_OBJ works with a DummyDN and real GUID via 7712ef7288a s4-selftest/drs Allow re-run of DRS tests after failed cleanup via 11540d828f7 s4-selftest/drs Allow some DRS tests to operate against an IP via 501728cdcfe s4-selftest/drs Add test of expected return code for invaid DNs in GetNCChanges via d0c2305b35a s4-dsdb: Add tests of SamDB.get_nc_root() via af00a0df70a s3/lib: Prevent use after free of messaging_ctdb_fde_ev structs via f21236ac004 s3:auth: call wbcFreeMemory(info) in auth3_generate_session_info_pac() via 6e6913bcac2 WHATSNEW: add acl_xattr:security_acl_name option via 8b97aca0dee WHATSNEW 4.18: mention samba-tool dsacl delete via 1886a72d966 VERSION: Bump version up to 4.18.0rc2... from fbba9a24796 VERSION: Disable GIT_SNAPSHOT for the Samba 4.18.0rc1 release.
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-18-stable - Log ----------------------------------------------------------------- ----------------------------------------------------------------------- Summary of changes: VERSION | 2 +- WHATSNEW.txt | 49 +++- python/samba/tests/dsdb.py | 122 ++++++++++ source3/auth/auth_generic.c | 1 + source3/lib/messages_ctdb.c | 19 ++ source4/dsdb/common/dsdb_dn.c | 183 ++++++++++++++- source4/dsdb/common/util.c | 335 ++++++++++++++++++++++++++-- source4/dsdb/samdb/ldb_modules/descriptor.c | 134 +++++++---- source4/rpc_server/drsuapi/drsutil.c | 28 ++- source4/rpc_server/drsuapi/getncchanges.c | 182 ++++++++++++--- source4/rpc_server/drsuapi/updaterefs.c | 39 +++- source4/torture/drs/python/drs_base.py | 15 +- source4/torture/drs/python/getnc_exop.py | 169 +++++++++++++- source4/torture/drs/python/getncchanges.py | 52 ++++- source4/torture/drs/python/repl_move.py | 11 +- source4/torture/drs/python/repl_rodc.py | 46 ++++ source4/torture/drs/python/repl_schema.py | 9 +- 17 files changed, 1242 insertions(+), 154 deletions(-) Changeset truncated at 500 lines: diff --git a/VERSION b/VERSION index 97e56c10a35..d8f3b790191 100644 --- a/VERSION +++ b/VERSION @@ -87,7 +87,7 @@ SAMBA_VERSION_PRE_RELEASE= # e.g. SAMBA_VERSION_RC_RELEASE=1 # # -> "3.0.0rc1" # ######################################################## -SAMBA_VERSION_RC_RELEASE=1 +SAMBA_VERSION_RC_RELEASE=2 ######################################################## # To mark SVN snapshots this should be set to 'yes' # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index eb71f69fadc..66a7a80e963 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,12 +1,12 @@ Release Announcements ===================== -This is the first release candidate of Samba 4.18. This is *not* +This is the second release candidate of Samba 4.18. This is *not* intended for production environments and is designed for testing purposes only. Please report any defects via the Samba bug reporting system at https://bugzilla.samba.org/. -Samba 4.18 will be the next version of the Samba suite. +Samba 4.18 will be the next version of the Samba suite... UPGRADING @@ -65,6 +65,14 @@ already used it, the defaults have changed slightly. set it overrides --color for the purpose of the output diagram, but not for other output like error messages. +New samba-tool dsacl subcommand for deleting ACES +------------------------------------------------- + +The samba-tool dsacl tool can now delete entries in directory access +control lists. The interface for 'samba-tool dsacl delete' is similar +to that of 'samba-tool dsacl set', with the difference being that the +ACEs described by the --sddl argument are deleted rather than added. + No colour with NO_COLOR environment variable -------------------------------------------- @@ -82,6 +90,28 @@ which forces the trust account password to be changed at a specified domain controller. If the specified domain controller cannot be contacted the password change fails rather than trying other DCs. +New option to change the NT ACL default location +------------------------------------------------ + +Usually the NT ACLs are stored in the security.NTACL extended +attribute (xattr) of files and directories. The new +"acl_xattr:security_acl_name" option allows to redefine the default +location. The default "security.NTACL" is a protected location, which +means the content of the security.NTACL attribute is not accessible +from normal users outside of Samba. When this option is set to use a +user-defined value, e.g. user.NTACL then any user can potentially +access and overwrite this information. The module prevents access to +this xattr over SMB, but the xattr may still be accessed by other +means (eg local access, SSH, NFS). This option must only be used when +this consequence is clearly understood and when specific precautions +are taken to avoid compromising the ACL content. + +Azure Active Directory / Office365 synchronisation improvements +-------------------------------------------------------------- + +Use of the Azure AD Connect cloud sync tool is now supported for +password hash synchronisation, allowing Samba AD Domains to synchronise +passwords with this popular cloud environment. REMOVED FEATURES ================ @@ -92,6 +122,21 @@ smb.conf changes Parameter Name Description Default -------------- ----------- ------- + acl_xattr:security_acl_name New security.NTACL + + +CHANGES SINCE 4.18.0rc1 +======================= + +o Andrew Bartlett <abart...@samba.org> + * BUG 10635: Office365 azure Password Sync not working. + +o Stefan Metzmacher <me...@samba.org> + * BUG 15286: auth3_generate_session_info_pac leaks wbcAuthUserInfo. + +o Noel Power <noel.po...@suse.com> + * BUG 15293: With clustering enabled samba-bgqd can core dump due to use + after free. KNOWN ISSUES diff --git a/python/samba/tests/dsdb.py b/python/samba/tests/dsdb.py index f4f7a705626..6c52994ece7 100644 --- a/python/samba/tests/dsdb.py +++ b/python/samba/tests/dsdb.py @@ -1029,6 +1029,128 @@ class DsdbTests(TestCase): str(part_dn) + "," + str(domain_dn)), self.samdb.normalize_dn_in_domain(part_dn)) +class DsdbNCRootTests(TestCase): + + def setUp(self): + super().setUp() + self.lp = samba.tests.env_loadparm() + self.creds = Credentials() + self.creds.guess(self.lp) + self.session = system_session() + self.samdb = SamDB(session_info=self.session, + credentials=self.creds, + lp=self.lp) + self.remote = False + + # These all use the local mode of operation inside + # dsdb_find_nc_root() using the partitions control + def test_dsdb_dn_nc_root_sid(self): + dom_sid = self.samdb.get_domain_sid() + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"<SID={dom_sid}>") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(domain_dn, nc_root) + + def test_dsdb_dn_nc_root_admin_sid(self): + dom_sid = self.samdb.get_domain_sid() + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"<SID={dom_sid}-500>") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(domain_dn, nc_root) + + def test_dsdb_dn_nc_root_users_container(self): + dom_sid = self.samdb.get_domain_sid() + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"CN=Users,{domain_dn}") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(domain_dn, nc_root) + + def test_dsdb_dn_nc_root_new_dn(self): + dom_sid = self.samdb.get_domain_sid() + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"CN=Xnotexisting,CN=Users,{domain_dn}") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(domain_dn, nc_root) + + def test_dsdb_dn_nc_root_new_dn_with_guid(self): + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"<GUID=828e3baf-fa02-4d82-ba5d-6f647dab5fd8>;CN=Xnotexisting,CN=Users,{domain_dn}") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(domain_dn, nc_root) + + def test_dsdb_dn_nc_root_guid(self): + ntds_guid = self.samdb.get_ntds_GUID() + configuration_dn = self.samdb.get_config_basedn() + dn = ldb.Dn(self.samdb, f"<GUID={ntds_guid}>") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(configuration_dn, nc_root) + + def test_dsdb_dn_nc_root_misleading_to_noexisting_guid(self): + ntds_guid = self.samdb.get_ntds_GUID() + configuration_dn = self.samdb.get_config_basedn() + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"<GUID={ntds_guid}>;CN=Xnotexisting,CN=Users,{domain_dn}") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(configuration_dn, nc_root) + + def test_dsdb_dn_nc_root_misleading_to_existing_guid(self): + ntds_guid = self.samdb.get_ntds_GUID() + configuration_dn = self.samdb.get_config_basedn() + domain_dn = ldb.Dn(self.samdb, self.samdb.domain_dn()) + dn = ldb.Dn(self.samdb, f"<GUID={ntds_guid}>;{domain_dn}") + try: + nc_root = self.samdb.get_nc_root(dn) + except ldb.LdbError as e: + (code, msg) = e.args + self.fail("Got unexpected exception %d - %s " + % (code, msg)) + self.assertEqual(configuration_dn, nc_root) + +class DsdbRemoteNCRootTests(DsdbNCRootTests): + def setUp(self): + super().setUp() + # Reconnect to the remote LDAP port + self.samdb = SamDB(url="ldap://%s" % samba.tests.env_get_var_value('SERVER'), + session_info=self.session, + credentials=self.get_credentials(), + lp=self.lp) + self.remote = True + class DsdbFullScanTests(TestCase): diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c index ff51307e43a..6c61eb4e827 100644 --- a/source3/auth/auth_generic.c +++ b/source3/auth/auth_generic.c @@ -143,6 +143,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, info->account_name, info->domain_name, info, &server_info); + wbcFreeMemory(info); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("make_server_info_wbcAuthUserInfo failed: %s\n", nt_errstr(status))); diff --git a/source3/lib/messages_ctdb.c b/source3/lib/messages_ctdb.c index 3e784bf7237..d55b53bf601 100644 --- a/source3/lib/messages_ctdb.c +++ b/source3/lib/messages_ctdb.c @@ -76,6 +76,21 @@ static int messaging_ctdb_recv( struct messaging_ctdb_context *global_ctdb_context; +static int global_ctdb_ctx_destructor(struct messaging_ctdb_context *ctx) +{ + if (ctx != NULL) { + struct messaging_ctdb_fde_ev *fde_ev = NULL; + for (fde_ev = ctx->fde_evs; + fde_ev != NULL; + fde_ev = fde_ev->next) { + if (fde_ev->ctx == ctx) { + fde_ev->ctx = NULL; + } + } + } + return 0; +} + int messaging_ctdb_init(const char *sockname, int timeout, uint64_t unique_id, void (*recv_cb)(struct tevent_context *ev, const uint8_t *msg, size_t msg_len, @@ -94,6 +109,10 @@ int messaging_ctdb_init(const char *sockname, int timeout, uint64_t unique_id, if (ctx == NULL) { return ENOMEM; } + + talloc_set_destructor(ctx, + global_ctdb_ctx_destructor); + ctx->recv_cb = recv_cb; ctx->recv_cb_private_data = private_data; diff --git a/source4/dsdb/common/dsdb_dn.c b/source4/dsdb/common/dsdb_dn.c index e348ab6aa94..c86848fd697 100644 --- a/source4/dsdb/common/dsdb_dn.c +++ b/source4/dsdb/common/dsdb_dn.c @@ -359,10 +359,12 @@ int dsdb_dn_string_comparison(struct ldb_context *ldb, void *mem_ctx, } /* - format a drsuapi_DsReplicaObjectIdentifier naming context as a string + * format a drsuapi_DsReplicaObjectIdentifier naming context as a string for debugging + * + * When forming a DN for DB access you must use drs_ObjectIdentifier_to_dn() */ -char *drs_ObjectIdentifier_to_string(TALLOC_CTX *mem_ctx, - struct drsuapi_DsReplicaObjectIdentifier *nc) +char *drs_ObjectIdentifier_to_debug_string(TALLOC_CTX *mem_ctx, + struct drsuapi_DsReplicaObjectIdentifier *nc) { char *ret = NULL; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); @@ -386,13 +388,172 @@ char *drs_ObjectIdentifier_to_string(TALLOC_CTX *mem_ctx, return ret; } -struct ldb_dn *drs_ObjectIdentifier_to_dn(TALLOC_CTX *mem_ctx, - struct ldb_context *ldb, - struct drsuapi_DsReplicaObjectIdentifier *nc) +/* + * Safely convert a drsuapi_DsReplicaObjectIdentifier into an LDB DN + * + * We need to have GUID and SID prority and not allow extended + * components in the DN. + * + * We must also totally honour the prority even if the string DN is not valid or able to parse as a DN. + */ +static struct ldb_dn *drs_ObjectIdentifier_to_dn(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + struct drsuapi_DsReplicaObjectIdentifier *nc) +{ + struct ldb_dn *new_dn = NULL; + + if (!GUID_all_zero(&nc->guid)) { + struct GUID_txt_buf buf; + char *guid = GUID_buf_string(&nc->guid, &buf); + + new_dn = ldb_dn_new_fmt(mem_ctx, + ldb, + "<GUID=%s>", + guid); + if (new_dn == NULL) { + DBG_ERR("Failed to prepare drs_ObjectIdentifier " + "GUID %s into a DN\n", + guid); + return NULL; + } + + return new_dn; + } + + if (nc->__ndr_size_sid != 0 && nc->sid.sid_rev_num != 0) { + struct dom_sid_buf buf; + char *sid = dom_sid_str_buf(&nc->sid, &buf); + + new_dn = ldb_dn_new_fmt(mem_ctx, + ldb, + "<SID=%s>", + sid); + if (new_dn == NULL) { + DBG_ERR("Failed to prepare drs_ObjectIdentifier " + "SID %s into a DN\n", + sid); + return NULL; + } + return new_dn; + } + + if (nc->__ndr_size_dn != 0 && nc->dn) { + int dn_comp_num = 0; + bool new_dn_valid = false; + + new_dn = ldb_dn_new(mem_ctx, ldb, nc->dn); + if (new_dn == NULL) { + /* Set to WARNING as this is user-controlled, don't print the value into the logs */ + DBG_WARNING("Failed to parse string DN in " + "drs_ObjectIdentifier into an LDB DN\n"); + return NULL; + } + + new_dn_valid = ldb_dn_validate(new_dn); + if (!new_dn_valid) { + /* + * Set to WARNING as this is user-controlled, + * but can print the value into the logs as it + * parsed a bit + */ + DBG_WARNING("Failed to validate string DN [%s] in " + "drs_ObjectIdentifier as an LDB DN\n", + ldb_dn_get_linearized(new_dn)); + return NULL; + } + + dn_comp_num = ldb_dn_get_comp_num(new_dn); + if (dn_comp_num <= 0) { + /* + * Set to WARNING as this is user-controlled, + * but can print the value into the logs as it + * parsed a bit + */ + DBG_WARNING("DN [%s] in drs_ObjectIdentifier " + "must have 1 or more components\n", + ldb_dn_get_linearized(new_dn)); + return NULL; + } + + if (ldb_dn_is_special(new_dn)) { + /* + * Set to WARNING as this is user-controlled, + * but can print the value into the logs as it + * parsed a bit + */ + DBG_WARNING("New string DN [%s] in " + "drs_ObjectIdentifier is a " + "special LDB DN\n", + ldb_dn_get_linearized(new_dn)); + return NULL; + } + + /* + * We want this just to be a string DN, extended + * components are manually handled above + */ + if (ldb_dn_has_extended(new_dn)) { + /* + * Set to WARNING as this is user-controlled, + * but can print the value into the logs as it + * parsed a bit + */ + DBG_WARNING("Refusing to parse New string DN [%s] in " + "drs_ObjectIdentifier as an " + "extended LDB DN " + "(GUIDs and SIDs should be in the " + ".guid and .sid IDL elelements, " + "not in the string\n", + ldb_dn_get_extended_linearized(mem_ctx, + new_dn, + 1)); + return NULL; + } + return new_dn; + } + + DBG_WARNING("Refusing to parse empty string DN " + "(and no GUID or SID) " + "drs_ObjectIdentifier into a empty " + "(eg RootDSE) LDB DN\n"); + return NULL; +} + +/* + * Safely convert a drsuapi_DsReplicaObjectIdentifier into a validated + * LDB DN of an existing DB entry, and/or find the NC root + * + * We need to have GUID and SID prority and not allow extended + * components in the DN. + * + * We must also totally honour the prority even if the string DN is + * not valid or able to parse as a DN. + * + * Finally, we must return the DN as found in the DB, as otherwise a + * subsequence ldb_dn_compare(dn, nc_root) will fail (as this is based + * on the string components). + */ +int drs_ObjectIdentifier_to_dn_and_nc_root(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + struct drsuapi_DsReplicaObjectIdentifier *nc, + struct ldb_dn **normalised_dn, + struct ldb_dn **nc_root) { - char *dn_string = drs_ObjectIdentifier_to_string(mem_ctx, nc); - struct ldb_dn *new_dn; - new_dn = ldb_dn_new(mem_ctx, ldb, dn_string); - talloc_free(dn_string); - return new_dn; + int ret; + struct ldb_dn *new_dn = NULL; + + new_dn = drs_ObjectIdentifier_to_dn(mem_ctx, + ldb, + nc); + if (new_dn == NULL) { + return LDB_ERR_INVALID_DN_SYNTAX; + } + + ret = dsdb_normalise_dn_and_find_nc_root(ldb, + mem_ctx, + new_dn, + normalised_dn, + nc_root); + TALLOC_FREE(new_dn); + return ret; } diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index f5de9d82cdf..55940227106 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -3550,9 +3550,49 @@ int drsuapi_DsReplicaCursor_compare(const struct drsuapi_DsReplicaCursor *c1, return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id); } +/* + * Return the NTDS object for a GUID, confirming it is in the + * configuration partition and a nTDSDSA object + */ +int samdb_get_ntds_obj_by_guid(TALLOC_CTX *mem_ctx, + struct ldb_context *sam_ctx, + const struct GUID *objectGUID, + const char **attrs, + struct ldb_message **msg) +{ + int ret; -- Samba Shared Repository