859 lines
30 KiB
C++
859 lines
30 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
#include <NapUtil.h>
|
|
#include <NapTypes.h>
|
|
#include <NapProtocol.h>
|
|
|
|
#include "napcommon.h"
|
|
#include "SampleShv.h"
|
|
#include "DebugHelper.h"
|
|
|
|
|
|
#include "SdkCommon.h"
|
|
#include <strsafe.h>
|
|
using namespace SDK_SAMPLE_COMMON;
|
|
|
|
extern LONG g_nComObjsInUse;
|
|
|
|
namespace SDK_SAMPLE_SHV
|
|
{
|
|
// {007a95d0-5a9b-4647-8adb-c8b91e64e925}
|
|
const CLSID CLSID_SampleShv =
|
|
{ 0x007a95d0, 0x5a9b, 0x4647, { 0x8a, 0xdb, 0xc8, 0xb9, 0x1e, 0x64, 0xe9, 0x25 } };
|
|
|
|
// Initialize static class member.
|
|
LONG CSampleShv::threadCount = 0;
|
|
|
|
// Constructor.
|
|
CSampleShv::CSampleShv() :
|
|
m_cRef(0)
|
|
{
|
|
InterlockedIncrement(&g_nComObjsInUse) ;
|
|
}
|
|
|
|
CSampleShv::~CSampleShv()
|
|
{
|
|
InterlockedDecrement(&g_nComObjsInUse);
|
|
};
|
|
|
|
//
|
|
// Implementation of INapSystemHealthValidator::Validate().
|
|
//
|
|
STDMETHODIMP CSampleShv::Validate(
|
|
/*[in]*/ __RPC__in_opt INapSystemHealthValidationRequest* pShvRequest,
|
|
/*[in]*/ UINT32 hintTimeOutInMsec,
|
|
/*[in]*/ __RPC__in_opt INapServerCallback* pCallback)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
asyncDataItems* requestData = NULL;
|
|
LONG t_count = 0;
|
|
|
|
// SDK Note:
|
|
// The method implementing IQuarSystemHealthValidator::Validate() should
|
|
// rarely abort early on error. In most cases, it's still responsible for
|
|
// generating an SoH Response (containing any errors in the Compliance
|
|
// Result Codes SoH attribute) and returning success; in rare cases, it
|
|
// can return a true error, in which case the client request will be
|
|
// dropped completely.
|
|
|
|
DebugPrintfW(L"\n\n----------------------------------------------------------------------------------\n");
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::Validate(IRequest*(0x%08x), Timeout(%d), ICallback*(0x%08x))",
|
|
pShvRequest, hintTimeOutInMsec, pCallback);
|
|
|
|
// Sanity check: a valid pShvRequest pointer should always be passed in.
|
|
// If this pointer is NULL, the SHV will be unable to get any information about the
|
|
// client from the SHV Host.)
|
|
//
|
|
if (! pShvRequest)
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::Validate(): The input Request object pointer is NULL! This object is required for the SHV to be able to perform any processing.");
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Sanity check: a valid pCallback pointer should always be passed in.
|
|
// If this pointer is NULL, the SHV will be unable to call OnComplete() function.
|
|
//
|
|
if (! pCallback)
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::Validate(): The input Callback object pointer is NULL! This object is required for the SHV to be able to perform any processing.");
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// SDK Note:
|
|
// If a SoH validation code determines that it needs to contact an
|
|
// external server to assist with validation, then it must start a
|
|
// separate helper thread to contact the server, and return E_PENDING in
|
|
// this thread. When that occurs, this thread must exit to the SHV Host,
|
|
// returning the E_PENDING result; the helper thread should independently
|
|
// determine the final validation result, generate the response SoH, and
|
|
// call the SHV Host's Callback interface.
|
|
//
|
|
// SDK Note:
|
|
// If a SHV does not require processing in a separate thread but can give back
|
|
// the response immediately, it should call the function QShvRespondSHVHost()
|
|
// just below this comment.
|
|
//
|
|
//hr = QShvRespondSHVHost();
|
|
//if (FAILED(hr))
|
|
//{
|
|
// DebugPrintfW(L" --- SdkShv:SampleShv:: Validate(): QShvRespondSHVHost returned error %#x!",hr);
|
|
// goto Cleanup;
|
|
//}
|
|
//else
|
|
//{
|
|
// DebugPrintfW(L" --- SdkShv:SampleShv:: Validate(): QShvRespondSHVHost returned success!");
|
|
// goto Cleanup;
|
|
//}
|
|
|
|
// SDK Note:
|
|
// Creating a separate helper thread is done whenever SHV requires need to contact the
|
|
// external server. Therefore, the following code of creating a thread should be done where
|
|
// and when the need arises.
|
|
//
|
|
// This SHV handles the Validate() call asynchronously.
|
|
// Validate() method return with hr = E_PENDING and does the processing of
|
|
// SoH request and generating SoHResponse in a separate thread.
|
|
//
|
|
// Here we assume that the SHV will always contact an external server. Therefore, create
|
|
// the thread at very start and do all the processing in the helper thread.
|
|
|
|
|
|
// NAP Server will quit sending Validate requests if it is shutting down, so the
|
|
// only thing we need to ensure is that we don't already have too many outstanding
|
|
// threads. Should do this before allocating extra resources
|
|
|
|
// increment the thread counter
|
|
t_count = InterlockedIncrement(&threadCount);
|
|
|
|
// if this increment puts us over the max, then decrement and exit
|
|
if ( t_count > SDK_SHV_MAX_THREADS )
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::Validate(): Too many threads already exist - dropping request");
|
|
InterlockedDecrement(&threadCount);
|
|
hr = HRESULT_FROM_WIN32(ERROR_TOO_MANY_THREADS);
|
|
goto Cleanup;
|
|
}
|
|
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::Validate(): Threadcount OK, preparing to start thread [%d]", t_count);
|
|
|
|
// Allocate space to store copies of the pointers; needed because local and member
|
|
// variables won't work in the multi-threaded case
|
|
//
|
|
requestData = static_cast<asyncDataItems*>(CoTaskMemAlloc(sizeof(asyncDataItems)));
|
|
if (!requestData)
|
|
{
|
|
// Failed to allocate memory
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::Validate(): Failed to allocate memory(error = 0x%08x)",hr);
|
|
hr = E_OUTOFMEMORY;
|
|
// Decrement the thread counter to finish releasing the resources that were used
|
|
InterlockedDecrement(&threadCount);
|
|
goto Cleanup;
|
|
}
|
|
ZeroMemory(requestData, sizeof(asyncDataItems));
|
|
|
|
|
|
// AddRef the request and callback pointers prior to the attempt to create the thread
|
|
pShvRequest->AddRef();
|
|
pCallback->AddRef();
|
|
|
|
// Store copies of the callback and request pointers to pass into the thread
|
|
requestData->pthis = this;
|
|
requestData->piShvRequest = pShvRequest;
|
|
requestData->piCallback = pCallback;
|
|
|
|
// Attempt to create a Thread to process the SoHRequest and reply with SoHResponse.
|
|
hr = QShvCreateThread( requestData );
|
|
if ( FAILED(hr) )
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::Validate(): QShvCreateThread() returned error %#x! ",hr);
|
|
// Release the references to the request and callback pointers,
|
|
// as well as our copies, since the thread failed to create
|
|
pShvRequest->Release();
|
|
pCallback->Release();
|
|
CoTaskMemFree(requestData);
|
|
// Decrement the thread counter to finish releasing the resources that were used
|
|
InterlockedDecrement(&threadCount);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Thread creation successful
|
|
DebugPrintfW(L" --- SdkShv::Validate(): QShvCreateThread() returned S_OK: thread successfully started. Returning E_PENDING to QSHVhost");
|
|
hr = E_PENDING;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Creates thread for asynchronous handling of Validate() call.
|
|
HRESULT CSampleShv::QShvCreateThread( /*in*/ _In_ asyncDataItems* requestData )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HANDLE threadHandle = NULL;
|
|
DWORD threadId = 0;
|
|
BOOL retCode = 0;
|
|
|
|
if (!requestData)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Windows API to create a thread.
|
|
threadHandle = (HANDLE) CreateThread(
|
|
NULL,
|
|
0,
|
|
AsyncThreadHandler,
|
|
static_cast<LPVOID>(requestData),
|
|
0,
|
|
&threadId);
|
|
if(threadHandle == NULL)
|
|
{
|
|
DebugPrintfW(L" --- SdkShv:SampleShv::QShvCreateThread(): CreateThread() returned error");
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CoTaskMemFree(requestData);
|
|
|
|
// cleanup of requestData and releasing of object references handled by caller
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Close the thread handle now, so that when the thread returns,
|
|
// it will be ended and unloaded
|
|
retCode = CloseHandle(threadHandle);
|
|
if(!retCode)
|
|
{
|
|
DebugPrintfW(L" --- SdkShv:SampleShv:: QShvCreateThread(): CloseHandle failed -LEAKED HANDLE (thread) !");
|
|
}
|
|
|
|
// Thread created successfully
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
// This is the thread handler. The input parameter is the pointer to an asynDataItems struct.
|
|
DWORD __stdcall CSampleShv::AsyncThreadHandler(_In_ LPVOID lpParameter)
|
|
{
|
|
DWORD errCode = ERROR_SUCCESS;
|
|
asyncDataItems * requestData = NULL;
|
|
long t_count = 0;
|
|
|
|
requestData = reinterpret_cast<asyncDataItems*>(lpParameter);
|
|
if ( (!requestData) ||
|
|
(!requestData->pthis) ||
|
|
(!requestData->piShvRequest) ||
|
|
(!requestData->piCallback) )
|
|
{
|
|
//can't use the passed values
|
|
DebugPrintfW(L" --- SdkShv:SampleShv::AsyncThreadHandler(): Invalid data passed in lpParameter");
|
|
// can't Release the objects, but we can decrement the thread counter
|
|
InterlockedDecrement(&threadCount);
|
|
goto Cleanup;
|
|
}
|
|
|
|
errCode = requestData->pthis->AsyncThreadHandlerMain(requestData);
|
|
if(errCode != ERROR_SUCCESS)
|
|
{
|
|
DebugPrintfW(L" --- SdkShv:SampleShv::AsyncThreadHandler(): AsyncThreadHandlerMain() returned error %d!",errCode);
|
|
}
|
|
|
|
// Release the request and callback object references
|
|
// regardless of the result, we are done with them
|
|
requestData->piShvRequest->Release();
|
|
requestData->piShvRequest = NULL;
|
|
requestData->piCallback->Release();
|
|
requestData->piCallback = NULL;
|
|
|
|
// also decrement the thread counter, since this thread is finishing
|
|
t_count = InterlockedDecrement(&threadCount);
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::AsyncThreadHandler() Thread finishing. After decrement, threadCount = [%d]", t_count);
|
|
|
|
Cleanup:
|
|
// Release the request and callback object references in case one of them was missing
|
|
// Release the requestData in both successful and erroneous cases
|
|
if (requestData != NULL)
|
|
{
|
|
if (requestData->piShvRequest != NULL)
|
|
{
|
|
requestData->piShvRequest->Release();
|
|
}
|
|
if (requestData->piCallback != NULL)
|
|
{
|
|
requestData->piCallback->Release();
|
|
}
|
|
|
|
CoTaskMemFree(requestData);
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
|
|
|
|
// The thread handler calls this method that has access to member variables.
|
|
DWORD CSampleShv::AsyncThreadHandlerMain(_In_ asyncDataItems *requestData)
|
|
{
|
|
DWORD errCode = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = QShvRespondSHVHost(requestData);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SdkShv:SampleShv:: AsyncThreadHandlerMain(): QShvRespondSHVHost returned error %#x!",hr);
|
|
}
|
|
else
|
|
{
|
|
DebugPrintfW(L" --- SdkShv:SampleShv:: AsyncThreadHandlerMain(): QShvRespondSHVHost returned success!");
|
|
}
|
|
|
|
errCode = HRESULT_CODE(hr);
|
|
return errCode;
|
|
}
|
|
|
|
|
|
|
|
// Handles processing of incoming SoH Request and generating SoH Response.
|
|
HRESULT CSampleShv::QShvRespondSHVHost(_In_ asyncDataItems *requestData)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRESULT validationResult = QUAR_E_NOTPATCHED;
|
|
|
|
// requestData is checked before it is passed in by QShvCreateThread()
|
|
|
|
hr = HandleRequestSoH(requestData->piShvRequest, validationResult);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::QShvRespondSHVHost(): HandleRequestSoH() returned error %#x!",hr);
|
|
// an error has occurred while reading and validating the SoH data
|
|
// calling OnComplete to notify NAP Server of our inability to evaluate health
|
|
// NAP Server will then map this failure code into one of the FailureCategory types
|
|
hr = requestData->piCallback->OnComplete(requestData->piShvRequest, hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Generate an outbound SoH response, to be sent to the client.
|
|
// In this example, the hr is used as the value passed.
|
|
//
|
|
hr = HandleResponseSoH(validationResult, requestData->piShvRequest);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::QShvRespondSHVHost(): HandleResponseSoH() returned error %#x! Calling OnComplete() to notify QSHVHost.",hr);
|
|
// Failed to set response. Calling OnComplete() to notify QSHVHost of inability to validate.
|
|
hr = requestData->piCallback->OnComplete(requestData->piShvRequest, hr);
|
|
}
|
|
else
|
|
{
|
|
DebugPrintfW(L" --- SdkShv::SampleShv::QShvRespondSHVHost(): HandleResponseSoH() returned success. Calling OnComplete() to notify QSHVHost.");
|
|
// SoH Response is now set. Calling OnComplete() notifies setting of SoHResponse.
|
|
hr = requestData->piCallback->OnComplete(requestData->piShvRequest, hr);
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" ---SdkShv::SampleShv::QShvRespondSHVHost(): OnComplete() returned error %#x!",hr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Perform all handling of inbound SoH requests from clients.
|
|
// Possible return values:
|
|
// NAP_E_MISSING_SOH
|
|
// NAP_E_INVALID_PACKET
|
|
// QUAR_E_NOTPATCHED
|
|
// Other COM errors
|
|
//
|
|
HRESULT CSampleShv::HandleRequestSoH(
|
|
_In_ INapSystemHealthValidationRequest* pShvRequest,
|
|
_Out_ HRESULT &complianceResult)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SoH *pSohRequest = NULL;
|
|
INapSoHProcessor *pSohProcessor = NULL;
|
|
SystemHealthEntityId systemHealthId = 0;
|
|
BOOL napAgentGenerated = FALSE;
|
|
|
|
complianceResult = QUAR_E_NOTPATCHED;
|
|
|
|
//
|
|
// Get the client's SoH request data.
|
|
//
|
|
|
|
// Obtain the client's SoH data from the SHV Host.
|
|
hr = pShvRequest->GetSoHRequest(&pSohRequest, &napAgentGenerated);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleRequestSoH(): pShvRequest->GetSoHRequest() returned error %#x!", hr);
|
|
pSohRequest = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// SDK Note:
|
|
// If the napAgentGenerated value is TRUE, it indicates that the SoH was generated
|
|
// by the NAPAgent on behalf of the SHA, and usually indicates an SHA failure.
|
|
// In this case, the SHV should expect an SoH packet with the following 3 TLVs :
|
|
// napAttributeTypeSystemHealthId,
|
|
// napAttributeTypeFailureCategory,
|
|
// napAttributeTypeErrorCodes
|
|
//
|
|
|
|
//
|
|
// In this sample we assume that napAgentGenerated is always FALSE to keep the code simple.
|
|
//
|
|
if (! pSohRequest)
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleRequestSoH(): The client did not send an SoH for this SHV.");
|
|
hr = NAP_E_MISSING_SOH;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DebugPrintfW(L" --- SampleShv::HandleRequestSoH(): Received SoH request for this SHV.");
|
|
|
|
// Wrap it inside an INapSoHProcessor object.
|
|
hr = CreateInputSoHProcessor(pSohProcessor, systemHealthId,
|
|
SOH_REQUEST, pSohRequest);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleRequestSoH(): Couldn't create an SoH Parser object! (error %#x)", hr);
|
|
pSohProcessor = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We support multiple config. Get the Config ID.
|
|
//
|
|
UINT32 uConfigID = 0 ;
|
|
hr = GetConfigIDFromQuery(pShvRequest, &uConfigID);
|
|
if (FAILED(hr))
|
|
{
|
|
uConfigID = 0;
|
|
}
|
|
|
|
//
|
|
// Validate whether the client's SoH request data is considered "healthy".
|
|
//
|
|
hr = CheckRequestSoHHealth(uConfigID, pSohProcessor, complianceResult);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleRequestSoH(): ValidateRequestSoH() returned error %#x!", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
// Release the SoH handler object.
|
|
ReleaseObject(pSohProcessor);
|
|
// Free the temporary SoH struct.
|
|
FreeSoH(pSohRequest);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Get the configuration ID.
|
|
HRESULT CSampleShv::GetConfigIDFromQuery(
|
|
_In_ INapSystemHealthValidationRequest* request,
|
|
_Out_ UINT32* configID)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
UINT32 id;
|
|
INapSystemHealthValidationRequest2 *r2 = NULL;
|
|
|
|
if (!request || !configID)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*configID = 0; //defaults to 0.
|
|
|
|
hr = request->QueryInterface(
|
|
__uuidof(INapSystemHealthValidationRequest2),
|
|
reinterpret_cast<LPVOID*>(&r2));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (E_NOINTERFACE == hr)
|
|
{
|
|
// Interface not supported, it might be Longhorn, so there's only
|
|
// config 0
|
|
hr = S_OK;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = r2->GetConfigID(&id);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*configID = id;
|
|
}
|
|
|
|
Cleanup:
|
|
if (r2)
|
|
{
|
|
r2->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Validate whether the client's SoH data is considered healthy.
|
|
HRESULT CSampleShv::CheckRequestSoHHealth(
|
|
_In_ UINT32 uConfigID,
|
|
_In_ INapSoHProcessor *pSohRequest,
|
|
_Out_ HRESULT &complianceResult)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// SDK Note:
|
|
// Each vendor should update this method & its helper functions,
|
|
// based upon their business logic.
|
|
//
|
|
|
|
UINT16 index = 0;
|
|
|
|
SoHAttributeType attrType = (SoHAttributeType) 0;
|
|
SoHAttributeValue *pAttrValue = NULL;
|
|
|
|
|
|
// Secure by default -- assume the client is unhealthy, until it
|
|
// proves otherwise.
|
|
complianceResult = QUAR_E_NOTPATCHED;
|
|
|
|
// Sanity check -- if no SoH data was passed in, leave client data unset.
|
|
if (! pSohRequest)
|
|
{
|
|
//
|
|
// SDK Note:
|
|
// - If this SHV is acting as an intrusion detection system, it
|
|
// should use its specific business logic to determine whether
|
|
// the client is trusted or malicious, and should create an
|
|
// SoH Response containing the appropriate error code to
|
|
// convey the result to the SHV Host.
|
|
// - All other SHVs should return NAP_E_MISSING_SOH.
|
|
|
|
DebugPrintfW(L" --- SampleShv::CheckRequestSoHHealth(): No SoH to evaluate.");
|
|
hr = NAP_E_MISSING_SOH;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// Check for the existence & values for each attribute expected from
|
|
// the client.
|
|
|
|
//
|
|
// SDK Note:
|
|
// This SHV is very simple, and only handles 1 single attribute value within
|
|
// the SoH passed. Most SHVs will need to handle multiple attributes, and will
|
|
// need to correctly handle them in whatever order they appear within the SoH.
|
|
//
|
|
|
|
// In this sample, we expect only 1 attribute passed back from SHA, so we are searching
|
|
// for first instance of sohAttributeTypeVendorSpecific
|
|
hr = pSohRequest->FindNextAttribute(0, sohAttributeTypeVendorSpecific, &index);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::Validate(): Error: hit error %#x while searching for the client's Vendor-Specific Data attribute!", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get the attribute located at the returned index
|
|
hr = pSohRequest->GetAttribute(index, &attrType, &pAttrValue);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::Validate(): Hit error %#x while reading attribute #%d!", hr, index);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Sanity check -- ignore attributes whose value pointer is NULL.
|
|
if (!pAttrValue)
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::Validate(): Hit error %#x while reading attribute #%d!", hr, index);
|
|
hr = NAP_E_INVALID_PACKET;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Check the configuration to see if it is enabled.
|
|
if (!IsConfigurationTurnedOn(uConfigID))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::Validate(): The configuration is off, evaluating to Compliant");
|
|
complianceResult = QUAR_E_COMPLIANT;
|
|
}
|
|
|
|
// Verify the attribute's value.
|
|
else if ( (pAttrValue->vendorSpecificVal.size == SDK_CLIENT_VENDOR_DATA_SIZE) &&
|
|
(pAttrValue->vendorSpecificVal.vendorId == QuarSampleSystemHealthId) &&
|
|
(memcmp(pAttrValue->vendorSpecificVal.vendorSpecificData,
|
|
SDK_CLIENT_VENDOR_DATA_HEALTHY,
|
|
pAttrValue->vendorSpecificVal.size) == 0) )
|
|
{
|
|
// This client's data is the 'healthy' value.
|
|
DebugPrintfW(L" --- SampleShv::Validate(): This client's SoH data has evaluated to Compliant");
|
|
complianceResult = QUAR_E_COMPLIANT;
|
|
}
|
|
else if ( (pAttrValue->vendorSpecificVal.size == SDK_CLIENT_VENDOR_DATA_SIZE) &&
|
|
(pAttrValue->vendorSpecificVal.vendorId == QuarSampleSystemHealthId) &&
|
|
(memcmp(pAttrValue->vendorSpecificVal.vendorSpecificData,
|
|
SDK_CLIENT_VENDOR_DATA_UNHEALTHY,
|
|
pAttrValue->vendorSpecificVal.size) == 0) )
|
|
{
|
|
// This client's data is the 'unhealthy' value.
|
|
DebugPrintfW(L" --- SampleShv::Validate(): This client's SoH data has evaluated to NOT Compliant");
|
|
complianceResult = QUAR_E_NOTPATCHED;
|
|
}
|
|
else
|
|
{
|
|
// data did not match size, ID and/or healthy/unhealthy values
|
|
hr = NAP_E_INVALID_PACKET;
|
|
DebugPrintfW(L" --- SampleShv::Validate(): This client's SoH data was not a recognized value");
|
|
}
|
|
|
|
// Free the returned attribute object before leaving.
|
|
FreeSoHAttributeValue(attrType, pAttrValue);
|
|
|
|
|
|
Cleanup:
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::Validate(): This client's SoH data successfully evaluated.");
|
|
}
|
|
else
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::Validate(): This client's SoH data not successfully validated. (code = 0x%08x", hr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL CSampleShv::IsConfigurationTurnedOn(
|
|
_In_ UINT32 uConfigID)
|
|
{
|
|
BOOL bIsConfigurationTurnedOn = TRUE;
|
|
|
|
INapComponentConfig3* pServer = NULL;
|
|
HRESULT hr = CoCreateInstance(
|
|
CLSID_SDK_SHV_UI,
|
|
NULL, // pUnkOuter
|
|
CLSCTX_LOCAL_SERVER,
|
|
IID_INapComponentConfig3,
|
|
reinterpret_cast<void**>(&pServer));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
UINT16 bCount = 0;
|
|
BYTE* pOutdata = NULL;
|
|
hr = pServer->GetConfigFromID(uConfigID, &bCount, &pOutdata);
|
|
if (SUCCEEDED(hr) && (pOutdata == NULL))
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ( (pOutdata != NULL) && (*pOutdata == 0))
|
|
{
|
|
bIsConfigurationTurnedOn = FALSE;
|
|
}
|
|
|
|
CoTaskMemFree(pOutdata);
|
|
}
|
|
pServer->Release();
|
|
}
|
|
|
|
return bIsConfigurationTurnedOn;
|
|
}
|
|
|
|
|
|
// Perform all handling of outbound SoH responses sent back to clients.
|
|
HRESULT CSampleShv::HandleResponseSoH(
|
|
_In_ HRESULT validationResult,
|
|
_In_ INapSystemHealthValidationRequest* pShvRequest)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
INapSoHConstructor *pSohConstructor = NULL;
|
|
SoH *pSohResponse = NULL;
|
|
|
|
//
|
|
// Create an empty SoH response.
|
|
//
|
|
|
|
hr = CreateOutputSoHConstructor(pSohConstructor,
|
|
QuarSampleSystemHealthId, SOH_RESPONSE);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleResponseSoH(): CreateResponseSoH() returned error %#x!",
|
|
hr);
|
|
pSohConstructor = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Populate the SoH response based on the client's health state.
|
|
//
|
|
|
|
hr = FillResponseSoH(validationResult, pSohConstructor);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleResponseSoH(): FillResponseSoH() returned error %#x!",
|
|
hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Save the SoH response into the SHV Host.
|
|
//
|
|
|
|
// Get portable SoH data.
|
|
hr = pSohConstructor->GetSoH(&pSohResponse);
|
|
|
|
if (SUCCEEDED(hr) && !pSohResponse)
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleResponseSoH(): Couldn't get the final Response SoH data! (error %#x, pointer = 0x%08x)",
|
|
hr, pSohResponse);
|
|
pSohResponse = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Pass it along to the SHV Host.
|
|
hr = pShvRequest->SetSoHResponse(pSohResponse);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::HandleResponseSoH(): SetSoHResponse() returned error %#x!",
|
|
hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
// Release the SoH handler object.
|
|
ReleaseObject(pSohConstructor);
|
|
|
|
// Free the temporary SoH struct.
|
|
FreeSoH(pSohResponse);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Fill the specified response SoH object, given the current client's health
|
|
// state.
|
|
HRESULT CSampleShv::FillResponseSoH(
|
|
_In_ HRESULT validationResult,
|
|
_In_ INapSoHConstructor* pSohResponse)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SoHAttributeValue value = {0};
|
|
|
|
|
|
//
|
|
// Append the mandatory attribute: Compliance Result Codes.
|
|
// If SHV was not able to contact the external server (Network Failure, Server Down...)
|
|
// SHV should return Failure Category attribute instead of Compliance Result.
|
|
// (with FailureCategory = failureCategoryServerCommunication)
|
|
//
|
|
// Here we assume there is no such failure.
|
|
|
|
value.codesVal.count = 1;
|
|
value.codesVal.results = &validationResult;
|
|
|
|
hr = pSohResponse->AppendAttribute(sohAttributeTypeComplianceResultCodes,
|
|
&value);
|
|
if (FAILED(hr))
|
|
{
|
|
DebugPrintfW(L" --- SampleShv::FillResponseSoH(): SoHResponse->AppendAttribute() returned error %#x!", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
ZeroMemory(&value, sizeof(value));
|
|
|
|
|
|
//
|
|
// Append any optional attributes.
|
|
//
|
|
|
|
//
|
|
// SDK Note:
|
|
// Append any other attributes that are appropriate for the given
|
|
// business logic. (I.e., Vendor-Specific-Data, IPv4-Fixup-Servers,
|
|
// Health-Class, Time-of-Last-Update, Software-Version, etc.)
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL CSampleShv::ThreadCountIsZeroed()
|
|
{
|
|
// if threadCount > 0, return false
|
|
// if threadCount <= 0, returns true
|
|
// side-effect - if threadCount == 0, then exchange it with 0
|
|
return ( 0 <= InterlockedCompareExchange( &threadCount, 0, 0) );
|
|
|
|
}
|
|
|
|
// Implementation of IUnknown
|
|
|
|
STDMETHODIMP CSampleShv::QueryInterface(
|
|
__RPC__in const IID& iid,
|
|
__RPC__out void** ppv)
|
|
{
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = static_cast<IUnknown*>(this);
|
|
}
|
|
else if (iid == IID_INapSystemHealthValidator)
|
|
{
|
|
*ppv = static_cast<INapSystemHealthValidator*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG CSampleShv::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
ULONG CSampleShv::Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
if (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
} // End "namespace SDK_SAMPLE_SHV".
|
|
|