4.1.15.3 Server Behavior of the IDL_DRSInterDomainMove Method
Informative summary of behavior: IDL_DRSInterDomainMove is used during a cross-NC move operation. This is a special object move operation because it involves moving an object from one DC into another. A normal move operation moves the object within one NC on one DC; a cross-NC move involves two DCs. IDL_DRSInterDomainMove is an intermediate step in the cross-NC move operation, which is initiated by an LDAP call. The IDL_DRSInterDomainMove call is done by the "source" DC to the "target" DC in order to move the object with all of its data from one NC replica into another.
Note IDL_DRSInterDomainMove transfers data that is normally not readable by the end user (such as password hashes and other secrets).
During the move, the ENTINF structure that contains the object data is constructed by the source DC and passed to the target DC. The target DC enforces certain constraints, transforms the data according to the processing rules, and then either creates the object in its NC replica or updates the existing object. For more information on cross-NC move operations, see [MS-ADTS] section 3.1.1.5.4.2.
-
ULONG IDL_DRSInterDomainMove( [in, ref] DRS_HANDLE hDrs, [in] DWORD dwInVersion, [in, ref, switch_is(dwInVersion)] DRS_MSG_MOVEREQ *pmsgIn, [out, ref] DWORD *pdwOutVersion, [out, ref, switch_is(*pdwOutVersion)] DRS_MSG_MOVEREPLY *pmsgOut) msgIn: DRS_MSG_MOVEREQ_V2 lastPrefixTableEntry: PrefixTableEntry prefixTable: PrefixTable dwErr: DWORD clientCreds: ClientAuthorizationInfo callerCreds: ClientAuthorizationInfo O: ENTINF existingObj: DSName attribute: ATTRTYP proxyEpoch: DWORD ValidateDRSInput(hDrs, 10) pdwOutVersion^ := 2 msgOut^.V2.win32error := ERROR_DS_GENERIC_ERROR msgOut^.V2.pAddedName := null if dwInVersion ≠ 2 then return ERROR_DS_DRA_INVALID_PARAMETER endif msgIn := pmsgIn^.V2 if msgIn.pExpectedTargetNC ≠ DefaultNC() then return ERROR_DS_DST_NC_MISMATCH endif if msgIn.PrefixTable.PrefixCount < 1 then return ERROR_SCHEMA_MISMATCH endif /* Remember last prefix table entry, and remove it from the prefix * table.*/ lastPrefixTableEntry := msgIn.PrefixTable.pPrefixEntry[msgIn.PrefixTable.PrefixCount] msgIn.PrefixTable.PrefixCount := msgIn.PrefixTable.PrefixCount-1 /* Perform a binary comparison of the value from the last * prefixTable entry with the SchemaInfo.*/ if lastPrefixTableEntry.oid ≠ SchemaInfo() then return ERROR_SCHEMA_MISMATCH endif prefixTable := AbstractPTFromConcretePT(msgIn.PrefixTable) /* Convert client creds into ClientAuthorizationInfo format. */ dwErr := AuthorizationInfoFromClientCredentials(msgIn.pClientCreds, clientCreds) if dwErr ≠ ERROR_SUCCESS then return dwErr endif /* Check that the caller (the "source" DC) is actually a DC by * checking Enterprise Domain Controllers SID in its token. */ callerCreds := GetCallerAuthorizationInfo() if not CheckGroupMembership(callerCreds, SidFromStringSid("S-1-5-9")) then return ERROR_DS_DRA_ACCESS_DENIED endif /* Validate input ENTINF. */ O := msgIn.pSrcObject^ if ADS_UF_SERVER_TRUST_ACCOUNT in ENTINF_GetValue( O, userAccountControl, prefixTable) or ADS_UF_INTERDOMAIN_TRUST_ACCOUNT in ENTINF_GetValue( O, userAccountControl, prefixTable) then /* Disallowed to move DC accounts and trust objects. */ return ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION endif existingObj := select one obj from all where (obj!distinguishedName = ENTINF_GetValue(O, distinguishedName, prefixTable)) if existingObj ≠ null and existingObj!objectGUID ≠ ENTINF_GetValue(O, objectGUID, prefixTable) then /* There's already an object with the same DN but different GUID.*/ return ERROR_DS_SRC_GUID_MISMATCH endif existingObj := select one obj from all where (obj!objectGUID = ENTINF_GetValue(O, objectGUID, prefixTable)) if existingObj ≠ null and existingObj!proxiedObjectName ≠ ENTINF_GetValue(O, proxiedObjectName, prefixTable) then /* There's already an object with the same guid, * but proxiedObjectName is different - not allowed. */ return ERROR_DS_EPOCH_MISMATCH endif if IsApplicationNC(GetObjectNC(O.pName^)) then return ERROR_DS_INTERNAL_FAILURE endif /* Scan through the ENTINF and throw away any attributes that are not * supposed to be moved. */ foreach attribute in ENTINF_EnumerateAttributes(O, prefixTable) if AttrIsBacklink(attribute) or AttrIsNonReplicated(attribute) or AttrIsConstructed(attribute) then ENTINF_SetValue(O, attribute, null, prefixTable) endif if attribute in { adminCount, badPasswordTime, badPwdCount, creationTime, distinguishedName, domainReplica, instanceType, isCriticalSystemObject, isDeleted, lastLogoff, lastLogon, lastLogonTimestamp, lockoutTime, logonCount, modifiedCount, modifiedCountAtLastProm, msDS-Cached-Membership, msDS-Cached-Membership-Time-Stamp, msDS-Site-Affinity, nextRid, nTSecurityDescriptor, objectCategory, operatorCount, primaryGroupID, proxiedObjectName, replPropertyMetaData, revision, rid, sAMAccountType, serverState, subRefs, systemFlags, uASCompat, uSNChanged, uSNCreated, uSNDSALastObjRemoved, uSNLastObjRem, whenChanged, whenCreated} then ENTINF_SetValue(O, attribute, null, prefixTable) endif endfor if ENTINF_GetValue(O, userAccountControl, prefixTable) ≠ null then /* Reset lockout bit. */ ENTINF_SetValue(O, userAccountControl, ENTINF_GetValue(O, userAccountControl) - {ADS_UF_LOCKOUT}, prefixTable) endif if ENTINF_GetValue(O, pwdLastSet, prefixTable) ≠ null and ENTINF_GetValue(O, pwdLastSet, prefixTable) ≠ 0 then /* If pwdLastSet is set to non-zero, then change it to -1. */ ENTINF_SetValue(O, pwdLastSet, (LONGLONG)-1, prefixTable) endif /* Append objectSid to sIDHistory. */ ENTINF_SetValue(O, sidHistory, ENTINF_GetValue(O, sidHistory, prefixTable) + {ENTINF_GetValue(O, objectSid, prefixTable)}) /* Compute the new proxiedObjectName value. */ if ENTINF_GetValue(O, proxiedObjectName, prefixTable) ≠ null and GetProxyType(ENTINF_GetValue(O, proxiedObjectName)) = PROXY_TYPE_MOVED_OBJECT then /* There's already a valid proxiedObjectName on the object, * so just increment the epoch value. */ proxyEpoch := GetProxyEpoch(ENTINF_GetValue(O, proxiedObjectName, prefixTable))+1 else /* No valid proxiedObjectName, so start a new one. */ proxyEpoch := 1 endif /* Stamp the new proxiedObjectName value into ENTINF. */ ENTINF_SetValue(O, proxiedObjectName, MakeProxyValue(msgIn.pSrcNC^, PROXY_TYPE_MOVED_OBJECT, proxyEpoch), prefixTable) if existingObj ≠ null then /* Purge existing object, it is about to be overwritten. */ Expunge(existingObj) endif ImpersonateAuthorizationInfo(clientCreds) O.pName := msgIn.pDstName dwErr := PerformAddOperation( O, msgOut^.V2.pAddedName^, AbstractPTFromConcretePT(msgIn.PrefixTable), TRUE) RevertToSelf() msgOut^.V2.win32error := dwErr return dwErr