windows-nt/Source/XPSP1/NT/ds/security/gina/userenv/rsop/collect.cpp
2020-09-26 16:20:57 +08:00

673 lines
21 KiB
C++

//******************************************************************************
//
// Microsoft Confidential. Copyright (c) Microsoft Corporation 1999. All rights reserved
//
// File: Collect.cpp
//
// Description: Support for Namespace Garbage Collection
//
// History: 12-01-99 leonardm Created
//
//******************************************************************************
#include "uenv.h"
#include "collect.h"
#include "..\rsoputil\smartptr.h"
#include "..\rsoputil\rsoputil.h"
#include "..\rsoputil\wbemtime.h"
//******************************************************************************
//
// Function: GetMinutesElapsed
//
// Description: Returns the number of minutes elapsed between a time represented
// in a BSTR in WBEM format and the present time.
// It expects a string in WBEM datetime format: "yyyymmddhhmmss.000000+000"
// where yyyy=year, mm=month, dd=day, hh=hour, mm=minutes, ss=seconds
//
//
// Parameters: xbstrOldTime - Reference to XBStr representing the time from which
// calculate the time span.
//
// pMinutesElapsed - Pointer to a ULONG that receives the minutes elapsed
// between xbstrOldTime and the present time.
//
// Return: S_OK on success. An HRESULT error code on failure.
//
// History: 12/01/99 leonardm Created.
//
//******************************************************************************
HRESULT GetMinutesElapsed(XBStr& xbstrOldTime, ULONG* pMinutesElapsed)
{
//
// Convert the WbemTime value to a SYSTEMTIME value.
//
SYSTEMTIME systemTime_Old;
HRESULT hr = WbemTimeToSystemTime(xbstrOldTime, systemTime_Old);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GetMinutesElapsed: WbemTimeToSystemTime failed. hr=0x%08X."), hr));
return hr;
}
//
// Convert the SYSTEMTIME value to a FILETIME value.
//
BOOL bRes;
FILETIME fileTime_Old;
bRes = SystemTimeToFileTime(&systemTime_Old, &fileTime_Old);
if(!bRes)
{
DWORD dwLastError = GetLastError();
DebugMsg((DM_WARNING, TEXT("GetMinutesElapsed: SystemTimeToFileTime failed. LastError=0x%08X."), dwLastError));
return E_FAIL;
}
unsigned __int64 old = fileTime_Old.dwHighDateTime;
old <<= 32;
old |= fileTime_Old.dwLowDateTime;
//
// Get the current time in SYSTEMTIME format
//
SYSTEMTIME systemTime_Current;
GetSystemTime(&systemTime_Current);
//
// Convert the current time from a SYSTEMTIME to a FILETIME value
//
FILETIME fileTime_Current;
bRes = SystemTimeToFileTime(&systemTime_Current, &fileTime_Current);
if(!bRes)
{
DWORD dwLastError = GetLastError();
DebugMsg((DM_WARNING, TEXT("GetMinutesElapsed: SystemTimeToFileTime failed. LastError=0x%08X."), dwLastError));
return E_FAIL;
}
//
// The time passed in as a parameter must precede the current time
//
unsigned __int64 current = fileTime_Current.dwHighDateTime;
current <<= 32;
current |= fileTime_Current.dwLowDateTime;
if(old > current)
{
return WBEM_E_INVALID_PARAMETER;
}
//
// We have converted SYSTEMTIMEs to FILETIMEs.
// "The FILETIME structure is a 64-bit value representing the number
// of 100-nanosecond intervals since January 1, 1601."
// Therefore we need to divide by ten million to obtain seconds
// and by sixty to obtain minutes.
//
*pMinutesElapsed = (ULONG) (( current - old ) / (60 * 10 * 1000 * 1000));
return S_OK;
}
//******************************************************************************
//
// Function: IsNamespaceStale
//
// Description: Check if namespace is stale Sub-namespaces 'User' and 'Computer' are expected to have
// RSOP_Session. The data member 'creationTime' of that instance is examined when
// evaluating whether the sub-namespace should be deleted. For some failures treat the
// namespace as garbage-collectable because we don't clean up properly when an error is
// encountered during the creation of the namespace, and setting up security etc.
//
//
// Parameters: pChildNamespace - Pointer to IWbemServices associated with child namespace
// TTLMinutes - ULONG variable that represents the maximum number of
// minutes that may have elapsed before a sub-namespace is
// deleted.
//
// Return: True if namespace is stale, false otherwise
//
//******************************************************************************
BOOL IsNamespaceStale( IWbemServices *pChildNamespace, ULONG TTLMinutes )
{
//
// compute TTL
// To do so compare the "creationTime" data member of class RSOP_Session with
// the current time. If the time span exceeds the threshold (as found in the
// registry), the namespace is to be deleted.
//
XBStr xbstrInstancePath = L"RSOP_Session.id=\"Session1\"";
if(!xbstrInstancePath)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return FALSE;
}
XInterface<IWbemClassObject>xpInstance = NULL;
HRESULT hr = pChildNamespace->GetObject(xbstrInstancePath, 0, NULL, &xpInstance, NULL);
if(FAILED(hr))
{
DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: GetObject failed. hr=0x%08X"), hr));
return TRUE;
}
XBStr xbstrPropertyName = L"creationTime";
if(!xbstrPropertyName)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return TRUE;
}
VARIANT var;
VariantInit(&var);
XVariant xVar(&var);
hr = xpInstance->Get(xbstrPropertyName, 0, &var, NULL, NULL);
if(FAILED(hr))
{
DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: Get failed. hr=0x%08X."), hr));
return TRUE;
}
if ( var.vt == VT_NULL )
return TRUE;
XBStr xbstrPropertyValue = var.bstrVal;
if(!xbstrPropertyValue)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return FALSE;
}
ULONG minutesElapsed = 10;
hr = GetMinutesElapsed(xbstrPropertyValue, &minutesElapsed);
if(FAILED(hr))
{
DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: GetMinutesElapsed failed. hr=0x%08X."), hr));
return TRUE;
}
if(minutesElapsed > TTLMinutes)
return TRUE;
else
return FALSE;
}
//******************************************************************************
//
// Function: GarbageCollectNamespace
//
// Description: Garabage-collects the namespace passed in as a parameter.
// If no sub-namespaces are found or if all sub-namespaces
// are deleted, it deletes the parent namespace as well.
// It deletes sub-namespaces whose TTL has expired.
// It computes the TTL from the 'creationTime' data member of the only
// instance of class RSOP_Session as defined in rsop.mof.
//
// Any of the sub-namespaces that is older than TTLMinutes will be deleted.
// If no sub-namespaces are left, then the parent namespace is deleted as well.
//
// Garbage-collectable are those namespaces which satisfy a set of
// criteria which at the present time is based solely on the naming convention
// as follows: namespaces under root\rsop whose name starts with "NS"
//
// Sub-namespaces 'User' and 'Computer' are expected to have an instance of class
// RSOP_Session. The data member 'creationTime' of that instance is examined when
// evaluating whether the sub-namespace should be deleted.
//
//
// Parameters: bstrNamespace - Name of the namesapce to garbage collect.
// pWbemServices - Pointer to IWbemServices associated with
// the parent of bstrNamespace (root\rsop)
// TTLMinutes - ULONG variable that represents the maximum number of
// minutes that may have elapsed before a sub-namespace is
// deleted.
//
// Return: S_OK on success. An HRESULT error code on failure.
//
// History: 12/01/99 leonardm Created.
//
//******************************************************************************
HRESULT GarbageCollectNamespace(BSTR bstrNamespace, IWbemServices* pWbemServices, ULONG TTLMinutes)
{
if(!bstrNamespace || !pWbemServices)
{
return E_FAIL;
}
//
// Connect to that namespace and enumerate instances of __namespace.
// It's assumed that there will be at most 2 namespaces:
// "User" and "Computer".
//
XInterface<IWbemServices> xpParentNamespace;
HRESULT hr = pWbemServices->OpenNamespace( bstrNamespace,
0,
NULL,
&xpParentNamespace,
NULL);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: OpenNamespace failed. hr=0x%08X"), hr));
return hr;
}
//
// Enumerate all instances of __namespace.
//
XInterface<IEnumWbemClassObject> xpEnum;
XBStr xbstrClass = L"__namespace";
if(!xbstrClass)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory")));
return E_OUTOFMEMORY;
}
hr = xpParentNamespace->CreateInstanceEnum( xbstrClass,
WBEM_FLAG_SHALLOW | WBEM_FLAG_FORWARD_ONLY,
NULL,
&xpEnum);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: CreateInstanceEnum failed. hr=0x%08X" ), hr ));
return hr;
}
//
// We re interested in data member "Name" of class __namespace.
//
XBStr xbstrProperty = L"Name";
if(!xbstrProperty)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory")));
return E_FAIL;
}
//
// This pointer will be used to iterate through every instance
// in the enumeration.
//
XInterface<IWbemClassObject>xpInstance = NULL;
ULONG ulReturned = 0;
long namespacesFound = 0;
long namespacesDeleted = 0;
while(1)
{
//
// Retrieve the next instance in the enumeration.
//
hr = xpEnum->Next( WBEM_NO_WAIT, 1, &xpInstance, &ulReturned);
if (hr != WBEM_S_NO_ERROR || !ulReturned)
{
//
// Either the end of the enumeration has been reached or an error
// ocurred. We will find out outside the loop.
//
break;
}
namespacesFound++;
//
// Get the namespace name.
//
VARIANT var;
VariantInit(&var);
hr = xpInstance->Get(xbstrProperty, 0L, &var, NULL, NULL);
//
// Release the pointer to the current element of the enumeration..
//
xpInstance = NULL;
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Get failed. hr=0x%x" ), hr ));
return E_FAIL;
}
//
// Use the name of the namespace to connect to it.
//
XBStr xbstrChildNamespace = var.bstrVal;
VariantClear( &var );
if(!xbstrChildNamespace)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory" )));
return E_FAIL;
}
XInterface<IWbemServices> xpChildNamespace = NULL;
hr = xpParentNamespace->OpenNamespace( xbstrChildNamespace,
0,
NULL,
&xpChildNamespace,
NULL);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: OpenNamespace returned 0x%x"), hr));
return hr;
}
BOOL bStale = IsNamespaceStale( xpChildNamespace, TTLMinutes );
xpChildNamespace = NULL;
if ( bStale )
{
//
// DeleteInstance
//
CWString sNamespaceToDelete = L"__namespace.name=\"";
sNamespaceToDelete += (WCHAR*)xbstrChildNamespace;
sNamespaceToDelete += L"\"";
if(!sNamespaceToDelete.ValidString())
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
XBStr xbstrInstancePath = sNamespaceToDelete;
if(!xbstrInstancePath)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
hr = xpParentNamespace->DeleteInstance(xbstrInstancePath, 0, NULL, NULL);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: DeleteInstance returned 0x%x"), hr));
return hr;
}
DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: Deleted namespace:%ws\\%ws\n"),
(WCHAR*)bstrNamespace, (WCHAR*)xbstrChildNamespace ));
namespacesDeleted++;
}
}
//
// Check to find out whther the loop was exited because the end of the
// enumeration was reached or because an error ocurred.
//
if(hr != WBEM_S_FALSE || ulReturned)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Get failed. hr=0x%x" ), hr ));
return E_FAIL;
}
//
// If no namespaces are found or if namespaces in enumeration
// equal deleted namespaces delete the parent namespace as well.
//
if((!namespacesFound) || (namespacesDeleted == namespacesFound))
{
xpParentNamespace = NULL;
CWString sNamespaceToDelete = L"__namespace.name=\"";
sNamespaceToDelete += (WCHAR*)bstrNamespace;
sNamespaceToDelete += L"\"";
if(!sNamespaceToDelete.ValidString())
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
XBStr xbstrInstancePath = sNamespaceToDelete;
if(!xbstrInstancePath)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
hr = pWbemServices->DeleteInstance(xbstrInstancePath, 0, NULL, NULL);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: DeleteInstance returned 0x%x"), hr));
return hr;
}
DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: Deleted namespace %ws\n"),
(WCHAR*)bstrNamespace ));
}
return S_OK;
}
//******************************************************************************
//
// Function: IsGarbageCollectable
//
// Description: Determines whether a namespace is garbage-collectable.
// Garbage-collectable are those namespaces which satisfy a set of
// criteria which at the present time is based solely on the naming convention
// as follows: namespaces under root\rsop whose name starts with "NS"
//
//
// Parameters: bstrNamespace - BSTR that represents the namespace name.
//
// Return: 'true' or 'false'.
//
// History: 12/01/99 leonardm Created.
//
//******************************************************************************
bool IsGarbageCollectable(BSTR bstrNamespace)
{
if(bstrNamespace && wcslen(bstrNamespace) > 1 && _wcsnicmp(bstrNamespace, L"NS", 2) == 0)
{
return true;
}
return false;
}
//******************************************************************************
//
// Function: GarbageCollectNamespaces
//
// Description: Iterates through namespaces under root\rsop and for each of those
// that are determined to be garbage-collectable, it connects to
// sub-namespaces 'User' and 'Computer'.
//
// Any of the sub-namespaces that is older than TTLMinutes will be deleted.
// If no sub-namespaces are left, then the parent namespace is deleted as well.
//
// Garbage-collectable are those namespaces which satisfy a set of
// criteria which at the present time is based solely on the naming convention
// as follows: namespaces under root\rsop whose name starts with "NS"
//
// Sub-namespaces 'User' and 'Computer' are expected to have an instance of class
// RSOP_Session. The data member 'creationTime' of that instance is examined when
// evaluating whether the sub-namespace should be deleted.
//
//
// Parameters: TTLMinutes - The maximum number of minutes that may have
// elapsed since the creation of a sub-namespace
//
// Return:
//
// History: 12/01/99 leonardm Created.
//
//******************************************************************************
HRESULT GarbageCollectNamespaces(ULONG TTLMinutes)
{
XInterface<IWbemLocator> xpWbemLocator = NULL;
//
// Connect to namespace ROOT\RSOP
//
HRESULT hr = CoCreateInstance( CLSID_WbemLocator,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
(LPVOID*) &xpWbemLocator);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: CoCreateInstance returned 0x%x"), hr));
return hr;
}
XInterface<IWbemServices>xpWbemServices = NULL;
XBStr xbstrNamespace = L"root\\rsop";
if(!xbstrNamespace)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
hr = xpWbemLocator->ConnectServer(xbstrNamespace,
NULL,
NULL,
0L,
0L,
NULL,
NULL,
&xpWbemServices);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: ConnectServer failed. hr=0x%x" ), hr ));
return hr;
}
//
// Enumerate all instances of __namespace at the root\rsop level.
//
XInterface<IEnumWbemClassObject> xpEnum;
XBStr xbstrClass = L"__namespace";
if(!xbstrClass)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
hr = xpWbemServices->CreateInstanceEnum( xbstrClass,
WBEM_FLAG_SHALLOW | WBEM_FLAG_FORWARD_ONLY,
NULL,
&xpEnum);
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: CreateInstanceEnum failed. hr=0x%x" ), hr ));
return hr;
}
XBStr xbstrProperty = L"Name";
if(!xbstrProperty)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory")));
return E_FAIL;
}
XInterface<IWbemClassObject>xpInstance = NULL;
ULONG ulReturned = 1;
while(1)
{
hr = xpEnum->Next( WBEM_NO_WAIT, 1, &xpInstance, &ulReturned);
if (hr != WBEM_S_NO_ERROR || !ulReturned)
{
break;
}
VARIANT var;
VariantInit(&var);
hr = xpInstance->Get(xbstrProperty, 0L, &var, NULL, NULL);
xpInstance = NULL;
if(FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Get failed. hr=0x%x" ), hr ));
return E_FAIL;
}
XBStr xbstrNamespace = var.bstrVal;
VariantClear( &var );
if(!xbstrNamespace)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory.")));
return E_OUTOFMEMORY;
}
//
// For every instance of __namespace under ROOT\RSOP
// find out whether it is garbage-collectable.
//
if(IsGarbageCollectable(xbstrNamespace))
{
//
// If it is garbage-collectable, delete it if it
// was created more than 'TTLMinutes' minutes ago.
// In case of failure, continue with next namespace
// in the enumeration.
//
GarbageCollectNamespace(xbstrNamespace, xpWbemServices, TTLMinutes);
}
}
if(hr != WBEM_S_FALSE || ulReturned)
{
DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Get failed. hr=0x%x" ), hr ));
return E_FAIL;
}
return S_OK;
}