// Trust.cpp : Implementation of CTrustMgrApp and DLL registration. #include "stdafx.h" //#import "\bin\McsVarSetMin.tlb" no_namespace, named_guids #import "VarSet.tlb" no_namespace, named_guids rename("property", "aproperty") #include "TrustMgr.h" #include "Trust.h" #include "Common.hpp" #include "UString.hpp" #include "ResStr.h" #include "ErrDct.hpp" #include "EaLen.hpp" #include "LSAUtils.h" #include #include #include #include #include "ntsecapi.h" #include "SecPI.h" #include "cipher.hpp" #ifndef TRUST_ATTRIBUTE_FOREST_TRANSITIVE #define TRUST_ATTRIBUTE_FOREST_TRANSITIVE 0x00000008 // This link may contain forest trust information #endif StringLoader gString; TErrorDct err; // This method is called by the dispatcher to verify that this is a valid plug-in // Only valid plug-ins will be sent out with the agents // The purpose of this check is to make it more difficult for unauthorized parties // to use our plug-in interface, since it is currently undocumented. STDMETHODIMP CTrust::Verify(/*[in,out]*/ULONG * pData,/*[in]*/ULONG size) { McsChallenge * pMcsChallenge; long lTemp1; long lTemp2; if( size == sizeof(McsChallenge) ) { pMcsChallenge = (McsChallenge*)(pData); SimpleCipher((LPBYTE)pMcsChallenge,size); pMcsChallenge->MCS[0] = 'M'; pMcsChallenge->MCS[1] = 'C'; pMcsChallenge->MCS[2] = 'S'; pMcsChallenge->MCS[3] = 0; lTemp1 = pMcsChallenge->lRand1 + pMcsChallenge->lRand2; lTemp2 = pMcsChallenge->lRand2 - pMcsChallenge->lRand1; pMcsChallenge->lRand1 = lTemp1; pMcsChallenge->lRand2 = lTemp2; pMcsChallenge->lTime += 100; SimpleCipher((LPBYTE)pMcsChallenge,size); } else return E_FAIL; return S_OK; } ///////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CTrust::QueryTrust(BSTR domTrusting, BSTR domTrusted, IUnknown **pVarSet) { HRESULT hr = S_OK; return hr; } STDMETHODIMP CTrust::CreateTrust(BSTR domTrusting, BSTR domTrusted, BOOL bBidirectional) { HRESULT hr = S_OK; hr = CheckAndCreate(domTrusting,domTrusted,NULL,NULL,NULL,NULL,NULL,NULL,TRUE,bBidirectional); return HRESULT_FROM_WIN32(hr); } STDMETHODIMP CTrust::CreateTrustWithCreds( BSTR domTrusting, BSTR domTrusted, BSTR credTrustingDomain, BSTR credTrustingAccount, BSTR credTrustingPassword, BSTR credTrustedDomain, BSTR credTrustedAccount, BSTR credTrustedPassword, BOOL bBidirectional ) { HRESULT hr = S_OK; hr = CheckAndCreate(domTrusting,domTrusted,credTrustingDomain,credTrustingAccount,credTrustingPassword, credTrustedDomain,credTrustedAccount,credTrustedPassword,TRUE,bBidirectional); return hr; } STDMETHODIMP CTrust::GetRegisterableFiles(/* [out] */SAFEARRAY ** pArray) { SAFEARRAYBOUND bound[1] = { 0, 0 }; // this plug-in runs locally, no files to distribute (*pArray) = SafeArrayCreate(VT_BSTR,1,bound); return S_OK; } STDMETHODIMP CTrust::GetRequiredFiles(/* [out] */SAFEARRAY ** pArray) { SAFEARRAYBOUND bound[1] = { 0, 0 }; // this plug-in runs locally, no files to distribute (*pArray) = SafeArrayCreate(VT_BSTR,1,bound); return S_OK; } STDMETHODIMP CTrust::GetDescription(/* [out] */ BSTR * description) { (*description) = SysAllocString(L"Sets up needed trusts between domains."); return S_OK; } BOOL IsDownLevel(WCHAR * sComputer) { BOOL bDownlevel = TRUE; WKSTA_INFO_100 * pInfo; long rc = NetWkstaGetInfo(sComputer,100,(LPBYTE*)&pInfo); if ( ! rc ) { if ( pInfo->wki100_ver_major >= 5 ) { bDownlevel = FALSE; } NetApiBufferFree(pInfo); } return bDownlevel; } // Helper function that finds a trusts in our varset list LONG CTrust::FindInboundTrust(IVarSet * pVarSet,WCHAR * sName,LONG max) { LONG ndx = -1; LONG curr = 0; WCHAR key[100]; _bstr_t tName; for ( curr = 0 ; curr < max ; curr++ ) { swprintf(key,L"Trusts.%ld",curr); tName = pVarSet->get(key); if ( ! UStrICmp(tName,sName) ) { // found it! ndx = curr; break; } } return ndx; } HRESULT CTrust::CheckAndCreateTrustingSide( WCHAR * trustingDomain, WCHAR * trustedDomain, WCHAR * trustingComp, WCHAR * trustedComp, WCHAR * trustedDNSName, BYTE * trustedSid, BOOL bCreate, BOOL bBidirectional ) { DWORD rc = S_OK; // if credentials are specified, use them LSA_HANDLE hTrusting = NULL; NTSTATUS status; LSA_AUTH_INFORMATION curr; LSA_AUTH_INFORMATION prev; WCHAR password[] = L"password"; if ( ! rc && bCreate ) { status = OpenPolicy(trustingComp,POLICY_VIEW_LOCAL_INFORMATION | POLICY_TRUST_ADMIN | POLICY_CREATE_SECRET,&hTrusting); rc = LsaNtStatusToWinError(status); if ( ! rc ) { // set up the auth information for the trust relationship curr.AuthInfo = (LPBYTE)password; curr.AuthInfoLength = UStrLen(password) * sizeof(WCHAR); curr.AuthType = TRUST_AUTH_TYPE_CLEAR; curr.LastUpdateTime.QuadPart = 0; prev.AuthInfo = (LPBYTE)password; prev.AuthInfoLength = UStrLen(password) * sizeof(WCHAR); prev.AuthType = TRUST_AUTH_TYPE_CLEAR; prev.LastUpdateTime.QuadPart = 0; // set up the trusting side of the relationship if ( IsDownLevel(trustingComp) ) { TRUSTED_DOMAIN_NAME_INFO nameInfo; InitLsaString(&nameInfo.Name,const_cast(trustedDomain)); status = LsaSetTrustedDomainInformation(hTrusting,trustedSid,TrustedDomainNameInformation,&nameInfo); rc = LsaNtStatusToWinError(status); if ( ! rc || rc == ERROR_ALREADY_EXISTS ) { // set the password for the new trust TRUSTED_PASSWORD_INFO pwdInfo; InitLsaString(&pwdInfo.Password,password); InitLsaString(&pwdInfo.OldPassword,NULL); status = LsaSetTrustedDomainInformation(hTrusting,trustedSid,TrustedPasswordInformation,&pwdInfo); rc = LsaNtStatusToWinError(status); } } else { // for Win2K domain, use LsaCreateTrustedDomainEx // to create the trustedDomain object. LSA_UNICODE_STRING sTemp; TRUSTED_DOMAIN_INFORMATION_EX trustedInfo; TRUSTED_DOMAIN_AUTH_INFORMATION trustAuth; InitLsaString(&sTemp, const_cast(trustedDomain)); trustedInfo.FlatName = sTemp; InitLsaString(&sTemp, trustedDNSName); trustedInfo.Name = sTemp; trustedInfo.Sid = trustedSid; if ( IsDownLevel(trustedComp) ) { trustedInfo.TrustAttributes = TRUST_TYPE_DOWNLEVEL; } else { trustedInfo.TrustAttributes = 0; } if ( bBidirectional ) trustedInfo.TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL; else trustedInfo.TrustDirection = TRUST_DIRECTION_OUTBOUND; trustedInfo.TrustType = TRUST_ATTRIBUTE_NON_TRANSITIVE; trustAuth.IncomingAuthInfos = bBidirectional ? 1 : 0; trustAuth.OutgoingAuthInfos = 1; trustAuth.IncomingAuthenticationInformation = bBidirectional ? &curr : NULL; trustAuth.IncomingPreviousAuthenticationInformation = NULL; trustAuth.OutgoingAuthenticationInformation = &curr; trustAuth.OutgoingPreviousAuthenticationInformation = NULL; LSA_HANDLE hTemp = NULL; status = LsaCreateTrustedDomainEx( hTrusting, &trustedInfo, &trustAuth, 0, &hTemp ); rc = LsaNtStatusToWinError(status); // if the trust already exists, update its password if ( status == STATUS_OBJECT_NAME_COLLISION ) { TRUSTED_DOMAIN_INFORMATION_EX * pTrustedInfo = NULL; status = LsaQueryTrustedDomainInfo(hTrusting,trustedSid,TrustedDomainInformationEx,(LPVOID*)&pTrustedInfo); if ( ! status ) { pTrustedInfo->TrustDirection |= trustedInfo.TrustDirection; status = LsaSetTrustedDomainInfoByName(hTrusting,&trustedInfo.Name,TrustedDomainInformationEx,(LPVOID*)pTrustedInfo); if ( ! status ) { status = LsaSetTrustedDomainInfoByName(hTrusting,&trustedInfo.Name,TrustedDomainAuthInformation,(LPVOID*)&trustAuth); } } rc = LsaNtStatusToWinError(status); } if( ! rc ) { if (hTemp) LsaClose(hTemp); } } } } if ( hTrusting ) LsaClose(hTrusting); return rc; } HRESULT CTrust::CheckAndCreateTrustedSide( WCHAR * trustingDomain, WCHAR * trustedDomain, WCHAR * trustingComp, WCHAR * trustedComp, WCHAR * trustingDNSName, BYTE * trustingSid, BOOL bCreate, BOOL bBidirectional ) { DWORD rc = S_OK; LSA_HANDLE hTrusted = NULL; LSA_HANDLE hTrusting = NULL; NTSTATUS status; LSA_AUTH_INFORMATION curr; LSA_AUTH_INFORMATION prev; WCHAR password[] = L"password"; // if credentials are specified, use them // open an LSA handle to the trusted domain status = OpenPolicy(trustedComp,POLICY_VIEW_LOCAL_INFORMATION | POLICY_TRUST_ADMIN | POLICY_CREATE_SECRET,&hTrusted); rc = LsaNtStatusToWinError(status); if ( ! rc ) { // set up the auth information for the trust relationship curr.AuthInfo = (LPBYTE)password; curr.AuthInfoLength = UStrLen(password) * sizeof(WCHAR); curr.AuthType = TRUST_AUTH_TYPE_CLEAR; curr.LastUpdateTime.QuadPart = 0; prev.AuthInfo = (LPBYTE)password; prev.AuthInfoLength = UStrLen(password) * sizeof(WCHAR); prev.AuthType = TRUST_AUTH_TYPE_CLEAR; prev.LastUpdateTime.QuadPart = 0; // set up the trusted side of the relationship if ( IsDownLevel(trustedComp) ) { // create an inter-domain trust account for the trusting domain on the trusted domain USER_INFO_1 uInfo; DWORD parmErr; WCHAR name[LEN_Account]; memset(&uInfo,0,(sizeof uInfo)); UStrCpy(name,trustingDomain); name[UStrLen(name) + 1] = 0; name[UStrLen(name)] = L'$'; if ( ! bCreate ) { USER_INFO_1 * tempInfo = NULL; rc = NetUserGetInfo(trustedComp,name,1,(LPBYTE*)&tempInfo); if ( ! rc ) { // the trust exists NetApiBufferFree(tempInfo); rc = NERR_UserExists; } else { if ( rc != NERR_UserNotFound ) { err.SysMsgWrite(ErrE,rc,DCT_MSG_TRUSTING_DOM_GETINFO_FAILED_SSD,trustingDomain,trustedComp,rc); } } } else { // this creates the trust account if it doesn't already exist // if the account does exist, reset its password uInfo.usri1_flags = UF_SCRIPT | UF_INTERDOMAIN_TRUST_ACCOUNT; uInfo.usri1_name = name; uInfo.usri1_password = password; uInfo.usri1_priv = 1; rc = NetUserAdd(trustedComp,1,(LPBYTE)&uInfo,&parmErr); if ( rc && rc != NERR_UserExists ) { err.SysMsgWrite(ErrE,rc,DCT_MSG_TRUSTING_DOM_CREATE_FAILED_SSD,trustingDomain,trustedDomain,rc); } else if ( rc == NERR_UserExists ) { // reset the password for the existing trust account USER_INFO_1003 pwdInfo; DWORD parmErr; pwdInfo.usri1003_password = password; rc = NetUserSetInfo(trustedComp,name,1003,(LPBYTE)&pwdInfo,&parmErr); } } } else { // Win2K, all trusts exist as trusted domain objects // Create the trustedDomain object. LSA_UNICODE_STRING sTemp; TRUSTED_DOMAIN_INFORMATION_EX trustedInfo; TRUSTED_DOMAIN_AUTH_INFORMATION trustAuth; InitLsaString(&sTemp, const_cast(trustingDomain)); trustedInfo.FlatName = sTemp; InitLsaString(&sTemp, trustingDNSName); trustedInfo.Name = sTemp; trustedInfo.Sid = trustingSid; if ( IsDownLevel(trustingComp) ) { trustedInfo.TrustAttributes = TRUST_TYPE_DOWNLEVEL; } else { trustedInfo.TrustAttributes = 0; } if ( bBidirectional ) trustedInfo.TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL; else trustedInfo.TrustDirection = TRUST_DIRECTION_INBOUND; trustedInfo.TrustType = TRUST_ATTRIBUTE_NON_TRANSITIVE; trustAuth.IncomingAuthInfos = 1; trustAuth.OutgoingAuthInfos = bBidirectional ? 1 : 0; trustAuth.OutgoingAuthenticationInformation = bBidirectional ? &curr : NULL; trustAuth.OutgoingPreviousAuthenticationInformation = NULL; trustAuth.IncomingAuthenticationInformation = &curr; trustAuth.IncomingPreviousAuthenticationInformation = NULL; if ( bCreate ) { status = LsaCreateTrustedDomainEx( hTrusted, &trustedInfo, &trustAuth, POLICY_VIEW_LOCAL_INFORMATION | POLICY_TRUST_ADMIN | POLICY_CREATE_SECRET, &hTrusting ); if ( status == STATUS_OBJECT_NAME_COLLISION ) { TRUSTED_DOMAIN_INFORMATION_EX * pTrustedInfo = NULL; // Get the old information status = LsaQueryTrustedDomainInfoByName(hTrusted,&sTemp,TrustedDomainInformationEx,(LPVOID*)&pTrustedInfo); if ( ! status ) { pTrustedInfo->TrustAttributes |= trustedInfo.TrustAttributes; pTrustedInfo->TrustDirection |= trustedInfo.TrustDirection; status = LsaSetTrustedDomainInfoByName(hTrusted,&sTemp,TrustedDomainInformationEx,pTrustedInfo); if (! status ) { status = LsaSetTrustedDomainInfoByName(hTrusted,&sTemp,TrustedDomainAuthInformation,&trustAuth); } LsaFreeMemory(pTrustedInfo); } } } else { TRUSTED_DOMAIN_INFORMATION_EX * pTrustedInfo = NULL; status = LsaQueryTrustedDomainInfoByName(hTrusted,&sTemp,TrustedDomainInformationEx,(LPVOID*)&pTrustedInfo); if ( ! status ) { LsaFreeMemory(pTrustedInfo); } } rc = LsaNtStatusToWinError(status); if ( ! rc ) { LsaClose(hTrusting); hTrusting = NULL; } } if ( bCreate && bBidirectional && IsDownLevel(trustingComp) ) { // set up the trust account for the other side of the relationship // For Win2K, both sides of the bidirectional trust are handled together, // but NT4 bidirectional trusts require 2 separate actions USER_INFO_1 uInfo; DWORD parmErr; WCHAR name2[LEN_Account]; memset(&uInfo,0,(sizeof uInfo)); UStrCpy(name2,trustedDomain); name2[UStrLen(name2) + 1] = 0; name2[UStrLen(name2)] = L'$'; uInfo.usri1_flags = UF_SCRIPT | UF_INTERDOMAIN_TRUST_ACCOUNT; uInfo.usri1_name = name2; uInfo.usri1_password = password; uInfo.usri1_priv = 1; rc = NetUserAdd(trustingComp,1,(LPBYTE)&uInfo,&parmErr); if ( rc == NERR_UserExists ) { LPUSER_INFO_1 puInfo; rc = NetUserGetInfo(trustingComp, name2, 1, (LPBYTE*)&puInfo); if ( !rc ) { puInfo->usri1_flags &= UF_INTERDOMAIN_TRUST_ACCOUNT; puInfo->usri1_password = password; rc = NetUserSetInfo(trustingComp,name2,1,(LPBYTE)puInfo,&parmErr); NetApiBufferFree(puInfo); } else { err.MsgWrite(0, DCT_MSG_INVALID_ACCOUNT_S, name2); } } else if ( rc ) { err.SysMsgWrite(ErrE,rc,DCT_MSG_TRUSTING_DOM_CREATE_FAILED_SSD,trustingDomain,trustedDomain,rc); } } } else { err.SysMsgWrite(ErrE,rc,DCT_MSG_LSA_OPEN_FAILED_SD,trustedComp,rc); } if( hTrusted ) LsaClose(hTrusted); return rc; } // Helper function that gets DNS and NETBIOS domain names void GetDnsAndNetbiosFromName(WCHAR const * name,WCHAR * netBios, WCHAR * dns) { IADs * pDomain = NULL; WCHAR strText[1000]; _bstr_t distinguishedName; // try to connect to the AD, using the specified name swprintf(strText,L"LDAP://%ls",name); netBios[0] = 0; dns[0] = 0; HRESULT hr = ADsGetObject(strText,IID_IADs,(void**)&pDomain); if ( SUCCEEDED(hr) ) { _variant_t var; // get the DNS name from the LDAP provider hr = pDomain->Get(L"distinguishedName",&var); if ( SUCCEEDED(hr) ) { WCHAR * dn = (WCHAR*)var.bstrVal; WCHAR * curr = dns; distinguishedName = dn; if ( !UStrICmp(dn,L"DC=",3) ) { // for each ",DC=" in the name, replace it with a . for ( curr = dns, dn = dn+3 ; // skip the leading "DC=" *dn ; // until the end of the string is reached curr++ // ) { if ( (L',' == *dn) && (L'D' == *(dn+1)) && (L'C' == *(dn+2)) && (L'=' == *(dn+3)) ) { (*curr) = L'.'; dn+=4; } else { // just copy the character (*curr) = (*dn); dn++; } } *(curr) = 0; } // get the NETBIOS name from the LDAP provider hr = pDomain->Get(L"nETBIOSName",&var); if ( SUCCEEDED(hr) ) { UStrCpy(netBios,(WCHAR*)var.bstrVal); } else { // currently, the netbiosName property is not filled in // so we will use a different method to get the flat-name for the domain // Here is our strategy to get the netbios name: // Enumerate the partitions container under the configuration container // look for a CrossRef object whose nCName property matches the distinguished name // we have for the domain. This object's CN property is the flat-name for the domain // get the name of the configuration container IADs * pDSE = NULL; swprintf(strText,L"LDAP://%ls/RootDSE",name); hr = ADsGetObject(strText,IID_IADs,(void**)&pDSE); if ( SUCCEEDED(hr) ) { hr = pDSE->Get(L"configurationNamingContext",&var); if ( SUCCEEDED(hr) ) { IADsContainer * pPart = NULL; swprintf(strText,L"LDAP://%ls/CN=Partitions,%ls",name,var.bstrVal); hr = ADsGetObject(strText,IID_IADsContainer,(void**)&pPart); if ( SUCCEEDED(hr) ) { IUnknown * pUnk = NULL; IEnumVARIANT * pEnum = NULL; IADs * pItem = NULL; ULONG lFetch = 0; // enumerate the contents of the Partitions container hr = pPart->get__NewEnum(&pUnk); if ( SUCCEEDED(hr) ) { hr = pUnk->QueryInterface(IID_IEnumVARIANT,(void**)&pEnum); pUnk->Release(); } if ( SUCCEEDED(hr) ) { hr = pEnum->Next(1,&var,&lFetch); } while ( hr == S_OK ) { if (lFetch == 1 ) { IDispatch * pDisp = V_DISPATCH(&var); BSTR bstr; if ( pDisp ) { hr = pDisp->QueryInterface(IID_IADs, (void**)&pItem); pDisp->Release(); if ( SUCCEEDED(hr) ) { hr = pItem->get_Class(&bstr); if ( !UStrICmp(bstr,L"crossRef") ) { // see if this is the one we are looking for hr = pItem->Get(L"nCName",&var); if ( SUCCEEDED(hr) ) { if ( !UStrICmp(var.bstrVal,(WCHAR*)distinguishedName) ) { // this is the one we want! hr = pItem->Get(L"cn",&var); if ( SUCCEEDED(hr) ) { UStrCpy(netBios,var.bstrVal); pItem->Release(); SysFreeString(bstr); break; } } } } SysFreeString(bstr); pItem->Release(); } } } hr = pEnum->Next(1, &var, &lFetch); } pPart->Release(); if ( pEnum ) pEnum->Release(); } } pDSE->Release(); } } } pDomain->Release(); } else { // default to using the specified name as both DNS and NETBIOS // this will work for NT 4 domains UStrCpy(netBios,name); UStrCpy(dns,name); } if ( ! (*netBios) ) { UStrCpy(netBios,name); WCHAR * temp = wcschr(netBios,L'.'); if ( temp ) *temp = 0; } if (! (*dns) ) { UStrCpy(dns,name); } } // Main function used to create trusts HRESULT CTrust::CheckAndCreate( WCHAR * trustingDomain, WCHAR * trustedDomain, WCHAR * credDomainTrusting, WCHAR * credAccountTrusting, WCHAR * credPasswordTrusting, WCHAR * credDomainTrusted, WCHAR * credAccountTrusted, WCHAR * credPasswordTrusted, BOOL bCreate, BOOL bBidirectional ) { // HRESULT hr = S_OK; DWORD rc = 0; WCHAR trustingDom[LEN_Domain]; WCHAR trustedDom[LEN_Domain]; WCHAR trustingComp[LEN_Computer]; WCHAR trustedComp[LEN_Computer]; WCHAR trustingDNSName[LEN_Path]; WCHAR trustedDNSName[LEN_Path]; BYTE trustingSid[200]; BYTE trustedSid[200]; WCHAR name[LEN_Account]; DWORD lenName = DIM(name); DWORD lenSid = DIM(trustingSid); SID_NAME_USE snu; DOMAIN_CONTROLLER_INFO * pInfo; WCHAR * curr = NULL; BOOL bConnectTrusted = FALSE; BOOL bConnectTrusting = FALSE; // Get the DC names, and domain SIDs for the source and target domains GetDnsAndNetbiosFromName(trustingDomain,trustingDom,trustingDNSName); GetDnsAndNetbiosFromName(trustedDomain,trustedDom,trustedDNSName); // make sure the NETBIOS domain names are in lower case for ( curr = trustingDom ; *curr ; curr++ ) { (*curr) = towupper(*curr); } for ( curr = trustedDom ; *curr ; curr++ ) { (*curr) = towupper(*curr); } rc = DsGetDcName(NULL, trustingDom, NULL, NULL, DS_PDC_REQUIRED, &pInfo); if ( !rc ) { wcscpy(trustingComp,pInfo->DomainControllerName); //wcscpy(trustingDNSName,pInfo->DomainName); NetApiBufferFree(pInfo); } else { err.SysMsgWrite(ErrE,rc,DCT_MSG_GET_DCNAME_FAILED_SD,trustingDom,rc); } if ( ! rc ) { rc = DsGetDcName(NULL, trustedDom, NULL, NULL, DS_PDC_REQUIRED, &pInfo); if ( !rc ) { wcscpy(trustedComp,pInfo->DomainControllerName); // wcscpy(trustedDNSName,pInfo->DomainName); NetApiBufferFree(pInfo); } else { err.SysMsgWrite(ErrE,rc,DCT_MSG_GET_DCNAME_FAILED_SD,trustedDom,rc); } } if ( credAccountTrusted && *credAccountTrusted ) { if ( EstablishSession(trustedComp,credDomainTrusted,credAccountTrusted,credPasswordTrusted,TRUE) ) { bConnectTrusted = TRUE; } else { rc = GetLastError(); } } if ( credAccountTrusting && *credAccountTrusting ) { if ( EstablishSession(trustingComp,credDomainTrusting,credAccountTrusting,credPasswordTrusting,TRUE) ) { bConnectTrusting = TRUE; } else { rc = GetLastError(); } } // Need to get the computer name and the SIDs for the domains. if ( ! rc && ! LookupAccountName(trustingComp,trustingDom,trustingSid,&lenSid,name,&lenName,&snu) ) { rc = GetLastError(); err.SysMsgWrite(ErrE,rc,DCT_MSG_GET_DOMAIN_SID_FAILED_1,trustingDom,rc); } lenSid = DIM(trustedSid); lenName = DIM(name); if (! rc && ! LookupAccountName(trustedComp,trustedDom,trustedSid,&lenSid,name,&lenName,&snu) ) { rc = GetLastError(); err.SysMsgWrite(ErrE,rc,DCT_MSG_GET_DOMAIN_SID_FAILED_1,trustedDom,rc); } // check the trusted side of the trust first if ( ! rc ) { rc = CheckAndCreateTrustedSide(trustingDom,trustedDom,trustingComp,trustedComp,trustingDNSName,trustingSid, bCreate,bBidirectional); } if ( ! rc ) { rc = CheckAndCreateTrustingSide(trustingDom,trustedDom,trustingComp,trustedComp,trustedDNSName,trustedSid, bCreate,bBidirectional); } if ( bConnectTrusted ) { EstablishSession(trustedComp,credDomainTrusted,credAccountTrusted,credPasswordTrusted,FALSE); } if ( bConnectTrusting ) { EstablishSession(trustingComp,credDomainTrusting,credAccountTrusting,credPasswordTrusting,FALSE); } return HRESULT_FROM_WIN32(rc); } long CTrust::EnumerateTrustedDomains(WCHAR * domain,BOOL bIsTarget,IVarSet * pVarSet,long ndxStart) { DWORD rcOs; // OS return code LSA_HANDLE hPolicy; NTSTATUS status; WCHAR computer[LEN_Computer]; DOMAIN_CONTROLLER_INFO * pInfo; WCHAR sName[LEN_Domain]; WCHAR key[100]; long ndxTrust = ndxStart; /* PDS_DOMAIN_TRUSTS *ChildDomains; ULONG numChilds; */ err.MsgWrite(0,DCT_MSG_ENUMERATING_TRUSTED_DOMAINS_S,domain); // open a handle to the source domain rcOs = DsGetDcName(NULL, domain, NULL, NULL, 0, &pInfo); if ( !rcOs ) { wcscpy(computer,pInfo->DomainControllerName); NetApiBufferFree(pInfo); } else { err.SysMsgWrite(ErrE,rcOs,DCT_MSG_GET_DCNAME_FAILED_SD,domain,rcOs); } if ( ! rcOs ) { if ( IsDownLevel(computer) ) { //Enumerate the trusted domains until there are no more to return. status = OpenPolicy(computer,POLICY_ALL_ACCESS | POLICY_VIEW_LOCAL_INFORMATION ,&hPolicy); if ( status == STATUS_SUCCESS ) { LSA_ENUMERATION_HANDLE lsaEnumHandle=0; // start an enum PLSA_TRUST_INFORMATION trustInfo = NULL; ULONG ulReturned; // number of items returned NTSTATUS status; DWORD rc; do { status = LsaEnumerateTrustedDomains( hPolicy, // open policy handle &lsaEnumHandle, // enumeration tracker (void**)&trustInfo, // buffer to receive data 32000, // recommended buffer size &ulReturned // number of items returned ); //Check the return status for error. rc = LsaNtStatusToWinError(status); if( (rc != ERROR_SUCCESS) && (rc != ERROR_MORE_DATA) && (rc != ERROR_NO_MORE_ITEMS) ) { err.SysMsgWrite(ErrE,rcOs,DCT_MSG_TRUSTED_ENUM_FAILED_SD,domain,rcOs); } else { // . . . Code to use the Trusted Domain information for ( ULONG ndx = 0 ; ndx < ulReturned ; ndx++ ) { _bstr_t direction; UStrCpy(sName,trustInfo[ndx].Name.Buffer, ( trustInfo[ndx].Name.Length / (sizeof WCHAR)) + 1); TRUSTED_DOMAIN_INFORMATION_EX * pTrustedInfo = NULL; status = LsaQueryTrustedDomainInfo(hPolicy,trustInfo[ndx].Sid,TrustedDomainInformationEx,(LPVOID*)&pTrustedInfo); if ( ! status ) { switch ( pTrustedInfo->TrustDirection ) { case TRUST_DIRECTION_DISABLED: direction = GET_BSTR(IDS_TRUST_DIRECTION_DISABLED); break; case TRUST_DIRECTION_INBOUND: direction = GET_BSTR(IDS_TRUST_DIRECTION_INBOUND); break; case TRUST_DIRECTION_OUTBOUND: direction = GET_BSTR(IDS_TRUST_DIRECTION_OUTBOUND); break; case TRUST_DIRECTION_BIDIRECTIONAL: direction = GET_BSTR(IDS_TRUST_DIRECTION_BIDIRECTIONAL); break; default: break; }; if ( ! bIsTarget ) { swprintf(key,L"Trusts.%ld.Type",ndxTrust); pVarSet->put(key, GET_BSTR(IDS_TRUST_RELATION_EXTERNAL)); } LsaFreeMemory(pTrustedInfo); } else { rcOs = LsaNtStatusToWinError(status); // My logic here is that we are checking Trusted domains here so this is atleast true // check whether this trust is already listed as an inbound trust //* direction = L"Outbound"; direction = GET_BSTR(IDS_TRUST_DIRECTION_OUTBOUND); } if ( ! bIsTarget ) { swprintf(key,L"Trusts.%ld",ndxTrust); pVarSet->put(key,sName); swprintf(key,L"Trusts.%ld.Direction",ndxTrust); pVarSet->put(key,direction); swprintf(key,L"Trusts.%ld.ExistsForTarget",ndxTrust); //* pVarSet->put(key,L"No"); pVarSet->put(key,GET_BSTR(IDS_No)); err.MsgWrite(0,DCT_MSG_SOURCE_TRUSTS_THIS_SS,sName,domain); } long ndx2 = FindInboundTrust(pVarSet,sName,ndxTrust); if ( ndx2 != -1 ) { if ( ! bIsTarget ) { // we've already seen this trust as an inbound trust // update the existing record! WCHAR key2[1000]; swprintf(key2,L"Trusts.%ld.Direction",ndx2); //* pVarSet->put(key2,L"Bidirectional"); pVarSet->put(key2,GET_BSTR(IDS_TRUST_DIRECTION_BIDIRECTIONAL)); continue; // don't update the trust entry index, since we used the existing // entry instead of creating a new one } else { swprintf(key,L"Trusts.%ld.ExistsForTarget",ndx2); //* pVarSet->put(key,L"Yes"); pVarSet->put(key,GET_BSTR(IDS_YES)); err.MsgWrite(0,DCT_MSG_TARGET_TRUSTS_THIS_SS,domain,sName); } } swprintf(key,L"Trusts.%ld.ExistsForTarget",ndxTrust); // check the trusted domain, to see if the target already trusts it //if ( UStrICmp(sName,target) ) // { // continue; // } if ( ! bIsTarget ) ndxTrust++; } // Free the buffer. LsaFreeMemory(trustInfo); } } while (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA ); LsaClose(hPolicy); } } else { ULONG ulCount; PDS_DOMAIN_TRUSTS pDomainTrusts; DWORD dwError = DsEnumerateDomainTrusts( computer, DS_DOMAIN_IN_FOREST|DS_DOMAIN_DIRECT_INBOUND|DS_DOMAIN_DIRECT_OUTBOUND, &pDomainTrusts, &ulCount ); if (dwError == NO_ERROR) { ULONG ulIndex; ULONG ulDomainIndex = (ULONG)-1L; ULONG ulParentIndex = (ULONG)-1L; // find local domain for (ulIndex = 0; ulIndex < ulCount; ulIndex++) { if (pDomainTrusts[ulIndex].Flags & DS_DOMAIN_PRIMARY) { ulDomainIndex = ulIndex; if (!(pDomainTrusts[ulIndex].Flags & DS_DOMAIN_TREE_ROOT)) { ulParentIndex = pDomainTrusts[ulIndex].ParentIndex; } break; } } for (ulIndex = 0; ulIndex < ulCount; ulIndex++) { DS_DOMAIN_TRUSTS& rDomainTrust = pDomainTrusts[ulIndex]; // filter out indirect trusts if (!(rDomainTrust.Flags & (DS_DOMAIN_DIRECT_INBOUND|DS_DOMAIN_DIRECT_OUTBOUND))) { continue; } // trusted or trusting domain name _bstr_t bstrName(rDomainTrust.NetbiosDomainName); // trust direction _bstr_t bstrDirection; switch (rDomainTrust.Flags & (DS_DOMAIN_DIRECT_INBOUND|DS_DOMAIN_DIRECT_OUTBOUND)) { case DS_DOMAIN_DIRECT_INBOUND: bstrDirection = GET_BSTR(IDS_TRUST_DIRECTION_INBOUND); break; case DS_DOMAIN_DIRECT_OUTBOUND: bstrDirection = GET_BSTR(IDS_TRUST_DIRECTION_OUTBOUND); break; case DS_DOMAIN_DIRECT_INBOUND|DS_DOMAIN_DIRECT_OUTBOUND: bstrDirection = GET_BSTR(IDS_TRUST_DIRECTION_BIDIRECTIONAL); break; default: // bstrDirection = ; break; } // trust relationship _bstr_t bstrRelationship; if (ulIndex == ulParentIndex) { bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_PARENT); } else if (rDomainTrust.Flags & DS_DOMAIN_IN_FOREST) { if (rDomainTrust.ParentIndex == ulDomainIndex) { bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_CHILD); } else if ((rDomainTrust.Flags & DS_DOMAIN_TREE_ROOT) && (pDomainTrusts[ulDomainIndex].Flags & DS_DOMAIN_TREE_ROOT)) { bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_ROOT); } else { bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_SHORTCUT); } } else { switch (rDomainTrust.TrustType) { case TRUST_TYPE_DOWNLEVEL: case TRUST_TYPE_UPLEVEL: bstrRelationship = (rDomainTrust.TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) ? GET_BSTR(IDS_TRUST_RELATION_FOREST) : GET_BSTR(IDS_TRUST_RELATION_EXTERNAL); break; case TRUST_TYPE_MIT: bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_MIT); break; case TRUST_TYPE_DCE: bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_DCE); break; default: bstrRelationship = GET_BSTR(IDS_TRUST_RELATION_UNKNOWN); break; } } if (bIsTarget) { // if same trust was found on source domain and the trust // directions match update exists for target to yes LONG lSourceIndex = FindInboundTrust(pVarSet, bstrName, ndxTrust); if (lSourceIndex >= 0) { // get source trust direction swprintf(key, L"Trusts.%ld.Direction", lSourceIndex); _bstr_t bstrSourceDirection = pVarSet->get(key); // if target trust direction is bi-directional or // target trust direction equals source trust direction // then set exists for target to yes bool bExists = false; if (bstrDirection == GET_BSTR(IDS_TRUST_DIRECTION_BIDIRECTIONAL)) { bExists = true; } else if (bstrDirection == bstrSourceDirection) { bExists = true; } if (bExists) { swprintf(key, L"Trusts.%ld.ExistsForTarget", lSourceIndex); pVarSet->put(key, GET_BSTR(IDS_YES)); // write trust directions to log if (rDomainTrust.Flags & DS_DOMAIN_DIRECT_OUTBOUND) { err.MsgWrite(0, DCT_MSG_TARGET_TRUSTS_THIS_SS, domain, (LPCTSTR)bstrName); } if (rDomainTrust.Flags & DS_DOMAIN_DIRECT_INBOUND) { err.MsgWrite(0, DCT_MSG_TARGET_TRUSTED_BY_THIS_SS, domain, (LPCTSTR)bstrName); } } } } else { // domain name swprintf(key, L"Trusts.%ld", ndxTrust); pVarSet->put(key, bstrName); // trust direction swprintf(key, L"Trusts.%ld.Direction", ndxTrust); pVarSet->put(key, bstrDirection); // trust relationship if (bstrRelationship.length() > 0) { swprintf(key, L"Trusts.%ld.Type", ndxTrust); pVarSet->put(key, bstrRelationship); } // trust exists on target // initially set to no until target domain is enumerated swprintf(key, L"Trusts.%ld.ExistsForTarget", ndxTrust); pVarSet->put(key, GET_BSTR(IDS_No)); // write trust directions to log if (rDomainTrust.Flags & DS_DOMAIN_DIRECT_OUTBOUND) { err.MsgWrite(0, DCT_MSG_SOURCE_TRUSTS_THIS_SS, (LPCTSTR)bstrName, domain); } if (rDomainTrust.Flags & DS_DOMAIN_DIRECT_INBOUND) { err.MsgWrite(0, DCT_MSG_SOURCE_IS_TRUSTED_BY_THIS_SS, (LPCTSTR)bstrName, domain); } ++ndxTrust; } } NetApiBufferFree(pDomainTrusts); } else { err.SysMsgWrite(ErrE, dwError, DCT_MSG_TRUSTED_ENUM_FAILED_SD, domain, dwError); } } } if ( bIsTarget ) { // make sure we have "Yes" for the target domain itself long ndx2 = FindInboundTrust(pVarSet,domain,ndxTrust); if ( ndx2 != -1 ) { swprintf(key,L"Trusts.%ld.ExistsForTarget",ndx2); //* pVarSet->put(key,L"Yes"); pVarSet->put(key,GET_BSTR(IDS_YES)); } } return ndxTrust; } long CTrust::EnumerateTrustingDomains(WCHAR * domain,BOOL bIsTarget,IVarSet * pVarSet,long ndxStart) { DWORD rcOs; // OS return code DWORD hEnum=0; // enumeration handle USER_INFO_1 * pNetUsers=NULL; // NetUserEnum array buffer USER_INFO_1 * pNetUser; // NetUserEnum array item DWORD nRead; // Entries read. DWORD nTotal; // Entries total. WCHAR sName[LEN_Domain]; // Domain name WCHAR * pNameEnd; // Null at end WCHAR computer[LEN_Computer]; DOMAIN_CONTROLLER_INFO * pInfo; long ndx = ndxStart; WCHAR key[100]; err.MsgWrite(0,DCT_MSG_ENUMERATING_TRUSTING_DOMAINS_S,domain); do { rcOs = DsGetDcName(NULL, domain, NULL, NULL, 0, &pInfo); if ( !rcOs ) { wcscpy(computer,pInfo->DomainControllerName); NetApiBufferFree(pInfo); } else { break; } // get the trusting domains for the NT 4 domain // for Win2K domains, the trusting domains will be listed as Incoming in the Trusted Domain enumeration if ( IsDownLevel(computer) ) { nRead = 0; nTotal = 0; rcOs = NetUserEnum( computer, 1, FILTER_INTERDOMAIN_TRUST_ACCOUNT, (BYTE **) &pNetUsers, 10240, &nRead, &nTotal, &hEnum ); switch ( rcOs ) { case 0: case ERROR_MORE_DATA: for ( pNetUser = pNetUsers; pNetUser < pNetUsers + nRead; pNetUser++ ) { // skip trust accounts whose password age is older than 30 days to avoid // delays caused by trying to enumerate defunct trusts if ( pNetUser->usri1_password_age > 60 * 60 * 24 * 30 ) // 30 days (age is in seconds) { err.MsgWrite(0,DCT_MSG_SKIPPING_OLD_TRUST_SD,pNetUser->usri1_name, pNetUser->usri1_password_age / ( 60*60*24) ); continue; } safecopy( sName, pNetUser->usri1_name ); pNameEnd = sName + UStrLen( sName ); if ( (pNameEnd > sName) && (pNameEnd[-1] == L'$') ) { pNameEnd[-1] = L'\0'; } if ( *sName ) { // Found a (probably) valid trust! if ( ! bIsTarget ) { // for the source domain, simply add the trusts to the list in the varset swprintf(key,L"Trusts.%ld",ndx); pVarSet->put(key,sName); swprintf(key,L"Trusts.%ld.Direction",ndx); //* pVarSet->put(key,L"Inbound"); pVarSet->put(key,GET_BSTR(IDS_TRUST_DIRECTION_INBOUND)); swprintf(key,L"Trusts.%ld.Type",ndx); pVarSet->put(key, GET_BSTR(IDS_TRUST_RELATION_EXTERNAL)); swprintf(key,L"Trusts.%ld.ExistsForTarget",ndx); //* pVarSet->put(key,L"No"); pVarSet->put(key,GET_BSTR(IDS_No)); err.MsgWrite(0,DCT_MSG_SOURCE_IS_TRUSTED_BY_THIS_SS,sName,domain); ndx++; } else { // for the target domain, look for this trust in the varset // and if it is there, mark that it exists on the target long ndxTemp = FindInboundTrust(pVarSet,sName,ndxStart); if ( ndxTemp != -1 ) { swprintf(key,L"Trusts.%ld.ExistsForTarget",ndxTemp); //* pVarSet->put(key,L"Yes"); pVarSet->put(key,GET_BSTR(IDS_YES)); err.MsgWrite(0,DCT_MSG_TARGET_TRUSTS_THIS_SS,sName,domain); } } } } break; default: break; } if ( pNetUsers ) { NetApiBufferFree( pNetUsers ); pNetUsers = NULL; } } else // if IsDownLevel() { // Win2K domain, don't need to enumerate the trusting domains here - they will all be included in the // trusted domain enum break; } } while ( rcOs == ERROR_MORE_DATA ); return ndx; } /*void CheckProc(void * arg,void * data) { CTrust * tr = (CTrust*)arg; }*/ STDMETHODIMP CTrust::PreMigrationTask(/* [in] */IUnknown * pVarSet) { /* IVarSetPtr pVS = pVarSet; BOOL bCreate; _bstr_t source = pVS->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t target = pVS->get(GET_BSTR(DCTVS_Options_TargetDomain)); _bstr_t logfile = pVS->get(GET_BSTR(DCTVS_Options_Logfile)); _bstr_t localOnly = pVS->get(GET_BSTR(DCTVS_Options_LocalProcessingOnly)); _bstr_t docreate = pVS->get(L"Options.CreateTrusts"); if ( !UStrICmp(localOnly,GET_STRING(IDS_YES)) ) { // don't do anything in local agent mode return S_OK; } if ( !UStrICmp(docreate,GET_STRING(IDS_YES)) ) { bCreate = TRUE; } else { bCreate = FALSE; } pVS->put(GET_BSTR(DCTVS_CurrentOperation),L"Verifying trust relationships"); err.LogOpen(logfile,1); err.LevelBeepSet(1000); err.LogClose(); */ return S_OK; } STDMETHODIMP CTrust::PostMigrationTask(/* [in] */IUnknown * pVarSet) { return S_OK; } STDMETHODIMP CTrust::GetName(/* [out] */BSTR * name) { (*name) = SysAllocString(L"Trust Manager"); return S_OK; } STDMETHODIMP CTrust::GetResultString(/* [in] */IUnknown * pVarSet,/* [out] */ BSTR * text) { WCHAR buffer[100] = L""; IVarSetPtr pVS; pVS = pVarSet; (*text) = SysAllocString(buffer); return S_OK; } STDMETHODIMP CTrust::StoreResults(/* [in] */IUnknown * pVarSet) { return S_OK; } STDMETHODIMP CTrust::ConfigureSettings(/*[in]*/IUnknown * pVarSet) { return S_OK; } STDMETHODIMP CTrust::QueryTrusts(BSTR domainSource,BSTR domainTarget, BSTR sLogFile, IUnknown **pVarSet) { HRESULT hr = S_OK; IVarSetPtr pVS(CLSID_VarSet); long ndx; _bstr_t sFile = sLogFile; err.LogOpen((WCHAR*) sFile, 1); err.LevelBeepSet(1000); hr = pVS.QueryInterface(IID_IUnknown,(long**)pVarSet); // Add a blank line to help differentiate different runs err.MsgWrite(0,DCT_MSG_GENERIC_S,L""); ndx = EnumerateTrustingDomains(domainSource,FALSE,pVS,0); EnumerateTrustingDomains(domainTarget,TRUE,pVS,0); ndx = EnumerateTrustedDomains(domainSource,FALSE,pVS,ndx); EnumerateTrustedDomains(domainTarget,TRUE,pVS,ndx); //err.LogClose(); pVS->put(L"Trusts",ndx); return hr; }