WindowsXP-SP1/admin/display/proppage/admin/trustwiz2.cxx
2020-09-30 16:53:49 +02:00

2029 lines
58 KiB
C++

//+----------------------------------------------------------------------------
//
// Windows NT Active Directory Domains and Trust
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2001
//
// File: trustwiz2.cxx
//
// Contents: More Domain trust creation wizard.
//
// History: 28-Sept-00 EricB created
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "proppage.h"
#include "trust.h"
#include "trustwiz.h"
#include <lmerr.h>
#include <crypt.h>
#ifdef DSADMIN
//+----------------------------------------------------------------------------
//
// Method: CallMember::Invoke subclasses
//
//-----------------------------------------------------------------------------
HRESULT
CallPolicyRead::Invoke(void)
{
TRACER(CallPolicyRead,Invoke);
HRESULT hr;
hr = _pWiz->RetryCollectInfo();
_pWiz->CredMgr.Revert();
return hr;
}
HRESULT
CallTrustExistCheck::Invoke(void)
{
TRACER(CallTrustExistCheck,Invoke);
HRESULT hr;
hr = _pWiz->RetryContinueCollectInfo();
_pWiz->CredMgr.Revert();
return hr;
}
//+----------------------------------------------------------------------------
//
// Method: CCreds::Impersonate
//
//-----------------------------------------------------------------------------
DWORD
CCreds::Impersonate(void)
{
TRACER(CCreds,Impersonate);
if (!_fSet)
{
return NO_ERROR;
}
if (_strUser.IsEmpty())
{
dspAssert(FALSE);
return ERROR_INVALID_PARAMETER;
}
DWORD dwErr = NO_ERROR;
if (!_hToken)
{
// The PW is encrypted, decrypt it before using it.
WCHAR wzPw[CREDUI_MAX_PASSWORD_LENGTH+1] = {0};
RtlCopyMemory(wzPw, _strPW, _cbPW);
RtlDecryptMemory(wzPw, _cbPW, 0);
if (!LogonUser(_strUser,
_strDomain,
wzPw,
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_WINNT50,
&_hToken))
{
dwErr = GetLastError();
dspDebugOut((DEB_ITRACE, "LogonUser failed with error %d on user name %ws\n",
dwErr, _strUser.GetBuffer(0)));
return dwErr;
}
RtlZeroMemory(wzPw, _cbPW);
// Because the login uses the LOGON32_LOGON_NEW_CREDENTIALS flag, no
// attempt is made to use the credentials until a remote resource is
// accessed. Thus, we don't yet know if the user entered credentials are
// valid at this point.
}
if (!ImpersonateLoggedOnUser(_hToken))
{
dwErr = GetLastError();
dspDebugOut((DEB_ITRACE, "ImpersonateLoggedOnUser failed with error %d on user name %ws\n",
dwErr, _strUser.GetBuffer(0)));
return dwErr;
}
_fImpersonating = TRUE;
return NO_ERROR;
}
//+----------------------------------------------------------------------------
//
// Method: CCreds::Revert
//
//-----------------------------------------------------------------------------
void
CCreds::Revert()
{
if (_fImpersonating)
{
TRACER(CCreds,Revert);
BOOL f = RevertToSelf();
dspAssert(f);
_fImpersonating = FALSE;
}
}
//+----------------------------------------------------------------------------
//
// Method: CCreds::Clear
//
//-----------------------------------------------------------------------------
void
CCreds::Clear(void)
{
Revert();
_strUser.Empty();
_strPW.Empty();
if (_hToken)
{
CloseHandle(_hToken);
_hToken = NULL;
}
_fSet = FALSE;
}
//+----------------------------------------------------------------------------
//
// Method: CCreds::SetUserAndPW
//
//-----------------------------------------------------------------------------
DWORD
CCreds::SetUserAndPW(PCWSTR pwzUser, PCWSTR pwzPW, PCWSTR pwzDomain)
{
DWORD dwErr = NO_ERROR;
WCHAR wzName[CRED_MAX_USERNAME_LENGTH+1] = {0},
wzDomain[CRED_MAX_USERNAME_LENGTH+1] = {0},
wzPw[CREDUI_MAX_PASSWORD_LENGTH+1+4] = {0}; // add 4 so that the PW size can be rounded up to 8 byte boundary.
dwErr = CredUIParseUserName(pwzUser, wzName, CRED_MAX_USERNAME_LENGTH,
wzDomain, CRED_MAX_USERNAME_LENGTH);
if (NO_ERROR == dwErr)
{
_strUser = wzName;
_strDomain = wzDomain;
}
else
{
if (ERROR_INVALID_PARAMETER == dwErr ||
ERROR_INVALID_ACCOUNT_NAME == dwErr)
{
// CredUIParseUserName returns this error if the user entered name
// does not include a domain. Default to the target domain.
//
_strUser = pwzUser;
if (pwzDomain)
{
_strDomain = pwzDomain;
}
}
else
{
dspDebugOut((DEB_ITRACE, "CredUIParseUserName failed with error %d\n", dwErr));
return dwErr;
}
}
// Encrypt the PW, rounding up buffer size to 8 byte boundary. The
// RtlEncryptMemory function encrypts in place, so copy to a buffer that is
// large enough for the round-up. The buffer is null-initialized, so it is
// guaranteed to be null terminated after the copy.
//
wcsncpy(wzPw, pwzPW, CREDUI_MAX_PASSWORD_LENGTH);
_cbPW = static_cast<ULONG>(wcslen(wzPw) * sizeof(WCHAR));
ULONG roundup = 8 - (_cbPW % 8);
if (roundup < 8)
{
_cbPW += roundup;
}
NTSTATUS status = RtlEncryptMemory(wzPw, _cbPW, 0);
dspDebugOut((DEB_ITRACE, "CCreds::SetUserAndPW: RtlEncryptMemory returned status 0x%x\n", status));
_strPW.GetBufferSetLength(_cbPW + 2); // make sure there is room for a null after the encrypted PW.
_strPW = wzPw;
_fSet = TRUE;
return NO_ERROR;
}
//+----------------------------------------------------------------------------
//
// Method: CCreds::PromptForCreds
//
// Synopsis: Post a login dialog.
//
//-----------------------------------------------------------------------------
int
CCreds::PromptForCreds(PCWSTR pwzDomain, HWND hParent)
{
TRACE3(CCreds, PromptForCreds);
if (pwzDomain)
{
_strDomain = pwzDomain;
}
int nRet = (int)DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_CRED_ENTRY),
hParent, (DLGPROC)CredPromptProc, (LPARAM)this);
if (-1 == nRet)
{
REPORT_ERROR(GetLastError(), hParent);
return GetLastError();
}
return nRet;
}
//+----------------------------------------------------------------------------
//
// Function: CredPromptProc
//
// Synopsis: Trust credentials dialog proc
//
//-----------------------------------------------------------------------------
INT_PTR CALLBACK
CredPromptProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int code;
CCreds * pCreds = (CCreds*)GetWindowLongPtr(hDlg, DWLP_USER);
switch (uMsg)
{
case WM_INITDIALOG:
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
pCreds = (CCreds *)lParam;
dspAssert(pCreds);
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
SendDlgItemMessage(hDlg, IDC_CREDMAN, CRM_SETUSERNAMEMAX, CREDUI_MAX_USERNAME_LENGTH, 0);
SendDlgItemMessage(hDlg, IDC_CREDMAN, CRM_SETPASSWORDMAX, CREDUI_MAX_PASSWORD_LENGTH, 0);
WCHAR wzMsg[MAX_PATH+1];
PWSTR pwzMsgFmt;
if (!LoadStringToTchar(IDS_TRUST_LOGON_MSG, &pwzMsgFmt))
{
REPORT_ERROR(E_OUTOFMEMORY, hDlg);
return FALSE;
}
swprintf(wzMsg, pwzMsgFmt, pCreds->_strDomain);
SetDlgItemText(hDlg, IDC_MSG, wzMsg);
delete pwzMsgFmt;
return TRUE;
case WM_COMMAND:
code = GET_WM_COMMAND_CMD(wParam, lParam);
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
if (code == BN_CLICKED)
{
DWORD dwErr;
dspAssert(pCreds);
WCHAR wzName[CREDUI_MAX_USERNAME_LENGTH+1] = {0},
wzPw[CREDUI_MAX_PASSWORD_LENGTH+1] = {0};
Credential_GetUserName(GetDlgItem(hDlg, IDC_CREDMAN), wzName,
CREDUI_MAX_USERNAME_LENGTH);
Credential_GetPassword(GetDlgItem(hDlg, IDC_CREDMAN), wzPw,
CREDUI_MAX_PASSWORD_LENGTH);
dwErr = pCreds->SetUserAndPW(wzName, wzPw);
CHECK_WIN32_REPORT(dwErr, hDlg, ;);
EndDialog(hDlg, dwErr);
}
break;
case IDCANCEL:
if (code == BN_CLICKED)
{
EndDialog(hDlg, ERROR_CANCELLED);
}
break;
case IDC_CREDMAN:
if (CRN_USERNAMECHANGE == code)
{
BOOL fNameEntered = SendDlgItemMessage(hDlg, IDC_CREDMAN,
CRM_GETUSERNAMELENGTH,
0, 0) > 0;
EnableWindow(GetDlgItem(hDlg, IDOK), fNameEntered);
}
break;
}
break;
default:
return(FALSE);
}
return(TRUE);
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::GetPrompt
//
//-----------------------------------------------------------------------------
PCWSTR
CCredMgr::GetPrompt()
{
_strPrompt.LoadString(g_hInstance, _fRemote ?
_fAdmin ? IDS_CRED_ADMIN_OTHER_PROMPT :
IDS_CRED_USER_OTHER_PROMPT :
IDS_CRED_ADMIN_LOCAL_PROMPT);
return _strPrompt;
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::GetDomainPrompt
//
//-----------------------------------------------------------------------------
PCWSTR
CCredMgr::GetDomainPrompt(void)
{
CStrW strFormat;
strFormat.LoadString(g_hInstance, _fRemote ? IDS_CRED_OTHER_DOMAIN :
IDS_CRED_LOCAL_DOMAIN);
_strDomainPrompt.Format(strFormat, _strDomain);
return _strDomainPrompt;
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::GetSubTitle
//
//-----------------------------------------------------------------------------
PCWSTR
CCredMgr::GetSubTitle(void)
{
_strSubTitle.LoadString(g_hInstance, _fRemote ?
_fAdmin ? IDS_TW_CREDS_SUBTITLE_OTHER :
IDS_TW_CREDS_SUBTITLE_OTHER_NONADMIN :
IDS_TW_CREDS_SUBTITLE_LOCAL);
return _strSubTitle;
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::SetNextFcn
//
//-----------------------------------------------------------------------------
BOOL
CCredMgr::SetNextFcn(CallMember * pNext)
{
if (!pNext)
{
return FALSE;
}
if (_pNextFcn)
{
delete _pNextFcn;
}
_pNextFcn = pNext;
_fNewCall = TRUE;
return TRUE;
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::InvokeNext
//
//-----------------------------------------------------------------------------
int
CCredMgr::InvokeNext(void)
{
dspAssert(_pNextFcn);
return _pNextFcn->Invoke();
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::SaveCreds
//
//-----------------------------------------------------------------------------
DWORD
CCredMgr::SaveCreds(HWND hWndCredCtrl)
{
TRACER(CCredMgr,SaveCreds);
WCHAR wzName[CREDUI_MAX_USERNAME_LENGTH+1] = {0},
wzPw[CREDUI_MAX_PASSWORD_LENGTH+1] = {0};
Credential_GetUserName(hWndCredCtrl, wzName, CREDUI_MAX_USERNAME_LENGTH);
Credential_GetPassword(hWndCredCtrl, wzPw, CREDUI_MAX_PASSWORD_LENGTH);
if (_fRemote)
{
return _RemoteCreds.SetUserAndPW(wzName, wzPw, _strDomain);
}
else
{
return _LocalCreds.SetUserAndPW(wzName, wzPw, _strDomain);
}
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::Impersonate
//
//-----------------------------------------------------------------------------
DWORD
CCredMgr::Impersonate(void)
{
TRACER(CCredMgr,Impersonate);
if (_fRemote)
{
return _RemoteCreds.Impersonate();
}
else
{
return _LocalCreds.Impersonate();
}
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::ImpersonateAnonymous
//
//-----------------------------------------------------------------------------
DWORD
CCredMgr::ImpersonateAnonymous(void)
{
TRACER(CCredMgr,ImpersonateAnonymous);
DWORD dwErr = NO_ERROR;
HANDLE hThread = NULL;
hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
if (!hThread)
{
dwErr = GetLastError();
dspDebugOut((DEB_ITRACE, "OpenThread failed with error %d\n", dwErr));
return dwErr;
}
if (!ImpersonateAnonymousToken(hThread))
{
CloseHandle(hThread);
dwErr = GetLastError();
dspDebugOut((DEB_ITRACE, "ImpersonateAnonymousToken failed with error %d\n", dwErr));
return dwErr;
}
CloseHandle(hThread);
_fImpersonatingAnonymous = TRUE;
return NO_ERROR;
}
//+----------------------------------------------------------------------------
//
// Method: CCredMgr::Revert
//
//-----------------------------------------------------------------------------
void
CCredMgr::Revert(void)
{
if (_fImpersonatingAnonymous)
{
TRACER(CCredMgr,Revert);
RevertToSelf();
_fImpersonatingAnonymous = FALSE;
}
else
{
_LocalCreds.Revert();
_RemoteCreds.Revert();
}
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::RetryCollectInfo
//
// Synopsis: Called after LsaOpenPolicy failed with access-denied and then
// obtaining credentials to the remote domain.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::RetryCollectInfo(void)
{
HRESULT hr = S_OK;
CWaitCursor Wait;
hr = OtherDomain.OpenLsaPolicy(CredMgr);
if (FAILED(hr))
{
CHECK_HRESULT(hr, ;);
WizError.SetErrorString2Hr(hr);
_hr = hr;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
int nRet = GetDomainInfo();
if (nRet)
{
// nRet will be non-zero if an error occured.
//
return nRet;
}
return ContinueCollectInfo();
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::RetryContinueCollectInfo
//
// Synopsis: Called after TrustExistCheck got access-denied and then
// obtaining credentials to the local domain.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::RetryContinueCollectInfo(void)
{
TRACER(CNewTrustWizard,RetryContinueCollectInfo);
return ContinueCollectInfo(FALSE);
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::CollectInfo
//
// Synopsis: Attempt to contact the domains and collect the domain/trust info.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::CollectInfo(void)
{
TRACER(CNewTrustWizard,CollectInfo);
CStrW strFmt, strErr;
CWaitCursor Wait;
int nRet = 0;
HRESULT hr = OtherDomain.DiscoverDC();
if (FAILED(hr))
{
CHECK_HRESULT(hr, ;);
WizError.SetErrorString2Hr(hr);
_hr = hr;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
if (OtherDomain.IsFound())
{
hr = OtherDomain.OpenLsaPolicy(CredMgr);
if (FAILED(hr))
{
CredMgr.Revert();
switch (hr)
{
case HRESULT_FROM_WIN32(STATUS_ACCESS_DENIED):
case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED):
//
// Normally an anonymous token is good enough to read the domain
// policy. However, if restrict anonymous is set, then real domain
// creds are needed. That must be the case if we are here.
// Go to the credentials page. If the creds are good, then
// call RetryCollectInfo() which will call GetDomainInfo
//
CredMgr.DoRemote();
CredMgr.SetAdmin(FALSE);
CredMgr.SetDomain(OtherDomain.GetUserEnteredName());
if (!CredMgr.SetNextFcn(new CallPolicyRead(this)))
{
WizError.SetErrorString2Hr(E_OUTOFMEMORY);
_hr = E_OUTOFMEMORY;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
return IDD_TRUSTWIZ_CREDS_PAGE;
default:
CHECK_HRESULT(hr, ;);
WizError.SetErrorString2Hr(hr);
_hr = hr;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
nRet = GetDomainInfo();
if (nRet)
{
// nRet will be non-zero if an error occured.
//
return nRet;
}
}
return ContinueCollectInfo();
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::ContinueCollectInfo
//
// Synopsis: Continues CollectInfo. Determines which pages to branch to
// next based on the collected info.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::ContinueCollectInfo(BOOL fPrompt)
{
CWaitCursor Wait;
// Set the trust type value.
//
if (OtherDomain.IsFound())
{
if (OtherDomain.IsUplevel())
{
// First, make sure the user isn't trying to create trust to the same
// domain.
//
if (_wcsicmp(TrustPage()->GetDnsDomainName(), OtherDomain.GetDnsName()) == 0)
{
WizError.SetErrorString1(IDS_TWERR_NOT_TO_SELF1);
WizError.SetErrorString2(IDS_TWERR_NOT_TO_SELF2);
_hr = E_FAIL;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
// TRUST_TYPE_UPLEVEL
//
Trust.SetTrustTypeUplevel();
}
else
{
// TRUST_TYPE_DOWNLEVEL
//
Trust.SetTrustTypeDownlevel();
}
}
else
{
// Only Realm trust is possible
//
// TRUST_TYPE_MIT
//
Trust.SetTrustTypeRealm();
}
// See if the trust is external. It is external if the other domain is
// downlevel or if they are not in the same forest.
//
Trust.SetExternal(!(OtherDomain.IsUplevel() &&
(_wcsicmp(OtherDomain.GetForestName(), TrustPage()->GetForestName()) == 0)
)
);
// Look for an existing trust and if found save the state.
//
int nRet = TrustExistCheck(fPrompt);
if (nRet)
{
// nRet will be non-zero if creds are needed or an error occured.
//
return nRet;
}
if (Trust.Exists())
{
// If an existing two-way trust, then nothing to do, otherwise a
// one-way trust can be made bi-di.
//
if (Trust.GetTrustDirection() == TRUST_DIRECTION_BIDIRECTIONAL)
{
// Nothing to do, bail.
//
WizError.SetErrorString1((Trust.GetTrustType() == TRUST_TYPE_MIT) ?
IDS_TWERR_REALM_ALREADY_EXISTS : IDS_TWERR_ALREADY_EXISTS);
WizError.SetErrorString2((Trust.GetTrustType() == TRUST_TYPE_MIT) ?
IDS_TWERR_REALM_CANT_CHANGE : IDS_TWERR_CANT_CHANGE);
_hr = E_FAIL;
return IDD_TRUSTWIZ_ALREADY_EXISTS_PAGE;
}
else
{
return IDD_TRUSTWIZ_BIDI_PAGE;
}
}
if (OtherDomain.IsFound())
{
// If the local domain qualifies for cross-forest trust and the other
// domain is external and root, then prompt for trust type.
//
if (_pTrustPage->QualifiesForestTrust() && Trust.IsExternal())
{
// Check if the other domain is the enterprise root.
//
int nRet = OtherDomainForestRootCheck();
if (nRet)
{
// An error occurred.
//
return nRet;
}
if (OtherDomainIsForestRoot())
{
return IDD_TRUSTWIZ_EXTERN_OR_FOREST_PAGE;
}
}
// New external or shortcut trust.
//
return IDD_TRUSTWIZ_DIRECTION_PAGE;
}
// Other domain not found, ask if user wants Realm trust.
//
return IDD_TRUSTWIZ_WIN_OR_MIT_PAGE;
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::GetDomainInfo
//
// Synopsis: Read the naming info from the remote domain.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::GetDomainInfo(void)
{
TRACER(CNewTrustWizard,GetDomainInfo);
HRESULT hr = S_OK;
// Read the LSA domain policy naming info for the other domain (flat and DNS
// names, SID, up/downlevel, forest root).
//
hr = OtherDomain.ReadDomainInfo();
CredMgr.Revert();
if (FAILED(hr))
{
CHECK_HRESULT(hr, ;);
WizError.SetErrorString2Hr(hr);
_hr = hr;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
return 0;
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::OtherDomainForestRootCheck
//
// Synopsis: See if this domain is the enterprise root.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::OtherDomainForestRootCheck(void)
{
HRESULT hr = S_OK;
hr = OtherDomain.IsForestRoot(&_fIsForestRoot);
if (FAILED(hr))
{
CHECK_HRESULT(hr, ;);
WizError.SetErrorString2Hr(hr);
_hr = hr;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
return 0;
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::CreateOrUpdateTrust
//
// Synopsis: When called, enough information has been gathered to create
// the trust. All of this info is in the Trust member object.
// Go ahead and create the trust.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::CreateOrUpdateTrust(void)
{
TRACER(CNewTrustWizard, CreateOrUpdateTrust);
HRESULT hr = S_OK;
DWORD dwErr = NO_ERROR;
if (CredMgr.IsLocalSet())
{
dwErr = CredMgr.ImpersonateLocal();
if (ERROR_SUCCESS != dwErr)
{
WizError.SetErrorString2Hr(dwErr);
_hr = HRESULT_FROM_WIN32(dwErr);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
CPolicyHandle cPolicy(TrustPage()->GetDomainDcName());
dwErr = cPolicy.OpenReqAdmin();
if (ERROR_SUCCESS != dwErr)
{
dspDebugOut((DEB_ITRACE, "LsaOpenPolicy returned 0x%x\n", dwErr));
WizError.SetErrorString2Hr(dwErr);
_hr = HRESULT_FROM_WIN32(dwErr);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
if (Trust.Exists())
{
// This is a modify rather than create operation.
//
dwErr = Trust.DoModify(cPolicy, OtherDomain);
CredMgr.Revert();
if (ERROR_ALREADY_EXISTS == dwErr)
{
WizError.SetErrorString1(IDS_TWERR_ALREADY_EXISTS);
WizError.SetErrorString2(IDS_TWERR_CANT_CHANGE);
_hr = E_FAIL;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
if (ERROR_SUCCESS != dwErr)
{
// TODO: better error reporting
WizError.SetErrorString2Hr(dwErr);
_hr = HRESULT_FROM_WIN32(dwErr);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
else
{
// Create a new trust.
//
dwErr = Trust.DoCreate(cPolicy, OtherDomain);
CredMgr.Revert();
if (ERROR_SUCCESS != dwErr)
{
// TODO: better error reporting
WizError.SetErrorString2Hr(dwErr);
_hr = HRESULT_FROM_WIN32(dwErr);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
if (Trust.GetTrustType() == TRUST_TYPE_MIT)
{
// If Realm, go to trust OK page.
//
return IDD_TRUSTWIZ_COMPLETE_OK_PAGE;
}
return IDD_TRUSTWIZ_STATUS_PAGE;
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::TrustExistCheck
//
// Synopsis: Look for an existing trust and if found save the state.
//
// Returns: Zero for success or a page ID if creds are needed or an error
// occured.
//-----------------------------------------------------------------------------
int
CNewTrustWizard::TrustExistCheck(BOOL fPrompt)
{
TRACER(CNewTrustWizard,TrustExistCheck);
NTSTATUS Status = STATUS_SUCCESS;
DWORD dwErr = NO_ERROR;
PTRUSTED_DOMAIN_FULL_INFORMATION pFullInfo = NULL;
CPolicyHandle cPolicy(TrustPage()->GetDomainDcName());
dwErr = cPolicy.OpenReqAdmin();
if (ERROR_SUCCESS != dwErr)
{
if ((STATUS_ACCESS_DENIED == dwErr || ERROR_ACCESS_DENIED == dwErr) &&
fPrompt)
{
// send prompt message to creds page. If the creds are good then
// call TrustExistCheck().
//
CredMgr.DoRemote(FALSE);
CredMgr.SetAdmin();
CredMgr.SetDomain(TrustPage()->GetDnsDomainName());
if (!CredMgr.SetNextFcn(new CallTrustExistCheck(this)))
{
WizError.SetErrorString2Hr(E_OUTOFMEMORY);
_hr = E_OUTOFMEMORY;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
return IDD_TRUSTWIZ_CREDS_PAGE;
}
else
{
dspDebugOut((DEB_ITRACE, "LsaOpenPolicy returned 0x%x\n", dwErr));
WizError.SetErrorString2Hr(dwErr);
_hr = HRESULT_FROM_WIN32(dwErr);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
Status = Trust.Query(cPolicy, OtherDomain, NULL, &pFullInfo);
if (STATUS_SUCCESS == Status)
{
dspAssert(pFullInfo);
if (pFullInfo->Information.TrustType != Trust.GetTrustType())
{
// The current domain state is not the same as that stored on the TDO.
//
UINT nID1 = IDS_WZERR_TYPE_UNEXPECTED, nID2 = IDS_WZERR_TYPE_DELETE;
CStrW strErr, strFormat;
switch (pFullInfo->Information.TrustType)
{
case TRUST_TYPE_MIT:
//
// The domain was contacted and is a Windows domain, yet the TDO
// thinks it is an MIT trust.
//
nID1 = IDS_WZERR_TYPE_MIT;
break;
case TRUST_TYPE_DOWNLEVEL:
case TRUST_TYPE_UPLEVEL:
if (Trust.GetTrustType() == TRUST_TYPE_MIT)
{
//
// The domain cannot be contacted, yet the TDO says it is a
// Windows trust.
//
nID1 = IDS_WZERR_TYPE_WIN;
nID2 = IDS_WZERR_TYPE_NOT_FOUND;
}
break;
}
strFormat.LoadString(g_hInstance, nID1);
strErr.Format(strFormat, OtherDomain.GetUserEnteredName());
WizError.SetErrorString1(strErr);
WizError.SetErrorString2(IDS_WZERR_TYPE_DELETE);
_hr = E_FAIL;
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
Trust.SetExists();
Trust.SetTrustDirection(pFullInfo->Information.TrustDirection);
Trust.SetTrustAttr(pFullInfo->Information.TrustAttributes);
LsaFreeMemory(pFullInfo);
}
else
{
// If the object isn't found, then a trust doesn't already exist. That
// isn't an error. The CTrust::_fExists property is initilized to be
// FALSE. If some other status is returned, then that is an error.
//
if (STATUS_OBJECT_NAME_NOT_FOUND != Status)
{
dspDebugOut((DEB_ITRACE, "LsaQueryTDIBN returned 0x%x\n", Status));
WizError.SetErrorString2Hr(LsaNtStatusToWinError(dwErr));
_hr = HRESULT_FROM_WIN32(dwErr);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
return 0;
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::VerifyOutboundTrust
//
// Synopsis: Verify the outbound trust.
//
// Returns: Zero for success or a page ID if an error occured.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::VerifyOutboundTrust(void)
{
TRACER(CNewTrustWizard,VerifyOutboundTrust);
DWORD dwRet = NO_ERROR;
dspAssert(Trust.GetTrustDirection() & TRUST_DIRECTION_OUTBOUND);
if (CredMgr.IsLocalSet())
{
dwRet = CredMgr.ImpersonateLocal();
if (ERROR_SUCCESS != dwRet)
{
WizError.SetErrorString1(IDS_ERR_CANT_VERIFY_CREDS);
WizError.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
// Because the login uses the LOGON32_LOGON_NEW_CREDENTIALS flag, no
// attempt is made to use the credentials until a remote resource is
// accessed. Thus, we don't yet know if the user entered credentials are
// valid at this point. Use LsaOpenPolicy to do a quick check.
//
CPolicyHandle Policy(TrustPage()->GetDomainDcName());
dwRet = Policy.OpenReqAdmin();
if (ERROR_SUCCESS != dwRet)
{
WizError.SetErrorString1(IDS_ERR_CANT_VERIFY_CREDS);
WizError.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
Policy.Close();
}
VerifyTrust.VerifyOutbound(TrustPage()->GetDomainDcName(),
OtherDomain.GetDnsName());
CredMgr.Revert();
return 0;
}
//+----------------------------------------------------------------------------
//
// Method: CNewTrustWizard::VerifyInboundTrust
//
// Synopsis: Verify the inbound trust.
//
// Returns: Zero for success or a page ID if an error occured.
//
//-----------------------------------------------------------------------------
int
CNewTrustWizard::VerifyInboundTrust(void)
{
TRACER(CNewTrustWizard,VerifyInboundTrust);
DWORD dwRet = NO_ERROR;
dspAssert(Trust.GetTrustDirection() & TRUST_DIRECTION_INBOUND);
if (CredMgr.IsRemoteSet())
{
dwRet = CredMgr.ImpersonateRemote();
if (ERROR_SUCCESS != dwRet)
{
WizError.SetErrorString1(IDS_ERR_CANT_VERIFY_CREDS);
WizError.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
CPolicyHandle Policy(OtherDomain.GetUncDcName());
dwRet = Policy.OpenReqAdmin();
if (ERROR_SUCCESS != dwRet)
{
WizError.SetErrorString1(IDS_ERR_CANT_VERIFY_CREDS);
WizError.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
Policy.Close();
}
VerifyTrust.VerifyInbound(OtherDomain.GetDcName(),
TrustPage()->GetDnsDomainName());
CredMgr.Revert();
return 0;
}
//+----------------------------------------------------------------------------
//
// Method: CVerifyTrust::Verify
//
// Synopsis: Verify the trust and record the results.
//
//-----------------------------------------------------------------------------
DWORD
CVerifyTrust::Verify(PCWSTR pwzDC, PCWSTR pwzDomain, BOOL fInbound)
{
TRACER(CVerifyTrust,Verify);
dspDebugOut((DEB_ITRACE, "Verifying %ws on %ws to %ws\n",
fInbound ? L"inbound" : L"outbound", pwzDC, pwzDomain));
DWORD dwRet = NO_ERROR;
NET_API_STATUS NetStatus = NO_ERROR;
BOOL fVerifySupported = TRUE;
PNETLOGON_INFO_2 NetlogonInfo2 = NULL;
PDS_DOMAIN_TRUSTS rgDomains = NULL;
ULONG DomainCount = 0;
dspAssert(pwzDC && pwzDomain);
// DsEnumerateDomainTrusts will block if there is a NetLogon trust
// update in progress. Call it to insure that our trust changes are
// known by NetLogon before we do the query/reset.
//
dwRet = DsEnumerateDomainTrusts(const_cast<PWSTR>(pwzDC),
DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND,
&rgDomains, &DomainCount);
if (ERROR_SUCCESS == dwRet)
{
NetApiBufferFree(rgDomains);
}
else
{
dspDebugOut((DEB_ERROR,
"**** DsEnumerateDomainTrusts ERROR <%s @line %d> -> %d\n",
__FILE__, __LINE__, dwRet));
}
if (fInbound)
{
_fInboundVerified = TRUE;
}
else
{
_fOutboundVerified = TRUE;
}
// First try a non-destructive trust password verification.
//
NetStatus = I_NetLogonControl2(pwzDC,
NETLOGON_CONTROL_TC_VERIFY,
2,
(LPBYTE)&pwzDomain,
(LPBYTE *)&NetlogonInfo2);
if (NERR_Success == NetStatus)
{
dspAssert(NetlogonInfo2);
if (NETLOGON_VERIFY_STATUS_RETURNED & NetlogonInfo2->netlog2_flags)
{
// The status of the verification is in the
// netlog2_pdc_connection_status field.
//
NetStatus = NetlogonInfo2->netlog2_pdc_connection_status;
dspDebugOut((DEB_ITRACE,
"NetLogon SC verify for %ws on DC %ws gives verify status %d to DC %ws\n\n",
pwzDomain, pwzDC, NetStatus,
NetlogonInfo2->netlog2_trusted_dc_name));
}
else
{
NetStatus = NetlogonInfo2->netlog2_tc_connection_status;
dspDebugOut((DEB_ITRACE,
"NetLogon SC verify for %ws on pre-2474 DC %ws gives conection status %d to DC %ws\n\n",
pwzDomain, pwzDC, NetStatus,
NetlogonInfo2->netlog2_trusted_dc_name));
}
NetApiBufferFree(NetlogonInfo2);
}
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", pwzDC);
fVerifySupported = FALSE;
}
else
{
dspDebugOut((DEB_ITRACE,
"NetLogon SC Verify for %ws on DC %ws returns error 0x%x\n\n",
pwzDomain, pwzDC, NetStatus));
}
}
CStrW strResult;
PWSTR pwzErr = NULL;
if (NERR_Success == NetStatus)
{
strResult.LoadString(g_hInstance,
fInbound ? IDS_TRUST_VERIFY_INBOUND : IDS_TRUST_VERIFY_OUTBOUND);
AppendResultString(strResult, fInbound);
return NO_ERROR;
}
else
{
if (fVerifySupported)
{
// Ignore ERROR_NO_LOGON_SERVERS because that is often due to the SC
// just not being set up yet.
//
if (ERROR_NO_LOGON_SERVERS == NetStatus)
{
strResult.LoadString(g_hInstance, IDS_VERIFY_TCV_LOGSRV);
AppendResultString(strResult, fInbound);
AppendResultString(g_wzCRLF, fInbound);
}
else
{
// LoadErrorMessage calls FormatMessage for a message from the
// system and places a crlf at the end of the string,
// so don't place another afterwards.
LoadErrorMessage(NetStatus, 0, &pwzErr);
dspAssert(pwzErr);
if (!pwzErr)
{
pwzErr = L"";
}
strResult.FormatMessage(g_hInstance, IDS_VERIFY_TCV_FAILED, NetStatus, pwzErr);
delete [] pwzErr;
pwzErr = NULL;
AppendResultString(strResult, fInbound);
}
}
else
{
strResult.LoadString(g_hInstance, IDS_VERIFY_TCV_NSUPP);
AppendResultString(strResult, fInbound);
AppendResultString(g_wzCRLF, fInbound);
}
strResult.LoadString(g_hInstance, IDS_VERIFY_DOING_RESET);
AppendResultString(strResult, fInbound);
}
// Now try a secure channel reset.
//
NetStatus = I_NetLogonControl2(pwzDC,
NETLOGON_CONTROL_REDISCOVER,
2,
(LPBYTE)&pwzDomain,
(LPBYTE *)&NetlogonInfo2);
if (NERR_Success == NetStatus)
{
dspAssert(NetlogonInfo2);
NetStatus = NetlogonInfo2->netlog2_tc_connection_status;
dspDebugOut((DEB_ITRACE,
"NetLogon SC reset for %ws on DC %ws gives status %d to DC %ws\n\n",
pwzDomain, pwzDC, NetStatus,
NetlogonInfo2->netlog2_trusted_dc_name));
NetApiBufferFree(NetlogonInfo2);
}
else
{
dspDebugOut((DEB_ITRACE,
"NetLogon SC reset for %ws on DC %ws returns error 0x%x\n\n",
pwzDomain, pwzDC, NetStatus));
}
AppendResultString(g_wzCRLF, fInbound);
if (NERR_Success == NetStatus)
{
strResult.LoadString(g_hInstance,
fInbound ? IDS_TRUST_VERIFY_INBOUND : IDS_TRUST_VERIFY_OUTBOUND);
return NO_ERROR;
}
else
{
LoadErrorMessage(NetStatus, 0, &pwzErr);
dspAssert(pwzErr);
if (!pwzErr)
{
pwzErr = L"";
}
strResult.FormatMessage(g_hInstance, IDS_VERIFY_RESET_FAILED, NetStatus, pwzErr);
delete [] pwzErr;
}
AppendResultString(strResult, fInbound);
SetResult(NetStatus, fInbound);
return NetStatus;
}
//+----------------------------------------------------------------------------
//
// Method: CVerifyTrust::ClearResults
//
//-----------------------------------------------------------------------------
void
CVerifyTrust::ClearResults(void)
{
TRACER(CVerifyTrust,ClearResults);
_strInboundResult.Empty();
_strOutboundResult.Empty();
_dwInboundResult = _dwOutboundResult = 0;
_fInboundVerified = _fOutboundVerified = FALSE;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::Query
//
// Synopsis: Read the TDO.
//
//-----------------------------------------------------------------------------
NTSTATUS
CTrust::Query(LSA_HANDLE hPolicy,
CRemoteDomain & OtherDomain,
PLSA_UNICODE_STRING pName, // optional, can be NULL
PTRUSTED_DOMAIN_FULL_INFORMATION * ppFullInfo)
{
NTSTATUS Status = STATUS_SUCCESS;
LSA_UNICODE_STRING Name = {0};
if (!pName)
{
pName = &Name;
}
if (_strTrustPartnerName.IsEmpty())
{
switch (GetTrustType())
{
case TRUST_TYPE_UPLEVEL:
_strTrustPartnerName = IsUpdated() ? OtherDomain.GetFlatName() :
OtherDomain.GetDnsName();
break;
case TRUST_TYPE_DOWNLEVEL:
_strTrustPartnerName = OtherDomain.GetFlatName();
break;
case TRUST_TYPE_MIT:
_strTrustPartnerName = OtherDomain.GetUserEnteredName();
break;
default:
dspAssert(FALSE);
return STATUS_INVALID_PARAMETER;
}
}
RtlInitUnicodeString(pName, _strTrustPartnerName);
Status = LsaQueryTrustedDomainInfoByName(hPolicy,
pName,
TrustedDomainFullInformation,
(PVOID *)ppFullInfo);
if (STATUS_OBJECT_NAME_NOT_FOUND == Status && !IsUpdated() &&
TRUST_TYPE_UPLEVEL == GetTrustType())
{
// If we haven't tried the flat name yet, try it now; can get here if a
// downlevel domain is upgraded to NT5. The name used the first time
// that Query is called would be the DNS name but the TDO would be
// named after the flat name.
//
dspDebugOut((DEB_ITRACE, "LsaQueryTDIBN: DNS name failed, trying flat name\n"));
RtlInitUnicodeString(pName, OtherDomain.GetFlatName());
Status = LsaQueryTrustedDomainInfoByName(hPolicy,
pName,
TrustedDomainFullInformation,
(PVOID *)ppFullInfo);
if (STATUS_SUCCESS == Status)
{
// Remember the fact that the flat name had to be used.
//
SetUpdated();
}
}
return Status;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::DoCreate
//
// Synopsis: Create the trust based on the settings in the CTrust object.
//
//-----------------------------------------------------------------------------
DWORD
CTrust::DoCreate(LSA_HANDLE hPolicy, CRemoteDomain & OtherDomain)
{
TRACER(CTrust, DoCreate);
NTSTATUS Status = STATUS_SUCCESS;
TRUSTED_DOMAIN_INFORMATION_EX tdix = {0};
LSA_AUTH_INFORMATION AuthData = {0};
TRUSTED_DOMAIN_AUTH_INFORMATION AuthInfoEx = {0};
RtlZeroMemory(&AuthData, sizeof(LSA_AUTH_INFORMATION));
RtlZeroMemory(&AuthInfoEx, sizeof(TRUSTED_DOMAIN_AUTH_INFORMATION));
GetSystemTimeAsFileTime((PFILETIME)&AuthData.LastUpdateTime);
AuthData.AuthType = TRUST_AUTH_TYPE_CLEAR;
AuthData.AuthInfoLength = static_cast<ULONG>(wcslen(GetTrustPW()) * sizeof(WCHAR));
AuthData.AuthInfo = (PUCHAR)GetTrustPW();
if (GetNewTrustDirection() & TRUST_DIRECTION_INBOUND)
{
AuthInfoEx.IncomingAuthInfos = 1;
AuthInfoEx.IncomingAuthenticationInformation = &AuthData;
AuthInfoEx.IncomingPreviousAuthenticationInformation = NULL;
}
if (GetNewTrustDirection() & TRUST_DIRECTION_OUTBOUND)
{
AuthInfoEx.OutgoingAuthInfos = 1;
AuthInfoEx.OutgoingAuthenticationInformation = &AuthData;
AuthInfoEx.OutgoingPreviousAuthenticationInformation = NULL;
}
tdix.TrustAttributes = GetNewTrustAttr();
switch (GetTrustType())
{
case TRUST_TYPE_UPLEVEL:
RtlInitUnicodeString(&tdix.Name, OtherDomain.GetDnsName());
RtlInitUnicodeString(&tdix.FlatName, OtherDomain.GetFlatName());
tdix.Sid = OtherDomain.GetSid();
tdix.TrustType = TRUST_TYPE_UPLEVEL;
SetTrustPartnerName(OtherDomain.GetDnsName());
break;
case TRUST_TYPE_DOWNLEVEL:
RtlInitUnicodeString(&tdix.Name, OtherDomain.GetFlatName());
RtlInitUnicodeString(&tdix.FlatName, OtherDomain.GetFlatName());
tdix.Sid = OtherDomain.GetSid();
tdix.TrustType = TRUST_TYPE_DOWNLEVEL;
SetTrustPartnerName(OtherDomain.GetFlatName());
break;
case TRUST_TYPE_MIT:
RtlInitUnicodeString(&tdix.Name, OtherDomain.GetUserEnteredName());
RtlInitUnicodeString(&tdix.FlatName, OtherDomain.GetUserEnteredName());
tdix.Sid = NULL;
tdix.TrustType = TRUST_TYPE_MIT;
tdix.TrustAttributes = TRUST_ATTRIBUTE_NON_TRANSITIVE;
SetTrustPartnerName(OtherDomain.GetUserEnteredName());
break;
default:
dspAssert(FALSE);
return ERROR_INVALID_PARAMETER;
}
tdix.TrustDirection = GetNewTrustDirection();
LSA_HANDLE hTrustedDomain;
Status = LsaCreateTrustedDomainEx(hPolicy,
&tdix,
&AuthInfoEx,
TRUSTED_SET_AUTH | TRUSTED_SET_POSIX,
&hTrustedDomain);
if (NT_SUCCESS(Status))
{
LsaClose(hTrustedDomain);
SetTrustDirection(GetNewTrustDirection());
SetTrustAttr(GetNewTrustAttr());
}
else
{
dspDebugOut((DEB_ITRACE, "LsaCreateTrustedDomainEx failed with error 0x%x\n",
Status));
}
return LsaNtStatusToWinError(Status);
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::DoModify
//
// Synopsis: Modify the trust based on the settings in the CTrust object.
//
//-----------------------------------------------------------------------------
DWORD
CTrust::DoModify(LSA_HANDLE hPolicy, CRemoteDomain & OtherDomain)
{
TRACER(CTrust, DoModify);
ULONG ulNewDir = GetTrustDirection() ^ GetNewTrustDirection();
BOOL fSetAttr = GetTrustAttr() != GetNewTrustAttr();
if (!ulNewDir & !fSetAttr)
{
// Nothing to do.
//
return ERROR_ALREADY_EXISTS;
}
NTSTATUS Status = STATUS_SUCCESS;
PTRUSTED_DOMAIN_FULL_INFORMATION pFullInfo = NULL;
LSA_UNICODE_STRING Name = {0};
Status = Query(hPolicy, OtherDomain, &Name, &pFullInfo);
if (STATUS_SUCCESS != Status)
{
dspDebugOut((DEB_ITRACE, "Trust.Query returned 0x%x\n", Status));
return LsaNtStatusToWinError(Status);
}
dspAssert(pFullInfo);
LSA_AUTH_INFORMATION AuthData = {0};
BOOL fSidSet = FALSE;
if (ulNewDir)
{
GetSystemTimeAsFileTime((PFILETIME)&AuthData.LastUpdateTime);
AuthData.AuthType = TRUST_AUTH_TYPE_CLEAR;
AuthData.AuthInfoLength = static_cast<ULONG>(GetTrustPWlen() * sizeof(WCHAR));
AuthData.AuthInfo = (PUCHAR)GetTrustPW();
if (TRUST_DIRECTION_INBOUND == ulNewDir)
{
// Adding Inbound.
//
pFullInfo->AuthInformation.IncomingAuthInfos = 1;
pFullInfo->AuthInformation.IncomingAuthenticationInformation = &AuthData;
pFullInfo->AuthInformation.IncomingPreviousAuthenticationInformation = NULL;
}
else
{
// Adding outbound.
//
pFullInfo->AuthInformation.OutgoingAuthInfos = 1;
pFullInfo->AuthInformation.OutgoingAuthenticationInformation = &AuthData;
pFullInfo->AuthInformation.OutgoingPreviousAuthenticationInformation = NULL;
}
pFullInfo->Information.TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL;
//
// Check for a NULL domain SID. The SID can be NULL if the inbound
// trust was created when the domain was NT4. MIT trusts don't have a SID.
//
if (!pFullInfo->Information.Sid && (TRUST_TYPE_MIT != GetTrustType()))
{
pFullInfo->Information.Sid = OtherDomain.GetSid();
fSidSet = TRUE;
}
}
if (fSetAttr)
{
pFullInfo->Information.TrustAttributes = GetNewTrustAttr();
}
Status = LsaSetTrustedDomainInfoByName(hPolicy,
&Name,
TrustedDomainFullInformation,
pFullInfo);
if (fSidSet)
{
// the SID memory is owned by OtherDomain, so don't free it here.
//
pFullInfo->Information.Sid = NULL;
}
LsaFreeMemory(pFullInfo);
if (NT_SUCCESS(Status))
{
SetTrustDirection(GetNewTrustDirection());
SetTrustAttr(GetNewTrustAttr());
}
return LsaNtStatusToWinError(Status);
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::SetMakeXForest
//
//-----------------------------------------------------------------------------
void
CTrust::SetMakeXForest(void)
{
if (!_dwNewAttr)
{
_dwNewAttr = _dwAttr;
}
_dwNewAttr |= TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::SetTrustAttr
//
//-----------------------------------------------------------------------------
void
CTrust::SetTrustAttr(DWORD attr)
{
TRACER(CTrust,SetTrustAttr);
_dwAttr = _dwNewAttr = attr;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::IsXForest
//
//-----------------------------------------------------------------------------
BOOL
CTrust::IsXForest(void)
{
return _dwAttr & TRUST_ATTRIBUTE_FOREST_TRANSITIVE ||
_dwNewAttr & TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::Clear
//
//-----------------------------------------------------------------------------
void
CTrust::Clear(void)
{
_strTrustPartnerName.Empty();
_strTrustPW.Empty();
_dwType = 0;
_dwDirection = 0;
_dwNewDirection = 0;
_dwAttr = 0;
_dwNewAttr = 0;
_fExists = FALSE;
_fUpdatedFromNT4 = FALSE;
_fExternal = FALSE;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::ReadFTInfo
//
// Synopsis: Read the forest trust name suffixes claimed by the trust
// partner.
//
// Arguments: [pwzLocalDC] - the name of the local DC.
// [pwzOtherDC] - the DC of the trust partner.
// [CredMgr] - credentials obtained earlier.
// [WizErr] - reference to the error object.
// [fCredErr] - if true, then the return value is a page ID.
//
// Returns: Page ID or error code depending on the value of fCredErr.
//
//-----------------------------------------------------------------------------
DWORD
CTrust::ReadFTInfo(PCWSTR pwzLocalDC, PCWSTR pwzOtherDC, CCredMgr & CredMgr,
CWizError & WizErr, bool & fCredErr)
{
TRACER(CTrust,ReadFTInfo);
DWORD dwRet = NO_ERROR;
if (!IsXForest() || _strTrustPartnerName.IsEmpty() || !pwzLocalDC ||
!pwzOtherDC)
{
dspAssert(FALSE);
return ERROR_INVALID_PARAMETER;
}
PLSA_FOREST_TRUST_INFORMATION pNewFTInfo = NULL;
if (GetNewTrustDirection() == TRUST_DIRECTION_INBOUND)
{
// Inbound-only trust must have the name fetch remoted to the other
// domain.
//
if (CredMgr.IsRemoteSet())
{
dwRet = CredMgr.ImpersonateRemote();
if (ERROR_SUCCESS != dwRet)
{
fCredErr = true;
WizErr.SetErrorString1(IDS_ERR_CANT_SAVE_CREDS);
WizErr.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
dwRet = DsGetForestTrustInformationW(pwzOtherDC,
NULL,
0,
&pNewFTInfo);
CredMgr.Revert();
if (NO_ERROR != dwRet)
{
return dwRet;
}
dspAssert(pNewFTInfo);
// Read the locally known names and then merge them with the names
// discovered from the other domain.
//
NTSTATUS status = STATUS_SUCCESS;
LSA_UNICODE_STRING TrustPartner = {0};
PLSA_FOREST_TRUST_INFORMATION pKnownFTInfo = NULL, pMergedFTInfo = NULL;
RtlInitUnicodeString(&TrustPartner, _strTrustPartnerName);
if (CredMgr.IsLocalSet())
{
dwRet = CredMgr.ImpersonateLocal();
if (ERROR_SUCCESS != dwRet)
{
NetApiBufferFree(pNewFTInfo);
fCredErr = true;
WizErr.SetErrorString1(IDS_ERR_CANT_SAVE_CREDS);
WizErr.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
CPolicyHandle cPolicy(pwzLocalDC);
dwRet = cPolicy.OpenNoAdmin();
dspAssert(!dwRet);
status = LsaQueryForestTrustInformation(cPolicy,
&TrustPartner,
&pKnownFTInfo);
if (STATUS_NOT_FOUND == status)
{
// no FT info stored yet, which is the expected state for a new trust.
//
status = STATUS_SUCCESS;
}
if (NO_ERROR != (dwRet = LsaNtStatusToWinError(status)))
{
NetApiBufferFree(pNewFTInfo);
CredMgr.Revert();
return dwRet;
}
if (pKnownFTInfo && pKnownFTInfo->RecordCount)
{
// Merge the two.
//
dwRet = DsMergeForestTrustInformationW(_strTrustPartnerName,
pNewFTInfo,
pKnownFTInfo,
&pMergedFTInfo);
NetApiBufferFree(pNewFTInfo);
CHECK_WIN32(dwRet, return dwRet);
dspAssert(pMergedFTInfo);
_FTInfo = pMergedFTInfo;
LsaFreeMemory(pKnownFTInfo);
LsaFreeMemory(pMergedFTInfo);
}
else
{
_FTInfo = pNewFTInfo;
NetApiBufferFree(pNewFTInfo);
}
// Now write the data. On return from the call the pColInfo struct
// will contain current collision data.
//
PLSA_FOREST_TRUST_COLLISION_INFORMATION pColInfo = NULL;
status = LsaSetForestTrustInformation(cPolicy,
&TrustPartner,
_FTInfo.GetFTInfo(),
FALSE,
&pColInfo);
CredMgr.Revert();
if (NO_ERROR != (dwRet = LsaNtStatusToWinError(status)))
{
return dwRet;
}
_CollisionInfo = pColInfo;
return NO_ERROR;
}
// Outbound or bi-di trust, call DsGetForestTrustInfo locally.
//
if (CredMgr.IsLocalSet())
{
dwRet = CredMgr.ImpersonateLocal();
if (ERROR_SUCCESS != dwRet)
{
fCredErr = true;
WizErr.SetErrorString1(IDS_ERR_CANT_SAVE_CREDS);
WizErr.SetErrorString2Hr(dwRet);
return IDD_TRUSTWIZ_FAILURE_PAGE;
}
}
dwRet = DsGetForestTrustInformationW(pwzLocalDC,
_strTrustPartnerName,
DS_GFTI_UPDATE_TDO,
&pNewFTInfo);
CredMgr.Revert();
if (NO_ERROR != dwRet)
{
return dwRet;
}
_FTInfo = pNewFTInfo;
NetApiBufferFree(pNewFTInfo);
return dwRet;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::WriteFTInfo
//
//-----------------------------------------------------------------------------
DWORD
CTrust::WriteFTInfo(PCWSTR pwzLocalDC)
{
TRACER(CTrust,WriteFTInfo);
DWORD dwRet;
if (!IsXForest() || _strTrustPartnerName.IsEmpty() || !_FTInfo.GetCount() ||
!pwzLocalDC)
{
dspAssert(FALSE);
return ERROR_INVALID_PARAMETER;
}
NTSTATUS status = STATUS_SUCCESS;
LSA_UNICODE_STRING Name = {0};
PLSA_FOREST_TRUST_COLLISION_INFORMATION pColInfo = NULL;
RtlInitUnicodeString(&Name, _strTrustPartnerName);
CPolicyHandle cPolicy(pwzLocalDC);
dwRet = cPolicy.OpenReqAdmin();
dspAssert(!dwRet);
status = LsaSetForestTrustInformation(cPolicy,
&Name,
_FTInfo.GetFTInfo(),
TRUE,
&pColInfo);
if (STATUS_SUCCESS != status)
{
return LsaNtStatusToWinError(status);
}
if (pColInfo && pColInfo->RecordCount)
{
PLSA_FOREST_TRUST_COLLISION_RECORD pRec = NULL;
PLSA_FOREST_TRUST_RECORD pFTRec = NULL;
for (UINT i = 0; i < pColInfo->RecordCount; i++)
{
pRec = pColInfo->Entries[i];
pFTRec = _FTInfo.GetFTInfo()->Entries[pRec->Index];
dspDebugOut((DEB_ITRACE, "Collision on record %d, type %d, flags 0x%x, name %ws\n",
pRec->Index, pRec->Type, pRec->Flags, pRec->Name.Buffer));
switch (pFTRec->ForestTrustType)
{
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
dspDebugOut((DEB_ITRACE, "Referenced FTInfo: %ws, type: TLN\n",
pFTRec->ForestTrustData.TopLevelName.Buffer));
pFTRec->Flags = pRec->Flags;
break;
case ForestTrustDomainInfo:
dspDebugOut((DEB_ITRACE, "Referenced FTInfo: %ws, type: Domain\n",
pFTRec->ForestTrustData.DomainInfo.DnsName.Buffer));
pFTRec->Flags = pRec->Flags;
break;
default:
break;
}
}
LsaFreeMemory(pColInfo);
}
status = LsaSetForestTrustInformation(cPolicy,
&Name,
_FTInfo.GetFTInfo(),
FALSE,
&pColInfo);
if (STATUS_SUCCESS != status)
{
return LsaNtStatusToWinError(status);
}
_CollisionInfo = pColInfo;
#if DBG == 1
if (pColInfo && pColInfo->RecordCount)
{
PLSA_FOREST_TRUST_COLLISION_RECORD pRec;
for (UINT i = 0; i < pColInfo->RecordCount; i++)
{
pRec = pColInfo->Entries[i];
dspDebugOut((DEB_ITRACE, "Collision on record %d, type %d, flags 0x%x, name %ws\n",
pRec->Index, pRec->Type, pRec->Flags, pRec->Name.Buffer));
}
}
#endif
return NO_ERROR;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::AreThereCollisions
//
//-----------------------------------------------------------------------------
BOOL
CTrust::AreThereCollisions(void)
{
if (_CollisionInfo.IsInConflict() || _FTInfo.IsInConflict())
{
return TRUE;
}
return FALSE;
}
//+----------------------------------------------------------------------------
//
// Method: CTrust::GetTrustDirStrID
//
//-----------------------------------------------------------------------------
int
CTrust::GetTrustDirStrID(DWORD dwDir)
{
switch (dwDir)
{
case TRUST_DIRECTION_INBOUND:
return IDS_TRUST_DIR_INBOUND_SHORTCUT;
case TRUST_DIRECTION_OUTBOUND:
return IDS_TRUST_DIR_OUTBOUND_SHORTCUT;
case TRUST_DIRECTION_BIDIRECTIONAL:
return IDS_TRUST_DIR_BIDI;
default:
return IDS_TRUST_DISABLED;
}
}
#endif // DSADMIN