/* Copyright (c) 1997, Microsoft Corporation, all rights reserved Description: History: Nov 1997: Vijay Baliga created original version. Sep 1998: Vijay Baliga moved functions from eaptls.c and dialog.c to util.c */ #include // Required by windows.h #include // Required by windows.h #include // Required by windows.h #include // Win32 base API's #include // Required by raseapif.h #include // For RTASSERT #include // For EAPLOGONINFO #include #include #include #include #define STRSAFE_NO_DEPRECATE #include #define SECURITY_WIN32 #include // For GetUserNameExA, CredHandle #include #include // For CredHandle #include // Required by sclogon.h #include // For SCardListReadersA #include // For ScHelperGetCertFromLogonInfo #include #include #include #include #include #include #define INCL_RASAUTHATTRIBUTES #include extern CRITICAL_SECTION g_csProtectCachedCredentials; extern BOOL g_fCriticalSectionInitialized; extern HANDLE g_hStoreChangeNotificationEvt; extern HANDLE g_hWaitonStoreChangeEvt; extern HCERTSTORE g_hLocalMachineStore; extern BOOL g_fChangeNotificationSetup; /* Returns: void Notes: Used for printing EAP TLS trace statements. */ VOID EapTlsTrace( IN CHAR* Format, ... ) { va_list arglist; RTASSERT(NULL != Format); va_start(arglist, Format); TraceVprintfExA(g_dwEapTlsTraceId, 0x00010000 | TRACE_USE_MASK | TRACE_USE_MSEC, Format, arglist); va_end(arglist); } #if WINVER > 0x0500 DWORD CheckCallerIdentity ( HANDLE hWVTStateData ) { DWORD dwRetCode = ERROR_ACCESS_DENIED; PCRYPT_PROVIDER_DATA pProvData = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL; PCRYPT_PROVIDER_SGNR pProvSigner = NULL; CERT_CHAIN_POLICY_PARA chainpolicyparams; CERT_CHAIN_POLICY_STATUS chainpolicystatus; if (!(pProvData = WTHelperProvDataFromStateData(hWVTStateData))) { goto done; } if (!(pProvSigner = WTHelperGetProvSignerFromChain(pProvData, 0, FALSE, 0))) { goto done; } chainpolicyparams.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); // // // We do want to test for microsoft test root flags. and dont care // for revocation flags... // chainpolicyparams.dwFlags = CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG | CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG | CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS; pChainContext = pProvSigner->pChainContext; if (!CertVerifyCertificateChainPolicy ( CERT_CHAIN_POLICY_MICROSOFT_ROOT, pChainContext, &chainpolicyparams, &chainpolicystatus)) { goto done; } else { if ( S_OK == chainpolicystatus.dwError ) { dwRetCode = NO_ERROR; } else { // // Check the base policy to see if this // is a Microsoft test root // if (!CertVerifyCertificateChainPolicy ( CERT_CHAIN_POLICY_BASE, pChainContext, &chainpolicyparams, &chainpolicystatus)) { goto done; } else { if ( S_OK == chainpolicystatus.dwError ) { dwRetCode = NO_ERROR; } } } } done: return dwRetCode; } DWORD SetupMachineChangeNotification () { DWORD dwRetCode = NO_ERROR; EapTlsTrace ("SetupMachineChangeNotification"); if ( g_fChangeNotificationSetup ) return dwRetCode; EnterCriticalSection ( &g_csProtectCachedCredentials ); if ( !g_fChangeNotificationSetup ) { // // Create and event and register the callback // to monitor the changes in the machine store // and handle cached credentials. // g_hStoreChangeNotificationEvt = CreateEvent(NULL, FALSE, FALSE, NULL); if ( NULL == g_hStoreChangeNotificationEvt ) { dwRetCode = GetLastError(); EapTlsTrace("Error creating Change notification event 0x%x",dwRetCode ); } else { //Register call back //and call certcontrolstore api. // if ( !RegisterWaitForSingleObject ( &(g_hWaitonStoreChangeEvt), g_hStoreChangeNotificationEvt , MachineStoreChangeNotification, NULL, INFINITE, WT_EXECUTEDEFAULT|WT_EXECUTELONGFUNCTION ) ) { dwRetCode = GetLastError(); EapTlsTrace("Error Registering Wait function. 0x%x", dwRetCode ); } else { // // Call cert control store api // // Open the "MY" certificate store. g_hLocalMachineStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, 0, (CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG), "MY"); if (NULL == g_hLocalMachineStore) { dwRetCode = GetLastError(); EapTlsTrace("CertOpenStore failed and returned 0x%x", dwRetCode); } else { if ( ! CertControlStore ( g_hLocalMachineStore, 0, CERT_STORE_CTRL_NOTIFY_CHANGE, &g_hStoreChangeNotificationEvt ) ) { dwRetCode = GetLastError(); EapTlsTrace ("CertControlStore failed and returned 0x%x",dwRetCode); } else { g_fChangeNotificationSetup = TRUE; } } } } } LeaveCriticalSection ( &g_csProtectCachedCredentials ); return dwRetCode; } DWORD g_dwVerifyCallerTrustLock = 0; /* */ DWORD VerifyCallerTrust ( void * callersAddress ) { DWORD dwRetCode = NO_ERROR; HRESULT hr = S_OK; WINTRUST_DATA wtData; WINTRUST_FILE_INFO wtFileInfo; WINTRUST_CATALOG_INFO wtCatalogInfo; BOOL fRet = FALSE; HCATADMIN hCATAdmin = NULL; static BOOL fOKToUseTLS = FALSE; GUID guidPublishedSoftware = WINTRUST_ACTION_GENERIC_VERIFY_V2; // // Following GUID is Mirosoft's Catalog System Root // GUID guidCatSystemRoot = { 0xf750e6c3, 0x38ee, 0x11d1,{ 0x85, 0xe5, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee } }; HCATINFO hCATInfo = NULL; CATALOG_INFO CatInfo; HANDLE hFile = INVALID_HANDLE_VALUE; BYTE bHash[40]; DWORD cbHash = 40; MEMORY_BASIC_INFORMATION mbi; SIZE_T nbyte; DWORD nchar; wchar_t callersModule[MAX_PATH + 1]; if ( fOKToUseTLS ) { goto done; } // // Allow only one thread to go and do this // while (InterlockedIncrement(&g_dwVerifyCallerTrustLock) > 1) { InterlockedDecrement(&g_dwVerifyCallerTrustLock); Sleep(500); } if ( fOKToUseTLS ) { goto decountanddone; } EapTlsTrace("Verifying caller..."); nbyte = VirtualQuery( callersAddress, &mbi, sizeof(mbi) ); if (nbyte < sizeof(mbi)) { dwRetCode = ERROR_ACCESS_DENIED; EapTlsTrace("Unauthorized use of TLS attempted"); goto decountanddone; } nchar = GetModuleFileNameW( (HMODULE)(mbi.AllocationBase), callersModule, MAX_PATH ); if (nchar == 0) { dwRetCode = GetLastError(); EapTlsTrace("Unauthorized use of TLS attempted"); goto decountanddone; } // // // Try and see if WinVerifyTrust will verify // the signature as a standalone file // // ZeroMemory ( &wtData, sizeof(wtData) ); ZeroMemory ( &wtFileInfo, sizeof(wtFileInfo) ); wtData.cbStruct = sizeof(wtData); wtData.dwUIChoice = WTD_UI_NONE; wtData.fdwRevocationChecks = WTD_REVOKE_NONE; wtData.dwStateAction = WTD_STATEACTION_VERIFY; wtData.dwUnionChoice = WTD_CHOICE_FILE; wtData.pFile = &wtFileInfo; wtFileInfo.cbStruct = sizeof( wtFileInfo ); wtFileInfo.pcwszFilePath = callersModule; hr = WinVerifyTrust ( NULL, &guidPublishedSoftware, &wtData ); if ( ERROR_SUCCESS == hr ) { // // Check to see if this is indeed microsoft // signed caller // dwRetCode = CheckCallerIdentity( wtData.hWVTStateData); wtData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(NULL, &guidPublishedSoftware, &wtData); fOKToUseTLS = TRUE; goto decountanddone; } wtData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(NULL, &guidPublishedSoftware, &wtData); // // We did not find the file was signed. // So check the system catalog to see if // the file is in the catalog and the catalog // is signed // // // Open the file // hFile = CreateFile ( callersModule, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( INVALID_HANDLE_VALUE == hFile ) { dwRetCode = GetLastError(); goto decountanddone; } fRet = CryptCATAdminAcquireContext( &hCATAdmin, &guidCatSystemRoot, 0 ); if ( !fRet ) { dwRetCode = GetLastError(); goto decountanddone; } // // Get the hash of the file here // fRet = CryptCATAdminCalcHashFromFileHandle ( hFile, &cbHash, bHash, 0 ); if ( !fRet ) { dwRetCode = GetLastError(); goto decountanddone; } ZeroMemory(&CatInfo, sizeof(CatInfo)); CatInfo.cbStruct = sizeof(CatInfo); ZeroMemory( &wtCatalogInfo, sizeof(wtCatalogInfo) ); wtData.dwUnionChoice = WTD_CHOICE_CATALOG; wtData.dwStateAction = WTD_STATEACTION_VERIFY; wtData.pCatalog = &wtCatalogInfo; wtCatalogInfo.cbStruct = sizeof(wtCatalogInfo); wtCatalogInfo.hMemberFile = hFile; wtCatalogInfo.pbCalculatedFileHash = bHash; wtCatalogInfo.cbCalculatedFileHash = cbHash; while ( ( hCATInfo = CryptCATAdminEnumCatalogFromHash ( hCATAdmin, bHash, cbHash, 0, &hCATInfo ) ) ) { if (!(CryptCATCatalogInfoFromContext(hCATInfo, &CatInfo, 0))) { // should do something (??) continue; } wtCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile; hr = WinVerifyTrust ( NULL, &guidPublishedSoftware, &wtData ); if ( ERROR_SUCCESS == hr ) { // // Verify that this file is trusted // dwRetCode = CheckCallerIdentity( wtData.hWVTStateData); wtData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(NULL, &guidPublishedSoftware, &wtData); fOKToUseTLS = TRUE; goto decountanddone; } } // // File not found in any of the catalogs // dwRetCode = ERROR_ACCESS_DENIED; decountanddone: InterlockedDecrement(&g_dwVerifyCallerTrustLock); done: if ( hCATInfo ) { CryptCATAdminReleaseCatalogContext( hCATAdmin, hCATInfo, 0 ); } if ( hCATAdmin ) { CryptCATAdminReleaseContext( hCATAdmin, 0 ); } if ( hFile ) { CloseHandle(hFile); } return dwRetCode; } #endif /* Returns: NO_ERROR: iff Success Notes: TraceRegister, RouterLogRegister, etc. */ DWORD g_EapTlsInitializeLock = 0; DWORD EapTlsInitialize2( IN BOOL fInitialize, IN BOOL fUI ) { static DWORD dwRefCount = 0; DWORD dwRetCode = NO_ERROR; // // This will make sure that only one thread is // in this function. Stolen from rasipcp/helper.c // while (InterlockedIncrement(&g_EapTlsInitializeLock) > 1) { InterlockedDecrement(&g_EapTlsInitializeLock); Sleep(1000); } if (fInitialize) { if (0 == dwRefCount) { InitializeCriticalSection( &g_csProtectCachedCredentials ); g_fCriticalSectionInitialized = TRUE; if (fUI) { g_dwEapTlsTraceId = TraceRegister(L"RASTLSUI"); // // Initialize the common controls library for the controls we use. // { INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_LISTVIEW_CLASSES; InitCommonControlsEx (&icc); } } else { g_dwEapTlsTraceId = TraceRegister(L"RASTLS"); } EapTlsTrace("EapTlsInitialize2"); } dwRefCount++; } else { dwRefCount--; if (0 == dwRefCount) { EapTlsTrace("EapTls[Un]Initialize2"); if ( !fUI ) { if ( g_fChangeNotificationSetup) { // // Call in to cleanup the change events. // if ( ! UnregisterWaitEx ( g_hWaitonStoreChangeEvt, INVALID_HANDLE_VALUE ) ) { dwRetCode = GetLastError(); EapTlsTrace("Error in UnregisterWaitEx 0x%x",dwRetCode ); } #if 0 if ( g_hWaitonStoreChangeEvt ) { CloseHandle(g_hWaitonStoreChangeEvt); g_hWaitonStoreChangeEvt = NULL; } #endif if ( g_hStoreChangeNotificationEvt ) { CloseHandle(g_hStoreChangeNotificationEvt); g_hStoreChangeNotificationEvt = NULL; } if (g_hLocalMachineStore) { CertCloseStore( g_hLocalMachineStore, CERT_CLOSE_STORE_CHECK_FLAG ); g_hLocalMachineStore = NULL; } g_fChangeNotificationSetup = FALSE; } } if (INVALID_TRACEID != g_dwEapTlsTraceId) { TraceDeregister(g_dwEapTlsTraceId); g_dwEapTlsTraceId = INVALID_TRACEID; } if ( g_fCriticalSectionInitialized ) { DeleteCriticalSection( &g_csProtectCachedCredentials ); g_fCriticalSectionInitialized = FALSE; } FreeScardDlgDll(); } } InterlockedDecrement(&g_EapTlsInitializeLock); return(dwRetCode); } /* Returns: NO_ERROR: iff Success Notes: */ DWORD EapTlsInitialize( IN BOOL fInitialize ) { return EapTlsInitialize2(fInitialize, FALSE /* fUI */); } /* Returns: Notes: Obfuscate PIN in place to foil memory scans for PINs. */ VOID EncodePin( IN EAPTLS_USER_PROPERTIES* pUserProp ) { UNICODE_STRING UnicodeString; UCHAR ucSeed = 0; RtlInitUnicodeString(&UnicodeString, pUserProp->pwszPin); RtlRunEncodeUnicodeString(&ucSeed, &UnicodeString); pUserProp->usLength = UnicodeString.Length; pUserProp->usMaximumLength = UnicodeString.MaximumLength; pUserProp->ucSeed = ucSeed; } /* Returns: Notes: */ VOID DecodePin( IN EAPTLS_USER_PROPERTIES* pUserProp ) { UNICODE_STRING UnicodeString; UnicodeString.Length = pUserProp->usLength; UnicodeString.MaximumLength = pUserProp->usMaximumLength; UnicodeString.Buffer = pUserProp->pwszPin; RtlRunDecodeUnicodeString(pUserProp->ucSeed, &UnicodeString); } /* Returns: TRUE: Success FALSE: Failure Notes: Converts FileTime to a printable form in *ppwszTime. If the function returns TRUE, the caller must ultimately call LocalFree(*ppwszTime). */ BOOL FFileTimeToStr( IN FILETIME FileTime, OUT WCHAR** ppwszTime ) { SYSTEMTIME SystemTime; FILETIME LocalTime; int nBytesDate; int nBytesTime; WCHAR* pwszTemp = NULL; BOOL fRet = FALSE; RTASSERT(NULL != ppwszTime); if (!FileTimeToLocalFileTime(&FileTime, &LocalTime)) { EapTlsTrace("FileTimeToLocalFileTime(%d %d) failed and returned %d", FileTime.dwLowDateTime, FileTime.dwHighDateTime, GetLastError()); goto LDone; } if (!FileTimeToSystemTime(&LocalTime, &SystemTime)) { EapTlsTrace("FileTimeToSystemTime(%d %d) failed and returned %d", LocalTime.dwLowDateTime, LocalTime.dwHighDateTime, GetLastError()); goto LDone; } nBytesDate = GetDateFormat(LOCALE_USER_DEFAULT, 0, &SystemTime, NULL, NULL, 0); if (0 == nBytesDate) { EapTlsTrace("GetDateFormat(%d %d %d %d %d %d %d %d) failed and " "returned %d", SystemTime.wYear, SystemTime.wMonth, SystemTime.wDayOfWeek, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds, GetLastError()); goto LDone; } nBytesTime = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &SystemTime, NULL, NULL, 0); if (0 == nBytesTime) { EapTlsTrace("GetTimeFormat(%d %d %d %d %d %d %d %d) failed and " "returned %d", SystemTime.wYear, SystemTime.wMonth, SystemTime.wDayOfWeek, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds, GetLastError()); goto LDone; } pwszTemp = LocalAlloc(LPTR, (nBytesDate + nBytesTime)*sizeof(WCHAR)); if (NULL == pwszTemp) { EapTlsTrace("LocalAlloc failed and returned %d", GetLastError()); goto LDone; } if (0 == GetDateFormat(LOCALE_USER_DEFAULT, 0, &SystemTime, NULL, pwszTemp, nBytesDate)) { EapTlsTrace("GetDateFormat(%d %d %d %d %d %d %d %d) failed and " "returned %d", SystemTime.wYear, SystemTime.wMonth, SystemTime.wDayOfWeek, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds, GetLastError()); goto LDone; } pwszTemp[nBytesDate - 1] = L' '; if (0 == GetTimeFormat(LOCALE_USER_DEFAULT, 0, &SystemTime, NULL, pwszTemp + nBytesDate, nBytesTime)) { EapTlsTrace("GetTimeFormat(%d %d %d %d %d %d %d %d) failed and " "returned %d", SystemTime.wYear, SystemTime.wMonth, SystemTime.wDayOfWeek, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds, GetLastError()); goto LDone; } *ppwszTime = pwszTemp; pwszTemp = NULL; fRet = TRUE; LDone: LocalFree(pwszTemp); return(fRet); } BOOL FFormatMachineIdentity1 ( LPWSTR lpszMachineNameRaw, LPWSTR * lppszMachineNameFormatted ) { BOOL fRetVal = FALSE; LPWSTR lpwszPrefix = L"host/"; RTASSERT(NULL != lpszMachineNameRaw ); RTASSERT(NULL != lppszMachineNameFormatted ); // // Prepend host/ to the UPN name // *lppszMachineNameFormatted = (LPWSTR)LocalAlloc ( LPTR, ( wcslen ( lpszMachineNameRaw ) + wcslen ( lpwszPrefix ) + 2 ) * sizeof(WCHAR) ); if ( NULL == *lppszMachineNameFormatted ) { goto done; } wcscpy( *lppszMachineNameFormatted, lpwszPrefix ); wcscat ( *lppszMachineNameFormatted, lpszMachineNameRaw ); fRetVal = TRUE; done: return fRetVal; } /* Returns: TRUE: Success FALSE: Failure Notes: Gets the machine name from the cert as a fully qualified path hostname/path for example, hostname.redmond.microsoft.com and reformats it in the domain\hostname format. */ BOOL FFormatMachineIdentity ( LPWSTR lpszMachineNameRaw, LPWSTR * lppszMachineNameFormatted ) { BOOL fRetVal = TRUE; LPTSTR s1 = lpszMachineNameRaw; LPTSTR s2 = NULL; RTASSERT(NULL != lpszMachineNameRaw ); RTASSERT(NULL != lppszMachineNameFormatted ); //Need to add 2 more chars. One for NULL and other for $ sign *lppszMachineNameFormatted = (LPTSTR )LocalAlloc ( LPTR, (wcslen(lpszMachineNameRaw) + 2)* sizeof(WCHAR) ); if ( NULL == *lppszMachineNameFormatted ) { return FALSE; } //find the first "." and that is the identity of the machine. //the second "." is the domain. //check to see if there at least 2 dots. If not the raw string is //the output string while ( *s1 ) { if ( *s1 == '.' ) { if ( !s2 ) //First dot s2 = s1; else //second dot break; } s1++; } //can perform several additional checks here if ( *s1 != '.' ) //there are no 2 dots so raw = formatted { wcscpy ( *lppszMachineNameFormatted, lpszMachineNameRaw ); goto done; } if ( s1-s2 < 2 ) { wcscpy ( *lppszMachineNameFormatted, lpszMachineNameRaw ); goto done; } memcpy ( *lppszMachineNameFormatted, s2+1, ( s1-s2-1) * sizeof(WCHAR)); memcpy ( (*lppszMachineNameFormatted) + (s1-s2-1) , L"\\", sizeof(WCHAR)); wcsncpy ( (*lppszMachineNameFormatted) + (s1-s2), lpszMachineNameRaw, s2-lpszMachineNameRaw ); done: //Append the $ sign no matter what... wcscat ( *lppszMachineNameFormatted, L"$" ); return fRetVal; } /* Returns: TRUE: Success FALSE: Failure Notes: Gets the name in the cert pointed to by pCertContext, and converts it to a printable form in *ppwszName. If the function returns TRUE, the caller must ultimately call LocalFree(*ppwszName). */ BOOL FUserCertToStr( IN PCCERT_CONTEXT pCertContext, OUT WCHAR** ppwszName ) { DWORD dwExtensionIndex; DWORD dwAltEntryIndex; CERT_EXTENSION* pCertExtension; CERT_ALT_NAME_INFO* pCertAltNameInfo; CERT_ALT_NAME_ENTRY* pCertAltNameEntry; CERT_NAME_VALUE* pCertNameValue; DWORD dwCertAltNameInfoSize; DWORD dwCertNameValueSize; WCHAR* pwszName = NULL; BOOL fExitOuterFor; BOOL fExitInnerFor; BOOL fRet = FALSE; // See if cert has UPN in AltSubjectName->otherName fExitOuterFor = FALSE; for (dwExtensionIndex = 0; dwExtensionIndex < pCertContext->pCertInfo->cExtension; dwExtensionIndex++) { pCertAltNameInfo = NULL; pCertExtension = pCertContext->pCertInfo->rgExtension+dwExtensionIndex; if (strcmp(pCertExtension->pszObjId, szOID_SUBJECT_ALT_NAME2) != 0) { goto LOuterForEnd; } dwCertAltNameInfoSize = 0; if (!CryptDecodeObjectEx( pCertContext->dwCertEncodingType, X509_ALTERNATE_NAME, pCertExtension->Value.pbData, pCertExtension->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (VOID*)&pCertAltNameInfo, &dwCertAltNameInfoSize)) { goto LOuterForEnd; } fExitInnerFor = FALSE; for (dwAltEntryIndex = 0; dwAltEntryIndex < pCertAltNameInfo->cAltEntry; dwAltEntryIndex++) { pCertNameValue = NULL; pCertAltNameEntry = pCertAltNameInfo->rgAltEntry + dwAltEntryIndex; if ( (CERT_ALT_NAME_OTHER_NAME != pCertAltNameEntry->dwAltNameChoice) || (NULL == pCertAltNameEntry->pOtherName) || (0 != strcmp(szOID_NT_PRINCIPAL_NAME, pCertAltNameEntry->pOtherName->pszObjId))) { goto LInnerForEnd; } // We found a UPN! dwCertNameValueSize = 0; if (!CryptDecodeObjectEx( pCertContext->dwCertEncodingType, X509_UNICODE_ANY_STRING, pCertAltNameEntry->pOtherName->Value.pbData, pCertAltNameEntry->pOtherName->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (VOID*)&pCertNameValue, &dwCertNameValueSize)) { goto LInnerForEnd; } // One extra char for the terminating NULL. pwszName = LocalAlloc(LPTR, pCertNameValue->Value.cbData + sizeof(WCHAR)); if (NULL == pwszName) { EapTlsTrace("LocalAlloc failed and returned %d", GetLastError()); fExitInnerFor = TRUE; fExitOuterFor = TRUE; goto LInnerForEnd; } CopyMemory(pwszName, pCertNameValue->Value.pbData, pCertNameValue->Value.cbData); *ppwszName = pwszName; pwszName = NULL; fRet = TRUE; fExitInnerFor = TRUE; fExitOuterFor = TRUE; LInnerForEnd: LocalFree(pCertNameValue); if (fExitInnerFor) { break; } } LOuterForEnd: LocalFree(pCertAltNameInfo); if (fExitOuterFor) { break; } } LocalFree(pwszName); return(fRet); } /* Returns: TRUE: Success FALSE: Failure Notes: Gets the name in the cert pointed to by pCertContext, and converts it to a printable form in *ppwszName. If the function returns TRUE, the caller must ultimately call LocalFree(*ppwszName). */ BOOL FOtherCertToStr( IN PCCERT_CONTEXT pCertContext, IN DWORD fFlags, OUT WCHAR** ppwszName ) { WCHAR* pwszTemp = NULL; DWORD dwSize; BOOL fRet = FALSE; DWORD dwType = 0; RTASSERT(NULL != ppwszName); dwType = CERT_NAME_SIMPLE_DISPLAY_TYPE; dwSize = CertGetNameString(pCertContext,dwType , fFlags, NULL, NULL, 0); // dwSize is the number of characters, including the terminating NULL. if (dwSize <= 1) { EapTlsTrace("CertGetNameString for CERT_NAME_SIMPLE_DISPLAY_TYPE failed."); goto LDone; } pwszTemp = LocalAlloc(LPTR, dwSize*sizeof(WCHAR)); if (NULL == pwszTemp) { EapTlsTrace("LocalAlloc failed and returned %d", GetLastError()); goto LDone; } dwSize = CertGetNameString(pCertContext, dwType, fFlags, NULL, pwszTemp, dwSize); if (dwSize <= 1) { EapTlsTrace("CertGetNameString failed."); goto LDone; } *ppwszName = pwszTemp; pwszTemp = NULL; fRet = TRUE; LDone: LocalFree(pwszTemp); return(fRet); } /* Returns: TRUE: Success FALSE: Failure Notes: Special function for getting the DNS machine name from the machine auth certificate */ BOOL FMachineAuthCertToStr ( IN PCCERT_CONTEXT pCertContext, OUT WCHAR ** ppwszName ) { DWORD dwExtensionIndex; DWORD dwAltEntryIndex; CERT_EXTENSION* pCertExtension; CERT_ALT_NAME_INFO* pCertAltNameInfo; CERT_ALT_NAME_ENTRY* pCertAltNameEntry; DWORD dwCertAltNameInfoSize; WCHAR* pwszName = NULL; BOOL fExitOuterFor; BOOL fExitInnerFor; BOOL fRet = FALSE; // See if cert has UPN in AltSubjectName->otherName fExitOuterFor = FALSE; for (dwExtensionIndex = 0; dwExtensionIndex < pCertContext->pCertInfo->cExtension; dwExtensionIndex++) { pCertAltNameInfo = NULL; pCertExtension = pCertContext->pCertInfo->rgExtension+dwExtensionIndex; if (strcmp(pCertExtension->pszObjId, szOID_SUBJECT_ALT_NAME2) != 0) { goto LOuterForEnd; } dwCertAltNameInfoSize = 0; if (!CryptDecodeObjectEx( pCertContext->dwCertEncodingType, X509_ALTERNATE_NAME, pCertExtension->Value.pbData, pCertExtension->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (VOID*)&pCertAltNameInfo, &dwCertAltNameInfoSize)) { goto LOuterForEnd; } fExitInnerFor = FALSE; for (dwAltEntryIndex = 0; dwAltEntryIndex < pCertAltNameInfo->cAltEntry; dwAltEntryIndex++) { pCertAltNameEntry = pCertAltNameInfo->rgAltEntry + dwAltEntryIndex; if ( (CERT_ALT_NAME_DNS_NAME != pCertAltNameEntry->dwAltNameChoice) || (NULL == pCertAltNameEntry->pwszDNSName) ) { goto LInnerForEnd; } // We found the DNS Name! // One extra char for the terminating NULL. pwszName = LocalAlloc(LPTR, wcslen( pCertAltNameEntry->pwszDNSName ) * sizeof(WCHAR) + sizeof(WCHAR)); if (NULL == pwszName) { EapTlsTrace("LocalAlloc failed and returned %d", GetLastError()); fExitInnerFor = TRUE; fExitOuterFor = TRUE; goto LInnerForEnd; } wcscpy (pwszName, pCertAltNameEntry->pwszDNSName ); *ppwszName = pwszName; pwszName = NULL; fRet = TRUE; fExitInnerFor = TRUE; fExitOuterFor = TRUE; LInnerForEnd: if (fExitInnerFor) { break; } } LOuterForEnd: LocalFree(pCertAltNameInfo); if (fExitOuterFor) { break; } } LocalFree(pwszName); return(fRet); } /* Returns: TRUE: Success FALSE: Failure Notes: Gets the name in the cert pointed to by pCertContext, and converts it to a printable form in *ppwszName. If the function returns TRUE, the caller must ultimately call LocalFree(*ppwszName). */ BOOL FCertToStr( IN PCCERT_CONTEXT pCertContext, IN DWORD fFlags, IN BOOL fMachineCert, OUT WCHAR** ppwszName ) { if (!fMachineCert) { if (FUserCertToStr(pCertContext, ppwszName)) { return(TRUE); } } return(FOtherCertToStr(pCertContext, fFlags, ppwszName)); } #if 0 BOOL FGetIssuerOrSubject ( IN PCCERT_CONTEXT pCertContext, IN DWORD dwFlags, OUT WCHAR ** ppszNameString ) { BOOL fRet = TRUE; DWORD cbNameString =0; LPWSTR lpwszNameString = NULL; // // Get the issued to field here // cbNameString = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, NULL, lpwszNameString, 0 ); if ( 0 == cbNameString ) { EapTlsTrace("Name String Item not found"); fRet = FALSE; goto LDone; } lpwszNameString = (LPWSTR)LocalAlloc(LPTR, cbNameString ); if ( NULL == lpwszNameString ) { EapTlsTrace("Error allocing memory for name string"); fRet = FALSE; goto LDone; } cbNameString = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, NULL, lpwszNameString, cbNameString ); *ppszNameString = lpwszNameString; lpwszNameString = NULL; LDone: LocalFree(lpwszNameString); return fRet; } #endif /* Returns: TRUE: Success FALSE: Failure Notes: Stores the friendly name of the cert pointed to by pCertContext in *ppwszName. If the function returns TRUE, the caller must ultimately call LocalFree(*ppwszName). */ BOOL FGetFriendlyName( IN PCCERT_CONTEXT pCertContext, OUT WCHAR** ppwszName ) { WCHAR* pwszName = NULL; DWORD dwBytes; BOOL fRet = FALSE; RTASSERT(NULL != ppwszName); if (!CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, NULL, &dwBytes)) { // If there is no Friendly Name property, don't print an error stmt. goto LDone; } pwszName = LocalAlloc(LPTR, dwBytes); if (NULL == pwszName) { EapTlsTrace("LocalAlloc failed and returned %d", GetLastError()); goto LDone; } if (!CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, pwszName, &dwBytes)) { EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", GetLastError()); goto LDone; } *ppwszName = pwszName; pwszName = NULL; fRet = TRUE; LDone: LocalFree(pwszName); return(fRet); } /* Returns: TRUE iff there is a smart card reader installed. Notes: This function was provided by Doug Barlow. If 0 is used as the SCARDCONTEXT parameter, it just looks in the registry for defined readers. This will return a list of all readers ever installed on the system. To actually detect the current state of the system, we have to use a valid SCARDCONTEXT handle. */ BOOL FSmartCardReaderInstalled( VOID ) { LONG lErr; DWORD dwLen = 0; SCARDCONTEXT hCtx = 0; BOOL fReturn = FALSE; lErr = SCardListReadersA(0, NULL, NULL, &dwLen); fReturn = ( (NO_ERROR == lErr) && (2 * sizeof(CHAR) < dwLen)); if (!fReturn) { goto LDone; } fReturn = FALSE; lErr = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, &hCtx); if (SCARD_S_SUCCESS != lErr) { goto LDone; } lErr = SCardListReadersA(hCtx, NULL, NULL, &dwLen); fReturn = ( (NO_ERROR == lErr) && (2 * sizeof(CHAR) < dwLen)); LDone: if (0 != hCtx) { SCardReleaseContext(hCtx); } return(fReturn); } //Get EKU Usage Blob out of the certificate Context DWORD DwGetEKUUsage ( IN PCCERT_CONTEXT pCertContext, OUT PCERT_ENHKEY_USAGE * ppUsage ) { DWORD dwBytes = 0; DWORD dwErr = ERROR_SUCCESS; PCERT_ENHKEY_USAGE pUsage = NULL; EapTlsTrace("FGetEKUUsage"); if (!CertGetEnhancedKeyUsage(pCertContext, 0, NULL, &dwBytes)) { dwErr = GetLastError(); if (CRYPT_E_NOT_FOUND == dwErr) { EapTlsTrace("No usage in cert"); goto LDone; } EapTlsTrace("FGetEKUUsage failed and returned 0x%x", dwErr); goto LDone; } pUsage = LocalAlloc(LPTR, dwBytes); if (NULL == pUsage) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } if (!CertGetEnhancedKeyUsage(pCertContext, 0, pUsage, &dwBytes)) { dwErr = GetLastError(); EapTlsTrace("FGetEKUUsage failed and returned 0x%x", dwErr); goto LDone; } *ppUsage = pUsage; LDone: return dwErr; } /* * This functionw will check to see if the registry based cert is * a smart card cert and if the context can be opened in silent * mode. */ BOOL FCheckSCardCertAndCanOpenSilentContext ( IN PCCERT_CONTEXT pCertContext ) { PCERT_ENHKEY_USAGE pUsageInternal = NULL; BOOL fRet = TRUE; DWORD dwIndex = 0; CRYPT_KEY_PROV_INFO * pCryptKeyProvInfo = NULL; HCRYPTPROV hProv = 0; DWORD dwParam = 0; DWORD dwDataLen = 0; #if 0 // // This is not required anymore. We use CertFindChainInStore // which will make sure if private key exists... // HCRYPTPROV hProv1 = 0; #endif EapTlsTrace("FCheckSCardCertAndCanOpenSilentContext"); if ( DwGetEKUUsage ( pCertContext, &pUsageInternal) != ERROR_SUCCESS ) { goto LDone; } for (dwIndex = 0; dwIndex < pUsageInternal->cUsageIdentifier; dwIndex++) { if ( !strcmp(pUsageInternal->rgpszUsageIdentifier[dwIndex], szOID_KP_SMARTCARD_LOGON)) { EapTlsTrace("Found SCard Cert in registey. Skipping..."); goto LDone; } } // //there is no scard logon oid in the cert //So, now check to see if the csp is mixed mode // if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwDataLen)) { EapTlsTrace("CertGetCertificateContextProperty failed: 0x%x", GetLastError()); goto LDone; } pCryptKeyProvInfo = LocalAlloc(LPTR, dwDataLen); if (NULL == pCryptKeyProvInfo) { EapTlsTrace("Out of memory: 0x%x", GetLastError()); goto LDone; } if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pCryptKeyProvInfo, &dwDataLen)) { EapTlsTrace("CertGetCertificateContextProperty failed: 0x%x", GetLastError()); goto LDone; } EapTlsTrace( "Acquiring Context for Container Name: %ws, ProvName: %ws, ProvType 0x%x", pCryptKeyProvInfo->pwszContainerName, pCryptKeyProvInfo->pwszProvName, pCryptKeyProvInfo->dwProvType ); if (!CryptAcquireContext( &hProv, pCryptKeyProvInfo->pwszContainerName, pCryptKeyProvInfo->pwszProvName, pCryptKeyProvInfo->dwProvType, (pCryptKeyProvInfo->dwFlags & ~CERT_SET_KEY_PROV_HANDLE_PROP_ID) | CRYPT_SILENT)) { DWORD dwErr = GetLastError(); /* if ( SCARD_E_NO_SMARTCARD == dwErr ) { //This CSP requires a smart card do this is a smart //card cert in registry fRet = TRUE; } */ EapTlsTrace("CryptAcquireContext failed. This CSP cannot be opened in silent mode. skipping cert.Err: 0x%x", dwErr); goto LDone; } dwDataLen = sizeof(dwParam); if ( !CryptGetProvParam ( hProv, PP_IMPTYPE, (BYTE *)&dwParam, &dwDataLen, 0 )) { EapTlsTrace("CryptGetProvParam failed: 0x%x", GetLastError()); goto LDone; } //now check to see if CSP is MIXED if ( ( dwParam & (CRYPT_IMPL_MIXED | CRYPT_IMPL_REMOVABLE) ) == (CRYPT_IMPL_MIXED | CRYPT_IMPL_REMOVABLE) ) { EapTlsTrace("Found SCard Cert in registey. Skipping..."); goto LDone; } #if 0 // // This is not required anymore. We use CertFindChainInStore // which will make sure that private key exists... // // // Check to see if we have the private // key corresponding to this cert // if not drop this cert. if (!CryptAcquireCertificatePrivateKey( pCertContext, CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_SILENT, NULL, &hProv1, NULL, NULL )) { EapTlsTrace("Found a certificate without private key. Skipping. Error 0x%x",GetLastError()); goto LDone; } CryptReleaseContext(hProv1, 0); #endif fRet = FALSE; LDone: if ( pUsageInternal ) LocalFree(pUsageInternal); if ( pCryptKeyProvInfo ) LocalFree(pCryptKeyProvInfo); if (0 != hProv) { CryptReleaseContext(hProv, 0); } return fRet; } /* Add selected certs to the */ VOID AddCertNodeToSelList ( EAPTLS_HASH * pHash, DWORD dwNumHashes, EAPTLS_CERT_NODE * pNode, EAPTLS_CERT_NODE ** ppSelCertList, //This is an array of pointers DWORD * pdwNextSelCert ) { DWORD dw = 0; DWORD dw1 = 0; RTASSERT(NULL != pNode); EapTlsTrace("Add Selected Cert to List"); //No selected certificates if ( 0 == dwNumHashes || !ppSelCertList ) goto done; while ( dw < dwNumHashes ) { if (!memcmp(&(pNode->Hash), (pHash+ dw), sizeof(EAPTLS_HASH))) { // //check to see if the node's already in the list. //iff not then add it. Looks like there is some //problem with possible dup certs in the cert store // while ( dw1 < *pdwNextSelCert ) { if ( ! memcmp( &(*(ppSelCertList+dw1))->Hash, &(pNode->Hash), sizeof(EAPTLS_HASH) ) ) { //This is a dup node in mmc. So Skip it... goto done; } dw1++; } *( ppSelCertList + *pdwNextSelCert ) = pNode; *pdwNextSelCert = *pdwNextSelCert + 1; break; } dw++; } done: return; } /* Returns: TRUE if no enhanced key usages exist, or pCertContext has the szOID_PKIX_KP_SERVER_AUTH or szOID_PKIX_KP_CLIENT_AUTH usage depending on whether fMachine is TRUE or FALSE. Notes: */ BOOL FCheckUsage( IN PCCERT_CONTEXT pCertContext, IN PCERT_ENHKEY_USAGE pUsage, IN BOOL fMachine ) { DWORD dwIndex; DWORD dwErr; BOOL fRet = FALSE; PCERT_ENHKEY_USAGE pUsageInternal = pUsage; EapTlsTrace("FCheckUsage"); if ( NULL == pUsageInternal ) { dwErr = DwGetEKUUsage ( pCertContext, &pUsageInternal); if ( dwErr != ERROR_SUCCESS ) goto LDone; } for (dwIndex = 0; dwIndex < pUsageInternal->cUsageIdentifier; dwIndex++) { if ( ( fMachine && !strcmp(pUsageInternal->rgpszUsageIdentifier[dwIndex], szOID_PKIX_KP_SERVER_AUTH)) || ( !fMachine && !strcmp(pUsageInternal->rgpszUsageIdentifier[dwIndex], szOID_PKIX_KP_CLIENT_AUTH))) { fRet = TRUE; break; } } LDone: if ( NULL == pUsage ) { if ( pUsageInternal ) LocalFree(pUsageInternal); } return(fRet); } DWORD DwCheckCertPolicy ( IN PCCERT_CONTEXT pCertContext, OUT PCCERT_CHAIN_CONTEXT * ppCertChainContext ) { DWORD dwRetCode = ERROR_SUCCESS; LPSTR lpszEnhUsage = szOID_PKIX_KP_CLIENT_AUTH; CERT_CHAIN_PARA ChainPara; CERT_ENHKEY_USAGE EnhKeyUsage; CERT_USAGE_MATCH CertUsage; PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_POLICY_PARA PolicyPara; CERT_CHAIN_POLICY_STATUS PolicyStatus; EapTlsTrace("FCheckPolicy"); *ppCertChainContext = NULL; ZeroMemory ( &ChainPara, sizeof(ChainPara) ); ZeroMemory ( &EnhKeyUsage, sizeof(EnhKeyUsage) ); ZeroMemory ( &CertUsage, sizeof(CertUsage) ); EnhKeyUsage.rgpszUsageIdentifier = &lpszEnhUsage; EnhKeyUsage.cUsageIdentifier = 1; EnhKeyUsage.rgpszUsageIdentifier = &lpszEnhUsage; CertUsage.dwType = USAGE_MATCH_TYPE_AND; CertUsage.Usage = EnhKeyUsage; ChainPara.cbSize = sizeof(CERT_CHAIN_PARA); ChainPara.RequestedUsage = CertUsage; if(!CertGetCertificateChain( NULL, pCertContext, NULL, pCertContext->hCertStore, &ChainPara, 0, NULL, &pChainContext)) { dwRetCode = GetLastError(); EapTlsTrace("CertGetCertificateChain failed and returned 0x%x", dwRetCode ); pChainContext = NULL; goto LDone; } ZeroMemory( &PolicyPara, sizeof(PolicyPara) ); PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.dwFlags = BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG; ZeroMemory( &PolicyStatus, sizeof(PolicyStatus) ); // // The chain already has verified the policy. // Chain context will have several bits set. // To get one error out of it, call CErtVerifyCertificateChainPolicy // if ( !CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_NT_AUTH, pChainContext, &PolicyPara, &PolicyStatus ) ) { dwRetCode = GetLastError(); EapTlsTrace( "CertVerifyCertificateChainPolicy failed. Continuing with root hash matching" "GetLastError = 0x%x.", dwRetCode); } else { // //Check to see if the policy status is good. If so, //there is no need to check for connectoid hashes any more... // if ( PolicyStatus.dwError != 0 ) { dwRetCode = PolicyStatus.dwError; EapTlsTrace( "CertVerifyCertificateChainPolicy succeeded but policy check failed 0x%x." , dwRetCode ); } else { *ppCertChainContext = pChainContext; } } LDone: if ( dwRetCode != ERROR_SUCCESS && pChainContext ) { CertFreeCertificateChain ( pChainContext ); } EapTlsTrace("FCheckPolicy done."); return dwRetCode; } /* Returns: TRUE iff the certificate is time valid. Notes: */ BOOL FCheckTimeValidity ( IN PCCERT_CONTEXT pCertContext ) { BOOL fRet = FALSE; SYSTEMTIME SysTime; FILETIME FileTime; EapTlsTrace("FCheckTimeValidity"); GetSystemTime(&SysTime); if ( !SystemTimeToFileTime ( &SysTime, &FileTime ) ) { EapTlsTrace ("Error converting from system time to file time %ld", GetLastError()); goto done; } if ( CertVerifyTimeValidity ( &FileTime, pCertContext->pCertInfo ) ) { //should return a 0 if the certificate is time valid. EapTlsTrace ( "Non Time Valid Certificate was encountered"); goto done; } fRet = TRUE; done: return fRet; } /* Returns: TRUE iff the CSP is Microsoft RSA SChannel Cryptographic Provider. Notes: */ BOOL FCheckCSP( IN PCCERT_CONTEXT pCertContext ) { DWORD dwBytes; CRYPT_KEY_PROV_INFO* pCryptKeyProvInfo = NULL; BOOL fRet = FALSE; EapTlsTrace("FCheckCSP"); if (!CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwBytes)) { EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", GetLastError()); goto LDone; } pCryptKeyProvInfo = LocalAlloc(LPTR, dwBytes); if (NULL == pCryptKeyProvInfo) { EapTlsTrace("LocalAlloc failed and returned %d", GetLastError()); goto LDone; } if (!CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pCryptKeyProvInfo, &dwBytes)) { EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", GetLastError()); goto LDone; } fRet = (PROV_RSA_SCHANNEL == pCryptKeyProvInfo->dwProvType); if ( !fRet ) { EapTlsTrace("Did not find a cert with a provider RSA_SCHANNEL or RSA_FULL"); } LDone: LocalFree(pCryptKeyProvInfo); return(fRet); } /* Returns: NO_ERROR: iff Success Notes: Gets the root cert hash of the cert represented by pCertContextServer. */ DWORD GetRootCertHashAndNameVerifyChain( IN PCERT_CONTEXT pCertContextServer, OUT EAPTLS_HASH* pHash, OUT WCHAR** ppwszName, IN BOOL fVerifyGP, OUT BOOL * pfRootCheckRequired ) { PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_PARA ChainPara; PCERT_SIMPLE_CHAIN pSimpleChain; PCCERT_CONTEXT pCurrentCert; DWORD dwIndex; BOOL fRootCertFound = FALSE; WCHAR* pwszName = NULL; DWORD dwErr = NO_ERROR; CERT_CHAIN_POLICY_PARA PolicyPara; CERT_CHAIN_POLICY_STATUS PolicyStatus; ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); *pfRootCheckRequired = TRUE; if(!CertGetCertificateChain( NULL, pCertContextServer, NULL, pCertContextServer->hCertStore, &ChainPara, 0, NULL, &pChainContext)) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateChain failed and returned 0x%x", dwErr); pChainContext = NULL; goto LDone; } //Get the hash and root cert name etc anyways... pSimpleChain = pChainContext->rgpChain[0]; for (dwIndex = 0; dwIndex < pSimpleChain->cElement; dwIndex++) { pCurrentCert = pSimpleChain->rgpElement[dwIndex]->pCertContext; if (CertCompareCertificateName(pCurrentCert->dwCertEncodingType, &pCurrentCert->pCertInfo->Issuer, &pCurrentCert->pCertInfo->Subject)) { fRootCertFound = TRUE; break; } } if (!fRootCertFound) { dwErr = ERROR_NOT_FOUND; goto LDone; } pHash->cbHash = MAX_HASH_SIZE; if (!CertGetCertificateContextProperty(pCurrentCert, CERT_HASH_PROP_ID, pHash->pbHash, &(pHash->cbHash))) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", dwErr); goto LDone; } if (!FCertToStr(pCurrentCert, 0, TRUE, &pwszName)) { dwErr = E_FAIL; goto LDone; } *ppwszName = pwszName; pwszName = NULL; if ( fVerifyGP ) { EapTlsTrace( "Checking against the NTAuth store to verify the certificate chain."); ZeroMemory( &PolicyPara, sizeof(PolicyPara) ); PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.dwFlags = BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG; ZeroMemory( &PolicyStatus, sizeof(PolicyStatus) ); //Authnticate against the NTAuth store and see if all's cool. if ( !CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_NT_AUTH, pChainContext, &PolicyPara, &PolicyStatus ) ) { EapTlsTrace( "CertVerifyCertificateChainPolicy failed. Continuing with root hash matching" "GetLastError = 0x%x.", GetLastError()); } else { // //Check to see if the policy status is good. If so, //there is no need to check for connectoid hashes any more... // if ( PolicyStatus.dwError != 0 ) { EapTlsTrace( "CertVerifyCertificateChainPolicy succeeded but returned 0x%x." "Continuing with root hash matching.", PolicyStatus.dwError); } else { *pfRootCheckRequired = FALSE; } } } LDone: if (pChainContext) { CertFreeCertificateChain(pChainContext); } LocalFree(pwszName); return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: Opens the EAP-TLS registry key, and returns the result in *phKeyEapTls. If the function returns NO_ERROR, the caller must ultimately call RegCloseKey(*phKeyEapTls). */ DWORD OpenEapTlsRegistryKey( IN WCHAR* pwszMachineName, IN REGSAM samDesired, OUT HKEY* phKeyEapTls ) { HKEY hKeyLocalMachine = NULL; BOOL fHKeyLocalMachineOpened = FALSE; BOOL fHKeyEapTlsOpened = FALSE; LONG lRet; DWORD dwErr = NO_ERROR; RTASSERT(NULL != phKeyEapTls); lRet = RegConnectRegistry(pwszMachineName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegConnectRegistry(%ws) failed and returned %d", pwszMachineName ? pwszMachineName : L"NULL", dwErr); goto LDone; } fHKeyLocalMachineOpened = TRUE; lRet = RegOpenKeyEx(hKeyLocalMachine, EAPTLS_KEY_13, 0, samDesired, phKeyEapTls); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegOpenKeyEx(%ws) failed and returned %d", EAPTLS_KEY_13, dwErr); goto LDone; } fHKeyEapTlsOpened = TRUE; LDone: if ( fHKeyEapTlsOpened && (ERROR_SUCCESS != dwErr)) { RegCloseKey(*phKeyEapTls); } if (fHKeyLocalMachineOpened) { RegCloseKey(hKeyLocalMachine); } return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: Reads/writes the server's config data. If fRead is TRUE, and the function returns NO_ERROR, LocalFree(*ppUserProp) must be called. */ DWORD ServerConfigDataIO( IN BOOL fRead, IN WCHAR* pwszMachineName, IN OUT BYTE** ppData, IN DWORD dwNumBytes ) { HKEY hKeyEapTls; EAPTLS_USER_PROPERTIES* pUserProp; BOOL fHKeyEapTlsOpened = FALSE; BYTE* pData = NULL; DWORD dwSize = 0; LONG lRet; DWORD dwType; DWORD dwErr = NO_ERROR; RTASSERT(NULL != ppData); dwErr = OpenEapTlsRegistryKey(pwszMachineName, fRead ? KEY_READ : KEY_WRITE, &hKeyEapTls); if (ERROR_SUCCESS != dwErr) { goto LDone; } fHKeyEapTlsOpened = TRUE; if (fRead) { lRet = RegQueryValueEx(hKeyEapTls, EAPTLS_VAL_SERVER_CONFIG_DATA, NULL, &dwType, NULL, &dwSize); if ( (ERROR_SUCCESS != lRet) || (REG_BINARY != dwType) || (sizeof(EAPTLS_USER_PROPERTIES) != dwSize)) { pData = LocalAlloc(LPTR, sizeof(EAPTLS_USER_PROPERTIES)); if (NULL == pData) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pUserProp = (EAPTLS_USER_PROPERTIES*)pData; pUserProp->dwVersion = 0; } else { pData = LocalAlloc(LPTR, dwSize); if (NULL == pData) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } lRet = RegQueryValueEx(hKeyEapTls, EAPTLS_VAL_SERVER_CONFIG_DATA, NULL, &dwType, pData, &dwSize); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegQueryValueEx(%ws) failed and returned %d", EAPTLS_VAL_SERVER_CONFIG_DATA, dwErr); goto LDone; } } pUserProp = (EAPTLS_USER_PROPERTIES*)pData; pUserProp->dwSize = sizeof(EAPTLS_USER_PROPERTIES); pUserProp->awszString[0] = 0; *ppData = pData; pData = NULL; } else { lRet = RegSetValueEx(hKeyEapTls, EAPTLS_VAL_SERVER_CONFIG_DATA, 0, REG_BINARY, *ppData, dwNumBytes); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegSetValueEx(%ws) failed and returned %d", EAPTLS_VAL_SERVER_CONFIG_DATA, dwErr); goto LDone; } } LDone: if (fHKeyEapTlsOpened) { RegCloseKey(hKeyEapTls); } LocalFree(pData); return(dwErr); } /* Returns: VOID Notes: */ VOID FreeCertList( IN EAPTLS_CERT_NODE* pNode ) { while (NULL != pNode) { LocalFree(pNode->pwszDisplayName); LocalFree(pNode->pwszFriendlyName); LocalFree(pNode->pwszIssuer); LocalFree(pNode->pwszExpiration); pNode = pNode->pNext; } } /* Returns: VOID Notes: Creates a linked list of certs from the pwszStoreName store. This list is created in *ppCertList. *ppCert is made to point to the cert whose hash is the same as the hash in *pHash. The linked list must eventually be freed by calling FreeCertList. */ VOID CreateCertList( IN BOOL fServer, IN BOOL fRouter, IN BOOL fRoot, OUT EAPTLS_CERT_NODE** ppCertList, OUT EAPTLS_CERT_NODE** ppCert, //This is an array of pointers... IN DWORD dwNumHashes, IN EAPTLS_HASH* pHash, //This is an array of hashes... IN WCHAR* pwszStoreName ) { HCERTSTORE hCertStore = NULL; EAPTLS_CERT_NODE* pCertList = NULL; EAPTLS_CERT_NODE* pCert = NULL; EAPTLS_CERT_NODE* pLastNode = NULL; PCCERT_CONTEXT pCertContext; BOOL fExitWhile; EAPTLS_CERT_NODE* pNode; DWORD dwErr = NO_ERROR; DWORD dwNextSelCert = 0; PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_FIND_BY_ISSUER_PARA FindPara; RTASSERT(NULL != ppCertList); hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, 0, CERT_STORE_READONLY_FLAG | ((fServer || fRouter) ? CERT_SYSTEM_STORE_LOCAL_MACHINE : CERT_SYSTEM_STORE_CURRENT_USER), pwszStoreName); if (NULL == hCertStore) { dwErr = GetLastError(); EapTlsTrace("CertOpenSystemStore failed and returned 0x%x", dwErr); goto LDone; } //Changed from fRoot||fServer to fRoot only. if (fRoot) { pCertList = LocalAlloc(LPTR, sizeof(EAPTLS_CERT_NODE)); if (NULL == pCertList) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pLastNode = pCertList; } fExitWhile = FALSE; pCertContext = NULL; ZeroMemory ( &FindPara, sizeof(FindPara) ); FindPara.cbSize = sizeof(FindPara); FindPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH; while (!fExitWhile) { dwErr = NO_ERROR; pNode = NULL; /* The returned pointer is freed when passed as the pPrevCertContext on a subsequent call. Otherwise, the pointer must be freed by calling CertFreeCertificateContext. A pPrevCertContext that is not NULL is always freed by this function (through a call to CertFreeCertificateContext), even for an error. */ if ( fRoot || fRouter || fServer ) { pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext); if (NULL == pCertContext) { fExitWhile = TRUE; goto LWhileEnd; } if ( !fRoot && !FCheckUsage(pCertContext, NULL, fServer)) { goto LWhileEnd; } } else { // // Use CertFindChainInStore to get to the certificate // pChainContext = CertFindChainInStore ( hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_CHAIN_FIND_BY_ISSUER, &FindPara, pChainContext ); if ( NULL == pChainContext ) { fExitWhile = TRUE; goto LWhileEnd; } //Set the cert context to appropriate value pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; } // //Skip if it is smart card cached certificate //or we cannot open this csp in silent mode //This is done iff it is not root certs and server // if ( !fRoot && !fServer && FCheckSCardCertAndCanOpenSilentContext ( pCertContext ) ) { goto LWhileEnd; } if ( !FCheckTimeValidity(pCertContext ) ) { goto LWhileEnd; } if ( fServer && !FCheckCSP(pCertContext)) { goto LWhileEnd; } pNode = LocalAlloc(LPTR, sizeof(EAPTLS_CERT_NODE)); if (NULL == pNode) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); fExitWhile = TRUE; goto LWhileEnd; } FGetFriendlyName(pCertContext, &(pNode->pwszFriendlyName)); // // If there is no UPN name, this cert will be skipped here // if ( !FCertToStr(pCertContext, 0, fServer || fRouter, &(pNode->pwszDisplayName)) || !FCertToStr(pCertContext, CERT_NAME_ISSUER_FLAG, TRUE, &(pNode->pwszIssuer)) || !FFileTimeToStr(pCertContext->pCertInfo->NotAfter, &(pNode->pwszExpiration))) { goto LWhileEnd; } pNode->Hash.cbHash = MAX_HASH_SIZE; if (!CertGetCertificateContextProperty( pCertContext, CERT_HASH_PROP_ID, pNode->Hash.pbHash, &(pNode->Hash.cbHash))) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", dwErr); fExitWhile = TRUE; goto LWhileEnd; } #if 0 // This is not being used anywhere. So dont worry about it. // // Get Issuer and subject information // FGetIssuerOrSubject ( pCertContext, 0, &(pNode->pwszIssuedTo) ); FGetIssuerOrSubject ( pCertContext, CERT_NAME_ISSUER_FLAG, &(pNode->pwszIssuedBy) ); #endif // // Finally copy the issued date into the structure // CopyMemory( &pNode->IssueDate, &pCertContext->pCertInfo->NotBefore, sizeof(FILETIME) ); if (NULL == pLastNode) { pCertList = pLastNode = pNode; } else { pLastNode->pNext = pNode; pLastNode = pNode; } //Check if the hash for current node is in the list //that has been passed to us AddCertNodeToSelList ( pHash, dwNumHashes, pNode, ppCert, //This is an array of pointers &dwNextSelCert ); pNode = NULL; LWhileEnd: if (NULL != pNode) { LocalFree(pNode->pwszDisplayName); LocalFree(pNode->pwszFriendlyName); LocalFree(pNode->pwszIssuer); LocalFree(pNode->pwszExpiration); LocalFree(pNode); pNode = NULL; } if ( fRoot || fRouter ) { if ( fExitWhile && (NULL != pCertContext)) { CertFreeCertificateContext(pCertContext); // Always returns TRUE; } } else { if ( fExitWhile && ( NULL != pChainContext ) ) { CertFreeCertificateChain(pChainContext); } } } // If we couldn't find an appropriate default cert, make the first // cert (if there is one) the default if (NULL == pCert) { pCert = pCertList; } LDone: if (NO_ERROR != dwErr) { FreeCertList(pCertList); } else { *ppCertList = pCertList; } if (NULL != hCertStore) { if (!CertCloseStore(hCertStore, 0)) { EapTlsTrace("CertCloseStore failed and returned 0x%x", GetLastError()); } } } DWORD GetLocalMachineName ( OUT WCHAR ** ppLocalMachineName ) { DWORD dwRetCode = NO_ERROR; WCHAR * pLocalMachineName = NULL; DWORD dwLocalMachineNameLen = 0; if ( !GetComputerNameEx ( ComputerNameDnsFullyQualified, pLocalMachineName, &dwLocalMachineNameLen ) ) { dwRetCode = GetLastError(); if ( ERROR_MORE_DATA != dwRetCode ) goto LDone; dwRetCode = NO_ERROR; } pLocalMachineName = (WCHAR *)LocalAlloc( LPTR, (dwLocalMachineNameLen * sizeof(WCHAR)) + sizeof(WCHAR) ); if ( NULL == pLocalMachineName ) { dwRetCode = GetLastError(); goto LDone; } if ( !GetComputerNameEx ( ComputerNameDnsFullyQualified, pLocalMachineName, &dwLocalMachineNameLen ) ) { dwRetCode = GetLastError(); goto LDone; } *ppLocalMachineName = pLocalMachineName; pLocalMachineName = NULL; LDone: LocalFree(pLocalMachineName); return dwRetCode; } /* Returns: NO_ERROR: iff Success Notes: If this function returns NO_ERROR, CertFreeCertificateContext(*ppCertContext) must be called. */ DWORD GetDefaultClientMachineCert( IN HCERTSTORE hCertStore, OUT PCCERT_CONTEXT* ppCertContext ) { CTL_USAGE CtlUsage; CHAR* szUsageIdentifier; PCCERT_CONTEXT pCertContext = NULL; EAPTLS_HASH FirstCertHash; //This is the hash of first cert //with client auth found in the store PCCERT_CONTEXT pCertContextPrev = NULL; //Previous context in the search EAPTLS_HASH SelectedCertHash; //Hash of the certificate last selected FILETIME SelectedCertNotBefore; //Not Before date of last selected EAPTLS_HASH TempHash; //Scratch variable WCHAR * pwszIdentity = NULL; //Machine Name in the cert WCHAR * pLocalMachineName = NULL; //Local Machine Name DWORD dwErr = NO_ERROR; BOOL fGotIdentity; CRYPT_HASH_BLOB HashBlob; EapTlsTrace("GetDefaultClientMachineCert"); RTASSERT(NULL != ppCertContext); ZeroMemory( &SelectedCertHash, sizeof(SelectedCertHash) ); ZeroMemory( &SelectedCertNotBefore, sizeof(SelectedCertNotBefore) ); *ppCertContext = NULL; dwErr = GetLocalMachineName ( &pLocalMachineName ); if ( NO_ERROR != dwErr ) { EapTlsTrace("Error getting LocalMachine Name 0x%x", dwErr); goto LDone; } szUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH; CtlUsage.cUsageIdentifier = 1; CtlUsage.rgpszUsageIdentifier = &szUsageIdentifier; pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_ENHKEY_USAGE, &CtlUsage, NULL); if ( NULL == pCertContext ) { dwErr = GetLastError(); EapTlsTrace("CertFindCertificateInStore failed and returned 0x%x", dwErr); if ( CRYPT_E_NOT_FOUND == dwErr ) { dwErr = ERROR_NO_EAPTLS_CERTIFICATE; } goto LDone; } FirstCertHash.cbHash = MAX_HASH_SIZE; // //Store the hash of first cert. In case we dont find any cert that exactly matches //the filtering, we need to use this. if (!CertGetCertificateContextProperty( pCertContext, CERT_HASH_PROP_ID, FirstCertHash.pbHash, &(FirstCertHash.cbHash) ) ) { dwErr = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x", dwErr); goto LDone; } do { //Check time validity of the cert. if ( !FCheckTimeValidity( pCertContext) ) { //cert expired. So skip it EapTlsTrace("Found expired Cert. Skipping this cert."); goto LWhileEnd; } fGotIdentity = FALSE; // //Get the subject Alt Name // if ( FMachineAuthCertToStr(pCertContext, &pwszIdentity) ) { fGotIdentity = TRUE; } else { EapTlsTrace("Could not get identity from subject alt name."); if ( FCertToStr(pCertContext, 0, TRUE, &pwszIdentity)) { fGotIdentity = TRUE; } } if ( fGotIdentity ) { // //Check to see if this is the same identity as this machine // if ( !_wcsicmp ( pwszIdentity, pLocalMachineName ) ) { // //Store the hash of cert. TempHash.cbHash = MAX_HASH_SIZE; if (!CertGetCertificateContextProperty( pCertContext, CERT_HASH_PROP_ID, TempHash.pbHash, &(TempHash.cbHash) ) ) { EapTlsTrace("CertGetCertificateContextProperty failed and " "returned 0x%x. Skipping this certificate", GetLastError()); goto LWhileEnd; } // //Got a cert so if there is already a cert selected, //compare the file time of this cert with the one //already selected. If this is more recent, use this //one. // if ( SelectedCertHash.cbHash ) { if ( CompareFileTime( &SelectedCertNotBefore, &(pCertContext->pCertInfo->NotBefore) ) < 0 ) { //Got a newer cert so replace the old cert with new one CopyMemory ( &SelectedCertHash, &TempHash, sizeof(SelectedCertHash) ); CopyMemory ( &SelectedCertNotBefore, &(pCertContext->pCertInfo->NotBefore), sizeof( SelectedCertNotBefore ) ); } } else { // //This is the first cert. So copy over the hash and //file time. CopyMemory ( &SelectedCertHash, &TempHash, sizeof(SelectedCertHash) ); CopyMemory ( &SelectedCertNotBefore, &(pCertContext->pCertInfo->NotBefore), sizeof( SelectedCertNotBefore ) ); } } else { EapTlsTrace("Could not get identity from the cert. skipping this cert."); } } else { EapTlsTrace("Could not get identity from the cert. skipping this cert."); } LWhileEnd: pCertContextPrev = pCertContext; if ( pwszIdentity ) { LocalFree ( pwszIdentity ); pwszIdentity = NULL; } //Get the next certificate. pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_ENHKEY_USAGE, &CtlUsage, pCertContextPrev ); }while ( pCertContext ); // //Now that we have enumerated all the certs, //check to see if we have a selected cert. If no selected //cert is present, send back the first cert. // if ( SelectedCertHash.cbHash ) { EapTlsTrace("Found Machine Cert based on machinename, client auth, time validity."); HashBlob.cbData = SelectedCertHash.cbHash; HashBlob.pbData = SelectedCertHash.pbHash; } else { EapTlsTrace("Did not find Machine Cert based on the given machinename, client auth, time validity. Using the first cert with Client Auth OID."); HashBlob.cbData = FirstCertHash.cbHash; HashBlob.pbData = FirstCertHash.pbHash; } *ppCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_HASH, &HashBlob, NULL); if (NULL == *ppCertContext) { dwErr = GetLastError(); EapTlsTrace("CertFindCertificateInStore failed with 0x%x.", dwErr); if ( CRYPT_E_NOT_FOUND == dwErr ) { dwErr = ERROR_NO_EAPTLS_CERTIFICATE; } } LDone: LocalFree (pLocalMachineName); LocalFree (pwszIdentity); if ( NO_ERROR != dwErr ) { if ( pCertContext ) { CertFreeCertificateContext( pCertContext ); } } EapTlsTrace("GetDefaultClientMachineCert done."); return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: If this function returns NO_ERROR, CertFreeCertificateContext(*ppCertContext) must be called. */ DWORD GetDefaultMachineCert( IN HCERTSTORE hCertStore, OUT PCCERT_CONTEXT* ppCertContext ) { CTL_USAGE CtlUsage; CHAR* szUsageIdentifier; PCCERT_CONTEXT pCertContext; DWORD dwErr = NO_ERROR; EapTlsTrace("GetDefaultMachineCert"); RTASSERT(NULL != ppCertContext); *ppCertContext = NULL; szUsageIdentifier = szOID_PKIX_KP_SERVER_AUTH; CtlUsage.cUsageIdentifier = 1; CtlUsage.rgpszUsageIdentifier = &szUsageIdentifier; pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_ENHKEY_USAGE, &CtlUsage, NULL); if (NULL == pCertContext) { dwErr = GetLastError(); EapTlsTrace("CertFindCertificateInStore failed and returned 0x%x", dwErr); goto LDone; } *ppCertContext = pCertContext; LDone: return(dwErr); } /* Returns: Notes: Stolen from \private\windows\gina\msgina\wlsec.c */ VOID RevealPassword( IN UNICODE_STRING* pHiddenPassword ) { SECURITY_SEED_AND_LENGTH* SeedAndLength; UCHAR Seed; SeedAndLength = (SECURITY_SEED_AND_LENGTH*)&pHiddenPassword->Length; Seed = SeedAndLength->Seed; SeedAndLength->Seed = 0; RtlRunDecodeUnicodeString(Seed, pHiddenPassword); } DWORD GetMBytePIN ( WCHAR * pwszPIN, CHAR ** ppszPIN ) { DWORD count = 0; CHAR * pszPin = NULL; DWORD dwErr = NO_ERROR; count = WideCharToMultiByte( CP_UTF8, 0, pwszPIN, -1, NULL, 0, NULL, NULL); if (0 == count) { dwErr = GetLastError(); EapTlsTrace("WideCharToMultiByte failed: %d", dwErr); goto LDone; } pszPin = LocalAlloc(LPTR, count); if (NULL == pszPin) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed: 0x%x", dwErr); goto LDone; } count = WideCharToMultiByte( CP_UTF8, 0, pwszPIN, -1, pszPin, count, NULL, NULL); if (0 == count) { dwErr = GetLastError(); EapTlsTrace("WideCharToMultiByte failed: %d", dwErr); goto LDone; } *ppszPIN = pszPin; LDone: if ( NO_ERROR != dwErr ) { if ( pszPin ) LocalFree(pszPin); } return dwErr; } /* Returns: NO_ERROR: iff Success Notes: */ DWORD GetCertFromLogonInfo( IN BYTE* pUserDataIn, IN DWORD dwSizeOfUserDataIn, OUT PCCERT_CONTEXT* ppCertContext ) { EAPLOGONINFO* pEapLogonInfo = (EAPLOGONINFO*)pUserDataIn; BYTE* pbLogonInfo = NULL; BYTE* pbPinInfo; WCHAR* wszPassword = NULL; CHAR* pszPIN = NULL; //Multibyte Version of the PIN UNICODE_STRING UnicodeString; PCCERT_CONTEXT pCertContext = NULL; BOOL fInitialized = FALSE; NTSTATUS Status; DWORD dwErr = NO_ERROR; CERT_KEY_CONTEXT stckContext; DWORD cbstckContext= sizeof(CERT_KEY_CONTEXT); EapTlsTrace("GetCertFromLogonInfo"); RTASSERT(NULL != ppCertContext); *ppCertContext = NULL; if ( 0 == pEapLogonInfo->dwLogonInfoSize || 0 == pEapLogonInfo->dwPINInfoSize || 0 == dwSizeOfUserDataIn) { dwErr = E_FAIL; goto LDone; } pbLogonInfo = LocalAlloc(LPTR, pEapLogonInfo->dwLogonInfoSize); if (NULL == pbLogonInfo) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pbLogonInfo, (BYTE*)pEapLogonInfo + pEapLogonInfo->dwOffsetLogonInfo, pEapLogonInfo->dwLogonInfoSize); pbPinInfo = (BYTE*)pEapLogonInfo + pEapLogonInfo->dwOffsetPINInfo; wszPassword = LocalAlloc(LPTR, pEapLogonInfo->dwPINInfoSize); if (NULL == wszPassword) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(wszPassword, pbPinInfo + 2 * sizeof(USHORT), pEapLogonInfo->dwPINInfoSize - 2 * sizeof(USHORT)); UnicodeString.Length = *((USHORT*)pbPinInfo); UnicodeString.MaximumLength = *((USHORT*)pbPinInfo + 1); UnicodeString.Buffer = wszPassword; Status = ScHelperInitializeContext(pbLogonInfo, pEapLogonInfo->dwLogonInfoSize); if (STATUS_SUCCESS != Status) { dwErr = Status; EapTlsTrace("ScHelperInitializeContext failed and returned 0x%x", dwErr); goto LDone; } fInitialized = TRUE; RevealPassword(&UnicodeString); Status = ScHelperGetCertFromLogonInfo(pbLogonInfo, &UnicodeString, &pCertContext); if (STATUS_SUCCESS != Status) { dwErr = Status; EapTlsTrace("ScHelperGetCertFromLogonInfo failed and returned 0x%x", dwErr); goto LDone; } //BUGID: 260728 - ScHelperGetCertFromLogonInfo does not associate the PIN // with certificate context. Hence the following lines of code are needed // to do the needful. if ( ! CertGetCertificateContextProperty ( pCertContext, CERT_KEY_CONTEXT_PROP_ID, &stckContext, &cbstckContext ) ) { dwErr = Status = GetLastError(); EapTlsTrace ("CertGetCertificateContextProperty failed and returned 0x%x", dwErr ); goto LDone; } dwErr = GetMBytePIN ( wszPassword, &pszPIN ); if ( dwErr != NO_ERROR ) { goto LDone; } if (!CryptSetProvParam( stckContext.hCryptProv, PP_KEYEXCHANGE_PIN, pszPIN, 0)) { dwErr = GetLastError(); EapTlsTrace("CryptSetProvParam failed: 0x%x", dwErr); ZeroMemory(pszPIN, strlen(pszPIN)); goto LDone; } // Zero the entire allocated buffer. ZeroMemory(wszPassword, pEapLogonInfo->dwPINInfoSize); ZeroMemory(pszPIN, strlen(pszPIN)); *ppCertContext = pCertContext; pCertContext = NULL; LDone: if (fInitialized) { ScHelperRelease(pbLogonInfo); } if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); // Always returns TRUE; } LocalFree(wszPassword); LocalFree(pbLogonInfo); if ( pszPIN ) LocalFree ( pszPIN ); return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: If this function returns TRUE, LocalFree(*ppwszIdentity) must be called. */ DWORD GetIdentityFromLogonInfo( IN BYTE* pUserDataIn, IN DWORD dwSizeOfUserDataIn, OUT WCHAR** ppwszIdentity ) { WCHAR* pwszIdentity = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwErr = NO_ERROR; RTASSERT(NULL != pUserDataIn); RTASSERT(NULL != ppwszIdentity); *ppwszIdentity = NULL; dwErr = GetCertFromLogonInfo(pUserDataIn, dwSizeOfUserDataIn, &pCertContext); if (NO_ERROR != dwErr) { goto LDone; } if (FCertToStr(pCertContext, 0, FALSE, &pwszIdentity)) { EapTlsTrace("(logon info) The name in the certificate is: %ws", pwszIdentity); *ppwszIdentity = pwszIdentity; pwszIdentity = NULL; } LDone: LocalFree(pwszIdentity); if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); // Always returns TRUE; } return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: There are two types of structures that can come in: 1. Version 0 structure which comes in as a a part of CM profile created on w2k or as a part of a connectoid that gor upgraded from w2k We change the data structure to new v1 data structure here 2. Get a version 1 structure and it is all cool. Note that the first x bytes of version 1 data structure are exactly the same as version 0. */ DWORD ReadConnectionData( IN BOOL fWireless, IN BYTE* pConnectionDataIn, IN DWORD dwSizeOfConnectionDataIn, OUT EAPTLS_CONN_PROPERTIES** ppConnProp ) { DWORD dwErr = NO_ERROR; EAPTLS_CONN_PROPERTIES* pConnProp = NULL; EAPTLS_CONN_PROPERTIES* pConnPropv1 = NULL; RTASSERT(NULL != ppConnProp); if ( dwSizeOfConnectionDataIn < sizeof(EAPTLS_CONN_PROPERTIES) ) { pConnProp = LocalAlloc(LPTR, sizeof(EAPTLS_CONN_PROPERTIES) + sizeof(EAPTLS_CONN_PROPERTIES_V1_EXTRA)); if (NULL == pConnProp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } //This is a new structure pConnProp->dwVersion = 2; pConnProp->dwSize = sizeof(EAPTLS_CONN_PROPERTIES); if ( fWireless ) { // // Set the defaults appropriately // pConnProp->fFlags |= EAPTLS_CONN_FLAG_REGISTRY; pConnProp->fFlags |= EAPTLS_CONN_FLAG_SIMPLE_CERT_SEL; pConnProp->fFlags |= EAPTLS_CONN_FLAG_NO_VALIDATE_NAME; } } else { RTASSERT(NULL != pConnectionDataIn); // //Check to see if this is a version 0 structure //If it is a version 0 structure then we migrate it to version1 // pConnProp = LocalAlloc(LPTR, dwSizeOfConnectionDataIn); if (NULL == pConnProp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } // If the user has mucked with the phonebook, we mustn't be affected. // The size must be correct. CopyMemory(pConnProp, pConnectionDataIn, dwSizeOfConnectionDataIn); pConnProp->dwSize = dwSizeOfConnectionDataIn; // // The Unicode string must be NULL terminated. // /* ((BYTE*)pConnProp)[dwSizeOfConnectionDataIn - 2] = 0; ((BYTE*)pConnProp)[dwSizeOfConnectionDataIn - 1] = 0; */ pConnPropv1 = LocalAlloc(LPTR, dwSizeOfConnectionDataIn + sizeof(EAPTLS_CONN_PROPERTIES_V1_EXTRA) ); if ( NULL == pConnPropv1 ) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed while allocating v1 structure and returned %d", dwErr ); goto LDone; } CopyMemory ( pConnPropv1, pConnProp, dwSizeOfConnectionDataIn); // //Check to see if the original struct has hash set // /* if ( pConnProp->Hash.cbHash ) { ConnPropSetNumHashes( pConnPropv1, 1 ); } */ if ( 2 != pConnPropv1->dwVersion ) { if ( pConnPropv1->fFlags & EAPTLS_CONN_FLAG_REGISTRY ) { pConnPropv1->fFlags |= EAPTLS_CONN_FLAG_SIMPLE_CERT_SEL; } pConnPropv1->dwVersion = 2; } LocalFree ( pConnProp ); pConnProp = pConnPropv1; pConnPropv1 = NULL; } *ppConnProp = pConnProp; pConnProp = NULL; LDone: LocalFree(pConnProp); LocalFree(pConnPropv1); return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: */ DWORD ReadUserData( IN BYTE* pUserDataIn, IN DWORD dwSizeOfUserDataIn, OUT EAPTLS_USER_PROPERTIES** ppUserProp ) { DWORD dwErr = NO_ERROR; EAPTLS_USER_PROPERTIES* pUserProp = NULL; RTASSERT(NULL != ppUserProp); if (dwSizeOfUserDataIn < sizeof(EAPTLS_USER_PROPERTIES)) { pUserProp = LocalAlloc(LPTR, sizeof(EAPTLS_USER_PROPERTIES)); if (NULL == pUserProp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pUserProp->dwVersion = 0; pUserProp->dwSize = sizeof(EAPTLS_USER_PROPERTIES); pUserProp->pwszDiffUser = pUserProp->awszString; pUserProp->dwPinOffset = 0; pUserProp->pwszPin = pUserProp->awszString + pUserProp->dwPinOffset; } else { RTASSERT(NULL != pUserDataIn); pUserProp = LocalAlloc(LPTR, dwSizeOfUserDataIn); if (NULL == pUserProp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pUserProp, pUserDataIn, dwSizeOfUserDataIn); // If someone has mucked with the registry, we mustn't // be affected. pUserProp->dwSize = dwSizeOfUserDataIn; pUserProp->pwszDiffUser = pUserProp->awszString; pUserProp->pwszPin = pUserProp->awszString + pUserProp->dwPinOffset; } *ppUserProp = pUserProp; pUserProp = NULL; LDone: LocalFree(pUserProp); return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: */ DWORD AllocUserDataWithNewIdentity( IN EAPTLS_USER_PROPERTIES* pUserProp, IN WCHAR* pwszIdentity, OUT EAPTLS_USER_PROPERTIES** ppUserProp ) { DWORD dwNumChars; EAPTLS_USER_PROPERTIES* pUserPropTemp = NULL; DWORD dwSize; DWORD dwErr = NO_ERROR; *ppUserProp = NULL; dwNumChars = wcslen(pwszIdentity); dwSize = sizeof(EAPTLS_USER_PROPERTIES) + (dwNumChars + wcslen(pUserProp->pwszPin) + 1) * sizeof(WCHAR); pUserPropTemp = LocalAlloc(LPTR, dwSize); if (NULL == pUserPropTemp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pUserPropTemp, pUserProp, sizeof(EAPTLS_USER_PROPERTIES)); pUserPropTemp->dwSize = dwSize; pUserPropTemp->pwszDiffUser = pUserPropTemp->awszString; wcscpy(pUserPropTemp->pwszDiffUser, pwszIdentity); pUserPropTemp->dwPinOffset = dwNumChars + 1; pUserPropTemp->pwszPin = pUserPropTemp->awszString + pUserPropTemp->dwPinOffset; wcscpy(pUserPropTemp->pwszPin, pUserProp->pwszPin); *ppUserProp = pUserPropTemp; pUserPropTemp = NULL; ZeroMemory(pUserProp, pUserProp->dwSize); LDone: LocalFree(pUserPropTemp); return(dwErr); } /* Returns: NO_ERROR: iff Success Notes: */ DWORD AllocUserDataWithNewPin( IN EAPTLS_USER_PROPERTIES* pUserProp, IN PBYTE pbPin, IN DWORD cbPin, OUT EAPTLS_USER_PROPERTIES** ppUserProp ) { DWORD dwNumChars; EAPTLS_USER_PROPERTIES* pUserPropTemp = NULL; DWORD dwSize; DWORD dwErr = NO_ERROR; *ppUserProp = NULL; dwNumChars = wcslen(pUserProp->pwszDiffUser); dwSize = sizeof(EAPTLS_USER_PROPERTIES) + (dwNumChars + 1 ) * sizeof(WCHAR) + cbPin; pUserPropTemp = LocalAlloc(LPTR, dwSize); if (NULL == pUserPropTemp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pUserPropTemp, pUserProp, sizeof(EAPTLS_USER_PROPERTIES)); pUserPropTemp->dwSize = dwSize; pUserPropTemp->pwszDiffUser = pUserPropTemp->awszString; wcscpy(pUserPropTemp->pwszDiffUser, pUserProp->pwszDiffUser); pUserPropTemp->dwPinOffset = dwNumChars + 1; pUserPropTemp->pwszPin = pUserPropTemp->awszString + pUserPropTemp->dwPinOffset; CopyMemory(pUserPropTemp->pwszPin, pbPin, cbPin); *ppUserProp = pUserPropTemp; pUserPropTemp = NULL; RtlSecureZeroMemory(pUserProp, pUserProp->dwSize); LDone: LocalFree(pUserPropTemp); return(dwErr); } /* Returns: Notes: String resource message loader routine. Returns the address of a string corresponding to string resource dwStringId or NULL if error. It is caller's responsibility to LocalFree the returned string. */ WCHAR* WszFromId( IN HINSTANCE hInstance, IN DWORD dwStringId ) { WCHAR* wszBuf = NULL; int cchBuf = 256; int cchGot; for (;;) { wszBuf = LocalAlloc(LPTR, cchBuf * sizeof(WCHAR)); if (NULL == wszBuf) { break; } /* LoadString wants to deal with character-counts rather than byte-counts...weird. Oh, and if you're thinking I could FindResource then SizeofResource to figure out the string size, be advised it doesn't work. From perusing the LoadString source, it appears the RT_STRING resource type requests a segment of 16 strings not an individual string. */ cchGot = LoadStringW(hInstance, (UINT)dwStringId, wszBuf, cchBuf); if (cchGot < cchBuf - 1) { // Good, got the whole string. break; } // Uh oh, LoadStringW filled the buffer entirely which could mean the // string was truncated. Try again with a larger buffer to be sure it // wasn't. LocalFree(wszBuf); cchBuf += 256; } return(wszBuf); } /* Following functions are required around the messy CONN_PROP structure to support v1/v0 etc. This is really bad. All the functions assume that version 1.0 format is passed in. */ EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED * ConnPropGetExtraPointer (EAPTLS_CONN_PROPERTIES * pConnProp) { return (EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED *) ( pConnProp->awszServerName + wcslen(pConnProp->awszServerName) + 1); } DWORD ConnPropGetNumHashes(EAPTLS_CONN_PROPERTIES * pConnProp ) { EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED * pExtra = ConnPropGetExtraPointer(pConnProp); return pExtra->dwNumHashes; } void ConnPropSetNumHashes(EAPTLS_CONN_PROPERTIES * pConnProp, DWORD dwNumHashes ) { EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED * pExtra = ConnPropGetExtraPointer(pConnProp); pExtra->dwNumHashes = dwNumHashes; return; } DWORD ConnPropGetV1Struct ( EAPTLS_CONN_PROPERTIES * pConnProp, EAPTLS_CONN_PROPERTIES_V1 ** ppConnPropv1 ) { DWORD dwRetCode = NO_ERROR; EAPTLS_CONN_PROPERTIES_V1 * pConnPropv1 = NULL; EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED * pExtra = ConnPropGetExtraPointer(pConnProp); // //This function assumes that the struct that comes in is at //version 1. Which means at least sizeof(EAPTLS_CONN_PROPERTIES) + //EAPTLS_CONN_PROPERTIES_V1_EXTRA in size. // // //First get the amount of memory required to be allocated // pConnPropv1 = LocalAlloc ( LPTR, sizeof( EAPTLS_CONN_PROPERTIES_V1 ) + //sizeof the basic struct pExtra->dwNumHashes * sizeof( EAPTLS_HASH ) + //num hashes wcslen( pConnProp->awszServerName ) * sizeof(WCHAR) + sizeof(WCHAR)//sizeof the string ); if ( NULL == pConnPropv1 ) { dwRetCode = GetLastError(); goto LDone; } // //Convert the structure // if ( pConnProp->dwVersion <= 1 ) pConnPropv1->dwVersion = 1; else pConnPropv1->dwVersion = 2; pConnPropv1->dwSize = sizeof( EAPTLS_CONN_PROPERTIES_V1 ) + pExtra->dwNumHashes * sizeof( EAPTLS_HASH ) + wcslen( pConnProp->awszServerName ) * sizeof(WCHAR) + sizeof(WCHAR); pConnPropv1->fFlags = pConnProp->fFlags; pConnPropv1->dwNumHashes = pExtra->dwNumHashes; if ( pExtra->dwNumHashes ) { CopyMemory( pConnPropv1->bData, &(pConnProp->Hash), sizeof(EAPTLS_HASH) ); if ( pExtra->dwNumHashes >1 ) { CopyMemory ( pConnPropv1->bData + sizeof(EAPTLS_HASH), pExtra->bData, (pExtra->dwNumHashes -1 ) * sizeof(EAPTLS_HASH) ); } } //Copy the server name wcscpy( (WCHAR *)( pConnPropv1->bData + (pExtra->dwNumHashes * sizeof(EAPTLS_HASH) ) ), pConnProp->awszServerName ); *ppConnPropv1 = pConnPropv1; pConnPropv1 = NULL; LDone: LocalFree(pConnPropv1); return dwRetCode; } DWORD ConnPropGetV0Struct ( EAPTLS_CONN_PROPERTIES_V1 * pConnPropv1, EAPTLS_CONN_PROPERTIES ** ppConnProp ) { DWORD dwRetCode = NO_ERROR; EAPTLS_CONN_PROPERTIES * pConnProp = NULL; DWORD dwSize = 0; EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED * pExtrav1 = NULL; // //First calulate the amount of memory to allocate // dwSize = sizeof(EAPTLS_CONN_PROPERTIES) + (pConnPropv1->dwNumHashes?( pConnPropv1->dwNumHashes - 1 ) * sizeof(EAPTLS_HASH):0) + ( wcslen( (LPWSTR) (pConnPropv1->bData + (pConnPropv1->dwNumHashes * sizeof(EAPTLS_HASH)) ) ) * sizeof(WCHAR) ) + sizeof(WCHAR); pConnProp = LocalAlloc ( LPTR, dwSize ); if ( NULL == pConnProp ) { dwRetCode = GetLastError(); goto LDone; } if ( pConnPropv1->dwVersion <= 1 ) pConnProp->dwVersion = 1; else pConnProp->dwVersion = 2; pConnProp->dwSize = dwSize; pConnProp->fFlags = pConnPropv1->fFlags; if ( pConnPropv1->dwNumHashes > 0 ) { CopyMemory( &(pConnProp->Hash), pConnPropv1->bData, sizeof(EAPTLS_HASH)); } if ( pConnPropv1->bData ) { wcscpy ( pConnProp->awszServerName, (LPWSTR )(pConnPropv1->bData + sizeof( EAPTLS_HASH ) * pConnPropv1->dwNumHashes) ); } pExtrav1 = (EAPTLS_CONN_PROPERTIES_V1_EXTRA UNALIGNED *)(pConnProp->awszServerName + wcslen( pConnProp->awszServerName) + 1); pExtrav1->dwNumHashes = pConnPropv1->dwNumHashes; if ( pExtrav1->dwNumHashes > 1 ) { CopyMemory( pExtrav1->bData, pConnPropv1->bData + sizeof(EAPTLS_HASH), ( pConnPropv1->dwNumHashes - 1 ) * sizeof(EAPTLS_HASH) ); } *ppConnProp = pConnProp; pConnProp = NULL; LDone: LocalFree(pConnProp); return dwRetCode; } void ShowCertDetails ( HWND hWnd, HCERTSTORE hStore, PCCERT_CONTEXT pCertContext) { CRYPTUI_VIEWCERTIFICATE_STRUCT vcs; BOOL fPropertiesChanged = FALSE; ZeroMemory (&vcs, sizeof (vcs)); vcs.dwSize = sizeof (vcs); vcs.hwndParent = hWnd; vcs.pCertContext = pCertContext; vcs.cStores = 1; vcs.rghStores = &hStore; vcs.dwFlags |= (CRYPTUI_DISABLE_EDITPROPERTIES|CRYPTUI_DISABLE_ADDTOSTORE); CryptUIDlgViewCertificate (&vcs, &fPropertiesChanged); return; } #if 0 // Location of policy parameters #define cwszEAPOLPolicyParams L"Software\\Policies\\Microsoft\\Windows\\Network Connections\\8021X" #define cszCARootHash "8021XCARootHash" #define SIZE_OF_CA_CONV_STR 3 #define SIZE_OF_HASH 20 // // ReadGPCARootHashes // // Description: // // Function to read parameters created by policy downloads // Currently, 8021XCARootHash will be downloaded to the HKLM // // Arguments: // pdwSizeOfRootHashBlob - Size of hash blob in bytes. Each root CA hash // will be of SIZE_OF_HASH bytes // ppbRootHashBlob - Pointer to hash blob. Caller should free it using // LocalFree // // Return values: // ERROR_SUCCESS - success // !ERROR_SUCCESS - error // DWORD ReadGPCARootHashes( DWORD *pdwSizeOfRootHashBlob, PBYTE *ppbRootHashBlob ) { HKEY hKey = NULL; DWORD dwType = 0; DWORD dwSize = 0; CHAR *pszCARootHash = NULL; DWORD i = 0; CHAR cszCharConv[SIZE_OF_CA_CONV_STR]; BYTE *pbRootHashBlob = NULL; DWORD dwSizeOfHashBlob = 0; LONG lError = ERROR_SUCCESS; lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, cwszEAPOLPolicyParams, 0, KEY_READ, &hKey ); if (lError != ERROR_SUCCESS) { EapTlsTrace("ReadCARootHashes: RegOpenKeyEx failed with error %ld", lError); goto LDone; } lError = RegQueryValueExA( hKey, cszCARootHash, 0, &dwType, NULL, &dwSize ); if (lError == ERROR_SUCCESS) { // Each SHA1 hash will be 2*SIZE_OF_HASH chars // Each BYTE in the hash will be represented by 2 CHARs, // 1 for each nibble // The hashblob should contain an integral number of hashes if ((dwSize-1*sizeof(CHAR))%(2*SIZE_OF_HASH*sizeof(CHAR))) { EapTlsTrace("ReadCARootHashes: Invalid hash length (%ld)", dwSize); goto LDone; } pszCARootHash = (CHAR *)LocalAlloc(LPTR, dwSize); if (pszCARootHash == NULL) { lError = GetLastError(); EapTlsTrace("ReadCARootHashes: LocalAlloc failed for pwszCARootHash"); goto LDone; } lError = RegQueryValueExA( hKey, cszCARootHash, 0, &dwType, (BYTE *)pszCARootHash, &dwSize ); if (lError != ERROR_SUCCESS) { EapTlsTrace("ReadCARootHashes: RegQueryValueEx 2 failed with error (%ld)", lError); goto LDone; } dwSizeOfHashBlob = (dwSize-1*sizeof(CHAR))/(2*sizeof(CHAR)); if ((pbRootHashBlob = LocalAlloc ( LPTR, dwSizeOfHashBlob*sizeof(BYTE))) == NULL) { lError = GetLastError(); EapTlsTrace("ReadCARootHashes: LocalAlloc failed for pbRootHashBlob"); goto LDone; } for (i=0; idwNumberOfEntries;dwCount++) { if ( pEntryProp->dwEapTypeId == dwTypeId ) { return pEntryProp; } PeapGetNextEntryUserProp ( pEntryProp, &pEntryProp ); } return NULL; } DWORD PeapGetFirstEntryUserProp ( PPEAP_USER_PROP pUserProp, PEAP_ENTRY_USER_PROPERTIES UNALIGNED ** ppEntryProp ) { * ppEntryProp = &( pUserProp->UserProperties ); return NO_ERROR; } DWORD PeapGetNextEntryUserProp ( PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pCurrentProp, PEAP_ENTRY_USER_PROPERTIES UNALIGNED ** ppEntryProp ) { *ppEntryProp = (PPEAP_ENTRY_USER_PROPERTIES)(((PBYTE) ( pCurrentProp ) )+ pCurrentProp->dwSize); return NO_ERROR; } DWORD PeapRemoveEntryUserProp ( PPEAP_USER_PROP pUserProp, PPEAP_EAP_INFO pEapInfo, PPEAP_USER_PROP * ppNewUserProp ) { DWORD dwRetCode = NO_ERROR; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp = NULL; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pNewEntryUserProp = NULL; PPEAP_USER_PROP pNewUserProp = NULL; DWORD dwCount = 0; DWORD dwNewCount = 0; // // Check to see if this entry prop is in the list // PeapGetFirstEntryUserProp ( pUserProp, &pEntryUserProp ); for ( dwCount = 0; dwCount < pUserProp->dwNumberOfEntries;dwCount++) { if ( pEntryUserProp->dwEapTypeId == pEapInfo->dwTypeId ) { // // Re allocate the blob // pNewUserProp = LocalAlloc ( LPTR, pUserProp->dwSize - pEntryUserProp->dwSize ); if ( NULL == pNewUserProp ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; goto LDone; } break; } PeapGetNextEntryUserProp ( pEntryUserProp, &pEntryUserProp ); } if ( pNewUserProp ) { //We have found the entry to delete //copy over everything but for the entry that needs deletion pNewUserProp->dwVersion = 2; pNewUserProp->dwSize = pUserProp->dwSize - pEntryUserProp->dwSize; pNewUserProp->dwFlags = pUserProp->dwFlags; CopyMemory ( &(pNewUserProp->CertHash), &(pUserProp->CertHash), sizeof(pNewUserProp->CertHash) ); pNewUserProp->dwNumberOfEntries = pUserProp->dwNumberOfEntries - 1; PeapGetFirstEntryUserProp ( pUserProp, &pEntryUserProp ); PeapGetFirstEntryUserProp ( pNewUserProp, &pNewEntryUserProp ); for ( dwNewCount = 0; dwNewCount < pUserProp->dwNumberOfEntries; dwNewCount ++ ) { if ( pEntryUserProp->dwEapTypeId != pEapInfo->dwTypeId ) { CopyMemory ( pNewEntryUserProp, pEntryUserProp, pEntryUserProp->dwSize ); PeapGetNextEntryUserProp ( pNewEntryUserProp, &pNewEntryUserProp ); } PeapGetNextEntryUserProp ( pEntryUserProp, &pEntryUserProp ); } *ppNewUserProp = pNewUserProp; pNewUserProp = NULL; goto LDone; } dwRetCode = ERROR_NOT_FOUND; LDone: LocalFree ( pNewUserProp ); return dwRetCode; } // // Add a new entry at the end in the list and return the // new UserProp back. // DWORD PeapAddEntryUserProp ( PPEAP_USER_PROP pUserProp, PPEAP_EAP_INFO pEapInfo, PPEAP_USER_PROP * ppNewUserProp ) { DWORD dwRetCode = NO_ERROR; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp = NULL; DWORD dwCount = 0; PPEAP_USER_PROP pNewUserProp = NULL; PeapGetFirstEntryUserProp ( pUserProp, &pEntryUserProp ); for ( dwCount = 0; dwCount < pUserProp->dwNumberOfEntries;dwCount++) { if ( pEntryUserProp->dwEapTypeId == pEapInfo->dwTypeId ) { // // This should never happen. The EAP type supplied is // already configured... // RTASSERT(FALSE); dwRetCode = ERROR_INTERNAL_ERROR; goto LDone; } PeapGetNextEntryUserProp ( pEntryUserProp, &pEntryUserProp ); } // // EapInfo does not exist add it to the UserProp. // pNewUserProp = (PPEAP_USER_PROP)LocalAlloc(LPTR, pUserProp->dwSize + FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES, bData)); if ( NULL == pNewUserProp ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; goto LDone; } CopyMemory ( pNewUserProp, pUserProp, pUserProp->dwSize ); pNewUserProp->dwNumberOfEntries = pUserProp->dwNumberOfEntries+1; pNewUserProp->dwSize = pUserProp->dwSize + FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES, bData); pEntryUserProp = (PEAP_ENTRY_USER_PROPERTIES UNALIGNED *) (((PBYTE)pNewUserProp)+pUserProp->dwSize); pEntryUserProp->dwVersion = 1; pEntryUserProp->dwSize = FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES, bData); pEntryUserProp->dwEapTypeId = pEapInfo->dwTypeId; pEntryUserProp->fUsingPeapDefault = !(pEapInfo->lpwszIdentityUIPath); *ppNewUserProp = pNewUserProp; pNewUserProp = NULL; LDone: LocalFree(pNewUserProp ); return dwRetCode; } // // Move a User Prop Entry up or down in the list // and return the new struct. // DWORD PeapMoveEntryUserProp ( PPEAP_USER_PROP pUserProp, DWORD dwEntryIndex, BOOL fDirectionUp ) { DWORD dwRetCode = NO_ERROR; DWORD dwSwapEntryIndex = 0; DWORD dwCount = 0; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp = NULL; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp1 = NULL; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp2 = NULL; if ( 0 == dwEntryIndex && fDirectionUp ) { dwRetCode = ERROR_INVALID_PARAMETER; goto LDone; } if ( pUserProp->dwNumberOfEntries -1 == dwEntryIndex && !fDirectionUp ) { dwRetCode = ERROR_INVALID_PARAMETER; goto LDone; } // // Swap the two entries in question // if ( fDirectionUp ) { dwSwapEntryIndex = dwEntryIndex; dwEntryIndex = dwEntryIndex-1; } else { dwSwapEntryIndex = dwEntryIndex +1; } PeapGetFirstEntryUserProp ( pUserProp, &pEntryUserProp ); for ( dwCount = 0; dwCount < pUserProp->dwNumberOfEntries; dwCount ++ ) { if ( dwCount == dwEntryIndex ) { pEntryUserProp1 = pEntryUserProp; } if ( dwCount == dwSwapEntryIndex ) { pEntryUserProp2 = pEntryUserProp; } PeapGetNextEntryUserProp ( pEntryUserProp, &pEntryUserProp ); } if ( NULL == pEntryUserProp1 ) { dwRetCode = ERROR_NOT_FOUND; goto LDone; } //Swap these 2 entries pEntryUserProp = (PEAP_ENTRY_USER_PROPERTIES UNALIGNED *) LocalAlloc(LPTR, pEntryUserProp1->dwSize); if ( NULL == pEntryUserProp) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; goto LDone; } CopyMemory ( pEntryUserProp, pEntryUserProp1, pEntryUserProp1->dwSize ); CopyMemory ( pEntryUserProp1, pEntryUserProp2, pEntryUserProp2->dwSize ); pEntryUserProp2 = (PEAP_ENTRY_USER_PROPERTIES UNALIGNED *) ( (PBYTE)pEntryUserProp1 + pEntryUserProp1->dwSize ) ; CopyMemory ( pEntryUserProp2, pEntryUserProp, pEntryUserProp->dwSize ); LocalFree ( pEntryUserProp); LDone: return dwRetCode; } DWORD PeapGetFirstEntryConnProp ( PPEAP_CONN_PROP pConnProp, PEAP_ENTRY_CONN_PROPERTIES UNALIGNED ** ppEntryProp ) { DWORD dwRetCode = NO_ERROR; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED *pFirstEntryConnProp = NULL; LPWSTR lpwszServerName; RTASSERT ( NULL != pConnProp ); RTASSERT ( NULL != ppEntryProp ); lpwszServerName = (LPWSTR )(pConnProp->EapTlsConnProp.bData + sizeof( EAPTLS_HASH ) * pConnProp->EapTlsConnProp.dwNumHashes); //Get the first entry in connprop pFirstEntryConnProp = ( PEAP_ENTRY_CONN_PROPERTIES UNALIGNED *) ( pConnProp->EapTlsConnProp.bData + pConnProp->EapTlsConnProp.dwNumHashes * sizeof(EAPTLS_HASH) + (lpwszServerName? wcslen(lpwszServerName) * sizeof(WCHAR):0) + sizeof(WCHAR) ); if (NULL == pFirstEntryConnProp ) { dwRetCode = ERROR_NOT_FOUND; goto LDone; } *ppEntryProp = pFirstEntryConnProp; LDone: return dwRetCode; } DWORD PeapReadConnectionData( IN BOOL fWireless, IN BYTE* pConnectionDataIn, IN DWORD dwSizeOfConnectionDataIn, OUT PPEAP_CONN_PROP* ppConnProp ) { DWORD dwRetCode = NO_ERROR; PPEAP_CONN_PROP pConnProp = NULL; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED * pEntryProp = NULL; EapTlsTrace("PeapReadConnectionData"); RTASSERT(NULL != ppConnProp); if ( dwSizeOfConnectionDataIn < sizeof(PEAP_CONN_PROP) ) { pConnProp = LocalAlloc(LPTR, sizeof(PEAP_CONN_PROP) + sizeof(PEAP_ENTRY_CONN_PROPERTIES)+ sizeof(WCHAR)); if (NULL == pConnProp) { dwRetCode = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwRetCode); goto LDone; } //This is a new structure pConnProp->dwVersion = 1; pConnProp->dwSize = sizeof(PEAP_CONN_PROP) + sizeof(PEAP_ENTRY_CONN_PROPERTIES); pConnProp->EapTlsConnProp.dwVersion = 1; pConnProp->EapTlsConnProp.dwSize = sizeof(EAPTLS_CONN_PROPERTIES_V1); pConnProp->EapTlsConnProp.fFlags |= EAPTLS_CONN_FLAG_REGISTRY; pConnProp->dwNumPeapTypes = 1; //pEntryProp = (PPEAP_ENTRY_CONN_PROPERTIES)(((PBYTE)(pConnProp)) + sizeof(PEAP_CONN_PROP) + sizeof(WCHAR)); pEntryProp = ( PEAP_ENTRY_CONN_PROPERTIES UNALIGNED *) ( pConnProp->EapTlsConnProp.bData + pConnProp->EapTlsConnProp.dwNumHashes * sizeof(EAPTLS_HASH) + sizeof(WCHAR) ); // // Also setup the first peap entry conn prop and set it to // eapmschapv2 // if ( fWireless ) { pConnProp->EapTlsConnProp.fFlags |= EAPTLS_CONN_FLAG_NO_VALIDATE_NAME; } pEntryProp->dwVersion = 1; pEntryProp->dwSize = sizeof(PEAP_ENTRY_CONN_PROPERTIES); pEntryProp->dwEapTypeId = PPP_EAP_MSCHAPv2; } else { RTASSERT(NULL != pConnectionDataIn); // //Check to see if this is a version 0 structure //If it is a version 0 structure then we migrate it to version1 // pConnProp = LocalAlloc(LPTR, dwSizeOfConnectionDataIn); if (NULL == pConnProp) { dwRetCode = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwRetCode); goto LDone; } // If the user has mucked with the phonebook, we mustn't be affected. // The size must be correct. CopyMemory(pConnProp, pConnectionDataIn, dwSizeOfConnectionDataIn); pConnProp->dwSize = dwSizeOfConnectionDataIn; pConnProp->EapTlsConnProp.fFlags |= EAPTLS_CONN_FLAG_REGISTRY; } *ppConnProp = pConnProp; pConnProp = NULL; LDone: LocalFree(pConnProp); return dwRetCode; } DWORD PeapReDoUserData ( IN DWORD dwNewTypeId, OUT PPEAP_USER_PROP* ppNewUserProp ) { DWORD dwRetCode = NO_ERROR; PPEAP_USER_PROP pUserProp = NULL; EapTlsTrace("PeapReDoUserData"); pUserProp = LocalAlloc(LPTR, sizeof(PEAP_USER_PROP)); if (NULL == pUserProp) { dwRetCode = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwRetCode); goto LDone; } pUserProp->dwVersion = 1; pUserProp->dwSize = sizeof(PEAP_USER_PROP); // // Setup the default user prop... // pUserProp->UserProperties.dwVersion = 1; pUserProp->UserProperties.dwSize = sizeof(PEAP_ENTRY_USER_PROPERTIES); pUserProp->UserProperties.dwEapTypeId = dwNewTypeId; *ppNewUserProp = pUserProp; LDone: return dwRetCode; } PEAP_ENTRY_USER_PROPERTIES * PeapGetEapConfigInfo( PEAP_USER_PROP* pUserProp, DWORD dwTypeId, PBYTE* ppConfigData, DWORD* pdwSizeOfConfigData) { DWORD i; PEAP_ENTRY_USER_PROPERTIES *pEntry; ASSERT(NULL != pUserProp); ASSERT(NULL != ppConfigData); ASSERT(NULL != pdwSizeOfConfigData); pEntry = &pUserProp->UserProperties; *ppConfigData = NULL; *pdwSizeOfConfigData = 0; // // Find the config data for this eap in the // user properties that was passed in. // for(i = 0; i < pUserProp->dwNumberOfEntries; i++) { ASSERT((BYTE *)pEntry < (BYTE *) pUserProp + pUserProp->dwSize); if(pEntry->dwEapTypeId == dwTypeId) { // // Assert that the size is valid here // ASSERT(pEntry->dwSize >= FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES, bData)); *pdwSizeOfConfigData = pEntry->dwSize - FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES,bData); if(*pdwSizeOfConfigData != 0) { *ppConfigData = pEntry->bData; } break; } // // Go on to the next entry. Assert that the pointer is still valid. // pEntry = (PEAP_ENTRY_USER_PROPERTIES *) ((BYTE *)pEntry + pEntry->dwSize); } if(i == pUserProp->dwNumberOfEntries) { return NULL; } return pEntry; } // // This function will verify that the UserProps blob // matches the EAPTypes. If an eaptype is unusable // due to unavailability then it will remove // it from the blob. This is used only on the server // side. // DWORD PeapVerifyUserData( PPEAP_EAP_INFO pEapInfo, PPEAP_USER_PROP pUserProp, PPEAP_USER_PROP * ppNewUserProp ) { DWORD dwRetCode = NO_ERROR; PPEAP_USER_PROP pTempUserProp = NULL; PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryUserProp = NULL; PPEAP_EAP_INFO pEapInfoNode = NULL; PEAP_EAP_INFO TempNode; DWORD dwCount = 0; if ( NULL == pUserProp ) { goto done; } PeapGetFirstEntryUserProp ( pUserProp, &pEntryUserProp ); for ( dwCount =0; dwCount < pUserProp->dwNumberOfEntries; dwCount++) { // // Check to see if the configured entry is present in // the list of entries that are legal on this machine // PeapEapInfoFindListNode ( pEntryUserProp->dwEapTypeId, pEapInfo, &pEapInfoNode ); if ( !pEapInfoNode ) { if ( pTempUserProp ) { LocalFree (pTempUserProp); pTempUserProp = NULL; } // // This node is not in our list of configured EAP Types. // ZeroMemory( &TempNode, sizeof(TempNode) ); TempNode.dwTypeId = pEntryUserProp->dwEapTypeId; PeapRemoveEntryUserProp( pUserProp, &TempNode, &pTempUserProp ); } pEapInfoNode = NULL; PeapGetNextEntryUserProp ( pEntryUserProp, &pEntryUserProp ); } *ppNewUserProp = pTempUserProp; done: return dwRetCode; } DWORD PeapReadUserData( IN BOOL fServer, IN BYTE* pUserDataIn, IN DWORD dwSizeOfUserDataIn, OUT PPEAP_USER_PROP* ppUserProp ) { DWORD dwErr = NO_ERROR; PPEAP_USER_PROP pUserProp = NULL; PPEAP_USER_PROP_V1 pUserPropv1 = NULL; EapTlsTrace("PeapReadUserData"); if (( fServer && ( dwSizeOfUserDataIn < FIELD_OFFSET(PEAP_USER_PROP,UserProperties) + FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES ,bData ) ) ) || ( !fServer && (dwSizeOfUserDataIn < sizeof(PEAP_USER_PROP)) ) ) { pUserProp = LocalAlloc(LPTR, sizeof(PEAP_USER_PROP)); if (NULL == pUserProp) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pUserProp->dwVersion = 2; pUserProp->dwNumberOfEntries = 1; // // Setup the default user prop... // pUserProp->UserProperties.dwVersion = 1; if ( fServer ) { pUserProp->UserProperties.dwSize = FIELD_OFFSET(PEAP_ENTRY_USER_PROPERTIES,bData); pUserProp->dwSize = FIELD_OFFSET(PEAP_USER_PROP,UserProperties) + pUserProp->UserProperties.dwSize; } else { pUserProp->UserProperties.dwSize = sizeof(PEAP_ENTRY_USER_PROPERTIES); pUserProp->dwSize = sizeof(PEAP_USER_PROP); } pUserProp->UserProperties.dwEapTypeId = PPP_EAP_MSCHAPv2; } else { RTASSERT(NULL != pUserDataIn); if ( *((DWORD *)pUserDataIn) <= 1 ) { pUserPropv1 = LocalAlloc(LPTR, dwSizeOfUserDataIn); if (NULL == pUserPropv1 ) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pUserPropv1 , pUserDataIn, dwSizeOfUserDataIn); // // Allocate the new version 2 structure here and convert // the version 1 to version 2 // pUserProp = LocalAlloc (LPTR, dwSizeOfUserDataIn + sizeof(DWORD) ); if ( NULL == pUserProp ) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } pUserProp->dwVersion = 2; pUserProp->dwSize = dwSizeOfUserDataIn + sizeof(DWORD); pUserProp->dwFlags = pUserPropv1->dwFlags; CopyMemory ( &(pUserProp->CertHash), &(pUserPropv1->CertHash), sizeof(EAPTLS_HASH) ); pUserProp->dwNumberOfEntries = 1; CopyMemory ( &(pUserProp->UserProperties), &(pUserPropv1->UserProperties), pUserPropv1->UserProperties.dwSize ); LocalFree (pUserPropv1); pUserPropv1 = NULL; } else { pUserProp = LocalAlloc(LPTR, dwSizeOfUserDataIn); if (NULL == pUserProp ) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } CopyMemory(pUserProp , pUserDataIn, dwSizeOfUserDataIn); } } *ppUserProp = pUserProp; pUserProp = NULL; LDone: LocalFree(pUserPropv1); LocalFree(pUserProp); return dwErr; } // // Add node at the head // DWORD PeapEapInfoAddListNode (PPEAP_EAP_INFO * ppEapInfo) { PPEAP_EAP_INFO pEapInfo = NULL; DWORD dwRetCode = NO_ERROR; pEapInfo = (PPEAP_EAP_INFO)LocalAlloc(LPTR, sizeof(PEAP_EAP_INFO)); if ( NULL == pEapInfo ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } if ( NULL == *ppEapInfo ) { *ppEapInfo = pEapInfo; } else { pEapInfo->pNext = *ppEapInfo; *ppEapInfo = pEapInfo; } LDone: return dwRetCode; } DWORD PeapEapInfoCopyListNode ( DWORD dwTypeId, PPEAP_EAP_INFO pEapInfoList, PPEAP_EAP_INFO * ppEapInfo ) { DWORD dwRetCode = ERROR_NOT_FOUND; PPEAP_EAP_INFO pEapInfoListInternal = pEapInfoList; while ( pEapInfoListInternal ) { if ( pEapInfoListInternal->dwTypeId == dwTypeId ) { *ppEapInfo = LocalAlloc( LPTR, sizeof(PEAP_EAP_INFO) ); if ( NULL == *ppEapInfo ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } CopyMemory ( *ppEapInfo, pEapInfoListInternal, sizeof(PEAP_EAP_INFO) ); dwRetCode = NO_ERROR; goto LDone; } pEapInfoListInternal = pEapInfoListInternal->pNext; } LDone: return dwRetCode; } DWORD PeapEapInfoFindListNode ( DWORD dwTypeId, PPEAP_EAP_INFO pEapInfoList, PPEAP_EAP_INFO * ppEapInfo ) { DWORD dwRetCode = ERROR_NOT_FOUND; PPEAP_EAP_INFO pEapInfoListInternal = pEapInfoList; while ( pEapInfoListInternal ) { if ( pEapInfoListInternal->dwTypeId == dwTypeId ) { *ppEapInfo = pEapInfoListInternal; dwRetCode = NO_ERROR; goto LDone; } pEapInfoListInternal = pEapInfoListInternal->pNext; } LDone: return dwRetCode; } VOID PeapEapInfoFreeNodeData ( PPEAP_EAP_INFO pEapInfo ) { LocalFree ( pEapInfo->lpwszFriendlyName ); LocalFree ( pEapInfo->lpwszConfigUIPath ); LocalFree ( pEapInfo->lpwszIdentityUIPath ); LocalFree ( pEapInfo->lpwszConfigClsId ); LocalFree ( pEapInfo->pbNewClientConfig ); LocalFree ( pEapInfo->lpwszInteractiveUIPath ); LocalFree ( pEapInfo->lpwszPath); if ( pEapInfo->hEAPModule ) { FreeLibrary(pEapInfo->hEAPModule); } } VOID PeapEapInfoRemoveHeadNode(PPEAP_EAP_INFO * ppEapInfo) { PPEAP_EAP_INFO pEapInfo = *ppEapInfo; if ( pEapInfo ) { *ppEapInfo = pEapInfo->pNext; PeapEapInfoFreeNodeData(pEapInfo); LocalFree ( pEapInfo ); } } VOID PeapEapInfoFreeList ( PPEAP_EAP_INFO pEapInfo ) { PPEAP_EAP_INFO pNext; while ( pEapInfo ) { if ( pEapInfo->PppEapInfo.RasEapInitialize ) pEapInfo->PppEapInfo.RasEapInitialize (FALSE); pNext = pEapInfo->pNext; PeapEapInfoFreeNodeData(pEapInfo); LocalFree ( pEapInfo ); pEapInfo = pNext; } } DWORD PeapEapInfoReadSZ (HKEY hkeyPeapType, LPWSTR pwszValue, LPWSTR * ppValueData ) { DWORD dwRetCode = NO_ERROR; DWORD dwType = 0; DWORD cbValueDataSize =0; PBYTE pbValue = NULL; dwRetCode = RegQueryValueEx( hkeyPeapType, pwszValue, NULL, &dwType, pbValue, &cbValueDataSize ); if ( dwRetCode != NO_ERROR ) { goto LDone; } pbValue = (PBYTE)LocalAlloc ( LPTR, cbValueDataSize ); if ( NULL == pbValue ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } dwRetCode = RegQueryValueEx( hkeyPeapType, pwszValue, NULL, &dwType, pbValue, &cbValueDataSize ); if ( dwRetCode != NO_ERROR ) { goto LDone; } *ppValueData = (LPWSTR)pbValue; pbValue = NULL; LDone: LocalFree ( pbValue ); return dwRetCode; } DWORD PeapEapInfoExpandSZ (HKEY hkeyPeapType, LPWSTR pwszValue, LPWSTR * ppValueData ) { DWORD dwRetCode = NO_ERROR; DWORD dwType = 0; DWORD cbValueDataSize =0; PBYTE pbValue = NULL; PBYTE pbExpandedValue = NULL; dwRetCode = RegQueryValueEx( hkeyPeapType, pwszValue, NULL, &dwType, pbValue, &cbValueDataSize ); if ( dwRetCode != NO_ERROR ) { goto LDone; } pbValue = (PBYTE)LocalAlloc ( LPTR, cbValueDataSize ); if ( NULL == pbValue ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } dwRetCode = RegQueryValueEx( hkeyPeapType, pwszValue, NULL, &dwType, pbValue, &cbValueDataSize ); if ( dwRetCode != NO_ERROR ) { goto LDone; } //now Expand the exvironment string cbValueDataSize = ExpandEnvironmentStrings( (LPWSTR)pbValue, NULL, 0 ); pbExpandedValue = (PBYTE)LocalAlloc ( LPTR, cbValueDataSize * sizeof(WCHAR) ); if ( NULL == pbExpandedValue ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } cbValueDataSize = ExpandEnvironmentStrings( (LPWSTR)pbValue, (LPWSTR)pbExpandedValue, sizeof(WCHAR) * cbValueDataSize ); if ( cbValueDataSize == 0 ) { dwRetCode = GetLastError(); goto LDone; } *ppValueData = (LPWSTR)pbExpandedValue; pbExpandedValue = NULL; LDone: LocalFree ( pbValue ); LocalFree ( pbExpandedValue ); return dwRetCode; } // // Get a list of all EAP types configured for PEAP. // DWORD PeapEapInfoGetList ( LPWSTR lpwszMachineName, BOOL fCheckDomainMembership, PPEAP_EAP_INFO * ppEapInfo) { DWORD dwRetCode = NO_ERROR; HKEY hKeyLM =0; HKEY hKeyPeap = 0; HKEY hkeyPeapType = 0; DWORD dwIndex; DWORD cb; WCHAR wszPeapType[200]; DWORD dwEapTypeId = 0; FARPROC pRasEapGetInfo; BOOL fStandAloneServer = FALSE; fStandAloneServer = IsStandaloneServer(lpwszMachineName); dwRetCode = RegConnectRegistry ( lpwszMachineName, HKEY_LOCAL_MACHINE, &hKeyLM ); if ( NO_ERROR != dwRetCode ) { goto LDone; } dwRetCode = RegOpenKeyEx( hKeyLM, PEAP_KEY_EAP, 0, KEY_READ, &hKeyPeap ); if (NO_ERROR != dwRetCode) { goto LDone; } for (dwIndex = 0; TRUE; ++dwIndex) { cb = sizeof(wszPeapType) / sizeof(WCHAR); dwRetCode = RegEnumKeyEx( hKeyPeap, dwIndex, wszPeapType, &cb, NULL, NULL, NULL, NULL ); if (dwRetCode != NO_ERROR) { // Includes "out of items", the normal loop termination. // dwRetCode = NO_ERROR; break; } dwRetCode = RegOpenKeyEx( hKeyPeap, wszPeapType, 0, KEY_READ, &hkeyPeapType ); if (dwRetCode != NO_ERROR) { dwRetCode = NO_ERROR; continue; } dwEapTypeId = _wtol(wszPeapType); if ( dwEapTypeId == PPP_EAP_PEAP ) { dwRetCode = NO_ERROR; continue; } { // // Check to see if we support this in peap // By default we do. DWORD dwRolesSupported = 0; DWORD cbValueSize = sizeof(dwRolesSupported); DWORD dwType = 0; dwRetCode = RegQueryValueEx( hkeyPeapType, PEAP_REGVAL_ROLESSUPPORTED, NULL, &dwType, (PBYTE)&dwRolesSupported, &cbValueSize ); if ( dwRetCode == NO_ERROR ) { // // We dont allow this method in PEAP. // if ( RAS_EAP_ROLE_EXCLUDE_IN_PEAP & dwRolesSupported ) { continue; } } } // // Read the required information and setup the node here // dwRetCode = PeapEapInfoAddListNode (ppEapInfo); if ( NO_ERROR != dwRetCode ) { goto LDone; } // // Setup the list node - if any of these entries are not // found skip the entry // (*ppEapInfo)->dwTypeId = dwEapTypeId; { //Get the stand alone supported flag here DWORD cbValueSize = sizeof( ((*ppEapInfo)->dwStandAloneSupported ) ); DWORD dwType = 0; dwRetCode = RegQueryValueEx( hkeyPeapType, PEAP_REGVAL_STANDALONESUPPORTED, NULL, &dwType, (PBYTE)&((*ppEapInfo)->dwStandAloneSupported), &cbValueSize ); if ( dwRetCode != NO_ERROR ) { (*ppEapInfo)->dwStandAloneSupported = 1; dwRetCode = NO_ERROR; } } // // Check to see if we need to check for domain membership // if ( fCheckDomainMembership ) { // // We need to check for domain membership. // if ( fStandAloneServer ) { if ( !((*ppEapInfo)->dwStandAloneSupported )) { // //We are a stand alone server and //the EAP type does not support //stnadalone mode. // PeapEapInfoRemoveHeadNode ( ppEapInfo ); dwRetCode = NO_ERROR; continue; } } } // // // dwRetCode = PeapEapInfoReadSZ ( hkeyPeapType, PEAP_REGVAL_FRIENDLYNAME, &((*ppEapInfo)->lpwszFriendlyName ) ); if ( NO_ERROR != dwRetCode ) { if ( ERROR_FILE_NOT_FOUND == dwRetCode ) { PeapEapInfoRemoveHeadNode(ppEapInfo); dwRetCode = NO_ERROR; continue; } goto LDone; } dwRetCode = PeapEapInfoExpandSZ ( hkeyPeapType, PEAP_REGVAL_CONFIGDLL, &((*ppEapInfo)->lpwszConfigUIPath ) ); if ( NO_ERROR != dwRetCode ) { if ( ERROR_FILE_NOT_FOUND == dwRetCode ) { // it is fine to have no config stuff any more. // We show the default identity dwRetCode = NO_ERROR; } else { goto LDone; } } dwRetCode = PeapEapInfoExpandSZ ( hkeyPeapType, PEAP_REGVAL_IDENTITYDLL, &((*ppEapInfo)->lpwszIdentityUIPath ) ); if ( NO_ERROR != dwRetCode ) { if ( ERROR_FILE_NOT_FOUND == dwRetCode ) { // // It is fine if we dont have any identity UI. Peap // will provide a default identity UI // dwRetCode = NO_ERROR; } else { goto LDone; } } dwRetCode = PeapEapInfoExpandSZ ( hkeyPeapType, PEAP_REGVAL_INTERACTIVEUIDLL, &((*ppEapInfo)->lpwszInteractiveUIPath ) ); if ( NO_ERROR != dwRetCode ) { if ( ERROR_FILE_NOT_FOUND == dwRetCode ) { //It is fine if we dont have interactive UI // dwRetCode = NO_ERROR; } else { goto LDone; } } dwRetCode = PeapEapInfoReadSZ ( hkeyPeapType, PEAP_REGVAL_CONFIGCLSID, &((*ppEapInfo)->lpwszConfigClsId ) ); if ( NO_ERROR != dwRetCode ) { if ( ERROR_FILE_NOT_FOUND == dwRetCode ) { // // Missing config clsid is also fine dwRetCode = NO_ERROR; } else { goto LDone; } } dwRetCode = PeapEapInfoExpandSZ ( hkeyPeapType, PEAP_REGVAL_PATH, &((*ppEapInfo)->lpwszPath ) ); if ( NO_ERROR != dwRetCode ) { // // This is not acceptable. So this is a problem. // if ( ERROR_FILE_NOT_FOUND == dwRetCode ) { PeapEapInfoRemoveHeadNode(ppEapInfo); dwRetCode = NO_ERROR; continue; } goto LDone; } // // Now get the EAP INFO from the DLL. // (*ppEapInfo)->hEAPModule = LoadLibrary( ( (*ppEapInfo)->lpwszPath ) ); if ( NULL == (*ppEapInfo)->hEAPModule ) { dwRetCode = GetLastError(); goto LDone; } pRasEapGetInfo = GetProcAddress( (*ppEapInfo)->hEAPModule , "RasEapGetInfo" ); if ( pRasEapGetInfo == (FARPROC)NULL ) { dwRetCode = GetLastError(); goto LDone; } (*ppEapInfo)->RasEapGetCredentials = (DWORD (*) ( DWORD,VOID *, VOID **)) GetProcAddress((*ppEapInfo)->hEAPModule, "RasEapGetCredentials"); (*ppEapInfo)->PppEapInfo.dwSizeInBytes = sizeof( PPP_EAP_INFO ); dwRetCode = (DWORD) (*pRasEapGetInfo)( dwEapTypeId, &((*ppEapInfo)->PppEapInfo) ); if ( dwRetCode != NO_ERROR ) { goto LDone; } // // Call initialize function here // if ( (*ppEapInfo)->PppEapInfo.RasEapInitialize ) { (*ppEapInfo)->PppEapInfo.RasEapInitialize(TRUE); } RegCloseKey(hkeyPeapType); hkeyPeapType = 0; } LDone: if ( hkeyPeapType ) RegCloseKey(hkeyPeapType); if ( hKeyPeap ) RegCloseKey(hKeyPeap); if ( hKeyLM ) RegCloseKey(hKeyLM); if ( NO_ERROR != dwRetCode ) { PeapEapInfoFreeList( *ppEapInfo ); } return dwRetCode; } DWORD PeapEapInfoGetItemCount ( PPEAP_EAP_INFO pEapInfo ) { DWORD dwCount = 0; PPEAP_EAP_INFO pEapInfoLocal = pEapInfo; while (pEapInfoLocal) { dwCount++; pEapInfoLocal = pEapInfoLocal->pNext; } return dwCount; } DWORD PeapEapInfoSetConnData ( PPEAP_EAP_INFO pEapInfo, PPEAP_CONN_PROP pPeapConnProp ) { DWORD dwRetCode = NO_ERROR; PEAP_ENTRY_CONN_PROPERTIES UNALIGNED *pEntryProp = NULL; PPEAP_EAP_INFO pEapInfoLocal; DWORD dwCount; RTASSERT(NULL != pPeapConnProp); RTASSERT(NULL != pEapInfo); if ( !pPeapConnProp->dwNumPeapTypes ) { goto LDone; } // // Right now there is only one EAP Type in the list // So it should not be a problem with this stuff now pEntryProp = ( PEAP_ENTRY_CONN_PROPERTIES*) ( pPeapConnProp->EapTlsConnProp.bData + pPeapConnProp->EapTlsConnProp.dwNumHashes * sizeof(EAPTLS_HASH) + sizeof(WCHAR) ); pEapInfoLocal = pEapInfo; while( pEapInfoLocal ) { if ( pEapInfoLocal->dwTypeId == pEntryProp->dwEapTypeId ) { if ( pEntryProp->dwSize > sizeof(PEAP_ENTRY_CONN_PROPERTIES)) { pEapInfoLocal->pbClientConfigOrig = pEntryProp->bData; pEapInfoLocal->dwClientConfigOrigSize = pEntryProp->dwSize - sizeof(PEAP_ENTRY_CONN_PROPERTIES) + 1; } else { pEapInfoLocal->pbClientConfigOrig = NULL; pEapInfoLocal->dwClientConfigOrigSize = 0; } break; } pEapInfoLocal = pEapInfoLocal->pNext; } #if 0 for ( dwCount = 0; dwCount < pPeapConnProp->dwNumPeapTypes; dwCount ++ ) { pEntryProp = (PEAP_ENTRY_CONN_PROPERTIES UNALIGNED * )(((BYTE UNALIGNED *)&(pPeapConnProp->EapTlsConnProp)) + pPeapConnProp->EapTlsConnProp.dwSize + sizeof(PEAP_ENTRY_CONN_PROPERTIES) * dwCount); pEapInfoLocal = pEapInfo; while( pEapInfoLocal ) { if ( pEapInfoLocal->dwTypeId == pEntryProp->dwEapTypeId ) { if ( pEntryProp->dwSize > sizeof(PEAP_ENTRY_CONN_PROPERTIES)) { pEapInfoLocal->pbClientConfigOrig = pEntryProp->bData; pEapInfoLocal->dwClientConfigOrigSize = pEntryProp->dwSize - sizeof(PEAP_ENTRY_CONN_PROPERTIES) + 1; } else { pEapInfoLocal->pbClientConfigOrig = NULL; pEapInfoLocal->dwClientConfigOrigSize = 0; } break; } pEapInfoLocal = pEapInfoLocal->pNext; } } #endif LDone: return dwRetCode; } DWORD PeapEapInfoInvokeIdentityUI ( HWND hWndParent, PPEAP_EAP_INFO pEapInfo, const WCHAR * pwszPhoneBook, const WCHAR * pwszEntry, PBYTE pbUserDataIn, // Got when using Winlogon DWORD cbUserDataIn, // Got when using Winlogon WCHAR** ppwszIdentityOut, DWORD fFlags) { DWORD dwRetCode = NO_ERROR; PBYTE pbUserDataNew = NULL; DWORD dwSizeOfUserDataNew = 0; RASEAPGETIDENTITY pIdenFunc = NULL; RASEAPFREE pFreeFunc = NULL; RTASSERT ( NULL != pEapInfo ); RTASSERT ( NULL != pEapInfo->lpwszIdentityUIPath ); pIdenFunc = (RASEAPGETIDENTITY) GetProcAddress(pEapInfo->hEAPModule, "RasEapGetIdentity"); if ( pIdenFunc == NULL) { dwRetCode = GetLastError(); goto LDone; } pFreeFunc = (RASEAPFREE) GetProcAddress(pEapInfo->hEAPModule, "RasEapFreeMemory"); if ( pFreeFunc == NULL ) { dwRetCode = GetLastError(); goto LDone; } dwRetCode = pIdenFunc ( pEapInfo->dwTypeId, hWndParent, fFlags, pwszPhoneBook, pwszEntry, pEapInfo->pbClientConfigOrig, pEapInfo->dwClientConfigOrigSize, ( fFlags & RAS_EAP_FLAG_LOGON ? pbUserDataIn: pEapInfo->pbUserConfigOrig ), ( fFlags & RAS_EAP_FLAG_LOGON ? cbUserDataIn: pEapInfo->dwUserConfigOrigSize ), &pbUserDataNew, &dwSizeOfUserDataNew, ppwszIdentityOut ); if ( NO_ERROR != dwRetCode ) { goto LDone; } if ( pbUserDataNew && dwSizeOfUserDataNew ) { // // we have new user data // pEapInfo->pbUserConfigNew = (PBYTE)LocalAlloc (LPTR, dwSizeOfUserDataNew ); if ( NULL == pEapInfo->pbUserConfigNew ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } CopyMemory ( pEapInfo->pbUserConfigNew, pbUserDataNew, dwSizeOfUserDataNew ); pEapInfo->dwNewUserConfigSize = dwSizeOfUserDataNew; } LDone: if ( pFreeFunc ) pFreeFunc( pbUserDataNew ); return dwRetCode; } DWORD PeapEapInfoInvokeClientConfigUI ( HWND hWndParent, PPEAP_EAP_INFO pEapInfo, DWORD fFlags) { DWORD dwRetCode = NO_ERROR; RASEAPINVOKECONFIGUI pInvokeConfigUI; RASEAPFREE pFreeConfigUIData; PBYTE pConnDataOut = NULL; DWORD dwConnDataOut = 0; RTASSERT ( NULL != pEapInfo ); RTASSERT ( NULL != pEapInfo->lpwszConfigUIPath ); if ( !(pInvokeConfigUI = (RASEAPINVOKECONFIGUI )GetProcAddress( pEapInfo->hEAPModule, "RasEapInvokeConfigUI" )) || !(pFreeConfigUIData = (RASEAPFREE) GetProcAddress( pEapInfo->hEAPModule, "RasEapFreeMemory" )) ) { dwRetCode = GetLastError(); goto LDone; } dwRetCode = pInvokeConfigUI ( pEapInfo->dwTypeId, hWndParent, fFlags, (pEapInfo->pbNewClientConfig? pEapInfo->pbNewClientConfig: pEapInfo->pbClientConfigOrig ), (pEapInfo->pbNewClientConfig? pEapInfo->dwNewClientConfigSize: pEapInfo->dwClientConfigOrigSize ), &pConnDataOut, &dwConnDataOut ); if ( NO_ERROR != dwRetCode ) { goto LDone; } if ( pConnDataOut && dwConnDataOut ) { if ( pEapInfo->pbNewClientConfig ) { LocalFree(pEapInfo->pbNewClientConfig ); pEapInfo->pbNewClientConfig = NULL; pEapInfo->dwNewClientConfigSize = 0; } pEapInfo->pbNewClientConfig = (PBYTE)LocalAlloc ( LPTR, dwConnDataOut ); if ( NULL == pEapInfo->pbNewClientConfig ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } CopyMemory( pEapInfo->pbNewClientConfig, pConnDataOut, dwConnDataOut ); pEapInfo->dwNewClientConfigSize = dwConnDataOut; } LDone: if ( pConnDataOut ) pFreeConfigUIData(pConnDataOut); return dwRetCode; } DWORD OpenPeapRegistryKey( IN WCHAR* pwszMachineName, IN REGSAM samDesired, OUT HKEY* phKeyPeap ) { HKEY hKeyLocalMachine = NULL; BOOL fHKeyLocalMachineOpened = FALSE; BOOL fHKeyPeapOpened = FALSE; LONG lRet; DWORD dwErr = NO_ERROR; RTASSERT(NULL != phKeyPeap); lRet = RegConnectRegistry(pwszMachineName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegConnectRegistry(%ws) failed and returned %d", pwszMachineName ? pwszMachineName : L"NULL", dwErr); goto LDone; } fHKeyLocalMachineOpened = TRUE; lRet = RegOpenKeyEx(hKeyLocalMachine, PEAP_KEY_25, 0, samDesired, phKeyPeap); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegOpenKeyEx(%ws) failed and returned %d", PEAP_KEY_25, dwErr); goto LDone; } fHKeyPeapOpened = TRUE; LDone: if ( fHKeyPeapOpened && (ERROR_SUCCESS != dwErr)) { RegCloseKey(*phKeyPeap); } if (fHKeyLocalMachineOpened) { RegCloseKey(hKeyLocalMachine); } return(dwErr); } DWORD PeapServerConfigDataIO( IN BOOL fRead, IN WCHAR* pwszMachineName, IN OUT BYTE** ppData, IN DWORD dwNumBytes ) { HKEY hKeyPeap; PEAP_USER_PROP* pUserProp; BOOL fHKeyPeapOpened = FALSE; BYTE* pData = NULL; DWORD dwSize = 0; LONG lRet; DWORD dwType; DWORD dwErr = NO_ERROR; RTASSERT(NULL != ppData); dwErr = OpenPeapRegistryKey(pwszMachineName, fRead ? KEY_READ : KEY_WRITE, &hKeyPeap); if (ERROR_SUCCESS != dwErr) { goto LDone; } fHKeyPeapOpened = TRUE; if (fRead) { lRet = RegQueryValueEx(hKeyPeap, PEAP_VAL_SERVER_CONFIG_DATA, NULL, &dwType, NULL, &dwSize); if ( (ERROR_SUCCESS != lRet) || (REG_BINARY != dwType) ) { dwErr = PeapReadUserData( TRUE,NULL, 0, &pUserProp ); } else { pData = LocalAlloc(LPTR, dwSize); if (NULL == pData) { dwErr = GetLastError(); EapTlsTrace("LocalAlloc failed and returned %d", dwErr); goto LDone; } lRet = RegQueryValueEx(hKeyPeap, PEAP_VAL_SERVER_CONFIG_DATA, NULL, &dwType, pData, &dwSize); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegQueryValueEx(%ws) failed and returned %d", EAPTLS_VAL_SERVER_CONFIG_DATA, dwErr); goto LDone; } dwErr = PeapReadUserData(TRUE, pData, dwSize, &pUserProp); } *ppData = (PBYTE)pUserProp; } else { // // Write the blob back out. // lRet = RegSetValueEx(hKeyPeap, PEAP_VAL_SERVER_CONFIG_DATA, 0, REG_BINARY, *ppData, dwNumBytes); if (ERROR_SUCCESS != lRet) { dwErr = lRet; EapTlsTrace("RegSetValueEx(%ws) failed and returned %d", PEAP_VAL_SERVER_CONFIG_DATA, dwErr); goto LDone; } } LDone: if (fHKeyPeapOpened) { RegCloseKey(hKeyPeap); } LocalFree(pData); return(dwErr); } DWORD GetIdentityFromUserName ( LPWSTR lpszUserName, LPWSTR lpszDomain, LPWSTR * ppwszIdentity ) { DWORD dwRetCode = NO_ERROR; DWORD dwNumBytes; //domain+ user + '\' + null dwNumBytes = (wcslen(lpszUserName) + wcslen(lpszDomain) + 1 + 1) * sizeof(WCHAR); *ppwszIdentity = LocalAlloc ( LPTR, dwNumBytes); if ( NULL == *ppwszIdentity ) { dwRetCode = ERROR_OUTOFMEMORY; goto LDone; } if ( *lpszDomain ) { wcsncpy ( *ppwszIdentity, lpszDomain, DNLEN ); wcscat( *ppwszIdentity, L"\\"); } wcscat ( *ppwszIdentity, lpszUserName ); LDone: return dwRetCode; } // // Format identity as domain\user. this is ok because our identity inside has not been // tampered with // BOOL FFormatUserIdentity ( LPWSTR lpszUserNameRaw, LPWSTR * lppszUserNameFormatted ) { BOOL fRetVal = TRUE; LPTSTR s1 = NULL; LPTSTR s2 = NULL; RTASSERT(NULL != lpszUserNameRaw ); RTASSERT(NULL != lppszUserNameFormatted ); //Need to add 2 more chars. One for NULL and other for $ sign *lppszUserNameFormatted = (LPTSTR )LocalAlloc ( LPTR, (wcslen(lpszUserNameRaw ) + 2)* sizeof(WCHAR) ); if ( NULL == *lppszUserNameFormatted ) { return FALSE; } //find the first "@" and that is the identity of the machine. //the second "." is the domain. //check to see if there at least 2 dots. If not the raw string is //the output string s1 = wcschr ( lpszUserNameRaw, '@' ); if ( s1 ) { // // get the first . // s2 = wcschr ( s1, '.'); } if ( s1 && s2 ) { memcpy ( *lppszUserNameFormatted, s1+1, (s2-s1-1) * sizeof(WCHAR)) ; memcpy ( (*lppszUserNameFormatted) + (s2-s1-1), L"\\", sizeof(WCHAR)); memcpy ( (*lppszUserNameFormatted)+ (s2-s1), lpszUserNameRaw, (s1-lpszUserNameRaw) * sizeof(WCHAR) ); } else { wcscpy ( *lppszUserNameFormatted, lpszUserNameRaw ); } return fRetVal; } VOID GetMarshalledCredFromHash( PBYTE pbHash, DWORD cbHash, CHAR *pszMarshalledCred, DWORD cchCredSize) { CERT_CREDENTIAL_INFO CertCredInfo; CHAR *pszMarshalledCredLocal = NULL; CertCredInfo.cbSize = sizeof(CertCredInfo); memcpy (CertCredInfo.rgbHashOfCert, pbHash, CERT_HASH_LENGTH ); if (CredMarshalCredentialA(CertCredential, (PVOID) &CertCredInfo, &pszMarshalledCredLocal )) { // // Got Marshalled Credential from the cert // Set it in the username field // ASSERT( NULL != pszMarshalledCredLocal ); (VOID) StringCchCopyA (pszMarshalledCred, cchCredSize, pszMarshalledCredLocal ); CredFree ( pszMarshalledCredLocal ); } else { EapTlsTrace("CredMarshalCredential Failed with Error:0x%x", GetLastError()); } } DWORD GetCredentialsFromUserProperties( EAPTLSCB *pEapTlsCb, VOID **ppCredentials) { DWORD dwRetCode = ERROR_SUCCESS; RASMAN_CREDENTIALS *pCreds = NULL; // // Note: Its important that this allocation is made from // the process heap. Ppp engine needs to change otherwise. // pCreds = LocalAlloc(LPTR, sizeof(RASMAN_CREDENTIALS)); if(NULL == pCreds) { dwRetCode = GetLastError(); goto done; } if( (NULL != pEapTlsCb->pSavedPin) && (NULL != pEapTlsCb->pSavedPin->pwszPin)) { UNICODE_STRING UnicodeString; // // Decode the saved pin // UnicodeString.Length = pEapTlsCb->pSavedPin->usLength; UnicodeString.MaximumLength = pEapTlsCb->pSavedPin->usMaximumLength; UnicodeString.Buffer = pEapTlsCb->pSavedPin->pwszPin; RtlRunDecodeUnicodeString(pEapTlsCb->pSavedPin->ucSeed, &UnicodeString); (VOID)StringCchCopyW(pCreds->wszPassword, PWLEN, pEapTlsCb->pSavedPin->pwszPin); ZeroMemory(pEapTlsCb->pSavedPin->pwszPin, wcslen(pEapTlsCb->pSavedPin->pwszPin) * sizeof(WCHAR)); LocalFree(pEapTlsCb->pSavedPin->pwszPin); LocalFree(pEapTlsCb->pSavedPin); pEapTlsCb->pSavedPin = NULL; } if(NULL != pEapTlsCb->pUserProp) { GetMarshalledCredFromHash( pEapTlsCb->pUserProp->Hash.pbHash, pEapTlsCb->pUserProp->Hash.cbHash, pCreds->szUserName, UNLEN); } pCreds->dwFlags = RASCRED_EAP; done: *ppCredentials = (VOID *) pCreds; return dwRetCode; } DWORD DwGetGlobalConfig(DWORD dwEapTypeId, PBYTE *ppbData, DWORD *pdwSizeofData) { DWORD dwErr = NO_ERROR; if(PPP_EAP_TLS == dwEapTypeId) { dwErr = ServerConfigDataIO( TRUE, NULL, ppbData, 0); if( (NO_ERROR != dwErr) || (NULL == *ppbData)) { goto done; } *pdwSizeofData = ((EAPTLS_USER_PROPERTIES *)*ppbData)->dwSize; } else { // // Since peap is available only from .net, its fine to not // call the underlying eap for now. This will get a v1 version // of PEAP_USER_PROP and when InvokeServerConfigUI2 is called // it will be automatically upgraded. // dwErr = PeapServerConfigDataIO( TRUE, NULL, ppbData, 0); if( (NO_ERROR != dwErr) || (NULL == *ppbData)) { goto done; } *pdwSizeofData = ((PEAP_USER_PROP *)*ppbData)->dwSize; } done: return dwErr; } /* * Concat pAttr2 list to pAttr1 forming pAttrOut */ DWORD RasAuthAttributeConcat ( IN RAS_AUTH_ATTRIBUTE * pAttr1, IN RAS_AUTH_ATTRIBUTE * pAttr2, OUT RAS_AUTH_ATTRIBUTE ** ppAttrOut ) { DWORD dwRetCode = NO_ERROR; RAS_AUTH_ATTRIBUTE * pAttrTemp = NULL; DWORD dwIndex = 0; EapTlsTrace("RasAuthAttributeConcat"); if (NULL != pAttr2) { for( dwIndex = 0; pAttr2[dwIndex].raaType != raatMinimum; dwIndex++ ); pAttrTemp = RasAuthAttributeCopyWithAlloc ( pAttr1, dwIndex ); if (NULL == pAttrTemp ) { dwRetCode = GetLastError(); EapTlsTrace("RasAuthAttributeCopyWithAlloc failed and returned %d", dwRetCode); goto done; } for ( dwIndex =0; pAttr2[dwIndex].raaType != raatMinimum; dwIndex++ ) { // dwRetCode = RasAuthAttributeInsert( dwIndex , pAttrTemp, pAttr2[dwIndex].raaType, FALSE, pAttr2[dwIndex].dwLength, pAttr2[dwIndex].Value ); if ( dwRetCode != NO_ERROR ) { EapTlsTrace ("RasAuthAttributeInsert failed and returned 0x%x", dwRetCode ); goto done; } } } else { pAttrTemp = RasAuthAttributeCopyWithAlloc ( pAttr1, 0 ); if (NULL == pAttrTemp ) { dwRetCode = GetLastError(); EapTlsTrace("RasAuthAttributeCopyWithAlloc failed and returned %d", dwRetCode); goto done; } } *ppAttrOut = pAttrTemp; pAttrTemp = NULL; done: if ( NULL != pAttrTemp ) { RasAuthAttributeDestroy(pAttrTemp); } return dwRetCode; } /* This routine adds PEAP attributes to the array of attribs to be returned back to the caller. */ DWORD PeapAddContextAttributes( IN PEAPCB* pPeapCb ) { DWORD dwErr = NO_ERROR; RAS_AUTH_ATTRIBUTE * pAttrTemp = NULL; EapTlsTrace("PeapAddContextAttributes"); pAttrTemp = RasAuthAttributeCopyWithAlloc ( pPeapCb->pTlsUserAttributes, 2 ); if (NULL == pAttrTemp ) { dwErr = GetLastError(); EapTlsTrace("RasAuthAttributeCopyWithAlloc failed and returned %d", dwErr); goto LDone; } if ( pPeapCb->pTlsUserAttributes ) { RasAuthAttributeDestroy ( pPeapCb->pTlsUserAttributes ); } pPeapCb->pTlsUserAttributes = pAttrTemp; // // This is important so that we dont crash at End time. // pPeapCb->pEapTlsCB->pAttributes = pPeapCb->pTlsUserAttributes; // // Add Embedded Eap Type used and If the session was a fast reconnect // attribs here. raatPEAPEmbeddedEAPTypeId and raatPEAPFastRoamedSession // dwErr = RasAuthAttributeInsert( 0, pPeapCb->pTlsUserAttributes, raatPEAPFastRoamedSession, FALSE, sizeof(DWORD), (PVOID)ULongToPtr(pPeapCb->fFastReconnectedSession)); if ( NO_ERROR != dwErr ) { EapTlsTrace("RasAuthAttributeInsert failed and returned %d", dwErr); goto LDone; } dwErr = RasAuthAttributeInsert( 1, pPeapCb->pTlsUserAttributes, raatPEAPEmbeddedEAPTypeId, FALSE, sizeof(DWORD), (PVOID)ULongToPtr(pPeapCb->pEapInfo->dwTypeId)); if ( NO_ERROR != dwErr ) { EapTlsTrace("RasAuthAttributeInsert failed and returned %d", dwErr); goto LDone; } LDone: return(dwErr); } // // Check to see if the cert has been renewed // - Stolen from IIS Admin tool. Needs to be // cleaned up in future releases. // #define CB_SHA_DIGEST_LEN 20 BOOL CheckForCertificateRenewal( DWORD dwProtocol, PCCERT_CONTEXT pCertContext, PCCERT_CONTEXT *ppNewCertificate) { BYTE rgbThumbprint[CB_SHA_DIGEST_LEN]; DWORD cbThumbprint = sizeof(rgbThumbprint); CRYPT_HASH_BLOB HashBlob; PCCERT_CONTEXT pNewCert; BOOL fMachineCert; PCRYPT_KEY_PROV_INFO pProvInfo = NULL; DWORD cbSize; HCERTSTORE hMyCertStore = 0; BOOL fRenewed = FALSE; HCERTSTORE g_hMyCertStore; if(dwProtocol & SP_PROT_SERVERS) { fMachineCert = TRUE; } else { fMachineCert = FALSE; } // // Loop through the linked list of renewed certificates, looking // for the last one. // while(TRUE) { // // Check for renewal property. // if(!CertGetCertificateContextProperty(pCertContext, CERT_RENEWAL_PROP_ID, rgbThumbprint, &cbThumbprint)) { // Certificate has not been renewed. break; } //DebugLog((DEB_TRACE, "Certificate has renewal property\n")); // // Determine whether to look in the local machine MY store // or the current user MY store. // if(!hMyCertStore) { if(CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbSize)) { //SafeAllocaAllocate(pProvInfo, cbSize); pProvInfo = (PCRYPT_KEY_PROV_INFO) LocalAlloc(LPTR,cbSize); if(pProvInfo == NULL) { break; } if(CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, &cbSize)) { if(pProvInfo->dwFlags & CRYPT_MACHINE_KEYSET) { fMachineCert = TRUE; } else { fMachineCert = FALSE; } } if (pProvInfo) { LocalFree(pProvInfo);pProvInfo=NULL; } //SafeAllocaFree(pProvInfo); } } // // Open up the appropriate MY store, and attempt to find // the new certificate. // if(!hMyCertStore) { if(fMachineCert) { g_hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,X509_ASN_ENCODING,0,CERT_SYSTEM_STORE_LOCAL_MACHINE,L"MY"); if(g_hMyCertStore) { hMyCertStore = g_hMyCertStore; } } else { hMyCertStore = CertOpenSystemStore(0, L"MY"); } if(!hMyCertStore) { //DebugLog((DEB_ERROR, "Error 0x%x opening %s MY certificate store!\n", GetLastError(),(fMachineCert ? "local machine" : "current user") )); break; } } HashBlob.cbData = cbThumbprint; HashBlob.pbData = rgbThumbprint; pNewCert = CertFindCertificateInStore(hMyCertStore, X509_ASN_ENCODING, 0, CERT_FIND_HASH, &HashBlob, NULL); if(pNewCert == NULL) { // Certificate has been renewed, but the new certificate // cannot be found. //DebugLog((DEB_ERROR, "New certificate cannot be found: 0x%x\n", GetLastError())); break; } // // Return the new certificate, but first loop back and see if it's been // renewed itself. // pCertContext = pNewCert; *ppNewCertificate = pNewCert; //DebugLog((DEB_TRACE, "Certificate has been renewed\n")); fRenewed = TRUE; } // // Cleanup. // if(hMyCertStore && hMyCertStore != g_hMyCertStore) { CertCloseStore(hMyCertStore, 0); } return fRenewed; } // // Match public key in the certificate context with // private key // DWORD MatchPublicPrivateKeys ( PCCERT_CONTEXT pCertContext, BOOL fSmartCardCert, // Is this a scard cert? LPWSTR lpwszPin ) { DWORD dwRetCode = NO_ERROR; CRYPT_KEY_PROV_INFO* pCryptKeyProvInfo = NULL; // // Provider context // HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; HCRYPTKEY hPubKey = 0; // // Verification Context // HCRYPTPROV hProvVerification = 0; BYTE bDataBuf[128] = {0}; DWORD dwDataLen = sizeof(bDataBuf)/sizeof(BYTE); DWORD cbData = 0; CHAR* pszPin = NULL; DWORD count = 0; DWORD dwSignLen =0; PBYTE pbSignature = NULL; EapTlsTrace ("MatchPublicPrivateKeys"); // Following steps are followed to match public and // private keys: // // Create a random blob of data // Open the crypto context for the cert context passed in // Export the public key blob from the cert and save it // Create Hash // Hash Random Data // Sign Hash. And save signature // Create a new verification crypto context // Import public key got from the cert // Create a new hash // Hash Random Data // Verify the signature to make sure that public key // matches private key. // if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData)) { dwRetCode = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed: 0x%x", dwRetCode); goto LDone; } pCryptKeyProvInfo = LocalAlloc(LPTR, cbData); if (NULL == pCryptKeyProvInfo) { dwRetCode = GetLastError(); EapTlsTrace("Out of memory"); goto LDone; } if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pCryptKeyProvInfo, &cbData)) { dwRetCode = GetLastError(); EapTlsTrace("CertGetCertificateContextProperty failed: 0x%x", dwRetCode); goto LDone; } if (!CryptAcquireContext( &hProv, pCryptKeyProvInfo->pwszContainerName, pCryptKeyProvInfo->pwszProvName, pCryptKeyProvInfo->dwProvType, (pCryptKeyProvInfo->dwFlags & ~CERT_SET_KEY_PROV_HANDLE_PROP_ID) | CRYPT_SILENT)) { dwRetCode = GetLastError(); EapTlsTrace("CryptAcquireContext failed: 0x%x", dwRetCode); goto LDone; } // // set the pin with prov param if needed // if ( fSmartCardCert && lpwszPin ) { count = WideCharToMultiByte( CP_UTF8, 0, lpwszPin, -1, NULL, 0, NULL, NULL); if (0 == count) { dwRetCode = GetLastError(); EapTlsTrace("WideCharToMultiByte failed: %d", dwRetCode); goto LDone; } pszPin = LocalAlloc(LPTR, count); if (NULL == pszPin) { dwRetCode = GetLastError(); EapTlsTrace("LocalAlloc failed: 0x%x", dwRetCode); goto LDone; } count = WideCharToMultiByte( CP_UTF8, 0, lpwszPin, -1, pszPin, count, NULL, NULL); if (0 == count) { dwRetCode = GetLastError(); EapTlsTrace("WideCharToMultiByte failed: %d", dwRetCode); goto LDone; } if (!CryptSetProvParam( hProv, PP_KEYEXCHANGE_PIN, pszPin, 0)) { dwRetCode = GetLastError(); EapTlsTrace("CryptSetProvParam failed: 0x%x", dwRetCode); if ( dwRetCode != SCARD_W_WRONG_CHV && dwRetCode != SCARD_E_INVALID_CHV ) { dwRetCode = SCARD_E_INVALID_CHV; } goto LDone; } } // // Acquire the verification context // if ( !CryptAcquireContext( &hProvVerification, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT| CRYPT_SILENT ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptAcquireContext for verification failed: 0x%x", dwRetCode); goto LDone; } if (!CryptGenRandom ( hProvVerification, dwDataLen, bDataBuf ) ) { dwRetCode = GetLastError(); EapTlsTrace ("CryptGenRandom failed: 0x%x", dwRetCode ); goto LDone; } if ( !CryptCreateHash( hProv, CALG_MD5, 0, 0, &hHash ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptCreateHash failed: 0x%x", dwRetCode ); goto LDone; } if(!CryptHashData(hHash, bDataBuf, dwDataLen, 0 ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptHashData failed: 0x%x", dwRetCode ); goto LDone; } if(!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &dwSignLen) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptSignHash failed: 0x%x", dwRetCode ); goto LDone; } //-------------------------------------------------------------------- // Allocate memory for the signature buffer. pbSignature = (BYTE *)LocalAlloc(LPTR, dwSignLen); if (NULL == pCryptKeyProvInfo) { dwRetCode = GetLastError(); EapTlsTrace("Out of memory"); goto LDone; } if(! CryptSignHash( hHash, AT_KEYEXCHANGE, NULL, 0, pbSignature, &dwSignLen) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptSignHash failed: 0x%x", dwRetCode ); goto LDone; } if (hHash) { CryptDestroyHash(hHash); hHash = 0; } // // Import the public key info from the cert into // the verification context // if ( !CryptImportPublicKeyInfo ( hProvVerification, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), &hPubKey ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptImportPublicKeyInfo failed: 0x%x", dwRetCode ); goto LDone; } if ( !CryptCreateHash(hProvVerification, CALG_MD5, 0, 0, &hHash ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptCreateHash on verification context failed: 0x%x", dwRetCode ); goto LDone; } if(!CryptHashData(hHash, bDataBuf, dwDataLen, 0 ) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptHashData failed: 0x%x", dwRetCode ); goto LDone; } if(!CryptVerifySignature(hHash, pbSignature, dwSignLen, hPubKey, NULL, 0) ) { dwRetCode = GetLastError(); EapTlsTrace("CryptVerifySignature failed: 0x%x", dwRetCode ); goto LDone; } // Yohoo...public and private keys match... LDone: if ( pszPin ) LocalFree(pszPin); if ( pCryptKeyProvInfo ) LocalFree (pCryptKeyProvInfo); if ( pbSignature ) LocalFree( pbSignature ); if ( hHash ) CryptDestroyHash(hHash); if ( hPubKey ) CryptDestroyKey(hPubKey); if ( hProv ) CryptReleaseContext(hProv,0); if ( hProvVerification ) CryptReleaseContext(hProvVerification ,0); return dwRetCode; } // // Set the type attributes in the PEAP CONTROL BLOCK // filter out the MPPE keys if any returned by the embedded // EAP method. // DWORD PeapSetTypeUserAttributes ( IN PEAPCB * pPeapCb, RAS_AUTH_ATTRIBUTE * pAttr) { DWORD dwRetCode = NO_ERROR; DWORD dwIndex = 0; DWORD dwAttributeCount = 0; DWORD dwAttribIndex; EapTlsTrace("PeapSetTypeUserAttributes"); if ( pAttr ) { for( dwIndex = 0; pAttr[dwIndex].raaType != raatMinimum; dwIndex++ ) { if ( pAttr[dwIndex].raaType == raatVendorSpecific ) { //Check to see if this is MPPE Key attribute if ( ((PBYTE)(pAttr[dwIndex].Value))[4] != 16 && ((PBYTE)(pAttr[dwIndex].Value))[4] != 17 ) { dwAttributeCount++; } } else { dwAttributeCount++; } } pPeapCb->pTypeUserAttributes = RasAuthAttributeCreate ( dwAttributeCount ); if (NULL == pPeapCb->pTypeUserAttributes ) { dwRetCode = GetLastError(); EapTlsTrace("RasAutAttributeCreate failed and returned %d", dwRetCode); goto done; } dwAttribIndex = 0; for ( dwIndex =0; pAttr[dwIndex].raaType != raatMinimum; dwIndex++ ) { // if ( pAttr[dwIndex].raaType == raatVendorSpecific ) { if ( ((PBYTE)(pAttr[dwIndex].Value))[4] != 16 && ((PBYTE)(pAttr[dwIndex].Value))[4] != 17 ) { dwRetCode = RasAuthAttributeInsert( dwAttribIndex , pPeapCb->pTypeUserAttributes, pAttr[dwIndex].raaType, FALSE, pAttr[dwIndex].dwLength, pAttr[dwIndex].Value ); if ( dwRetCode != NO_ERROR ) { EapTlsTrace ("RasAuthAttributeInsert failed and returned 0x%x", dwRetCode ); goto done; } dwAttribIndex ++; } } else { dwRetCode = RasAuthAttributeInsert( dwAttribIndex , pPeapCb->pTypeUserAttributes, pAttr[dwIndex].raaType, FALSE, pAttr[dwIndex].dwLength, pAttr[dwIndex].Value ); if ( dwRetCode != NO_ERROR ) { EapTlsTrace ("RasAuthAttributeInsert failed and returned 0x%x", dwRetCode ); goto done; } dwAttribIndex ++; } } } done: if ( NO_ERROR != dwRetCode ) { if ( pPeapCb->pTypeUserAttributes ) { RasAuthAttributeDestroy ( pPeapCb->pTypeUserAttributes ); pPeapCb->pTypeUserAttributes = NULL; } } return dwRetCode; }