2025-04-27 07:49:33 -04:00

827 lines
23 KiB
C++

// KeyManager.cpp: implementation of the CKeyManager class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <iphlpapi.h>
#include <wbemidl.h>
#include "KeyManagerHash.h"
#ifdef UNIX
#include "sha.cpp"
#else
#include "nt/sha.h"
#endif
HRESULT WMIIsNicMAC(BSTR bstrInstanceName);
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30];
}ASTAT, * PASTAT;
// NOTE: NetBios is not thread safe and having two threads running
// through the CTor at the same time is bad. Therefore, we use
// a named mutex so that no more than one thread on a single machine
// can ever execute the ctor at the same time.
CKeyManagerHash::CKeyManagerHash()
{
ULONG ulBufSize = 0;
PIP_ADAPTER_INFO pHead = NULL;
DWORD dwResult = 0;
#define CRAP "12398sdfksjdflk"
#define CRAP2 "sdflkj31409sd"
// Key must be at least that long
_ASSERT(A_SHA_DIGEST_LEN > 12);
m_pks = NULL;
m_nKeys = 0;
m_nEncryptKey = (ULONG)-1;
m_ok = FALSE;
m_dwLoggingEnabled = FALSE;
// Check for logging enabled.
LONG lResult;
HKEY hkPassport;
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Passport"),
0,
KEY_READ,
&hkPassport);
if(lResult == ERROR_SUCCESS)
{
DWORD dwLen;
dwLen = sizeof(DWORD);
lResult = RegQueryValueEx(hkPassport,
TEXT("KeyManLoggingEnabled"),
NULL,
NULL,
(LPBYTE)&m_dwLoggingEnabled,
&dwLen);
#if defined(_DEBUG)
BYTE abKey[6];
dwLen = sizeof(abKey);
lResult = RegQueryValueEx(hkPassport,
TEXT("CryptKeyOverride"),
NULL,
NULL,
abKey,
&dwLen);
if(lResult == ERROR_SUCCESS)
{
m_nKeys = 1;
m_pks = new DES3TABLE[1];
m_nEncryptKey = 1;
makeDESKey(abKey, 0);
m_ok = TRUE;
goto Cleanup;
}
#endif
RegCloseKey(hkPassport);
}
// In multi-process scenarios, the CreateMutex call can fail
// with access denied. If this happens, sleep for a short period
// of time and try again.
HANDLE hMutex;
hMutex = CreateMutex(NULL, FALSE, TEXT("Passport.KeyManager.Ctor"));
if(hMutex == NULL)
{
int nCreateCount;
for(nCreateCount = 0; nCreateCount < 50 && hMutex == NULL; nCreateCount++)
{
Sleep(100);
hMutex = CreateMutex(NULL, FALSE, TEXT("Passport.KeyManager.Ctor"));
}
if(hMutex == NULL)
{
DWORD dwError = GetLastError();
LogBlob((LPBYTE)&dwError, sizeof(DWORD), "CREATE MUTEX ERROR");
goto Cleanup;
}
}
WaitForSingleObject(hMutex, INFINITE);
#if 0
HRESULT hr;
hr = LoadKeysFromWMI();
//
// If WMI failed, try IPHLPAPI and NetBios.
//
if(hr != S_OK)
#endif
{
//
// Get adapter information
//
ulBufSize = 0;
dwResult = GetAdaptersInfo(pHead, &ulBufSize);
if(dwResult == ERROR_BUFFER_OVERFLOW)
{
if(ulBufSize == 0)
{
LogBlob(NULL, 0, "NO ADAPTERS!");
goto Cleanup;
}
pHead = (PIP_ADAPTER_INFO)malloc(ulBufSize);
if(pHead == NULL)
{
LogBlob(NULL, 0, "OUT OF MEMORY!");
goto Cleanup;
}
dwResult = GetAdaptersInfo(pHead, &ulBufSize);
if(dwResult != NO_ERROR)
goto Cleanup;
//
// How many adapters are there?
//
PIP_ADAPTER_INFO pCurrent;
for(pCurrent = pHead; pCurrent != NULL; pCurrent = pCurrent->Next)
m_nKeys++;
//
// Make a key table big enough to hold all of them.
//
m_pks = new DES3TABLE[m_nKeys];
if(m_pks == NULL)
goto Cleanup;
memset(m_pks, 0, sizeof(DES3TABLE) * m_nKeys);
//
// Make all the keys.
//
unsigned char zeros[8];
memset( zeros, 0, 8 );
int i;
for(pCurrent = pHead, i = 0; pCurrent != NULL ; pCurrent = pCurrent->Next, i++)
{
if (memcmp(pCurrent->Address, zeros, 6) == 0)
{
LogBlob((LPBYTE)pCurrent->Address, 6, "CANDIDATE MAC");
continue;
}
// Now generate a DES key
if(m_nEncryptKey == (ULONG)-1)
m_nEncryptKey = i;
makeDESKey(pCurrent->Address, i);
}
}
else if(dwResult == ERROR_NO_DATA)
{
LogBlob(NULL, 0, "NO ADAPTERS!");
goto Cleanup;
}
else
{
//
// This machine doesn't support the necessary iphlpapi functions, so try
// netbios instead. This won't work if the machine has netbios disabled,
// but it's the best we can do.
//
NCB Ncb;
LANA_ENUM lenum;
memset( &Ncb, 0, sizeof(Ncb) );
Ncb.ncb_command = NCBENUM;
Ncb.ncb_buffer = (UCHAR *)&lenum;
Ncb.ncb_length = sizeof(lenum);
UCHAR uRetCode;
uRetCode = Netbios( &Ncb );
if(lenum.length == 0)
{
LogBlob(NULL, 0, "NO ADAPTERS!");
}
else
{
m_nKeys = lenum.length;
m_pks = new DES3TABLE[m_nKeys];
if(m_pks == NULL)
goto Cleanup;
memset(m_pks, 0, sizeof(DES3TABLE) * m_nKeys);
}
unsigned char zeros[8];
memset( zeros, 0, 8 );
int i;
for(i=0; i < lenum.length ;i++)
{
memset( &Ncb, 0, sizeof(Ncb) );
Ncb.ncb_command = NCBRESET;
Ncb.ncb_lana_num = lenum.lana[i];
uRetCode = Netbios( &Ncb );
if (uRetCode != 0)
continue;
memset( &Ncb, 0, sizeof (Ncb) );
Ncb.ncb_command = NCBASTAT;
Ncb.ncb_lana_num = lenum.lana[i];
strcpy( (char*)Ncb.ncb_callname, "* " );
ASTAT Adapter;
Ncb.ncb_buffer = (unsigned char *) &Adapter;
Ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios( &Ncb );
if (uRetCode != 0)
continue;
if (memcmp(Adapter.adapt.adapter_address, zeros, 6)==0)
{
LogBlob((LPBYTE)&Adapter.adapt.adapter_address, 6, "CANDIDATE MAC");
continue;
}
// Now generate a DES key
if(m_nEncryptKey == (ULONG)-1)
m_nEncryptKey = i;
makeDESKey(Adapter.adapt.adapter_address, i);
}
}
}
Cleanup:
if(!m_ok)
LogBlob(NULL, 0, "CTOR NO MAC ADDRESS");
if(pHead != NULL)
free(pHead);
if(hMutex)
{
ReleaseMutex(hMutex);
CloseHandle(hMutex);
}
}
CKeyManagerHash::~CKeyManagerHash()
{
if(m_pks)
delete [] m_pks;
}
#define ToHex(n) ((n > 9) ? ('A' + (n - 10)) : ('0' + n))
void CKeyManagerHash::LogBlob(
LPBYTE pbBlob,
DWORD dwBlobLen,
LPCSTR pszCaption)
{
HANDLE hFile;
BYTE bZero = 0;
TCHAR achBuf[2048];
CHAR achOutput[8];
DWORD dwBytesWritten;
UINT i;
const CHAR achSep[] = " = ";
const CHAR achEOL[] = "\r\n";
if(!m_dwLoggingEnabled)
return;
GetSystemDirectory(achBuf, sizeof(achBuf));
lstrcat(achBuf, TEXT("\\ppkeyman.log"));
hFile = CreateFile(achBuf,
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile == INVALID_HANDLE_VALUE) return;
SetFilePointer(hFile, 0, NULL, FILE_END);
WriteFile(hFile, pszCaption, lstrlenA(pszCaption), &dwBytesWritten, NULL);
WriteFile(hFile, achSep, 3, &dwBytesWritten, NULL);
for(i = 0; i < dwBlobLen; i++)
{
BYTE bHigh = (pbBlob[i] >> 4) & 0xF;
BYTE bLow = pbBlob[i] & 0xF;
achOutput[0] = ToHex(bHigh);
achOutput[1] = ToHex(bLow);
WriteFile(hFile, achOutput, 2, &dwBytesWritten, NULL);
}
WriteFile(hFile, achEOL, 2, &dwBytesWritten, NULL);
CloseHandle(hFile);
}
void CKeyManagerHash::makeDESKey(LPBYTE pbKey, ULONG nKey)
{
LogBlob(pbKey, 6, "MAC");
unsigned char key[2*A_SHA_DIGEST_LEN];
A_SHA_CTX ictx;
A_SHAInit(&ictx);
A_SHAUpdate(&ictx, (BYTE*) CRAP, strlen(CRAP));
A_SHAUpdate(&ictx, pbKey, 6);
A_SHAUpdate(&ictx, (BYTE*) CRAP2, strlen(CRAP2));
A_SHAFinal(&ictx, key);
A_SHAInit(&ictx);
A_SHAUpdate(&ictx, (BYTE*) CRAP2, strlen(CRAP2));
A_SHAUpdate(&ictx, pbKey, 6);
A_SHAUpdate(&ictx, (BYTE*) CRAP, strlen(CRAP));
A_SHAFinal(&ictx, key+12);
#ifdef UNIX
int ok;
ok = des_key_sched((C_Block*)(key), ks1) ||
des_key_sched((C_Block*)(key+8), ks2) ||
des_key_sched((C_Block*)(key+16), ks3);
m_ok = (ok == 0);
#else
tripledes3key(&(m_pks[nKey]), (BYTE*) key);
m_ok = TRUE;
#endif
}
static unsigned char kdsync[] = { 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10 };
HRESULT CKeyManagerHash::encryptKey(RAWKEY input, ENCKEY output)
{
HRESULT hr;
char ivec[9];
BYTE inBuf[48];
A_SHA_CTX ctx;
if (!m_ok)
{
LogBlob(NULL, 0, "ENCRYPT !!!INVALID KEY MANAGER OBJECT!!!");
hr = E_FAIL;
goto Cleanup;
}
LogBlob((LPBYTE)&(m_pks[m_nEncryptKey]), sizeof(DES3TABLE), "ENCRYPT KEY");
LogBlob(input, sizeof(RAWKEY), "ENCRYPT IN ");
memcpy(ivec, "12345678", 8);
// build up the input buffer + sha-1 hash
memcpy(inBuf, input, sizeof(RAWKEY));
A_SHAInit(&ctx);
A_SHAUpdate(&ctx, inBuf, sizeof(RAWKEY));
A_SHAFinal(&ctx, inBuf + sizeof(RAWKEY));
memset(inBuf + 44, 0, 4);
// Do a simple DES encryption using our key
// We only encrypt 40 of the 44 bytes
#ifdef UNIX
des_ede3_cbc_encrypt((C_Block*)(kdsync), (C_Block*)(output), 8,
ks1, ks2, ks3, (C_Block*) ivec, DES_ENCRYPT);
des_ede3_cbc_encrypt((C_Block*)(input), (C_Block*)(output+8), 24,
ks1, ks2, ks3, (C_Block*) ivec, DES_ENCRYPT);
#else
CBC(tripledes, 8, output, kdsync, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
CBC(tripledes, 8, output+8, inBuf, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
CBC(tripledes, 8, output+16, inBuf+8, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
CBC(tripledes, 8, output+24, inBuf+16, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
CBC(tripledes, 8, output+32, inBuf+24, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
CBC(tripledes, 8, output+40, inBuf+32, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
CBC(tripledes, 8, output+48, inBuf+40, &(m_pks[m_nEncryptKey]), 1, (BYTE*)ivec);
#endif
LogBlob(output, sizeof(ENCKEY), "ENCRYPT OUT");
hr = S_OK;
Cleanup:
return hr;
}
HRESULT CKeyManagerHash::decryptKey(ENCKEY input, RAWKEY output)
{
HRESULT hr;
char ivec[9];
BYTE outBuf[48];
BYTE hashOut[A_SHA_DIGEST_LEN];
A_SHA_CTX ctx;
ULONG nCurKey;
DES3TABLE nullTable;
memset(&nullTable, 0, sizeof(DES3TABLE));
if (!m_ok)
{
LogBlob(NULL, 0, "DECRYPT !!!INVALID KEY MANAGER OBJECT!!!");
hr = E_FAIL;
goto Cleanup;
}
//LogBlob((LPBYTE)&m_ks, sizeof(DES3TABLE), "DECRYPT KEY");
LogBlob(input, sizeof(ENCKEY), "DECRYPT IN ");
memcpy(ivec, "12345678", 8);
// Do simple DES decryption
unsigned char syncin[8];
for(nCurKey = 0; nCurKey < m_nKeys; nCurKey++)
{
if(memcmp(&(m_pks[nCurKey]), &nullTable, sizeof(DES3TABLE)) == 0)
continue;
#ifdef UNIX
des_ede3_cbc_encrypt((C_Block*)(input),(C_Block*)(syncin), 8,
ks1, ks2, ks3, (C_Block*) ivec, DES_DECRYPT);
des_ede3_cbc_encrypt((C_Block*)(input+8),(C_Block*)(output), 24,
ks1, ks2, ks3, (C_Block*) ivec, DES_DECRYPT);
#else
CBC(tripledes, 8, syncin, input, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
CBC(tripledes, 8, outBuf, input+8, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
CBC(tripledes, 8, outBuf+8, input+16, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
CBC(tripledes, 8, outBuf+16, input+24, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
CBC(tripledes, 8, outBuf+24, input+32, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
CBC(tripledes, 8, outBuf+32, input+40, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
CBC(tripledes, 8, outBuf+40, input+48, &(m_pks[nCurKey]), 0, (BYTE*)ivec);
#endif
LogBlob(outBuf, 48, "DECRYPT OUT");
if (memcmp(kdsync,syncin,8) != 0)
{
LogBlob((LPBYTE)&kdsync, 8, "DECRYPT KDSYNC");
LogBlob((LPBYTE)&syncin, 8, "DECRYPT SYNCIN");
continue;
}
// compute and compare hash
A_SHAInit(&ctx);
A_SHAUpdate(&ctx, outBuf, sizeof(RAWKEY));
A_SHAFinal(&ctx, hashOut);
if(memcmp(outBuf + sizeof(RAWKEY), hashOut, A_SHA_DIGEST_LEN) != 0)
{
LogBlob((LPBYTE)outBuf + sizeof(RAWKEY), A_SHA_DIGEST_LEN, "OUTBUF HASH");
LogBlob((LPBYTE)hashOut, A_SHA_DIGEST_LEN, "COMPUTED HASH");
continue;
}
memcpy(output, outBuf, sizeof(RAWKEY));
hr = S_OK;
goto Cleanup;
}
hr = E_FAIL;
Cleanup:
return hr;
}
#define MAX_WMI_ADDRESSES 32
HRESULT CKeyManagerHash::LoadKeysFromWMI()
{
HRESULT hr;
bool bMadeKeys = false;
IWbemLocator* piLocator = NULL;
IWbemServices* piServices = NULL;
IWbemClassObject* piClassObject = NULL;
IEnumWbemClassObject* piEnumObjects = NULL;
IClientSecurity* piClientSecurity = NULL;
IWbemClassObject* apiObjects[MAX_WMI_ADDRESSES];
BSTR bstrRootName = SysAllocString(L"root\\wmi");
BSTR bstrClassName = SysAllocString(L"MsNdis_EthernetPermanentAddress");
if(bstrRootName == NULL || bstrClassName == NULL)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
memset(apiObjects, 0, sizeof(apiObjects));
hr = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_SERVER, IID_IWbemLocator, (void**)&piLocator);
if(FAILED(hr))
goto Cleanup;
hr = piLocator->ConnectServer(bstrRootName,
NULL,
NULL,
NULL,
0,
NULL,
NULL,
&piServices);
if(FAILED(hr))
goto Cleanup;
hr = piServices->QueryInterface(IID_IClientSecurity,
(void**)&piClientSecurity);
if(FAILED(hr))
goto Cleanup;
hr = piClientSecurity->SetBlanket(piServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE);
if(FAILED(hr))
goto Cleanup;
hr = piServices->CreateInstanceEnum(bstrClassName,
0,
NULL,
&piEnumObjects);
if(FAILED(hr))
goto Cleanup;
hr = piEnumObjects->Next(WBEM_INFINITE, MAX_WMI_ADDRESSES, apiObjects, &m_nKeys);
if(FAILED(hr))
goto Cleanup;
//
// Make a key table big enough to hold all of them.
//
m_pks = new DES3TABLE[m_nKeys];
if(m_pks == NULL)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
memset(m_pks, 0, sizeof(DES3TABLE) * m_nKeys);
//
// Make all the keys.
//
unsigned char zeros[8];
memset( zeros, 0, 8 );
ULONG i;
for(i = 0; i < m_nKeys; i++)
{
VARIANT vValue;
CIMTYPE type;
LONG lFlavor;
VARIANT vInstanceName;
VariantInit(&vInstanceName);
VariantInit(&vValue);
hr = apiObjects[i]->Get(L"NdisPermanentAddress", 0, &vValue, &type, &lFlavor);
if (SUCCEEDED(hr))
hr = apiObjects[i]->Get(L"InstanceName", 0, &vInstanceName, NULL, NULL);
if(SUCCEEDED(hr))
{
IWbemClassObject* piAddress = NULL;
hr = vValue.punkVal->QueryInterface(IID_IWbemClassObject, (void**)&piAddress);
if(SUCCEEDED(hr))
{
VARIANT vAddrVal;
VariantInit(&vAddrVal);
hr = piAddress->Get(L"Address", 0, &vAddrVal, NULL, NULL);
if(SUCCEEDED(hr))
{
//
// Make sure we have an array, and it's the correct length.
//
if((vAddrVal.vt & VT_ARRAY) &&
vAddrVal.parray->rgsabound[0].cElements == 6)
{
LPBYTE puiAddress;
SafeArrayAccessData(vAddrVal.parray, (void**)&puiAddress);
if (memcmp(puiAddress, zeros, 6) == 0 || S_OK != WMIIsNicMAC(vInstanceName.bstrVal))
{
LogBlob((LPBYTE)puiAddress, 6, "CANDIDATE MAC");
// prevent memory leak
SafeArrayUnaccessData(vAddrVal.parray);
VariantClear(&vValue);
piAddress->Release();
VariantClear(&vInstanceName);
continue;
}
// Now generate a DES key
if(m_nEncryptKey == (ULONG)-1)
m_nEncryptKey = i;
makeDESKey(puiAddress, i);
SafeArrayUnaccessData(vAddrVal.parray);
bMadeKeys = true;
}
}
piAddress->Release();
}
VariantClear(&vInstanceName);
VariantClear(&vValue);
}
}
hr = bMadeKeys ? S_OK : S_FALSE;
Cleanup:
if(piLocator != NULL)
{
piLocator->Release();
}
if(piServices != NULL)
{
piServices->Release();
}
if(piClientSecurity != NULL)
{
piClientSecurity->Release();
}
if(piEnumObjects != NULL)
{
piEnumObjects->Release();
}
for(i = 0; i < MAX_WMI_ADDRESSES; i++)
{
if(apiObjects[i] != NULL)
{
apiObjects[i]->Release();
}
}
SysFreeString(bstrRootName);
SysFreeString(bstrClassName);
return hr;
}
HRESULT WMIIsNicMAC(BSTR bstrInstanceName)
{
HRESULT hr;
IWbemLocator* piLocator = NULL;
IWbemServices* piServices = NULL;
IWbemClassObject* piClassObject = NULL;
IEnumWbemClassObject* piNICEnumObjects = NULL;
IClientSecurity* piClientSecurity = NULL;
IWbemClassObject* nicObjects[MAX_WMI_ADDRESSES];
BSTR bstrRootName = SysAllocString(L"root\\cimv2");
BSTR bstrQuery = SysAllocString(L"select * from win32_networkadapterconfiguration where ipenabled = true and "
L"macaddress != NULL and settingid != NULL");
BSTR bstrWQL = SysAllocString(L"WQL");
ULONG nNicAddresses=0;
if(bstrRootName == NULL || bstrQuery == NULL || bstrWQL == NULL)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
memset(nicObjects, 0, sizeof(nicObjects));
hr = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_SERVER, IID_IWbemLocator, (void**)&piLocator);
if(FAILED(hr))
goto Cleanup;
hr = piLocator->ConnectServer(bstrRootName,
NULL,
NULL,
NULL,
0,
NULL,
NULL,
&piServices);
if(FAILED(hr))
goto Cleanup;
hr = piServices->QueryInterface(IID_IClientSecurity,
(void**)&piClientSecurity);
if(FAILED(hr))
goto Cleanup;
hr = piClientSecurity->SetBlanket(piServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE);
if(FAILED(hr))
goto Cleanup;
hr = piServices->ExecQuery( bstrWQL,
bstrQuery,
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&piNICEnumObjects);
if(FAILED(hr))
goto Cleanup;
hr = piNICEnumObjects->Next(WBEM_INFINITE, MAX_WMI_ADDRESSES, nicObjects, &nNicAddresses);
if(FAILED(hr))
goto Cleanup;
/////////////
// if it IS a nic card
VARIANT vValue;
CIMTYPE type;
LONG lFlavor;
for (ULONG i=0; i < nNicAddresses; i++)
{
VariantInit(&vValue);
nicObjects[i]->Get(L"Caption", 0, &vValue, &type, &lFlavor);
if (vValue.vt == VT_BSTR && wcsstr(vValue.bstrVal, bstrInstanceName))
{
hr = S_OK;
VariantClear(&vValue);
goto Cleanup;
}
VariantClear(&vValue);
}
Cleanup:
if(piLocator != NULL)
{
piLocator->Release();
}
if(piServices != NULL)
{
piServices->Release();
}
if(piClientSecurity != NULL)
{
piClientSecurity->Release();
}
if (piNICEnumObjects != NULL)
{
piNICEnumObjects->Release();
}
for(i = 0; i < MAX_WMI_ADDRESSES; i++)
{
if (nicObjects[i] != NULL)
{
nicObjects[i]->Release();
}
}
SysFreeString(bstrRootName);
SysFreeString(bstrQuery);
SysFreeString(bstrWQL);
return hr;
}