// KeyManager.cpp: implementation of the CKeyManager class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #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; }