WindowsXP-SP1/admin/select/src/bindinfo.cxx
2020-09-30 16:53:49 +02:00

714 lines
22 KiB
C++

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1998.
//
// File: bindinfo.cxx
//
// Contents: Class for keeping bind hanles in cache.
//
// Classes: CBindInfo
//
// History: 20-oct-2000 hiteshr Created
//
//---------------------------------------------------------------------------
#include "headers.hxx"
#pragma hdrstop
DEBUG_DECLARE_INSTANCE_COUNTER(CBindInfo)
//===========================================================================
//
// CBindInfo
//
//===========================================================================
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::CBindInfo
//
// Synopsis: ctor
//
// Arguments: [pBinder] - backpointer to parent
// [pwzDomainDns] - Bind to a dc of this domain.
//---------------------------------------------------------------------------
CBindInfo::CBindInfo(
CBinder *pBinder,
PCWSTR pwzDomainDns,
DWORD dwFlag):
m_pBinder(pBinder),
m_hrLastCredError(S_OK),
m_pNext(NULL),
m_hDs(0),
m_dwFlag(dwFlag)
{
//TRACE_CONSTRUCTOR(CBindInfo);
Dbg(DEB_BIND, "CBindInfo::CBindInfo(%x) '%ws'\n", this, pwzDomainDns);
DBG_INDENTER;
DEBUG_INCREMENT_INSTANCE_COUNTER(CBindInfo);
m_strDomainPath = pwzDomainDns;
//
// Truncate at a trailing dot because sometimes we'll get it as
// part of the server and sometimes we won't, and we don't want the
// string comparison of server names to fail to match because of it.
//
m_strDomainPath.strip(String::TRAILING, L'.');
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::IsForDomain
//
// Synopsis: Return TRUE if this has a container interface for server
// [pwzServer].
//
// Arguments: [flProvider] - PROVIDER_*
// [pwzServer] - server name
//
// History: 08-16-2000 DavidMun Created
//
//---------------------------------------------------------------------------
BOOL
CBindInfo::IsForDomain(
PCWSTR pwzDomain)
{
return !m_strDomainPath.icompare(pwzDomain);
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::Init
//
// Synopsis: Perform the first part of the initialization of this which
// can fail and is therefore done outside the ctor
//
// Arguments: [hwnd] - for binding
// [pwzPath] - NULL or path to bind to
// [riid] - identifies desired interface
// [ppv] - optional; on success filled with requested
// interface
//
// Returns: HRESULT
//
// History: 07-14-1998 DavidMun Created
//
// Notes: This method is only called once for each instance of this
// class. The _RepeatableInit() method may be called multiple
// times.
//
//---------------------------------------------------------------------------
HRESULT
CBindInfo::Init(
HWND hwnd)
{
TRACE_METHOD(CBindInfo, Init);
HRESULT hr = S_OK;
BOOL fUserIsLocal = IsCurrentUserLocalUser();
BOOL fUseDefaultCred = FALSE;
BOOL fPromptForCred = FALSE;
WCHAR wzUserName[MAX_PATH];
WCHAR wzUserNameCopy[MAX_PATH];
WCHAR wzPassword[MAX_PATH];
BOOL bDoneForceDiscovery = FALSE;
DWORD dwErr = ERROR_SUCCESS;
BOOL bHadDefaultCred = FALSE;
String strServer;
do
{
if (!m_strDomainPath.length())
{
hr = E_POINTER;
DBG_OUT_HRESULT(hr);
break;
}
//
//if User is logged in locally, don't attempt to bind
//Use DefaultCred
//
if (fUserIsLocal)
{
fUseDefaultCred = TRUE;
m_hrLastCredError = E_ACCESSDENIED;
}
while(true)
{
RPC_AUTH_IDENTITY_HANDLE AuthIdentity = 0;
if (fUseDefaultCred || fPromptForCred)
{
if(fPromptForCred)
{
Dbg(DEB_BIND, "Previous call tried default creds\n");
fPromptForCred = TRUE;
hr = _AskForCreds(hwnd, wzUserName, wzPassword);
BREAK_ON_FAIL_HRESULT(hr);
}
else
{
//
// Obtaining the container interface requires credentials. If
// there are default credentials available from the binder, try
// using those first. If they aren't available or result in a
// credential error, go into a loop prompting for and using
// credentials from user.
//
g_pBinder->GetDefaultCreds(PROVIDER_LDAP,
wzUserName,
wzPassword);
if (*wzUserName)
{
bHadDefaultCred = TRUE;
Dbg(DEB_BIND, "Using default credentials (%ws)\n", wzUserName);
}
else
{
Dbg(DEB_BIND, "No default credentials, prompting\n", hr);
fPromptForCred = TRUE;
hr = _AskForCreds(hwnd, wzUserName, wzPassword);
BREAK_ON_FAIL_HRESULT(hr);
}
}
//
//UserName is in the format Domain\UserName
//
LPWSTR pszDomain = NULL;
LPWSTR pszUser = NULL;
wcscpy(wzUserNameCopy,wzUserName);
LPTSTR pszWhack = wcschr(wzUserNameCopy,L'\\');
LPTSTR pszAt = wcschr(wzUserNameCopy, L'@');
if(pszWhack)
{
*pszWhack = L'\0';
pszUser = pszWhack + 1;
pszDomain = wzUserNameCopy;
}
else if(pszAt)
{
*pszAt = L'\0';
pszUser = wzUserNameCopy;
pszDomain = pszAt + 1;
}
else
{
ASSERT(FALSE);
}
if(pszUser && pszDomain)
{
dwErr = DsMakePasswordCredentials(pszUser,
pszDomain,
wzPassword,
&AuthIdentity);
hr = HRESULT_FROM_WIN32(dwErr);
BREAK_ON_FAIL_HRESULT(hr);
}
}
PDOMAIN_CONTROLLER_INFOW pDCInfo = NULL;
ULONG GetDcFlags = DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED;
if(m_dwFlag & OP_GC_SERVER_REQUIRED)
GetDcFlags |= DS_GC_SERVER_REQUIRED;
//If is DsGetDcName is not called already
if(!strServer.length())
{
dwErr = DsGetDcNameW(NULL,
m_strDomainPath.c_str(),
NULL,
NULL,
GetDcFlags,
&pDCInfo);
hr = HRESULT_FROM_WIN32(dwErr);
BREAK_ON_FAIL_HRESULT(hr);
//Get the DCNAme
strServer = pDCInfo->DomainControllerName;
LocalFree(pDCInfo);
pDCInfo = NULL;
}
//Bind
if(!AuthIdentity)
{
dwErr = DsBind(strServer.c_str(), NULL, &m_hDs);
}
else
{
dwErr = DsBindWithCred(strServer.c_str(),
NULL,
AuthIdentity,
&m_hDs);
DsFreePasswordCredentials(AuthIdentity);
AuthIdentity = 0;
}
hr = HRESULT_FROM_WIN32(dwErr);
if (FAILED(hr))
{
if (IsCredError(hr))
{
Dbg(DEB_BIND,
"NULL credentials returned credential hr %#x\n",
hr);
m_hrLastCredError = hr;
if(!fUseDefaultCred)
{
//
//Use Default Cred
//
fUseDefaultCred = TRUE;
continue;
}
if(!fPromptForCred)
{
//
//Prompt for Cred
//
fPromptForCred = TRUE;
continue;
}
//
//Keep Prompting until user presses cancel
//
continue;
}
else if(!bDoneForceDiscovery)
{
// Try again, the DC returned above was unavailable (i.e., the
// cache list was stale).
//
bDoneForceDiscovery = TRUE;
GetDcFlags |= DS_FORCE_REDISCOVERY;
dwErr = DsGetDcNameW(NULL,
m_strDomainPath.c_str(),
NULL,
NULL,
GetDcFlags,
&pDCInfo);
hr = HRESULT_FROM_WIN32(dwErr);
BREAK_ON_FAIL_HRESULT(hr);
strServer = pDCInfo->DomainControllerName;
LocalFree(pDCInfo);
pDCInfo = NULL;
//Bind
if(!AuthIdentity)
{
dwErr = DsBind(strServer.c_str(), NULL, &m_hDs);
}
else
{
dwErr = DsBindWithCred(strServer.c_str(),
NULL,
AuthIdentity,
&m_hDs);
DsFreePasswordCredentials(AuthIdentity);
AuthIdentity = 0;
}
hr = HRESULT_FROM_WIN32(dwErr);
if (FAILED(hr))
{
if (IsCredError(hr))
{
Dbg(DEB_BIND,
"NULL credentials returned credential hr %#x\n",
hr);
m_hrLastCredError = hr;
if(!fUseDefaultCred)
{
fUseDefaultCred = TRUE;
continue;
}
if(!fPromptForCred)
{
fPromptForCred = TRUE;
continue;
}
continue;
}
}
}
}
if(SUCCEEDED(hr) && fPromptForCred && !bHadDefaultCred)
{
g_pBinder->SetDefaultCreds(wzUserName,
wzPassword);
ZeroMemory(wzPassword, sizeof(wzPassword));
}
//If we fall through here, we don't need to reloop
break;
}
} while (0);
return hr;
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::~CBindInfo
//
// Synopsis: dtor
//
// History: 04-26-1998 DavidMun Created
//
//---------------------------------------------------------------------------
CBindInfo::~CBindInfo()
{
TRACE_DESTRUCTOR(CBindInfo);
DEBUG_DECREMENT_INSTANCE_COUNTER(CBindInfo);
if(m_hDs)
DsUnBind(&m_hDs);
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::_AskForCreds
//
// Synopsis: Dispatch based on whether we're running in the thread that
// created [hwndParent].
//
// Arguments: [hwndParent] - parent for modal password dialog
// [wzUserName] - buffer to fill with name entered by user
// [wzPassword] - buffer to fill with password entered by user
//
// Returns: S_OK if user enters credentials.
// E_* if password dialog couldn't be opened.
// Last credential error if user cancels password dialog.
//
// Modifies: *[wzUserName], *[wzPassword]
//
// History: 04-29-1998 DavidMun Created
//
// Notes: Called by CBindInfo::OpenObject when it gets a credential
// error.
//
// This method blocks until password dialog is closed.
//
//---------------------------------------------------------------------------
HRESULT
CBindInfo::_AskForCreds(
HWND hwndParent,
PWSTR wzUserName,
PWSTR wzPassword)
{
TRACE_METHOD(CBindInfo, _AskForCreds);
HRESULT hr;
//
// This method is called via g_pBinder->BindToObject which itself may
// be called from either thread.
//
// If we're running in the primary thread, use SendMessage to call
// the routine that invokes the password dialog.
//
// If in non-primary thread post message to main thread to
// pop up password dialog, then wait on event for it to complete.
//
ASSERT(!hwndParent || IsWindow(hwndParent));
if (!hwndParent
|| GetWindowLongPtr(hwndParent, DWLP_DLGPROC) != (LONG_PTR) CDlg::_DlgProc)
{
Dbg(DEB_TRACE, "hwndParent = %#x, invoking password dialog directly\n", hwndParent);
CPasswordDialog PasswordDlg(PROVIDER_LDAP,
m_strDomainPath.c_str(),
wzUserName,
wzPassword);
hr = PasswordDlg.DoModalDialog(hwndParent);
}
else if (GetWindowThreadProcessId(hwndParent, NULL) == GetCurrentThreadId())
{
hr = _AskForCredsViaSendMessage(hwndParent, wzUserName, wzPassword);
}
else
{
hr = _AskForCredsViaPostMessage(hwndParent, wzUserName, wzPassword);
}
if (hr == S_FALSE)
{
hr = m_hrLastCredError;
}
else if (SUCCEEDED(hr))
{
//
// If the username is neither in NT4 (domain\user) or UPN
// (user@domain) format, then the bind will definitely fail.
// If that's the case, change the name to domain\user, where
// domain is the name of the thing we're trying to get access
// to.
//
if (!wcschr(wzUserName, L'\\') && !wcschr(wzUserName, L'@'))
{
String strNewName(m_strDomainPath);
//
// Strip port number, if any
//
if (strNewName.find(L':') != String::npos)
{
strNewName.erase(strNewName.find(L':'),
String::npos);
}
//
// If the server looks like a DNS name (contains at least one
// '.' and one alpha character), use UPN format.
// Otherwise assume it is a netbios or ip address and use NT4
// format.
//
if (strNewName.find(L'.') != String::npos &&
find_if(strNewName.begin(), strNewName.end(), IsCharAlpha) !=
strNewName.end())
{
strNewName.insert(0, L"@");
strNewName.insert(0, wzUserName);
Dbg(DEB_TRACE,
"Replacing user name %ws with %ws\n",
wzUserName,
strNewName.c_str());
lstrcpyn(wzUserName, strNewName.c_str(), MAX_PATH);
}
else
{
strNewName += L"\\";
strNewName += wzUserName;
Dbg(DEB_TRACE,
"Replacing user name %ws with %ws\n",
wzUserName,
strNewName.c_str());
lstrcpyn(wzUserName, strNewName.c_str(), MAX_PATH);
}
}
}
return hr;
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::_AskForCredsViaSendMessage
//
// Synopsis: Use SendMessage to call the CDsBrowseMgr routine that invokes
// the password dialog.
//
// Arguments: See _AskForCreds.
//
// Returns: HRESULT from password dialog.
//
// History: 04-29-1998 DavidMun Created
//
// Notes: Called only from main thread. Blocks until password dialog
// closes.
//
//---------------------------------------------------------------------------
HRESULT
CBindInfo::_AskForCredsViaSendMessage(
HWND hwndParent,
PWSTR wzUserName,
PWSTR wzPassword)
{
TRACE_METHOD(CBindInfo, _AskForCredsViaSendMessage);
CRED_MSG_INFO cmi = {
PROVIDER_LDAP,
m_strDomainPath.c_str(),
wzUserName,
wzPassword,
NULL,
S_OK
};
SendMessage(hwndParent, OPM_PROMPT_FOR_CREDS, 0, (LPARAM)&cmi);
return cmi.hr;
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::_AskForCredsViaPostMessage
//
// Synopsis: Request that the main thread put up the password dialog.
//
// Arguments: See _AskForCreds.
//
// Returns: HRESULT from password dialog.
//
// History: 04-29-1998 DavidMun Created
//
// Notes: Called only from worker thread. Blocks until main thread
// sets event after password dialog closes.
//
//---------------------------------------------------------------------------
HRESULT
CBindInfo::_AskForCredsViaPostMessage(
HWND hwndParent,
PWSTR wzUserName,
PWSTR wzPassword)
{
TRACE_METHOD(CBindInfo, _AskForCredsViaPostMessage);
ASSERT(hwndParent && IsWindow(hwndParent));
CRED_MSG_INFO cmi = {
PROVIDER_LDAP,
m_strDomainPath.c_str(),
wzUserName,
wzPassword,
NULL,
S_OK
};
cmi.hPrompt = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!cmi.hPrompt)
{
DBG_OUT_LASTERROR;
cmi.hr = HRESULT_FROM_LASTERROR;
return cmi.hr;
}
PostMessage(hwndParent, OPM_PROMPT_FOR_CREDS, 0, (LPARAM)&cmi);
WaitForSingleObject(cmi.hPrompt, INFINITE);
CloseHandle(cmi.hPrompt);
return cmi.hr;
}
//+--------------------------------------------------------------------------
//
// Member: CBindInfo::_PopupCredErr
//
// Synopsis: Pop up a dialog indicating a failure with the credentials
// the user entered and block until the dialog is dismissed
//
// Arguments: [hwnd] - parent wiindow
// [ids] - resource id of dialog message
// [pwzUserName] - name used in creds
// [pwzError] - error received when using them
//
// History: 06-23-2000 DavidMun Created
//
// Notes: This method may safely be called from the worker thread
//
//---------------------------------------------------------------------------
void
CBindInfo::_PopupCredErr(
HWND hwnd,
ULONG ids,
PCWSTR pwzUserName,
PCWSTR pwzError)
{
TRACE_METHOD(CBindInfo, _PopupCredErr);
//
// If we're in same thread that created [hwnd], just pop up the message
//
if (!hwnd || GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
{
PopupMessage(hwnd, ids, pwzUserName, pwzError);
return;
}
//
// We're not running in the thread that created [hwnd], so post a message
// to it and ask it to display message.
//
POPUP_MSG_INFO mi = {
hwnd,
ids,
pwzUserName,
pwzError,
NULL
};
mi.hPrompt = CreateEvent(NULL, FALSE, FALSE, NULL);
if (mi.hPrompt)
{
PostMessage(hwnd,
OPM_POPUP_CRED_ERROR,
0,
reinterpret_cast<LPARAM>(&mi));
WaitForSingleObject(mi.hPrompt, INFINITE);
CloseHandle(mi.hPrompt);
}
else
{
DBG_OUT_LASTERROR;
}
}