Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1603 lines
52 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000
//
// File: dsquery.cpp
//
// Contents: Defines the main function DSQUERY
// command line utility
//
// History: 06-Sep-2000 hiteshr Created
//
//
//--------------------------------------------------------------------------
#include "pch.h"
#include "cstrings.h"
#include "usage.h"
#include "querytable.h"
#include "querybld.h"
#include "dsquery.h"
#include "query.h"
#include "resource.h"
#include "output.h"
#include <dscmn.h>
#include "Ntdsapi.h"
//
//Structure Defined to Store Global Values at one place.
//
typedef struct _GlobalInfo
{
ADS_SCOPEENUM scope; //Scope of query
DSQUERY_OUTPUT_FORMAT outputFormat; //Output Format
}GLOBAL_INFO,*PGLOBAL_INFO;
bool g_bQuiet = false;
int g_iQueryLimit = 100;
bool g_bDeafultLimit = true;
DSQUERY_COMMAND_ENUM g_eGC = (DSQUERY_COMMAND_ENUM)-1;
//
// Forward Function Declarations
//
HRESULT DoQueryValidation(PARG_RECORD pCommandArgs,
PDSQueryObjectTableEntry pObjectEntry,
PGLOBAL_INFO pcommon_info);
HRESULT DoQuery(PARG_RECORD pCommandArgs,
PDSQueryObjectTableEntry pObjectEntry,
PGLOBAL_INFO pcommon_info);
HRESULT GetAttributesToFetch(IN PGLOBAL_INFO pcommon_info,
IN PARG_RECORD pCommandArgs,
IN PDSQueryObjectTableEntry pObjectEntry,
OUT LPWSTR **ppszAttributes,
OUT DWORD * pCount);
VOID FreeAttributesToFetch( IN LPWSTR *ppszAttributes,
IN DWORD dwCount);
HRESULT GetSearchRoot(IN IN PDSQueryObjectTableEntry pObjectEntry,
IN PARG_RECORD pCommandArgs,
IN CDSCmdBasePathsInfo& refBasePathsInfo,
OUT CComBSTR& refsbstrDN,
OUT BOOL *pbSearchAtForestRoot,
OUT BOOL *pbSearchAtGC);
HRESULT GetSearchObject(IN IN PDSQueryObjectTableEntry pObjectEntry,
IN CDSCmdBasePathsInfo& refBasePathsInfo,
IN PARG_RECORD pCommandArgs,
IN CDSCmdCredentialObject& refCredentialObject,
IN CComBSTR& refsbstrDN,
IN BOOL bSearchAtForestRoot,
IN BOOL bSearchAtGC,
OUT CComPtr<IDirectorySearch>& refspSearchObject);
BOOL
TranslateNameFromDnToDns(const CComBSTR& bstrInputDN,
CComBSTR& bstrOutputDNS);
HRESULT GetGCIndex(PDSQueryObjectTableEntry pObjectEntry, int& nCommandEnum);
//
//Main Function
//
int __cdecl _tmain( VOID )
{
int argc = 0;
LPTOKEN pToken = NULL;
HRESULT hr = S_OK;
PARG_RECORD pNewCommandArgs = 0;
//
// False loop
//
do
{
//
// Initialize COM
//
hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
break;
}
//Get CommandLine Input
DWORD dwErr = GetCommandInput(&argc,&pToken);
hr = HRESULT_FROM_WIN32(dwErr);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
break;
}
if(argc == 1)
{
//
// Display the error message and then break out of the false loop
//
DisplayMessage(USAGE_DSQUERY,TRUE);
hr = E_INVALIDARG;
break;
}
if(argc == 2)
{
if(IsTokenHelpSwitch(pToken + 1))
{
hr = S_OK;
DisplayMessage(USAGE_DSQUERY,TRUE);
break;
}
}
//
// Find which object table entry to use from
// the second command line argument
//
PDSQueryObjectTableEntry pObjectEntry = NULL;
UINT idx = 0;
PWSTR pszObjectType = (pToken+1)->GetToken();
while (true)
{
pObjectEntry = g_DSObjectTable[idx++];
if (!pObjectEntry)
{
break;
}
//Security Review:Both strings are null terminated.
if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
{
break;
}
}
if (!pObjectEntry)
{
//
// Display the error message and then break out of the false loop
//
hr = E_INVALIDARG;
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_INVALID_OBJECTTYPE);
}
break;
}
//
// Now that we have the correct table entry, merge the command line table
// for this object with the common commands
//
hr = MergeArgCommand(DSQUERY_COMMON_COMMANDS,
pObjectEntry->pParserTable,
&pNewCommandArgs);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
break;
}
//
//Parse the Input
//
PARSE_ERROR Error;
if(!ParseCmd(g_pszDSCommandName,
pNewCommandArgs,
argc-1,
pToken+1,
pObjectEntry->pUsageTable,
&Error,
TRUE))
{
//ParseCmd did not display any error. Error should
//be handled here. Check DisplayParseError for the
//cases where Error is not shown by ParseCmd
if(!Error.MessageShown)
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
break;
}
if(Error.ErrorSource == ERROR_FROM_PARSER
&& Error.Error == PARSE_ERROR_HELP_SWITCH)
{
hr = S_OK;
break;
}
hr = E_INVALIDARG;
break;
}
//
// Check to see if we are doing debug spew
//
#ifdef DBG
bool bDebugging = pNewCommandArgs[eCommDebug].bDefined &&
pNewCommandArgs[eCommDebug].nValue;
if (bDebugging)
{
ENABLE_DEBUG_OUTPUT(pNewCommandArgs[eCommDebug].nValue);
}
#else
DISABLE_DEBUG_OUTPUT();
#endif
// Get the GC switch (if supported)
int nCommandEnum = -1;
if (FAILED(GetGCIndex(pObjectEntry, nCommandEnum)))
{
// An object type is missing in GetGCIndex
if(!Error.MessageShown)
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
break;
}
}
g_eGC = (DSQUERY_COMMAND_ENUM) nCommandEnum;
//
// Set the global quiet flag
//
g_bQuiet = pNewCommandArgs[eCommQuiet].bDefined &&
pNewCommandArgs[eCommQuiet].bValue;
//
//
//
if(pNewCommandArgs[eCommLimit].bDefined)
{
g_iQueryLimit = pNewCommandArgs[eCommLimit].nValue;
g_bDeafultLimit = false;
}
GLOBAL_INFO common_info;
common_info.scope = ADS_SCOPE_SUBTREE;
common_info.outputFormat = DSQUERY_OUTPUT_DN;
//
// Do extra validation like switch dependency check etc.
// Also collect Query Scope and Output format
//
hr = DoQueryValidation(pNewCommandArgs,
pObjectEntry,
&common_info);
if (FAILED(hr))
break;
//
// Command line parsing succeeded
//
hr = DoQuery(pNewCommandArgs,
pObjectEntry,
&common_info);
if(FAILED(hr))
break;
} while (false); //False Loop
//
//Do the CleanUp
//
//
// Free the memory associated with the command values
//
if(pNewCommandArgs)
FreeCmd(pNewCommandArgs);
//
// Free the tokens
//
if (pToken)
{
delete[] pToken;
pToken = 0;
}
//
// Uninitialize COM
//
CoUninitialize();
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: DoQueryValidation
//
// Synopsis: Checks to be sure that command line switches that are mutually
// exclusive are not both present and those that are dependent are
// both presetn, and other validations which cannot be done by parser.
//
// Arguments: [pCommandArgs - IN] : the command line argument structure used
// to retrieve the values for each switch
// [pObjectEntry - IN] : pointer to the object table entry for the
// object type that will be queryied
// [pcommon_info - OUT]: gets scope and output format info
//
// Returns: HRESULT : S_OK if everything succeeded
// E_INVALIDARG if the object entry wasn't found
//
// History: 25-Sep-2000 hiteshr Created
//
//---------------------------------------------------------------------------
HRESULT DoQueryValidation(IN PARG_RECORD pCommandArgs,
IN PDSQueryObjectTableEntry pObjectEntry,
OUT PGLOBAL_INFO pcommon_info)
{
ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoQueryValidation, hr);
if (!pCommandArgs || !pObjectEntry || !pcommon_info)
{
ASSERT(pCommandArgs);
ASSERT(pObjectEntry);
ASSERT(pcommon_info);
hr = E_INVALIDARG;
return hr;
}
// Check to be sure the server and domain switches
// are mutually exclusive
if (pCommandArgs[eCommServer].bDefined &&
pCommandArgs[eCommDomain].bDefined)
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
//
//Validate OutputFormat for "dsquery objectType"
//
//Security Review:Both strings are null terminated.
if(_wcsicmp(pObjectEntry->pszCommandLineObjectType,g_pszStar))
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"dsquery <objectType> processing will be performed");
if(pCommandArgs[eCommOutputFormat].bDefined &&
pCommandArgs[eCommOutputFormat].strValue)
{
//
//ppValidOutput contains the validoutput type for a
//given object type
//
ASSERT(pObjectEntry->ppValidOutput);
BOOL bMatch = FALSE;
for(UINT i = 0; i < pObjectEntry->dwOutputCount; ++i)
{
//Security Review:Both strings are null terminated.
if(_wcsicmp(pCommandArgs[eCommOutputFormat].strValue,
pObjectEntry->ppValidOutput[i]->pszOutputFormat) == 0 )
{
bMatch = TRUE;
pcommon_info->outputFormat = pObjectEntry->ppValidOutput[i]->outputFormat;
break;
}
}
if(!bMatch)
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_INVALID_OUTPUT);
return hr;
}
}
//
//default output format is DN
//
else
pcommon_info->outputFormat = DSQUERY_OUTPUT_DN;
}
else
{
//
//-o is invalid switch form dsquery *, but since its
//common for all other objects its kept in common table
//and we do the special casing for dsquery *
//
if(pCommandArgs[eCommOutputFormat].bDefined &&
pCommandArgs[eCommOutputFormat].strValue)
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_O_NOT_FOR_STAR);
return hr;
}
DEBUG_OUTPUT(MINIMAL_LOGGING, L"dsquery * processing will be performed");
if(pCommandArgs[eStarAttrsOnly].bDefined)
pcommon_info->outputFormat = DSQUERY_OUTPUT_ATTRONLY;
else
pcommon_info->outputFormat = DSQUERY_OUTPUT_ATTR;
}
//
//Validate Scope string.
//default scope is subtree.
//
pcommon_info->scope = ADS_SCOPE_SUBTREE;
if(pObjectEntry->nScopeID != -1)
{
if( pCommandArgs[pObjectEntry->nScopeID].bDefined &&
pCommandArgs[pObjectEntry->nScopeID].strValue )
{
LPCWSTR pszScope = pCommandArgs[pObjectEntry->nScopeID].strValue;
//Security Review:Both strings are null terminated.
if( _wcsicmp(pszScope,g_pszSubTree) == 0 )
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = subtree");
pcommon_info->scope = ADS_SCOPE_SUBTREE;
}
else if( _wcsicmp(pszScope,g_pszOneLevel) == 0 )
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = onelevel");
pcommon_info->scope = ADS_SCOPE_ONELEVEL;
}
else if( _wcsicmp(pszScope,g_pszBase) == 0 )
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = base");
pcommon_info->scope = ADS_SCOPE_BASE;
}
else
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Unknown scope = %s", pszScope);
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_ERROR_SCOPE);
return hr;
}
}
}
//
//if startnode is forestroot, only valid scope is ADS_SCOPE_SUBTREE
//NTRAID#NTBUG9-382511-2001/05/14-hiteshr
//
if(pCommandArgs[eCommStartNode].bDefined &&
pCommandArgs[eCommStartNode].strValue)
{
//Security Review:Both strings are null terminated.
if((_wcsicmp(pCommandArgs[eCommStartNode].strValue, g_pszForestRoot) == 0 )
&& (pcommon_info->scope != ADS_SCOPE_SUBTREE))
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Startnode is forestroot, Scope must be SubTree");
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_FOREST_SEARCH_SCOPE);
return hr;
}
if((_wcsicmp(pCommandArgs[eCommStartNode].strValue, g_pszForestRoot) == 0 )
&& _wcsicmp(pObjectEntry->pszCommandLineObjectType, g_pszQuota) == 0)
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"A Startnode of forestroot is not allowed for quotas");
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_FOREST_SEARCH_SCOPE_QUOTAS);
return hr;
}
}
//
//Limit must be 0 or greater
//
if(pCommandArgs[eCommLimit].bDefined)
{
if(pCommandArgs[eCommLimit].nValue < 0)
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_ERROR_LIMIT);
return hr;
}
}
//
//Forestwide Search implies the -GC switch so define it if it isn't already
//
if(pCommandArgs[eCommStartNode].bDefined &&
pCommandArgs[eCommStartNode].strValue )
{
//Security Review:Both strings are null terminated.
if(_wcsicmp(pCommandArgs[eCommStartNode].strValue,g_pszForestRoot) == 0)
{
// partitions and quotas don't support forestRoot so
// g_eGC will be valid unless there is an upstream bug
// so this ASSERT will catch that in private tests
ASSERT(g_eGC != -1);
if(!(pCommandArgs[g_eGC].bDefined &&
pCommandArgs[g_eGC].bValue))
{
pCommandArgs[g_eGC].bDefined = TRUE;
pCommandArgs[g_eGC].bValue = TRUE;
}
}
}
//
//For dsquery server, if none of the -domain, -forest, -site is
//specified, then define -domain as its default
//
//Security Review:Both strings are null terminated.
if(!_wcsicmp(pObjectEntry->pszCommandLineObjectType,g_pszServer))
{
//
//Value is assigned in DoQuery function
//
if(!pCommandArgs[eServerDomain].bDefined &&
!pCommandArgs[eServerForest].bDefined &&
!pCommandArgs[eServerSite].bDefined)
{
pCommandArgs[eServerDomain].bDefined = TRUE;
}
}
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: DoQuery
//
// Synopsis: Does the query
// Arguments: [pCommandArgs - IN] : the command line argument structure used
// to retrieve the values for each switch
// [pObjectEntry - IN] : pointer to the object table entry for the
// object type that will be modified
// [pcommon_info - IN] : scope and outputformat info
//
// Returns: HRESULT : S_OK if everything succeeded
// E_INVALIDARG if the object entry wasn't found
// Anything else is a failure code from an ADSI call
//
// History: 25-Sep-2000 hiteshr Created
//
//---------------------------------------------------------------------------
HRESULT DoQuery(PARG_RECORD pCommandArgs,
PDSQueryObjectTableEntry pObjectEntry,
PGLOBAL_INFO pcommon_info)
{
ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoQuery, hr);
if (!pCommandArgs || !pObjectEntry || !pcommon_info)
{
ASSERT(pCommandArgs);
ASSERT(pObjectEntry);
ASSERT(pcommon_info);
hr = E_INVALIDARG;
return hr;
}
CDSCmdCredentialObject credentialObject;
if (pCommandArgs[eCommUserName].bDefined)
{
credentialObject.SetUsername(pCommandArgs[eCommUserName].strValue);
credentialObject.SetUsingCredentials(true);
}
if (pCommandArgs[eCommPassword].bDefined)
{
//Security Review:pCommandArgs[eCommPassword].strValue is encrypted.
//Decrypt pCommandArgs[eCommPassword].strValue and then pass it to the
//credentialObject.SetPassword.
//See NTRAID#NTBUG9-571544-2000/11/13-hiteshr
credentialObject.SetEncryptedPassword(&pCommandArgs[eCommPassword].encryptedDataBlob);
credentialObject.SetUsingCredentials(true);
}
// If this is something that supports the GC switch then do the next check
if(g_eGC != -1)
{
//if -GC and -s flags are specified together than server must be
//GC.
if(pCommandArgs[g_eGC].bDefined &&
pCommandArgs[g_eGC].bValue &&
pCommandArgs[eCommServer].bDefined &&
pCommandArgs[eCommServer].strValue)
{
if(!IsServerGC(pCommandArgs[eCommServer].strValue,credentialObject))
{
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_SEVER_NOT_GC);
return hr;
}
}
}
//
// Initialize the base paths info from the command line args
//
CDSCmdBasePathsInfo basePathsInfo;
if (pCommandArgs[eCommServer].bDefined)
{
hr = basePathsInfo.InitializeFromName(credentialObject,
pCommandArgs[eCommServer].strValue,
true);
}
else if (pCommandArgs[eCommDomain].bDefined)
{
hr = basePathsInfo.InitializeFromName(credentialObject,
pCommandArgs[eCommDomain].strValue,
false);
}
else
{
hr = basePathsInfo.InitializeFromName(credentialObject, NULL, false);
}
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
//
//Check if to search GC and get the search root path
//
BOOL bSearchAtGC = FALSE;
BOOL bSearchAtForestRoot = FALSE;
CComBSTR sbstrObjectDN;
hr = GetSearchRoot(pObjectEntry,
pCommandArgs,
basePathsInfo,
sbstrObjectDN,
&bSearchAtForestRoot,
&bSearchAtGC);
if (FAILED(hr))
{
//Error is displayed in the function itself.
return hr;
}
DEBUG_OUTPUT(MINIMAL_LOGGING, L"start node = %s", sbstrObjectDN);
//
//Build The Filter For Query
//
CComBSTR strSubSiteSuffix;
PVOID pParam = NULL;
//Security Review:Both strings are null terminated.
if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSubnet) == 0)
{
GetSiteContainerPath(basePathsInfo, strSubSiteSuffix);
pParam = (PVOID)&strSubSiteSuffix;
}
CComBSTR strLDAPFilter;
hr = BuildQueryFilter(pCommandArgs,
pObjectEntry,
basePathsInfo,
credentialObject,
pParam,
strLDAPFilter);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
//
//Create The IDirectorySearchObject
//
CComPtr<IDirectorySearch> spSearchObject;
hr = GetSearchObject(pObjectEntry,
basePathsInfo,
pCommandArgs,
credentialObject,
sbstrObjectDN,
bSearchAtForestRoot,
bSearchAtGC,
spSearchObject);
if (FAILED(hr))
{
//Error is displayed in the function itself.
return hr;
}
//
//Get the arributes to fetch
//
LPWSTR *ppszAttributes = NULL;
DWORD dwCountAttr = 0;
hr = GetAttributesToFetch(pcommon_info,
pCommandArgs,
pObjectEntry,
&ppszAttributes,
&dwCountAttr);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
//
//Lets Query Now
//
CDSSearch searchObject;
hr = searchObject.Init(spSearchObject);
if (FAILED(hr))
{
DEBUG_OUTPUT(MINIMAL_LOGGING,
L"Initializing search object failed: hr = 0x%x",
hr);
FreeAttributesToFetch(ppszAttributes, dwCountAttr);
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
searchObject.SetFilterString(strLDAPFilter);
searchObject.SetSearchScope(pcommon_info->scope);
searchObject.SetAttributeList(ppszAttributes,dwCountAttr?dwCountAttr:-1);
hr = searchObject.DoQuery();
if(FAILED(hr))
{
DEBUG_OUTPUT(MINIMAL_LOGGING, L"DoQuery failed hr = 0x%x", hr);
FreeAttributesToFetch(ppszAttributes,dwCountAttr);
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
//
//Find out the display format for dsquery *
//It can be either List or Table
//
BOOL bListFormat = TRUE;
if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR)
{
//
//If all attributes are to be displayed, only List Format is valid
//If attributes to fetch are specified at commandline, Table is default format.
if(dwCountAttr &&
!pCommandArgs[eStarList].bDefined)
bListFormat = FALSE;
}
bool bUseStandardOutput = true;
if (pCommandArgs[eCommObjectType].bDefined &&
_wcsicmp(pCommandArgs[eCommObjectType].strValue, g_pszServer) == 0)
//Security Review:Both strings are null terminated.
{
//
// "dsquery server" requires additional processing if either the
// -isgc or the -hasfsmo switch is specified
//
if ((pCommandArgs[eServerIsGC].bDefined && pCommandArgs[eServerIsGC].bValue) ||
(pCommandArgs[eServerHasFSMO].bDefined && pCommandArgs[eServerHasFSMO].strValue)||
(pCommandArgs[eServerDomain].bDefined && pCommandArgs[eServerDomain].strValue))
{
bUseStandardOutput = false;
hr = DsQueryServerOutput(pcommon_info->outputFormat,
ppszAttributes,
dwCountAttr,
searchObject,
credentialObject,
basePathsInfo,
pCommandArgs);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
}
}
}
if (bUseStandardOutput)
{
//
//Output the result of search
//
hr = DsQueryOutput(pcommon_info->outputFormat,
ppszAttributes,
dwCountAttr,
&searchObject,
bListFormat);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
}
}
FreeAttributesToFetch(ppszAttributes,dwCountAttr);
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: GetAttributesToFetch
//
// Synopsis: Make an array of attributes to fetch.
// Arguments: [pcommon_info - IN] : outputformat and scope info
// [ppszAttributes - OUT] : array of attributes to fetch
// [pCount - OUT] : count of attributes in array
//
// Returns: HRESULT : S_OK if everything succeeded
// E_INVALIDARG if the object entry wasn't found
// Anything else is a failure code from an ADSI call
//
// History: 25-Sep-2000 hiteshr Created
//
//---------------------------------------------------------------------------
HRESULT GetAttributesToFetch(IN PGLOBAL_INFO pcommon_info,
IN PARG_RECORD pCommandArgs,
IN PDSQueryObjectTableEntry pObjectEntry,
OUT LPWSTR **ppszAttributes,
OUT DWORD * pCount)
{
ENTER_FUNCTION_HR(MINIMAL_LOGGING, GetAttributesToFetch, hr);
if(!pcommon_info || !pCommandArgs || !pObjectEntry)
{
ASSERT(pcommon_info);
ASSERT(pCommandArgs);
ASSERT(pObjectEntry);
hr = E_INVALIDARG;
return hr;
}
if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR ||
pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTRONLY)
{
if(pCommandArgs[eStarAttr].bDefined)
{
//
//If input is "*", fetch all attributes
//
//Security Review:Both strings are null terminated.
if(wcscmp(pCommandArgs[eStarAttr].strValue,L"*") == 0 )
{
*ppszAttributes = NULL;
*pCount = 0;
return hr;
}
LPWSTR *ppszTemp = NULL;
UINT argc = 0;
ParseNullSeparatedString(pCommandArgs[eStarAttr].strValue,
&ppszTemp,
&argc);
LPWSTR *ppszAttr = (LPWSTR *)LocalAlloc(LPTR,argc*sizeof(LPCTSTR));
if(!ppszAttr)
{
hr = E_OUTOFMEMORY;
return hr;
}
for(UINT i = 0; i < argc; ++i)
{
if(FAILED(LocalCopyString(ppszAttr+i, ppszTemp[i])))
{
LocalFree(ppszAttr);
hr = E_OUTOFMEMORY;
return hr;
}
}
*ppszAttributes = ppszAttr;
*pCount = argc;
if(ppszTemp)
LocalFree(ppszTemp);
hr = S_OK;
return hr;
}
}
LPCWSTR pszAttr = NULL;
if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR)
{
//
//If eStarAttr is not defined, Fetch only DN
pcommon_info->outputFormat = DSQUERY_OUTPUT_DN;
if(_wcsicmp(pObjectEntry->pszCommandLineObjectType, g_pszPartition) == 0)
pszAttr = g_szAttrNCName;
else
pszAttr = g_szAttrDistinguishedName;
}
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTRONLY)
pszAttr = g_szAttrDistinguishedName;
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_DN)
{
if(_wcsicmp(pObjectEntry->pszCommandLineObjectType, g_pszPartition) == 0)
pszAttr = g_szAttrNCName;
else
pszAttr = g_szAttrDistinguishedName;
}
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_UPN)
pszAttr = g_szAttrUserPrincipalName;
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_SAMID)
pszAttr = g_szAttrSamAccountName;
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_RDN)
pszAttr = g_szAttrRDN;
//
// Always include the DN in the search results as well. It is quite useful.
//
size_t entries = 2;
//Security Review:Both strings are null terminated.
if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszServer) == 0)
{
//
// Add an addition space for the serverReference
++entries;
}
LPWSTR *ppszAttr = (LPWSTR *)LocalAlloc(LPTR,sizeof(LPWSTR) * entries);
if(!ppszAttr)
{
hr = E_OUTOFMEMORY;
return hr;
}
//Security Review:Correct buffer size is passed.
ZeroMemory(ppszAttr, sizeof(LPWSTR) * entries);
if(FAILED(LocalCopyString(ppszAttr,pszAttr)))
{
LocalFree(ppszAttr);
hr = E_OUTOFMEMORY;
return hr;
}
//
// Always include the DN in the search results as well. It is quite useful.
//
if (FAILED(LocalCopyString(&(ppszAttr[1]), g_szAttrDistinguishedName)))
{
LocalFree(ppszAttr);
hr = E_OUTOFMEMORY;
return hr;
}
//Security Review:Both strings are null terminated.
if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszServer) == 0)
{
ASSERT(entries >= 3);
if (FAILED(LocalCopyString(&(ppszAttr[2]), g_szAttrServerReference)))
{
LocalFree(ppszAttr);
hr = E_OUTOFMEMORY;
return hr;
}
}
*ppszAttributes = ppszAttr;
*pCount = static_cast<DWORD>(entries);
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: FreeAttributesToFetch
//
// Synopsis: Function to free memory allocated by GetAttributesToFetch
// Arguments: [dwszAttributes - in] : array of attributes to fetch
// [dwCount - in] : count of attributes in array
//
// Returns: HRESULT : S_OK if everything succeeded
// E_INVALIDARG if the object entry wasn't found
// Anything else is a failure code from an ADSI call
//
// History: 25-Sep-2000 hiteshr Created
//
//---------------------------------------------------------------------------
VOID FreeAttributesToFetch( IN LPWSTR *ppszAttributes,
IN DWORD dwCount)
{
while(dwCount)
{
LocalFree(ppszAttributes[--dwCount]);
}
LocalFree(ppszAttributes);
}
//+--------------------------------------------------------------------------
//
// Function: GetSearchRoot
//
// Synopsis: Builds the path to the root of the search as determined by
// the parameters passed in from the command line.
//
// Arguments: [pObjectEntry - IN] : pointer to the object table entry for the
// object type that will be modified
// [pCommandArgs IN] : the table of the command line input
// [refBasePathsInfo IN] : reference to the base paths info
// [refsbstrDN OUT] : reference to a CComBSTR that will
// receive the DN at which to start
// the search
// [pbSearchAtForestRoot] :Set to true is startnode is equal to
// forestroot
//
// Returns: HRESULT
//
// History: 24-April-2001 hiteshr Created
//
//---------------------------------------------------------------------------
HRESULT GetSearchRoot(IN IN PDSQueryObjectTableEntry pObjectEntry,
IN PARG_RECORD pCommandArgs,
IN CDSCmdBasePathsInfo& refBasePathsInfo,
OUT CComBSTR& refsbstrDN,
OUT BOOL *pbSearchAtForestRoot,
OUT BOOL *pbSearchAtGC)
{
if(!pCommandArgs ||
!pObjectEntry ||
!pbSearchAtForestRoot ||
!pbSearchAtGC)
{
return E_POINTER;
}
PWSTR pszInputDN = NULL;
// If GC switch isn't supported then set to false
// otherwise check to see if it has been passed
if(g_eGC == -1)
{
*pbSearchAtGC = FALSE;
}
else
{
if(pCommandArgs[g_eGC].bDefined &&
pCommandArgs[g_eGC].bValue)
{
DEBUG_OUTPUT(LEVEL5_LOGGING, L"Searching the GC");
*pbSearchAtGC = TRUE;
}
}
//
//Get the starting node
//
if(pCommandArgs[eCommStartNode].bDefined &&
pCommandArgs[eCommStartNode].strValue )
{
pszInputDN = pCommandArgs[eCommStartNode].strValue;
//Security Review:Both strings are null terminated.
if(_wcsicmp(pszInputDN,g_pszDomainRoot) == 0)
{
refsbstrDN = refBasePathsInfo.GetDefaultNamingContext();
}
//Security Review:Both strings are null terminated.
else if(_wcsicmp(pszInputDN,g_pszForestRoot) == 0)
{
*pbSearchAtForestRoot = TRUE;
}
else
{
//
//DN is entered
//
refsbstrDN = pszInputDN;
}
}
else
{
//Security Review:Both strings are null terminated.
if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszServer) == 0)
{
if (pCommandArgs[eServerDomain].bDefined &&
!pCommandArgs[eServerDomain].strValue)
{
PWSTR pszName = 0;
CComBSTR bstrDomainName = refBasePathsInfo.GetDefaultNamingContext();
HRESULT hr = CrackName(bstrDomainName,
&pszName,
GET_DNS_DOMAIN_NAME,
NULL);
if (FAILED(hr))
{
DEBUG_OUTPUT(LEVEL3_LOGGING,
L"Failed to crack the DN into a domain name: hr = 0x%x",
hr);
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
pCommandArgs[eServerDomain].strValue = pszName;
}
//
// Get the base path that corresponds with the scope
//
GetServerSearchRoot(pCommandArgs,
refBasePathsInfo,
refsbstrDN);
}
//Security Review:Both strings are null terminated.
else if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSite) == 0)
{
//
// Scope is the configuration container
//
refsbstrDN = refBasePathsInfo.GetConfigurationNamingContext();
}
//Security Review:Both strings are null terminated.
else if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSubnet) == 0)
{
//
// Get the base path that corresponds with the scope
//
GetSubnetSearchRoot(refBasePathsInfo,
refsbstrDN);
}
else if (_wcsicmp(pObjectEntry->pszCommandLineObjectType, g_pszPartition) == 0)
{
// For partitions, search from Configuration
refsbstrDN = L"CN=Partitions,";
refsbstrDN += refBasePathsInfo.GetConfigurationNamingContext();
}
else
{
//
//default is Domain DN
//
refsbstrDN = refBasePathsInfo.GetDefaultNamingContext();
}
}
return S_OK;
}
HRESULT GetSearchObject(IN IN PDSQueryObjectTableEntry pObjectEntry,
IN CDSCmdBasePathsInfo& refBasePathsInfo,
IN PARG_RECORD pCommandArgs,
IN CDSCmdCredentialObject& refCredentialObject,
IN CComBSTR& refsbstrDN,
IN BOOL bSearchAtForestRoot,
IN BOOL bSearchAtGC,
OUT CComPtr<IDirectorySearch>& refspSearchObject)
{
ENTER_FUNCTION_HR(MINIMAL_LOGGING, GetSearchObject, hr);
if(!pObjectEntry || !pCommandArgs)
return E_POINTER;
//
//Rules for determining where to search.
//if -s server is given always search at server
//if -gc is given search at gc.
//if startnode is forestroot, search at gc. so -gc is implicit here.
//if startnode is forestroot, and -s server is provided, server must
//be gc.
//if -s server and -gc are given, server must be gc. This check is
//done in DoQueryValidation
//
if(!bSearchAtForestRoot)
{
CComBSTR sbstrObjectPath;
bool bBindToServer = true;
//
//Search at GC
//
if(bSearchAtGC)
{
//
//Change the provider in sbstrObjectPath from LDAP to GC
//
CComPtr<IADsPathname> spPathNameObject;
//Security Review:CLSCTX_INPROC_SERVER is passed. This is fine.
hr = CoCreateInstance(CLSID_Pathname,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsPathname,
(LPVOID*)&spPathNameObject);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
//Set Provider to GC
hr = spPathNameObject->Set(CComBSTR(L"GC"), ADS_SETTYPE_PROVIDER);
ASSERT(SUCCEEDED(hr));
//Set the DN
hr = spPathNameObject->Set(refsbstrDN, ADS_SETTYPE_DN);
ASSERT(SUCCEEDED(hr));
//If server name present, search there. Server must be GC, check
//already done DoQueryValidation.
if(pCommandArgs[eCommServer].bDefined &&
pCommandArgs[eCommServer].strValue)
{
//Convert DN to adsi path with GC provider
hr = spPathNameObject->Set(CComBSTR(pCommandArgs[eCommServer].strValue), ADS_SETTYPE_SERVER);
//
//server name in path
//
bBindToServer = true;
}
else
{
//
//No server name in path
//
bBindToServer = false;
}
hr = spPathNameObject->Retrieve(bBindToServer ? ADS_FORMAT_X500 : ADS_FORMAT_X500_NO_SERVER,
&sbstrObjectPath);
ASSERT(SUCCEEDED(hr));
}
else
{
//
// Convert the DN to an ADSI path
//
refBasePathsInfo.ComposePathFromDN(refsbstrDN, sbstrObjectPath);
//Security Review:Both strings are null terminated.
if((_wcsicmp(pObjectEntry->pszObjectClass, g_pszUser) == 0 &&
pCommandArgs[eUserInactive].bDefined) ||
//Security Review:Both strings are null terminated.
(_wcsicmp(pObjectEntry->pszObjectClass, g_pszComputer) == 0 &&
pCommandArgs[eComputerInactive].bDefined))
{
INT nDomainBehaviorVersion = 0;
CComPtr<IADs> spDomain;
CComBSTR sbstrBasePath;
refBasePathsInfo.ComposePathFromDN(refBasePathsInfo.GetDefaultNamingContext(),
sbstrBasePath);
hr = DSCmdOpenObject(refCredentialObject,
sbstrBasePath,
IID_IADs,
(void**)&spDomain,
bBindToServer);
if (SUCCEEDED(hr))
{
CComVariant varVer;
hr = spDomain->GetInfo();
if(SUCCEEDED(hr))
{
CComBSTR bstrVer = L"msDS-Behavior-Version";
hr = spDomain->Get(bstrVer, &varVer);
if(SUCCEEDED(hr))
{
ASSERT(varVer.vt == VT_I4);
nDomainBehaviorVersion = static_cast<UINT>(varVer.lVal);
}
}
}
if(nDomainBehaviorVersion == 0)
{
DEBUG_OUTPUT(LEVEL3_LOGGING,
L"DomainBehaviorVersion is 0.");
hr = E_INVALIDARG;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr,
IDS_FILTER_LAST_LOGON_VERSION);
return hr;
}
}
}
hr = DSCmdOpenObject(refCredentialObject,
sbstrObjectPath,
IID_IDirectorySearch,
(void**)&refspSearchObject,
bBindToServer);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
}
else
{
CComBSTR bstrSearchRoot = L"GC://";
bool bBindToServer = false;
//If server name is provided search there
if(pCommandArgs[eCommServer].bDefined &&
pCommandArgs[eCommServer].strValue)
{
bstrSearchRoot += pCommandArgs[eCommServer].strValue;
bBindToServer = true;
}
else
{
//Get RootDse
CComPtr<IADs> spRootDSE = refBasePathsInfo.GetRootDSE();
//Get name of forest
VARIANT Default;
VariantInit(&Default);
hr = spRootDSE->Get (CComBSTR(L"rootDomainNamingContext"), &Default);
if(FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
ASSERT(Default.vt == VT_BSTR);
CComBSTR bstrForestDN = Default.bstrVal;
::VariantClear(&Default);
//Convert DN to dns path
CComBSTR bstrForestDNS;
if(!TranslateNameFromDnToDns(bstrForestDN,
bstrForestDNS))
{
hr = E_FAIL;
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
bBindToServer = false;
bstrSearchRoot += bstrForestDNS;
}
//BIND to GC to search entire forest
hr = DSCmdOpenObject(refCredentialObject,
bstrSearchRoot,
IID_IDirectorySearch,
(void**)&refspSearchObject,
bBindToServer);
if (FAILED(hr))
{
DisplayErrorMessage(g_pszDSCommandName,
NULL,
hr);
return hr;
}
}
return hr;
}
BOOL
TranslateNameFromDnToDns(const CComBSTR& bstrInputDN,
CComBSTR& bstrOutputDNS)
{
if(bstrInputDN.Length() == 0)
return FALSE;
bstrOutputDNS.Empty();
LPCWSTR pstrName = bstrInputDN;
PDS_NAME_RESULT pResult = NULL;
if( DS_NAME_NO_ERROR
== DsCrackNames(NULL,
DS_NAME_FLAG_SYNTACTICAL_ONLY,
DS_FQDN_1779_NAME,
DS_CANONICAL_NAME,
1,
(LPWSTR*)(&pstrName),
&pResult))
{
if(pResult &&
pResult->cItems == 1 &&
pResult->rItems[0].status == DS_NAME_NO_ERROR &&
pResult->rItems[0].pDomain)
{
bstrOutputDNS = pResult->rItems[0].pDomain;
}
if(pResult)
{
DsFreeNameResult(pResult);
}
}
return !!bstrOutputDNS.Length();
}
//+--------------------------------------------------------------------------
//
// Function: GetGCIndex
//
// Synopsis: Performs a lookup to determine which enum value is holding
// the GC (if any). This was necessary removing -gc from common
//
// Arguments: [pObjectEntry IN] : ObjectEntry from the parser
// [nCommandEnum OUT] : Enum value of the object, else -1
//
// Returns: HRESULT : S_OK if everything succeeded
// E_INVALIDARG
//
// Remarks:
//
//
// History: 10-Sep-2002 ronmart Created
//
//---------------------------------------------------------------------------
HRESULT GetGCIndex(PDSQueryObjectTableEntry pObjectEntry, int& nCommandEnum)
{
HRESULT hr = S_OK;
do // false loop
{
// Init nCommandEnum to an error value by default
nCommandEnum = -1;
if(NULL == pObjectEntry)
{
hr = E_INVALIDARG;
break;
}
// Get a pointer to the object class for readability
PCWSTR pszCommandLineObjectType = pObjectEntry->pszCommandLineObjectType;
// Now compare each object type against the specified
// object class to see what the enum index is
if(0 == lstrcmpi(pszCommandLineObjectType, g_pszStar))
{
nCommandEnum = eStarGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszOU))
{
nCommandEnum = eOUGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszUser))
{
nCommandEnum = eUserGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszContact))
{
nCommandEnum = eContactGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszComputer))
{
nCommandEnum = eComputerGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszGroup))
{
nCommandEnum = eGroupGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszServer))
{
nCommandEnum = eServerGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszSite))
{
nCommandEnum = eSiteGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType,g_pszSubnet))
{
nCommandEnum = eSubnetGC;
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszQuota))
{
nCommandEnum = -1; // -gc not supported
break;
}
else if(0 == lstrcmpi(pszCommandLineObjectType, g_pszPartition))
{
nCommandEnum = -1; // -gc not supported
break;
}
else
{
hr = E_FAIL;
// If you get here, then you've added a new object
// to cstrings.* without adding it to the
// if statement. This should only happen
// when testing a new object for the first time
// without a corresponding check above.
ASSERT(FALSE);
break;
}
} while(false);
return hr;
}