diff --git a/Changelog.txt b/Changelog.txt index b032439..26d4b7d 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,5 +1,6 @@ Version 2.14 - Added support for Windows Server 2016 ntds.dit encryption. +- Added support for replication with renamed domains. - Added support for reading security descriptors (ACLs) from both ntds.dit files and DRS-R. - Added support for the AdminCount attribute. - Updated the forked ManagedEsent source codes to version 1.9.3.3. diff --git a/Src/DSInternals.PowerShell/DSInternals.psd1 b/Src/DSInternals.PowerShell/DSInternals.psd1 index e1c001a..ffe244a 100644 --- a/Src/DSInternals.PowerShell/DSInternals.psd1 +++ b/Src/DSInternals.PowerShell/DSInternals.psd1 @@ -115,6 +115,7 @@ PrivateData = @{ # ReleaseNotes of this module ReleaseNotes = @" - Added support for Windows Server 2016 ntds.dit encryption. +- Added support for replication with renamed domains. - Added support for reading security descriptors (ACLs) from both ntds.dit files and DRS-R. - Added support for the AdminCount attribute. "@ diff --git a/Src/DSInternals.Replication.Interop/DrsConnection.cpp b/Src/DSInternals.Replication.Interop/DrsConnection.cpp index 539759d..7183aff 100644 --- a/Src/DSInternals.Replication.Interop/DrsConnection.cpp +++ b/Src/DSInternals.Replication.Interop/DrsConnection.cpp @@ -24,24 +24,19 @@ namespace DSInternals : SafeHandleZeroOrMinusOneIsInvalid(true) { this->_clientDsa = clientDsa; + this->_serverReplEpoch = DrsConnection::defaultReplEpoch; // Register the RetrieveSessionKey as RCP security callback. Mind the delegate lifecycle. this->_securityCallback = gcnew SecurityCallback(this, &DrsConnection::RetrieveSessionKey); - RPC_STATUS status1 = RpcBindingSetOption(rpcHandle.ToPointer(), RPC_C_OPT_SECURITY_CALLBACK, (ULONG_PTR)Marshal::GetFunctionPointerForDelegate(this->_securityCallback).ToPointer()); + RPC_STATUS status = RpcBindingSetOption(rpcHandle.ToPointer(), RPC_C_OPT_SECURITY_CALLBACK, (ULONG_PTR)Marshal::GetFunctionPointerForDelegate(this->_securityCallback).ToPointer()); - UUID clientDsaUuid = RpcTypeConverter::ToUUID(clientDsa); - auto clientInfo = CreateClientInfo(); - DRS_EXTENSIONS *genericServerInfo = nullptr; - DRS_HANDLE drsHandle = nullptr; - ULONG result = IDL_DRSBind_NoSEH(rpcHandle.ToPointer(), &clientDsaUuid, (DRS_EXTENSIONS*)clientInfo.get(), &genericServerInfo, &drsHandle); - Validator::AssertSuccess((Win32ErrorCode)result); - // Store the DRS handle - this->SetHandle((IntPtr)drsHandle); - // Prevent memory leak by storing the genericServerInfo in midl_ptr - auto genericServerInfoSafePtr = midl_ptr(genericServerInfo); - // TODO: Parse the server info - DRS_EXTENSIONS_INT serverInfo = DRS_EXTENSIONS_INT(genericServerInfo); - this->_serverSiteObjectGuid = RpcTypeConverter::ToGuid(serverInfo.siteObjGuid); + this->Bind(rpcHandle); + if (this->_serverReplEpoch != DrsConnection::defaultReplEpoch) + { + // The domain must have been renamed, so we need to rebind with the proper dwReplEpoch. + this->ReleaseHandle(); + this->Bind(rpcHandle); + } } DrsConnection::DrsConnection(IntPtr preexistingDrssHandle, bool ownsHandle) @@ -55,6 +50,30 @@ namespace DSInternals { } + void DrsConnection::Bind(IntPtr rpcHandle) + { + // Init binding parameters + UUID clientDsaUuid = RpcTypeConverter::ToUUID(this->_clientDsa); + auto clientInfo = this->CreateClientInfo(); + DRS_EXTENSIONS *genericServerInfo = nullptr; + DRS_HANDLE drsHandle = nullptr; + + // Bind + ULONG result = IDL_DRSBind_NoSEH(rpcHandle.ToPointer(), &clientDsaUuid, (DRS_EXTENSIONS*)clientInfo.get(), &genericServerInfo, &drsHandle); + Validator::AssertSuccess((Win32ErrorCode)result); + + // Prevent memory leak by storing the genericServerInfo in midl_ptr + auto genericServerInfoSafePtr = midl_ptr(genericServerInfo); + + // Store the DRS handle + this->SetHandle((IntPtr)drsHandle); + + // Parse the server info + DRS_EXTENSIONS_INT serverInfo = DRS_EXTENSIONS_INT(genericServerInfo); + this->_serverSiteObjectGuid = RpcTypeConverter::ToGuid(serverInfo.siteObjGuid); + this->_serverReplEpoch = serverInfo.dwReplEpoch; + } + array^ DrsConnection::SessionKey::get() { return this->_sessionKey; @@ -70,6 +89,7 @@ namespace DSInternals clientInfo->dwFlags = DRS_EXT::ALL_EXT; clientInfo->dwFlagsExt = DRS_EXT2::DRS_EXT_LH_BETA2; clientInfo->dwExtCaps = DRS_EXT2::DRS_EXT_LH_BETA2 | DRS_EXT2::DRS_EXT_RECYCLE_BIN; + clientInfo->dwReplEpoch = this->_serverReplEpoch; return clientInfo; } diff --git a/Src/DSInternals.Replication.Interop/DrsConnection.h b/Src/DSInternals.Replication.Interop/DrsConnection.h index c3778d5..b3418ac 100644 --- a/Src/DSInternals.Replication.Interop/DrsConnection.h +++ b/Src/DSInternals.Replication.Interop/DrsConnection.h @@ -24,10 +24,12 @@ namespace DSInternals array^ _sessionKey; Guid _clientDsa; Guid _serverSiteObjectGuid; + DWORD _serverReplEpoch; SecurityCallback^ _securityCallback; static const size_t defaultMaxObjects = 1000; // 8MB static const size_t defaultMaxBytes = 8 * 1024 * 1024; + static const DWORD defaultReplEpoch = 0; public: DrsConnection(IntPtr rpcHandle, Guid clientDsa); DrsConnection(IntPtr preexistingDrssHandle, bool ownsHandle); @@ -51,17 +53,18 @@ namespace DSInternals virtual bool ReleaseHandle() override; private: DrsConnection(); + void Bind(IntPtr rpcHandle); midl_ptr GetNCChanges(midl_ptr &&request); midl_ptr CrackNames(midl_ptr &&request); String^ ResolveName(String^ name, DS_NAME_FORMAT formatOffered, DS_NAME_FORMAT formatDesired); String^ ResolveName(midl_ptr &&request); + midl_ptr CreateClientInfo(); midl_ptr CreateReplicateAllRequest(ReplicationCookie^ cookie, array^ partialAttributeSet, ULONG maxBytes, ULONG maxObjects); midl_ptr CreateReplicateSingleRequest(String^ distinguishedName, array^ partialAttributeSet); midl_ptr CreateReplicateSingleRequest(Guid objectGuid, array^ partialAttributeSet); midl_ptr CreateGenericReplicateRequest(midl_ptr &&dsName, array^ partialAttributeSet, ULONG maxBytes, ULONG maxObjects); void RetrieveSessionKey(void* rpcContext); static midl_ptr CreateNativePas(array^ partialAttributeSet); - static midl_ptr CreateClientInfo(); static array^ ReadValue(const ATTRVAL &value); static array^>^ ReadValues(const ATTRVALBLOCK &values); static ReplicaAttribute^ ReadAttribute(const ATTR &attribute);