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.
 
 
 
 
 
 

801 lines
26 KiB

#include "DfsGeneric.hxx"
#include "dsgetdc.h"
#include "dsrole.h"
#include "DfsDomainInformation.hxx"
#include "DfsTrustedDomain.hxx"
#include "DfsReferralData.hxx"
#include "DomainControllerSupport.hxx"
#include "DfsReplica.hxx"
#include "dfsadsiapi.hxx"
#include "lmdfs.h"
#include "dfserror.hxx"
#include "dfsfilterapi.hxx"
#include "DomainControllerSupport.tmh"
#define RemoteServerNameString L"remoteServerName"
extern
DFS_SERVER_GLOBAL_DATA DfsServerGlobalData;
#define HRESULT_TO_DFSSTATUS(_x) (_x)
DFSSTATUS
DfsDcInit(
PBOOLEAN pIsDc )
{
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE hThread;
DWORD idThread;
Status = DsRoleGetPrimaryDomainInformation( NULL,
DsRolePrimaryDomainInfoBasic,
(PBYTE *)&pPrimaryDomainInfo);
if (Status == ERROR_SUCCESS)
{
#if defined (DC_TESTING)
pPrimaryDomainInfo->MachineRole = DsRole_RoleBackupDomainController;
#endif
if(pPrimaryDomainInfo->MachineRole == DsRole_RoleStandaloneServer)
{
DfsServerGlobalData.IsWorkGroup = TRUE;
}
else
{
DfsServerGlobalData.IsWorkGroup = FALSE;
}
if ( (pPrimaryDomainInfo->MachineRole == DsRole_RoleBackupDomainController) ||
(pPrimaryDomainInfo->MachineRole == DsRole_RolePrimaryDomainController) )
{
*pIsDc = TRUE;
hThread = CreateThread( NULL, //Security attributes
0, //Use default stack size
DcUpdateLoop, // Thread entry procedure
0, // Thread context parameter
0, // Start immediately
&idThread); // Thread ID
if (hThread == NULL)
{
//
// log this
//
Status = GetLastError();
}
else
{
CloseHandle(hThread);
}
}
DfsSetDomainNameFlat( pPrimaryDomainInfo->DomainNameFlat);
DfsSetDomainNameDns( pPrimaryDomainInfo->DomainNameDns);
DsRoleFreeMemory(pPrimaryDomainInfo);
}
return Status;
}
#define DC_PERIODIC_UPDATE_INTERVAL (1000 * 60 * 10) // 10 minutes
DWORD
DcUpdateLoop(
LPVOID lpThreadParams)
{
UNREFERENCED_PARAMETER(lpThreadParams);
DFSSTATUS Status;
DfsDomainInformation *pDomainInfo;
LONG InitialRetry = 10;
ULONG SleepTime = 1000 * 15; // 15 seconds.
static ULONG DomainRefreshFixedInterval;
static ULONG DomainRefreshIntervalOnError;
ULONG DomainRefreshTime;
// Default is 72 update intervals -> 12 hrs
DomainRefreshFixedInterval = (DfsServerGlobalData.DomainNameRefreshInterval / (DC_PERIODIC_UPDATE_INTERVAL / 1000));
// On enumeration errors, one hour (1/12th the time of domain-name-refresh-interval) is a better time to wait.
DomainRefreshIntervalOnError = DomainRefreshFixedInterval/12;
if (DomainRefreshFixedInterval == 0)
{
DomainRefreshFixedInterval = 1;
}
if (DomainRefreshIntervalOnError == 0)
{
DomainRefreshIntervalOnError = 1;
}
pDomainInfo = NULL;
Status = GetDomainInformation(&pDomainInfo);
//
// It's possible to get only a part of the domain information because of an error
// (typically because the DC discovery failed). In that case we have a Status != ERROR_SUCCESS here,
// but still have a valid DomainInfo.
//
if (pDomainInfo != NULL)
{
DfsSetGlobalDomainInfo(pDomainInfo);
pDomainInfo->ReleaseReference();
pDomainInfo = NULL;
}
while ( (Status != ERROR_SUCCESS) &&
(InitialRetry-- > 0) )
{
WaitForSingleObject(DfsServerGlobalData.ShutdownHandle, SleepTime);
if (DfsIsShuttingDown())
{
goto Exit;
}
Status = GetDomainInformation(&pDomainInfo);
if (pDomainInfo != NULL)
{
DfsSetGlobalDomainInfo(pDomainInfo);
pDomainInfo->ReleaseReference();
pDomainInfo = NULL;
}
DFS_TRACE_LOW(REFERRAL_SERVER, "startup Updating Domain info...%p, %x\n", pDomainInfo,Status);
}
SleepTime = DC_PERIODIC_UPDATE_INTERVAL;
DomainRefreshTime = DomainRefreshFixedInterval;
do {
WaitForSingleObject(DfsServerGlobalData.ShutdownHandle, SleepTime);
if (DfsIsShuttingDown())
{
break;
}
//
// every so often, throw away our entire domain information
// and rebuild.
// Every 10 minutes, purge our DC information.
//
//
if (--DomainRefreshTime == 0)
{
Status = GetDomainInformation(&pDomainInfo);
DFS_TRACE_LOW(REFERRAL_SERVER, "DcUpdateLoop: Updating Domain info...%p, %x\n", pDomainInfo,Status);
// It'll be 12 * 6 more update-intervals for the next enumeration if everythings working well.
// Otherwise it's 6.
if (Status == ERROR_SUCCESS)
{
DomainRefreshTime = DomainRefreshFixedInterval; // 72 update intervals = 12 hrs by default.
}
else
{
DomainRefreshTime = DomainRefreshIntervalOnError; // 6 update intervals = 1 hr
DFS_TRACE_HIGH(REFERRAL_SERVER, "DcUpdateLoop: Status 0x%x enumerating domains (info=%p), Resetting refresh time to %d mins\n",
Status, pDomainInfo, DomainRefreshTime * SleepTime / 60000);
}
if (pDomainInfo != NULL)
{
DfsSetGlobalDomainInfo(pDomainInfo);
pDomainInfo->ReleaseReference();
pDomainInfo = NULL;
}
}
else
{
Status = DfsAcquireDomainInfo(&pDomainInfo);
DFS_TRACE_LOW(REFERRAL_SERVER, "DcUpdateLoop: Purging DC info...%p, %x\n", pDomainInfo,Status);
if (Status == ERROR_SUCCESS)
{
pDomainInfo->PurgeDCReferrals();
pDomainInfo->ReleaseReference();
pDomainInfo = NULL;
}
}
} while ( TRUE );
Exit:
return 0;
}
DFSSTATUS
GetDomainInformation(
DfsDomainInformation **ppDomainInfo )
{
DFSSTATUS Status = ERROR_SUCCESS;
DFSSTATUS XforestStatus = ERROR_SUCCESS;
*ppDomainInfo = NULL;
DfsDomainInformation *pNewDomainInfo = new DfsDomainInformation( &Status, &XforestStatus );
if (pNewDomainInfo == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
else if (Status != ERROR_SUCCESS)
{
pNewDomainInfo->ReleaseReference();
}
if (Status == ERROR_SUCCESS)
{
*ppDomainInfo = pNewDomainInfo;
//
// Although we have a DomainInfo, our x-forest enumerations
// may have failed. We go ahead, but we still let the caller know
// that it needs to retry by sending the error status.
//
Status = XforestStatus;
}
return Status;
}
#if 0
//+-------------------------------------------------------------------------
//
// Function: DfsGenerateReferralDataFromRemoteServerNames
// IADs *pObject - the object
// DfsfolderReferralData *pReferralData - the referral data
//
//
// Returns: Status: Success or Error status code
//
// Description: This routine reads the remote server name
// attribute and creates a referral data structure
// so that we can pass a referral based on these names.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsGenerateReferralDataFromRemoteServerNames(
LPWSTR RootName,
DfsReferralData **ppReferralData )
{
HRESULT HResult = S_OK;
BOOLEAN CacheHit = FALSE;
DfsReplica *pReplicas = NULL;
DFSSTATUS Status = ERROR_SUCCESS;
DfsReferralData *pReferralData = NULL;
IADs *pObject = NULL;
VARIANT Variant;
LPWSTR pszAttrs[] = { RemoteServerNameString };
DWORD Number = sizeof(pszAttrs) / sizeof(LPWSTR);
DFS_TRACE_HIGH( REFERRAL_SERVER, "Entering DfsGenerateReferralDataFromRemoteServerNames for RootName %ws\n",
RootName);
Status = DfsGetDfsRootADObject(NULL,
RootName,
&pObject );
if (Status == ERROR_SUCCESS)
{
pReferralData = new DfsReferralData (&Status );
if (pReferralData == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
else if (Status != ERROR_SUCCESS)
{
pReferralData->ReleaseReference();
}
VariantInit( &Variant );
//
// Try to cache the RemoteServerNameString attribute locally,
// if this fails, we dont care since we will be using the
// GetEx call anyway.
//
if (Status == ERROR_SUCCESS)
{
HResult = ADsBuildVarArrayStr( pszAttrs, Number, &Variant);
if ( SUCCEEDED(HResult))
{
HResult = pObject->GetInfoEx( Variant, 0);
}
VariantClear( &Variant);
}
if (Status == ERROR_SUCCESS)
{
LONG StartNdx, LastNdx;
SAFEARRAY *PropertyArray;
HResult = pObject->GetEx( RemoteServerNameString, &Variant );
if ( SUCCEEDED(HResult) )
{
PropertyArray = V_ARRAY( &Variant );
HResult = SafeArrayGetLBound( PropertyArray, 1, &StartNdx );
if ( SUCCEEDED(HResult) )
{
HResult = SafeArrayGetUBound( PropertyArray, 1, &LastNdx );
}
}
else
{
DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsGenerateReferralDataFromRemoteServerNames-GetEx failed for RootName %ws with Status %x\n",
RootName, HResult);
}
if ( SUCCEEDED(HResult) &&
(LastNdx > StartNdx) )
{
VARIANT VariantItem;
pReplicas = new DfsReplica [ LastNdx - StartNdx ];
if (pReplicas != NULL)
{
for ( LONG Index = StartNdx; Index < LastNdx; Index++ )
{
VariantInit( &VariantItem );
CacheHit = FALSE;
HResult = SafeArrayGetElement( PropertyArray,
&Index,
&VariantItem );
if ( SUCCEEDED(HResult) )
{
UNICODE_STRING ServerName, Remaining, Replica;
LPWSTR ReplicaString = V_BSTR( &VariantItem );
Status = DfsRtlInitUnicodeStringEx( &Replica, ReplicaString );
if(Status == ERROR_SUCCESS)
{
DfsGetFirstComponent( &Replica,
&ServerName,
&Remaining );
Status = (&pReplicas[ Index - StartNdx])->SetTargetServer( &ServerName, &CacheHit );
if (Status == ERROR_SUCCESS)
{
Status = (&pReplicas[ Index - StartNdx])->SetTargetFolder( &Remaining );
}
}
}
else {
Status = DfsGetErrorFromHr( HResult );
DFS_TRACE_HIGH(REFERRAL_SERVER, "Leaving DfsGenerateReferralDataFromRemoteServerNames- SafeArrayGetElement for RootName %ws with Status %x\n",
RootName, HResult);
}
VariantClear( &VariantItem );
if (Status != ERROR_SUCCESS)
{
delete [] pReplicas;
pReplicas = NULL;
break;
}
}
}
else
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
else
{
DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsGenerateReferralDataFromRemoteServerNames- DfsGetDfsRootADObjectfailed for RootName %ws with Status %x\n",
RootName, HResult);
Status = DfsGetErrorFromHr( HResult );
}
VariantClear( &Variant );
if (Status == ERROR_SUCCESS)
{
pReferralData->Timeout = DFS_DEFAULT_REFERRAL_TIMEOUT;
pReferralData->ReplicaCount = LastNdx - StartNdx;
pReferralData->pReplicas = pReplicas;
*ppReferralData = pReferralData;
}
if (Status != ERROR_SUCCESS)
{
pReferralData->ReleaseReference();
}
}
pObject->Release();
}
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Leaving DfsGenerateReferralDataFromRemoteServerNames for RootName %ws and Status %x\n",
RootName, HResult);
return Status;
}
#endif
//+-------------------------------------------------------------------------
//
// Function: DfsUpdateRemoteServerName
//
// Arguments:
// IADs *pObject - the ds object of interest.
// LPWSTR ServerName - the server name to add or delete
// LPWSTR RemainingName - the rest of the name
// BOOLEAN Add - true for add, false for delete.
//
// Returns: Status: Success or Error status code
//
// Description: This routine updates the RemoteServerName attribute
// in the DS object, either adding a \\servername\remaining
// to the existing DS attribute or removing it, depending
// on add/delete.
// The caller must make sure this parameter does not
// already exist in the add case, or that the parameter
// to be deleted does exist in the delete case.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsUpdateRemoteServerName(
IADs *pObject,
LPWSTR ServerName,
LPWSTR RemainingName,
BOOLEAN Add )
{
HRESULT HResult = S_OK;
DFSSTATUS Status = ERROR_SUCCESS;
VARIANT Variant;
UNICODE_STRING UpdateName;
LPWSTR pServers[1];
BSTR RemoteServerNameBstr;
RemoteServerNameBstr = SysAllocString(RemoteServerNameString);
if (RemoteServerNameBstr == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
DFS_TRACE_HIGH( REFERRAL_SERVER, "Entering DfsUpdateRemoteServerName for ServerName %ws, remaining name %ws, add %d\n",
ServerName, RemainingName, Add);
//
// create a unc path using the server and remaining name
// to get a path of type \\servername\remainingname
//
Status = DfsCreateUnicodePathString( &UpdateName,
2, // unc path: 2 leading seperators,
ServerName,
RemainingName);
pServers[0] = UpdateName.Buffer;
if (Status == ERROR_SUCCESS)
{
//
// initialize the variant.
//
VariantInit( &Variant );
//
// Create the variant array with a single entry in it.
//
HResult = ADsBuildVarArrayStr( pServers,
1,
&Variant );
if ( SUCCEEDED(HResult) )
{
//
// either append or delete this string from the remote server
// name attribute
//
HResult = pObject->PutEx( (Add ? ADS_PROPERTY_APPEND : ADS_PROPERTY_DELETE),
RemoteServerNameBstr,
Variant );
if ( SUCCEEDED(HResult) )
{
//
// now update the object in the DS with this info.
//
HResult = pObject->SetInfo();
}
//
// clear the variant
//
VariantClear( &Variant );
}
if ( SUCCEEDED(HResult) == FALSE)
{
Status = DfsGetErrorFromHr( HResult );
}
//
// free the unicode string we created earlier on.
//
DfsFreeUnicodeString( &UpdateName );
}
SysFreeString(RemoteServerNameBstr);
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Leaving DfsUpdateRemoteServerName ServerName %ws, RemainingName %ws, Add %d, and Status %x\n",
ServerName, RemainingName, Add, HResult);
return Status;
}
DFSSTATUS
DfsDcEnumerateRoots(
LPWSTR DfsName,
LPBYTE pBuffer,
ULONG BufferSize,
PULONG pEntriesRead,
DWORD MaxEntriesToRead,
LPDWORD pResumeHandle,
PULONG pSizeRequired )
{
DFSSTATUS Status = ERROR_SUCCESS;
DFSSTATUS PDCStatus;
ULONG_PTR CurrentBuffer = (ULONG_PTR)pBuffer;
ULONG CurrentSize = BufferSize;
DfsString *pPDCName = NULL;
LPWSTR UseDC = NULL;
UNREFERENCED_PARAMETER(DfsName);
PDCStatus = DfsGetBlobPDCName( &pPDCName, 0 );
if (PDCStatus == ERROR_SUCCESS)
{
UseDC = pPDCName->GetString();
//
// At this point we dont care: if we got a dc name use it,
// otherwise, just keep going, we will go to the local dc.
//
Status = DfsEnumerateDfsADRoots( UseDC,
&CurrentBuffer,
&CurrentSize,
pEntriesRead,
MaxEntriesToRead,
pResumeHandle,
pSizeRequired );
DfsReleaseBlobPDCName(pPDCName);
}
return Status;
}
DFSSTATUS
DfsUpdateRootRemoteServerName(
LPWSTR Root,
LPWSTR DCName,
LPWSTR ServerName,
LPWSTR RemainingName,
BOOLEAN Add )
{
IADs *pRootObject = NULL;
DFSSTATUS Status = ERROR_SUCCESS;
Status = DfsGetDfsRootADObject( DCName,
Root,
&pRootObject );
if (Status == ERROR_SUCCESS)
{
Status = DfsUpdateRemoteServerName( pRootObject,
ServerName,
RemainingName,
Add );
pRootObject->Release();
}
return Status;
}
#define UNICODE_STRING_STRUCT(s) \
{sizeof(s) - sizeof(WCHAR), sizeof(s) - sizeof(WCHAR), (s)}
static UNICODE_STRING DfsSpecialDCShares[] = {
UNICODE_STRING_STRUCT(L"SYSVOL"),
UNICODE_STRING_STRUCT(L"NETLOGON"),
};
//+-------------------------------------------------------------------------
//
// Function: DfsIsRemoteServerNameEqual
//
//
// Returns: Status: Success or Error status code. pIsEqual = TRUE if
// the remoteServerName contains the passed in pServerName in its entirety.
//
// Description: This routine reads the remote server name
// attribute and does a string match on its components.
//--------------------------------------------------------------------------
DFSSTATUS
DfsIsRemoteServerNameEqual(
LPWSTR RootName,
PUNICODE_STRING pServerName,
PBOOLEAN pIsEqual)
{
HRESULT HResult = S_OK;
DFSSTATUS Status = ERROR_SUCCESS;
IADs *pObject = NULL;
VARIANT Variant;
LPWSTR pszAttrs[] = { RemoteServerNameString };
DWORD Number = sizeof(pszAttrs) / sizeof(LPWSTR);
DFS_TRACE_NORM( REFERRAL_SERVER, "Entering DfsIsRemoteServerNameEqual for RootName %ws, ServerName %wZ\n",
RootName, pServerName);
*pIsEqual = FALSE;
Status = DfsGetDfsRootADObject(NULL,
RootName,
&pObject );
if (Status == ERROR_SUCCESS)
{
VariantInit( &Variant );
HResult = ADsBuildVarArrayStr( pszAttrs, Number, &Variant);
if ( SUCCEEDED(HResult))
{
LONG StartNdx, LastNdx;
SAFEARRAY *PropertyArray;
BSTR RemoteServerNameBstr = SysAllocString(RemoteServerNameString);
if (RemoteServerNameBstr == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
if (Status == ERROR_SUCCESS)
{
HResult = pObject->GetEx( RemoteServerNameBstr, &Variant );
if ( SUCCEEDED(HResult) )
{
PropertyArray = V_ARRAY( &Variant );
HResult = SafeArrayGetLBound( PropertyArray, 1, &StartNdx );
if ( SUCCEEDED(HResult) )
{
HResult = SafeArrayGetUBound( PropertyArray, 1, &LastNdx );
}
}
else
{
DFS_TRACE_HIGH(REFERRAL_SERVER,
"DfsIsRemoteServerNameEqual -GetEx failed for RootName %ws with Status %x\n",
RootName, HResult);
}
if ( SUCCEEDED(HResult) &&
(LastNdx > StartNdx) )
{
VARIANT VariantItem;
for ( LONG Index = StartNdx; Index < LastNdx; Index++ )
{
VariantInit( &VariantItem );
HResult = SafeArrayGetElement( PropertyArray,
&Index,
&VariantItem );
if ( SUCCEEDED(HResult) )
{
UNICODE_STRING FirstComp, Replica;
LPWSTR ReplicaString = V_BSTR( &VariantItem );
Status = DfsRtlInitUnicodeStringEx( &Replica, ReplicaString );
if(Status == ERROR_SUCCESS)
{
Status = DfsGetFirstComponent( &Replica, &FirstComp, NULL );
if (Status == ERROR_SUCCESS)
{
//
// See if this component matches our servername in its entirety.
//
if (RtlEqualDomainName( pServerName, &FirstComp ))
{
*pIsEqual = TRUE;
}
}
}
}
else {
Status = DfsGetErrorFromHr( HResult );
DFS_TRACE_HIGH(REFERRAL_SERVER, "Leaving DfsGenerateReferralDataFromRemoteServerNames- SafeArrayGetElement for RootName %ws with Status %x\n",
RootName, HResult);
}
VariantClear( &VariantItem );
if (Status != ERROR_SUCCESS || *pIsEqual == TRUE)
{
break;
}
}
}
if (RemoteServerNameBstr != NULL)
{
SysFreeString( RemoteServerNameBstr );
}
}
VariantClear( &Variant );
}
pObject->Release();
}
else
{
DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsIsRemoteServerNameEqual- DfsGetDfsRootADObjectfailed for RootName %ws with Status %x\n",
RootName, HResult);
Status = DfsGetErrorFromHr( HResult );
}
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER,
"Leaving DfsIsRemoteServerNameEqual for RootName %ws, Server %wZ, Status %x, IsEqual? 0x%x\n",
RootName, pServerName, HResult, *pIsEqual);
return Status;
}
BOOLEAN
DfsIsSpecialDomainShare(
PUNICODE_STRING pShareName )
{
ULONG Index;
ULONG TotalShares;
BOOLEAN SpecialShare = FALSE;
TotalShares = sizeof(DfsSpecialDCShares) / sizeof(DfsSpecialDCShares[0]);
for (Index = 0; Index < TotalShares; Index++ )
{
if (DfsSpecialDCShares[Index].Length == pShareName->Length) {
if (_wcsnicmp(DfsSpecialDCShares[Index].Buffer,
pShareName->Buffer,
pShareName->Length/sizeof(WCHAR)) == 0)
{
SpecialShare = TRUE;
break;
}
}
}
return SpecialShare;
}