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

4437 lines
134 KiB
C++

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1998.
//
// File: dsobject.cxx
//
// Contents: Class that represents a single object in the DS.
//
// Classes: CDsObject
//
// History: 08-07-1998 DavidMun Created
//
//---------------------------------------------------------------------------
#include "headers.hxx"
#pragma hdrstop
HRESULT
CrackName(IN HWND hwnd,
IN LPWSTR pwzNameIn,
IN DS_NAME_FORMAT FormatOffered,
IN LPWSTR pwzDomainName,
IN BOOL bCrackInExtForest,
OUT LPWSTR * ppwzResultName,
OUT PBOOL pbExtForest,
OUT PBOOL pbAddDollar);
void AddDollarToNameToCrack(IN DS_NAME_FORMAT FormatOffered,
String &strNameToCrack);
DEBUG_DECLARE_INSTANCE_COUNTER(CDsObject)
#define MAX_SEARCH_HITS 1000
#define MAX_SEARCH_HITS_STR L"1000"
#define NAME_QUERY_PAGE_TIME_LIMIT 45 // seconds
const Variant CDsObject::s_varEmpty;
//+--------------------------------------------------------------------------
//
// Member: CDsObject::CDsObject
//
// Synopsis: ctor
//
// Arguments: [idOwningScope] - id of scope which contains (owns) this
// [pwzName] - name typed by user
//
// History: 08-13-1998 DavidMun Created
//
//---------------------------------------------------------------------------
CDsObject::CDsObject(
ULONG idOwningScope,
PCWSTR pwzName)
{
Dbg(DEB_DSOBJECT,
"CDsObject::CDsObject(%x) user entry=%ws\n",
this,
pwzName);
DEBUG_INCREMENT_INSTANCE_COUNTER(CDsObject);
ASSERT(idOwningScope);
ASSERT(pwzName);
_SetFlag(DSO_FLAG_UNPROCESSED_USER_ENTRY);
_SetOwningScopeId(idOwningScope);
m_AttrValueMap[AK_USER_ENTERED_TEXT] = Variant(pwzName);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::CDsObject
//
// Synopsis: ctor
//
// Arguments: [idOwningScope] - id of scope which contains (owns) this
// [strName] - object RDN
// [strClass] - objectClass attribute value
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
CDsObject::CDsObject(
ULONG idOwningScope,
const String &strName,
const String &strClass)
{
Dbg(DEB_DSOBJECT,
"CDsObject::CDsObject(%x) Name=%ws, Class=%ws\n",
this,
strName.c_str(),
strClass.c_str());
DBG_INDENTER;
DEBUG_INCREMENT_INSTANCE_COUNTER(CDsObject);
ASSERT(idOwningScope);
m_AttrValueMap[AK_NAME] = Variant(strName.c_str());
m_AttrValueMap[AK_OBJECT_CLASS] = Variant(strClass.c_str());
_SetOwningScopeId(idOwningScope);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::CDsObject
//
// Synopsis: ctor
//
// Arguments: [Init] - contains various attribute values for this object
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
CDsObject::CDsObject(
const SDsObjectInit &Init)
{
Dbg(DEB_DSOBJECT,
"CDsObject::CDsObject(%x) Name=%ws, Localized Name=%ws, Class=%ws\n",
this,
Init.pwzName,
Init.pwzLocalizedName,
Init.pwzClass);
ASSERT(Init.idOwningScope);
DBG_INDENTER;
DEBUG_INCREMENT_INSTANCE_COUNTER(CDsObject);
_SetOwningScopeId(Init.idOwningScope);
if (Init.pwzName && *Init.pwzName)
{
m_AttrValueMap[AK_NAME] = Variant(Init.pwzName);
}
if (Init.pwzLocalizedName && *Init.pwzLocalizedName)
{
m_AttrValueMap[AK_LOCALIZED_NAME] = Variant(Init.pwzLocalizedName);
}
if (Init.pwzClass && *Init.pwzClass)
{
m_AttrValueMap[AK_OBJECT_CLASS] = Variant(Init.pwzClass);
}
if (Init.pwzADsPath && *Init.pwzADsPath)
{
m_AttrValueMap[AK_ADSPATH] = Variant(Init.pwzADsPath);
}
if (Init.pwzUpn && *Init.pwzUpn)
{
m_AttrValueMap[AK_USER_PRINCIPAL_NAME] = Variant(Init.pwzUpn);
}
if (Init.fDisabled)
{
_SetFlag(DSO_FLAG_DISABLED);
}
}
#define ADD_IF_SUCCEEDED(hr, idx) \
if (SUCCEEDED(hr)) \
{ \
ASSERT(!bstr.Empty()); \
m_AttrValueMap[idx] = Variant(bstr.c_str()); \
bstr.Clear(); \
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::CDsObject
//
// Synopsis: ctor
//
// Arguments: [idOwningScope] - id of scope which contains (owns) this
// [pADs] - pointer to interface on object
// represented by this
//
// History: 06-22-2000 DavidMun Created
//
// Notes: Initializes this by querying [pADs] for attribute values.
// Generally used to represent objects returned from downlevel
// scopes.
//
//---------------------------------------------------------------------------
CDsObject::CDsObject(
ULONG idOwningScope,
IADs *pADs)
{
Dbg(DEB_DSOBJECT,
"CDsObject::CDsObject(%x) pADs=%#x\n",
this,
pADs);
DBG_INDENTER;
DEBUG_INCREMENT_INSTANCE_COUNTER(CDsObject);
ASSERT(idOwningScope);
HRESULT hr;
Bstr bstr;
hr = pADs->get_Name(&bstr);
ADD_IF_SUCCEEDED(hr, AK_NAME);
if (SUCCEEDED(hr))
{
Dbg(DEB_TRACE, "name = %ws\n", GetName());
}
hr = pADs->get_Class(&bstr);
ADD_IF_SUCCEEDED(hr, AK_OBJECT_CLASS);
hr = pADs->get_ADsPath(&bstr);
ADD_IF_SUCCEEDED(hr, AK_ADSPATH);
if (IsDisabled(pADs))
{
_SetFlag(DSO_FLAG_DISABLED);
}
_SetOwningScopeId(idOwningScope);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::CDsObject
//
// Synopsis: copy ctor
//
// Arguments: [dso] - object to copy
//
// History: 03-06-2000 davidmun Created
//
//---------------------------------------------------------------------------
CDsObject::CDsObject(
const CDsObject &dso)
{
Dbg(DEB_DSOBJECT,
"CDsObject::CDsObject(%x) copying %#x name=%ws\n",
this,
&dso,
dso.GetName());
DBG_INDENTER;
DEBUG_INCREMENT_INSTANCE_COUNTER(CDsObject);
this->operator=(dso);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::CDsObject
//
// Synopsis: ctor
//
// Arguments: [idOwningScope] - id of scope which contains (owns) this
// [atvm] - attributes to copy
//
// History: 06-22-2000 DavidMun Created
//
// Notes: [idOwningScope] value overrides any previous value in [atvm]
//
//---------------------------------------------------------------------------
CDsObject::CDsObject(
ULONG idOwningScope,
const AttrValueMap &atvm):
m_AttrValueMap(atvm)
{
Dbg(DEB_DSOBJECT,
"CDsObject::CDsObject(%x) copying AttrValueMap, name=%ws\n",
this,
GetName());
DBG_INDENTER;
DEBUG_INCREMENT_INSTANCE_COUNTER(CDsObject);
_SetOwningScopeId(idOwningScope);
if (GetAttr(AK_USER_ACCT_CTRL).GetUI4() & UF_ACCOUNTDISABLE)
{
_SetFlag(DSO_FLAG_DISABLED);
}
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_SetOwningScopeId
//
// Synopsis: Store the id of the scope which contains (owns) this
//
// Arguments: [idOwningScope] - id of scope which contains (owns) this
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_SetOwningScopeId(
ULONG idOwningScope)
{
ASSERT(idOwningScope);
Variant &var = m_AttrValueMap[AK_FLAGS];
if (var.Empty())
{
V_VT(&var) = VT_UI8;
V_UI8(&var) = static_cast<ULONGLONG>(idOwningScope) << 32;
}
else
{
ASSERT(V_VT(&var) == VT_UI8);
ULONGLONG ullNewFlagsVal = (V_UI8(&var) & ULONG_MAX);
ullNewFlagsVal |= static_cast<ULONGLONG>(idOwningScope) << 32;
V_UI8(&var) = ullNewFlagsVal;
}
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::GetDisplayName
//
// Synopsis: Fill *[pstrDisplayName] with the string to display in the
// listview's "Name" column. May differ from object's RDN.
//
// Arguments: [pstrDisplayName] - filled with name to display
// [fForSelectionWell] - nonzero if name is being displayed in
// the selection dialog (as opposed to
// the browse dialog).
//
// History: 11-24-1998 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::GetDisplayName(
String *pstrDisplayName,
BOOL fForSelectionWell) const
{
BSTR bstrNameForDisplay = GetLocalizedName();
// If localized name available, use it in preference to RDN
if (!*bstrNameForDisplay)
{
bstrNameForDisplay = GetName();
}
// use UPN if available
if (*GetUpn())
{
*pstrDisplayName = String::format(g_wzColumn1Format,
bstrNameForDisplay,
GetUpn());
return;
}
if (!fForSelectionWell)
{
*pstrDisplayName = bstrNameForDisplay;
return;
}
do
{
//
// for the selection well, and there's no upn. if this
// represents a downlevel object, display name is NT4 format,
// i.e., DOMAIN\NAME, unless it's a downlevel object with
// a path derived from its SID, e.g. we want ANONYMOUS to be
// displayed as ANONYMOUS instead of NT AUTHORITY\ANONYMOUS.
//
if (_IsFlagSet(DSO_FLAG_HAS_DOWNLEVEL_SID_PATH))
{
break;
}
ULONG ulProvider = PROVIDER_UNKNOWN;
if (!*GetADsPath())
{
break;
}
(void) ProviderFlagFromPath(GetADsPath(), &ulProvider);
if (ulProvider != PROVIDER_WINNT)
{
break;
}
String strDisplayPath(GetADsPath());
size_t idxSlash = strDisplayPath.rfind(L'/');
if (!idxSlash || idxSlash == String::npos)
{
break;
}
*pstrDisplayName = strDisplayPath;
pstrDisplayName->erase(idxSlash, 1);
pstrDisplayName->insert(idxSlash, L"\\");
idxSlash = pstrDisplayName->rfind(L'/', idxSlash - 1);
if (idxSlash != String::npos)
{
pstrDisplayName->erase(0, idxSlash + 1);
}
} while (0);
if (pstrDisplayName->empty())
{
*pstrDisplayName = bstrNameForDisplay;
}
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::operator ==
//
// Synopsis: Compare this against [dsoRhs], returning TRUE if the name,
// class, provider, and DN of both match.
//
// Arguments: [sliRhs] - object to compare against this
//
// History: 08-08-1998 DavidMun Created
//
//---------------------------------------------------------------------------
BOOL
CDsObject::operator ==(
const CDsObject &dsoRhs) const
{
BOOL fEqual = FALSE;
Bstr bstrMyDN;
Bstr bstrRhsDn;
do
{
if (lstrcmpi(GetName(), dsoRhs.GetName()))
{
break;
}
//
// If exactly one of lhs or rhs is an unprocessed entry, treat
// them as different since the unprocessed entry may resolve to
// anything.
//
if (IsUnprocessedUserEntry() && !dsoRhs.IsUnprocessedUserEntry() ||
!IsUnprocessedUserEntry() && dsoRhs.IsUnprocessedUserEntry())
{
break;
}
//
// If both are unprocessed entries, compare their text
//
if (IsUnprocessedUserEntry() && dsoRhs.IsUnprocessedUserEntry())
{
fEqual = !lstrcmpi(GetAttr(AK_USER_ENTERED_TEXT).GetBstr(),
dsoRhs.GetAttr(AK_USER_ENTERED_TEXT).GetBstr());
break;
}
if (lstrcmpi(GetClass(), dsoRhs.GetClass()))
{
break;
}
//
// Objects have same relative name and class.
//
//
// If neither has a path, they're the same. If one has a path
// and one does not, they're different.
//
if (!*GetADsPath())
{
if (!*dsoRhs.GetADsPath())
{
fEqual = TRUE;
}
break;
}
else if (!*dsoRhs.GetADsPath())
{
break;
}
//
// If both use the WinNT provider, compare paths directly.
// If exactly one uses the WinNT provider, consider them
// different.
//
BSTR bstrADsPath = GetADsPath();
BSTR bstrRhsADsPath = dsoRhs.GetADsPath();
BOOL fWinNT = wcsstr(bstrADsPath, c_wzWinNTPrefix) != NULL;
BOOL fRhsWinNT = wcsstr(bstrRhsADsPath, c_wzWinNTPrefix) != NULL;
if (fWinNT && fRhsWinNT)
{
fEqual = !lstrcmpi(bstrADsPath, bstrRhsADsPath);
break;
}
else if (fWinNT || fRhsWinNT)
{
break;
}
//
// Neither uses the WinNT provider, and the RDNs are the same.
// compare the distinguished names. A string compare
// of the paths is insufficient, since they may use different
// providers or servers but actually have the same DN.
//
HRESULT hr;
hr = g_pADsPath->SetRetrieve(ADS_SETTYPE_FULL,
bstrADsPath,
ADS_FORMAT_X500_DN,
&bstrMyDN);
BREAK_ON_FAIL_HRESULT(hr);
hr = g_pADsPath->SetRetrieve(ADS_SETTYPE_FULL,
bstrRhsADsPath,
ADS_FORMAT_X500_DN,
&bstrRhsDn);
BREAK_ON_FAIL_HRESULT(hr);
if (lstrcmpi(bstrMyDN.c_str(), bstrRhsDn.c_str()))
{
break;
}
fEqual = TRUE;
} while (0);
return fEqual;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::Process
//
// Synopsis: Perform whatever work is required on this to make it ready
// to return in a data object.
//
// Arguments: [hwnd] - parent for error or multimatch
// dialogs
// [rop] - containing object picker instance
// [pdsolExtras] - llist to which to add items
// generated by processing user
// entries, NULL if single select
// [fMultiselect] - may differ from what [rop] reports if
// this object resides in a single-select
// richedit not in the base dialog (i.e.,
// in CDnDlg).
//
// Returns: NAME_PROCESS_RESULT
//
// Modifies: *[pdsolExtras]
//
// History: 08-10-1998 DavidMun Created
//
//---------------------------------------------------------------------------
NAME_PROCESS_RESULT
CDsObject::Process(
HWND hwnd,
const CObjectPicker &rop,
CDsObjectList *pdsolExtras)
{
TRACE_METHOD(CDsObject, Process);
NAME_PROCESS_RESULT npr = NPR_SUCCESS;
BOOL fDisplayedError = FALSE;
HRESULT hr = S_OK;
do
{
//
// If this has a string the user typed in, convert it to an
// object.
//
if (IsUnprocessedUserEntry())
{
ASSERT(!_IsFlagSet(DSO_FLAG_FETCHED_ATTRIBUTES));
ASSERT(!_IsFlagSet(DSO_FLAG_CONVERTED_PROVIDER));
_ProcessUserEntry(hwnd, rop, pdsolExtras, &npr);
if (NAME_PROCESSING_FAILED(npr))
{
fDisplayedError = TRUE;
break;
}
}
//
// If attribute fetch is required, do it
//
(void) _FetchAttributes(hwnd, rop);
//
// If provider conversion is required, do it
//
hr = _ConvertProvider(hwnd, rop, &npr);
BREAK_ON_FAIL_PROCESS_RESULT(npr);
} while (0);
if (npr == NPR_STOP_PROCESSING && !fDisplayedError)
{
String strError = GetErrorMessage(hr);
PopupMessage(hwnd,
IDS_CANNOT_PROCESS,
GetName(),
strError.c_str());
}
if (!NAME_PROCESSING_FAILED(npr))
{
_ClearFlag(DSO_FLAG_UNPROCESSED_USER_ENTRY);
}
return npr;
}
/*
Flags used when processing user entered text:
DSO_FLAG_MULTISELECT
indicates that the multiple match dialog should allow multiselect.
DSO_FLAG_IS_COMPUTER
The value for the AK_USER_ENTERED_TEXT attribute should be considered the
name of a computer.
DSO_FLAG_MIGHT_BE_UPN
The value for the AK_USER_ENTERED_TEXT attribute could represent a
userPrincipalName attribute value.
*/
#define DSO_NAME_PROCESSING_FLAG_MULTISELECT 0x00000001
#define DSO_NAME_PROCESSING_FLAG_IS_COMPUTER 0x00000002
#define DSO_NAME_PROCESSING_FLAG_MIGHT_BE_UPN 0x00000004
#define DSO_NAME_PROCESSING_FLAG_EXACT_UPN 0x00000008
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_ProcessUserEntry
//
// Synopsis: Convert the user-entered string to an actual
// object (if doing so generates more than one object, the
// second through last are put in [pdsolExtras]).
//
// Arguments: [hwnd] - object picker frame window
// [pdsolExtras] - NULL if single select
//
// Returns: S_OK - all items processed successfully
// S_FALSE - user cancelled a dialog, quit processing
// E_*
//
// Modifies: *[pdsolExtras]
//
// History: 08-12-1998 DavidMun Created
//
// Notes: The name in AK_USER_ENTERED_TEXT is a single name (i.e. not
// multiple semicolon delimited names) and has already had
// leading and trailing spaces removed.
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_ProcessUserEntry(
HWND hwnd,
const CObjectPicker &rop,
CDsObjectList *pdsolExtras,
NAME_PROCESS_RESULT *pnpr)
{
TRACE_METHOD(CDsObject, _ProcessUserEntry);
HRESULT hr = S_OK;
CWaitCursor Hourglass;
CDsObjectList dsolMatches;
BOOL fMultiselect = rop.GetInitInfoOptions() &
DSOP_FLAG_MULTISELECT;
ASSERT(fMultiselect && pdsolExtras || !fMultiselect && !pdsolExtras);
const CScopeManager &rsm = rop.GetScopeManager();
const CFilterManager &rfm = rop.GetFilterManager();
//
// Loop on this entry until it has been sucessfully processed, an
// error occurs, or user selects 'remove this object' from a dialog
// asking for info about this name.
//
String strName = GetAttr(AK_USER_ENTERED_TEXT).GetBstr();
ASSERT(strName.length());
#if (DBG == 1)
_DumpProcessUserEntry(rop, strName);
#endif
while (TRUE)
{
size_t idxFirstWhack = strName.find(L'\\');
//
//Find the rightmost @
//
size_t idxLastAt= strName.rfind(L'@');
ULONG flProcess = 0;
if (fMultiselect)
{
flProcess |= DSO_NAME_PROCESSING_FLAG_MULTISELECT;
}
if (idxLastAt != String::npos)
{
//
//This means name is either a UPN or Name in name@dnsDomainName
//format. In the comments UPN is used to mean both name formats.
//
flProcess |= DSO_NAME_PROCESSING_FLAG_MIGHT_BE_UPN;
}
if (idxFirstWhack == 0 && strName[1] == L'\\')
{
flProcess |= DSO_NAME_PROCESSING_FLAG_IS_COMPUTER;
}
//
// If the name has backslashes beyond the first two, it's an error.
//
if (flProcess & DSO_NAME_PROCESSING_FLAG_IS_COMPUTER)
{
size_t idxExtraWhack = strName.find(L'\\', 2);
if (idxExtraWhack != String::npos)
{
if (fMultiselect)
{
CNameNotFoundDlg Dlg(rop, IDS_BAD_NAME, &strName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
BREAK_ON_FAIL_HRESULT(hr);
if (NAME_PROCESSING_FAILED(*pnpr))
{
break;
}
ASSERT(dsolMatches.empty());
continue;
}
else
{
PopupMessage(hwnd, IDS_BAD_NAME, strName.c_str());
*pnpr = NPR_STOP_PROCESSING;
break;
}
}
//
// Name starts with exactly two backslashes. Strip them off.
//
strName.erase(0, 2);
}
//
// If the name is of the form 'foo\bar' then treat 'foo' as a domain
// or computer name and try to find 'bar' within it. Note checking
// for !(flProcess & DSO_NAME_PROCESSING_FLAG_IS_COMPUTER) doesn't mean
// object can't be a computer, it just means the name wasn't in the
// form \\foo.
//
if (idxFirstWhack != String::npos &&
!(flProcess & DSO_NAME_PROCESSING_FLAG_IS_COMPUTER))
{
//
// Note _SearchDomain has the ds customizer do a prefix
// search in the domain 'foo' and includes those items in
// dsolMatches.
//
// If the domain search fails and the user edits the string,
// loop around and retry. Can't just retry inside _SearchDomain
// because the name may no longer be in form foo\bar.
//
_SearchDomain(&strName,
hwnd,
rop,
idxFirstWhack,
flProcess,
pnpr,
&dsolMatches);
if (NAME_PROCESSING_FAILED(*pnpr))
{
break;
}
if (*pnpr == NPR_EDITED)
{
ASSERT(dsolMatches.empty());
continue;
}
}
else
{
//
//For UPN format name, there is no prefix search. So once the
//object is found we set bFoundObject to true and we don't do
//any further search.
//
BOOL bFoundObject = FALSE;
//
//For UPN names, if UserEnteredUpLevelScope is present, we assume
//that name after @ is domain name and do the search in it
//this flag keeps track if we have already done the search in
//domain name after @
//
BOOL bDoneUserSearch = FALSE;
//
//This is set to true by crackname if nameafter@ is in trusted Xforest
//bExtForest can be true even if the object is not found in Xforest,
//that means name before @ was entered incorrectly
//
BOOL bExtForest = FALSE;
const CScope *pUpnDerivedScope = NULL;
const CScope *pCrackNameScope = NULL;
const CScope *pGCScope = NULL;
//
// strName does not contain slash or backslash. It is
// either a computer name, a UPN, or an RDN.
//
if (flProcess & DSO_NAME_PROCESSING_FLAG_MIGHT_BE_UPN)
{
//
// If name might be upn, try to find domain scope
// with dns name of portion after @.
String strNameAfterAt(strName);
strNameAfterAt.erase(0, idxLastAt + 1);
//
//Lookfor a scope with name of strNameAfterAt
//
pUpnDerivedScope = &rsm.LookupScopeByDisplayName(strNameAfterAt);
if (IsUplevel(pUpnDerivedScope))
{
bDoneUserSearch = TRUE;
//
//When a Search in cross forest in done, a new scope of
//type DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN is added.
//Check if its Cross Forest.
//
BOOL bXForest = FALSE;
if(const_cast<CScope *>(pUpnDerivedScope)->GetType() == DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN)
{
const CLdapDomainScope *pLdapScope =
dynamic_cast<const CLdapDomainScope*>(pUpnDerivedScope);
bXForest = pLdapScope->IsXForest();
}
//
//Size before Query
//
size_t cItemsAtStart = dsolMatches.size();
_QueryForName(hwnd,
rop,
*pUpnDerivedScope,
strName,
strName,
flProcess,
&dsolMatches,
bXForest);
//
//we found a match in query.
//
if(cItemsAtStart < dsolMatches.size())
bFoundObject = TRUE;
}
//
//Now try dscrack. This may give a reference to another forest also.
//We try dscrack name only for uplevel domain.
//
MACHINE_CONFIG mc = rop.GetTargetComputerConfig();
const SScopeParameters *pspUserUplevel =
rsm.GetScopeParams(ST_USER_ENTERED_UPLEVEL_SCOPE);
const SScopeParameters *pspExtUplevel =
rsm.GetScopeParams(ST_EXTERNAL_UPLEVEL_DOMAIN);
//
//Do DsCrackNames if following conditons are true
//1) Object is not found yet
//2) We do dscrack begining from GC in this forest. So machine must be
// joined to uplevel domain or mustbe uplevel DC
//3) And one of the scopes which can contain UPN name, must be present
//
if (!bFoundObject && //1
(mc == MC_JOINED_NT5 || mc == MC_NT5_DC) && //2
(rsm.GetScopeParams(ST_UPLEVEL_JOINED_DOMAIN) || //3
rsm.GetScopeParams(ST_ENTERPRISE_DOMAIN) ||
rsm.GetScopeParams(ST_GLOBAL_CATALOG) ||
pspExtUplevel ||
pspUserUplevel))
{
//
//Try to crack in external forest only if ST_EXTERNAL_UPLEVEL_DOMAIN
//Scope is present.
//
BOOL bCrackInExtForest = FALSE;
if(pspExtUplevel)
bCrackInExtForest = TRUE;
//
//Crack the name
//
LPWSTR pwzResultName = NULL;
BOOL bAddDollar = FALSE;
hr = CrackName(hwnd,
(LPWSTR)strName.c_str(),
DS_USER_PRINCIPAL_NAME,
(LPWSTR)rop.GetTargetForest().c_str(),
bCrackInExtForest,
&pwzResultName,
&bExtForest,
&bAddDollar);
if(SUCCEEDED(hr) && pwzResultName)
{
bDoneUserSearch = TRUE;
size_t cItemsAtStart = dsolMatches.size();
String strDomainFromCrack= pwzResultName;
LocalFree(pwzResultName);
pwzResultName = NULL;
//
//Object is in this forest only
//
if(!bExtForest)
{
pCrackNameScope =
&rsm.LookupScopeByDisplayName(strDomainFromCrack);
pGCScope =
&rsm.LookupScopeByType(ST_GLOBAL_CATALOG);
//
//1)if any of the enumerated domain match
// strDomainFromCrack, Search there
//2)else if userEnteredUplevelScope is present
// try to search in strDomainCrack
//3)else if wehave GC Scope
// search there
//
if(pCrackNameScope && //1
!IsInvalid(*pCrackNameScope) &&
(pCrackNameScope != pUpnDerivedScope))
{
_QueryForName(hwnd,
rop,
*pCrackNameScope,
strName,
strName,
flProcess,
&dsolMatches,
FALSE);
}
else if(pspUserUplevel) //2
{
_SearchUplevelDomain(hwnd,
rop,
strDomainFromCrack,
pspUserUplevel,
strName,
strName,
flProcess,
FALSE,
FALSE,
&dsolMatches);
}
else if(pGCScope &&
!IsInvalid(*pGCScope))
{
_QueryForName(hwnd,
rop,
*pGCScope,
strName,
strName,
flProcess|DSO_NAME_PROCESSING_FLAG_EXACT_UPN,
&dsolMatches,
FALSE);
}
}
else
{
//
//Ok the Object in XForest. Check if this scope is in
//our list already. This needs to be done because
//strDomainFromCrack can be different from nameafter@
//
String strXForestName = strName;
if(bAddDollar)
AddDollarToNameToCrack(DS_USER_PRINCIPAL_NAME, strXForestName);
pCrackNameScope =
&rsm.LookupScopeByDisplayName(strDomainFromCrack);
if(pCrackNameScope && //1
!IsInvalid(*pCrackNameScope))
{
_QueryForName(hwnd,
rop,
*pCrackNameScope,
strXForestName,
strXForestName,
flProcess,
&dsolMatches,
TRUE);
}
else
{
_SearchUplevelDomain(hwnd,
rop,
strDomainFromCrack,
pspExtUplevel,
strXForestName,
strXForestName,
flProcess,
FALSE,
TRUE,
&dsolMatches);
}
}
//
//we found a match in query.
//
if(cItemsAtStart < dsolMatches.size())
bFoundObject = TRUE;
}
//
// If CrackName tells that domain is in External Forest and
// And EXTERNAL_UPLEVEL scope is present, no need to do UserSearch
//
if(bExtForest && pspExtUplevel)
bDoneUserSearch = TRUE;
}
}
//
// Search the current scope but only if we didn't just search it
// as rUpnDerivedScope.
//
if(!bFoundObject && !bExtForest)
{
//Size before Query
size_t cItemsAtStart = dsolMatches.size();
if (IsUplevel(rsm.GetCurScope()))
{
BOOL fSearchCurScope = TRUE;
//
// Search the current scope if:
//
// a. there is no UPN derived scope
// b. there is a UPN derived scope, but it is not the current
// scope and, if the current scope is the GC, the UPN
// derived scope is not an enterprise domain.
// c. the current scope is downlevel
//
if (!pUpnDerivedScope || ( pUpnDerivedScope && IsInvalid(*pUpnDerivedScope)) )
{
fSearchCurScope = TRUE;
}
else if (rsm.GetCurScope().Type() == ST_GLOBAL_CATALOG &&
(pUpnDerivedScope->Type() == ST_UPLEVEL_JOINED_DOMAIN ||
pUpnDerivedScope->Type() == ST_ENTERPRISE_DOMAIN))
{
fSearchCurScope = FALSE;
}
else if (&rsm.GetCurScope() == pUpnDerivedScope ||
&rsm.GetCurScope() == pCrackNameScope ||
&rsm.GetCurScope() == pGCScope)
{
fSearchCurScope = FALSE;
}
else
{
fSearchCurScope = TRUE;
}
if (fSearchCurScope)
{
_QueryForName(hwnd,
rop,
rsm.GetCurScope(),
strName,
strName,
flProcess & DSO_NAME_PROCESSING_FLAG_MIGHT_BE_UPN ?
flProcess|DSO_NAME_PROCESSING_FLAG_EXACT_UPN:
flProcess,
&dsolMatches,
FALSE);
}
}
else if (IsDownlevel(rsm.GetCurScope()))
{
_BindForName(hwnd,
rop,
rsm.GetCurScope(),
strName,
&dsolMatches);
}
//
//Did we find the object
//
if(cItemsAtStart < dsolMatches.size())
bFoundObject = TRUE;
}
//
//if nothing found, try to bind to the domain directly
//
if(!bFoundObject &&
rsm.GetScopeParams(ST_USER_ENTERED_UPLEVEL_SCOPE) &&
!bDoneUserSearch &&
(flProcess & DSO_NAME_PROCESSING_FLAG_MIGHT_BE_UPN))
{
String strNameAfterAt(strName);
strNameAfterAt.erase(0, idxLastAt + 1);
String strNameBeforeAt(strName);
strNameBeforeAt.erase(idxLastAt);
const SScopeParameters *pspUserUplevel =
rsm.GetScopeParams(ST_USER_ENTERED_UPLEVEL_SCOPE);
_SearchUplevelDomain(hwnd,
rop,
strNameAfterAt,
pspUserUplevel,
strNameBeforeAt,
strName,
flProcess,
FALSE,
FALSE,
&dsolMatches);
}
//
// If the caller allows computer objects in user entered domains
// and we have not already discovered a computer object with an
// exact matching name then try binding to name as a computer
// object.
//
_BindForComputer(hwnd, rop, strName, &dsolMatches);
//
// Ask the dsbrowse customizer to do a prefix search for the
// objects it would add to the current scope.
//
_CustomizerPrefixSearch(hwnd,
rop,
rsm.GetCurScope(),
strName,
&dsolMatches);
}
//
// If disabled objects are considered illegal, remove from the list
// all which are disabled. If this results in an empty list, make
// a note of it so the appropriate error can be displayed in the
// invalid name dialog.
//
BOOL fEmptyBecauseDisabledItemsRemoved = FALSE;
if (g_fExcludeDisabled && !dsolMatches.empty())
{
CDsObjectList::iterator itCur;
CDsObjectList::iterator itNext;
for (itCur = dsolMatches.begin(); itCur != dsolMatches.end(); )
{
if (itCur->GetDisabled())
{
Dbg(DEB_TRACE,
"Removing disabled match %ws\n",
itCur->GetName());
itNext = itCur;
itNext++;
dsolMatches.erase(itCur, itNext);
itCur = itNext;
}
else
{
itCur++;
}
}
if (dsolMatches.empty())
{
fEmptyBecauseDisabledItemsRemoved = TRUE;
}
}
//
// Ask the customizer to approve the matches, if any were found.
// Note because this only REMOVES objects from matches, this is
// called regardless of whether the DSOP_FILTER_EXTERNAL_CUSTOMIZER
// or DSOP_DOWNLEVEL_FILTER_EXTERNAL_CUSTOMIZER flags are set.
//
// Those flags are only provided to prevent the ADDITION of objects
// from customizers.
//
ICustomizeDsBrowser *pCustomize = rop.GetExternalCustomizer();
if (pCustomize && !dsolMatches.empty())
{
RefCountPointer<CDataObject> rpdo;
rpdo.Acquire(new CDataObject(const_cast<CObjectPicker*>(&rop),
dsolMatches));
BOOL *afApproved = new BOOL[dsolMatches.size()];
ZeroMemory(afApproved, sizeof(BOOL) * dsolMatches.size());
IDsObjectPickerScope *pDsopScope =
(IDsObjectPickerScope *)&rsm.GetCurScope();
hr = pCustomize->ApproveObjects(pDsopScope,
rpdo.get(),
afApproved);
if (FAILED(hr))
{
dsolMatches.clear();
}
else if (hr == S_FALSE)
{
ULONG i;
CDsObjectList::iterator itCur;
CDsObjectList::iterator itNext;
for (i = 0, itCur = dsolMatches.begin();
itCur != dsolMatches.end();
i++)
{
if (!afApproved[i])
{
Dbg(DEB_TRACE,
"Removing unapproved match %ws\n",
itCur->GetName());
itNext = itCur;
itNext++;
dsolMatches.erase(itCur, itNext);
itCur = itNext;
}
else
{
itCur++;
}
}
}
delete [] afApproved;
hr = S_OK;
}
//
// If no matches were found anywhere, have the user edit the name
// and try again.
//
if (dsolMatches.empty())
{
if (fMultiselect)
{
ULONG idsMsg;
if (fEmptyBecauseDisabledItemsRemoved)
{
idsMsg = IDS_DISABLED_WARNING_FMT;
}
else
{
idsMsg = IDS_NAME_NOT_FOUND_FMT_MULTI;
}
CNameNotFoundDlg Dlg(rop, idsMsg, &strName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
if (NAME_PROCESSING_FAILED(*pnpr))
{
break;
}
//
// strName has been updated. Loop around and try again.
//
ASSERT(*pnpr == NPR_EDITED);
continue;
}
else
{
ULONG idsMsg;
if (fEmptyBecauseDisabledItemsRemoved)
{
idsMsg = IDS_DISABLED_WARNING_FMT;
}
else
{
idsMsg = IDS_NAME_NOT_FOUND_FMT_SINGLE;
}
//
//Truncate the object name to MAX_OBJECTNAME_DISPLAY_LEN
//
String strObjectName = strName;
if(!strObjectName.empty() && (strObjectName.size() > MAX_OBJECTNAME_DISPLAY_LEN))
{
strObjectName.erase(MAX_OBJECTNAME_DISPLAY_LEN,strObjectName.size());
//
//Add three dots to indicate that name is truncated
//
strObjectName.append(L"...");
}
PopupMessage(hwnd,
idsMsg,
strObjectName.c_str(),
rfm.GetFilterDescription(hwnd, FOR_LOOK_FOR).c_str());
*pnpr = NPR_STOP_PROCESSING;
break;
}
}
else if (dsolMatches.size() > 1)
{
//
// More than one match was found. Ask the user to pick which
// is (are) valid.
//
hr = _MultiMatchDialog(hwnd,
rop,
fMultiselect,
strName,
pnpr,
&dsolMatches,
pdsolExtras);
BREAK_ON_FAIL_HRESULT(hr);
if (*pnpr == NPR_EDITED)
{
ASSERT(dsolMatches.empty());
continue;
}
}
break;
}
//
// Out of the processing loop for this entry. If it was successful,
// dsolMatches has exactly one object.
//
if (!NAME_PROCESSING_FAILED(*pnpr) && SUCCEEDED(hr) && hr != S_FALSE)
{
ASSERT(dsolMatches.size() == 1);
*this = dsolMatches.front();
}
if (FAILED(hr))
{
if (!NAME_PROCESSING_FAILED(*pnpr))
{
*pnpr = NPR_STOP_PROCESSING;
}
}
// Preserve any edits user made
m_AttrValueMap[AK_USER_ENTERED_TEXT] = Variant(strName);
return hr;
}
#if (DBG == 1)
void
CDsObject::_DumpProcessUserEntry(
const CObjectPicker &rop,
const String &strName)
{
const CFilterManager &rfm = rop.GetFilterManager();
const CScopeManager &rsm = rop.GetScopeManager();
ULONG flCurFilterFlags = rfm.GetCurScopeSelectedFilterFlags();
String strFilter = DbgGetFilterDescr(rop, flCurFilterFlags);
Dbg(DEB_TRACE, "UA: Processing entry: '%ws'\n", strName.c_str());
Dbg(DEB_TRACE, "UA: Current scope is: %ws\n",
rsm.GetCurScope().GetDisplayName().c_str());
Dbg(DEB_TRACE, "UA: Current classes are: %ws\n", strFilter.c_str());
}
#endif // (DBG == 1)
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_SearchDomain
//
// Synopsis: Search within the domain specified by the portion of the
// user's string entry before the backslash at [idxFirstWhack].
//
// Arguments: [pstrName] - name for which to search
// [hwnd] - for bind
// [rop] - containing object picker instance
// [idxFirstWhack] - index of first '\' character in *[pstrName]
// [flProcess] - DSO_NAME_PROCESSING_FLAG_* bits
// [pnpr] - filled with result of processing
// [pdsolMatches] - any matching names are added to this list
//
// History: 08-15-1998 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_SearchDomain(
String *pstrName,
HWND hwnd,
const CObjectPicker &rop,
size_t idxFirstWhack,
ULONG flProcess,
NAME_PROCESS_RESULT *pnpr,
CDsObjectList *pdsolMatches)
{
TRACE_METHOD(CDsObject, _SearchDomain);
ASSERT(pdsolMatches->empty());
*pnpr = NPR_SUCCESS;
HRESULT hr = S_OK;
String strScopeName(pstrName->substr(0, idxFirstWhack));
String strRdn;
const CFilterManager &rfm = rop.GetFilterManager();
strRdn = pstrName->substr(idxFirstWhack + 1,
pstrName->length() - idxFirstWhack - 1);
do
{
//
// Complain if there's more than one backslash
//
if (pstrName->find(L'\\', idxFirstWhack + 1) != String::npos)
{
if (flProcess & DSO_NAME_PROCESSING_FLAG_MULTISELECT)
{
CNameNotFoundDlg Dlg(rop, IDS_BAD_NAME_EXTRA_SLASH, pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
else
{
PopupMessage(hwnd,
IDS_BAD_NAME_EXTRA_SLASH,
pstrName->c_str());
*pnpr = NPR_STOP_PROCESSING;
}
break;
}
//
// Complain if there's nothing after the backslash (this would
// generate a query that matches everything)
//
if (!(*pstrName)[idxFirstWhack + 1])
{
if (flProcess & DSO_NAME_PROCESSING_FLAG_MULTISELECT)
{
CNameNotFoundDlg Dlg(rop, IDS_BAD_NAME_SLASH_AT_END, pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
else
{
PopupMessage(hwnd,
IDS_BAD_NAME_SLASH_AT_END,
pstrName->c_str());
*pnpr = NPR_STOP_PROCESSING;
}
break;
}
// try the crack before any other scopes, to fix
// NTRAID#NTBUG9-243391-2000/12/13-sburns
//
// Well this is inefficient as it does crackname everytime
// and also introduces the regression
// NTRAID#NTBUG9-282051-2001/01/17-hiteshr
// i am moving it back to its original position.
const CScopeManager &rsm = rop.GetScopeManager();
//
// Look for scope with display name matching portion of user's
// string before the backslash.
//
Dbg(DEB_NAMEEDIT,
"Looking for scope with flat name '%ws'\n",
strScopeName.c_str());
const CScope *pMatchingScope = &rsm.LookupScopeByFlatName(strScopeName);
//
// If a matching scope is found, query for or bind to the name
// within that scope, and perform a prefix search of the custom
// objects for that scope.
//
if (pMatchingScope && !IsInvalid(*pMatchingScope))
{
size_t cItemsAtStart = pdsolMatches->size();
if (IsUplevel(*pMatchingScope))
{
BOOL bXForest = FALSE;
if(const_cast<CScope*>(pMatchingScope)->GetType() == DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN)
{
const CLdapDomainScope *pLdapScope =
dynamic_cast<const CLdapDomainScope*>(pMatchingScope);
bXForest = pLdapScope->IsXForest();
}
_QueryForName(hwnd,
rop,
*pMatchingScope,
strRdn,
*pstrName,
flProcess,
pdsolMatches,
bXForest);
}
else
{
_BindForName(hwnd,
rop,
*pMatchingScope,
strRdn,
pdsolMatches);
}
_CustomizerPrefixSearch(hwnd,
rop,
*pMatchingScope,
strRdn,
pdsolMatches);
if (cItemsAtStart == pdsolMatches->size())
{
if (flProcess & DSO_NAME_PROCESSING_FLAG_MULTISELECT)
{
CNameNotFoundDlg Dlg(rop,
IDS_NAME_NOT_FOUND_FMT_MULTI,
pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
else
{
PopupMessage(hwnd,
IDS_NAME_NOT_FOUND_FMT_SINGLE,
pstrName->c_str(),
rfm.GetFilterDescription(hwnd, FOR_LOOK_FOR).c_str());
*pnpr = NPR_STOP_PROCESSING;
}
}
break;
}
//
//Now try dscrack. This may give a reference to another forest also.
//We try dscrack name only for uplevel domain.
//
const SScopeParameters *pspExtUplevel =
rsm.GetScopeParams(ST_EXTERNAL_UPLEVEL_DOMAIN);
MACHINE_CONFIG mc = rop.GetTargetComputerConfig();
if ((mc == MC_JOINED_NT5 || mc == MC_NT5_DC) &&
pspExtUplevel)
{
//
//Crack the name
//
LPWSTR pwzResultName = NULL;
BOOL bExtForest = FALSE;
BOOL bAddDollar = FALSE;
hr = CrackName(hwnd,
(LPWSTR)pstrName->c_str(),
DS_NT4_ACCOUNT_NAME,
(LPWSTR)rop.GetTargetForest().c_str(),
TRUE,
&pwzResultName,
&bExtForest,
&bAddDollar);
if(SUCCEEDED(hr) && bExtForest)
{
size_t cItemsAtStart = pdsolMatches->size();
if(pwzResultName )
{
String strXForestRdn = strRdn;
String strXForestUserEnteredString = *pstrName;
if(bAddDollar)
{
strXForestRdn += L"$";
AddDollarToNameToCrack(DS_NT4_ACCOUNT_NAME,strXForestUserEnteredString);
}
String strDomainFromCrack = pwzResultName;
LocalFree(pwzResultName);
pwzResultName = NULL;
pMatchingScope = &rsm.LookupScopeByFlatName(strDomainFromCrack);
//
// If a matching scope is found, query for or bind to the name
// within that scope, and perform a prefix search of the custom
// objects for that scope.
//
if (pMatchingScope && !IsInvalid(*pMatchingScope))
{
_QueryForName(hwnd,
rop,
*pMatchingScope,
strXForestRdn,
strXForestUserEnteredString,
flProcess,
pdsolMatches,
TRUE);
if(cItemsAtStart == pdsolMatches->size())
{
_CustomizerPrefixSearch(hwnd,
rop,
*pMatchingScope,
strXForestRdn,
pdsolMatches);
}
}
else
{
_SearchUplevelDomain(hwnd,
rop,
strDomainFromCrack,
pspExtUplevel,
strXForestRdn,
strXForestUserEnteredString,
flProcess,
TRUE,
TRUE,
pdsolMatches);
}
break;
}
//else
//{
//We come here means in "a\b" "a" is in Xforest but
//there is no object named "b" in "a". We should quit
//Here we need to show the NameNotFound message.
//Below if statement will show that.
//}
if(cItemsAtStart == pdsolMatches->size())
{
if (flProcess & DSO_NAME_PROCESSING_FLAG_MULTISELECT)
{
CNameNotFoundDlg Dlg(rop,
IDS_NAME_NOT_FOUND_FMT_MULTI,
pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
else
{
PopupMessage(hwnd,
IDS_NAME_NOT_FOUND_FMT_SINGLE,
pstrName->c_str(),
rfm.GetFilterDescription(hwnd, FOR_LOOK_FOR).c_str());
*pnpr = NPR_STOP_PROCESSING;
}
break;
}
}
}
//
// No matching scope. If the caller doesn't want us to search in
// domains that didn't appear in the scope control, fail now.
//
// Note this code that requires the full list of domain scopes has
// been populated before we get here, otherwise we could reject the
// user's entry claiming that a scope which hasn't yet been added to
// the scope list doesn't exist.
//
const SScopeParameters *pspUserUplevel =
rsm.GetScopeParams(ST_USER_ENTERED_UPLEVEL_SCOPE);
const SScopeParameters *pspUserDownlevel =
rsm.GetScopeParams(ST_USER_ENTERED_DOWNLEVEL_SCOPE);
if (!pspUserUplevel && !pspUserDownlevel)
{
//
// See if the user entered the bogus form:
// dns-name\object-name
// If so, present a message explaining that they should
// either use netbios-name\object-name or object-name@dns-name.
//
const CLdapDomainScope *pMatchingDisplayScope =
dynamic_cast<const CLdapDomainScope *>
(&rsm.LookupScopeByDisplayName(strScopeName));
String strValidNB;
String strValidUPN;
if (pMatchingDisplayScope)
{
strValidNB = pMatchingDisplayScope->GetFlatName();
strValidNB += L"\\";
strValidNB += strRdn;
strValidUPN = strRdn + L"@";
strValidUPN += strScopeName;
}
if (flProcess & DSO_NAME_PROCESSING_FLAG_MULTISELECT)
{
if (pMatchingDisplayScope)
{
//
// yep. build up an error message that explains
// what they did wrong and how to fix it.
//
String strError = String::format(IDS_DNS_SLASH_NAME,
pstrName->c_str(),
strValidNB.c_str(),
strValidUPN.c_str());
CNameNotFoundDlg Dlg(rop, strError, pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
else
{
//
// nope, they entered some random string which
// doesn't match the dns or the netbios name of
// anything in the lookin control.
//
CNameNotFoundDlg Dlg(rop, IDS_UNKNOWN_DOMAIN, pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
}
else
{
if (pMatchingDisplayScope)
{
PopupMessage(hwnd,
IDS_UNKNOWN_DOMAIN,
pstrName->c_str(),
strValidNB.c_str(),
strValidUPN.c_str());
}
else
{
PopupMessage(hwnd,
IDS_UNKNOWN_DOMAIN,
pstrName->c_str());
}
*pnpr = NPR_STOP_PROCESSING;
}
break;
}
//
// Try to find an uplevel domain with name matching what the
// user typed before the backslash, then query within it for
// items starting with the characters after the backslash.
//
size_t cBeforeUplevelSearch = pdsolMatches->size();
if (pspUserUplevel)
{
_SearchUplevelDomain(hwnd,
rop,
strScopeName,
pspUserUplevel,
strRdn,
*pstrName,
flProcess,
TRUE,
FALSE,
pdsolMatches);
}
//
// If no objects found that way, try searching for the object
// strRdn in a downlevel domain with name strScopeName.
//
if (pspUserDownlevel &&
pdsolMatches->size() == cBeforeUplevelSearch)
{
_SearchDownlevelDomain(hwnd,
rop,
strScopeName,
strRdn,
pdsolMatches);
if (pdsolMatches->size() == cBeforeUplevelSearch)
{
if (flProcess & DSO_NAME_PROCESSING_FLAG_MULTISELECT)
{
CNameNotFoundDlg Dlg(rop,
IDS_NAME_NOT_FOUND_FMT_MULTI,
pstrName);
hr = Dlg.DoModalDialog(hwnd, pnpr);
}
else
{
PopupMessage(hwnd,
IDS_NAME_NOT_FOUND_FMT_SINGLE,
pstrName->c_str(),
rfm.GetFilterDescription(hwnd, FOR_LOOK_FOR).c_str());
*pnpr = NPR_STOP_PROCESSING;
}
}
}
}
while (0);
if (*pnpr == NPR_EDITED)
{
pdsolMatches->clear();
}
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_SearchUplevelDomain
//
// Synopsis: Attempt to bind to an uplevel domain with name [strScopeName]
// and search for a name starting with [strRdn] there.
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
// [strScopeName] - name of scope to search in
// [pspUserUplevel] - parameters for that scope
// [strRdn] [strNamePrefix] - Name to search for.
// If user entered the name in domain\foo
// format its foo
// If user entered the name in foo@domain
// format its foo@domain
// If use entered the foo, its foo
// [strUserEnteredString] The string user entered. Its used to
// get the sid in XForest case.
// [bDoCustomizePrefix] Call ExternalCustomizer. This is true
// if user entered name in format
// Domain\Foo and false if foo@domain
// [bXForest] Is name in XForest
// [flProcess] - DSO_NAME_PROCESSING_FLAG_* bits
// [pdsolMatches] - any matches are added to this list
//
// History: 08-15-1998 DavidMun Created
//
// Notes: If uplevel domain is found, creates a new scope object.
//
//---------------------------------------------------------------------------
void
CDsObject::_SearchUplevelDomain(
HWND hwnd,
const CObjectPicker &rop,
const String &strScopeName,
const SScopeParameters *pspUserUplevel,
const String &strRdn,
const String &strUserEnteredString,
ULONG flProcess,
BOOL bDoCustomizePrefix,
BOOL bXForest,
list<CDsObject> *pdsolMatches)
{
TRACE_METHOD(CDsObject, _SearchUplevelDomain);
HRESULT hr = S_OK;
String strDomainPath(c_wzLDAPPrefix);
RpIDirectorySearch rpDirSearch;
do
{
strDomainPath += strScopeName;
hr = g_pBinder->BindToObject(hwnd,
strDomainPath.c_str(),
IID_IDirectorySearch,
(void**)&rpDirSearch);
BREAK_ON_FAIL_HRESULT(hr);
//
// Create a scope for this domain. It will not be made
// visible (added to the dropdown scope list) but will be
// included in the list of those searched if another name
// in the form foo\bar is processed.
//
ADD_SCOPE_INFO asi;
if(!bXForest)
asi.flType = DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE;
else
asi.flType = DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN;
asi.FilterFlags = pspUserUplevel->FilterFlags;
asi.Visibility = SCOPE_HIDDEN;
asi.Domain.strScopeName = strScopeName;
asi.Domain.strFlatName = strScopeName;
asi.Domain.strADsPath = strDomainPath;
const CScopeManager &rsm = rop.GetScopeManager();
const CScope *pNewScope;
if(!bXForest)
pNewScope = &rsm.AddUserEnteredScope(asi);
else
pNewScope = &rsm.AddCrossForestDomainScope(asi);
//
// Query on the new scope for the rdn, also look for whatever
// custom objects would be added to that domain scope.
//
size_t cItemsAtStart = pdsolMatches->size();
_QueryForName(hwnd,
rop,
*pNewScope,
strRdn,
strUserEnteredString,
flProcess,
pdsolMatches,
bXForest);
//
//In cross forest we don't do prefix search, so if we have already found some objects
//don't do further search
//
if(bDoCustomizePrefix && !(bXForest && (cItemsAtStart != pdsolMatches->size())))
_CustomizerPrefixSearch(hwnd, rop, *pNewScope, strRdn, pdsolMatches);
} while (0);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_SearchDownlevelDomain
//
// Synopsis: Attempt to bind to a downlevel domain with name
// [strScopeName], then bind within that domain for a name
// starting with [strRdn].
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
// [strScopeName] - name of downlevel domain
// [strRdn] - name to search for
// [pdsolMatches] - any matches are added to this list
//
// History: 08-15-1998 DavidMun Created
//
// Notes: If downlevel domain is found, creates a new scope object.
//
//---------------------------------------------------------------------------
void
CDsObject::_SearchDownlevelDomain(
HWND hwnd,
const CObjectPicker &rop,
const String &strScopeName,
const String &strRdn,
CDsObjectList *pdsolMatches)
{
TRACE_METHOD(CDsObject, _SearchDownlevelDomain);
HRESULT hr = S_OK;
String strScopePath;
String strScopePathWithHint;
RpIADs rpADs;
do
{
strScopePath += c_wzWinNTPrefix + strScopeName;
//
// Try strScopeName as a computer first.
//
strScopePathWithHint = strScopePath + L",Computer";
hr = g_pBinder->BindToObject(hwnd,
strScopePathWithHint.c_str(),
IID_IADs,
(void**)&rpADs);
if (FAILED(hr))
{
// Nope. Try as domain.
strScopePathWithHint = strScopePath + L",Domain";
hr = g_pBinder->BindToObject(hwnd,
strScopePathWithHint.c_str(),
IID_IADs,
(void**)&rpADs);
}
BREAK_ON_FAIL_HRESULT(hr);
//
// Bind succeeded, therefore computer or domain exists. Add a scope
// for it and look for the RDN within it.
//
ADD_SCOPE_INFO asi;
const CScopeManager &rsm = rop.GetScopeManager();
const SScopeParameters *pspUserDownlevel =
rsm.GetScopeParams(ST_USER_ENTERED_DOWNLEVEL_SCOPE);
asi.flType = DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE;
asi.FilterFlags = pspUserDownlevel->FilterFlags;
asi.Visibility = SCOPE_HIDDEN;
asi.Domain.strScopeName = strScopeName;
asi.Domain.strFlatName = strScopeName;
asi.Domain.strADsPath = strScopePathWithHint;
const CScope &rNewScope = rsm.AddUserEnteredScope(asi);
//
//Size of query before search
//
size_t cItemsAtStart = pdsolMatches->size();
_BindForName(hwnd, rop, rNewScope, strRdn, pdsolMatches);
_CustomizerPrefixSearch(hwnd, rop, rNewScope, strRdn, pdsolMatches);
//
//We don't keep this scope in list if nothing is found in this scope.
//NTRAID#NTBUG9-243391-2001/01/17-hiteshr
//
if(cItemsAtStart == pdsolMatches->size())
rsm.DeleteLastScope();
} while (0);
}
HRESULT
_tThread_Proc(
CProgressDialog& dialog)
{
CRow * pRow = dialog.m_pRow;
ULONG flProcess = dialog.m_flProcess;
ULONG cHits = 0;
BOOL bXForest = dialog.m_bXForest;
const CObjectPicker &rop = dialog.m_rop;
const CScope &Scope = dialog.m_Scope;
const String &strUserEnteredString = dialog.m_strUserEnteredString;
CDsObjectList *pdsolMatches = dialog.m_pdsolMatches;
ASSERT(pRow && pdsolMatches);
String strFormat = String::load((int)IDS_PROGRESS_MESSAGE, g_hinst);
HRESULT hr = S_OK;
while (SUCCEEDED(hr))
{
//
//User pressed the stop button. Stop now.
//
if(dialog.HasUserCancelled())
break;
WCHAR buffer[34];
_itow(cHits,buffer,10);
String strMessage = String::format(strFormat, buffer);
dialog.UpdateText(strMessage);
hr = pRow->Next();
if (hr == S_ADS_NOMORE_ROWS)
{
Dbg(DEB_TRACE,
"_QueryForName: S_ADS_NOMORE_ROWS, got %u\n",
cHits);
ULONG ulADsLastError;
WCHAR wzError[MAX_PATH];
WCHAR wzProvider[MAX_PATH];
HRESULT hr2 = ADsGetLastError(&ulADsLastError,
wzError,
ARRAYLEN(wzError),
wzProvider,
ARRAYLEN(wzProvider));
if (SUCCEEDED(hr2) && ulADsLastError == ERROR_MORE_DATA)
{
Dbg(DEB_TRACE, "Got ERROR_MORE_DATA, trying again\n");
continue;
}
break;
}
BREAK_ON_FAIL_HRESULT(hr);
//
// If we know that the user is looking for a computer, discard
// objects not of class computer.
//
if (flProcess & DSO_NAME_PROCESSING_FLAG_IS_COMPUTER)
{
PCWSTR pwzClass = pRow->GetColumnStr(AK_OBJECT_CLASS);
if (!pwzClass)
{
continue;
}
if (lstrcmpi(pwzClass, c_wzComputerObjectClass))
{
continue;
}
}
//
// Create a new object and add it to the list
//
PCWSTR pwzName = pRow->GetColumnStr(AK_NAME);
PCWSTR pwzClass = pRow->GetColumnStr(AK_OBJECT_CLASS);
PCWSTR pwzPath = pRow->GetColumnStr(AK_ADSPATH);
if (!pwzClass || !pwzName || !pwzPath)
{
Dbg(DEB_WARN,
"Skipping item missing class ('%ws'), name ('%ws'), or path ('%ws')\n",
pwzClass ? pwzClass : L"",
pwzName ? pwzName : L"",
pwzPath ? pwzPath : L"");
continue;
}
//
// Stop fetching items if we've exceeded the max for the multimatch
// dialog.
//
if (++cHits > MAX_SEARCH_HITS)
{
PopupMessageEx(dialog.GetHwnd(),
IDI_WARNING,
IDS_MAX_HITS,
MAX_SEARCH_HITS_STR);
break;
}
//
//If object is from Xforest, there is possibility of SID spoofing
//we must verify that SID if fetched is good
//
if(bXForest)
{
PSID pSid = pRow->GetObjectSid();
if(pSid)
{
BOOL bGoodSid = FALSE;
hr = IsSidGood(rop.GetTargetComputer().c_str(),
strUserEnteredString.c_str(),
pSid,
&bGoodSid);
BREAK_ON_FAIL_HRESULT(hr);
if(!bGoodSid)
{
Dbg(DEB_WARN,
"Skipping item Bad Sid('%ws'), name ('%ws'), or path ('%ws')\n",
pwzClass ? pwzClass : L"",
pwzName ? pwzName : L"",
pwzPath ? pwzPath : L"");
continue;
}
}
}
//
// Add this new object if it isn't already in the list
//
CDsObject dsoNew(Scope.GetID(), pRow->GetAttributes());
if (find(pdsolMatches->begin(), pdsolMatches->end(), dsoNew) ==
pdsolMatches->end())
{
pdsolMatches->push_back(dsoNew);
}
}
dialog.ThreadDone();
return S_OK;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_QueryForName
//
// Synopsis: Perform a query in uplevel domain represented by [pDsScope]
// for an object whose name starts with [strNamePrefix].
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
// [Scope] - scope in which to query
// [strNamePrefix] - Name to search for.
// If user entered the name in domain\foo
// format its foo
// If user entered the name in foo@domain
// format its foo@domain
// If use entered the foo, its foo
// [strUserEnteredString] The string user entered. Its used to
// get the sid from LSA in case of cross forest.
// [flProcess] - DSO_NAME_PROCESSING_FLAG_* bits
// [pdsolMatches] - list to which to append matches
// [bXForest] - Is strNamePrefix in other forest.
// If the name is another forest, we don't
// support prefix search
// History: 08-15-1998 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_QueryForName(
HWND hwnd,
const CObjectPicker &rop,
const CScope &Scope,
const String &strNamePrefix,
const String &strUserEnteredString,
ULONG flProcess,
CDsObjectList *pdsolMatches,
BOOL bXForest)
{
TRACE_METHOD(CDsObject, _QueryForName);
ASSERT(IsUplevel(Scope));
HRESULT hr = S_OK;
const CLdapContainerScope *pLdapScope =
dynamic_cast<const CLdapContainerScope *>(&Scope);
if (!pLdapScope)
{
Dbg(DEB_TRACE,
"Scope '%ws' type %u cast to CLdapContainerScope failed\n",
Scope.GetDisplayName().c_str(),
Scope.Type());
ASSERT(0 && "expected to recieve ldap scope");
return;
}
//
// If the scope to query in is not the same as the current scope, then
// it might not allow the types of objects selected for the current
// scope.
//
const CFilterManager &rfm = rop.GetFilterManager();
String strLdapContainerFilter = rfm.GetLdapFilter(hwnd, Scope);
if (strLdapContainerFilter.empty())
{
Dbg(DEB_TRACE,
"Scope '%ws' has no ldap query, returning\n",
Scope.GetDisplayName().c_str());
return;
}
String strADsPath;
hr = pLdapScope->GetADsPath(hwnd, &strADsPath);
if (FAILED(hr))
{
Dbg(DEB_TRACE,
"Scope '%ws' has no ldap path, returning\n",
Scope.GetDisplayName().c_str());
return;
}
RpIDirectorySearch rpDirSearch;
hr = g_pBinder->BindToObject(hwnd,
strADsPath.c_str(),
IID_IDirectorySearch,
(void**)&rpDirSearch);
if (FAILED(hr))
{
DBG_OUT_HRESULT(hr);
return;
}
//
// Make a copy of the standard preferences and modify time limit
// so it will be longer. Want more generous limit for finding a
// name the user typed in than just browsing.
//
ADS_SEARCHPREF_INFO aSearchPrefs[NUM_SEARCH_PREF];
CopyMemory(aSearchPrefs, g_aSearchPrefs, sizeof(aSearchPrefs));
ULONG i;
for (i = 0; i < NUM_SEARCH_PREF; i++)
{
if (aSearchPrefs[i].dwSearchPref == ADS_SEARCHPREF_PAGED_TIME_LIMIT)
{
aSearchPrefs[i].vValue.Integer = NAME_QUERY_PAGE_TIME_LIMIT;
break;
}
}
hr = rpDirSearch->SetSearchPreference(aSearchPrefs, NUM_SEARCH_PREF);
if (FAILED(hr))
{
DBG_OUT_HRESULT(hr);
return;
}
//
// Build the query clause, using an escaped version of the name.
//
String strEscaped(strNamePrefix);
LdapEscape(&strEscaped);
String strNameFilter;
if(flProcess & DSO_NAME_PROCESSING_FLAG_EXACT_UPN)
{
strNameFilter = String::format(c_wzUpnQueryFormatExact,
strEscaped.c_str());
}
else if (flProcess & DSO_NAME_PROCESSING_FLAG_MIGHT_BE_UPN)
{
String strNameBeforeAt(strNamePrefix);
size_t idxLastAt = strNameBeforeAt.rfind(L'@');
if (idxLastAt != String::npos)
{
strNameBeforeAt.erase(idxLastAt);
}
LdapEscape(&strNameBeforeAt);
strNameFilter = String::format(c_wzUpnQueryFormatEx,
strNameBeforeAt.c_str(),
strEscaped.c_str());
}
else
{
if(bXForest)
strNameFilter = String::format(c_wzCnQueryFormatExact, strEscaped.c_str());
else
strNameFilter = String::format(c_wzCnQueryFormat, strEscaped.c_str());
}
#if (DBG == 1)
Dbg(DEB_TRACE,
"Querying for name %ws in scope %ws\n",
strEscaped.c_str(),
Scope.GetDisplayName().c_str());
#endif
String strQuery;
strQuery = L"(&";
strQuery += strLdapContainerFilter;
strQuery += strNameFilter;
strQuery += L")";
//
// Perform the query
//
AttrKeyVector vakAttrToFetch;
vakAttrToFetch.push_back(AK_NAME);
vakAttrToFetch.push_back(AK_OBJECT_CLASS);
vakAttrToFetch.push_back(AK_ADSPATH);
vakAttrToFetch.push_back(AK_USER_ACCT_CTRL);
vakAttrToFetch.push_back(AK_USER_PRINCIPAL_NAME);
vakAttrToFetch.push_back(AK_EMAIL_ADDRESSES);
vakAttrToFetch.push_back(AK_SAMACCOUNTNAME);
//
//If object in cross forest, get the sid
//
if(bXForest)
vakAttrToFetch.push_back(AK_OBJECT_SID);
CRow Row(hwnd, rop, rpDirSearch.get(), strQuery, vakAttrToFetch);
CProgressDialog ProgressDialog(_tThread_Proc,
IDA_SEARCH,
1000*3,
&Row,
flProcess,
bXForest,
rop,
Scope,
strUserEnteredString,
pdsolMatches);
ProgressDialog.CreateProgressDialog(hwnd);
}
//+--------------------------------------------------------------------------
//
// Class: CStringCompare
//
// Purpose: Used as functor to search for string [rhs]
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
class CStringCompare
{
public:
CStringCompare(
const String &rhs):
m_rhs(rhs)
{
}
BOOL
operator()(const String &lhs)
{
return !m_rhs.icompare(lhs);
}
private:
String m_rhs;
};
enum GROUP_TYPE
{
GT_UNKNOWN,
GT_LOCAL,
GT_GLOBAL
};
#define NUM_SUB_AUTHORITES_FOR_BUILTIN_GROUPS 2
//+--------------------------------------------------------------------------
//
// Function: WantThisGroup
//
// Synopsis: Return TRUE if group object represented by [pADs] is valid in
// scope [pDsScope].
//
// Arguments: [pDsScope] - scope in which to check for acceptable groups
// [pADs] - points to group object to check
// [ppwzClass] - filled with pointer to constant string
//
// Returns: TRUE if group object is acceptable in scope [pDsScope],
// FALSE otherwise.
//
// Modifies: *[ppwzClass] points to "localGroup" or "globalGroup"
//
// History: 7-01-1999 davidmun Created
//
//---------------------------------------------------------------------------
BOOL
WantThisGroup(
ULONG flDownlevel,
IADs *pADs,
PCWSTR *ppwzClass)
{
ASSERT(flDownlevel & DOWNLEVEL_FILTER_BIT);
ASSERT(pADs);
HRESULT hr = S_OK;
BOOL fWantGroup = FALSE;
//
// Get the downlevel filter flags for the current scope. Also, before
// entering the loop(s), determine whether it is necessary to check for the
// group type of returned groups.
//
BOOL fWantLocal = (flDownlevel & DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS) ==
DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS;
BOOL fWantGlobal = (flDownlevel & DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS) ==
DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS;
BOOL fExcludeBuiltin = (flDownlevel &
DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS)
== DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS;
GROUP_TYPE eGroupType = GT_UNKNOWN;
Variant varGroupType;
do
{
// Get group type
hr = pADs->Get((PWSTR)c_wzGroupTypeAttr, &varGroupType);
BREAK_ON_FAIL_HRESULT(hr);
if (V_UI4(&varGroupType) & ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP)
{
eGroupType = GT_LOCAL;
}
else if (V_UI4(&varGroupType) & ADS_GROUP_TYPE_GLOBAL_GROUP)
{
eGroupType = GT_GLOBAL;
}
else
{
Dbg(DEB_ERROR,
"Unknown group type value %#x\n",
V_UI4(&varGroupType));
break;
}
// if excluding builtin local groups, check for that
if (fExcludeBuiltin && eGroupType == GT_LOCAL)
{
Variant varSid;
hr = pADs->Get((BSTR)c_wzObjectSidAttr, &varSid);
BREAK_ON_FAIL_HRESULT(hr);
PSID psid = NULL;
hr = varSid.SafeArrayAccessData((VOID**)&psid);
BREAK_ON_FAIL_HRESULT(hr);
ASSERT(IsValidSid(psid));
PUCHAR pcSubAuth = NULL;
pcSubAuth = GetSidSubAuthorityCount(psid);
ASSERT(pcSubAuth);
if (*pcSubAuth == NUM_SUB_AUTHORITES_FOR_BUILTIN_GROUPS)
{
break;
}
}
//
// If we want one type of group but not another, check
// that.
//
ASSERT(fWantLocal || fWantGlobal);
if (!fWantLocal && eGroupType == GT_LOCAL ||
!fWantGlobal && eGroupType == GT_GLOBAL)
{
break;
}
//
// Translate from the WinNT provider's group class/
// type combo to an internal-use-only group class
// of localgroup or globalgroup.
//
if (eGroupType == GT_LOCAL)
{
*ppwzClass = c_wzLocalGroupClass;
}
else
{
ASSERT(eGroupType == GT_GLOBAL);
*ppwzClass = c_wzGlobalGroupClass;
}
fWantGroup = TRUE;
} while (0);
return fWantGroup;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_BindForName
//
// Synopsis: Attempt to bind to object [strName] in downlevel domain
// represented by [pDsScope].
//
// Arguments: [hwnd] - parent window, required for binding
// [rop] - containing object picker instance
// [Scope] - downlevel scope containing object
// [strName] - rdn of object
// [pdsolMatches] - list to which to append new object
//
// History: 08-16-1998 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_BindForName(
HWND hwnd,
const CObjectPicker &rop,
const CScope &Scope,
const String &strName,
CDsObjectList *pdsolMatches)
{
TRACE_METHOD(CDsObject, _BindForName);
ASSERT(pdsolMatches);
ASSERT(IsDownlevel(Scope));
String strObjectPath;
Bstr bstrName;
Bstr bstrClass;
Bstr bstrPath;
RpIADsContainer rpADsContainer;
RpIDispatch rpdisp;
RpIADs rpADs;
HRESULT hr = S_OK;
do
{
const CAdsiScope *pAdsiScope = dynamic_cast<const CAdsiScope *>(&Scope);
if (!pAdsiScope)
{
Dbg(DEB_ERROR, "Expected scope to have CAdsiScope base\n");
break;
}
hr = pAdsiScope->GetADsPath(hwnd, &strObjectPath);
if (FAILED(hr))
{
Dbg(DEB_TRACE,
"Scope '%ws' has no path, returning\n",
pAdsiScope->GetDisplayName());
break;
}
hr = g_pBinder->BindToObject(hwnd,
strObjectPath.c_str(),
IID_IADsContainer,
(void**) &rpADsContainer);
BREAK_ON_FAIL_HRESULT(hr);
hr = rpADsContainer->GetObject(NULL,
(PWSTR) strName.c_str(),
(IDispatch**)&rpdisp);
if (FAILED(hr))
{
Dbg(DEB_ERROR,
"GetObject(NULL,'%ws') error %#x\n",
strName.c_str(),
hr);
break;
}
hr = rpADs.AcquireViaQueryInterface(*(IUnknown*)rpdisp.get(),
IID_IADs);
BREAK_ON_FAIL_HRESULT(hr);
//
// Check the item's class against what the caller wants.
//
hr = rpADs->get_Class(&bstrClass);
BREAK_ON_FAIL_HRESULT(hr);
PCWSTR pwzClass = bstrClass.c_str();
vector<String> vsWinNtFilter;
const CFilterManager &rfm = rop.GetFilterManager();
rfm.GetWinNtFilter(hwnd, Scope, &vsWinNtFilter);
vector<String>::const_iterator itFilter;
CStringCompare StringCompare(pwzClass);
itFilter = find_if(vsWinNtFilter.begin(),
vsWinNtFilter.end(),
StringCompare);
if (itFilter == vsWinNtFilter.end())
{
break;
}
ULONG flFilter;
hr = rfm.GetSelectedFilterFlags(hwnd, Scope, &flFilter);
BREAK_ON_FAIL_HRESULT(hr);
if (!lstrcmpi(bstrClass.c_str(), c_wzGroupObjectClass) &&
!WantThisGroup(flFilter, rpADs.get(), &pwzClass))
{
break;
}
//
// Found item and it has allowed class. Ask it for the rest of
// the attributes we need.
//
hr = rpADs->get_Name(&bstrName);
BREAK_ON_FAIL_HRESULT(hr);
ASSERT(bstrName.c_str());
hr = rpADs->get_ADsPath(&bstrPath);
BREAK_ON_FAIL_HRESULT(hr);
ASSERT(bstrPath.c_str());
SDsObjectInit Init;
Init.idOwningScope = Scope.GetID();
Init.pwzName = bstrName.c_str();
Init.pwzClass = pwzClass;
Init.pwzADsPath = bstrPath.c_str();
Init.fDisabled = IsDisabled(rpADs.get());
pdsolMatches->push_back(CDsObject(Init));
} while (0);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_CustomizerPrefixSearch
//
// Synopsis: Ask the customizer to do a prefix
// search for [strNamePrefix].
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
// [Scope] - scope in which to search
// [strNamePrefix] - start of name to search for
// [pdsolMatches] - list to which to append matches
//
// History: 08-16-1998 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_CustomizerPrefixSearch(
HWND hwnd,
const CObjectPicker &rop,
const CScope &Scope,
const String &strNamePrefix,
CDsObjectList *pdsolMatches)
{
TRACE_METHOD(CDsObject, _CustomizerPrefixSearch);
IDataObject *pdoToAdd = NULL;
const CFilterManager &rfm = rop.GetFilterManager();
ULONG flCurFilterFlags = rfm.GetCurScopeSelectedFilterFlags();
ICustomizeDsBrowser *pExternalCustomizer = rop.GetExternalCustomizer();
do
{
//
// First check to see if the selected filter flags include the ones
// for the internal or external customizer. If none are set, bail.
//
if (!(flCurFilterFlags & DSOP_FILTER_EXTERNAL_CUSTOMIZER) &&
!(flCurFilterFlags & ALL_UPLEVEL_INTERNAL_CUSTOMIZER_FILTERS) &&
!IsDownlevelFlagSet(flCurFilterFlags,
ALL_DOWNLEVEL_INTERNAL_CUSTOMIZER_FILTERS))
{
break;
}
CDsObjectList dsol;
//
// If an external customizer is provided, use it
//
if (pExternalCustomizer &&
(flCurFilterFlags & DSOP_FILTER_EXTERNAL_CUSTOMIZER))
{
HRESULT hr;
IDsObjectPickerScope *pDsopScope =
static_cast<IDsObjectPickerScope *>(const_cast<CScope*>(&Scope));
hr = pExternalCustomizer->PrefixSearch(pDsopScope,
strNamePrefix.c_str(),
&pdoToAdd);
if (SUCCEEDED(hr) && pdoToAdd)
{
const CScopeManager &rsm = rop.GetScopeManager();
const CScope &rCurScope = rsm.GetCurScope();
AddFromDataObject(rCurScope.GetID(), pdoToAdd, NULL, 0, &dsol);
}
}
//
// Assume if the caller set flags that the internal customizer knows
// about that it should also be used.
//
if ((flCurFilterFlags & ALL_UPLEVEL_INTERNAL_CUSTOMIZER_FILTERS) ||
IsDownlevelFlagSet(flCurFilterFlags,
ALL_DOWNLEVEL_INTERNAL_CUSTOMIZER_FILTERS))
{
const CAdminCustomizer &ac = rop.GetDefaultCustomizer();
ac.PrefixSearch(hwnd, Scope, strNamePrefix, &dsol);
}
//
// Move any objects from pdsol which aren't already in pdsolMatches
// into the latter.
//
CDsObjectList::iterator it;
for (it = dsol.begin(); it != dsol.end(); it++)
{
if (find(pdsolMatches->begin(),
pdsolMatches->end(),
*it) == pdsolMatches->end())
{
pdsolMatches->push_back(*it);
}
}
} while (0);
SAFE_RELEASE(pdoToAdd);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_BindForComputer
//
// Synopsis: Attempt to bind to a computer with name [strName], if
// successful, add an object representing it to [pdsolMatches]
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
// [strName] - name of computer
// [pdsolMatches] - list to which to append matches
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_BindForComputer(
HWND hwnd,
const CObjectPicker &rop,
const String &strName,
CDsObjectList *pdsolMatches)
{
TRACE_METHOD(CDsObject, _BindForComputer);
const CScopeManager &rsm = rop.GetScopeManager();
const SScopeParameters *pspUserUplevel =
rsm.GetScopeParams(ST_USER_ENTERED_UPLEVEL_SCOPE);
const SScopeParameters *pspUserDownlevel =
rsm.GetScopeParams(ST_USER_ENTERED_DOWNLEVEL_SCOPE);
const CFilterManager &rfm = rop.GetFilterManager();
ULONG flCurFilterFlags = rfm.GetCurScopeSelectedFilterFlags();
do
{
// don't try to find computer if user hasn't checked the Computers
// box
if (flCurFilterFlags & DOWNLEVEL_FILTER_BIT)
{
if (!IsDownlevelFlagSet(flCurFilterFlags,
DSOP_DOWNLEVEL_FILTER_COMPUTERS))
{
Dbg(DEB_TRACE, "Cur scope selected filter flags don't contain computer, exiting\n");
break;
}
}
else if (!(flCurFilterFlags & DSOP_FILTER_COMPUTERS))
{
Dbg(DEB_TRACE, "Cur scope selected filter flags don't contain computer, exiting\n");
break;
}
ULONG flUplevel = 0;
if (pspUserUplevel)
{
flUplevel = pspUserUplevel->FilterFlags.Uplevel.flBothModes
| pspUserUplevel->FilterFlags.Uplevel.flNativeModeOnly
| pspUserUplevel->FilterFlags.Uplevel.flMixedModeOnly;
}
ULONG flDownlevel = 0;
if (pspUserDownlevel)
{
flDownlevel = pspUserDownlevel->FilterFlags.flDownlevel;
}
if (!(flUplevel & DSOP_FILTER_COMPUTERS) &&
!((flDownlevel & DSOP_DOWNLEVEL_FILTER_COMPUTERS) ==
DSOP_DOWNLEVEL_FILTER_COMPUTERS))
{
break;
}
BOOL fFound = FALSE;
if (!pdsolMatches->empty())
{
CDsObjectList::iterator it = pdsolMatches->begin();
do
{
it = find(it, pdsolMatches->end(), strName);
if (it != pdsolMatches->end())
{
//
// name matches. if class is computer, we've found
// it already, so don't continue with attempt to bind.
//
if (!lstrcmpi(it->GetClass(), c_wzComputerObjectClass))
{
fFound = TRUE;
break;
}
it++;
}
} while (!fFound && it != pdsolMatches->end());
if (fFound)
{
Dbg(DEB_TRACE,
"Found computer %ws in matches, no need to bind for it\n",
strName.c_str());
break;
}
}
//
// No computer object with matching name in pdsolMatches. If the
// computer name looks legal, try to bind.
//
if (strName.find_first_of(String(c_wzIllegalComputerNameChars)) !=
String::npos)
{
Dbg(DEB_TRACE,
"Name contains illegal character(s), not attempting to bind\n");
break;
}
String strADsPath = c_wzWinNTPrefix;
strADsPath += strName;
strADsPath += L",Computer";
RpIADs rpADs;
Dbg(DEB_TRACE, "Attempting to bind to computer object\n");
HRESULT hr = g_pBinder->BindToObject(hwnd,
strADsPath.c_str(),
IID_IADs,
(void **)&rpADs);
BREAK_ON_FAIL_HRESULT(hr);
//
// OK, we bound to the computer, so it exists. We'll take the
// easy way out here and instead of figuring out all the info
// about the domain or workgroup to which the computer belongs,
// just create a hidden scope with no name or address.
//
// Note this means you can enter a computer name which is
// in a domain in the forest (but not yet propagated to GC, or
// GC unavailable), or in some external trusted domain, which is
// not represented by the current scope at the time the name was
// entered, but is nevertheless represented by some other scope,
// and the computer object will be returned with a scope type
// of DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE or
// DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE instead of the
// scope type of its "real" owner.
//
ADD_SCOPE_INFO asi;
if (flUplevel & DSOP_FILTER_COMPUTERS)
{
asi.flType = DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE;
asi.FilterFlags = pspUserUplevel->FilterFlags;
}
else
{
asi.flType = DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE;
asi.FilterFlags = pspUserDownlevel->FilterFlags;
}
asi.Visibility = SCOPE_HIDDEN;
const CScope &rNewScope = rsm.AddUserEnteredScope(asi);
CDsObject dsoComputer(rNewScope.GetID(), rpADs.get());
pdsolMatches->push_back(dsoComputer);
} while (0);
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_FetchAttributes
//
// Synopsis: Attempt to read for this object the attributes caller
// requested be returned on all selected objects.
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
//
// Returns: HRESULT
//
// History: 06-22-2000 DavidMun Created
//
// Notes: Object Picker does not guarantee to its callers that it
// will successfully fetch any attributes.
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_FetchAttributes(
HWND hwnd,
const CObjectPicker &rop)
{
TRACE_METHOD(CDsObject, _FetchAttributes);
HRESULT hr = S_OK;
const BSTR wzName = GetName();
const BSTR wzClass = GetClass();
const BSTR wzADsPath = GetADsPath();
do
{
//
// If we've already been here, bail.
//
if (_IsFlagSet(DSO_FLAG_FETCHED_ATTRIBUTES))
{
Dbg(DEB_TRACE,
"object %ws has already fetched attributes\n",
wzName);
break;
}
//
// See if there are any attributes to fetch. If not, bail.
//
const vector<String> &rvAttrToFetch = rop.GetAttrToFetchVector();
if (rvAttrToFetch.empty())
{
break;
}
//
// If this object has a path, bind to it for IADs and use that
// to read the attributes.
//
if (*wzADsPath)
{
RpIADs rpADs;
//
// Iterate through the attribute names, fetching each.
//
Dbg(DEB_TRACE, "fetching attributes for object %ws\n", wzName);
vector<String>::const_iterator itAttrName;
for (itAttrName = rvAttrToFetch.begin(); itAttrName != rvAttrToFetch.end(); itAttrName++)
{
HRESULT hr2;
BOOL fIsGroupTypeAttr = !itAttrName->icompare(c_wzGroupTypeAttr);
Variant varFetched;
if (fIsGroupTypeAttr &&
!lstrcmpi(wzClass, c_wzLocalGroupClass))
{
varFetched.SetUI4(ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP);
}
else if (fIsGroupTypeAttr &&
!lstrcmpi(wzClass, c_wzGlobalGroupClass))
{
varFetched.SetUI4(ADS_GROUP_TYPE_GLOBAL_GROUP);
}
else
{
//
// If we haven't bound to the object yet, do so. If this
// fails, exit the loop since we won't be able to get
// any attributes from ADSI without the interface.
//
if (!rpADs.get())
{
hr = g_pBinder->BindToObject(hwnd,
wzADsPath,
IID_IADs,
(void**) &rpADs);
BREAK_ON_FAIL_HRESULT(hr);
}
hr2 = rpADs->Get(const_cast<PWSTR>(itAttrName->c_str()),
&varFetched);
if (FAILED(hr2))
{
// this is pretty noisy and not necessarily an error
Dbg(DEB_WARN,
"Err %#x fetching attribute '%ws' on '%ws'\n",
hr2,
itAttrName->c_str(),
wzName);
hr = hr2;
ASSERT(varFetched.Empty());
}
}
//
// If we couldn't get this attribute, go on to the next one
// to fetch.
//
if (varFetched.Empty())
{
continue;
}
//
// The attribute value is now in varFetched, put its value in
// m_AttrValueMap. To do this we have to get the ATTR_KEY that
// corresponds to the attribute's ADSI name in itAttrName.
//
const CAttributeManager &ram = rop.GetAttributeManager();
ATTR_KEY ak = ram.GetAttrKey(*itAttrName);
m_AttrValueMap[ak] = varFetched;
}
break;
}
//
// This object doesn't have a path. It probably doesn't really
// exist in the DS and was added by CustomizeDsBrowser interface.
// If caller wants objectSid or groupType attribute, fabricate
// a variant with the property.
//
const CAdminCustomizer &rac = rop.GetDefaultCustomizer();
PSID psid = rac.LookupDownlevelName(wzName);
vector<String>::const_iterator it;
for (it = rvAttrToFetch.begin(); it != rvAttrToFetch.end(); it++)
{
if (!it->icompare(c_wzObjectSidAttr))
{
Variant varSid;
hr = _CreateSidVariant(psid, &varSid);
BREAK_ON_FAIL_HRESULT(hr);
m_AttrValueMap[AK_OBJECT_SID] = varSid;
}
else if (!it->icompare(c_wzGroupTypeAttr))
{
Variant varGroupType;
if (!lstrcmpi(wzClass, c_wzLocalGroupClass))
{
varGroupType.SetUI4(ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP);
}
else if (!lstrcmpi(wzClass, c_wzGlobalGroupClass))
{
varGroupType.SetUI4(ADS_GROUP_TYPE_GLOBAL_GROUP);
}
m_AttrValueMap[AK_GROUP_TYPE] = varGroupType;
}
}
} while (0);
_SetFlag(DSO_FLAG_FETCHED_ATTRIBUTES);
return hr;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_CreateSidVariant
//
// Synopsis: Fill variant pointed to by [pvar] with the array of bytes
// contained in the SID pointed to by [psid].
//
// Arguments: [psid] - points to SID to copy
// [pvar] - points to variant to fill with copy
//
// Returns: HRESULT
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_CreateSidVariant(
PSID psid,
VARIANT *pvar)
{
HRESULT hr = S_OK;
PUCHAR pcSubAuth = NULL;
ULONG cbSid = 0;
SAFEARRAYBOUND sabound;
SAFEARRAY *psa = NULL;
ULONG i;
PVOID pvData = NULL;
PULONG pulSubAuth = NULL;
ASSERT(psid);
if (!psid)
{
return E_POINTER;
}
ASSERT(IsValidSid(psid));
ASSERT(V_VT(pvar) == VT_EMPTY);
do
{
pcSubAuth = GetSidSubAuthorityCount(psid);
ASSERT(pcSubAuth);
cbSid = GetSidLengthRequired(*pcSubAuth);
ASSERT(cbSid == (*pcSubAuth - 1) * (sizeof(DWORD)) + sizeof(SID));
sabound.cElements = cbSid;
sabound.lLbound = 0;
psa = SafeArrayCreate(VT_UI1, 1, &sabound);
BREAK_ON_FAIL_HRESULT(hr);
hr = SafeArrayAccessData(psa, &pvData);
BREAK_ON_FAIL_HRESULT(hr);
pulSubAuth = (LPDWORD)pvData;
SID sid;
ZeroMemory(&sid, sizeof sid);
sid.Revision = SID_REVISION;
sid.SubAuthorityCount = *pcSubAuth;
sid.IdentifierAuthority = *GetSidIdentifierAuthority(psid);
CopyMemory(pvData, &sid, sizeof(SID));
pulSubAuth = (PULONG)((PBYTE)pvData + sizeof(SID) - sizeof(ULONG));
for (i = 0; i < *pcSubAuth; i++)
{
*pulSubAuth++ = *GetSidSubAuthority(psid, i);
}
ASSERT(IsValidSid((PSID)pvData));
SafeArrayUnaccessData(psa);
pvar->vt = VT_ARRAY | VT_UI1;
pvar->parray = psa;
} while (0);
return hr;
}
#define DSOP_ACCEPTABLE_PROVIDER_SCOPE_FLAGS \
(DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT \
| DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP)
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_ConvertProvider
//
// Synopsis: Convert the path of this object as necessary to make it
// use one of the acceptable providers for its owning scope.
//
// Arguments: [hwnd] - for bind
// [rop] - containing object picker instance
// [pnpr] - filled with result of processing
//
// Returns: HRESULT
//
// History: 08-18-1998 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_ConvertProvider(
HWND hwnd,
const CObjectPicker &rop,
NAME_PROCESS_RESULT *pnpr)
{
TRACE_METHOD(CDsObject, _ConvertProvider);
HRESULT hr = S_OK;
do
{
if (_IsFlagSet(DSO_FLAG_CONVERTED_PROVIDER))
{
break;
}
//
// Some special objects don't have paths. If the scope flag
// DSOP_SCOPE_FLAG_WANT_DOWNLEVEL_SID_PATH is set, create one based on
// the object's sid, otherwise leave it empty.
//
const CScopeManager &sm = rop.GetScopeManager();
const CScope &rOwningScope = sm.LookupScopeById(GetOwningScopeID());
ASSERT(rOwningScope.Type() != ST_INVALID);
if (!*GetADsPath())
{
//
// see if the scope params for the scope in which this
// object resides indicate we should generate a path
//
if (rOwningScope.GetScopeFlags() &
DSOP_SCOPE_FLAG_WANT_DOWNLEVEL_BUILTIN_PATH)
{
hr = _CreateDownlevelSidPath(rop);
}
break;
}
//
// Force conversion of this object to LDAP://<SID=x> format if
// objects in the owning scope are to be converted and this
// object has an objectSid attribute.
//
if (rOwningScope.GetScopeFlags() & DSOP_SCOPE_FLAG_WANT_SID_PATH)
{
hr = _CreateUplevelSidPath(hwnd);
break;
}
//
// Compare the provider used by this object against the ones
// allowed by its owning scope. If it's already in an acceptable
// form, no conversion is required.
//
ULONG flThisProvider;
hr = ProviderFlagFromPath(GetADsPath(), &flThisProvider);
BREAK_ON_FAIL_HRESULT(hr);
ULONG flAcceptableProviders =
rOwningScope.GetScopeFlags() & DSOP_ACCEPTABLE_PROVIDER_SCOPE_FLAGS;
if (!flAcceptableProviders ||
(flThisProvider & flAcceptableProviders))
{
break;
}
//
// Path is using unacceptable provider.
//
//
// If the acceptable provider list includes LDAP and the
// path is based on GC, use IADsPathname to make the change.
//
if ((flAcceptableProviders & DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP) &&
(flThisProvider & PROVIDER_GC))
{
String strPath = GetADsPath();
hr = g_pADsPath->ConvertProvider(&strPath, L"LDAP");
if (SUCCEEDED(hr))
{
Variant varPath;
hr = varPath.SetBstr(strPath);
BREAK_ON_FAIL_HRESULT(hr);
m_AttrValueMap[AK_PROCESSED_ADSPATH] = varPath;
}
break;
}
//
// If the acceptable provider list includes WinNT, use
// IADsNameTranslate to convert.
//
if (flAcceptableProviders & PROVIDER_WINNT)
{
ASSERT((flThisProvider & PROVIDER_GC) ||
(flThisProvider & DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP));
ASSERT(IsUplevel(rOwningScope));
RpIADsNameTranslate rpADsNameTranslate;
Bstr bstrMyDN;
if (rOwningScope.Type() == ST_INVALID)
{
hr = E_UNEXPECTED;
DBG_OUT_HRESULT(hr);
break;
}
const CLdapContainerScope *pOwningLdapScope =
dynamic_cast<const CLdapContainerScope *>(&rOwningScope);
ASSERT(pOwningLdapScope);
if (!pOwningLdapScope)
{
hr = E_UNEXPECTED;
DBG_OUT_HRESULT(hr);
break;
}
String strADsPath;
hr = pOwningLdapScope->GetADsPath(hwnd, &strADsPath);
BREAK_ON_FAIL_HRESULT(hr);
{
IADsNameTranslate *pNameTranslate = NULL;
hr = g_pBinder->GetNameTranslate(hwnd,
strADsPath.c_str(),
&pNameTranslate);
BREAK_ON_FAIL_HRESULT(hr);
rpADsNameTranslate.Acquire(pNameTranslate);
}
hr = g_pADsPath->SetRetrieve(ADS_SETTYPE_FULL,
GetADsPath(),
ADS_FORMAT_X500_DN,
&bstrMyDN);
hr = rpADsNameTranslate->Set(ADS_NAME_TYPE_1779, bstrMyDN.c_str());
BREAK_ON_FAIL_HRESULT(hr);
Bstr bstrNT4;
hr = rpADsNameTranslate->Get(ADS_NAME_TYPE_NT4, &bstrNT4);
BREAK_ON_FAIL_HRESULT(hr);
PWSTR pwzWhack = wcschr(bstrNT4.c_str(), L'\\');
if (pwzWhack)
{
*pwzWhack = L'/';
}
else
{
Dbg(DEB_WARN, "Expected backslash in nt4 name '%s'\n", bstrNT4);
}
String strProcessedADsPath = c_wzWinNTPrefix;
strProcessedADsPath += bstrNT4.c_str();
Variant var;
hr = var.SetBstr(strProcessedADsPath);
BREAK_ON_FAIL_HRESULT(hr);
m_AttrValueMap[AK_PROCESSED_ADSPATH] = var;
m_AttrValueMap.erase(AK_DISPLAY_PATH);
break;
}
//
// If we're still here then the conversion hasn't happened yet.
// There's one last permutation: the acceptable provider list
// includes LDAP, but the current object is using WINNT. The
// only way to convert is if the object has an objectSid attribute.
//
if ((flAcceptableProviders & DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP) &&
(flThisProvider & PROVIDER_WINNT))
{
hr = _CreateUplevelSidPath(hwnd);
}
else
{
Dbg(DEB_ERROR,
"Unexpected combination: flPathProvider 0x%x, flAcceptableProviders 0x%x\n",
flThisProvider,
flAcceptableProviders);
hr = E_FAIL;
}
} while (0);
if (FAILED(hr))
{
*pnpr = NPR_STOP_PROCESSING;
}
else
{
_SetFlag(DSO_FLAG_CONVERTED_PROVIDER);
}
return hr;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_CreateUplevelSidPath
//
// Synopsis: Convert this object's path to the form LDAP://<sid=x> where
// x is the string of hex digits (no spaces) that make up the
// objectSid attribute value.
//
// Arguments: [hwnd] - for bind
//
// Returns: HRESULT
//
// History: 08-18-1998 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_CreateUplevelSidPath(
HWND hwnd)
{
HRESULT hr = S_OK;
RpIADs rpADs;
Variant varSid;
PSID psid = NULL;
BOOL fAccessedData = FALSE;
do
{
if (!*GetADsPath())
{
break;
}
hr = g_pBinder->BindToObject(hwnd,
GetADsPath(),
IID_IADs,
(void**)&rpADs);
BREAK_ON_FAIL_HRESULT(hr);
hr = rpADs->Get((PWSTR)c_wzObjectSidAttr, &varSid);
BREAK_ON_FAIL_HRESULT(hr);
ASSERT(varSid.Type() == (VT_ARRAY | VT_UI1));
hr = SafeArrayAccessData(V_ARRAY(&varSid), (VOID**)&psid);
BREAK_ON_FAIL_HRESULT(hr);
fAccessedData = TRUE;
ASSERT(IsValidSid(psid));
String strPath = c_wzLDAPPrefix;
strPath += c_wzSidPathPrefix;
//
// Convert the bytes of the sid to hex chars.
//
PBYTE pbSid = (PBYTE) psid;
ULONG i;
PUCHAR pcSubAuth = NULL;
pcSubAuth = GetSidSubAuthorityCount(psid);
ASSERT(pcSubAuth);
ULONG cbSid = GetSidLengthRequired(*pcSubAuth);
ASSERT(cbSid);
ASSERT(cbSid == (*pcSubAuth - 1) * (sizeof(DWORD)) + sizeof(SID));
for (i = 0; i < cbSid; i++)
{
WCHAR wzCur[3];
wsprintf(wzCur, L"%02x", *pbSid);
pbSid++;
strPath += wzCur;
}
strPath += c_wzSidPathSuffix;
Variant var;
hr = var.SetBstr(strPath);
BREAK_ON_FAIL_HRESULT(hr);
m_AttrValueMap[AK_PROCESSED_ADSPATH] = var;
} while (0);
if (fAccessedData)
{
SafeArrayUnaccessData(V_ARRAY(&varSid));
}
return hr;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_CreateDownlevelSidPath
//
// Synopsis: Create a path in the form WinNT://NT AUTHORITY/Interactive
//
// Arguments: [rop] - containing object picker instance
//
// Returns: HRESULT
//
// History: 07-21-1999 davidmun Created
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_CreateDownlevelSidPath(
const CObjectPicker &rop)
{
TRACE_METHOD(CDsObject, _CreateDownlevelSidPath);
HRESULT hr = S_OK;
do
{
const CAdminCustomizer &rac = rop.GetDefaultCustomizer();
PCWSTR pwzPath = rac.LookupDownlevelPath(GetName());
if (!pwzPath)
{
hr = E_FAIL;
Dbg(DEB_WARN,
"AdminCustomizer gave no path for %ws\n",
GetName());
break;
}
Variant var;
hr = var.SetBstr(pwzPath);
BREAK_ON_FAIL_HRESULT(hr);
m_AttrValueMap[AK_PROCESSED_ADSPATH] = var;
_SetFlag(DSO_FLAG_HAS_DOWNLEVEL_SID_PATH);
} while (0);
return hr;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_MultiMatchDialog
//
// Synopsis: Invoke the multi-match dialog so the user can select which
// of the multiple hits from the prefix search of the name
// they entered they want to keep.
//
// Arguments: [hwnd] - parent window
// [rop] - containing object picker instance
// [fMultiselect] - if FALSE user forced to pick only one match
// [strName] - string for which multiple matches were found.
// [pnpr] - filled with result of processing
// [pdsolMatches] - on input, contains all matches. on output
// with hr==S_OK, contains exactly one.
// [pdsolExtras] - on output, matches 2..n have been appended.
//
// Returns: S_OK
// E_*
//
// Modifies: *[pdsolMatches], *[pdsolExtras]
//
// History: 08-18-1998 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
CDsObject::_MultiMatchDialog(
HWND hwnd,
const CObjectPicker &rop,
BOOL fMultiselect,
const String & strName,
NAME_PROCESS_RESULT *pnpr,
CDsObjectList *pdsolMatches,
CDsObjectList *pdsolExtras)
{
TRACE_METHOD(CDsObject, _MultiMatchDialog);
ASSERT(IsUnprocessedUserEntry());
CMultiDlg MultiMatchDlg(rop, strName);
HRESULT hr;
hr = MultiMatchDlg.DoModalDialog(hwnd,
fMultiselect,
pnpr,
pdsolMatches);
if (FAILED(hr) || *pnpr != NPR_SUCCESS)
{
return hr;
}
ASSERT(!pdsolMatches->empty());
if (pdsolMatches->size() > 1)
{
CDsObjectList::iterator start = pdsolMatches->begin();
start++;
pdsolExtras->splice(pdsolExtras->end(),
*pdsolMatches,
start,
pdsolMatches->end());
}
return S_OK;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::_InitDisplayPath
//
// Synopsis: Return the canonical path to the container of this object.
//
// Returns: Pointer to display path string, L"" on failure.
//
// History: 07-21-1998 DavidMun Created
//
//---------------------------------------------------------------------------
void
CDsObject::_InitDisplayPath() const
{
HRESULT hr = S_OK;
do
{
//
// Certain custom objects may not have paths. Skip those.
//
if (GetAttr(AK_ADSPATH).Empty())
{
break;
}
//
// Well-known security principals' display paths are blank, even
// though they are real objects with valid ADsPaths.
//
if (!lstrcmpi(GetAttr(AK_OBJECT_CLASS).GetBstr(),
c_wzForeignPrincipalsClass))
{
break;
}
//
// Need to construct a display path, start with the ads path. Note
// we don't want AK_PROCESSED_ADSPATH because that may be something
// ugly like a SID path.
//
String strDisplayPath = GetAttr(AK_ADSPATH).GetBstr();
BOOL fWinNTPath = !_wcsnicmp(strDisplayPath.c_str(),
c_wzWinNTPrefix,
ARRAYLEN(c_wzWinNTPrefix) - 1);
//
// If the path uses the WinNT provider, it is of the form
// WinNT://domain/object or WinNT://domain/machine/object
// which is displayed as "domain" or "machine".
//
if (fWinNTPath)
{
strDisplayPath.erase(0, ARRAYLEN(c_wzWinNTPrefix) - 1);
// domain/object or domain/machine/object
size_t idxSlash1 = strDisplayPath.find(L'/');
ASSERT(idxSlash1 != String::npos);
size_t idxSlash2 = strDisplayPath.find(L'/', idxSlash1 + 1);
if (idxSlash2 == String::npos)
{
// domain/object
strDisplayPath.erase(idxSlash1);
// domain
}
else
{
// domain/machine/object
strDisplayPath.erase(idxSlash2);
// domain/machine
strDisplayPath.erase(0, idxSlash1 + 1);
// machine
}
m_AttrValueMap[AK_DISPLAY_PATH].SetBstr(strDisplayPath.c_str());
break;
}
//
// It's not a WinNT provider based path. Convert container path
// from 1779 to canonical, or a close approximation. This is for
// display only, it will never be passed to name translate.
//
CPathWrapLock lock(g_pADsPath);
hr = g_pADsPath->Set(strDisplayPath.c_str(), ADS_SETTYPE_FULL);
BREAK_ON_FAIL_HRESULT(hr);
hr = g_pADsPath->RemoveLeafElement();
BREAK_ON_FAIL_HRESULT(hr);
long cElem;
hr = g_pADsPath->GetNumElements(&cElem);
BREAK_ON_FAIL_HRESULT(hr);
ASSERT(cElem > 0);
long i;
strDisplayPath = L"";
for (i = cElem - 1; i >= 0; i--)
{
Bstr bstrElem;
hr = g_pADsPath->GetElement(i, &bstrElem);
BREAK_ON_FAIL_HRESULT(hr);
String strElem(bstrElem.c_str());
strElem.replace(L"\\\\", L"\\"); // undo escaping
size_t idxEqual = strElem.find(L'=', 0);
ASSERT(idxEqual != String::npos);
if (!lstrcmpi(strElem.substr(0, idxEqual).c_str(), L"DC"))
{
if (strDisplayPath.empty())
{
strDisplayPath = strElem.substr(idxEqual + 1,
String::npos);
}
else
{
strDisplayPath.insert(0, L".");
strDisplayPath.insert(0, strElem.substr(idxEqual + 1,
String::npos));
}
}
else
{
ASSERT(!strDisplayPath.empty());
strDisplayPath += L"/";
strDisplayPath += strElem.substr(idxEqual + 1, String::npos);
}
}
BREAK_ON_FAIL_HRESULT(hr);
m_AttrValueMap[AK_DISPLAY_PATH].SetBstr(strDisplayPath.c_str());
} while (0);
if (FAILED(hr))
{
m_AttrValueMap[AK_DISPLAY_PATH].SetBstr(L"");
}
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::GetAttr
//
// Synopsis: Return attribute with name [strAdsiName].
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
const Variant &
CDsObject::GetAttr(
const String &strAdsiName,
const CObjectPicker &rop) const
{
return GetAttr(rop.GetAttributeManager().GetAttrKey(strAdsiName));
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::GetAttr
//
// Synopsis: Return attribute having key [ak].
//
// Arguments: [ak] - ATTR_KEY value representing attribute
//
// Returns: Reference to variant containing attribute, or to empty
// variant if this doesn't contain the requested attribute.
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
const Variant &
CDsObject::GetAttr(
ATTR_KEY ak) const
{
AttrValueMap::const_iterator it;
if (ak == AK_DISPLAY_PATH)
{
_InitDisplayPath();
}
it = m_AttrValueMap.find(ak);
if (it == m_AttrValueMap.end())
{
return s_varEmpty;
}
return it->second;
}
//+--------------------------------------------------------------------------
//
// Member: CDsObject::GetMarshalSize
//
// Synopsis: Get the size required to put this into a block of data for
// a data object which is to be returned to caller.
//
// Returns: Size in bytes needed to marshal this (not counting fetched
// attributes, which are stored separately).
//
// History: 06-22-2000 DavidMun Created
//
//---------------------------------------------------------------------------
ULONG
CDsObject::GetMarshalSize() const
{
ULONG cch;
BSTR bstrName = GetName();
BSTR bstrADsPath = GetAttr(AK_PROCESSED_ADSPATH).GetBstr();
if (!*bstrADsPath)
{
bstrADsPath = GetADsPath();
}
BSTR bstrClass = GetClass();
BSTR bstrUpn = GetUpn();
if (!lstrcmpi(bstrClass, c_wzLocalGroupClass) ||
!lstrcmpi(bstrClass, c_wzGlobalGroupClass))
{
cch = lstrlen(bstrName) + 1 +
lstrlen(c_wzGroupObjectClass) + 1 +
lstrlen(bstrADsPath) + 1 +
lstrlen(bstrUpn) + 1;
}
else
{
cch = lstrlen(bstrName) + 1 +
lstrlen(bstrClass) + 1 +
lstrlen(bstrADsPath) + 1 +
lstrlen(bstrUpn) + 1;
}
return cch * sizeof(WCHAR);
}
void AddDollarToNameToCrack(IN DS_NAME_FORMAT FormatOffered,
String &strNameToCrack)
{
if(FormatOffered == DS_USER_PRINCIPAL_NAME)
{
//
//Find the rightmost @
//
size_t idxLastAt = strNameToCrack.rfind(L'@');
if (idxLastAt != String::npos)
{
strNameToCrack.insert(idxLastAt,L"$");
}
}
else if(FormatOffered == DS_NT4_ACCOUNT_NAME)
{
strNameToCrack.append(L"$");
}
}
//+----------------------------------------------------------------------------
//
// Function: CrackName
//
// Synopsis: Given an object name, returns the DnsDomainName of the Domain in
// which it resides. If bCrackInExtForest is true, tries to chase to
// the "DS_NAME_ERROR_TRUST_REFERRAL" referral.
// ARGUMENTS
// [IN hwnd] Handle to owning window
// [IN pwzNameIn] Name to crack
// [IN FormatOffered] Format of pwzNameIn. It can be DS_USER_PRINCIPAL_NAME,
// DS_NT4_ACCOUNT_NAME, or DS_UNKNOWN_NAME
// [IN pwzDomainName] Domain Name from where to start crack
// [IN bCrackInExtForest] If to follow the DS_NAME_ERROR_TRUST_REFERRAL
// [in ppwzResultName] Output dnsDomainName. This value can be Null
// even if function returns S_OK if
// bCrackInExtForest is FALSE and object existin
// other forest.
// [pbExtForest] True if object exist trusted forest
// Return Value: S_OK if succeeds. Else DSCrackError or E_FAIL
//-----------------------------------------------------------------------------
HRESULT
CrackName(IN HWND hwnd,
IN LPWSTR pwzNameIn,
IN DS_NAME_FORMAT FormatOffered,
IN LPWSTR pwzDomainName,
IN BOOL bCrackInExtForest,
OUT LPWSTR * ppwzResultName,
OUT PBOOL pbExtForest,
OUT PBOOL pbAddDollar)
{
TRACE_FUNCTION(Entering CrackName);
if(!pwzNameIn ||
!pwzDomainName ||
!ppwzResultName ||
!pbExtForest ||
!pbAddDollar)
{
ASSERT(FALSE);
return E_INVALIDARG;
}
Dbg(DEB_TRACE,
"Name to crack'%ws' Starting Domain '%ws' \n",
pwzNameIn,
pwzDomainName);
HRESULT hr = S_OK;
HANDLE hDS = (HANDLE)-1;
DWORD dwErr = 0;
PDS_NAME_RESULTW pDsNameResult = NULL;
BOOL fLoopAgain = FALSE;
DS_NAME_FORMAT FormatRequested = DS_CANONICAL_NAME;
//
//First Crack Name is at GC
//
DWORD BindToDcFlag = OP_GC_SERVER_REQUIRED;
//
//Init strDomain. strDomain contains the domain name at which to try
//the next DsCrackName.
//
String strDomain = pwzDomainName;
*pbExtForest = FALSE;
String strNameToCrack = pwzNameIn;
//
//If CrackName at GC returns TRUST_REFERRAL and CrackName at referral returns
//NO_MAPPING error try by adding a $ to the objectname.
// NTRAID#NTBUG9-401249-2001/05/21-hiteshr
//
BOOL bTriedWithDollar = FALSE;
*pbAddDollar = FALSE;
do
{
//
// Get a DC name for the domain and bind to it.
//
Dbg(DEB_TRACE,
"Trying to do dsbind to '%ws' \n",
strDomain.c_str());
hr = g_pBinder->BindToDcInDomain(hwnd,
strDomain.c_str(),
BindToDcFlag,
&hDS
);
BREAK_ON_FAIL_HRESULT(hr);
//
//We don't need GC for other calls
//
BindToDcFlag &= ~OP_GC_SERVER_REQUIRED;
//
// Convert the object name.
//
Dbg(DEB_TRACE,
"Calling DsCrackNamesW \n");
LPCWSTR pwzNameToCrack = strNameToCrack.c_str();
dwErr = DsCrackNamesW(hDS,
DS_NAME_FLAG_TRUST_REFERRAL,
FormatOffered,
FormatRequested,
1,
&pwzNameToCrack,
&pDsNameResult);
if(dwErr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(dwErr);
BREAK_ON_FAIL_HRESULT(hr);
}
ASSERT(pDsNameResult);
ASSERT(pDsNameResult->cItems == 1);
switch (pDsNameResult->rItems->status)
{
//
// The object info is in another domain. But we only
// need the DNS domain name. So we don't need to loop again.
//
case DS_NAME_ERROR_DOMAIN_ONLY:
{
Dbg(DEB_TRACE,
"DsCrackNamesW Status is DS_NAME_ERROR_DOMAIN_ONLY.\n DnsDomain name is '%ws'",
pDsNameResult->rItems->pDomain);
hr = LocalAllocString(ppwzResultName,pDsNameResult->rItems->pDomain);
DsFreeNameResultW(pDsNameResult);
pDsNameResult = NULL;
fLoopAgain = FALSE;
}
break;
//
// The object info is in another FOREST
// Try dscrackname again
//
case DS_NAME_ERROR_TRUST_REFERRAL:
{
Dbg(DEB_TRACE,
"DsCrackNamesW Status is DS_NAME_ERROR_TRUST_REFERRAL.\n DnsDomainname returned is '%ws'",
pDsNameResult->rItems->pDomain);
if(bCrackInExtForest)
{
strDomain = pDsNameResult->rItems->pDomain;
fLoopAgain = TRUE;
}
else
{
//
//if bCrackInExtForest is false, we don't try to crack and
//*ppwzResultName is NULL. Return is S_OK.
//
fLoopAgain = FALSE;
}
DsFreeNameResultW(pDsNameResult);
pDsNameResult = NULL;
*pbExtForest = TRUE;
}
break;
case DS_NAME_NO_ERROR:
{
//
// Success!
//
Dbg(DEB_TRACE,
"DsCrackNamesW Status is DS_NAME_NO_ERROR.\n DnsDomainname returned is '%ws'",
pDsNameResult->rItems->pDomain);
hr = LocalAllocString(ppwzResultName,pDsNameResult->rItems->pDomain);
DsFreeNameResultW(pDsNameResult);
pDsNameResult = NULL;
fLoopAgain = FALSE;
if(bTriedWithDollar)
*pbAddDollar = TRUE;
}
break;
case DS_NAME_ERROR_RESOLVING:
case DS_NAME_ERROR_NOT_FOUND:
case DS_NAME_ERROR_NO_MAPPING:
{
if(*pbExtForest && !bTriedWithDollar)
{
bTriedWithDollar = TRUE;
AddDollarToNameToCrack(FormatOffered, strNameToCrack);
Dbg(DEB_TRACE,"DsCrackNamesW Status is DS_NAME_ERROR_NO_MAPPING.\n Trying to crack with $ sign added\n");
fLoopAgain = TRUE;
}
else
{
Dbg(DEB_TRACE,
"DsCrackNamesW Status is DS_NAME_ERROR_NO_MAPPING.\n");
if(!fLoopAgain)
hr = E_FAIL;
fLoopAgain = FALSE;
}
}
break;
default:
{
fLoopAgain = FALSE;
hr = E_FAIL;
}
break;
}
}while (fLoopAgain);
if (pDsNameResult)
{
DsFreeNameResultW(pDsNameResult);
}
return hr;
}