//+---------------------------------------------------------------------------- // // Windows 2000 Active Directory Service domain trust verification WMI provider // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000 // // File: trust.cpp // // Contents: Trust class implementation // // Classes: CTrustInfo // // History: 27-Mar-00 EricB created // //----------------------------------------------------------------------------- #include PCWSTR CSTR_PROP_TRUSTED_DOMAIN = L"TrustedDomain"; // String PCWSTR CSTR_PROP_FLAT_NAME = L"FlatName"; // String PCWSTR CSTR_PROP_SID = L"SID"; // String PCWSTR CSTR_PROP_TRUST_DIRECTION = L"TrustDirection"; // uint32 PCWSTR CSTR_PROP_TRUST_TYPE = L"TrustType"; // uint32 PCWSTR CSTR_PROP_TRUST_ATTRIBUTES = L"TrustAttributes"; // uint32 PCWSTR CSTR_PROP_TRUST_STATUS = L"TrustStatus"; // uint32 PCWSTR CSTR_PROP_TRUST_STATUS_STRING = L"TrustStatusString"; // String PCWSTR CSTR_PROP_TRUST_IS_OK = L"TrustIsOk"; // Boolean PCWSTR CSTR_PROP_TRUSTED_DC_NAME = L"TrustedDCName"; // String // Define NETLOGON_CONTROL_TC_VERIFY if not found so this will build for W2K. // This constant is in the Whistler version of lmaccess.h #if !defined(NETLOGON_CONTROL_TC_VERIFY) # define NETLOGON_CONTROL_TC_VERIFY (10) #endif //+---------------------------------------------------------------------------- // // Class: CTrustInfo // //----------------------------------------------------------------------------- CTrustInfo::CTrustInfo() : m_ulTrustDirection(0), m_ulTrustType(0), m_ulTrustAttributes(0), m_trustStatus(ERROR_SUCCESS), m_VerifyStatus(VerifyStatusNone), m_fPwVerifySupported(TRUE) { m_liLastVerified.QuadPart = 0; } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::Verify // // Synopsis: Verify the status of the trust // // Returns: FALSE if the trust was not outbound. // //----------------------------------------------------------------------------- BOOL CTrustInfo::Verify(TrustCheckLevel CheckLevel) { TRACE(L"CTrustInfo::Verify, verify level %d\n", CheckLevel); NET_API_STATUS netStatus = NERR_Success; NETLOGON_INFO_2 * pNetlogonInfo2 = NULL; VerifyStatus Status = VerifyStatusNone; PCWSTR pwzTrustedDomain = GetTrustedDomain(); CString strDCName, strResetTarget = GetTrustedDomain(); if (DONT_VERIFY == CheckLevel) { TRACE(L"\tCheck-Level set to not verify trust.\n"); SetTrustStatus(NERR_Success, VerifyStatusTrustNotChecked); return TRUE; } TRACE(L"\tVerifying trust with %s\n", GetTrustedDomain()); if (TRUST_TYPE_MIT == GetTrustType()) { // don't verify non-Windows trusts. // TRACE(L"\tNot a windows trust, returning.\n"); SetTrustStatus(NERR_Success, VerifyStatusNotWindowsTrust); SetLastVerifiedTime(); return TRUE; } if (!IsTrustOutbound()) { // don't verify inbound-only trusts. // TRACE(L"\tInbound-only trust, returning.\n"); SetTrustStatus(NERR_Success, VerifyStatusNotOutboundTrust); SetLastVerifiedTime(); return FALSE; } // // NETLOGON_CONTROL_TC_QUERY - get the status (locally) and the name of trusted DC // Note that the secure channel is set up only on demand, so it is not an error if // it is not set up. The SC_QUERY will return ERROR_NO_LOGON_SERVERS if this is the // case. // netStatus = I_NetLogonControl2(NULL, NETLOGON_CONTROL_TC_QUERY, 2, (LPBYTE)&pwzTrustedDomain, (LPBYTE *)&pNetlogonInfo2); if (NERR_Success == netStatus) { ASSERT(pNetlogonInfo2); netStatus = pNetlogonInfo2->netlog2_tc_connection_status; if (netStatus == NERR_Success) { SetTrustedDCName(pNetlogonInfo2->netlog2_trusted_dc_name); strDCName = pNetlogonInfo2->netlog2_trusted_dc_name; #if !defined(NT4_BUILD) // // Compose the domain\dc string for the reset command so it will not change // DCs as a result of the reset. This only works with NT5 or later NetLogon. // strResetTarget += L"\\"; strResetTarget += pNetlogonInfo2->netlog2_trusted_dc_name + 2; // skip the UNC double slashes #endif } else { if (ERROR_NO_LOGON_SERVERS == netStatus) { // This is the error returned when the SC has not yet been set up. // It is also returned if no DCs are reachable. DsGetDcName is called with the // force flag to discover if any DCs are reachable on the net. // PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; DWORD dwRet = NO_ERROR; #if !defined(NT4_BUILD) dwRet = DsGetDcName(NULL, pwzTrustedDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDCInfo); #endif if (NO_ERROR == dwRet) { // A DC is reachable, so it is safe to assume that the SC has not yet been // set up. Treat this as success. // netStatus = NERR_Success; TRACE(L"SC_QUERY has returned ERROR_NO_LOGON_SERVERS, SC not yet set up.\n"); #if !defined(NT4_BUILD) SetTrustedDCName(pDCInfo->DomainControllerName); NetApiBufferFree(pDCInfo); #endif } else { // If there are no DCs, there is nothing to be done except return the error. // TRACE(L"DsGetDcName /FORCE has returned %d, DC not found.\n", dwRet); // Save the error code and fixed by method SetTrustStatus(dwRet, VerifyStatusBroken); SetLastVerifiedTime(); return TRUE; } } else { TRACE(L"SC_QUERY has returned %d.\n", netStatus); } } NetApiBufferFree(pNetlogonInfo2); } else { TRACE(L"I_NetLogonControl2 has returned %d.\n", netStatus); } // // Do a trust PW verification if the other domain supports it. // if (PW_VERIFY == CheckLevel) { if (m_fPwVerifySupported) { netStatus = I_NetLogonControl2(NULL, NETLOGON_CONTROL_TC_VERIFY, 2, (LPBYTE)&pwzTrustedDomain, (LPBYTE *)&pNetlogonInfo2); if (NERR_Success == netStatus) { ASSERT(pNetlogonInfo2); netStatus = pNetlogonInfo2->netlog2_tc_connection_status; NetApiBufferFree(pNetlogonInfo2); } if (NERR_Success == netStatus) { TRACE(L"PW Verify successful on %s\n", pwzTrustedDomain); Status = VerifyStatusTrustOK; } else { if (ERROR_INVALID_LEVEL == netStatus || ERROR_NOT_SUPPORTED == netStatus || RPC_S_PROCNUM_OUT_OF_RANGE == netStatus || RPC_NT_PROCNUM_OUT_OF_RANGE == netStatus) { TRACE(L"NETLOGON_CONTROL_TC_VERIFY is not supported on %s\n", pwzTrustedDomain); m_fPwVerifySupported = FALSE; Status = VerifyStatusPwCheckNotSupported; netStatus = NERR_Success; // call it success since we don't know the true state } else { TRACE(L"NETLOGON_CONTROL_TC_VERIFY returned 0x%08x on %s\n", netStatus, pwzTrustedDomain); Status = VerifyStatusBroken; } } } else { Status = VerifyStatusPwCheckNotSupported; } } // // Try an SC Reset against the DC returned by the SC query // if (SC_RESET == CheckLevel) { PCWSTR pwzResetTarget = strResetTarget; netStatus = I_NetLogonControl2(NULL, NETLOGON_CONTROL_REDISCOVER, 2, (LPBYTE)&pwzResetTarget, (LPBYTE *)&pNetlogonInfo2); if (NERR_Success == netStatus) { ASSERT(pNetlogonInfo2); netStatus = pNetlogonInfo2->netlog2_tc_connection_status; NetApiBufferFree(pNetlogonInfo2); } if (NERR_Success == netStatus) { TRACE(L"SC_RESET successfull on %s\n", pwzResetTarget); Status = VerifyStatusRediscover; } else { TRACE(L"SC_RESET returned 0x%08x on %s\n", netStatus, pwzResetTarget); } } #ifdef NT4_BUILD // // Force trust pw replication from PDC to BDCs; only works on pre-W2K. // if (netStatus != NERR_Success) { // perform only once, ignore the result ForceReplication(); } #endif // // If still in an error state, do an SC reset against any DC // if (netStatus != NERR_Success) { netStatus = ForceRediscover(NULL, &strDCName); if (NERR_Success == netStatus) { Status = VerifyStatusRediscover; SetTrustedDCName(const_cast((PCWSTR)strDCName)); } } // // Walk through the DCs trying to establish an SC: TRCHK_RETARGET_ON_ERROR // if (NERR_Success != netStatus) { vector dcList; LPBYTE pbuf = NULL; TRACE(L"Attempting to retarget...\n"); // // Enumerate all DCs in the trusted domain // Attempt reconnecting to another DC. // // The returned value is not recorded. // (if not enumerated, skip this step) // if( NERR_Success == GetDCList(strDCName, dcList, &pbuf)) { // // Try to connect to every DC until success // for (vector::iterator ppszDCName = dcList.begin(); NERR_Success != netStatus && ppszDCName != dcList.end(); ppszDCName++) { netStatus = ForceRediscover(*ppszDCName, &strDCName); } } if (NERR_Success == netStatus) { SetTrustedDCName(const_cast((PCWSTR)strDCName)); Status = VerifyStatusRetarget; } // // Clean up the DC list // if (pbuf) { VERIFY( NERR_Success == NetApiBufferFree(pbuf)); } } // Save the error code and Status SetTrustStatus(netStatus, Status); SetLastVerifiedTime(); return TRUE; } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::SetLastVerifiedTime // // Synopsis: Record the time of verification. // //----------------------------------------------------------------------------- void CTrustInfo::SetLastVerifiedTime(void) { SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, (LPFILETIME)&m_liLastVerified); } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::IsVerificationStale // // Synopsis: Checks to see if the last verification time is older than the // passed in criteria. // // Returns: TRUE if older. // // Notes: If the trust hasn't been verified (m_liLastVerified == 0), // then the verification is defined to be stale. // //----------------------------------------------------------------------------- BOOL CTrustInfo::IsVerificationStale(LARGE_INTEGER liMaxAge) { TRACE(L"CTrustInfo::IsVerificationStale(0x%08x), MaxAge = %d\n", this, liMaxAge.QuadPart / TRUSTMON_FILETIMES_PER_MINUTE); BOOL fStale = FALSE; LARGE_INTEGER liCurrentTime; SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, (LPFILETIME)&liCurrentTime); //TRACE(L"\tlast: %I64d, cur: %I64d, max: %I64d\n", m_liLastVerified, liCurrentTime, liMaxAge); fStale = (m_liLastVerified.QuadPart + liMaxAge.QuadPart) < liCurrentTime.QuadPart; return fStale; } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::GetDCList // // Synopsis: Enumerate all DCs in a domain and return a list in random order. // //----------------------------------------------------------------------------- NET_API_STATUS CTrustInfo::GetDCList(PCWSTR pszKnownServer, // OPTIONAL The server name to be placed in the end of the list vector & dcList, // Vector of PCWSTRs, pointing to the DC names inside pbufptr LPBYTE * pbufptr ) // This buffer must be freed with NetApiBufferFree when done. { TRACE(L"CTrustInfo::GetDCList\n"); ASSERT( pbufptr ); ASSERT( !(*pbufptr) ); NET_API_STATUS netStatus = NERR_Success; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; DWORD dwResumeHandle = 0; DWORD dwIndKnownServer = MAXDWORD; DWORD dwInd = 0; do { // Init dcList.clear(); // // Enumerate all the servers belonging to the specified domain // netStatus = NetServerEnum( NULL, 100, // SERVER_INFO_100 pbufptr, MAX_PREFERRED_LENGTH, & dwEntriesRead, & dwTotalEntries, SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL, GetTrustedDomain(), & dwResumeHandle ); TRACE(L"NetServerEnum returned 0x%08x! (%d entries)\n", netStatus, dwEntriesRead); if( netStatus == ERROR_MORE_DATA ) { // should never happen (no enum handle) ASSERT( FALSE ); // process whatever NetServerEnum returned. netStatus = NERR_Success; } if( netStatus != NERR_Success || !dwEntriesRead || !(*pbufptr) ) { TRACE(L"Failure, exiting...\n"); dcList.clear(); if( *pbufptr ) { VERIFY( NERR_Success == NetApiBufferFree( *pbufptr ) ); *pbufptr = NULL; } break; } // To simplify buffer access... PSERVER_INFO_100 pServerInfo100 = PSERVER_INFO_100( *pbufptr ); // Reserve enough space for all the entries dcList.reserve( dwEntriesRead ); // // Create a list of Servers // for( dwInd = 0; dwInd < dwEntriesRead; dwInd++ ) { if( pszKnownServer && !_wcsicmp( pszKnownServer, pServerInfo100[dwInd].sv100_name ) ) { dwIndKnownServer = dwInd; // postpone until the end } else { dcList.push_back( pServerInfo100[dwInd].sv100_name ); } } ASSERT( dwEntriesRead ); // // Known server should go to the end of the list // if( MAXDWORD != dwIndKnownServer ) { TRACE(L"Server %s placed @ the end\n", pszKnownServer); dcList.push_back( pServerInfo100[dwIndKnownServer].sv100_name ); // Shuffling should not include the last entry dwEntriesRead--; } // // Initialize randomizer // srand( (unsigned) time( NULL ) ); // // Shuffle by replacing each entry with another random entry // for( dwInd = 0; dwInd < (int) dwEntriesRead; dwInd++ ) { DWORD dwRandPos = DWORD( rand() % dwEntriesRead ); if( dwRandPos == dwInd ) continue; // Swap! LPWSTR pstrTemp = dcList[ dwRandPos ]; dcList[ dwRandPos ] = dcList[ dwInd ]; dcList[ dwInd ] = pstrTemp; } } while( FALSE ); return netStatus; } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::ForceRediscover // // Synopsis: // //----------------------------------------------------------------------------- NET_API_STATUS CTrustInfo::ForceRediscover(PCWSTR pstrDCName, CString * pstrDCNameRet) { TRACE(L"CTrustInfo::ForceRediscover\n"); NET_API_STATUS netStatus = NERR_Success; NETLOGON_INFO_2 * pNetlogonInfo2 = NULL; CString strTemp; PCWSTR pstrDomainName = GetTrustedDomain(); if( pstrDCName ) { // // Form domain\dc request // strTemp = pstrDomainName; strTemp += L"\\"; strTemp += pstrDCName; // Retarget pstrDomainName to the new string pstrDomainName = strTemp; } // // Attempt to re-establish trust // netStatus = I_NetLogonControl2( NULL, NETLOGON_CONTROL_REDISCOVER, 2, ( LPBYTE ) &pstrDomainName, ( LPBYTE *) &pNetlogonInfo2 ); TRACE(L"I_NetLogonControl2:NETLOGON_CONTROL_REDISCOVER to %s returned 0x%08x\n", pstrDomainName, netStatus); // // Clean-up // if( pNetlogonInfo2 ) { *pstrDCNameRet = pNetlogonInfo2->netlog2_trusted_dc_name; TRACE(L"netlog2_flags=0x%08x, netlog2_pdc_connection_status=0x%08x\n", pNetlogonInfo2->netlog2_flags, pNetlogonInfo2->netlog2_pdc_connection_status); TRACE(L"netlog2_trusted_dc_name=%s, netlog2_tc_connection_status=0x%08x\n", pNetlogonInfo2->netlog2_trusted_dc_name, pNetlogonInfo2->netlog2_tc_connection_status); NetApiBufferFree( pNetlogonInfo2 ); } return netStatus; } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::SetTrustStatus // // Synopsis: Set the status string based on the netStatus value if an error // else based on the VerifyStatus. // //----------------------------------------------------------------------------- void CTrustInfo::SetTrustStatus(ULONG netStatus, VerifyStatus Status) { WCHAR wzBuf[512]; m_trustStatus = netStatus; m_VerifyStatus = Status; if (NERR_Success == netStatus) { int nStrID; switch (Status) { case VerifyStatusNone: // // This is the default value for the Status parameter. // case VerifyStatusTrustOK: nStrID = IDS_TRUST_STATUS_OK; break; case VerifyStatusNotWindowsTrust: nStrID = IDS_MIT_TRUST_STATUS; break; case VerifyStatusNotOutboundTrust: nStrID = IDS_STATUS_INBOUND_ONLY; break; case VerifyStatusTrustNotChecked: nStrID = IDS_STATUS_NOT_CHECKED; break; case VerifyStatusPwCheckNotSupported: nStrID = IDS_PW_VERIFY_NOT_SUPPORTED; break; case VerifyStatusRetarget: nStrID = IDS_FIXED_BY_RETARGET; break; case VerifyStatusRediscover: nStrID = IDS_STATUS_REDISCOVER; break; case VerifyStatusBroken: ASSERT(FALSE); // shouldn't get here, fall through. default: nStrID = IDS_STATUS_UNKNOWN; } LoadString(_Module.GetModuleInstance(), nStrID, wzBuf, 512); m_strTrustStatus = wzBuf; } else { PWSTR pwzMsg; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, netStatus, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (PWSTR)&pwzMsg, 0, NULL)) { PWSTR pwzSuffix = wcsstr(pwzMsg, L"\r\n"); if (pwzSuffix) { *pwzSuffix = L'\0'; } m_strTrustStatus = pwzMsg; LocalFree(pwzMsg); } else { LoadString(_Module.GetModuleInstance(), IDS_TRUST_STATUS_FAILED, wzBuf, 512); m_strTrustStatus = wzBuf; } } } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::SetTrustDirectionFromFlags // //----------------------------------------------------------------------------- void CTrustInfo::SetTrustDirectionFromFlags(ULONG ulFlags) { m_ulTrustDirection = 0; if (DS_DOMAIN_DIRECT_OUTBOUND & ulFlags) { m_ulTrustDirection = TRUST_DIRECTION_OUTBOUND; } if (DS_DOMAIN_DIRECT_INBOUND & ulFlags) { m_ulTrustDirection |= TRUST_DIRECTION_INBOUND; } } //+---------------------------------------------------------------------------- // // Method: CTrustInfo::SetSid // //----------------------------------------------------------------------------- BOOL CTrustInfo::SetSid(PSID pSid) { if (!pSid) { return TRUE; } #if !defined(NT4_BUILD) PWSTR buffer; BOOL fRet = ConvertSidToStringSid(pSid, &buffer); if (fRet) { m_strSid = buffer; LocalFree(buffer); } return fRet; #else // TODO: Code for NT4 ?? #pragma message("need ConvertSidToStringSid for NT4"); #endif } #ifdef NT4_BUILD //+---------------------------------------------------------------------------- // // Function: ForceReplication // // Synopsis: Force local Domain Replication -- works only for NT4 domains // //----------------------------------------------------------------------------- NET_API_STATUS ForceReplication(void) { TRACE(L"ForceReplication\n"); NET_API_STATUS netStatus = NERR_Success; LPBYTE pbInputDataPtr = NULL; PNETLOGON_INFO_1 pNetlogonInfo1 = NULL; netStatus = I_NetLogonControl2( NULL, NETLOGON_CONTROL_REPLICATE, 1, (LPBYTE ) &pbInputDataPtr, (LPBYTE *) &pNetlogonInfo1 ); TRACE(L"I_NetLogonControl2:NETLOGON_CONTROL_REPLICATE returned 0x%08x\n", netStatus); if( pNetlogonInfo1 ) { TRACE(L"netlog1_flags=0x%08x, netlog1_pdc_connection_status=0x%08x\n", pNetlogonInfo1->netlog1_flags, pNetlogonInfo1->netlog1_pdc_connection_status); NetApiBufferFree( pNetlogonInfo1 ); } return netStatus; } #endif //NT4_BUILD