An "Access Denied" exception is generated migrating users from Windows 2000 or 2003 domain to a Windows 2008 domain using DsAddSidHistory API and pass "SrcDomainController" & "SrcDomainCreds" as NULL

An "Access Denied" exception is generated migrating users from Windows 2000 or 2003 domain to a Windows 2008 domain using DsAddSidHistory API (msdn.microsoft.com/en-us/library/ms675918(VS.85).aspx) and pass "SrcDomainController" & "SrcDomainCreds" as NULL.

In general, you do not want to hard code administrator credentials into code. Not only is this a security risk, because you can figure out what those credentials are even without access to the source code, but this also makes it difficult to maintain your code, since you will have to recompile it whenever a change is made. Many API’s are capable of delegating the credentials of a user that is running the code to the destination domain. However, DsAddSidHistory() does not, by design, delegate the user’s credentials to the destination domain.

If we are binding to target domain using DsBind() before calling DsAddSidHistory() with "SrcDomainController" and "SrcDomainCreds" as NULL even after adhering all the requirements specified in msdn.microsoft.com/en-us/library/ms677982(VS.85).aspx DsAddSidHistroy an “Access Denied” error code value of 5.

The correct approach for using DsAddSidHistory(), if we do not want to pass the "SrcDomainController" and "SrcDomainCreds" explicitly would be to use the DsBindWithSpnEx() msdn.microsoft.com/en-us/library/ms675963(VS.85).aspx API. We need to also pass the target Domain Controller's SPN in the ServicePrincipalName parameter with the NTDSAPI_BIND_ALLOW_DELEGATION flag in the BindFlags parameter.

Below is a code snippet that can be used to reprodue the "Access Denied" error:

     dRet = DsBind(NULL,szTgtDomain,&hds);           
    if(hds)
    {                                               
        dRet=DsAddSidHistory(hds,
                             NULL,
                             szSrcDomain,
                             szSrcUser,
                             NULL,               //Passing NULL, since we do not want to hard code the admin  
                             NULL,               //credentials in the code 
                             szTgtDomain,
                             szTgtUser
                            );
    }

The follow code snippet illustrates how to properly setup your program to delegate the credentials of current process by calling the DsBindWithSpnEx API using the Service Principal Name (SPN) of the target domain controller.

     dRet = DsBindWithSpnEx(L"DestDC.DestDOM.LOCAL",        //Destination Domain controller’s FQDN
                           L"DestDOM.LOCAL",               //Destination Domain’s FQDN
                           NULL,                           // Use the calling processes credentials
                           L"HOST/DestDC.DestDOM.LOCAL",   //SPN for the destination Domain controller
                           NTDSAPI_BIND_ALLOW_DELEGATION,  //Flag for delegation
                           &hds                            // Bind handle to be used with DsAddSidHistory
                          );
    
    if(hds)
    {
        dRet=DsAddSidHistory(hds,
                             NULL,
                             szSrcDomain,
                             szSrcUser,
                             NULL,
                             NULL,
                             szTgtDomain,
                             szTgtUser
                            );
    }