You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6760 lines
179 KiB
6760 lines
179 KiB
/*
|
|
|
|
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 <nt.h> // Required by windows.h
|
|
#include <ntrtl.h> // Required by windows.h
|
|
#include <nturtl.h> // Required by windows.h
|
|
#include <windows.h> // Win32 base API's
|
|
|
|
#include <rasauth.h> // Required by raseapif.h
|
|
#include <rtutils.h> // For RTASSERT
|
|
#include <rasman.h> // For EAPLOGONINFO
|
|
#include <wintrust.h>
|
|
#include <softpub.h>
|
|
#include <mscat.h>
|
|
#include <wincred.h>
|
|
#define STRSAFE_NO_DEPRECATE
|
|
#include <strsafe.h>
|
|
|
|
#define SECURITY_WIN32
|
|
#include <security.h> // For GetUserNameExA, CredHandle
|
|
#include <schannel.h>
|
|
#include <sspi.h> // For CredHandle
|
|
|
|
|
|
#include <wincrypt.h> // Required by sclogon.h
|
|
#include <winscard.h> // For SCardListReadersA
|
|
#include <sclogon.h> // For ScHelperGetCertFromLogonInfo
|
|
#include <cryptui.h>
|
|
#include <stdlib.h>
|
|
#include <raserror.h>
|
|
#include <commctrl.h>
|
|
#include <eaptypeid.h>
|
|
#include <eaptls.h>
|
|
|
|
#define INCL_RASAUTHATTRIBUTES
|
|
#include <ppputil.h>
|
|
|
|
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; i<dwSizeOfHashBlob; i++)
|
|
{
|
|
ZeroMemory(cszCharConv, SIZE_OF_CA_CONV_STR);
|
|
cszCharConv[0]=pszCARootHash[2*i];
|
|
cszCharConv[1]=pszCARootHash[2*i+1];
|
|
pbRootHashBlob[i] = (BYTE)strtol(cszCharConv, NULL, 16);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
EapTlsTrace("ReadCARootHashes: 802.1X Policy Parameters RegQueryValueEx 1 failed with error (%ld)",
|
|
lError);
|
|
goto LDone;
|
|
}
|
|
|
|
LDone:
|
|
|
|
if (lError != ERROR_SUCCESS)
|
|
{
|
|
if (pbRootHashBlob != NULL)
|
|
{
|
|
LocalFree(pbRootHashBlob);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppbRootHashBlob = pbRootHashBlob;
|
|
*pdwSizeOfRootHashBlob = dwSizeOfHashBlob;
|
|
}
|
|
|
|
|
|
if (hKey != NULL)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (pszCARootHash != NULL)
|
|
{
|
|
LocalFree(pszCARootHash);
|
|
}
|
|
|
|
return lError;
|
|
}
|
|
|
|
#endif
|
|
|
|
/////////////////////// ALL PEAP related utils go here ///////////////////////////
|
|
|
|
PEAP_ENTRY_USER_PROPERTIES UNALIGNED *
|
|
PeapFindEntryUserProp ( PPEAP_USER_PROP pUserProp,
|
|
DWORD dwTypeId
|
|
)
|
|
{
|
|
PEAP_ENTRY_USER_PROPERTIES UNALIGNED * pEntryProp = NULL;
|
|
DWORD dwCount = 0;
|
|
PeapGetFirstEntryUserProp ( pUserProp,
|
|
&pEntryProp
|
|
);
|
|
for ( dwCount = 0; dwCount < pUserProp->dwNumberOfEntries;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;
|
|
}
|