|
|
#include <precomp.h>
#include "wzcsvc.h"
#include "tracing.h"
#include "utils.h"
#include "intflist.h"
#include "rpcsrv.h"
#include "database.h"
extern HASH sessionHash;
//-------------------------------------------------
// Globals used for the RPC interface
BOOL g_bRpcStarted = FALSE; PSECURITY_DESCRIPTOR g_pSecurityDescr = NULL; GENERIC_MAPPING g_Mapping = { WZC_READ, WZC_WRITE, WZC_EXECUTE, WZC_ALL_ACCESS};
//-------------------------------------------------
// Initialize the security settings for the RPC API
DWORD WZCSvcInitRPCSecurity() { DWORD dwErr = ERROR_SUCCESS; NTSTATUS ntStatus; ACE_DATA AceData[6] = { {ACCESS_ALLOWED_ACE_TYPE, 0, 0, WZC_ACCESS_SET|WZC_ACCESS_QUERY, &LocalSystemSid}, {ACCESS_ALLOWED_ACE_TYPE, 0, 0, WZC_ACCESS_SET|WZC_ACCESS_QUERY, &AliasAdminsSid}, {ACCESS_ALLOWED_ACE_TYPE, 0, 0, WZC_ACCESS_SET|WZC_ACCESS_QUERY, &AliasAccountOpsSid}, {ACCESS_ALLOWED_ACE_TYPE, 0, 0, WZC_ACCESS_SET|WZC_ACCESS_QUERY, &AliasSystemOpsSid}, {ACCESS_ALLOWED_ACE_TYPE, 0, 0, WZC_ACCESS_SET|WZC_ACCESS_QUERY, &AliasUsersSid}, // for now (WinXP Client RTM) the decision was made to let everybody party, but based on
// the acl below. Later, the security schema won't change by default, but support will be
// added allowing admins to tighten up the access to the service RPC APIs.
{ACCESS_ALLOWED_ACE_TYPE, 0, 0, WZC_ACCESS_SET|WZC_ACCESS_QUERY, &WorldSid}};
DbgPrint((TRC_TRACK, "[WZCSvcInitRPCSecurity"));
// create the well known SIDs;
dwErr = RtlNtStatusToDosError( NetpCreateWellKnownSids(NULL) ); DbgAssert((dwErr == ERROR_SUCCESS, "Error %d creating the well known Sids!", dwErr));
// create the security object.
if (dwErr == ERROR_SUCCESS) { dwErr = RtlNtStatusToDosError( NetpCreateSecurityObject( AceData, sizeof(AceData)/sizeof(ACE_DATA), NULL, NULL, &g_Mapping, &g_pSecurityDescr) ); DbgAssert((dwErr == ERROR_SUCCESS, "Error %d creating the global security object!", dwErr)); }
DbgPrint((TRC_TRACK, "WZCSvcInitRPCSecurity]=%d", dwErr)); return dwErr; }
//-------------------------------------------------
// Check the access for the particular access mask provided
DWORD WZCSvcCheckRPCAccess(DWORD dwAccess) { DWORD dwErr = ERROR_SUCCESS;
if (g_pSecurityDescr != NULL) { dwErr = NetpAccessCheckAndAudit( _T("WZCSVC"), _T("WZCSVC"), g_pSecurityDescr, dwAccess, &g_Mapping);
DbgPrint((TRC_GENERIC, ">>> Security check reports err=%d.", dwErr)); }
return dwErr; }
//-------------------------------------------------
// Check the validity of a RAW_DATA pointer
DWORD WZCSvcCheckParamRawData(PRAW_DATA prd) { DWORD dwErr = ERROR_SUCCESS;
if (prd != NULL) { if (prd->dwDataLen != 0 && prd->pData == NULL) dwErr = ERROR_INVALID_PARAMETER; }
return dwErr; }
//-------------------------------------------------
// Check the validity of an SSID embedded in a RAW_DATA pointer
DWORD WZCSvcCheckSSID(PNDIS_802_11_SSID pndSSID, UINT nBytes) { DWORD dwErr = ERROR_SUCCESS;
if (pndSSID != NULL) { if (nBytes < FIELD_OFFSET(NDIS_802_11_SSID, Ssid) || pndSSID->SsidLength > nBytes - FIELD_OFFSET(NDIS_802_11_SSID, Ssid)) dwErr = ERROR_INVALID_PARAMETER; }
return dwErr; }
//-------------------------------------------------
// Check the validity of a list of configurations embedded in a RAW_DATA pointer
DWORD WZCSvcCheckConfig(PWZC_WLAN_CONFIG pwzcConfig, UINT nBytes) { DWORD dwErr = ERROR_SUCCESS;
if (pwzcConfig != NULL) { if (pwzcConfig->Length > nBytes || pwzcConfig->Length != sizeof(WZC_WLAN_CONFIG)) dwErr = ERROR_INVALID_PARAMETER;
if (dwErr == ERROR_SUCCESS) dwErr = WZCSvcCheckSSID(&pwzcConfig->Ssid, sizeof(NDIS_802_11_SSID));
if (dwErr == ERROR_SUCCESS && pwzcConfig->KeyLength > WZCCTL_MAX_WEPK_MATERIAL) dwErr = ERROR_INVALID_PARAMETER; } return dwErr; }
//-------------------------------------------------
// Check the validity of a list of configurations embedded in a RAW_DATA pointer
DWORD WZCSvcCheckConfigList(PWZC_802_11_CONFIG_LIST pwzcList, UINT nBytes) { DWORD dwErr = ERROR_SUCCESS;
if (pwzcList != NULL) { UINT i;
if (nBytes < FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config)) dwErr = ERROR_INVALID_PARAMETER;
nBytes -= FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config);
if (dwErr == ERROR_SUCCESS && ((pwzcList->NumberOfItems * sizeof(WZC_WLAN_CONFIG) > nBytes) || (pwzcList->Index > pwzcList->NumberOfItems) ) ) dwErr = ERROR_INVALID_PARAMETER;
for (i = 0; i < pwzcList->NumberOfItems && dwErr == ERROR_SUCCESS; i++) dwErr = WZCSvcCheckConfig(&(pwzcList->Config[i]), sizeof(WZC_WLAN_CONFIG)); }
return dwErr; }
//-------------------------------------------------
// Check the validity of the "input" fields from the INTF_ENTRY.
DWORD WZCSvcCheckParamIntfEntry(PINTF_ENTRY pIntfEntry) { DWORD dwErr = ERROR_SUCCESS;
if (pIntfEntry != NULL) { if (dwErr == ERROR_SUCCESS) dwErr = WZCSvcCheckParamRawData(&pIntfEntry->rdSSID); if (dwErr == ERROR_SUCCESS) dwErr = WZCSvcCheckParamRawData(&pIntfEntry->rdBSSID); if (dwErr == ERROR_SUCCESS) dwErr = WZCSvcCheckParamRawData(&pIntfEntry->rdStSSIDList); if (dwErr == ERROR_SUCCESS) dwErr = WZCSvcCheckConfigList( (PWZC_802_11_CONFIG_LIST)pIntfEntry->rdStSSIDList.pData, pIntfEntry->rdStSSIDList.dwDataLen); if (dwErr == ERROR_SUCCESS) dwErr = WZCSvcCheckParamRawData(&pIntfEntry->rdCtrlData); }
return dwErr; }
//-------------------------------------------------
// Cleanup whatever data was used for RPC security settings
DWORD WZCSvcTermRPCSecurity() { DWORD dwErr = ERROR_SUCCESS;
DbgPrint((TRC_TRACK, "[WZCSvcTermRPCSecurity"));
dwErr = RtlNtStatusToDosError(NetpDeleteSecurityObject(&g_pSecurityDescr)); DbgAssert((dwErr == ERROR_SUCCESS, "Failed to delete the global security descriptor!")); g_pSecurityDescr = NULL;
NetpFreeWellKnownSids();
DbgPrint((TRC_TRACK, "WZCSvcTermRPCSecurity]=%d", dwErr)); return dwErr; }
RPC_STATUS CallbackCheckLocal( IN RPC_IF_HANDLE *Interface, IN void *Context) { RPC_STATUS rpcStat = RPC_S_OK; LPTSTR pBinding = NULL; LPTSTR pProtSeq = NULL;
rpcStat = RpcBindingToStringBinding(Context, &pBinding); if (rpcStat == RPC_S_OK) { rpcStat = RpcStringBindingParse( pBinding, NULL, &pProtSeq, NULL, NULL, NULL); }
if (rpcStat == RPC_S_OK) { if (_tcsicmp((LPCTSTR)pProtSeq, _T("ncalrpc")) != 0) rpcStat = RPC_S_ACCESS_DENIED; }
if (pBinding != NULL) RpcStringFree(&pBinding); if (pProtSeq != NULL) RpcStringFree(&pProtSeq);
return rpcStat; }
DWORD WZCSvcStartRPCServer() { DWORD dwStatus = RPC_S_OK;
DbgPrint((TRC_TRACK, "[WZCSvcStartRPCServer"));
if (dwStatus == RPC_S_OK) { dwStatus = RpcServerUseProtseqEp( L"ncalrpc", 10, L"wzcsvc", NULL); if (dwStatus == RPC_S_DUPLICATE_ENDPOINT) dwStatus = RPC_S_OK; }
if (dwStatus == RPC_S_OK) { dwStatus = RpcServerRegisterIfEx( winwzc_ServerIfHandle, 0, 0, RPC_IF_ALLOW_SECURE_ONLY, // WZCSAPI is using RPC_C_PROTECT_LEVEL_PKT_PRIVACY
0, // ignored for non auto-listen interfaces
CallbackCheckLocal); }
if (dwStatus == RPC_S_OK) { dwStatus = RpcServerRegisterAuthInfo( 0, RPC_C_AUTHN_WINNT, 0, 0); }
if (dwStatus == RPC_S_OK) { dwStatus = RpcServerRegisterAuthInfo( 0, RPC_C_AUTHN_GSS_KERBEROS, 0, 0); }
if (dwStatus == RPC_S_OK) { dwStatus = RpcServerRegisterAuthInfo( 0, RPC_C_AUTHN_GSS_NEGOTIATE, 0, 0); }
if (dwStatus == RPC_S_OK) { dwStatus = RpcServerListen( 3, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE); if (dwStatus == RPC_S_ALREADY_LISTENING) dwStatus = RPC_S_OK; }
if (dwStatus != RPC_S_OK) { RpcServerUnregisterIfEx( winwzc_ServerIfHandle, 0, 0); }
g_bRpcStarted = (dwStatus == RPC_S_OK);
WZCSvcInitRPCSecurity();
DbgPrint((TRC_TRACK, "WZCSvcStartRPCServer]=%d", dwStatus)); return (dwStatus); }
DWORD WZCSvcStopRPCServer() { DWORD dwStatus = RPC_S_OK; DbgPrint((TRC_TRACK, "[WZCSvcStopRPCServer"));
if (g_bRpcStarted) { g_bRpcStarted = FALSE;
WZCSvcTermRPCSecurity();
dwStatus = RpcServerUnregisterIfEx( winwzc_ServerIfHandle, 0, 0);
// don't stop RPC from listening - other services could rely on this
//RpcMgmtStopServerListening(0);
}
DbgPrint((TRC_TRACK, "WZCSvcStopRPCServer]=%d", dwStatus)); return (dwStatus); }
DWORD RpcEnumInterfaces( STRING_HANDLE pSrvAddr, PINTFS_KEY_TABLE pIntfsTable) { DWORD dwErr = ERROR_SUCCESS; DWORD dwNumIntfs;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK, "[RpcEnumInterfaces")); dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); if (dwErr == ERROR_SUCCESS) { dwNumIntfs = LstNumInterfaces();
DbgPrint((TRC_GENERIC, "Num interfaces = %d", dwNumIntfs));
if (dwNumIntfs == 0) goto exit;
pIntfsTable->pIntfs = RpcCAlloc(dwNumIntfs*sizeof(INTF_KEY_ENTRY)); if (pIntfsTable->pIntfs == NULL) { dwErr = GetLastError(); goto exit; } dwErr = LstGetIntfsKeyInfo(pIntfsTable->pIntfs, &dwNumIntfs);
if (dwErr != ERROR_SUCCESS || dwNumIntfs == 0) { RpcFree(pIntfsTable->pIntfs); pIntfsTable->pIntfs = NULL; goto exit; }
pIntfsTable->dwNumIntfs = dwNumIntfs; for (dwNumIntfs = 0; dwNumIntfs < pIntfsTable->dwNumIntfs; dwNumIntfs++) { DbgPrint((TRC_GENERIC, "Intf %d:\t%S", dwNumIntfs, pIntfsTable->pIntfs[dwNumIntfs].wszGuid == NULL ? L"(null)" : pIntfsTable->pIntfs[dwNumIntfs].wszGuid)); } } exit: DbgPrint((TRC_TRACK, "RpcEnumInterfaces]=%d", dwErr));
InterlockedDecrement(&g_nThreads); return dwErr; }
DWORD RpcQueryInterface( STRING_HANDLE pSrvAddr, DWORD dwInFlags, PINTF_ENTRY pIntfEntry, LPDWORD pdwOutFlags) { DWORD dwErr = ERROR_SUCCESS;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK, "[RpcQueryInterface(0x%x,%S)", dwInFlags, pIntfEntry->wszGuid)); dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); if (dwErr == ERROR_SUCCESS) { dwErr = LstQueryInterface(dwInFlags, pIntfEntry, pdwOutFlags); } DbgPrint((TRC_TRACK, "RpcQueryInterface]=%d", dwErr));
InterlockedDecrement(&g_nThreads); return dwErr; }
DWORD RpcSetInterface( STRING_HANDLE pSrvAddr, DWORD dwInFlags, PINTF_ENTRY pIntfEntry, LPDWORD pdwOutFlags) { DWORD dwErr = ERROR_SUCCESS;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK, "[RpcSetInterface(0x%x,%S)", dwInFlags, pIntfEntry->wszGuid)); dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_SET);
if (dwErr == ERROR_SUCCESS) { dwErr = WZCSvcCheckParamIntfEntry(pIntfEntry); }
if (dwErr == ERROR_SUCCESS) { dwErr = LstSetInterface(dwInFlags, pIntfEntry, pdwOutFlags); } DbgPrint((TRC_TRACK, "RpcSetInterface]=%d", dwErr));
InterlockedDecrement(&g_nThreads); return dwErr; }
DWORD RpcRefreshInterface( STRING_HANDLE pSrvAddr, DWORD dwInFlags, PINTF_ENTRY pIntfEntry, LPDWORD pdwOutFlags) { DWORD dwErr = ERROR_SUCCESS;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK, "[RpcRefreshInterface(0x%x,%S)", dwInFlags, pIntfEntry->wszGuid)); dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_SET); if (dwErr == ERROR_SUCCESS) { dwErr = LstRefreshInterface(dwInFlags, pIntfEntry, pdwOutFlags); } DbgPrint((TRC_TRACK, "RpcRefreshInterface]=%d", dwErr));
InterlockedDecrement(&g_nThreads); return dwErr; }
DWORD RpcQueryContext( STRING_HANDLE pSrvAddr, DWORD dwInFlags, PWZC_CONTEXT pContext, LPDWORD pdwOutFlags) { DWORD dwErr = ERROR_SUCCESS;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK, "[RpcQueryContext(0x%x)", dwInFlags)); dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); if (dwErr == ERROR_SUCCESS) { dwErr = WzcContextQuery(dwInFlags, pContext, pdwOutFlags); } DbgPrint((TRC_TRACK, "RpcQueryContext]=%d", dwErr));
InterlockedDecrement(&g_nThreads); return dwErr;
}
DWORD RpcSetContext( STRING_HANDLE pSrvAddr, DWORD dwInFlags, PWZC_CONTEXT pContext, LPDWORD pdwOutFlags) { DWORD dwErr = ERROR_SUCCESS; BOOL bLogEnabled = FALSE;
InterlockedIncrement(&g_nThreads);
DbgPrint((TRC_TRACK, "[RpcSetContext(0x%x)", dwInFlags)); dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_SET); if (dwErr == ERROR_SUCCESS) { dwErr = WzcContextSet(dwInFlags, pContext, pdwOutFlags); } DbgPrint((TRC_TRACK, "RpcSetContext]=%d", dwErr)); BAIL_ON_WIN32_ERROR(dwErr);
EnterCriticalSection(&g_wzcInternalCtxt.csContext);
bLogEnabled = ((g_wzcInternalCtxt.wzcContext.dwFlags & WZC_CTXT_LOGGING_ON) != 0);
dwErr = WZCSetLoggingState(bLogEnabled); BAIL_ON_LOCK_ERROR(dwErr);
lock: LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
error:
InterlockedDecrement(&g_nThreads); return dwErr;
}
extern SERVICE_STATUS g_WZCSvcStatus; DWORD RpcCmdInterface( IN DWORD dwHandle, IN DWORD dwCmdCode, IN LPWSTR wszIntfGuid, IN PRAW_DATA prdUserData) { DWORD dwErr = ERROR_SUCCESS;
InterlockedIncrement(&g_nThreads); // We need to avoid processing this command if the service is not currently running
// We need this protection only for this call because other than for the other calls,
// RpcCmdInterface is called from 802.1x which runs within the same service. For all
// the other Rpc stubs, the RPC server is shut down prior to destroying the global data
// hence there is guaranteed no other calls will be made afterwards.
if (g_WZCSvcStatus.dwCurrentState == SERVICE_RUNNING) { DbgPrint((TRC_TRACK, "[RpcCmdInterface(0x%x,%S)", dwCmdCode, wszIntfGuid));
dwErr = WZCSvcCheckRPCAccess(WZC_ACCESS_SET);
// currently this is not an RPC call! This is called directly from 802.1x. Consequently,
// WZCSvcCheckRPCAccess will return RPC_S_NO_CALL_ACTIVE. We could either remove the
// RPC check for now, or pass through the RPC_S_NO_CALL_ACTIVE (since later this could
// become an RPC call). We do the latter!
if (dwErr == ERROR_SUCCESS || dwErr == RPC_S_NO_CALL_ACTIVE) { dwErr = LstCmdInterface(dwHandle, dwCmdCode, wszIntfGuid, prdUserData); } DbgPrint((TRC_TRACK, "RpcCmdInterface]=%d", dwErr)); } InterlockedDecrement(&g_nThreads); return dwErr; }
VOID WZC_DBLOG_SESSION_HANDLE_rundown( WZC_DBLOG_SESSION_HANDLE hSession ) { if (!g_bRpcStarted) { return; } if (!IsDBOpened()) { return; }
if (hSession) { (VOID) CloseWZCDbLogSession( hSession ); }
return; }
DWORD RpcOpenWZCDbLogSession( STRING_HANDLE pServerName, WZC_DBLOG_SESSION_HANDLE * phSession ) { DWORD dwError = 0;
InterlockedIncrement(&g_nThreads); dwError = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); BAIL_ON_WIN32_ERROR(dwError);
if (!phSession) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_WIN32_ERROR(dwError); }
dwError = OpenWZCDbLogSession( pServerName, 0, phSession ); BAIL_ON_WIN32_ERROR(dwError);
error: InterlockedDecrement(&g_nThreads); return (dwError); }
DWORD RpcCloseWZCDbLogSession( WZC_DBLOG_SESSION_HANDLE * phSession ) { DWORD dwError = 0;
InterlockedIncrement(&g_nThreads);
dwError = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); BAIL_ON_WIN32_ERROR(dwError);
if (!IsDBOpened()) { dwError = ERROR_SERVICE_DISABLED; BAIL_ON_WIN32_ERROR(dwError); }
if (!phSession) { InterlockedDecrement(&g_nThreads); return (ERROR_INVALID_PARAMETER); }
dwError = CloseWZCDbLogSession( *phSession ); BAIL_ON_WIN32_ERROR(dwError);
*phSession = NULL;
error:
InterlockedDecrement(&g_nThreads); return (dwError); }
DWORD RpcEnumWZCDbLogRecords( WZC_DBLOG_SESSION_HANDLE hSession, PWZC_DB_RECORD_CONTAINER pTemplateRecordContainer, PBOOL pbEnumFromStart, DWORD dwPreferredNumEntries, PWZC_DB_RECORD_CONTAINER * ppRecordContainer ) { DWORD dwError = 0; PWZC_DB_RECORD pWZCRecords = NULL; DWORD dwNumRecords = 0; PWZC_DB_RECORD pTemplateRecord = NULL;
InterlockedIncrement(&g_nThreads);
dwError = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); BAIL_ON_WIN32_ERROR(dwError);
if (!IsDBOpened()) { dwError = ERROR_SERVICE_DISABLED; BAIL_ON_WIN32_ERROR(dwError); }
if (!hSession || !pbEnumFromStart) { InterlockedDecrement(&g_nThreads); return (ERROR_NOT_SUPPORTED); }
if (!pTemplateRecordContainer || !ppRecordContainer || !*ppRecordContainer) { InterlockedDecrement(&g_nThreads); return (ERROR_INVALID_PARAMETER); }
if (pTemplateRecordContainer->pWZCRecords) { if (pTemplateRecordContainer->dwNumRecords != 1) { InterlockedDecrement(&g_nThreads); return (ERROR_INVALID_PARAMETER); } pTemplateRecord = pTemplateRecordContainer->pWZCRecords; }
dwError = EnumWZCDbLogRecordsSummary( hSession, pTemplateRecord, pbEnumFromStart, dwPreferredNumEntries, &pWZCRecords, &dwNumRecords, NULL ); if (dwError != ERROR_NO_MORE_ITEMS) { BAIL_ON_WIN32_ERROR(dwError); }
(*ppRecordContainer)->pWZCRecords = pWZCRecords; (*ppRecordContainer)->dwNumRecords = dwNumRecords;
InterlockedDecrement(&g_nThreads); return (dwError);
error:
(*ppRecordContainer)->pWZCRecords = NULL; (*ppRecordContainer)->dwNumRecords = 0; InterlockedDecrement(&g_nThreads); return (dwError); }
DWORD RpcFlushWZCDbLog( WZC_DBLOG_SESSION_HANDLE hSession ) { DWORD dwError = 0;
InterlockedIncrement(&g_nThreads); dwError = WZCSvcCheckRPCAccess(WZC_ACCESS_SET); BAIL_ON_WIN32_ERROR(dwError);
if (!IsDBOpened()) { dwError = ERROR_SERVICE_DISABLED; BAIL_ON_WIN32_ERROR(dwError); }
if (!hSession){ InterlockedDecrement(&g_nThreads); return(ERROR_INVALID_PARAMETER); }
dwError = FlushWZCDbLog( hSession ); BAIL_ON_WIN32_ERROR(dwError);
error: InterlockedDecrement(&g_nThreads); return (dwError); }
DWORD RpcGetWZCDbLogRecord( WZC_DBLOG_SESSION_HANDLE hSession, PWZC_DB_RECORD_CONTAINER pTemplateRecordContainer, PWZC_DB_RECORD_CONTAINER * ppRecordContainer ) { DWORD dwError = 0; PWZC_DB_RECORD pWZCRecords = NULL; DWORD dwNumRecords = 0; PWZC_DB_RECORD pTemplateRecord = NULL;
InterlockedIncrement(&g_nThreads);
dwError = WZCSvcCheckRPCAccess(WZC_ACCESS_QUERY); BAIL_ON_WIN32_ERROR(dwError);
if (!IsDBOpened()) { dwError = ERROR_SERVICE_DISABLED; BAIL_ON_WIN32_ERROR(dwError); } if(!hSession){ InterlockedDecrement(&g_nThreads); return(ERROR_NOT_SUPPORTED); }
if (!pTemplateRecordContainer || !ppRecordContainer || !*ppRecordContainer) { InterlockedDecrement(&g_nThreads); return (ERROR_INVALID_PARAMETER); }
if (pTemplateRecordContainer->pWZCRecords) { if (pTemplateRecordContainer->dwNumRecords != 1) { InterlockedDecrement(&g_nThreads); return (ERROR_INVALID_PARAMETER); } pTemplateRecord = pTemplateRecordContainer->pWZCRecords; }
dwError = GetWZCDbLogRecord( hSession, pTemplateRecord, &pWZCRecords, NULL ); BAIL_ON_WIN32_ERROR(dwError);
(*ppRecordContainer)->pWZCRecords = pWZCRecords; (*ppRecordContainer)->dwNumRecords = 1;
InterlockedDecrement(&g_nThreads); return (dwError);
error:
(*ppRecordContainer)->pWZCRecords = NULL; (*ppRecordContainer)->dwNumRecords = 0; InterlockedDecrement(&g_nThreads); return (dwError); }
|