|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 2000
//
// File: autoenro.cpp
//
//--------------------------------------------------------------------------
#include <windows.h>
#include <winuser.h>
#include <wincrypt.h>
#include <cryptui.h>
#include <lmcons.h>
#include <lmapibuf.h>
#include <dsgetdc.h>
#include <oleauto.h>
#define SECURITY_WIN32
#include <rpc.h>
#include <security.h>
#include <winldap.h>
#include <dsrole.h>
#include <shobjidl.h>
#include <shellapi.h>
#include <commctrl.h>
#include <winscard.h>
#include <Rpcdce.h>
#include <certca.h>
#include <certsrv.h>
#include <autoenr.h>
#include <autoenro.h>
#include <autolog.h>
#include <resource.h>
#include <xenroll.h>
//*******************************************************************************
//
//
// Global Defines and Data Structures
//
//
//*******************************************************************************
HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself.
#if DBG
DWORD g_AutoenrollDebugLevel = AE_ERROR; //| AE_WARNING | AE_INFO | AE_TRACE;
#endif
//when we look at supersede relationship, we based on the following order
DWORD g_rgdwSupersedeOrder[]={CERT_REQUEST_STATUS_OBTAINED, CERT_REQUEST_STATUS_ACTIVE, CERT_REQUEST_STATUS_PENDING, CERT_REQUEST_STATUS_SUPERSEDE_ACTIVE};
DWORD g_dwSupersedeOrder=sizeof(g_rgdwSupersedeOrder)/sizeof(g_rgdwSupersedeOrder[0]);
//the list of certificate store to update
AE_STORE_INFO g_rgStoreInfo[]={ L"ROOT", L"ldap://%s/CN=Certification Authorities,CN=Public Key Services,CN=Services,%s?cACertificate?one?objectCategory=certificationAuthority", L"NTAuth", L"ldap://%s/CN=Public Key Services,CN=Services,%s?cACertificate?one?cn=NTAuthCertificates", L"CA", L"ldap://%s/CN=AIA,CN=Public Key Services,CN=Services,%s?crossCertificatePair,cACertificate?one?objectCategory=certificationAuthority" };
DWORD g_dwStoreInfo=sizeof(g_rgStoreInfo)/sizeof(g_rgStoreInfo[0]);
typedef IEnroll4 * (WINAPI *PFNPIEnroll4GetNoCOM)();
static WCHAR * s_wszLocation = L"CN=Public Key Services,CN=Services,";
//Enhanced key usage for DS email replication
#ifndef CT_FLAG_REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE
#define CT_FLAG_REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE 0x00000400
#endif
//*******************************************************************************
//
//
// Implementation of IQueryContinue for use autoenrollment notification
//
//
//*******************************************************************************
//--------------------------------------------------------------------------
// CQueryContinue
//--------------------------------------------------------------------------
CQueryContinue::CQueryContinue() { m_cRef=1; m_pIUserNotification=NULL; m_hTimer=NULL; }
//--------------------------------------------------------------------------
// ~CQueryContinue
//--------------------------------------------------------------------------
CQueryContinue::~CQueryContinue() {
}
//--------------------------------------------------------------------------
// CQueryContinue
//--------------------------------------------------------------------------
HRESULT CQueryContinue::QueryInterface(REFIID riid, void **ppv) { if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IQueryContinue)) { *ppv=(LPVOID)this; AddRef(); return S_OK; }
return E_NOINTERFACE; }
//--------------------------------------------------------------------------
// AddRef
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CQueryContinue::AddRef() { return InterlockedIncrement(&m_cRef); }
//--------------------------------------------------------------------------
// Release
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CQueryContinue::Release() { if (InterlockedDecrement(&m_cRef)) return m_cRef;
delete this; return 0; }
//--------------------------------------------------------------------------
// CQueryContinue
//--------------------------------------------------------------------------
HRESULT CQueryContinue::QueryContinue() { //disable the balloon
if(m_pIUserNotification) m_pIUserNotification->SetBalloonInfo(NULL, NULL, NIIF_INFO);
//wait for the timer to be activated
if(m_hTimer) { if(WAIT_OBJECT_0 == WaitForSingleObject(m_hTimer, 0)) return S_FALSE; } return S_OK; }
//--------------------------------------------------------------------------
// DoBalloon()
//--------------------------------------------------------------------------
HRESULT CQueryContinue::DoBalloon() {
HRESULT hr=E_FAIL; WCHAR wszTitle[MAX_DN_SIZE]; WCHAR wszText[MAX_DN_SIZE]; HICON hIcon=NULL; LARGE_INTEGER DueTime;
if(S_OK != (hr=CoCreateInstance(CLSID_UserNotification, NULL, CLSCTX_ALL, IID_IUserNotification, (void **)&m_pIUserNotification))) goto Ret;
if(NULL==m_pIUserNotification) { hr=E_FAIL; goto Ret; }
//create a waitable timer with default security setting
m_hTimer=CreateWaitableTimer(NULL, TRUE, NULL);
if(NULL==m_hTimer) { hr=E_FAIL; goto Ret; }
//set the timer
DueTime.QuadPart = Int32x32To64(-10000, AUTO_ENROLLMENT_BALLOON_LENGTH * 1000);
if(!SetWaitableTimer(m_hTimer, &DueTime, 0, NULL, 0, FALSE)) { hr=E_FAIL; goto Ret; }
if(S_OK != (hr=m_pIUserNotification->SetBalloonRetry(AUTO_ENROLLMENT_SHOW_TIME * 1000, AUTO_ENROLLMENT_INTERVAL * 1000, AUTO_ENROLLMENT_RETRIAL))) goto Ret;
if((!LoadStringW(g_hmodThisDll,IDS_ICON_TIP, wszText, MAX_DN_SIZE)) || (NULL==(hIcon=LoadIcon(g_hmodThisDll, MAKEINTRESOURCE(IDI_AUTOENROLL_ICON))))) { hr=E_FAIL; goto Ret; }
if(S_OK != (hr=m_pIUserNotification->SetIconInfo(hIcon, wszText))) goto Ret;
if((!LoadStringW(g_hmodThisDll,IDS_BALLOON_TITLE, wszTitle, MAX_DN_SIZE)) || (!LoadStringW(g_hmodThisDll,IDS_BALLOON_TEXT, wszText, MAX_DN_SIZE))) { hr=E_FAIL; goto Ret; }
if(S_OK !=(hr=m_pIUserNotification->SetBalloonInfo(wszTitle, wszText, NIIF_INFO))) goto Ret;
//user did not click on the icon or we time out
hr= m_pIUserNotification->Show(this, AUTO_ENROLLMENT_QUERY_INTERVAL * 1000);
Ret: if(m_hTimer) { CloseHandle(m_hTimer); m_hTimer=NULL; }
if(m_pIUserNotification) { m_pIUserNotification->Release(); m_pIUserNotification=NULL; }
return hr; }
//*******************************************************************************
//
//
// Functions for autoenrollment
//
//
//*******************************************************************************
//--------------------------------------------------------------------------
//
// Name: FindCertificateInOtherStore
//
//--------------------------------------------------------------------------
PCCERT_CONTEXT FindCertificateInOtherStore( IN HCERTSTORE hOtherStore, IN PCCERT_CONTEXT pCert ) { BYTE rgbHash[SHA1_HASH_LENGTH]; CRYPT_DATA_BLOB HashBlob;
HashBlob.pbData = rgbHash; HashBlob.cbData = SHA1_HASH_LENGTH; if (!CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbHash, &HashBlob.cbData ) || SHA1_HASH_LENGTH != HashBlob.cbData) return NULL;
return CertFindCertificateInStore( hOtherStore, ENCODING_TYPE, // dwCertEncodingType
0, // dwFindFlags
CERT_FIND_SHA1_HASH, (const void *) &HashBlob, NULL //pPrevCertContext
); }
//--------------------------------------------------------------------------
//
// AEUpdateCertificateStore
//
// Description: This function enumerates all of the certificate in the DS based
// LdapPath, and moves them into the corresponding local machine store.
//
//--------------------------------------------------------------------------
HRESULT WINAPI AEUpdateCertificateStore(LDAP *pld, LPWSTR pwszConfig, LPWSTR pwszDCName, LPWSTR pwszStoreName, LPWSTR pwszLdapPath) { HRESULT hr = S_OK; PCCERT_CONTEXT pContext = NULL, pOtherCert = NULL;
LPWSTR pwszLdapStore = NULL; HCERTSTORE hEnterpriseStore = NULL, hLocalStore = NULL;
if((NULL==pld) || (NULL==pwszConfig) || (NULL==pwszDCName) || (NULL==pwszStoreName) || (NULL==pwszLdapPath)) { hr = E_INVALIDARG; goto error; }
pwszLdapStore = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(pwszConfig)+wcslen(pwszDCName)+wcslen(pwszLdapPath))); if(pwszLdapStore == NULL) { hr = E_OUTOFMEMORY; goto error; }
wsprintf(pwszLdapStore, pwszLdapPath, pwszDCName, pwszConfig);
hLocalStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE, pwszStoreName); if(hLocalStore == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); AE_DEBUG((AE_ERROR, L"Unable to open ROOT store (%lx)\n\r", hr)); goto error; }
hEnterpriseStore = CertOpenStore(CERT_STORE_PROV_LDAP, 0, 0, CERT_STORE_READONLY_FLAG | CERT_LDAP_STORE_SIGN_FLAG | CERT_LDAP_STORE_AREC_EXCLUSIVE_FLAG, pwszLdapStore); if(hEnterpriseStore == NULL) { DWORD err = GetLastError();
if((err == ERROR_FILE_NOT_FOUND)) { // There was no store, so there are no certs
hr = S_OK; goto error; }
hr = HRESULT_FROM_WIN32(err);
AE_DEBUG((AE_ERROR, L"Unable to open ROOT store (%lx)\n\r", hr)); goto error; }
while(pContext = CertEnumCertificatesInStore(hEnterpriseStore, pContext)) { if (pOtherCert = FindCertificateInOtherStore(hLocalStore, pContext)) { CertFreeCertificateContext(pOtherCert); } else { CertAddCertificateContextToStore(hLocalStore, pContext, CERT_STORE_ADD_ALWAYS, NULL); } }
while(pContext = CertEnumCertificatesInStore(hLocalStore, pContext)) { if (pOtherCert = FindCertificateInOtherStore(hEnterpriseStore, pContext)) { CertFreeCertificateContext(pOtherCert); } else { CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pContext)); } }
error:
if(hr != S_OK) { AELogAutoEnrollmentEvent( STATUS_SEVERITY_ERROR, //this event will always be logged
TRUE, hr, EVENT_FAIL_DOWNLOAD_CERT, TRUE, NULL, 3, pwszStoreName, pwszConfig, pwszLdapPath);
}
if(pwszLdapStore) { LocalFree(pwszLdapStore); }
if(hEnterpriseStore) { CertCloseStore(hEnterpriseStore,0); }
if(hLocalStore) { CertCloseStore(hLocalStore,0); }
return hr; }
//--------------------------------------------------------------------------
//
// AENeedToUpdateDSCache
//
//--------------------------------------------------------------------------
BOOL AENeedToUpdateDSCache(LDAP *pld, LPWSTR pwszDCInvocationID, LPWSTR pwszConfig, AE_DS_INFO *pAEDSInfo) { BOOL fNeedToUpdate=TRUE; DWORD dwRegObject=0; ULARGE_INTEGER maxRegUSN; ULARGE_INTEGER maxDsUSN; DWORD dwType=0; DWORD dwSize=0; DWORD dwDisp=0; struct l_timeval timeout; LPWSTR rgwszAttrs[] = {AUTO_ENROLLMENT_USN_ATTR, NULL}; LDAPMessage *Entry=NULL;
LPWSTR *awszValue = NULL; HKEY hDSKey=NULL; HKEY hDCKey=NULL; LDAPMessage *SearchResult = NULL; LPWSTR pwszContainer=NULL;
if((NULL==pld) || (NULL==pwszDCInvocationID) || (NULL==pwszConfig) || (NULL==pAEDSInfo)) goto error;
//init
memset(pAEDSInfo, 0, sizeof(AE_DS_INFO));
//compute the # of objects and maxUSN from the directory
pwszContainer=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (1 + wcslen(pwszConfig) + wcslen(s_wszLocation))); if(NULL == pwszContainer) goto error; wcscpy(pwszContainer, s_wszLocation); wcscat(pwszContainer, pwszConfig);
timeout.tv_sec = 300; timeout.tv_usec = 0; if(LDAP_SUCCESS != ldap_search_stW( pld, pwszContainer, LDAP_SCOPE_SUBTREE, L"(objectCategory=certificationAuthority)", rgwszAttrs, 0, &timeout, &SearchResult)) goto error;
//get the # of objects
pAEDSInfo->dwObjects = ldap_count_entries(pld, SearchResult);
for(Entry = ldap_first_entry(pld, SearchResult); Entry != NULL; Entry = ldap_next_entry(pld, Entry)) {
awszValue = ldap_get_values(pld, Entry, AUTO_ENROLLMENT_USN_ATTR);
if(NULL==awszValue) goto error;
if(NULL==awszValue[0]) goto error;
maxDsUSN.QuadPart=0;
maxDsUSN.QuadPart=_wtoi64(awszValue[0]);
//if any error happens, maxDsUSN will be 0.
if(0 == maxDsUSN.QuadPart) goto error;
if((pAEDSInfo->maxUSN).QuadPart < maxDsUSN.QuadPart) (pAEDSInfo->maxUSN).QuadPart = maxDsUSN.QuadPart;
ldap_value_free(awszValue); awszValue=NULL; }
//signal that we have retrieved correct data from the directory
pAEDSInfo->fValidData=TRUE;
//find if we have cached any information about the DC of interest
if(ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, AUTO_ENROLLMENT_DS_KEY, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hDSKey, &dwDisp)) goto error;
if(ERROR_SUCCESS != RegOpenKeyEx( hDSKey, pwszDCInvocationID, 0, KEY_ALL_ACCESS, &hDCKey)) goto error;
dwSize=sizeof(dwRegObject);
if(ERROR_SUCCESS != RegQueryValueEx( hDCKey, AUTO_ENROLLMENT_DS_OBJECT, NULL, &dwType, (PBYTE)(&dwRegObject), &dwSize)) goto error;
if(REG_DWORD != dwType) goto error;
dwSize=sizeof(maxRegUSN);
if(ERROR_SUCCESS != RegQueryValueEx( hDCKey, AUTO_ENROLLMENT_DS_USN, NULL, &dwType, (PBYTE)(&(maxRegUSN)), &dwSize)) goto error;
if(REG_BINARY != dwType) goto error;
//compare the registry data with the data from directory
if(dwRegObject != (pAEDSInfo->dwObjects)) goto error;
if(maxRegUSN.QuadPart != ((pAEDSInfo->maxUSN).QuadPart)) goto error;
fNeedToUpdate=FALSE;
error: if(awszValue) ldap_value_free(awszValue);
if(pwszContainer) LocalFree(pwszContainer);
if(hDCKey) RegCloseKey(hDCKey);
if(hDSKey) RegCloseKey(hDSKey);
if(SearchResult) ldap_msgfree(SearchResult);
//remove the temporary data
if(pAEDSInfo) { if(FALSE == fNeedToUpdate) memset(pAEDSInfo, 0, sizeof(AE_DS_INFO)); }
return fNeedToUpdate; }
//--------------------------------------------------------------------------
//
// AEUpdateDSCache
//
//--------------------------------------------------------------------------
BOOL AEUpdateDSCache(LPWSTR pwszDCInvocationID, AE_DS_INFO *pAEDSInfo) {
BOOL fResult=FALSE; DWORD dwDisp=0;
HKEY hDSKey=NULL; HKEY hDCKey=NULL;
if((NULL==pwszDCInvocationID) || (NULL==pAEDSInfo)) goto error;
if(ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, AUTO_ENROLLMENT_DS_KEY, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hDSKey, &dwDisp)) goto error;
//create the key named by the DC
if(ERROR_SUCCESS != RegCreateKeyEx(hDSKey, pwszDCInvocationID, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hDCKey, &dwDisp)) goto error;
//set the # of objects value
if(ERROR_SUCCESS != RegSetValueEx(hDCKey, AUTO_ENROLLMENT_DS_OBJECT, NULL, REG_DWORD, (PBYTE)&(pAEDSInfo->dwObjects), sizeof(pAEDSInfo->dwObjects))) goto error;
//set the max uSN value
if(ERROR_SUCCESS != RegSetValueEx(hDCKey, AUTO_ENROLLMENT_DS_USN, NULL, REG_BINARY, (PBYTE)&(pAEDSInfo->maxUSN), sizeof(pAEDSInfo->maxUSN))) goto error;
fResult=TRUE;
error:
if(hDCKey) RegCloseKey(hDCKey);
if(hDSKey) RegCloseKey(hDSKey);
return fResult; }
//--------------------------------------------------------------------------
//
// AERetrieveInvocationID
//
//--------------------------------------------------------------------------
BOOL AERetrieveInvocationID(LDAP *pld, LPWSTR *ppwszID) { BOOL fResult=FALSE; struct l_timeval timeout; LPWSTR rgwszDSAttrs[] = {L"dsServiceName", NULL}; LPWSTR rgwszIDAttr[] = {L"invocationId", NULL}; LDAPMessage *Entry=NULL;
LPWSTR *awszValues = NULL; LDAPMessage *SearchResults = NULL; struct berval **apUUID = NULL; LDAPMessage *SearchIDResult = NULL; BYTE *pbUUID=NULL;
if((NULL==pld) || (NULL==ppwszID)) goto error;
*ppwszID=NULL;
//retrieve the dsSerivceName attribute
timeout.tv_sec = 300; timeout.tv_usec = 0;
if(LDAP_SUCCESS != ldap_search_stW( pld, NULL, //NULL DN for dsServiceName
LDAP_SCOPE_BASE, L"(objectCategory=*)", rgwszDSAttrs, 0, &timeout, &SearchResults)) goto error;
Entry = ldap_first_entry(pld, SearchResults);
if(NULL == Entry) goto error;
awszValues = ldap_get_values(pld, Entry, rgwszDSAttrs[0]);
if(NULL==awszValues) goto error;
if(NULL==awszValues[0]) goto error;
//retrieve the invocationId attribute
timeout.tv_sec = 300; timeout.tv_usec = 0;
if(LDAP_SUCCESS != ldap_search_stW( pld, awszValues[0], LDAP_SCOPE_BASE, L"(objectCategory=*)", rgwszIDAttr, 0, &timeout, &SearchIDResult)) goto error;
Entry = ldap_first_entry(pld, SearchIDResult);
if(NULL == Entry) goto error;
apUUID = ldap_get_values_len(pld, Entry, rgwszIDAttr[0]);
if(NULL == apUUID) goto error;
if(NULL == (*apUUID)) goto error;
pbUUID = (BYTE *)LocalAlloc(LPTR, (*apUUID)->bv_len);
if(NULL == (pbUUID)) goto error;
memcpy(pbUUID, (*apUUID)->bv_val, (*apUUID)->bv_len);
if(RPC_S_OK != UuidToStringW((UUID *)pbUUID, ppwszID)) goto error;
fResult=TRUE;
error:
if(pbUUID) LocalFree(pbUUID);
if(apUUID) ldap_value_free_len(apUUID);
if(SearchIDResult) ldap_msgfree(SearchIDResult);
if(awszValues) ldap_value_free(awszValues);
if(SearchResults) ldap_msgfree(SearchResults);
return fResult; }
//--------------------------------------------------------------------------
//
// AEDownloadStore
//
//--------------------------------------------------------------------------
BOOL WINAPI AEDownloadStore(LDAP *pld, LPWSTR pwszDCName) { BOOL fResult = TRUE; DWORD dwIndex = 0; AE_DS_INFO AEDSInfo;
LPWSTR wszConfig = NULL; LPWSTR pwszDCInvocationID = NULL;
memset(&AEDSInfo, 0, sizeof(AEDSInfo));
if(S_OK != AEGetConfigDN(pld, &wszConfig)) { fResult=FALSE; goto error; }
//get the pwszDCInvocationID. NULL means AENeedToUpdateDSCache will return TRUE
AERetrieveInvocationID(pld, &pwszDCInvocationID);
if(AENeedToUpdateDSCache(pld, pwszDCInvocationID, wszConfig, &AEDSInfo)) { for(dwIndex =0; dwIndex < g_dwStoreInfo; dwIndex++) { fResult = fResult && (S_OK == AEUpdateCertificateStore( pld, wszConfig, pwszDCName, g_rgStoreInfo[dwIndex].pwszStoreName, g_rgStoreInfo[dwIndex].pwszLdapPath)); }
//only update the new DS cached information if we have a successful download
if((fResult) && (TRUE == AEDSInfo.fValidData) && (pwszDCInvocationID)) AEUpdateDSCache(pwszDCInvocationID, &AEDSInfo); }
error:
if(pwszDCInvocationID) RpcStringFreeW(&pwszDCInvocationID);
if(wszConfig) { LocalFree(wszConfig); }
return fResult; }
//--------------------------------------------------------------------------
//
// AESetWakeUpFlag
//
// We set the flag to tell winlogon if autoenrollment should be waken up
// during each policy check
//
//--------------------------------------------------------------------------
BOOL WINAPI AESetWakeUpFlag(BOOL fMachine, BOOL fWakeUp) { BOOL fResult = FALSE; DWORD dwDisp = 0; DWORD dwFlags = 0;
HKEY hAEKey = NULL; if(ERROR_SUCCESS != RegCreateKeyEx( fMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, AUTO_ENROLLMENT_FLAG_KEY, 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hAEKey, &dwDisp)) goto Ret;
if(fWakeUp) dwFlags = AUTO_ENROLLMENT_WAKE_UP_REQUIRED;
if(ERROR_SUCCESS != RegSetValueEx( hAEKey, AUTO_ENROLLMENT_FLAG, 0, REG_DWORD, (PBYTE)&dwFlags, sizeof(dwFlags))) goto Ret;
fResult=TRUE;
Ret: if(hAEKey) RegCloseKey(hAEKey);
return fResult; }
//--------------------------------------------------------------------------
//
// AESetWakeUpTimer
//
// Set the timer to wake us up in 8 hrs
//
//--------------------------------------------------------------------------
BOOL WINAPI AESetWakeUpTimer(BOOL fMachine, LARGE_INTEGER *pPreTime, LARGE_INTEGER *pPostTime) { HRESULT hr; HKEY hKey; HKEY hCurrent; DWORD dwType, dwSize, dwResult; LONG lTimeout; LARGE_INTEGER DueTime; WCHAR * wszTimerName; LARGE_INTEGER EnrollmentTime;
// must be cleaned up
HANDLE hTimer=NULL;
// Build a timer event to ping us in about 8 hours if we don't get notified sooner.
lTimeout=AE_DEFAULT_REFRESH_RATE;
// Query for the refresh timer value
if (ERROR_SUCCESS==RegOpenKeyEx((fMachine?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER), SYSTEM_POLICIES_KEY, 0, KEY_READ, &hKey)) { dwSize=sizeof(lTimeout); RegQueryValueEx(hKey, TEXT("AutoEnrollmentRefreshTime"), NULL, &dwType, (LPBYTE) &lTimeout, &dwSize); RegCloseKey(hKey); }
// Limit the timeout to once every 240 hours (10 days)
if (lTimeout>=240) { lTimeout=240; } else if (lTimeout<0) { lTimeout=0; }
// Convert hours to milliseconds
lTimeout=lTimeout*60*60*1000;
// Special case 0 milliseconds to be 7 seconds
if (lTimeout==0) { lTimeout=7000; }
// convert to 10^-7s. not yet negative values are relative
DueTime.QuadPart=Int32x32To64(-10000, lTimeout);
// if user has hold on the UI for too long and the cycle passed the 8 hours.
// we set the time for 1 hour
EnrollmentTime.QuadPart=pPostTime->QuadPart - pPreTime->QuadPart;
if(EnrollmentTime.QuadPart > 0) { if((-(DueTime.QuadPart)) > EnrollmentTime.QuadPart) { DueTime.QuadPart = DueTime.QuadPart + EnrollmentTime.QuadPart; } else { // Convert hours to milliseconds
lTimeout=AE_DEFAULT_POSTPONE*60*60*1000; DueTime.QuadPart = Int32x32To64(-10000, lTimeout); } }
// find the timer
if (fMachine) { wszTimerName=L"Global\\" MACHINE_AUTOENROLLMENT_TIMER_NAME; } else { wszTimerName=USER_AUTOENROLLMENT_TIMER_NAME; } hTimer=OpenWaitableTimer(TIMER_MODIFY_STATE, false, wszTimerName); if (NULL==hTimer) { hr=HRESULT_FROM_WIN32(GetLastError()); AE_DEBUG((AE_ERROR, L"OpenWaitableTimer(%s) failed with 0x%08X.\n", wszTimerName, hr)); goto error; }
// set the timer
if (!SetWaitableTimer (hTimer, &DueTime, 0, NULL, 0, FALSE)) { hr=HRESULT_FROM_WIN32(GetLastError()); AE_DEBUG((AE_ERROR, L"SetWaitableTimer failed with 0x%08X.\n", hr)); goto error; }
AE_DEBUG((AE_INFO, L"Set wakeup timer.\n"));
hr=S_OK; error: if (NULL!=hTimer) { CloseHandle(hTimer); } return (S_OK==hr); }
//--------------------------------------------------------------------------
//
// AEGetPendingRequestProperty
//
//--------------------------------------------------------------------------
BOOL AEGetPendingRequestProperty(IEnroll4 *pIEnroll4, DWORD dwIndex, DWORD dwProp, LPVOID pProp) { CRYPT_DATA_BLOB *pBlob=NULL; BOOL fResult=FALSE;
if((NULL==pIEnroll4) || (NULL==pProp)) return FALSE;
switch(dwProp) { case XEPR_REQUESTID: case XEPR_DATE: case XEPR_VERSION: fResult = (S_OK == pIEnroll4->enumPendingRequestWStr(dwIndex, dwProp, pProp)); break; case XEPR_CANAME: case XEPR_CAFRIENDLYNAME: case XEPR_CADNS: case XEPR_V1TEMPLATENAME: case XEPR_V2TEMPLATEOID: case XEPR_HASH: pBlob=(CRYPT_DATA_BLOB *)pProp;
pBlob->cbData=0; pBlob->pbData=NULL;
if(S_OK != pIEnroll4->enumPendingRequestWStr(dwIndex, dwProp, pProp)) goto Ret;
if(0 == pBlob->cbData) goto Ret;
pBlob->pbData=(BYTE *)LocalAlloc(LPTR, pBlob->cbData); if(NULL == pBlob->pbData) goto Ret;
fResult = (S_OK == pIEnroll4->enumPendingRequestWStr(dwIndex, dwProp, pProp));
break;
default: break; }
Ret: if(FALSE==fResult) { if(pBlob) { if(pBlob->pbData) LocalFree(pBlob->pbData);
memset(pBlob, 0, sizeof(CRYPT_DATA_BLOB)); } }
return fResult; } //--------------------------------------------------------------------------
//
// AERetrieveRequestProperty
//
//--------------------------------------------------------------------------
BOOL AERetrieveRequestProperty(IEnroll4 *pIEnroll4, DWORD dwIndex, DWORD *pdwCount, DWORD *pdwMax, CRYPT_DATA_BLOB **prgblobHash) { BOOL fResult=FALSE; CRYPT_DATA_BLOB *pblobHash=NULL;
if((NULL==pIEnroll4) || (NULL==pdwCount) || (NULL==pdwMax) || (NULL==prgblobHash) || (NULL==*prgblobHash)) goto Ret;
//need to alloc more memory
if((*pdwCount) >= (*pdwMax)) { pblobHash=*prgblobHash;
*prgblobHash=(CRYPT_DATA_BLOB *)LocalAlloc(LPTR, ((*pdwMax) + PENDING_ALLOC_SIZE) * sizeof(CRYPT_DATA_BLOB)); if(NULL==(*prgblobHash)) { *prgblobHash=pblobHash; pblobHash=NULL; goto Ret; }
memset(*prgblobHash, 0, ((*pdwMax) + PENDING_ALLOC_SIZE) * sizeof(CRYPT_DATA_BLOB));
//copy the old memmory
memcpy(*prgblobHash, pblobHash, (*pdwMax) * sizeof(CRYPT_DATA_BLOB));
*pdwMax=(*pdwMax) + PENDING_ALLOC_SIZE; }
if(!AEGetPendingRequestProperty(pIEnroll4, dwIndex, XEPR_HASH, &((*prgblobHash)[*pdwCount]))) goto Ret;
(*pdwCount)=(*pdwCount) + 1;
fResult=TRUE;
Ret:
if(pblobHash) LocalFree(pblobHash);
return fResult; }
//--------------------------------------------------------------------------
//
// AERemovePendingRequest
//
//--------------------------------------------------------------------------
BOOL AERemovePendingRequest(IEnroll4 *pIEnroll4, DWORD dwCount, CRYPT_DATA_BLOB *rgblobHash) { DWORD dwIndex=0; BOOL fResult=TRUE;
if((NULL==pIEnroll4) || (NULL==rgblobHash)) return FALSE;
for(dwIndex=0; dwIndex < dwCount; dwIndex++) { if(S_OK != (pIEnroll4->removePendingRequestWStr(rgblobHash[dwIndex]))) fResult=FALSE; }
return fResult; }
//--------------------------------------------------------------------------
//
// AEFreePendingRequests
//
//--------------------------------------------------------------------------
BOOL AEFreePendingRequests(DWORD dwCount, CRYPT_DATA_BLOB *rgblobHash) { DWORD dwIndex=0;
if(rgblobHash) { for(dwIndex=0; dwIndex < dwCount; dwIndex++) { if(rgblobHash[dwIndex].pbData) LocalFree(rgblobHash[dwIndex].pbData); }
LocalFree(rgblobHash); }
return TRUE; }
//--------------------------------------------------------------------------
//
// AEValidVersionCert
//
// Verify the certificate returned from CA has the latest version info.
// If so, copy the certificate to the hIssuedStore for potentical publishing
//
//--------------------------------------------------------------------------
BOOL AEValidVersionCert(AE_CERTTYPE_INFO *pCertType, IEnroll4 *pIEnroll4, CRYPT_DATA_BLOB *pBlobPKCS7) { BOOL fValid=FALSE;
PCCERT_CONTEXT pCertContext=NULL; AE_TEMPLATE_INFO AETemplateInfo;
memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
if((NULL==pCertType) || (NULL==pIEnroll4) || (NULL==pBlobPKCS7)) goto Ret;
if(NULL==(pBlobPKCS7->pbData)) goto Ret;
if(S_OK != pIEnroll4->getCertContextFromResponseBlob(pBlobPKCS7, &pCertContext)) goto Ret;
if(!AERetrieveTemplateInfo(pCertContext, &AETemplateInfo)) goto Ret;
if(AETemplateInfo.pwszOid) { if(AETemplateInfo.dwVersion >= (pCertType->dwVersion)) fValid=TRUE; } else { //V1 template
if(NULL == AETemplateInfo.pwszName) goto Ret;
fValid=TRUE; }
if(pCertContext && (TRUE == fValid)) { CertAddCertificateContextToStore(pCertType->hIssuedStore, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL); }
Ret: if(pCertContext) CertFreeCertificateContext(pCertContext);
AEFreeTemplateInfo(&AETemplateInfo);
return fValid; }
//--------------------------------------------------------------------------
//
// AECopyPendingBlob
//
// Copy the issued PKCS7 and request hash.
//
//--------------------------------------------------------------------------
BOOL AECopyPendingBlob(CRYPT_DATA_BLOB *pBlobPKCS7, IEnroll4 *pIEnroll4, DWORD dwXenrollIndex, AE_CERTTYPE_INFO *pCertType) { BOOL fResult=FALSE; DWORD dwIndex=0;
AE_PEND_INFO *pPendInfo=NULL;
if((NULL==pBlobPKCS7)||(NULL==pIEnroll4)||(NULL==pCertType)) goto Ret;
if(NULL==(pBlobPKCS7->pbData)) goto Ret;
dwIndex=pCertType->dwPendCount;
//increase the memory array
if(0 != dwIndex) { pPendInfo=pCertType->rgPendInfo;
pCertType->rgPendInfo=(AE_PEND_INFO *)LocalAlloc(LPTR, (dwIndex + 1) * sizeof(AE_PEND_INFO));
if(NULL==(pCertType->rgPendInfo)) { pCertType->rgPendInfo=pPendInfo; pPendInfo=NULL; goto Ret; }
memset(pCertType->rgPendInfo, 0, (dwIndex + 1) * sizeof(AE_PEND_INFO));
//copy the old memmory
memcpy(pCertType->rgPendInfo, pPendInfo, (dwIndex) * sizeof(AE_PEND_INFO)); } else { pCertType->rgPendInfo=(AE_PEND_INFO *)LocalAlloc(LPTR, sizeof(AE_PEND_INFO));
if(NULL==(pCertType->rgPendInfo)) goto Ret;
memset(pCertType->rgPendInfo, 0, sizeof(AE_PEND_INFO)); }
//copy the issued PKCS7 blob
(pCertType->rgPendInfo)[dwIndex].blobPKCS7.pbData=(BYTE *)LocalAlloc( LPTR, pBlobPKCS7->cbData);
if(NULL == ((pCertType->rgPendInfo)[dwIndex].blobPKCS7.pbData)) goto Ret; memcpy((pCertType->rgPendInfo)[dwIndex].blobPKCS7.pbData, pBlobPKCS7->pbData, pBlobPKCS7->cbData);
(pCertType->rgPendInfo)[dwIndex].blobPKCS7.cbData=pBlobPKCS7->cbData;
//copy the hash of the request
if(!AEGetPendingRequestProperty(pIEnroll4, dwXenrollIndex, XEPR_HASH, &((pCertType->rgPendInfo)[dwIndex].blobHash))) { LocalFree((pCertType->rgPendInfo)[dwIndex].blobPKCS7.pbData); (pCertType->rgPendInfo)[dwIndex].blobPKCS7.pbData=NULL; (pCertType->rgPendInfo)[dwIndex].blobPKCS7.cbData=0; goto Ret; }
(pCertType->dwPendCount)++;
fResult=TRUE;
Ret: if(pPendInfo) LocalFree(pPendInfo);
return fResult; } //--------------------------------------------------------------------------
//
// AEProcessUIPendingRequest
//
// In this function, we install the issued pending certificate request
// that will require UI.
//
//--------------------------------------------------------------------------
BOOL WINAPI AEProcessUIPendingRequest(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex=0; DWORD dwPendIndex=0; AE_CERTTYPE_INFO *pCertTypeInfo=pAE_General_Info->rgCertTypeInfo; AE_CERTTYPE_INFO *pCertType=NULL; BOOL fInit=FALSE; PFNPIEnroll4GetNoCOM pfnPIEnroll4GetNoCOM=NULL; HMODULE hXenroll=NULL; HRESULT hr=E_FAIL;
IEnroll4 *pIEnroll4=NULL;
if(NULL==pAE_General_Info) goto Ret;
//has to be in the UI mode
if(FALSE == pAE_General_Info->fUIProcess) goto Ret;
if(NULL==pCertTypeInfo) goto Ret;
hXenroll=pAE_General_Info->hXenroll;
if(NULL==hXenroll) goto Ret;
if(NULL==(pfnPIEnroll4GetNoCOM=(PFNPIEnroll4GetNoCOM)GetProcAddress( hXenroll, "PIEnroll4GetNoCOM"))) goto Ret;
if(FAILED(CoInitialize(NULL))) goto Ret;
fInit=TRUE;
if(NULL==(pIEnroll4=pfnPIEnroll4GetNoCOM())) goto Ret;
//Set the request store flag based on fMachine
if(pAE_General_Info->fMachine) { if(S_OK != pIEnroll4->put_RequestStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE)) goto Ret; } else { if(S_OK != pIEnroll4->put_RequestStoreFlags(CERT_SYSTEM_STORE_CURRENT_USER)) goto Ret; }
//initialize the enumerator
if(S_OK != pIEnroll4->enumPendingRequestWStr(XEPR_ENUM_FIRST, 0, NULL)) goto Ret;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { pCertType = &(pCertTypeInfo[dwIndex]);
if(pCertType->dwPendCount) { for(dwPendIndex=0; dwPendIndex < pCertType->dwPendCount; dwPendIndex++) { //check if cancel button is clicked
if(AECancelled(pAE_General_Info->hCancelEvent)) break;
//report the current enrollment action
AEUIProgressReport(TRUE, pCertType, pAE_General_Info->hwndDlg, pAE_General_Info->hCancelEvent);
//install the certificate
if(S_OK == (hr = pIEnroll4->acceptResponseBlob( &((pCertType->rgPendInfo)[dwPendIndex].blobPKCS7)))) { //mark the status to obtained if required
//this is a valid certificate
if(AEValidVersionCert(pCertType, pIEnroll4, &((pCertType->rgPendInfo)[dwPendIndex].blobPKCS7))) pCertType->dwStatus = CERT_REQUEST_STATUS_OBTAINED;
//the certificate is successfully issued and installed
//remove the request from the request store
pIEnroll4->removePendingRequestWStr((pCertType->rgPendInfo)[dwPendIndex].blobHash);
AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_PENDING_INSTALLED, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, pCertType->awszDisplay[0]);
} else { //doing this for summary page
if((SCARD_E_CANCELLED != hr) && (SCARD_W_CANCELLED_BY_USER != hr)) pCertType->idsSummary=IDS_SUMMARY_INSTALL;
AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, TRUE, hr, EVENT_PENDING_FAILED, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, pCertType->awszDisplay[0]); }
//advance progress
AEUIProgressAdvance(pAE_General_Info); } } }
Ret: if(pIEnroll4) pIEnroll4->Release();
if(fInit) CoUninitialize(); return TRUE; }
//--------------------------------------------------------------------------
//
// AEProcessPendingRequest -- UIless call.
//
// In this function, we check each pending requests in the request store.
// We install the certificate is the request has been issued by the CA, and
// mark the certificate type status to obtained if the certificate is issued
// and of correct version
//
// We remove any requests that are stale based on the # of days defined
// in the registry. If no value is defined in the registry, use
// AE_PENDING_REQUEST_ACTIVE_PERIOD (60 days).
//
// Also, if there is no more pending requests active in the request store,
// we set the registry value to indicate that winlogon should not wake us up.
//
//--------------------------------------------------------------------------
BOOL WINAPI AEProcessPendingRequest(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwRequestID=0; LONG dwDisposition=0; DWORD dwIndex=0; DWORD dwCount=0; DWORD dwMax=PENDING_ALLOC_SIZE; AE_CERTTYPE_INFO *pCertType=NULL; PFNPIEnroll4GetNoCOM pfnPIEnroll4GetNoCOM=NULL; BOOL fInit=FALSE; AE_TEMPLATE_INFO AETemplateInfo; CRYPT_DATA_BLOB blobPKCS7; HMODULE hXenroll=NULL; VARIANT varCMC; HRESULT hr=E_FAIL;
IEnroll4 *pIEnroll4=NULL; ICertRequest2 *pICertRequest=NULL; BSTR bstrCert=NULL; LPWSTR pwszCAConfig=NULL; BSTR bstrConfig=NULL; CRYPT_DATA_BLOB *rgblobHash=NULL; CRYPT_DATA_BLOB blobCAName; CRYPT_DATA_BLOB blobCALocation; CRYPT_DATA_BLOB blobName;
if(NULL==pAE_General_Info) goto Ret;
//init the dwUIPendCount to 0
pAE_General_Info->dwUIPendCount=0;
//has to be in the UIless mode
if(TRUE == pAE_General_Info->fUIProcess) goto Ret;
hXenroll=pAE_General_Info->hXenroll;
if(NULL==hXenroll) goto Ret;
if(NULL==(pfnPIEnroll4GetNoCOM=(PFNPIEnroll4GetNoCOM)GetProcAddress( hXenroll, "PIEnroll4GetNoCOM"))) goto Ret;
if(FAILED(CoInitialize(NULL))) goto Ret;
fInit=TRUE;
if(NULL==(pIEnroll4=pfnPIEnroll4GetNoCOM())) goto Ret;
if(S_OK != CoCreateInstance(CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest2, (void **)&pICertRequest)) goto Ret;
//Set the request store flag based on fMachine
if(pAE_General_Info->fMachine) { if(S_OK != pIEnroll4->put_RequestStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE)) goto Ret; } else { if(S_OK != pIEnroll4->put_RequestStoreFlags(CERT_SYSTEM_STORE_CURRENT_USER)) goto Ret; }
memset(&blobCAName, 0, sizeof(blobCAName)); memset(&blobCALocation, 0, sizeof(blobCALocation)); memset(&blobName, 0, sizeof(blobName)); memset(&AETemplateInfo, 0, sizeof(AETemplateInfo));
rgblobHash=(CRYPT_DATA_BLOB *)LocalAlloc(LPTR, dwMax * sizeof(CRYPT_DATA_BLOB)); if(NULL==rgblobHash) goto Ret;
memset(rgblobHash, 0, dwMax * sizeof(CRYPT_DATA_BLOB));
//initialize the enumerator
if(S_OK != pIEnroll4->enumPendingRequestWStr(XEPR_ENUM_FIRST, 0, NULL)) goto Ret;
//initlialize the variant
VariantInit(&varCMC);
while(AEGetPendingRequestProperty( pIEnroll4, dwIndex, XEPR_REQUESTID, &dwRequestID)) {
//query the status of the requests to the CA
if(!AEGetPendingRequestProperty( pIEnroll4, dwIndex, XEPR_CANAME, &blobCAName)) goto Next;
if(!AEGetPendingRequestProperty( pIEnroll4, dwIndex, XEPR_CADNS, &blobCALocation)) goto Next;
//build the config string
pwszCAConfig=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (wcslen((LPWSTR)(blobCALocation.pbData)) + wcslen((LPWSTR)(blobCAName.pbData)) + wcslen(L"\\") + 1));
if(NULL==pwszCAConfig) goto Next;
wcscpy(pwszCAConfig, (LPWSTR)(blobCALocation.pbData)); wcscat(pwszCAConfig, L"\\"); wcscat(pwszCAConfig, (LPWSTR)(blobCAName.pbData));
//conver to bstr
bstrConfig=SysAllocString(pwszCAConfig); if(NULL==bstrConfig) goto Next;
//find the template information
//get the version and the template name of the request
if(AEGetPendingRequestProperty(pIEnroll4, dwIndex, XEPR_V2TEMPLATEOID, &blobName)) { AETemplateInfo.pwszOid=(LPWSTR)blobName.pbData; } else { if(!AEGetPendingRequestProperty(pIEnroll4, dwIndex, XEPR_V1TEMPLATENAME, &blobName)) goto Next;
AETemplateInfo.pwszName=(LPWSTR)blobName.pbData; }
//find the template
if(NULL==(pCertType=AEFindTemplateInRequestTree( &AETemplateInfo, pAE_General_Info))) goto Next;
if(S_OK != pICertRequest->RetrievePending( dwRequestID, bstrConfig, &dwDisposition)) goto Next;
switch(dwDisposition) { case CR_DISP_ISSUED: if(S_OK != pICertRequest->GetFullResponseProperty( FR_PROP_FULLRESPONSE, 0, PROPTYPE_BINARY, CR_OUT_BINARY, &varCMC)) { goto Next; }
// Check to make sure we've gotten a BSTR back:
if (VT_BSTR != varCMC.vt) { goto Next; }
bstrCert = varCMC.bstrVal;
// Marshal the cert into a CRYPT_DATA_BLOB:
blobPKCS7.cbData = (DWORD)SysStringByteLen(bstrCert); blobPKCS7.pbData = (BYTE *)bstrCert;
// we will keep the PKCS7 blob for installation
if(CT_FLAG_USER_INTERACTION_REQUIRED & (pCertType->dwEnrollmentFlag)) { //signal that we should pop up the UI balloon
(pAE_General_Info->dwUIPendCount)++;
//copy the PKCS7 blob from the cert server
AECopyPendingBlob(&blobPKCS7, pIEnroll4, dwIndex, pCertType); } else { //install the certificate
if(S_OK != (hr = pIEnroll4->acceptResponseBlob(&blobPKCS7))) { AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, TRUE, hr, EVENT_PENDING_FAILED, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, pCertType->awszDisplay[0]);
goto Next; }
//mark the status to obtained if required
//this is a valid certificate
if(AEValidVersionCert(pCertType, pIEnroll4, &blobPKCS7)) pCertType->dwStatus = CERT_REQUEST_STATUS_OBTAINED;
//the certificate is successfully issued and installed
//remove the request from the request store
AERetrieveRequestProperty(pIEnroll4, dwIndex, &dwCount, &dwMax, &rgblobHash); }
AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_PENDING_ISSUED, pAE_General_Info->fMachine, pAE_General_Info->hToken, 2, pCertType->awszDisplay[0], pwszCAConfig); break;
case CR_DISP_UNDER_SUBMISSION:
AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_PENDING_PEND, pAE_General_Info->fMachine, pAE_General_Info->hToken, 2, pCertType->awszDisplay[0], pwszCAConfig);
break;
case CR_DISP_INCOMPLETE: case CR_DISP_ERROR: case CR_DISP_DENIED: case CR_DISP_ISSUED_OUT_OF_BAND: //we consider it a failure in this case
case CR_DISP_REVOKED: default: //requests failed. remove the request from the request store
AERetrieveRequestProperty(pIEnroll4, dwIndex, &dwCount, &dwMax, &rgblobHash);
if(S_OK == pICertRequest->GetLastStatus(&hr)) { AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, TRUE, hr, EVENT_PENDING_DENIED, pAE_General_Info->fMachine, pAE_General_Info->hToken, 2, pwszCAConfig, pCertType->awszDisplay[0]); }
break; } Next: if(pwszCAConfig) LocalFree(pwszCAConfig); pwszCAConfig=NULL;
if(bstrConfig) SysFreeString(bstrConfig); bstrConfig=NULL;
if(bstrCert) SysFreeString(bstrCert); bstrCert=NULL;
if(blobCAName.pbData) LocalFree(blobCAName.pbData); memset(&blobCAName, 0, sizeof(blobCAName));
if(blobCALocation.pbData) LocalFree(blobCALocation.pbData); memset(&blobCALocation, 0, sizeof(blobCALocation));
if(blobName.pbData) LocalFree(blobName.pbData); memset(&blobName, 0, sizeof(blobName));
memset(&AETemplateInfo, 0, sizeof(AETemplateInfo));
VariantInit(&varCMC);
dwIndex++; }
//remove the requests based the hash
AERemovePendingRequest(pIEnroll4, dwCount, rgblobHash);
Ret:
AEFreePendingRequests(dwCount, rgblobHash);
if(pICertRequest) pICertRequest->Release();
if(pIEnroll4) pIEnroll4->Release();
if(fInit) CoUninitialize(); return TRUE; } //--------------------------------------------------------------------------
//
// AEIsLocalSystem
//
//--------------------------------------------------------------------------
BOOL AEIsLocalSystem(BOOL *pfIsLocalSystem) { HANDLE hToken = 0; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; BOOL fRet = FALSE; BOOL fRevertToSelf = FALSE;
PSID psidLocalSystem = NULL;
*pfIsLocalSystem = FALSE;
if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken)) { if (ERROR_NO_TOKEN != GetLastError()) goto Ret;
//we need to impersonateself and get the thread token again
if(!ImpersonateSelf(SecurityImpersonation)) goto Ret;
fRevertToSelf = TRUE;
if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken)) goto Ret; }
//build the well known local system SID (s-1-5-18)
if (!AllocateAndInitializeSid( &siaNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidLocalSystem )) goto Ret;
fRet = CheckTokenMembership( hToken, psidLocalSystem, pfIsLocalSystem);
Ret:
if(fRevertToSelf) RevertToSelf();
if(psidLocalSystem) FreeSid(psidLocalSystem);
if (hToken) CloseHandle(hToken);
return fRet; }
//--------------------------------------------------------------------------
//
// AEInSafeBoot
//
// copied from the service controller code
//--------------------------------------------------------------------------
BOOL WINAPI AEInSafeBoot() { DWORD dwSafeBoot = 0; DWORD cbSafeBoot = sizeof(dwSafeBoot); DWORD dwType = 0;
HKEY hKeySafeBoot = NULL;
if(ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, L"system\\currentcontrolset\\control\\safeboot\\option", &hKeySafeBoot)) { // we did in fact boot under safeboot control
if(ERROR_SUCCESS != RegQueryValueExW( hKeySafeBoot, L"OptionValue", NULL, &dwType, (LPBYTE)&dwSafeBoot, &cbSafeBoot)) { dwSafeBoot = 0; }
if(hKeySafeBoot) RegCloseKey(hKeySafeBoot); }
return (0 != dwSafeBoot); }
//--------------------------------------------------------------------------
//
// AEIsDomainMember
//
//--------------------------------------------------------------------------
BOOL WINAPI AEIsDomainMember() { DWORD dwErr; BOOL bIsDomainMember=FALSE;
// must be cleaned up
DSROLE_PRIMARY_DOMAIN_INFO_BASIC * pDomInfo=NULL;
dwErr=DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (BYTE **)&pDomInfo); if (ERROR_SUCCESS==dwErr) { if (DsRole_RoleStandaloneWorkstation!=pDomInfo->MachineRole && DsRole_RoleStandaloneServer!=pDomInfo->MachineRole) { bIsDomainMember=TRUE; } }
if (NULL!=pDomInfo) { DsRoleFreeMemory(pDomInfo); }
return bIsDomainMember; }
//-----------------------------------------------------------------------
//
// AEGetPolicyFlag
//
//-----------------------------------------------------------------------
BOOL AEGetPolicyFlag(BOOL fMachine, DWORD *pdwPolicy) { DWORD dwPolicy = 0; DWORD cbPolicy = sizeof(dwPolicy); DWORD dwType = 0;
HKEY hKey = NULL;
if(ERROR_SUCCESS == RegOpenKeyW( fMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, AUTO_ENROLLMENT_KEY, &hKey)) { if(ERROR_SUCCESS != RegQueryValueExW( hKey, AUTO_ENROLLMENT_POLICY, NULL, &dwType, (LPBYTE)&dwPolicy, &cbPolicy)) { dwPolicy = 0; }
if(hKey) RegCloseKey(hKey);
}
*pdwPolicy=dwPolicy;
return TRUE; }
//-----------------------------------------------------------------------
//
// AERetrieveLogLevel
//
//-----------------------------------------------------------------------
BOOL AERetrieveLogLevel(BOOL fMachine, DWORD *pdwLogLevel) { DWORD dwLogLevel = STATUS_SEVERITY_ERROR; //we default to highest logging level
DWORD cbLogLevel = sizeof(dwLogLevel); DWORD dwType = 0;
HKEY hKey = NULL;
if(ERROR_SUCCESS == RegOpenKeyW( fMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, AUTO_ENROLLMENT_EVENT_LEVEL_KEY, &hKey)) { if(ERROR_SUCCESS != RegQueryValueExW( hKey, AUTO_ENROLLMENT_EVENT_LEVEL, NULL, &dwType, (LPBYTE)&dwLogLevel, &cbLogLevel)) { dwLogLevel = STATUS_SEVERITY_ERROR; }
if(hKey) RegCloseKey(hKey);
}
*pdwLogLevel=dwLogLevel;
return TRUE; }
//-----------------------------------------------------------------------
//
// AERetrieveTemplateInfo
//
//-----------------------------------------------------------------------
BOOL AERetrieveTemplateInfo(PCCERT_CONTEXT pCertCurrent, AE_TEMPLATE_INFO *pTemplateInfo) { BOOL fResult = FALSE; PCERT_EXTENSION pExt = NULL; DWORD cbData=0;
CERT_NAME_VALUE *pbName = NULL; CERT_TEMPLATE_EXT *pbTemplate = NULL;
if((NULL==pCertCurrent) || (NULL==pTemplateInfo)) goto Ret;
memset(pTemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
//try to find V2 template extension first
if(pExt = CertFindExtension(szOID_CERTIFICATE_TEMPLATE, pCertCurrent->pCertInfo->cExtension, pCertCurrent->pCertInfo->rgExtension)) { if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_CERTIFICATE_TEMPLATE, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbData)) goto Ret;
pbTemplate = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbData);
if(NULL==pbTemplate) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_CERTIFICATE_TEMPLATE, pExt->Value.pbData, pExt->Value.cbData, 0, pbTemplate, &cbData)) goto Ret;
//copy the version
pTemplateInfo->dwVersion=pbTemplate->dwMajorVersion;
//copy the extension oid
if(NULL==pbTemplate->pszObjId) goto Ret;
if(0 == (cbData = MultiByteToWideChar(CP_ACP, 0, pbTemplate->pszObjId, -1, NULL, 0))) goto Ret;
if(NULL==(pTemplateInfo->pwszOid=(LPWSTR)LocalAlloc(LPTR, cbData * sizeof(WCHAR)))) goto Ret;
if(0 == MultiByteToWideChar(CP_ACP, 0, pbTemplate->pszObjId, -1, pTemplateInfo->pwszOid, cbData)) goto Ret;
} else {
//try V1 template extension
if(NULL == (pExt = CertFindExtension( szOID_ENROLL_CERTTYPE_EXTENSION, pCertCurrent->pCertInfo->cExtension, pCertCurrent->pCertInfo->rgExtension))) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, X509_UNICODE_ANY_STRING, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbData)) goto Ret;
pbName = (CERT_NAME_VALUE *)LocalAlloc(LPTR, cbData);
if(NULL==pbName) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, X509_UNICODE_ANY_STRING, pExt->Value.pbData, pExt->Value.cbData, 0, pbName, &cbData)) goto Ret;
if(!AEAllocAndCopy((LPWSTR)(pbName->Value.pbData), &(pTemplateInfo->pwszName))) goto Ret; }
fResult = TRUE;
Ret:
if(pbTemplate) LocalFree(pbTemplate);
if(pbName) LocalFree(pbName);
return fResult; }
//-----------------------------------------------------------------------
//
// AEFreeTemplateInfo
//
//-----------------------------------------------------------------------
BOOL AEFreeTemplateInfo(AE_TEMPLATE_INFO *pAETemplateInfo) { if(pAETemplateInfo->pwszName) LocalFree(pAETemplateInfo->pwszName);
if(pAETemplateInfo->pwszOid) LocalFree(pAETemplateInfo->pwszOid);
memset(pAETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
return TRUE; }
//-----------------------------------------------------------------------
//
// AEFindTemplateInRequestTree
//
//-----------------------------------------------------------------------
AE_CERTTYPE_INFO *AEFindTemplateInRequestTree(AE_TEMPLATE_INFO *pTemplateInfo, AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex = 0; AE_CERTTYPE_INFO *rgCertTypeInfo=NULL; AE_CERTTYPE_INFO *pCertType=NULL; if(NULL == (rgCertTypeInfo=pAE_General_Info->rgCertTypeInfo)) return NULL;
if( (NULL == pTemplateInfo->pwszName) && (NULL == pTemplateInfo->pwszOid)) return NULL;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if(pTemplateInfo->pwszOid) { //we are guaranteed to have an OID if the schema is greater than or equal to 2
if(rgCertTypeInfo[dwIndex].dwSchemaVersion >= CERTTYPE_SCHEMA_VERSION_2) { if(0 == wcscmp(pTemplateInfo->pwszOid, (rgCertTypeInfo[dwIndex].awszOID)[0])) { pCertType = &(rgCertTypeInfo[dwIndex]); break; } } } else { //we are guaranteed to have a name
if(0 == wcscmp(pTemplateInfo->pwszName, (rgCertTypeInfo[dwIndex].awszName)[0])) { pCertType = &(rgCertTypeInfo[dwIndex]); break; } } }
return pCertType; }
//-----------------------------------------------------------------------
//
// AEIsLogonDCCertificate
//
//
//-----------------------------------------------------------------------
BOOL AEIsLogonDCCertificate(PCCERT_CONTEXT pCertContext) { BOOL fDCCert=FALSE; PCERT_EXTENSION pExt = NULL; DWORD cbData = 0; DWORD dwIndex = 0; BOOL fFound = FALSE;
CERT_ENHKEY_USAGE *pbKeyUsage=NULL; AE_TEMPLATE_INFO AETemplateInfo;
memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
if(NULL==pCertContext) return FALSE;
if(!AERetrieveTemplateInfo(pCertContext, &AETemplateInfo)) goto Ret;
if(AETemplateInfo.pwszName) { //this is a V1 template. Search for the hard coded DC template name
if(0 == _wcsicmp(wszCERTTYPE_DC, AETemplateInfo.pwszName)) fDCCert=TRUE; } else { //this is a V2 template. Search for the smart card logon OID
if(NULL==(pExt=CertFindExtension(szOID_ENHANCED_KEY_USAGE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension))) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbData)) goto Ret;
pbKeyUsage=(CERT_ENHKEY_USAGE *)LocalAlloc(LPTR, cbData); if(NULL==pbKeyUsage) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, 0, pbKeyUsage, &cbData)) goto Ret;
for(dwIndex=0; dwIndex < pbKeyUsage->cUsageIdentifier; dwIndex++) { if(0==_stricmp(szOID_KP_SMARTCARD_LOGON,(pbKeyUsage->rgpszUsageIdentifier)[dwIndex])) { fDCCert=TRUE; break; } } }
Ret:
if(pbKeyUsage) LocalFree(pbKeyUsage);
AEFreeTemplateInfo(&AETemplateInfo);
return fDCCert;
} //-----------------------------------------------------------------------
//
// AEValidateCertificateInfo
//
// This function verifies if the certificate needs to be renewed or
// re-enrolled based on:
//
// 1. Presence of the private key
// 2. Chaining of the certificate
// 3. If the certificate is close to expiration
//
//-----------------------------------------------------------------------
BOOL AEValidateCertificateInfo(AE_GENERAL_INFO *pAE_General_Info, AE_CERTTYPE_INFO *pCertType, BOOL fCheckForPrivateKey, PCCERT_CONTEXT pCertCurrent, AE_CERT_INFO *pAECertInfo) { BOOL fResult = TRUE; DWORD cbData = 0; CERT_CHAIN_PARA ChainParams; CERT_CHAIN_POLICY_PARA ChainPolicy; CERT_CHAIN_POLICY_STATUS PolicyStatus; LARGE_INTEGER ftTime; HRESULT hrChainStatus = S_OK; LARGE_INTEGER ftHalfLife;
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
if((NULL==pCertCurrent) || (NULL==pAECertInfo) || (NULL==pAE_General_Info)) { SetLastError(E_INVALIDARG); fResult = FALSE; goto Ret; }
//assume the certificate is bad
pAECertInfo->fValid = FALSE; pAECertInfo->fRenewal = FALSE;
//////////////////////////////////////////////////
//
//check for the private key information
//
//////////////////////////////////////////////////
if(fCheckForPrivateKey) { if(!CertGetCertificateContextProperty( pCertCurrent, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData)) goto Ret; }
/////////////////////////////////////////////////////////
//
//check for chaining, revoke status and expiration of the certificate
//
/////////////////////////////////////////////////////////
memset(&ChainParams, 0, sizeof(ChainParams)); ChainParams.cbSize = sizeof(ChainParams); ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
ChainParams.RequestedUsage.Usage.cUsageIdentifier = 0; ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
// Build a small time skew into the chain building in order to deal
// with servers that may skew slightly fast.
GetSystemTimeAsFileTime((LPFILETIME)&ftTime); ftTime.QuadPart += Int32x32To64(FILETIME_TICKS_PER_SECOND, AE_DEFAULT_SKEW);
// Build a cert chain for the current status of the cert..
if(!CertGetCertificateChain(pAE_General_Info->fMachine?HCCE_LOCAL_MACHINE:HCCE_CURRENT_USER, pCertCurrent, (LPFILETIME)&ftTime, NULL, &ChainParams, CERT_CHAIN_REVOCATION_CHECK_CHAIN, NULL, &pChainContext)) { AE_DEBUG((AE_WARNING, L"Could not build certificate chain (%lx)\n\r", GetLastError())); goto Ret; } //validate the certificate chain
//special case for domain controller certificate.
//it should not have any revocation error, even for status unknown case
//check against the base policy
memset(&ChainPolicy, 0, sizeof(ChainPolicy)); ChainPolicy.cbSize = sizeof(ChainPolicy); ChainPolicy.dwFlags = 0; // ignore nothing
ChainPolicy.pvExtraPolicyPara = NULL;
memset(&PolicyStatus, 0, sizeof(PolicyStatus)); PolicyStatus.cbSize = sizeof(PolicyStatus); PolicyStatus.dwError = 0; PolicyStatus.lChainIndex = -1; PolicyStatus.lElementIndex = -1; PolicyStatus.pvExtraPolicyStatus = NULL;
if(!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, pChainContext, &ChainPolicy, &PolicyStatus)) { AE_DEBUG((AE_WARNING, L"Base Chain Policy failed (%lx) - must get new cert\n\r", GetLastError())); goto Ret; }
hrChainStatus = PolicyStatus.dwError;
if((S_OK == hrChainStatus) || (CRYPT_E_NO_REVOCATION_CHECK == hrChainStatus) || (CRYPT_E_REVOCATION_OFFLINE == hrChainStatus)) { // The cert is still currently acceptable by trust standards, so we can renew it
pAECertInfo->fRenewal = TRUE; } else { goto Ret; }
if(pChainContext) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; }
/////////////////////////////////////////////////////////////////////
//
// Check if the certificate is close to expire
//
///////////////////////////////////////////////////////////////////////
if(NULL==pCertType) goto Ret;
// Nudge the evaluation of the cert chain by the expiration
// offset so we know if is expired by that time in the future.
GetSystemTimeAsFileTime((LPFILETIME)&ftTime);
// Build the certificate chain for trust operations
memset(&ChainParams, 0, sizeof(ChainParams)); ChainParams.cbSize = sizeof(ChainParams); ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
ChainParams.RequestedUsage.Usage.cUsageIdentifier = 0; ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
//get the half lifetime of the certificate
ftHalfLife.QuadPart = (((LARGE_INTEGER UNALIGNED *)&(pCertCurrent->pCertInfo->NotAfter))->QuadPart - ((LARGE_INTEGER UNALIGNED *)&(pCertCurrent->pCertInfo->NotBefore))->QuadPart)/2;
//check if the old cert is time nesting invalid
if(ftHalfLife.QuadPart < 0) goto Ret;
//check if the offset was specified in a relative value
if(pCertType->ftExpirationOffset.QuadPart < 0) { if(ftHalfLife.QuadPart > (- pCertType->ftExpirationOffset.QuadPart)) { ftTime.QuadPart -= pCertType->ftExpirationOffset.QuadPart; } else { ftTime.QuadPart += ftHalfLife.QuadPart; } } else { //the offset was specified in an absolute value
if(0 < pCertType->ftExpirationOffset.QuadPart) ftTime = pCertType->ftExpirationOffset; else //use the half time mark if the offset is 0
ftTime.QuadPart += ftHalfLife.QuadPart; }
//check the certificate chain at a future time
if(!CertGetCertificateChain(pAE_General_Info->fMachine?HCCE_LOCAL_MACHINE:HCCE_CURRENT_USER, pCertCurrent, (LPFILETIME)&ftTime, NULL, //no additional store
&ChainParams, 0, //no revocation check
NULL, //Reserved
&pChainContext)) { AE_DEBUG((AE_WARNING, L"Could not build certificate chain (%lx)\n\r", GetLastError())); goto Ret; }
// Verify expiration of the certificate
memset(&ChainPolicy, 0, sizeof(ChainPolicy)); ChainPolicy.cbSize = sizeof(ChainPolicy); ChainPolicy.dwFlags = 0; // ignore nothing
ChainPolicy.pvExtraPolicyPara = NULL;
memset(&PolicyStatus, 0, sizeof(PolicyStatus)); PolicyStatus.cbSize = sizeof(PolicyStatus); PolicyStatus.dwError = 0; PolicyStatus.lChainIndex = -1; PolicyStatus.lElementIndex = -1; PolicyStatus.pvExtraPolicyStatus = NULL;
if(!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, pChainContext, &ChainPolicy, &PolicyStatus)) { AE_DEBUG((AE_WARNING, L"Base Chain Policy failed (%lx) - must get new cert\n\r", GetLastError())); goto Ret; }
hrChainStatus = PolicyStatus.dwError;
if((S_OK != hrChainStatus) && (CRYPT_E_NO_REVOCATION_CHECK != hrChainStatus) && (CRYPT_E_REVOCATION_OFFLINE != hrChainStatus)) { // The cert is close to expire. we must re-renewal
goto Ret; }
//the certificate is good
pAECertInfo->fValid = TRUE;
fResult = TRUE;
Ret: if(pChainContext) CertFreeCertificateChain(pChainContext);
return fResult; }
//-----------------------------------------------------------------------
//
// AESameOID
//
// Check if the two OIDs are the same
//-----------------------------------------------------------------------
BOOL AESameOID(LPWSTR pwszOID, LPSTR pszOID) { DWORD cbChar=0; BOOL fSame=FALSE;
LPSTR pszNewOID=NULL;
if((NULL==pwszOID) || (NULL==pszOID)) return FALSE;
cbChar= WideCharToMultiByte( CP_ACP, // codepage
0, // dwFlags
pwszOID, -1, NULL, 0, NULL, NULL);
if(0 == cbChar) goto Ret;
if(NULL==(pszNewOID=(LPSTR)LocalAlloc(LPTR, cbChar))) goto Ret;
cbChar= WideCharToMultiByte( CP_ACP, // codepage
0, // dwFlags
pwszOID, -1, pszNewOID, cbChar, NULL, NULL);
if(0 == cbChar) goto Ret;
if(0 == _stricmp(pszNewOID, pszOID)) fSame=TRUE;
Ret:
if(pszNewOID) LocalFree(pszNewOID);
return fSame; }
//-----------------------------------------------------------------------
//
// AEValidRAPolicyWithProperty
//
// Check if the certificate matches the RA signature requirement
// of the certificate type
//-----------------------------------------------------------------------
BOOL AEValidRAPolicyWithProperty(PCCERT_CONTEXT pCertContext, LPWSTR *rgwszPolicy, LPWSTR *rgwszAppPolicy) { PCERT_EXTENSION pExt = NULL; DWORD cbData = 0; DWORD dwIndex = 0; DWORD dwFindIndex=0; BOOL fFound = FALSE; BOOL fValid = FALSE;
CERT_ENHKEY_USAGE *pbKeyUsage=NULL; CERT_POLICIES_INFO *pbAppPolicy=NULL; CERT_POLICIES_INFO *pbPolicy=NULL;
//find the EKUs
if(pExt=CertFindExtension(szOID_ENHANCED_KEY_USAGE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension)) { cbData=0; if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbData)) goto Ret;
pbKeyUsage=(CERT_ENHKEY_USAGE *)LocalAlloc(LPTR, cbData); if(NULL==pbKeyUsage) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, 0, pbKeyUsage, &cbData)) goto Ret; }
//get the cert issurance policy
if(pExt=CertFindExtension(szOID_CERT_POLICIES, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension)) { cbData=0; if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_CERT_POLICIES, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbData)) goto Ret;
pbPolicy=(CERT_POLICIES_INFO *)LocalAlloc(LPTR, cbData); if(NULL==pbPolicy) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_CERT_POLICIES, pExt->Value.pbData, pExt->Value.cbData, 0, pbPolicy, &cbData)) goto Ret; } //get the cert application policy
if(pExt=CertFindExtension(szOID_APPLICATION_CERT_POLICIES, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension)) { cbData=0; if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_CERT_POLICIES, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbData)) goto Ret;
pbAppPolicy=(CERT_POLICIES_INFO *)LocalAlloc(LPTR, cbData); if(NULL==pbAppPolicy) goto Ret;
if(!CryptDecodeObject(X509_ASN_ENCODING, szOID_CERT_POLICIES, pExt->Value.pbData, pExt->Value.cbData, 0, pbAppPolicy, &cbData)) goto Ret; }
if(rgwszPolicy) { if(rgwszPolicy[0]) { if(NULL==pbPolicy) goto Ret;
dwIndex=0; while(rgwszPolicy[dwIndex]) { fFound=FALSE;
for(dwFindIndex=0; dwFindIndex < pbPolicy->cPolicyInfo; dwFindIndex++) { if(AESameOID(rgwszPolicy[dwIndex], (pbPolicy->rgPolicyInfo)[dwFindIndex].pszPolicyIdentifier)) { fFound=TRUE; break; } }
if(FALSE == fFound) goto Ret;
dwIndex++; } } }
if(rgwszAppPolicy) { if(rgwszAppPolicy[0]) { if((NULL==pbAppPolicy) && (NULL==pbKeyUsage)) goto Ret;
dwIndex=0; while(rgwszAppPolicy[dwIndex]) { fFound=FALSE;
if(pbAppPolicy) { for(dwFindIndex=0; dwFindIndex < pbAppPolicy->cPolicyInfo; dwFindIndex++) { if(AESameOID(rgwszAppPolicy[dwIndex], (pbAppPolicy->rgPolicyInfo)[dwFindIndex].pszPolicyIdentifier)) { fFound=TRUE; break; } } }
if((FALSE == fFound) && (pbKeyUsage)) { for(dwFindIndex=0; dwFindIndex < pbKeyUsage->cUsageIdentifier; dwFindIndex++) { if(AESameOID(rgwszAppPolicy[dwIndex],(pbKeyUsage->rgpszUsageIdentifier)[dwFindIndex])) { fFound=TRUE; break; } } }
if(FALSE == fFound) goto Ret;
dwIndex++; } } }
fValid=TRUE;
Ret: if(pbKeyUsage) LocalFree(pbKeyUsage);
if(pbPolicy) LocalFree(pbPolicy);
if(pbAppPolicy) LocalFree(pbAppPolicy);
return fValid; }
//-----------------------------------------------------------------------
//
// AEValidRAPolicy
//
// Check if the certificate matches the RA signature requirement
// of the certificate type
//-----------------------------------------------------------------------
BOOL AEValidRAPolicy(PCCERT_CONTEXT pCertContext, AE_CERTTYPE_INFO *pCertType) { BOOL fValid=FALSE;
LPWSTR *rgwszPolicy=NULL; LPWSTR *rgwszAppPolicy=NULL;
if((NULL==pCertType) || (NULL==pCertContext)) return FALSE;
//get the certificate type properties
CAGetCertTypePropertyEx(pCertType->hCertType, CERTTYPE_PROP_RA_POLICY, &rgwszPolicy);
CAGetCertTypePropertyEx(pCertType->hCertType, CERTTYPE_PROP_RA_APPLICATION_POLICY, &rgwszAppPolicy);
fValid = AEValidRAPolicyWithProperty(pCertContext, rgwszPolicy, rgwszAppPolicy);
if(rgwszPolicy) CAFreeCertTypeProperty(pCertType->hCertType, rgwszPolicy);
if(rgwszAppPolicy) CAFreeCertTypeProperty(pCertType->hCertType, rgwszAppPolicy);
return fValid;
}
//-----------------------------------------------------------------------
//
// AESomeCSPSupported
//
//-----------------------------------------------------------------------
BOOL AESomeCSPSupported(HCERTTYPE hCertType) { BOOL fResult=FALSE; DWORD dwIndex=0; DWORD dwCSPIndex=0; DWORD dwProviderType=0; DWORD cbSize=0;
LPWSTR *awszCSP=NULL; LPWSTR pwszProviderName=NULL; HCRYPTPROV hProv=NULL;
if(NULL==hCertType) goto Ret;
//no CSPs means all CSPs are fine
if((S_OK != CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_CSP_LIST, &awszCSP)) || (NULL == awszCSP)) { fResult=TRUE; goto Ret; }
//no CSP means all CSPs are fine
if(NULL == awszCSP[0]) { fResult=TRUE; goto Ret; }
for(dwIndex=0; NULL != awszCSP[dwIndex]; dwIndex++) { for (dwCSPIndex = 0; CryptEnumProvidersW(dwCSPIndex, 0, 0, &dwProviderType, NULL, &cbSize); dwCSPIndex++) { pwszProviderName = (LPWSTR)LocalAlloc(LPTR, cbSize);
if(NULL == pwszProviderName) goto Ret; //get the provider name and type
if(!CryptEnumProvidersW(dwCSPIndex, 0, 0, &dwProviderType, pwszProviderName, &cbSize)) goto Ret;
if(0 == _wcsicmp(pwszProviderName, awszCSP[dwIndex])) { //find the CSP. See if it is present in the box
if(CryptAcquireContextW( &hProv, NULL, awszCSP[dwIndex], dwProviderType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
CryptReleaseContext(hProv, 0); hProv=NULL;
fResult=TRUE; break; } }
//keep the CSP enumeration
if(pwszProviderName) LocalFree(pwszProviderName);
pwszProviderName=NULL; cbSize=0; dwProviderType=0; }
//detect if a valid CSP if found
if(TRUE == fResult) { break; }
cbSize=0; dwProviderType=0; }
Ret: if(pwszProviderName) LocalFree(pwszProviderName);
if(hProv) CryptReleaseContext(hProv, 0);
if(awszCSP) CAFreeCertTypeProperty(hCertType, awszCSP);
return fResult;
}
//-----------------------------------------------------------------------
//
// AESmartcardOnlyTemplate
//
//-----------------------------------------------------------------------
BOOL AESmartcardOnlyTemplate(HCERTTYPE hCertType) { BOOL fResult=FALSE; DWORD dwIndex=0; DWORD dwImpType=0; DWORD cbData=0; DWORD dwSCCount=0;
LPWSTR *awszCSP=NULL; HCRYPTPROV hProv = NULL;
if(NULL==hCertType) goto Ret;
if(S_OK != CAGetCertTypePropertyEx( hCertType, CERTTYPE_PROP_CSP_LIST, &awszCSP)) goto Ret;
if(NULL==awszCSP) goto Ret;
for(dwIndex=0; NULL != awszCSP[dwIndex]; dwIndex++) { dwImpType=0;
//all smart card CSPs are RSA_FULL.
if(CryptAcquireContextW( &hProv, NULL, awszCSP[dwIndex], PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
cbData = sizeof(dwImpType); if(CryptGetProvParam(hProv, PP_IMPTYPE, (BYTE *)(&dwImpType), &cbData, 0)) { if((CRYPT_IMPL_REMOVABLE & dwImpType) && (CRYPT_IMPL_MIXED & dwImpType)) dwSCCount++; }
CryptReleaseContext(hProv, 0); hProv=NULL; } }
//smart card CSP only if all CSPs are for smart card only
if((0 != dwIndex) && (dwIndex==dwSCCount)) fResult=TRUE;
Ret: if(hProv) CryptReleaseContext(hProv, 0);
if(awszCSP) CAFreeCertTypeProperty(hCertType, awszCSP);
return fResult; }
//-----------------------------------------------------------------------
//
// AEUserProtectionForTemplate
//
//-----------------------------------------------------------------------
BOOL AEUserProtectionForTemplate(AE_GENERAL_INFO *pAE_General_Info, PCERT_CONTEXT pCertContext) { BOOL fUserProtection=FALSE; AE_CERTTYPE_INFO *pCertType=NULL;
AE_TEMPLATE_INFO AETemplateInfo;
memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
if((NULL == pAE_General_Info) || (NULL == pCertContext)) goto Ret;
//get the template information for the certificate
if(!AERetrieveTemplateInfo(pCertContext, &AETemplateInfo)) goto Ret;
pCertType=AEFindTemplateInRequestTree(&AETemplateInfo, pAE_General_Info);
if(NULL==pCertType) goto Ret;
if(CT_FLAG_STRONG_KEY_PROTECTION_REQUIRED & (pCertType->dwPrivateKeyFlag)) fUserProtection=TRUE;
Ret:
AEFreeTemplateInfo(&AETemplateInfo);
return fUserProtection; }
//-----------------------------------------------------------------------
//
// AEUISetForTemplate
//
//-----------------------------------------------------------------------
BOOL AEUISetForTemplate(AE_GENERAL_INFO *pAE_General_Info, PCERT_CONTEXT pCertContext) { BOOL fUI=FALSE; AE_CERTTYPE_INFO *pCertType=NULL;
AE_TEMPLATE_INFO AETemplateInfo;
memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
if((NULL == pAE_General_Info) || (NULL == pCertContext)) goto Ret;
//get the template information for the certificate
if(!AERetrieveTemplateInfo(pCertContext, &AETemplateInfo)) goto Ret;
pCertType=AEFindTemplateInRequestTree(&AETemplateInfo, pAE_General_Info);
if(NULL==pCertType) goto Ret;
if(CT_FLAG_USER_INTERACTION_REQUIRED & (pCertType->dwEnrollmentFlag)) fUI=TRUE;
Ret:
AEFreeTemplateInfo(&AETemplateInfo);
return fUI; }
//-----------------------------------------------------------------------
//
// AECanEnrollCertType
//
//-----------------------------------------------------------------------
BOOL AECanEnrollCertType(HANDLE hToken, AE_CERTTYPE_INFO *pCertType, AE_GENERAL_INFO *pAE_General_Info, BOOL *pfUserProtection) { DWORD dwValue = 0; PCCERT_CONTEXT pCertCurrent=NULL; AE_CERT_INFO AECertInfo;
memset(&AECertInfo, 0, sizeof(AE_CERT_INFO));
*pfUserProtection=FALSE;
//check enrollment ACL
if(S_OK != CACertTypeAccessCheckEx( pCertType->hCertType, hToken, CERTTYPE_ACCESS_CHECK_ENROLL | CERTTYPE_ACCESS_CHECK_NO_MAPPING)) return FALSE;
//check the subject requirements
if(S_OK != CAGetCertTypeFlagsEx( pCertType->hCertType, CERTTYPE_SUBJECT_NAME_FLAG, &dwValue)) return FALSE;
if((CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT & dwValue) || (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME & dwValue)) return FALSE;
//check if we are doing smart card CSPs and there is no reader installed
if(FALSE == (pAE_General_Info->fSmartcardSystem)) { if(AESmartcardOnlyTemplate(pCertType->hCertType)) return FALSE; }
//check if all CSPs on the template is not supported
{ if(!AESomeCSPSupported(pCertType->hCertType)) return FALSE; }
//we might not get the RA property for V1 template
dwValue = 0;
//check the RA support
if(S_OK != CAGetCertTypePropertyEx( pCertType->hCertType, CERTTYPE_PROP_RA_SIGNATURE, &dwValue)) return TRUE;
if(0==dwValue) return TRUE;
//self-template RA
if((CT_FLAG_PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT & (pCertType->dwEnrollmentFlag)) && ((pCertType->fRenewal) && (pCertType->pOldCert)) ) { //the request has to be RAed
pCertType->fNeedRA=TRUE; return TRUE; }
//autoenrollment only deal with one RA signature.
//it is sufficient for autoenrollment RA scenarios
if(1!=dwValue) return FALSE;
//the certificate template requires one and only one RA signature
//cross-template RA
//enumerate all certificate in store
while(pCertCurrent = CertEnumCertificatesInStore(pAE_General_Info->hMyStore, pCertCurrent)) { //check if we need to enroll/renewal for the certificate
AEValidateCertificateInfo(pAE_General_Info, NULL, TRUE, //valid private key
pCertCurrent, &AECertInfo);
//the certificate is good enough for RA signature purpose
if(AECertInfo.fRenewal) { if(AEValidRAPolicy(pCertCurrent, pCertType)) { if(AEUserProtectionForTemplate(pAE_General_Info, (PCERT_CONTEXT)pCertCurrent)) { if(pAE_General_Info->fMachine) { *pfUserProtection=TRUE; continue; } else { if(0==(CT_FLAG_USER_INTERACTION_REQUIRED & (pCertType->dwEnrollmentFlag))) { *pfUserProtection=TRUE; continue; } } }
pCertType->fRenewal=TRUE;
if(pCertType->pOldCert) { CertFreeCertificateContext(pCertType->pOldCert); pCertType->pOldCert=NULL; }
//we will free the certificate context later
pCertType->pOldCert=(PCERT_CONTEXT)pCertCurrent;
//we mark UI required if the RAing certificate template requires UI
if(AEUISetForTemplate(pAE_General_Info, pCertType->pOldCert)) pCertType->fUIActive=TRUE;
//we mark the requests has to be RAed.
pCertType->fNeedRA=TRUE;
//we mark that we are doing cross RAing.
pCertType->fCrossRA=TRUE;
*pfUserProtection=FALSE;
return TRUE; } }
memset(&AECertInfo, 0, sizeof(AE_CERT_INFO)); }
return FALSE; } //-----------------------------------------------------------------------
//
// AEMarkAutoenrollment
//
//-----------------------------------------------------------------------
BOOL AEMarkAutoenrollment(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex = 0;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if(CT_FLAG_AUTO_ENROLLMENT & ((pAE_General_Info->rgCertTypeInfo)[dwIndex].dwEnrollmentFlag)) { //check the autoenrollment ACL
if(S_OK != CACertTypeAccessCheckEx( (pAE_General_Info->rgCertTypeInfo)[dwIndex].hCertType, pAE_General_Info->hToken, CERTTYPE_ACCESS_CHECK_AUTO_ENROLL | CERTTYPE_ACCESS_CHECK_NO_MAPPING)) continue;
//mark the template nees to be auto-enrolled
(pAE_General_Info->rgCertTypeInfo)[dwIndex].dwStatus=CERT_REQUEST_STATUS_ACTIVE; (pAE_General_Info->rgCertTypeInfo)[dwIndex].fCheckMyStore=TRUE; } }
return TRUE; }
//-----------------------------------------------------------------------
//
// IsACRSStoreEmpty
//
//
//-----------------------------------------------------------------------
BOOL IsACRSStoreEmpty(BOOL fMachine) { DWORD dwOpenStoreFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG; LPSTR pszCTLUsageOID = NULL; BOOL fEmpty = TRUE; CERT_PHYSICAL_STORE_INFO PhysicalStoreInfo; CTL_FIND_USAGE_PARA CTLFindUsage;
PCCTL_CONTEXT pCTLContext = NULL; HCERTSTORE hStoreACRS=NULL;
memset(&PhysicalStoreInfo, 0, sizeof(PhysicalStoreInfo)); memset(&CTLFindUsage, 0, sizeof(CTLFindUsage));
// if the auto enrollment is for a user then we need to shut off inheritance
// from the local machine store so that we don't try and enroll for certs
// which are meant to be for the machine
if (FALSE == fMachine) { dwOpenStoreFlags = CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_READONLY_FLAG;
PhysicalStoreInfo.cbSize = sizeof(PhysicalStoreInfo); PhysicalStoreInfo.dwFlags = CERT_PHYSICAL_STORE_OPEN_DISABLE_FLAG;
if (!CertRegisterPhysicalStore(ACRS_STORE, CERT_SYSTEM_STORE_CURRENT_USER, CERT_PHYSICAL_STORE_LOCAL_MACHINE_NAME, &PhysicalStoreInfo, NULL)) { AE_DEBUG((AE_ERROR, L"Could not register ACRS store: (%lx)\n\r", GetLastError())); goto Ret; } }
// open the ACRS store and fine the CTL based on the auto enrollment usage
if (NULL == (hStoreACRS = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, ENCODING_TYPE, NULL, dwOpenStoreFlags, ACRS_STORE))) { AE_DEBUG((AE_ERROR, L"Could not open ACRS store: (%lx)\n\r", GetLastError())); goto Ret; }
//find the template name specified in the CTLContext
CTLFindUsage.cbSize = sizeof(CTLFindUsage); CTLFindUsage.SubjectUsage.cUsageIdentifier = 1; pszCTLUsageOID = szOID_AUTO_ENROLL_CTL_USAGE; CTLFindUsage.SubjectUsage.rgpszUsageIdentifier = &pszCTLUsageOID;
while(pCTLContext = CertFindCTLInStore(hStoreACRS, X509_ASN_ENCODING, CTL_FIND_SAME_USAGE_FLAG, CTL_FIND_USAGE, &CTLFindUsage, pCTLContext)) { fEmpty=FALSE; break; }
Ret:
if(pCTLContext) CertFreeCTLContext(pCTLContext);
if(hStoreACRS) CertCloseStore(hStoreACRS, 0);
return fEmpty; }
//-----------------------------------------------------------------------
//
// AEMarkAEObject
//
// Mark the active status based on ACRS store
//
// INFORMATION:
// we do not honor the CA specified in the autoenrollment object anymore. All CAs
// in the enterprise should be treated equal; and once the CA is renewed, it certificate
// will be changed anyway.
//-----------------------------------------------------------------------
BOOL AEMarkAEObject(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwOpenStoreFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG; PCCTL_CONTEXT pCTLContext = NULL; LPSTR pszCTLUsageOID = NULL; LPWSTR wszCertTypeName = NULL; AE_CERTTYPE_INFO *pCertType=NULL; CERT_PHYSICAL_STORE_INFO PhysicalStoreInfo; CTL_FIND_USAGE_PARA CTLFindUsage; AE_TEMPLATE_INFO AETemplateInfo;
HCERTSTORE hStoreACRS=NULL;
memset(&PhysicalStoreInfo, 0, sizeof(PhysicalStoreInfo)); memset(&CTLFindUsage, 0, sizeof(CTLFindUsage)); memset(&AETemplateInfo, 0, sizeof(AETemplateInfo));
// if the auto enrollment is for a user then we need to shut off inheritance
// from the local machine store so that we don't try and enroll for certs
// which are meant to be for the machine
if (FALSE == (pAE_General_Info->fMachine)) { dwOpenStoreFlags = CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_READONLY_FLAG;
PhysicalStoreInfo.cbSize = sizeof(PhysicalStoreInfo); PhysicalStoreInfo.dwFlags = CERT_PHYSICAL_STORE_OPEN_DISABLE_FLAG;
if (!CertRegisterPhysicalStore(ACRS_STORE, CERT_SYSTEM_STORE_CURRENT_USER, CERT_PHYSICAL_STORE_LOCAL_MACHINE_NAME, &PhysicalStoreInfo, NULL)) { AE_DEBUG((AE_ERROR, L"Could not register ACRS store: (%lx)\n\r", GetLastError())); goto Ret; } }
// open the ACRS store and fine the CTL based on the auto enrollment usage
if (NULL == (hStoreACRS = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, ENCODING_TYPE, NULL, dwOpenStoreFlags, ACRS_STORE))) { AE_DEBUG((AE_ERROR, L"Could not open ACRS store: (%lx)\n\r", GetLastError())); goto Ret; }
//find the template name specified in the CTLContext
CTLFindUsage.cbSize = sizeof(CTLFindUsage); CTLFindUsage.SubjectUsage.cUsageIdentifier = 1; pszCTLUsageOID = szOID_AUTO_ENROLL_CTL_USAGE; CTLFindUsage.SubjectUsage.rgpszUsageIdentifier = &pszCTLUsageOID;
while(pCTLContext = CertFindCTLInStore(hStoreACRS, X509_ASN_ENCODING, CTL_FIND_SAME_USAGE_FLAG, CTL_FIND_USAGE, &CTLFindUsage, pCTLContext)) { if(NULL== (pCTLContext->pCtlInfo->ListIdentifier.pbData)) continue;
wszCertTypeName = wcschr((LPWSTR)pCTLContext->pCtlInfo->ListIdentifier.pbData, L'|'); if(wszCertTypeName) { wszCertTypeName++; } else { wszCertTypeName = (LPWSTR)pCTLContext->pCtlInfo->ListIdentifier.pbData; }
AETemplateInfo.pwszName = wszCertTypeName;
if(pCertType=AEFindTemplateInRequestTree(&AETemplateInfo, pAE_General_Info)) { if(0 == pCertType->dwStatus) { //mark the template needs to be auto-enrolled
pCertType->dwStatus=CERT_REQUEST_STATUS_ACTIVE; pCertType->fCheckMyStore=TRUE; } } else { //log that the template is invalid
AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_INVALID_ACRS_OBJECT, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, wszCertTypeName); }
}
Ret:
if(hStoreACRS) CertCloseStore(hStoreACRS, 0);
return TRUE; }
//-----------------------------------------------------------------------
//
// AEManageAndMarkMyStore
//
//-----------------------------------------------------------------------
BOOL AEManageAndMarkMyStore(AE_GENERAL_INFO *pAE_General_Info) { AE_CERT_INFO AECertInfo; AE_CERTTYPE_INFO *pCertType=NULL; BOOL fNeedToValidate=TRUE; PCCERT_CONTEXT pCertCurrent = NULL; DWORD cbData=0;
AE_TEMPLATE_INFO AETemplateInfo;
memset(&AECertInfo, 0, sizeof(AE_CERT_INFO)); memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
//enumerate all certificate in store
while(pCertCurrent = CertEnumCertificatesInStore(pAE_General_Info->hMyStore, pCertCurrent)) { //only interested in certificate with template information
if(AERetrieveTemplateInfo(pCertCurrent, &AETemplateInfo)) { if(pCertType=AEFindTemplateInRequestTree( &AETemplateInfo, pAE_General_Info)) { //if we are not supposed to check for my store, only search
//for template with ACTIVE status
if(0 == (AUTO_ENROLLMENT_ENABLE_MY_STORE_MANAGEMENT & (pAE_General_Info->dwPolicy))) { if(!(pCertType->fCheckMyStore)) goto Next; }
//make sure the version of the certificate template is up to date
//we do not have version for V1 template
if(AETemplateInfo.pwszOid) { if(AETemplateInfo.dwVersion < pCertType->dwVersion) { AECertInfo.fValid=FALSE; AECertInfo.fRenewal = FALSE;
//self-RA renewal
if(CT_FLAG_PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT & pCertType->dwEnrollmentFlag) { if(CertGetCertificateContextProperty( pCertCurrent, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData)) AECertInfo.fRenewal = TRUE; }
fNeedToValidate=FALSE; } } if(fNeedToValidate) { //check if we need to enroll/renewal for the certificate
AEValidateCertificateInfo(pAE_General_Info, pCertType, TRUE, //valid private key
pCertCurrent, &AECertInfo); }
if(AECertInfo.fValid) { //if the certificate is valid, mark as obtained. And copy the
//certificate to the obtained store. Keep the archive store.
pCertType->dwStatus = CERT_REQUEST_STATUS_OBTAINED;
CertAddCertificateContextToStore( pCertType->hObtainedStore, pCertCurrent, CERT_STORE_ADD_ALWAYS, NULL);
} else { //the certificate is not valid
//mark the status to active if it is not obtained
if(CERT_REQUEST_STATUS_OBTAINED != pCertType->dwStatus) { pCertType->dwStatus = CERT_REQUEST_STATUS_ACTIVE;
if(AECertInfo.fRenewal) { //we only need to copy renewal information once
if(!pCertType->fRenewal) { pCertType->fRenewal=TRUE; pCertType->pOldCert=(PCERT_CONTEXT)CertDuplicateCertificateContext(pCertCurrent); } } }
//copy the certificate to the Archive certificate store
CertAddCertificateContextToStore( pCertType->hArchiveStore, pCertCurrent, CERT_STORE_ADD_ALWAYS, NULL); } } else { //log that the template is invalid
AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_INVALID_TEMPLATE_MY_STORE, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, AETemplateInfo.pwszName ? AETemplateInfo.pwszName : AETemplateInfo.pwszOid);
} }
Next: memset(&AECertInfo, 0, sizeof(AE_CERT_INFO)); AEFreeTemplateInfo(&AETemplateInfo); fNeedToValidate=TRUE; cbData=0; }
return TRUE; }
//-----------------------------------------------------------------------
//
// AEOpenUserDSStore
//
// INFORMATION: We could just open the "UserDS" store as if it is "My"
//
//-----------------------------------------------------------------------
HCERTSTORE AEOpenUserDSStore(AE_GENERAL_INFO *pAE_General_Info, DWORD dwOpenFlag) { LPWSTR pwszPath=L"ldap:///%s?userCertificate?base?objectCategory=user"; DWORD dwSize=0; WCHAR wszDN[MAX_DN_SIZE];
LPWSTR pwszDN=NULL; LPWSTR pwszStore=NULL; HCERTSTORE hStore=NULL;
dwSize=MAX_DN_SIZE;
if(!GetUserNameExW(NameFullyQualifiedDN, wszDN, &dwSize)) { if(dwSize > MAX_DN_SIZE) { pwszDN=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * dwSize);
if(NULL==pwszDN) goto Ret;
if(!GetUserNameExW(NameFullyQualifiedDN, pwszDN, &dwSize)) goto Ret; } else goto Ret; }
pwszStore = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(pwszDN ? pwszDN : wszDN)+wcslen(pwszPath)+1)); if(pwszStore == NULL) goto Ret;
wsprintf(pwszStore, pwszPath, pwszDN ? pwszDN : wszDN);
hStore = CertOpenStore(CERT_STORE_PROV_LDAP, ENCODING_TYPE, NULL, dwOpenFlag, pwszStore);
Ret:
if(pwszStore) LocalFree(pwszStore);
if(pwszDN) LocalFree(pwszDN);
return hStore; }
//-----------------------------------------------------------------------
//
// AECheckUserDSStore
//
//-----------------------------------------------------------------------
BOOL AECheckUserDSStore(AE_GENERAL_INFO *pAE_General_Info) { PCCERT_CONTEXT pCertCurrent = NULL; AE_CERTTYPE_INFO *pCertType=NULL; BOOL fNeedToValidate=TRUE; AE_CERT_INFO AECertInfo;
HCERTSTORE hUserDS = NULL; AE_TEMPLATE_INFO AETemplateInfo; memset(&AECertInfo, 0, sizeof(AE_CERT_INFO)); memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO)); pCertType=pAE_General_Info->rgCertTypeInfo;
if(NULL==pCertType) goto Ret;
if(NULL== (hUserDS = AEOpenUserDSStore(pAE_General_Info, CERT_STORE_READONLY_FLAG))) goto Ret;
pCertType = NULL; while(pCertCurrent = CertEnumCertificatesInStore(hUserDS, pCertCurrent)) { //only interested in certificate with template information
if(AERetrieveTemplateInfo(pCertCurrent, &AETemplateInfo)) { if(pCertType=AEFindTemplateInRequestTree( &AETemplateInfo, pAE_General_Info)) { //if we are not supposed to check for UserDS store, only search
//for template with ACTIVE status
if(0 == (AUTO_ENROLLMENT_ENABLE_MY_STORE_MANAGEMENT & (pAE_General_Info->dwPolicy))) { if(!(pCertType->fCheckMyStore)) goto Next; }
//make sure the version of the certificate template is up to date
//we do not have version for V1 template
if(AETemplateInfo.pwszOid) { if(AETemplateInfo.dwVersion < pCertType->dwVersion) { AECertInfo.fValid=FALSE; AECertInfo.fRenewal=FALSE; fNeedToValidate=FALSE; } } if(fNeedToValidate) { //check if we need to enroll/renewal for the certificate
AEValidateCertificateInfo(pAE_General_Info, pCertType, FALSE, //does not valid private key
pCertCurrent, &AECertInfo); }
//we only interested in any valid certificate
if(AECertInfo.fValid) { if((CT_FLAG_AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE & (pCertType->dwEnrollmentFlag)) && (CERT_REQUEST_STATUS_OBTAINED != pCertType->dwStatus)) { //mark the status as obtained.
pCertType->dwStatus = CERT_REQUEST_STATUS_OBTAINED; }
CertAddCertificateContextToStore( pCertType->hObtainedStore, pCertCurrent, CERT_STORE_ADD_USE_EXISTING, NULL);
} else { //copy the certificate to the Archive certificate store
CertAddCertificateContextToStore( pCertType->hArchiveStore, pCertCurrent, CERT_STORE_ADD_USE_EXISTING, NULL); } } }
Next: memset(&AECertInfo, 0, sizeof(AE_CERT_INFO)); AEFreeTemplateInfo(&AETemplateInfo); fNeedToValidate=TRUE; } Ret: if(hUserDS) CertCloseStore(hUserDS, 0);
return TRUE; }
//-----------------------------------------------------------------------
//
// AECheckPendingRequests
//
// If we have pending update-to-date certificate requests, no need
// to enroll/renew for duplicates.
//-----------------------------------------------------------------------
BOOL AECheckPendingRequests(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex=0; DWORD dwVersion=0; AE_CERTTYPE_INFO *pCertType=NULL; BOOL fValid=FALSE; DWORD dwCount=0; DWORD dwMax=PENDING_ALLOC_SIZE; PFNPIEnroll4GetNoCOM pfnPIEnroll4GetNoCOM=NULL; FILETIME ftTime; LARGE_INTEGER ftRequestTime; AE_TEMPLATE_INFO AETemplateInfo;
IEnroll4 *pIEnroll4=NULL; CRYPT_DATA_BLOB *rgblobHash=NULL; CRYPT_DATA_BLOB blobName;
//init before any goto Ret
memset(&blobName, 0, sizeof(blobName)); memset(&AETemplateInfo, 0, sizeof(AETemplateInfo));
if(NULL==pAE_General_Info->hXenroll) goto Ret;
if(NULL==(pfnPIEnroll4GetNoCOM=(PFNPIEnroll4GetNoCOM)GetProcAddress( pAE_General_Info->hXenroll, "PIEnroll4GetNoCOM"))) goto Ret;
if(NULL==(pIEnroll4=pfnPIEnroll4GetNoCOM())) goto Ret;
GetSystemTimeAsFileTime(&ftTime);
if(pAE_General_Info->fMachine) { if(S_OK != pIEnroll4->put_RequestStoreFlags(CERT_SYSTEM_STORE_LOCAL_MACHINE)) goto Ret; } else { if(S_OK != pIEnroll4->put_RequestStoreFlags(CERT_SYSTEM_STORE_CURRENT_USER)) goto Ret; }
//enumerate all the pending requests
rgblobHash=(CRYPT_DATA_BLOB *)LocalAlloc(LPTR, dwMax * sizeof(CRYPT_DATA_BLOB)); if(NULL==rgblobHash) goto Ret;
memset(rgblobHash, 0, dwMax * sizeof(CRYPT_DATA_BLOB));
//initialize the enumerator
if(S_OK != pIEnroll4->enumPendingRequestWStr(XEPR_ENUM_FIRST, 0, NULL)) goto Ret;
while(AEGetPendingRequestProperty( pIEnroll4, dwIndex, XEPR_DATE, &ftRequestTime)) { ftRequestTime.QuadPart += Int32x32To64(FILETIME_TICKS_PER_SECOND, AE_PENDING_REQUEST_ACTIVE_PERIOD * 24 * 3600);
//remove the request if out of date
if(0 <= CompareFileTime(&ftTime, (LPFILETIME)&ftRequestTime)) { AERetrieveRequestProperty(pIEnroll4, dwIndex, &dwCount, &dwMax, &rgblobHash); } else { //get the version and the template name of the request
if(AEGetPendingRequestProperty(pIEnroll4, dwIndex, XEPR_V2TEMPLATEOID, &blobName)) { //this is a V2 template
if(!AEGetPendingRequestProperty(pIEnroll4, dwIndex, XEPR_VERSION, &dwVersion)) goto Next;
AETemplateInfo.pwszOid=(LPWSTR)blobName.pbData;
} else { if(!AEGetPendingRequestProperty(pIEnroll4, dwIndex, XEPR_V1TEMPLATENAME, &blobName)) goto Next;
AETemplateInfo.pwszName=(LPWSTR)blobName.pbData; }
//find the template
if(NULL==(pCertType=AEFindTemplateInRequestTree( &AETemplateInfo, pAE_General_Info))) goto Next;
if(AETemplateInfo.pwszName) fValid=TRUE; else { if(dwVersion >= pCertType->dwVersion) fValid=TRUE; }
if(fValid) { //this is a valid pending request
if(CERT_REQUEST_STATUS_OBTAINED != pCertType->dwStatus) pCertType->dwStatus=CERT_REQUEST_STATUS_PENDING; } else { if(CERT_REQUEST_STATUS_OBTAINED == pCertType->dwStatus) AERetrieveRequestProperty(pIEnroll4, dwIndex, &dwCount, &dwMax, &rgblobHash); } }
Next: if(blobName.pbData) LocalFree(blobName.pbData); memset(&blobName, 0, sizeof(blobName));
memset(&AETemplateInfo, 0, sizeof(AETemplateInfo));
fValid=FALSE; dwVersion=0;
dwIndex++; }
//remove the requests based the hash
if(dwCount) { AERemovePendingRequest(pIEnroll4, dwCount, rgblobHash); AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_PENDING_INVALID, pAE_General_Info->fMachine, pAE_General_Info->hToken, 0); }
Ret:
AEFreePendingRequests(dwCount, rgblobHash);
if(blobName.pbData) LocalFree(blobName.pbData);
if(pIEnroll4) pIEnroll4->Release();
return TRUE; }
//-----------------------------------------------------------------------
//
// AECheckSupersedeRequest
//
//-----------------------------------------------------------------------
BOOL AECheckSupersedeRequest(DWORD dwCurIndex, AE_CERTTYPE_INFO *pCurCertType, AE_CERTTYPE_INFO *pSupersedingCertType, AE_GENERAL_INFO *pAE_General_Info) { BOOL fFound=FALSE;
LPWSTR *awszSuperseding=NULL;
if(S_OK == CAGetCertTypePropertyEx( pSupersedingCertType->hCertType, CERTTYPE_PROP_SUPERSEDE, &(awszSuperseding))) { if(awszSuperseding && awszSuperseding[0]) { if(AEIfSupersede(pCurCertType->awszName[0], awszSuperseding, pAE_General_Info)) { switch(pCurCertType->dwStatus) { case CERT_REQUEST_STATUS_ACTIVE: case CERT_REQUEST_STATUS_SUPERSEDE_ACTIVE: //remove the active status if it is superseded by an obtained certificate
if(CERT_REQUEST_STATUS_OBTAINED != pSupersedingCertType->dwStatus) { pCurCertType->dwStatus = CERT_REQUEST_STATUS_SUPERSEDE_ACTIVE; pSupersedingCertType->prgActive[pSupersedingCertType->dwActive]=dwCurIndex; (pSupersedingCertType->dwActive)++; } else { pCurCertType->dwStatus = 0; }
case CERT_REQUEST_STATUS_PENDING: AECopyCertStore(pCurCertType->hArchiveStore, pSupersedingCertType->hArchiveStore);
break;
case CERT_REQUEST_STATUS_OBTAINED:
AECopyCertStore(pCurCertType->hObtainedStore, pSupersedingCertType->hArchiveStore);
break;
default:
break; }
//we consider that we find a valid superseding template only if the status
//is obtained. If the status is anyting else, we need to keep searching since
//enrollment/renewal requests might not be granted
if(CERT_REQUEST_STATUS_OBTAINED == pSupersedingCertType->dwStatus) fFound=TRUE; }
//clear the visited flag in AE_General_Info
AEClearVistedFlag(pAE_General_Info); }
//free the property
if(awszSuperseding) CAFreeCertTypeProperty( pSupersedingCertType->hCertType, awszSuperseding);
awszSuperseding=NULL; }
return fFound; }
//-----------------------------------------------------------------------
//
// AEIsCALonger
//
// For renewal, the CA's certificate has to live longer than the
// renewing certificate
//
//-----------------------------------------------------------------------
BOOL AEIsCALonger(HCAINFO hCAInfo, PCERT_CONTEXT pOldCert) { BOOL fCALonger=TRUE;
PCCERT_CONTEXT pCACert=NULL;
//we assume the CA is good unless we found something wrong
if((NULL == hCAInfo) || (NULL == pOldCert)) goto Ret;
if(S_OK != CAGetCACertificate(hCAInfo, &pCACert)) goto Ret;
if(NULL == pCACert) goto Ret;
//CA cert's NotAfter should be longer than the issued certificate' NotAfger
if(1 == CompareFileTime(&(pCACert->pCertInfo->NotAfter), &(pOldCert->pCertInfo->NotAfter))) goto Ret;
fCALonger=FALSE;
Ret:
if(pCACert) CertFreeCertificateContext(pCACert);
return fCALonger; }
//-----------------------------------------------------------------------
//
// AECanFindCAForCertType
//
// Check if there exists a CA that can issue the specified certificate
// template.
//
//-----------------------------------------------------------------------
BOOL AECanFindCAForCertType(AE_GENERAL_INFO *pAE_General_Info, AE_CERTTYPE_INFO *pCertType) { DWORD dwIndex=0; BOOL fFound=FALSE; AE_CA_INFO *prgCAInfo=pAE_General_Info->rgCAInfo; BOOL fRenewal=FALSE;
//detect if we are performing an enrollment or renewal
if((pCertType->fRenewal) && (pCertType->pOldCert)) { if((pCertType->fNeedRA) && (pCertType->fCrossRA)) fRenewal=FALSE; else fRenewal=TRUE; } else fRenewal=FALSE;
if(prgCAInfo) { for(dwIndex=0; dwIndex < pAE_General_Info->dwCA; dwIndex++) { //make sure the CA supports the specific template
if(AEIsAnElement((pCertType->awszName)[0], (prgCAInfo[dwIndex]).awszCertificateTemplate)) { if(FALSE == fRenewal) { fFound=TRUE; break; } else { if(AEIsCALonger(prgCAInfo[dwIndex].hCAInfo, pCertType->pOldCert)) { fFound=TRUE; break; } } } } }
return fFound; }
//-----------------------------------------------------------------------
//
// AEManageActiveTemplates
//
// We make sure that for all active templates, we can in deed enroll
// for it.
//
//-----------------------------------------------------------------------
BOOL AEManageActiveTemplates(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex=0; AE_CERTTYPE_INFO *pCertTypeInfo=pAE_General_Info->rgCertTypeInfo; AE_CERTTYPE_INFO *pCurCertType=NULL; BOOL fCanEnrollCertType=FALSE; BOOL fUserProtection=FALSE; DWORD dwEventID=0;
if(pCertTypeInfo) { for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { pCurCertType = &(pCertTypeInfo[dwIndex]);
fCanEnrollCertType=FALSE; fUserProtection=FALSE;
if(CERT_REQUEST_STATUS_PENDING == pCurCertType->dwStatus) { //check if UI is required
if(CT_FLAG_USER_INTERACTION_REQUIRED & (pCurCertType->dwEnrollmentFlag)) { pCurCertType->fUIActive=TRUE;
if(pAE_General_Info->fMachine) { pCurCertType->dwStatus = 0;
//log that user does not have access right to the template
AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_NO_ACCESS_ACRS_OBJECT, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, (pCurCertType->awszDisplay)[0]); } }
continue; }
if(CERT_REQUEST_STATUS_ACTIVE != pCurCertType->dwStatus) continue;
//check if CRYPT_USER_PROTECTED is used for machine certificate template
if(CT_FLAG_STRONG_KEY_PROTECTION_REQUIRED & pCurCertType->dwPrivateKeyFlag) { if(pAE_General_Info->fMachine) { pCurCertType->dwStatus = 0;
//log that machine template should not require user password
AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_NO_ACCESS_ACRS_OBJECT, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, (pCurCertType->awszDisplay)[0]);
continue; } else { if(0 == (CT_FLAG_USER_INTERACTION_REQUIRED & (pCurCertType->dwEnrollmentFlag))) { pCurCertType->dwStatus = 0;
//log that user interaction is not set
AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_NO_ACCESS_ACRS_OBJECT, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, (pCurCertType->awszDisplay)[0]);
continue; } } }
fCanEnrollCertType=AECanEnrollCertType(pAE_General_Info->hToken, pCurCertType, pAE_General_Info, &fUserProtection);
if((!fCanEnrollCertType) || (!AECanFindCAForCertType(pAE_General_Info, pCurCertType)) ) { pCurCertType->dwStatus = 0;
//log that user does not have access right to the template
if(FALSE == fUserProtection) { dwEventID=EVENT_NO_ACCESS_ACRS_OBJECT; } else { if(pAE_General_Info->fMachine) dwEventID=EVENT_NO_ACCESS_ACRS_OBJECT; else dwEventID=EVENT_NO_ACCESS_ACRS_OBJECT; }
AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, dwEventID, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, (pCurCertType->awszDisplay)[0]); } else { //check if UI is required
if(CT_FLAG_USER_INTERACTION_REQUIRED & (pCurCertType->dwEnrollmentFlag)) { pCurCertType->fUIActive=TRUE;
if(pAE_General_Info->fMachine) { pCurCertType->dwStatus = 0;
//log that user does not have access right to the template
AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_NO_ACCESS_ACRS_OBJECT, pAE_General_Info->fMachine, pAE_General_Info->hToken, 1, (pCurCertType->awszDisplay)[0]); } }
} }
}
return TRUE; } //-----------------------------------------------------------------------
//
// AEManageSupersedeRequests
// remove duplicated requests based on "Supersede" relationship
//
//
//-----------------------------------------------------------------------
BOOL AEManageSupersedeRequests(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex=0; DWORD dwSuperseding=0; DWORD dwOrder=0; AE_CERTTYPE_INFO *pCertTypeInfo=pAE_General_Info->rgCertTypeInfo; AE_CERTTYPE_INFO *pCurCertType=NULL; AE_CERTTYPE_INFO *pSupersedingCertType=NULL; BOOL fFound=FALSE;
if(pCertTypeInfo) { for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { pCurCertType = &(pCertTypeInfo[dwIndex]);
//we only consider templates with valid status
if(0 == pCurCertType->dwStatus) continue;
fFound=FALSE;
for(dwOrder=0; dwOrder < g_dwSupersedeOrder; dwOrder++) { for(dwSuperseding=0; dwSuperseding < pAE_General_Info->dwCertType; dwSuperseding++) { //one can not be superseded by itself
if(dwIndex == dwSuperseding) continue;
pSupersedingCertType = &(pCertTypeInfo[dwSuperseding]);
//we consider templates with obtained status first
if(g_rgdwSupersedeOrder[dwOrder] != pSupersedingCertType->dwStatus) continue;
fFound = AECheckSupersedeRequest(dwIndex, pCurCertType, pSupersedingCertType, pAE_General_Info);
//we find a valid superseding template
if(fFound) break; }
//we find a valid superseding template
if(fFound) break; } } }
return TRUE; }
//-----------------------------------------------------------------------
//
// AEDoOneEnrollment
//
//-----------------------------------------------------------------------
/*BOOL AEDoOneEnrollment(HWND hwndParent,
BOOL fUIProcess, BOOL fMachine, LPWSTR pwszMachineName, AE_CERTTYPE_INFO *pCertType, AE_CA_INFO *pCAInfo, DWORD *pdwStatus) { BOOL fResult = FALSE; CRYPTUI_WIZ_CERT_REQUEST_INFO CertRequestInfo; CRYPTUI_WIZ_CERT_TYPE CertWizType; CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW CertPvkNew; CRYPT_KEY_PROV_INFO KeyProvInfo;
memset(&CertRequestInfo, 0, sizeof(CRYPTUI_WIZ_CERT_REQUEST_INFO)); memset(&CertWizType, 0, sizeof(CRYPTUI_WIZ_CERT_TYPE)); memset(&CertPvkNew, 0, sizeof(CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW)); memset(&KeyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
CertRequestInfo.dwSize = sizeof(CRYPTUI_WIZ_CERT_REQUEST_INFO);
//enroll or renewal
if((pCertType->fRenewal) && (pCertType->pOldCert)) { CertRequestInfo.dwPurpose = CRYPTUI_WIZ_CERT_RENEW; CertRequestInfo.pRenewCertContext = pCertType->pOldCert; } else CertRequestInfo.dwPurpose = CRYPTUI_WIZ_CERT_ENROLL;
//machine name
if(fMachine) { CertRequestInfo.pwszMachineName = pwszMachineName; }
//private key information
CertRequestInfo.dwPvkChoice = CRYPTUI_WIZ_CERT_REQUEST_PVK_CHOICE_NEW; CertRequestInfo.pPvkNew = &CertPvkNew;
CertPvkNew.dwSize = sizeof(CertPvkNew); CertPvkNew.pKeyProvInfo = &KeyProvInfo; CertPvkNew.dwGenKeyFlags = 0; //no need to specify the exportable flags
//SILENT is always set for machine
if(fMachine) KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET | CRYPT_SILENT; else { if(fUIProcess) KeyProvInfo.dwFlags = 0; else KeyProvInfo.dwFlags = CRYPT_SILENT; }
//CA information
CertRequestInfo.pwszCALocation = pCAInfo->awszCADNS[0]; CertRequestInfo.pwszCAName = pCAInfo->awszCAName[0];
//enroll for the template
CertRequestInfo.dwCertChoice = CRYPTUI_WIZ_CERT_REQUEST_CERT_TYPE; CertRequestInfo.pCertType = &CertWizType;
CertWizType.dwSize = sizeof(CertWizType); CertWizType.cCertType = 1; CertWizType.rgwszCertType = &(pCertType->awszName[0]);
//ISSUE: we need to call Duncanb's new no-DS look up API
//for faster performance
fResult = CryptUIWizCertRequest(CRYPTUI_WIZ_NO_UI_EXCEPT_CSP | CRYPTUI_WIZ_NO_INSTALL_ROOT, hwndParent, NULL, &CertRequestInfo, NULL, //pCertContext
pdwStatus); return fResult; } */
//-----------------------------------------------------------------------
//
// AECreateEnrollmentRequest
//
//
//-----------------------------------------------------------------------
BOOL AECreateEnrollmentRequest( HWND hwndParent, BOOL fUIProcess, BOOL fMachine, LPWSTR pwszMachineName, AE_CERTTYPE_INFO *pCertType, AE_CA_INFO *pCAInfo, HANDLE *phRequest, DWORD *pdwLastError) { BOOL fResult = FALSE; CRYPTUI_WIZ_CREATE_CERT_REQUEST_INFO CreateRequestInfo; CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW CertPvkNew; CRYPT_KEY_PROV_INFO KeyProvInfo; DWORD dwFlags=CRYPTUI_WIZ_NO_UI_EXCEPT_CSP | CRYPTUI_WIZ_NO_INSTALL_ROOT | CRYPTUI_WIZ_ALLOW_ALL_TEMPLATES | CRYPTUI_WIZ_ALLOW_ALL_CAS; DWORD dwSize=0; DWORD dwAcquireFlags=0; BOOL fResetProv=FALSE;
CRYPT_KEY_PROV_INFO *pKeyProvInfo=NULL; HANDLE hRequest=NULL;
memset(&CreateRequestInfo, 0, sizeof(CRYPTUI_WIZ_CREATE_CERT_REQUEST_INFO)); memset(&CertPvkNew, 0, sizeof(CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW)); memset(&KeyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
CreateRequestInfo.dwSize = sizeof(CreateRequestInfo);
//enroll or renewal
if((pCertType->fRenewal) && (pCertType->pOldCert)) { CreateRequestInfo.dwPurpose = CRYPTUI_WIZ_CERT_RENEW; CreateRequestInfo.pRenewCertContext = pCertType->pOldCert;
//we should not archive renewal certificate for cross template RA
if((pCertType->fNeedRA) && (pCertType->fCrossRA)) dwFlags |= CRYPTUI_WIZ_NO_ARCHIVE_RENEW_CERT;
//we should disalbe UI for machine or non-UI enrollment renew/RA certificate
if((TRUE == fMachine) || (FALSE == fUIProcess)) { dwSize=0; if(!CertGetCertificateContextProperty(pCertType->pOldCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize)) goto error;
pKeyProvInfo=(CRYPT_KEY_PROV_INFO *)LocalAlloc(LPTR, dwSize);
if(NULL == pKeyProvInfo) goto error;
if(!CertGetCertificateContextProperty(pCertType->pOldCert, CERT_KEY_PROV_INFO_PROP_ID, pKeyProvInfo, &dwSize)) goto error;
dwAcquireFlags=pKeyProvInfo->dwFlags;
pKeyProvInfo->dwFlags |= CRYPT_SILENT;
//set the property
if(!CertSetCertificateContextProperty(pCertType->pOldCert, CERT_KEY_PROV_INFO_PROP_ID, CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, pKeyProvInfo)) goto error;
fResetProv=TRUE; } } else CreateRequestInfo.dwPurpose = CRYPTUI_WIZ_CERT_ENROLL;
//cert template information
CreateRequestInfo.hCertType = pCertType->hCertType;
//machine name
if(fMachine) { CreateRequestInfo.fMachineContext = TRUE; }
//private key information
CreateRequestInfo.dwPvkChoice = CRYPTUI_WIZ_CERT_REQUEST_PVK_CHOICE_NEW; CreateRequestInfo.pPvkNew = &CertPvkNew;
CertPvkNew.dwSize = sizeof(CertPvkNew); CertPvkNew.pKeyProvInfo = &KeyProvInfo; CertPvkNew.dwGenKeyFlags = 0; //no need to specify the exportable flags
//SILENT is always set for machine
if(fMachine) KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET | CRYPT_SILENT; else { if(fUIProcess) KeyProvInfo.dwFlags = 0; else KeyProvInfo.dwFlags = CRYPT_SILENT; }
//CA information
CreateRequestInfo.pwszCALocation = pCAInfo->awszCADNS[0]; CreateRequestInfo.pwszCAName = pCAInfo->awszCAName[0];
if(!CryptUIWizCreateCertRequestNoDS( dwFlags, hwndParent, &CreateRequestInfo, &hRequest)) goto error;
if(NULL==hRequest) goto error;
*phRequest=hRequest;
hRequest=NULL;
fResult = TRUE;
error:
//get the last error
if(FALSE == fResult) { *pdwLastError=GetLastError(); }
//reset the property
if(TRUE == fResetProv) { if((pKeyProvInfo) && (pCertType->pOldCert)) { pKeyProvInfo->dwFlags = dwAcquireFlags;
//set the property
CertSetCertificateContextProperty(pCertType->pOldCert, CERT_KEY_PROV_INFO_PROP_ID, CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, pKeyProvInfo); } }
if(pKeyProvInfo) LocalFree(pKeyProvInfo);
if(hRequest) CryptUIWizFreeCertRequestNoDS(hRequest);
return fResult; }
//-----------------------------------------------------------------------
//
// AECancelled
//
//-----------------------------------------------------------------------
BOOL AECancelled(HANDLE hCancelEvent) { if(NULL==hCancelEvent) return FALSE;
//test if the event is signalled
if(WAIT_OBJECT_0 == WaitForSingleObject(hCancelEvent, 0)) return TRUE;
return FALSE; } //-----------------------------------------------------------------------
//
// AEDoEnrollment
//
// return TRUE is no need to do another renewal.
// *pdwStatus contain the real enrollment status.
//
//-----------------------------------------------------------------------
BOOL AEDoEnrollment(HWND hwndParent, HANDLE hCancelEvent, BOOL fUIProcess, DWORD dwLogLevel, HANDLE hToken, BOOL fMachine, LPWSTR pwszMachineName, AE_CERTTYPE_INFO *pCertType, DWORD dwCA, AE_CA_INFO *rgCAInfo, DWORD *pdwStatus) { BOOL fResult = FALSE; DWORD dwIndex = 0; DWORD dwCAIndex = 0; BOOL fRenewal = FALSE; DWORD dwEventID = 0; BOOL fFoundCA = FALSE; DWORD idsSummary = 0; //keep the last failure case
DWORD dwLastError = 0;
CRYPTUI_WIZ_QUERY_CERT_REQUEST_INFO QueryCertRequestInfo;
HANDLE hRequest=NULL; PCCERT_CONTEXT pCertContext=NULL;
//init the out parameter
*pdwStatus = CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_ERROR;
//detect if we are performing an enrollment or renewal
if((pCertType->fRenewal) && (pCertType->pOldCert)) { if((pCertType->fNeedRA) && (pCertType->fCrossRA)) fRenewal=FALSE; else fRenewal=TRUE; } else fRenewal=FALSE;
//loop through all the CAs
for(dwIndex =0; dwIndex < dwCA; dwIndex++) { dwCAIndex = (dwIndex + pCertType->dwRandomCAIndex) % dwCA;
if(AECancelled(hCancelEvent)) { //no need to renew any more
fResult=TRUE;
//log that autoenrollment is cancelled
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_AUTOENROLL_CANCELLED, fMachine, hToken, 0);
break; }
//make sure the CA supports the specific template
if(!AEIsAnElement((pCertType->awszName)[0], rgCAInfo[dwCAIndex].awszCertificateTemplate)) continue;
//make sure the CA's validity period of more than the renewing certificate
if(TRUE == fRenewal) { if(!AEIsCALonger(rgCAInfo[dwCAIndex].hCAInfo, pCertType->pOldCert)) continue; }
//enroll to the CA
*pdwStatus = CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_ERROR; fFoundCA = TRUE;
//create a certificate request
if(NULL==hRequest) { if(!AECreateEnrollmentRequest(hwndParent, fUIProcess, fMachine, pwszMachineName, pCertType, &(rgCAInfo[dwCAIndex]), &hRequest, &dwLastError)) { //check if user cancelled the enrollment. If so, no
//need to try another CA.
if((HRESULT_FROM_WIN32(ERROR_CANCELLED) == dwLastError) || (SCARD_W_CANCELLED_BY_USER == dwLastError)) { //no need to renewal anymore
fResult = TRUE;
//log that autoenrollment is cancelled
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_AUTOENROLL_CANCELLED_TEMPLATE, fMachine, hToken, 1, pCertType->awszDisplay[0]);
break;
} else { idsSummary=IDS_SUMMARY_REQUEST;
if(CT_FLAG_REQUIRE_PRIVATE_KEY_ARCHIVAL & pCertType->dwPrivateKeyFlag) { //we have a chance of success with another CA
if(hRequest) { CryptUIWizFreeCertRequestNoDS(hRequest); hRequest=NULL; }
continue;
} else { //we have no hope to create a request successfully
//mark dwIndex to the dwCA so that we will log an event at the end of the loop
dwIndex=dwCA; break; } } } }
//check the cancel again because significant time can pass during
//request creation
if(AECancelled(hCancelEvent)) { //no need to renew any more
fResult=TRUE;
//log that autoenrollment is cancelled
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_AUTOENROLL_CANCELLED, fMachine, hToken, 0);
break; }
if(CryptUIWizSubmitCertRequestNoDS( hRequest, hwndParent, rgCAInfo[dwCAIndex].awszCAName[0], rgCAInfo[dwCAIndex].awszCADNS[0], pdwStatus, &pCertContext)) { //no need to try another CA if the request is successful or pending
if((CRYPTUI_WIZ_CERT_REQUEST_STATUS_SUCCEEDED == (*pdwStatus)) || (CRYPTUI_WIZ_CERT_REQUEST_STATUS_UNDER_SUBMISSION == (*pdwStatus)) ) { //no need to renewal anymore
fResult = TRUE;
if(CRYPTUI_WIZ_CERT_REQUEST_STATUS_SUCCEEDED == (*pdwStatus)) { //we copy the certificate to publishing
if(pCertContext) { CertAddCertificateContextToStore(pCertType->hIssuedStore, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL);
CertFreeCertificateContext(pCertContext); pCertContext=NULL; }
dwEventID=fRenewal ? EVENT_RENEWAL_SUCCESS_ONCE : EVENT_ENROLL_SUCCESS_ONCE; } else { dwEventID=fRenewal ? EVENT_RENEWAL_PENDING_ONCE : EVENT_ENROLL_PENDING_ONCE; }
//log the enrollment sucess or pending event
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, dwEventID, fMachine, hToken, 3, pCertType->awszDisplay[0], rgCAInfo[dwCAIndex].awszCADisplay[0], rgCAInfo[dwCAIndex].awszCADNS[0]);
//log if the private key is re-used
memset(&QueryCertRequestInfo, 0, sizeof(QueryCertRequestInfo)); QueryCertRequestInfo.dwSize=sizeof(QueryCertRequestInfo);
if(CryptUIWizQueryCertRequestNoDS(hRequest, &QueryCertRequestInfo)) { if(CRYPTUI_WIZ_QUERY_CERT_REQUEST_STATUS_CREATE_REUSED_PRIVATE_KEY & (QueryCertRequestInfo.dwStatus)) { AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_PRIVATE_KEY_REUSED, fMachine, hToken, 1, pCertType->awszDisplay[0]); } }
break; } }
//get the last error
dwLastError=GetLastError();
idsSummary=IDS_SUMMARY_CA;
//log the one enrollment warning
AELogAutoEnrollmentEvent(dwLogLevel, TRUE, HRESULT_FROM_WIN32(dwLastError), fRenewal ? EVENT_RENEWAL_FAIL_ONCE : EVENT_ENROLL_FAIL_ONCE, fMachine, hToken, 3, pCertType->awszDisplay[0], rgCAInfo[dwCAIndex].awszCADisplay[0], rgCAInfo[dwCAIndex].awszCADNS[0]);
//we should recreate the request for key archival
if(CT_FLAG_REQUIRE_PRIVATE_KEY_ARCHIVAL & pCertType->dwPrivateKeyFlag) { if(hRequest) { CryptUIWizFreeCertRequestNoDS(hRequest); hRequest=NULL; } } }
//log all enrollments error
//the loop will exit only if CANCEL, or SUCCEED, or we run out of CAs to try or
//the request can not be created
if(dwIndex == dwCA) { //we either run out of CAs to try or the request can not be created
if(0 != idsSummary) pCertType->idsSummary=idsSummary;
if(fFoundCA) { dwEventID = fRenewal ? EVENT_RENEWAL_FAIL : EVENT_ENROLL_FAIL; } else { //if there is no CA, no need to try re-enrollment
if(fRenewal) pCertType->fRenewal=FALSE; dwEventID = fRenewal ? EVENT_RENEWAL_NO_CA_FAIL : EVENT_ENROLL_NO_CA_FAIL; }
AELogAutoEnrollmentEvent(dwLogLevel, fFoundCA ? TRUE : FALSE, HRESULT_FROM_WIN32(dwLastError), dwEventID, fMachine, hToken, 1, pCertType->awszDisplay[0]); }
if(hRequest) CryptUIWizFreeCertRequestNoDS(hRequest);
return fResult; }
//-----------------------------------------------------------------------
//
// AEEnrollmentCertificates
//
//-----------------------------------------------------------------------
BOOL AEEnrollmentCertificates(AE_GENERAL_INFO *pAE_General_Info, DWORD dwEnrollStatus) { AE_CERTTYPE_INFO *rgCertTypeInfo = NULL; DWORD dwIndex =0 ; DWORD dwStatus= 0; DWORD dwRandom = 0;
HCRYPTPROV hProv = NULL;
rgCertTypeInfo = pAE_General_Info->rgCertTypeInfo;
if(NULL == rgCertTypeInfo) return FALSE;
if((0 == pAE_General_Info->dwCA) || (NULL==pAE_General_Info->rgCAInfo)) return FALSE;
if(!CryptAcquireContextW(&hProv, NULL, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) hProv=NULL;
//going through all the active requests
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { //we enroll/renew for templates that are active
if(dwEnrollStatus != rgCertTypeInfo[dwIndex].dwStatus) continue;
if(pAE_General_Info->fUIProcess != rgCertTypeInfo[dwIndex].fUIActive) continue; //select a random CA index to balance the load
if((hProv) && (CryptGenRandom(hProv, sizeof(dwRandom), (BYTE *)(&dwRandom)))) { rgCertTypeInfo[dwIndex].dwRandomCAIndex = dwRandom % (pAE_General_Info->dwCA); } else rgCertTypeInfo[dwIndex].dwRandomCAIndex = 0;
//enroll
dwStatus=0;
//report progress
if(pAE_General_Info->fUIProcess) { //continue if user choose CANCEL in view RA dialogue
if(!AEUIProgressReport(FALSE, &(rgCertTypeInfo[dwIndex]),pAE_General_Info->hwndDlg, pAE_General_Info->hCancelEvent)) { AEUIProgressAdvance(pAE_General_Info); continue; }
}
if(AEDoEnrollment( pAE_General_Info->hwndDlg ? pAE_General_Info->hwndDlg : pAE_General_Info->hwndParent, pAE_General_Info->hCancelEvent, pAE_General_Info->fUIProcess, pAE_General_Info->dwLogLevel, pAE_General_Info->hToken, pAE_General_Info->fMachine, pAE_General_Info->wszMachineName, &(rgCertTypeInfo[dwIndex]), pAE_General_Info->dwCA, pAE_General_Info->rgCAInfo, &dwStatus)) { //mark the status
if(CRYPTUI_WIZ_CERT_REQUEST_STATUS_SUCCEEDED == dwStatus) rgCertTypeInfo[dwIndex].dwStatus=CERT_REQUEST_STATUS_OBTAINED; } else { //if renewal failed, we try to re-enrollment if no RA is required
if((rgCertTypeInfo[dwIndex].fRenewal) && (FALSE == (rgCertTypeInfo[dwIndex].fNeedRA))) { rgCertTypeInfo[dwIndex].fRenewal=FALSE; dwStatus=0;
if(AEDoEnrollment( pAE_General_Info->hwndDlg ? pAE_General_Info->hwndDlg : pAE_General_Info->hwndParent, pAE_General_Info->hCancelEvent, pAE_General_Info->fUIProcess, pAE_General_Info->dwLogLevel, pAE_General_Info->hToken, pAE_General_Info->fMachine, pAE_General_Info->wszMachineName, &(rgCertTypeInfo[dwIndex]), pAE_General_Info->dwCA, pAE_General_Info->rgCAInfo, &dwStatus)) { //mark the status
if(CRYPTUI_WIZ_CERT_REQUEST_STATUS_SUCCEEDED == dwStatus) rgCertTypeInfo[dwIndex].dwStatus=CERT_REQUEST_STATUS_OBTAINED; } } }
//advance progress
if(pAE_General_Info->fUIProcess) { AEUIProgressAdvance(pAE_General_Info); }
}
if(hProv) CryptReleaseContext(hProv, 0);
return TRUE; }
//-----------------------------------------------------------------------
//
// AEIsDeletableCert
// Decide if we should archive or delete the certificate
//
//-----------------------------------------------------------------------
BOOL AEIsDeletableCert(PCCERT_CONTEXT pCertContext, AE_GENERAL_INFO *pAE_General_Info) { AE_CERTTYPE_INFO *pCertType=NULL; BOOL fDelete=FALSE;
AE_TEMPLATE_INFO AETemplateInfo;
memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
//only interested in certificate with template information
if(!AERetrieveTemplateInfo(pCertContext, &AETemplateInfo)) goto Ret;
pCertType=AEFindTemplateInRequestTree(&AETemplateInfo, pAE_General_Info);
if(NULL==pCertType) goto Ret;
if(CT_FLAG_REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE & (pCertType->dwEnrollmentFlag)) fDelete=TRUE; else fDelete=FALSE; Ret:
AEFreeTemplateInfo(&AETemplateInfo);
return fDelete; }
//-----------------------------------------------------------------------
//
// AEArchiveObsoleteCertificates
// archive old certificate after the enrollment/renewal
//
// clean up the hUserDS store (delete the expired or revoked certificate)
//-----------------------------------------------------------------------
BOOL AEArchiveObsoleteCertificates(AE_GENERAL_INFO *pAE_General_Info) { AE_CERTTYPE_INFO *rgCertTypeInfo = NULL; DWORD dwIndex = 0; CRYPT_DATA_BLOB Archived; BOOL fArchived = FALSE; AE_CERT_INFO AECertInfo; BOOL fRepublish=FALSE; BYTE rgbHash[SHA1_HASH_LENGTH]; CRYPT_HASH_BLOB blobHash; BOOL fHash=FALSE;
HCERTSTORE hUserDS = NULL; PCCERT_CONTEXT pCertContext = NULL; PCCERT_CONTEXT pMyContext = NULL; PCCERT_CONTEXT pDSContext = NULL; PCCERT_CONTEXT pIssuedContext = NULL;
rgCertTypeInfo = pAE_General_Info->rgCertTypeInfo;
if(NULL == rgCertTypeInfo) return FALSE;
memset(&Archived, 0, sizeof(CRYPT_DATA_BLOB)); memset(&AECertInfo, 0, sizeof(AE_CERT_INFO));
//open the UserDS store
if(!(pAE_General_Info->fMachine)) { hUserDS = AEOpenUserDSStore(pAE_General_Info, 0); }
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { fHash=FALSE; fRepublish=FALSE;
if(CERT_REQUEST_STATUS_OBTAINED == rgCertTypeInfo[dwIndex].dwStatus) { //get the hash of newly enrolled certificate
blobHash.cbData=SHA1_HASH_LENGTH; blobHash.pbData=rgbHash;
if(rgCertTypeInfo[dwIndex].hIssuedStore) { if(pIssuedContext = CertEnumCertificatesInStore( rgCertTypeInfo[dwIndex].hIssuedStore, NULL)) { if(CryptHashCertificate( NULL, 0, X509_ASN_ENCODING, pIssuedContext->pbCertEncoded, pIssuedContext->cbCertEncoded, blobHash.pbData, &(blobHash.cbData))) { fHash=TRUE; } }
//free the cert context
if(pIssuedContext) { CertFreeCertificateContext(pIssuedContext); pIssuedContext = NULL; } }
while(pCertContext = CertEnumCertificatesInStore( rgCertTypeInfo[dwIndex].hArchiveStore, pCertContext)) { //archive or delete the certificate from my store
pMyContext = FindCertificateInOtherStore( pAE_General_Info->hMyStore, pCertContext);
if(pMyContext) { //set the Hash of the newly enrolled certificate
if(fHash) { CertSetCertificateContextProperty( pMyContext, CERT_RENEWAL_PROP_ID, 0, &blobHash); }
if(AEIsDeletableCert(pMyContext, pAE_General_Info)) { CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pMyContext)); } else { // We force an archive on the old cert and close it.
CertSetCertificateContextProperty(pMyContext, CERT_ARCHIVED_PROP_ID, 0, &Archived);
fArchived=TRUE; }
CertFreeCertificateContext(pMyContext); pMyContext = NULL; }
//check the DS store. remove the certificates from DS store
if(hUserDS) { if(pMyContext = FindCertificateInOtherStore( hUserDS, pCertContext)) { CertDeleteCertificateFromStore(pMyContext); pMyContext = NULL; fRepublish=TRUE; } } } } }
//now we are done with archiving, we clean up user DS store
if(AUTO_ENROLLMENT_ENABLE_MY_STORE_MANAGEMENT & (pAE_General_Info->dwPolicy)) { if(hUserDS) { while(pDSContext = CertEnumCertificatesInStore(hUserDS, pDSContext)) { AEValidateCertificateInfo(pAE_General_Info, NULL, //do not evaluate soon to expire
FALSE, //do not valid private key
pDSContext, &AECertInfo);
if(FALSE == AECertInfo.fRenewal) { CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pDSContext)); fRepublish=TRUE; }
memset(&AECertInfo, 0, sizeof(AE_CERT_INFO)); } } }
//we have to republish the certificates as we have rewritten the user DS store
//CA might has just published to the location
if(fRepublish) { if(hUserDS) { for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if(CERT_REQUEST_STATUS_OBTAINED == rgCertTypeInfo[dwIndex].dwStatus) { if((rgCertTypeInfo[dwIndex].hIssuedStore) && (CT_FLAG_PUBLISH_TO_DS & rgCertTypeInfo[dwIndex].dwEnrollmentFlag) ) { pCertContext=NULL; while(pCertContext = CertEnumCertificatesInStore( rgCertTypeInfo[dwIndex].hIssuedStore, pCertContext)) { CertAddCertificateContextToStore(hUserDS, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL); } } } } } } //report the event if archival has happened
if(fArchived) AELogAutoEnrollmentEvent(pAE_General_Info->dwLogLevel, FALSE, S_OK, EVENT_ARCHIVE_CERT, pAE_General_Info->fMachine, pAE_General_Info->hToken, 0);
if(hUserDS) CertCloseStore(hUserDS, 0);
return TRUE; }
//-----------------------------------------------------------------------
//
// AERemoveSupersedeActive
// Remove supersedeActive flag after any successful the enrollment/renewal
//
//-----------------------------------------------------------------------
BOOL AERemoveSupersedeActive(AE_GENERAL_INFO *pAE_General_Info) { AE_CERTTYPE_INFO *rgCertTypeInfo = NULL; DWORD dwIndex = 0; DWORD dwActiveIndex = 0; DWORD dwMarkIndex = 0;
rgCertTypeInfo = pAE_General_Info->rgCertTypeInfo;
if(NULL == rgCertTypeInfo) return FALSE;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if(CERT_REQUEST_STATUS_OBTAINED == rgCertTypeInfo[dwIndex].dwStatus) { for(dwActiveIndex=0; dwActiveIndex < rgCertTypeInfo[dwIndex].dwActive; dwActiveIndex++) { dwMarkIndex = rgCertTypeInfo[dwIndex].prgActive[dwActiveIndex]; rgCertTypeInfo[dwMarkIndex].dwStatus=CERT_REQUEST_STATUS_OBTAINED; } } }
return TRUE; }
//-----------------------------------------------------------------------
//
// AEEnrollmentWalker
//
// This functin performs enrollment tasks
//
//
//-----------------------------------------------------------------------
BOOL AEEnrollmentWalker(AE_GENERAL_INFO *pAE_General_Info) {
BOOL fResult = FALSE;
//we need to set the range for the progress bar in the
//UI case
if((pAE_General_Info->fUIProcess) && (pAE_General_Info->hwndDlg)) { //set the range
if(0 != (pAE_General_Info->dwUIEnrollCount)) { SendMessage(GetDlgItem(pAE_General_Info->hwndDlg, IDC_ENROLL_PROGRESS), PBM_SETRANGE, 0, MAKELPARAM(0, ((pAE_General_Info->dwUIEnrollCount) & (0xFFFF))) );
SendMessage(GetDlgItem(pAE_General_Info->hwndDlg, IDC_ENROLL_PROGRESS), PBM_SETSTEP, (WPARAM)1, 0);
SendMessage(GetDlgItem(pAE_General_Info->hwndDlg, IDC_ENROLL_PROGRESS), PBM_SETPOS, (WPARAM)0, 0); } }
//retrieve the pending request. Mark the status to obtained if the
//certificate is issued and of the correct version
if(AUTO_ENROLLMENT_ENABLE_PENDING_FETCH & (pAE_General_Info->dwPolicy)) { if(FALSE == pAE_General_Info->fUIProcess) { if(!AEProcessPendingRequest(pAE_General_Info)) goto Ret; } else { if(!AEProcessUIPendingRequest(pAE_General_Info)) goto Ret; } }
//remove duplicated requests based on "Supersede" relationship
//supress active templates that are superseded by other templates
if(!AEManageSupersedeRequests(pAE_General_Info)) goto Ret;
//do enrollment/renewal
if(!AEEnrollmentCertificates(pAE_General_Info, CERT_REQUEST_STATUS_ACTIVE)) goto Ret;
//We try to get the superseded templates if supserseding templates failed.
//Only for machine for the case of two V2 DC templates.
if(TRUE == pAE_General_Info->fMachine) { //remove supersedeActive based on the obtained flag
if(!AERemoveSupersedeActive(pAE_General_Info)) goto Ret;
//do enrollment/renewal again since we migh fail to get superseding templates
if(!AEEnrollmentCertificates(pAE_General_Info, CERT_REQUEST_STATUS_SUPERSEDE_ACTIVE)) goto Ret; }
fResult = TRUE;
Ret:
return fResult; }
//-----------------------------------------------------------------------------
//
// AEUIProgressAdvance
//
// Increase the progress bar by one step
//-----------------------------------------------------------------------------
BOOL AEUIProgressAdvance(AE_GENERAL_INFO *pAE_General_Info) { BOOL fResult=FALSE;
if(NULL==pAE_General_Info) goto Ret;
if(NULL==(pAE_General_Info->hwndDlg)) goto Ret;
//check if CANCEL button is clicked
if(AECancelled(pAE_General_Info->hCancelEvent)) { fResult=TRUE; goto Ret; }
//advance the progress bar
SendMessage(GetDlgItem(pAE_General_Info->hwndDlg, IDC_ENROLL_PROGRESS), PBM_STEPIT, 0, 0);
fResult=TRUE;
Ret:
return fResult; }
//-----------------------------------------------------------------------------
//
// AEUIGetNameFromCert
//
// Retrieve a unique string to identify the certificate.
//-----------------------------------------------------------------------------
BOOL AEUIGetNameFromCert(PCCERT_CONTEXT pCertContext, LPWSTR *ppwszRACert) {
BOOL fResult=FALSE; DWORD dwChar=0; DWORD cbOID=0; PCCRYPT_OID_INFO pOIDInfo=NULL;
LPWSTR pwszRACert=NULL; AE_TEMPLATE_INFO TemplateInfo; LPSTR szOID=NULL;
if((NULL==pCertContext) || (NULL==ppwszRACert)) goto Ret;
*ppwszRACert=NULL; memset(&TemplateInfo, 0, sizeof(TemplateInfo));
//get the template name first
if(!AERetrieveTemplateInfo(pCertContext, &TemplateInfo)) goto Ret; if(TemplateInfo.pwszName) { pwszRACert=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (wcslen(TemplateInfo.pwszName) + 1)); if(NULL == pwszRACert) goto Ret;
wcscpy(pwszRACert, TemplateInfo.pwszName); } else { if(NULL==(TemplateInfo.pwszOid)) goto Ret;
//find the OID
if(0 == (cbOID = WideCharToMultiByte(CP_ACP, 0, TemplateInfo.pwszOid, -1, NULL, 0, NULL, NULL))) goto Ret;
szOID=(LPSTR)LocalAlloc(LPTR, cbOID);
if(NULL==szOID) goto Ret;
if(0 == WideCharToMultiByte(CP_ACP, 0, TemplateInfo.pwszOid, -1, szOID, cbOID, NULL, NULL)) goto Ret; pOIDInfo=CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, szOID, CRYPT_TEMPLATE_OID_GROUP_ID);
if(pOIDInfo) { if(pOIDInfo->pwszName) { pwszRACert=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (wcslen(pOIDInfo->pwszName) + 1)); if(NULL== pwszRACert) goto Ret;
wcscpy(pwszRACert, pOIDInfo->pwszName); } }
}
//if template name does not exist. Get the subject name for now
/* if(NULL==pwszRACert)
{ if(0 == (dwChar=CertGetNameStringW( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0))) goto Ret;
pwszRACert=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (dwChar)); if(NULL== pwszRACert) goto Ret;
if(0 == (dwChar=CertGetNameStringW( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pwszRACert, dwChar))) goto Ret; } */
*ppwszRACert = pwszRACert; pwszRACert=NULL;
fResult=TRUE;
Ret:
if(pwszRACert) LocalFree(pwszRACert);
if(szOID) LocalFree(szOID);
AEFreeTemplateInfo(&TemplateInfo);
return fResult;
}
//-----------------------------------------------------------------------------
//
// AEGetRACertInfo
//
//-----------------------------------------------------------------------------
BOOL AEGetRACertInfo(PCERT_CONTEXT pRAContext, LPWSTR pwszRATemplate, LPWSTR *ppwszRACertInfo) { BOOL fResult=FALSE; UINT idsMessage=0; DWORD dwSize=0;
LPWSTR pwszIssuer=NULL;
if(NULL==pRAContext) goto Ret;
if(pwszRATemplate) idsMessage=IDS_VIEW_RA_INFO; else idsMessage=IDS_VIEW_RA_INFO_GENERAL;
//the cert has to have an issuer
if(0 == (dwSize=CertNameToStrW( ENCODING_TYPE, &(pRAContext->pCertInfo->Issuer), CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, NULL, 0))) goto Ret;
pwszIssuer=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * dwSize); if(NULL==pwszIssuer) goto Ret;
if(0 == CertNameToStrW( ENCODING_TYPE, &(pRAContext->pCertInfo->Issuer), CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, pwszIssuer, dwSize)) goto Ret;
if(!FormatMessageUnicode( ppwszRACertInfo, idsMessage, pwszIssuer, pwszRATemplate)) goto Ret;
fResult=TRUE;
Ret:
if(pwszIssuer) LocalFree(pwszIssuer);
return fResult; }
//-----------------------------------------------------------------------------
//
// WinProc for the view RA certificate dialogue
//
//-----------------------------------------------------------------------------
INT_PTR CALLBACK AEViewRADlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL fPropertyChanged = FALSE; AE_VIEW_RA_INFO *pAEViewRAInfo = NULL; CRYPTUI_VIEWCERTIFICATE_STRUCT CertViewStruct;
LPWSTR pwszRACertInfo=NULL;
switch (msg) { case WM_INITDIALOG: pAEViewRAInfo=(AE_VIEW_RA_INFO *)lParam;
if(NULL==pAEViewRAInfo) break;
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pAEViewRAInfo);
//display the RA template and issuer dynamically
if(AEGetRACertInfo(pAEViewRAInfo->pRAContext, pAEViewRAInfo->pwszRATemplate, &pwszRACertInfo)) { SetDlgItemTextW(hwndDlg, IDC_EDIT3, pwszRACertInfo);
LocalFree((HLOCAL)pwszRACertInfo);
}
return TRUE; break;
case WM_NOTIFY: break;
case WM_CLOSE: EndDialog(hwndDlg, IDC_BUTTON3); return TRUE; break;
case WM_COMMAND: switch (LOWORD(wParam)) { //view certificate
case IDC_BUTTON1: if(NULL==(pAEViewRAInfo=(AE_VIEW_RA_INFO *)GetWindowLongPtr(hwndDlg, DWLP_USER))) break; if(NULL==pAEViewRAInfo->pRAContext) break;
//show the certificate
memset(&CertViewStruct, 0, sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCT)); CertViewStruct.dwSize=sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCT); CertViewStruct.hwndParent=hwndDlg; CertViewStruct.dwFlags=CRYPTUI_DISABLE_EDITPROPERTIES; CertViewStruct.pCertContext=pAEViewRAInfo->pRAContext;
fPropertyChanged=FALSE;
CryptUIDlgViewCertificate(&CertViewStruct, &fPropertyChanged);
return TRUE;
//OK
case IDC_BUTTON2: EndDialog(hwndDlg, IDC_BUTTON2); return TRUE; } break;
default: return FALSE; }
return FALSE; } //-----------------------------------------------------------------------------
//
// AEUIProgressReport
//
// Report the current enrollment action. Return FALSE if no progress status
// can be reported.
//-----------------------------------------------------------------------------
BOOL AEUIProgressReport(BOOL fPending, AE_CERTTYPE_INFO *pCertType, HWND hwndDlg, HANDLE hCancelEvent) { BOOL fResult=FALSE; UINT idsMessage=0; INT_PTR ret=0; AE_VIEW_RA_INFO AEViewRAInfo;
LPWSTR *awszFriendlyName=NULL; LPWSTR pwszRACert=NULL; LPWSTR pwszReport=NULL;
memset(&AEViewRAInfo, 0, sizeof(AE_VIEW_RA_INFO));
if((NULL==pCertType) || (NULL==hwndDlg)) goto Ret;
if(NULL==(pCertType->hCertType)) goto Ret;
if(AECancelled(hCancelEvent)) { fResult=TRUE; goto Ret; }
if(fPending) idsMessage=IDS_REPORT_PENDING; else { if((pCertType->fRenewal) && (pCertType->pOldCert)) { if(pCertType->fNeedRA) { if(FALSE == (pCertType->fCrossRA)) idsMessage=IDS_REPORT_RENEW; else idsMessage=IDS_REPORT_ENROLL_RA; } else idsMessage=IDS_REPORT_RENEW; } else idsMessage=IDS_REPORT_ENROLL; }
//retrieve the template's friendly name
if(S_OK != CAGetCertTypePropertyEx( pCertType->hCertType, CERTTYPE_PROP_FRIENDLY_NAME, &awszFriendlyName)) goto Ret;
if(NULL==awszFriendlyName) goto Ret;
if(NULL==(awszFriendlyName[0])) goto Ret;
//retrieve the RA certificate's template name
if(IDS_REPORT_ENROLL_RA == idsMessage) { if(!AEUIGetNameFromCert(pCertType->pOldCert, &pwszRACert)) { pwszRACert=NULL; } }
if(!FormatMessageUnicode(&pwszReport, idsMessage, awszFriendlyName[0])) goto Ret;
if(0 == SetDlgItemTextW(hwndDlg, IDC_EDIT2, pwszReport)) goto Ret;
//we will give user an opportunity to view the RA certificate before we go on
//format the view message
if(IDS_REPORT_ENROLL_RA != idsMessage) { //no need to do anything more
fResult=TRUE; goto Ret; }
AEViewRAInfo.pRAContext=pCertType->pOldCert; AEViewRAInfo.pwszRATemplate=pwszRACert;
//ask user if he/she wants to view the RA certificate
ret=DialogBoxParam(g_hmodThisDll, (LPCWSTR)MAKEINTRESOURCE(IDD_VIEW_RA_CERTIFICATE_DLG), hwndDlg, AEViewRADlgProc, (LPARAM)(&AEViewRAInfo));
fResult=TRUE;
Ret:
if(pwszRACert) LocalFree(pwszRACert);
if(awszFriendlyName) CAFreeCertTypeProperty(pCertType->hCertType, awszFriendlyName);
if(pwszReport) LocalFree((HLOCAL) pwszReport); return fResult; }
//-----------------------------------------------------------------------------
//
// the call back function to compare summary column
//
//-----------------------------------------------------------------------------
int CALLBACK CompareSummary(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { AE_CERTTYPE_INFO *pCertTypeOne=NULL; AE_CERTTYPE_INFO *pCertTypeTwo=NULL; DWORD dwColumn=0; int iCompare=0; LPWSTR pwszOne=NULL; LPWSTR pwszTwo=NULL;
pCertTypeOne=(AE_CERTTYPE_INFO *)lParam1; pCertTypeTwo=(AE_CERTTYPE_INFO *)lParam2;
dwColumn=(DWORD)lParamSort;
if((NULL==pCertTypeOne) || (NULL==pCertTypeTwo)) goto Ret;
switch(dwColumn & 0x0000FFFF) { case AE_SUMMARY_COLUMN_TYPE: //we should use wcsicoll instead of wcsicmp since wcsicoll use the
//lexicographic order of current code page.
iCompare=CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pCertTypeOne->awszDisplay[0], -1, pCertTypeTwo->awszDisplay[0], -1); break;
case AE_SUMMARY_COLUMN_REASON: pwszOne=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (MAX_DN_SIZE)); pwszTwo=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (MAX_DN_SIZE));
if((NULL==pwszOne) || (NULL==pwszTwo)) goto Ret;
if(0 == LoadStringW(g_hmodThisDll, pCertTypeOne->idsSummary, pwszOne, MAX_DN_SIZE)) goto Ret;
if(0 == LoadStringW(g_hmodThisDll, pCertTypeTwo->idsSummary, pwszTwo, MAX_DN_SIZE)) goto Ret;
//we should use wcsicoll instead of wcsicmp since wcsicoll use the
//lexicographic order of current code page.
iCompare=CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pwszOne, -1, pwszTwo, -1); break; default: goto Ret; break; }
switch(iCompare) { case CSTR_LESS_THAN:
iCompare=-1; break; case CSTR_EQUAL:
iCompare=0; break;
case CSTR_GREATER_THAN:
iCompare=1; break;
default: goto Ret; break; }
if(dwColumn & SORT_COLUMN_DESCEND) iCompare = 0-iCompare;
Ret:
if(pwszOne) LocalFree(pwszOne);
if(pwszTwo) LocalFree(pwszTwo);
return iCompare; }
//-----------------------------------------------------------------------------
//
// AEDisplaySummaryInfo
//
//-----------------------------------------------------------------------------
BOOL AEDisplaySummaryInfo(HWND hWndListView, AE_GENERAL_INFO *pAE_General_Info) { BOOL fResult=FALSE; AE_CERTTYPE_INFO *rgCertTypeInfo = NULL; DWORD dwIndex =0; DWORD dwItem=0; LV_ITEMW lvItem; WCHAR wszReason[MAX_DN_SIZE]; AE_CERTTYPE_INFO *pCertType=NULL;
if((NULL==hWndListView) || (NULL==pAE_General_Info)) goto Ret;
rgCertTypeInfo = pAE_General_Info->rgCertTypeInfo;
if(NULL == rgCertTypeInfo) goto Ret;
// set up the fields in the list view item struct that don't change from item to item
lvItem.mask = LVIF_TEXT | LVIF_PARAM; lvItem.state = 0; lvItem.stateMask = 0; lvItem.iItem=0; lvItem.iSubItem=0; lvItem.iImage = 0; lvItem.lParam = NULL;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if((TRUE == rgCertTypeInfo[dwIndex].fUIActive) && (0 != rgCertTypeInfo[dwIndex].idsSummary)) { if(0 != LoadStringW(g_hmodThisDll, rgCertTypeInfo[dwIndex].idsSummary, wszReason, MAX_DN_SIZE)) { lvItem.iItem=dwItem; lvItem.iSubItem=0; dwItem++;
pCertType=&(rgCertTypeInfo[dwIndex]);
lvItem.lParam = (LPARAM)(pCertType);
//template name
lvItem.pszText=rgCertTypeInfo[dwIndex].awszDisplay[0];
ListView_InsertItem(hWndListView, &lvItem);
//reason
lvItem.iSubItem++;
ListView_SetItemText(hWndListView, lvItem.iItem, lvItem.iSubItem, wszReason); } } }
fResult=TRUE;
Ret:
return fResult; }
//-----------------------------------------------------------------------------
//
// WinProc for the summary page
//
//-----------------------------------------------------------------------------
INT_PTR CALLBACK AESummaryDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
AE_GENERAL_INFO *pAE_General_Info=NULL; HWND hWndListView=NULL; UINT rgIDS[]={IDS_COLUMN_TYPE, IDS_COLUMN_REASON}; DWORD dwIndex=0; DWORD dwCount=0; LV_COLUMNW lvC; WCHAR wszText[AE_SUMMARY_COLUMN_SIZE]; NM_LISTVIEW *pnmv=NULL; DWORD dwSortParam=0; static DWORD rgdwSortParam[]= {AE_SUMMARY_COLUMN_TYPE | SORT_COLUMN_ASCEND, AE_SUMMARY_COLUMN_REASON | SORT_COLUMN_DESCEND};
switch (msg) { case WM_INITDIALOG: pAE_General_Info=(AE_GENERAL_INFO *)lParam;
if(NULL==pAE_General_Info) break;
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pAE_General_Info);
//init the list view control
//add the colums to the list view
hWndListView = GetDlgItem(hwndDlg, IDC_LIST2);
if(NULL==hWndListView) break;
dwCount=sizeof(rgIDS)/sizeof(rgIDS[0]);
//set up the common info for the column
memset(&lvC, 0, sizeof(LV_COLUMNW));
lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvC.fmt = LVCFMT_LEFT; // Left-align the column.
lvC.cx = 150; // Width of the column, in pixels.
lvC.iSubItem=0; lvC.pszText = wszText; // The text for the column.
//insert the column one at a time
for(dwIndex=0; dwIndex < dwCount; dwIndex++) { //get the column header
wszText[0]=L'\0';
if(0 != LoadStringW(g_hmodThisDll, rgIDS[dwIndex], wszText, AE_SUMMARY_COLUMN_SIZE)) { ListView_InsertColumn(hWndListView, dwIndex, &lvC); } }
// set the style in the list view so that it highlights an entire line
SendMessage(hWndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
AEDisplaySummaryInfo(hWndListView, pAE_General_Info);
//autosize the columns
for(dwIndex=0; dwIndex < dwCount; dwIndex++) { ListView_SetColumnWidth(hWndListView, dwIndex, LVSCW_AUTOSIZE); }
//sort 1st column of the list view
dwSortParam=rgdwSortParam[0];
SendDlgItemMessage(hwndDlg, IDC_LIST2, LVM_SORTITEMS, (WPARAM) (LPARAM) dwSortParam, (LPARAM) (PFNLVCOMPARE)CompareSummary);
return TRUE; break;
case WM_NOTIFY: switch (((NMHDR FAR *) lParam)->code) { //the column has been changed
case LVN_COLUMNCLICK:
pnmv = (NM_LISTVIEW *) lParam;
dwSortParam=0;
//get the column number
switch(pnmv->iSubItem) { case 0: case 1: dwSortParam=rgdwSortParam[pnmv->iSubItem]; break; default: dwSortParam=0; break; }
if(0!=dwSortParam) { //remember to flip the ascend ording
if(dwSortParam & SORT_COLUMN_ASCEND) { dwSortParam &= 0x0000FFFF; dwSortParam |= SORT_COLUMN_DESCEND; } else { if(dwSortParam & SORT_COLUMN_DESCEND) { dwSortParam &= 0x0000FFFF; dwSortParam |= SORT_COLUMN_ASCEND; } }
//sort the column
SendDlgItemMessage(hwndDlg, IDC_LIST2, LVM_SORTITEMS, (WPARAM) (LPARAM) dwSortParam, (LPARAM) (PFNLVCOMPARE)CompareSummary);
rgdwSortParam[pnmv->iSubItem]=dwSortParam; }
break; } break;
case WM_CLOSE: EndDialog(hwndDlg, IDC_BUTTON1); return TRUE; break;
case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_BUTTON1: EndDialog(hwndDlg, IDC_BUTTON1); return TRUE; } break;
default: return FALSE; }
return FALSE; }
//-----------------------------------------------------------------------------
//
// AEDisplaySummaryPage
//
//-----------------------------------------------------------------------------
BOOL AEDisplaySummaryPage(AE_GENERAL_INFO *pAE_General_Info) { BOOL fResult=FALSE; DWORD dwIndex=0; BOOL fSummary=FALSE; AE_CERTTYPE_INFO *rgCertTypeInfo=NULL; AE_CERTTYPE_INFO *pCertType=NULL;
//decide if there is need to show the summary page.
//Checking for idsSummary for each template
if(NULL == pAE_General_Info) goto Ret;
if(NULL == (rgCertTypeInfo=pAE_General_Info->rgCertTypeInfo)) goto Ret;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if((TRUE == rgCertTypeInfo[dwIndex].fUIActive) && (0 != rgCertTypeInfo[dwIndex].idsSummary)) { fSummary=TRUE; break; } }
//show the summary dialogue
if(TRUE == fSummary) { if(pAE_General_Info->hwndDlg) { DialogBoxParam(g_hmodThisDll, (LPCWSTR)MAKEINTRESOURCE(IDD_USER_SUMMARY_DLG), pAE_General_Info->hwndDlg, AESummaryDlgProc, (LPARAM)(pAE_General_Info)); } }
fResult=TRUE;
Ret: return fResult; }
//-----------------------------------------------------------------------------
// WinProc for the autoenrollment progress window
//
//-----------------------------------------------------------------------------
INT_PTR CALLBACK progressDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { AE_GENERAL_INFO *pAE_General_Info = NULL;
switch (msg) { case WM_INITDIALOG: pAE_General_Info=(AE_GENERAL_INFO *)lParam;
//copy the hwndDlg to the enrollment thread
pAE_General_Info->hwndDlg=hwndDlg;
//start the interacive enrollment thread
if(1 != ResumeThread(pAE_General_Info->hThread)) { pAE_General_Info->hwndDlg=NULL;
//we have to end the dialogue
EndDialog(hwndDlg, IDC_BUTTON1); return TRUE; }
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pAE_General_Info);
return TRUE; break;
case WM_NOTIFY: break;
case WM_CLOSE:
if(NULL==(pAE_General_Info=(AE_GENERAL_INFO *)GetWindowLongPtr(hwndDlg, DWLP_USER))) break;
//disable the cancel button
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1), FALSE);
//signal the cancel event
if(pAE_General_Info->hCancelEvent) SetEvent(pAE_General_Info->hCancelEvent);
//close the dialogue if the enrollment work is completed
if(WAIT_OBJECT_0 == WaitForSingleObject(pAE_General_Info->hCompleteEvent, 0)) { EndDialog(hwndDlg, IDC_BUTTON1); } return TRUE;
break;
case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_BUTTON1: if(NULL==(pAE_General_Info=(AE_GENERAL_INFO *)GetWindowLongPtr(hwndDlg, DWLP_USER))) break;
//disable the cancel button
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1), FALSE);
//signal the cancel event
if(pAE_General_Info->hCancelEvent) SetEvent(pAE_General_Info->hCancelEvent);
return TRUE; } break;
default: return FALSE; }
return FALSE; }
//-----------------------------------------------------------------------------
// AEInteractiveThreadProc
//
// The thread procedue to do interactive enrollment
//-----------------------------------------------------------------------------
DWORD WINAPI AEInteractiveThreadProc(LPVOID lpParameter) { BOOL fResult=FALSE; AE_GENERAL_INFO *pAE_General_Info = NULL;
if(NULL==lpParameter) return FALSE;
__try {
pAE_General_Info=(AE_GENERAL_INFO *)lpParameter;
pAE_General_Info->fUIProcess=TRUE; fResult = AEEnrollmentWalker(pAE_General_Info);
//show the summary page if not canceled
if(!AECancelled(pAE_General_Info->hCancelEvent)) { AEDisplaySummaryPage(pAE_General_Info); }
//signal that the process is completed
SetEvent(pAE_General_Info->hCompleteEvent); //signal the progress window that we are done
if(pAE_General_Info->hwndDlg) { //click the close button
SendMessage(pAE_General_Info->hwndDlg, WM_CLOSE, //WM_COMMAND,
0, //IDC_BUTTON1,
NULL); } } __except ( EXCEPTION_EXECUTE_HANDLER ) { } return fResult; }
//-----------------------------------------------------------------------------
// AEInteractiveEnrollment
//
// We are doing interactive enrollment
//-----------------------------------------------------------------------------
BOOL AEInteractiveEnrollment(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwThreadID=0; BOOL fResult=FALSE;
//create a notification event for cancel process
pAE_General_Info->hCancelEvent=CreateEvent( NULL, TRUE, // bmanual reset type
FALSE, // initial state
NULL);
if(NULL==(pAE_General_Info->hCancelEvent)) goto ret;
//create a notification event for complete process
pAE_General_Info->hCompleteEvent=CreateEvent( NULL, TRUE, // bmanual reset type
FALSE, // initial state
NULL);
if(NULL==(pAE_General_Info->hCompleteEvent)) goto ret;
//spawn a thread
pAE_General_Info->hThread = CreateThread(NULL, 0, AEInteractiveThreadProc, pAE_General_Info, CREATE_SUSPENDED, //suspend execution
&dwThreadID); if(NULL==(pAE_General_Info->hThread)) goto ret;
//create the dialogue
DialogBoxParam( g_hmodThisDll, MAKEINTRESOURCE(IDD_USER_AUTOENROLL_GENERAL_DLG), pAE_General_Info->hwndParent, progressDlgProc, (LPARAM)(pAE_General_Info));
//wait for thread to finish
if(WAIT_FAILED == WaitForSingleObject(pAE_General_Info->hThread, INFINITE)) goto ret;
fResult=TRUE;
ret:
//log the event
if(!fResult) { AELogAutoEnrollmentEvent( pAE_General_Info->dwLogLevel, TRUE, HRESULT_FROM_WIN32(GetLastError()), EVENT_FAIL_INTERACTIVE_START, pAE_General_Info->fMachine, pAE_General_Info->hToken, 0); }
return fResult; }
//-----------------------------------------------------------------------------
//
// WinProc for the confirmation to start certificate autoenrollment
//
//-----------------------------------------------------------------------------
INT_PTR CALLBACK AEConfirmDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG:
return TRUE; break;
case WM_NOTIFY: break;
case WM_CLOSE: EndDialog(hwndDlg, IDC_BUTTON2); return TRUE; break;
case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_BUTTON1: EndDialog(hwndDlg, IDC_BUTTON1); return TRUE;
case IDC_BUTTON2: EndDialog(hwndDlg, IDC_BUTTON2); return TRUE; } break;
default: return FALSE; }
return FALSE; }
//-----------------------------------------------------------------------
//
// AERegisterSysTrayApp
//
// This functin registers autoenrollment in the sys tray area
// as an notification
//
//
//-----------------------------------------------------------------------
BOOL AERegisterSysTrayApp(HWND hwndParent) { BOOL fResult=FALSE; BOOL fInit=FALSE; INT_PTR ret=0; DWORD dwError=0;
CQueryContinue *pCQueryContinue=NULL;
if(FAILED(CoInitialize(NULL))) goto Ret;
fInit=TRUE;
pCQueryContinue=new CQueryContinue();
if(NULL==pCQueryContinue) goto Ret;
if(S_OK != pCQueryContinue->DoBalloon()) goto Ret;
//ask user if autoenrollment should be performed
ret=DialogBox(g_hmodThisDll, (LPCWSTR)MAKEINTRESOURCE(IDD_USER_AUTOENROLL_INFO_DLG), hwndParent, AEConfirmDlgProc);
if(IDC_BUTTON1 != ret) { dwError=GetLastError(); goto Ret; } fResult=TRUE;
Ret:
if(pCQueryContinue) { delete pCQueryContinue; }
if(fInit) CoUninitialize();
return fResult;
}
//-----------------------------------------------------------------------
//
// AEUIDisabled
//
// Detect if the user notification balloon is disabled by user
// setting the autoenrollment registry key in current user
//
//
//-----------------------------------------------------------------------
BOOL AEUIDisabled() { BOOL fResult=FALSE; HKEY hKey=NULL;
if(ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, // handle to open key
AUTO_ENROLLMENT_DISABLE_KEY, // subkey name
0, // reserved
KEY_READ, // security access mask
&hKey)) // handle to open key
{ fResult=TRUE; }
if(hKey) RegCloseKey(hKey);
return fResult; }
//-----------------------------------------------------------------------
//
// AEUIRequired
//
// Detect if the user notification balloon is needed
//
//
//-----------------------------------------------------------------------
BOOL AEUIRequired(AE_GENERAL_INFO *pAE_General_Info) { BOOL fUI=FALSE; AE_CERTTYPE_INFO *rgCertTypeInfo = NULL; DWORD dwIndex = 0;
if(NULL==pAE_General_Info) return FALSE;
rgCertTypeInfo = pAE_General_Info->rgCertTypeInfo;
pAE_General_Info->dwUIEnrollCount=0;
if(NULL == rgCertTypeInfo) return FALSE;
for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { if(rgCertTypeInfo[dwIndex].fUIActive) { if(CERT_REQUEST_STATUS_ACTIVE == rgCertTypeInfo[dwIndex].dwStatus) { fUI=TRUE; (pAE_General_Info->dwUIEnrollCount)++; } } }
//add the pending count
if(pAE_General_Info->dwUIPendCount) { fUI=TRUE; (pAE_General_Info->dwUIEnrollCount) +=(pAE_General_Info->dwUIPendCount); }
return fUI; }
//-----------------------------------------------------------------------
//
// AEProcessEnrollment
//
// This functin does the autoenrollment based on ACL and manage MY
// store.
//
//
//-----------------------------------------------------------------------
BOOL AEProcessEnrollment(HWND hwndParent, BOOL fMachine, LDAP *pld, DWORD dwPolicy, DWORD dwLogLevel) { BOOL fResult=FALSE;
AE_GENERAL_INFO *pAE_General_Info=NULL;
pAE_General_Info=(AE_GENERAL_INFO *)LocalAlloc(LPTR, sizeof(AE_GENERAL_INFO));
if(NULL==pAE_General_Info) goto Ret;
memset(pAE_General_Info, 0, sizeof(AE_GENERAL_INFO));
if(NULL==pld) goto Ret;
//we obtain all information needed for process enrollment
pAE_General_Info->hwndParent = hwndParent; pAE_General_Info->pld = pld; pAE_General_Info->fMachine = fMachine; pAE_General_Info->dwPolicy = dwPolicy; pAE_General_Info->dwLogLevel = dwLogLevel;
__try {
if(!AERetrieveGeneralInfo(pAE_General_Info)) { AELogAutoEnrollmentEvent(dwLogLevel, TRUE, HRESULT_FROM_WIN32(GetLastError()), EVENT_FAIL_GENERAL_INFOMATION, fMachine, pAE_General_Info->hToken, 0); goto Ret; }
if((0 == pAE_General_Info->dwCertType) || (NULL==pAE_General_Info->rgCertTypeInfo)) { AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_NO_CERT_TEMPLATE, fMachine, pAE_General_Info->hToken,0);
AE_DEBUG((AE_WARNING, L"No CertType's available for auto-enrollment\n\r")); goto Ret; }
//we build the auto-enrollment requests based on the ACL on the DS
if(AUTO_ENROLLMENT_ENABLE_TEMPLATE_CHECK & (pAE_General_Info->dwPolicy)) { if(!AEMarkAutoenrollment(pAE_General_Info)) goto Ret; }
//we build the auto-enrollment requests based on the ARCS store
//this is enabled by default and can only be disabled if autoenrollment is
//completely disabled
if(!AEMarkAEObject(pAE_General_Info)) goto Ret;
//manage MY store. Check if we already have required certificates
//we should always check my store with different behavior based on
//AUTO_ENROLLMENT_ENABLE_MY_STORE_MANAGEMENT flag
if(!AEManageAndMarkMyStore(pAE_General_Info)) goto Ret;
//manage UserDS store for user autoenrollment
if(!fMachine) { if(!AECheckUserDSStore(pAE_General_Info)) goto Ret; }
//manage pending request store. Remove expired pending requests
if(AUTO_ENROLLMENT_ENABLE_PENDING_FETCH & (pAE_General_Info->dwPolicy)) { if(!AECheckPendingRequests(pAE_General_Info)) goto Ret; }
//get CA information
if(!AERetrieveCAInfo(pAE_General_Info->pld, pAE_General_Info->fMachine, pAE_General_Info->hToken, &(pAE_General_Info->dwCA), &(pAE_General_Info->rgCAInfo))) {
AELogAutoEnrollmentEvent(dwLogLevel, TRUE, HRESULT_FROM_WIN32(GetLastError()), EVENT_FAIL_CA_INFORMATION, fMachine, pAE_General_Info->hToken, 0);
AE_DEBUG((AE_ERROR, L"Unable to retrieve CA information (%lx)\n\r", GetLastError()));
goto Ret; }
if((0 == pAE_General_Info->dwCA) || (NULL==pAE_General_Info->rgCAInfo)) { //we do not have any CAs on the domain. All we need to do is to archive
//archive old certificate after the enrollment/renewal
AEArchiveObsoleteCertificates(pAE_General_Info);
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_NO_CA, fMachine, pAE_General_Info->hToken, 0);
AE_DEBUG((AE_WARNING, L"No CA's available for auto-enrollment\n\r"));
goto Ret; }
//we check if active templates do have a CA that we can enroll for
if(!AEManageActiveTemplates(pAE_General_Info)) goto Ret;
//perform autoenrollment as the background
pAE_General_Info->fUIProcess=FALSE; if(!AEEnrollmentWalker(pAE_General_Info)) goto Ret;
//perform autoenrollment as a sys tray application for user only
if(FALSE == fMachine) { //test if the notification balloon is disabled
if(!AEUIDisabled()) { //test if the notification balloon is needed
if(AEUIRequired(pAE_General_Info)) { //register the sys tray application
if(AERegisterSysTrayApp(pAE_General_Info->hwndParent)) { //perform autoenrollment in interactive mode
AEInteractiveEnrollment(pAE_General_Info); } } } }
//archive old certificate after the enrollment/renewal
if(!AEArchiveObsoleteCertificates(pAE_General_Info)) goto Ret;
} __except ( EXCEPTION_EXECUTE_HANDLER ) { goto Ret; }
fResult=TRUE;
Ret:
//free memory only if no thread is created
if(pAE_General_Info) { AEFreeGeneralInfo(pAE_General_Info); LocalFree(pAE_General_Info); }
return fResult; }
//-----------------------------------------------------------------------
//
// AEExpress
//
// Detect if the user autoenrollment has the express key set. If the
// Express key is set, user autoenrollment will not wait for machine
// autoenrollment to complete on root certificates download
//
//
//-----------------------------------------------------------------------
BOOL AEExpress() { BOOL fResult=FALSE; HKEY hKey=NULL;
if(ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, // handle to open key
AUTO_ENROLLMENT_EXPRESS_KEY, // subkey name
0, // reserved
KEY_READ, // security access mask
&hKey)) // handle to open key
{ fResult=TRUE; }
if(hKey) RegCloseKey(hKey);
return fResult; }
//-----------------------------------------------------------------------
//
// AEMainThreadProc
//
// The background thread for non-blocking autoenrollment background
// processing.
//
//-----------------------------------------------------------------------
DWORD WINAPI AEMainThreadProc(LPVOID lpParameter) { HRESULT hr=S_OK; BOOL fMachine=FALSE; DWORD dwPolicy=0; DWORD dwLogLevel=STATUS_SEVERITY_ERROR; HWND hwndParent=0; DWORD dwStatus=0; LARGE_INTEGER ftPreTimeStamp; LARGE_INTEGER ftPostTimeStamp; BOOL fNeedToSetupTimer=FALSE;
LDAP *pld = NULL; LPWSTR pwszDCName=NULL;
//get the system time stamp
GetSystemTimeAsFileTime((LPFILETIME)&ftPreTimeStamp);
//the two input parameters are not yet used
if(NULL==lpParameter) goto CommonReturn;
hwndParent = ((AE_MAIN_THREAD_INFO *)lpParameter)->hwndParent; dwStatus = ((AE_MAIN_THREAD_INFO *)lpParameter)->dwStatus;
AE_DEBUG((AE_INFO, L"Beginning CertAutoEnrollment(%s).\n", (CERT_AUTO_ENROLLMENT_START_UP==dwStatus?L"START_UP":L"WAKE_UP")));
//no autoenrollment in the safe boot mode
//no autoenrollment if we are not in a domain
if(AEInSafeBoot() || !AEIsDomainMember()) goto CommonReturn;
//we need to set up the timer
fNeedToSetupTimer=TRUE;
//detect if we are running under user or machine context
if(!AEIsLocalSystem(&fMachine)) goto CommonReturn; AE_DEBUG((AE_INFO, L"CertAutoEnrollment running as %s.\n", (fMachine?L"machine":L"user")));
AESetWakeUpFlag(fMachine, TRUE); //we wait for 70 seconds for user case to give enough time for
//machine autoenrollment to complete, which will download certificates
//from the directory
if(!fMachine) { if(!AEExpress()) { Sleep(USER_AUTOENROLL_DELAY_FOR_MACHINE * 1000); } }
//get the autoenrollment log level
if(!AERetrieveLogLevel(fMachine, &dwLogLevel)) goto CommonReturn;
//log the autoenrollment start event
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_AUTOENROLL_START, fMachine, NULL, 0);
//get the autoenrollment policy flag
if(!AEGetPolicyFlag(fMachine, &dwPolicy)) goto CommonReturn;
//no need to do anything if autoenrollment is completely disabled
if(AUTO_ENROLLMENT_DISABLE_ALL & dwPolicy) goto CommonReturn;
//download NTAuth And Enterprise root store for machine
if(fMachine) { //bind to the DS
if(S_OK != (hr=AERobustLdapBind(&pld, &pwszDCName))) { SetLastError(hr); AELogAutoEnrollmentEvent(dwLogLevel, TRUE, hr, EVENT_FAIL_BIND_TO_DS, fMachine, NULL, 0); goto CommonReturn; }
AEDownloadStore(pld, pwszDCName); }
//if we are required to do a WIN2K style autoenrollment, and the machine/user's
//ACRS store is empty, just return as we done.
if(0 == dwPolicy) { if(IsACRSStoreEmpty(fMachine)) goto CommonReturn; }
if(NULL==pld) { //bind to the DS
if(S_OK != (hr=AERobustLdapBind(&pld, NULL))) { SetLastError(hr); AELogAutoEnrollmentEvent(dwLogLevel, TRUE, hr, EVENT_FAIL_BIND_TO_DS, fMachine, NULL, 0); goto CommonReturn; } }
AEProcessEnrollment(hwndParent, fMachine, pld, dwPolicy, dwLogLevel);
CommonReturn:
//get the system time
GetSystemTimeAsFileTime((LPFILETIME)&ftPostTimeStamp);
//set up the timer for next time
if(TRUE == fNeedToSetupTimer) { // we will need to do this again in a few hours.
AESetWakeUpTimer(fMachine, &ftPreTimeStamp, &ftPostTimeStamp); }
if(pld) ldap_unbind(pld);
if(pwszDCName) LocalFree(pwszDCName);
if(lpParameter) LocalFree((HLOCAL)lpParameter);
AELogAutoEnrollmentEvent(dwLogLevel, FALSE, S_OK, EVENT_AUTOENROLL_COMPLETE, fMachine, NULL, 0);
return TRUE; }
//--------------------------------------------------------------------------
//
// CertAutoEnrollment
//
// Function to perform autoenrollment actions. It creates a working
// thread and return immediately so that it is non-blocking.
//
// Parameters:
// IN hwndParent: The parent window
// IN dwStatus: The status under which the function is called.
// It can be one of the following:
// CERT_AUTO_ENROLLMENT_START_UP
// CERT_AUTO_ENROLLMENT_WAKE_UP
//
//--------------------------------------------------------------------------
HANDLE WINAPI CertAutoEnrollment(IN HWND hwndParent, IN DWORD dwStatus) { DWORD dwThreadID=0; //memory will be freed in the main thread
AE_MAIN_THREAD_INFO *pAE_Main_Thread_Info=NULL; HANDLE hThread=NULL;
pAE_Main_Thread_Info=(AE_MAIN_THREAD_INFO *)LocalAlloc(LPTR, sizeof(AE_MAIN_THREAD_INFO)); if(NULL==pAE_Main_Thread_Info) return NULL;
memset(pAE_Main_Thread_Info, 0, sizeof(AE_MAIN_THREAD_INFO)); pAE_Main_Thread_Info->hwndParent=hwndParent; pAE_Main_Thread_Info->dwStatus=dwStatus;
hThread = CreateThread(NULL, 0, AEMainThreadProc, pAE_Main_Thread_Info, 0, //execute immediately
&dwThreadID);
//set the thread priority to low so that we will not compete with the shell
SetThreadPriority(hThread, THREAD_PRIORITY_BELOW_NORMAL);
return hThread; }
//--------------------------------------------------------------------
//
// AERetrieveClientToken
//
//--------------------------------------------------------------------
BOOL AERetrieveClientToken(HANDLE *phToken) { HRESULT hr = S_OK;
HANDLE hHandle = NULL; HANDLE hClientToken = NULL;
hHandle = GetCurrentThread(); if (NULL == hHandle) { hr = HRESULT_FROM_WIN32(GetLastError()); } else {
if (!OpenThreadToken(hHandle, TOKEN_QUERY, TRUE, // open as self
&hClientToken)) { hr = HRESULT_FROM_WIN32(GetLastError()); CloseHandle(hHandle); hHandle = NULL; } }
if(hr != S_OK) { hHandle = GetCurrentProcess(); if (NULL == hHandle) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { HANDLE hProcessToken = NULL; hr = S_OK;
if (!OpenProcessToken(hHandle, TOKEN_DUPLICATE, &hProcessToken)) { hr = HRESULT_FROM_WIN32(GetLastError()); CloseHandle(hHandle); hHandle = NULL; } else { if(!DuplicateToken(hProcessToken, SecurityImpersonation, &hClientToken)) { hr = HRESULT_FROM_WIN32(GetLastError()); CloseHandle(hHandle); hHandle = NULL; } CloseHandle(hProcessToken); } } }
if(S_OK == hr) *phToken = hClientToken;
if(hHandle) CloseHandle(hHandle);
return (S_OK == hr); }
//--------------------------------------------------------------------------
//
// AERetrieveGeneralInfo
//
//
//--------------------------------------------------------------------------
BOOL AERetrieveGeneralInfo(AE_GENERAL_INFO *pAE_General_Info) { BOOL fResult = FALSE; DWORD dwOpenStoreFlags = CERT_SYSTEM_STORE_CURRENT_USER; DWORD cMachineName = MAX_COMPUTERNAME_LENGTH + 2; LONG dwResult = 0;
SCARDCONTEXT hSCContext=NULL;
//get the client token
if(pAE_General_Info->fMachine) { if(!AENetLogonUser(NULL, NULL, NULL, &(pAE_General_Info->hToken))) { AE_DEBUG((AE_ERROR, L"Obtain local system's token (%lx)\n\r", GetLastError())); goto Ret; } } else { if(!AERetrieveClientToken(&(pAE_General_Info->hToken))) goto Ret; }
//get the machine name
if (!GetComputerNameW(pAE_General_Info->wszMachineName, &cMachineName)) goto Ret;
if(pAE_General_Info->fMachine) dwOpenStoreFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
//open my store
if (NULL == (pAE_General_Info->hMyStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, ENCODING_TYPE, NULL, dwOpenStoreFlags, MY_STORE))) { AE_DEBUG((AE_ERROR, L"Unable to open MY store (%lx)\n\r", GetLastError())); goto Ret; }
if(!CertControlStore(pAE_General_Info->hMyStore, 0, CERT_STORE_CTRL_AUTO_RESYNC, NULL)) { AE_DEBUG((AE_ERROR, L"Unable configure MY store for auto-resync(%lx)\n\r", GetLastError())); goto Ret; }
//open request store
if (NULL == (pAE_General_Info->hRequestStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, ENCODING_TYPE, NULL, dwOpenStoreFlags, REQUEST_STORE))) { AE_DEBUG((AE_ERROR, L"Unable to open Request store (%lx)\n\r", GetLastError())); goto Ret; }
//get CertType information
if(!AERetrieveCertTypeInfo( pAE_General_Info->pld, pAE_General_Info->fMachine, &(pAE_General_Info->dwCertType), &(pAE_General_Info->rgCertTypeInfo))) { AE_DEBUG((AE_ERROR, L"Unable to retrieve CertType information (%lx)\n\r", GetLastError())); goto Ret; }
//load xenroll module. No need to check errors since this is not a fatal error
pAE_General_Info->hXenroll = LoadLibrary(L"xenroll.dll");
//detect if the smart card subsystem if running for users only
if(FALSE == pAE_General_Info->fMachine) { dwResult = SCardEstablishContext( SCARD_SCOPE_USER, NULL, NULL, &hSCContext );
if((0 == dwResult) && (NULL != hSCContext)) pAE_General_Info->fSmartcardSystem=TRUE; }
fResult = TRUE;
Ret:
if(hSCContext) SCardReleaseContext(hSCContext);
if(FALSE == fResult) AEFreeGeneralInfo(pAE_General_Info);
return fResult; }
//--------------------------------------------------------------------------
//
// AEFreeGeneralInfo
//
//
//--------------------------------------------------------------------------
BOOL AEFreeGeneralInfo(AE_GENERAL_INFO *pAE_General_Info) { if(pAE_General_Info) { if(pAE_General_Info->hToken) CloseHandle(pAE_General_Info->hToken);
if(pAE_General_Info->hMyStore) CertCloseStore(pAE_General_Info->hMyStore, 0);
if(pAE_General_Info->hRequestStore) CertCloseStore(pAE_General_Info->hRequestStore, 0);
//free CA information
AEFreeCAInfo(pAE_General_Info->dwCA, pAE_General_Info->rgCAInfo);
//free CertType information
AEFreeCertTypeInfo(pAE_General_Info->dwCertType, pAE_General_Info->rgCertTypeInfo);
if(pAE_General_Info->hXenroll) FreeLibrary(pAE_General_Info->hXenroll);
if(pAE_General_Info->hCancelEvent) CloseHandle(pAE_General_Info->hCancelEvent);
if(pAE_General_Info->hCompleteEvent) CloseHandle(pAE_General_Info->hCompleteEvent);
if(pAE_General_Info->hThread) CloseHandle(pAE_General_Info->hThread);
memset(pAE_General_Info, 0, sizeof(AE_GENERAL_INFO));
}
return TRUE; }
//--------------------------------------------------------------------------
//
// AERetrieveCertTypeInfo
//
//--------------------------------------------------------------------------
BOOL AERetrieveCertTypeInfo(LDAP *pld, BOOL fMachine, DWORD *pdwCertType, AE_CERTTYPE_INFO **prgCertType) { BOOL fResult=FALSE; DWORD dwCount=0; DWORD dwCertType=0; DWORD dwIndex=0; HRESULT hr=E_FAIL;
HCERTTYPE hCTCurrent = NULL; HCERTTYPE hCTNew = NULL; AE_CERTTYPE_INFO *rgCertTypeInfo=NULL;
*pdwCertType=0; *prgCertType=NULL;
if(S_OK != (hr = CAEnumCertTypesEx( (LPCWSTR)pld, fMachine?CT_ENUM_MACHINE_TYPES | CT_FIND_LOCAL_SYSTEM | CT_FLAG_SCOPE_IS_LDAP_HANDLE: CT_ENUM_USER_TYPES | CT_FLAG_SCOPE_IS_LDAP_HANDLE, &hCTCurrent))) { SetLastError(hr); goto Ret; }
if((NULL == hCTCurrent) || (0 == (dwCount = CACountCertTypes(hCTCurrent)))) { AE_DEBUG((AE_WARNING, L"No CT's available for auto-enrollment\n\r")); fResult=TRUE; goto Ret; }
rgCertTypeInfo=(AE_CERTTYPE_INFO *)LocalAlloc(LPTR, sizeof(AE_CERTTYPE_INFO) * dwCount); if(NULL==rgCertTypeInfo) { SetLastError(E_OUTOFMEMORY); goto Ret; }
memset(rgCertTypeInfo, 0, sizeof(AE_CERTTYPE_INFO) * dwCount);
for(dwIndex = 0; dwIndex < dwCount; dwIndex++ ) {
//check if we have a new certificate template
if(dwIndex > 0) { hr = CAEnumNextCertType(hCTCurrent, &hCTNew);
if((S_OK != hr) || (NULL == hCTNew)) { // Clean up from previous calls
if(dwCertType < dwCount) AEFreeCertTypeStruct(&(rgCertTypeInfo[dwCertType]));
break; }
hCTCurrent = hCTNew; }
// Clean up from previous calls
AEFreeCertTypeStruct(&(rgCertTypeInfo[dwCertType]));
//copy the new CertType' data
//hCertType
rgCertTypeInfo[dwCertType].hCertType = hCTCurrent;
//CTName
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_DN, &(rgCertTypeInfo[dwCertType].awszName));
if((S_OK != hr) || (NULL == rgCertTypeInfo[dwCertType].awszName) || (NULL == (rgCertTypeInfo[dwCertType].awszName)[0]) ) { AE_DEBUG((AE_INFO, L"No name property for CertType\n\r")); continue; } //FriendlyName
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_FRIENDLY_NAME, &(rgCertTypeInfo[dwCertType].awszDisplay)); if((S_OK != hr) || (NULL == rgCertTypeInfo[dwCertType].awszDisplay) || (NULL == (rgCertTypeInfo[dwCertType].awszDisplay)[0]) ) { AE_DEBUG((AE_INFO, L"No display property for CertType\n\r"));
//get the DN as the display name
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_DN, &(rgCertTypeInfo[dwCertType].awszDisplay)); if((S_OK != hr) || (NULL == rgCertTypeInfo[dwCertType].awszDisplay) || (NULL == (rgCertTypeInfo[dwCertType].awszDisplay)[0]) ) { AE_DEBUG((AE_INFO, L"No name property for CertType\n\r")); continue; } }
//dwSchemaVersion
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_SCHEMA_VERSION, &(rgCertTypeInfo[dwCertType].dwSchemaVersion));
if(hr != S_OK) { AE_DEBUG((AE_INFO, L"No schema version for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//dwVersion
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_REVISION, &(rgCertTypeInfo[dwCertType].dwVersion));
if(hr != S_OK) { AE_DEBUG((AE_INFO, L"No major version for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//dwEnrollmentFlag
hr = CAGetCertTypeFlagsEx( hCTCurrent, CERTTYPE_ENROLLMENT_FLAG, &(rgCertTypeInfo[dwCertType].dwEnrollmentFlag));
if(hr != S_OK) { AE_DEBUG((AE_INFO, L"No enrollment flag for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//dwPrivatekeyFlag
hr = CAGetCertTypeFlagsEx( hCTCurrent, CERTTYPE_PRIVATE_KEY_FLAG, &(rgCertTypeInfo[dwCertType].dwPrivateKeyFlag));
if(hr != S_OK) { AE_DEBUG((AE_INFO, L"No private key flag for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//expiration offset
hr = CAGetCertTypeExpiration( hCTCurrent, NULL, (LPFILETIME)&(rgCertTypeInfo[dwCertType].ftExpirationOffset));
//we might not get the expiration date
if(hr != S_OK) { AE_DEBUG((AE_WARNING, L"Could not get cert type expirations: %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); }
//oid
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_OID, &(rgCertTypeInfo[dwCertType].awszOID));
//we might not get the oid property
if(rgCertTypeInfo[dwCertType].dwSchemaVersion >= CERTTYPE_SCHEMA_VERSION_2) { if((S_OK != hr) || (NULL == rgCertTypeInfo[dwCertType].awszOID) || (NULL == (rgCertTypeInfo[dwCertType].awszOID)[0]) ) { AE_DEBUG((AE_INFO, L"No oid for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; } }
//supersede
hr = CAGetCertTypePropertyEx( hCTCurrent, CERTTYPE_PROP_SUPERSEDE, &(rgCertTypeInfo[dwCertType].awszSupersede));
//we might not get the supersede property
if(hr != S_OK) { AE_DEBUG((AE_INFO, L"No supersede for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); }
//hArchiveStore
if(NULL == (rgCertTypeInfo[dwCertType].hArchiveStore=CertOpenStore( CERT_STORE_PROV_MEMORY, ENCODING_TYPE, NULL, 0, NULL)))
{ AE_DEBUG((AE_INFO, L"Unable to open archive cert store for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//hObtainedStore
if(NULL == (rgCertTypeInfo[dwCertType].hObtainedStore=CertOpenStore( CERT_STORE_PROV_MEMORY, ENCODING_TYPE, NULL, 0, NULL)))
{ AE_DEBUG((AE_INFO, L"Unable to open obtained cert store for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//hIssuedStore
if(NULL == (rgCertTypeInfo[dwCertType].hIssuedStore=CertOpenStore( CERT_STORE_PROV_MEMORY, ENCODING_TYPE, NULL, 0, NULL)))
{ AE_DEBUG((AE_INFO, L"Unable to open issued cert store for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
//allocate memory
rgCertTypeInfo[dwCertType].prgActive=(DWORD *)LocalAlloc(LPTR, sizeof(DWORD) * dwCount); if(NULL == rgCertTypeInfo[dwCertType].prgActive) { AE_DEBUG((AE_INFO, L"Unable to allocate memory for CT %ls\n\r", (rgCertTypeInfo[dwCertType].awszName)[0])); continue; }
memset(rgCertTypeInfo[dwCertType].prgActive, 0, sizeof(DWORD) * dwCount);
dwCertType++; }
*pdwCertType=dwCertType; *prgCertType=rgCertTypeInfo;
fResult = TRUE;
Ret:
return fResult; }
//--------------------------------------------------------------------------
//
// AEFreeCertTypeInfo
//
//
//--------------------------------------------------------------------------
BOOL AEFreeCertTypeInfo(DWORD dwCertType, AE_CERTTYPE_INFO *rgCertTypeInfo) { DWORD dwIndex=0; if(rgCertTypeInfo) { for(dwIndex=0; dwIndex < dwCertType; dwIndex++) AEFreeCertTypeStruct(&(rgCertTypeInfo[dwIndex]));
LocalFree(rgCertTypeInfo); } return TRUE; }
//--------------------------------------------------------------------------
//
// AEFreeCertTypeStruct
//
//
//--------------------------------------------------------------------------
BOOL AEFreeCertTypeStruct(AE_CERTTYPE_INFO *pCertTypeInfo) { DWORD dwIndex=0;
if(pCertTypeInfo) { if(pCertTypeInfo->hCertType) { if(pCertTypeInfo->awszName) CAFreeCertTypeProperty(pCertTypeInfo->hCertType, pCertTypeInfo->awszName);
if(pCertTypeInfo->awszDisplay) CAFreeCertTypeProperty(pCertTypeInfo->hCertType, pCertTypeInfo->awszDisplay);
if(pCertTypeInfo->awszOID) CAFreeCertTypeProperty(pCertTypeInfo->hCertType, pCertTypeInfo->awszOID); if(pCertTypeInfo->awszSupersede) CAFreeCertTypeProperty(pCertTypeInfo->hCertType, pCertTypeInfo->awszSupersede);
CACloseCertType(pCertTypeInfo->hCertType); }
if(pCertTypeInfo->prgActive) LocalFree(pCertTypeInfo->prgActive);
if(pCertTypeInfo->pOldCert) CertFreeCertificateContext(pCertTypeInfo->pOldCert);
if(pCertTypeInfo->hArchiveStore) CertCloseStore(pCertTypeInfo->hArchiveStore, 0);
if(pCertTypeInfo->hObtainedStore) CertCloseStore(pCertTypeInfo->hObtainedStore, 0);
if(pCertTypeInfo->hIssuedStore) CertCloseStore(pCertTypeInfo->hIssuedStore, 0);
if(pCertTypeInfo->dwPendCount) { if(pCertTypeInfo->rgPendInfo) { for(dwIndex=0; dwIndex < pCertTypeInfo->dwPendCount; dwIndex++) { if((pCertTypeInfo->rgPendInfo[dwIndex]).blobPKCS7.pbData) LocalFree((pCertTypeInfo->rgPendInfo[dwIndex]).blobPKCS7.pbData);
if((pCertTypeInfo->rgPendInfo[dwIndex]).blobHash.pbData) LocalFree((pCertTypeInfo->rgPendInfo[dwIndex]).blobHash.pbData); }
LocalFree(pCertTypeInfo->rgPendInfo); } }
memset(pCertTypeInfo, 0, sizeof(AE_CERTTYPE_INFO)); }
return TRUE; }
//--------------------------------------------------------------------------
//
// AERetrieveCAInfo
//
//
//--------------------------------------------------------------------------
BOOL AERetrieveCAInfo(LDAP *pld, BOOL fMachine, HANDLE hToken, DWORD *pdwCA, AE_CA_INFO **prgCAInfo) { BOOL fResult = FALSE; DWORD dwCount=0; DWORD dwCA=0; DWORD dwIndex=0; HRESULT hr=E_FAIL;
HCAINFO hCACurrent = NULL; HCAINFO hCANew = NULL; AE_CA_INFO *rgCAInfo=NULL;
*pdwCA=0; *prgCAInfo=NULL;
if(S_OK != (hr = CAEnumFirstCA( (LPCWSTR)pld, CA_FLAG_SCOPE_IS_LDAP_HANDLE | (fMachine?CA_FIND_LOCAL_SYSTEM:0), &hCACurrent))) { SetLastError(hr); goto Ret; }
if((NULL == hCACurrent) || (0 == (dwCount = CACountCAs(hCACurrent)))) { AE_DEBUG((AE_WARNING, L"No CA's available for auto-enrollment\n\r")); fResult=TRUE; goto Ret; }
rgCAInfo=(AE_CA_INFO *)LocalAlloc(LPTR, sizeof(AE_CA_INFO) * dwCount); if(NULL==rgCAInfo) { SetLastError(E_OUTOFMEMORY); goto Ret; }
memset(rgCAInfo, 0, sizeof(AE_CA_INFO) * dwCount);
for(dwIndex = 0; dwIndex < dwCount; dwIndex++ ) {
//check if we have a new CA
if(dwIndex > 0) { hr = CAEnumNextCA(hCACurrent, &hCANew);
if((S_OK != hr) || (NULL == hCANew)) { // Clean up from previous calls
if(dwCA < dwCount) AEFreeCAStruct(&(rgCAInfo[dwCA]));
break; }
hCACurrent = hCANew; }
// Clean up from previous calls
AEFreeCAStruct(&(rgCAInfo[dwCA]));
//copy the new CA' data
//hCAInfo
rgCAInfo[dwCA].hCAInfo = hCACurrent;
//CAName
hr = CAGetCAProperty(hCACurrent, CA_PROP_NAME, &(rgCAInfo[dwCA].awszCAName));
if((S_OK != hr) || (NULL == rgCAInfo[dwCA].awszCAName) || (NULL == (rgCAInfo[dwCA].awszCAName)[0]) ) { AE_DEBUG((AE_INFO, L"No name property for ca\n\r")); continue; }
//access check
if(S_OK != CAAccessCheckEx(rgCAInfo[dwCA].hCAInfo, hToken, CERTTYPE_ACCESS_CHECK_ENROLL | CERTTYPE_ACCESS_CHECK_NO_MAPPING)) { AE_DEBUG((AE_INFO, L"No access for CA %ls\n\r", (rgCAInfo[dwCA].awszCAName)[0])); continue; }
//CA Display
hr = CAGetCAProperty(hCACurrent, CA_PROP_DISPLAY_NAME, &(rgCAInfo[dwCA].awszCADisplay));
if((S_OK != hr) || (NULL == rgCAInfo[dwCA].awszCADisplay) || (NULL == (rgCAInfo[dwCA].awszCADisplay)[0]) ) { AE_DEBUG((AE_INFO, L"No display name property for ca\n\r"));
hr = CAGetCAProperty(hCACurrent, CA_PROP_NAME, &(rgCAInfo[dwCA].awszCADisplay));
if((S_OK != hr) || (NULL == rgCAInfo[dwCA].awszCADisplay) || (NULL == (rgCAInfo[dwCA].awszCADisplay)[0]) ) { AE_DEBUG((AE_INFO, L"No name property for ca\n\r")); continue; } }
//CADNS
hr = CAGetCAProperty(hCACurrent, CA_PROP_DNSNAME, &(rgCAInfo[dwCA].awszCADNS));
if((S_OK != hr) || (NULL == rgCAInfo[dwCA].awszCADNS) || (NULL == (rgCAInfo[dwCA].awszCADNS)[0]) ) { AE_DEBUG((AE_INFO, L"No DNS property for CA %ls\n\r", (rgCAInfo[dwCA].awszCAName)[0])); continue; }
//CACertificateTemplate
hr = CAGetCAProperty(hCACurrent, CA_PROP_CERT_TYPES, &(rgCAInfo[dwCA].awszCertificateTemplate));
if((S_OK != hr) || (NULL == rgCAInfo[dwCA].awszCertificateTemplate) || (NULL == (rgCAInfo[dwCA].awszCertificateTemplate)[0]) ) { AE_DEBUG((AE_INFO, L"No CertType property for CA %ls\n\r", (rgCAInfo[dwCA].awszCAName)[0])); continue; }
dwCA++; }
*pdwCA=dwCA; *prgCAInfo=rgCAInfo;
fResult = TRUE;
Ret:
return fResult; }
//--------------------------------------------------------------------------
//
// AEFreeCAInfo
//
//
//--------------------------------------------------------------------------
BOOL AEFreeCAInfo(DWORD dwCA, AE_CA_INFO *rgCAInfo) { DWORD dwIndex=0;
if(rgCAInfo) { for(dwIndex=0; dwIndex < dwCA; dwIndex++) AEFreeCAStruct(&(rgCAInfo[dwIndex]));
LocalFree(rgCAInfo); }
return TRUE; }
//--------------------------------------------------------------------------
//
// AEFreeCAStruct
//
//
//--------------------------------------------------------------------------
BOOL AEFreeCAStruct(AE_CA_INFO *pCAInfo) { if(pCAInfo) { if(pCAInfo->hCAInfo) { if(pCAInfo->awszCAName) { CAFreeCAProperty(pCAInfo->hCAInfo,pCAInfo->awszCAName); } if(pCAInfo->awszCADisplay) { CAFreeCAProperty(pCAInfo->hCAInfo,pCAInfo->awszCADisplay); } if(pCAInfo->awszCADNS) { CAFreeCAProperty(pCAInfo->hCAInfo, pCAInfo->awszCADNS); } if(pCAInfo->awszCertificateTemplate) { CAFreeCAProperty(pCAInfo->hCAInfo,pCAInfo->awszCertificateTemplate); }
CACloseCA(pCAInfo->hCAInfo); }
memset(pCAInfo, 0, sizeof(AE_CA_INFO)); }
return TRUE; }
//--------------------------------------------------------------------------
//
// AEClearVistedFlag
//
//--------------------------------------------------------------------------
BOOL AEClearVistedFlag(AE_GENERAL_INFO *pAE_General_Info) { DWORD dwIndex=0;
if(pAE_General_Info) { if(pAE_General_Info->rgCertTypeInfo) { for(dwIndex=0; dwIndex < pAE_General_Info->dwCertType; dwIndex++) { (pAE_General_Info->rgCertTypeInfo)[dwIndex].fSupersedeVisited=FALSE; } } }
return TRUE; } //--------------------------------------------------------------------------
//
// AEIfSupersede
//
// Recursively find if pwsz is superseded by one of the template in awsz.
// Notice that we should not loop in the superseding relationship.
// Superseding tree should be one directional tree without duplicated nodes.
//
//--------------------------------------------------------------------------
BOOL AEIfSupersede(LPWSTR pwsz, LPWSTR *awsz, AE_GENERAL_INFO *pAE_General_Info) { BOOL fResult = FALSE; LPWSTR *pwszArray = awsz; AE_TEMPLATE_INFO AETemplateInfo; AE_CERTTYPE_INFO *pCertType = NULL;
LPWSTR *awszSupersede=NULL;
if((NULL==pwsz) || (NULL==awsz)) return FALSE;
while(*pwszArray) { if(0 == wcscmp(pwsz, *pwszArray)) { fResult = TRUE; break; }
//find the template
memset(&AETemplateInfo, 0, sizeof(AE_TEMPLATE_INFO));
AETemplateInfo.pwszName=*pwszArray;
pCertType = AEFindTemplateInRequestTree( &AETemplateInfo, pAE_General_Info);
if(pCertType) { if(!(pCertType->fSupersedeVisited)) { //mark that we have visited superseding relationship for this template
pCertType->fSupersedeVisited=TRUE;
if(S_OK == CAGetCertTypePropertyEx( pCertType->hCertType, CERTTYPE_PROP_SUPERSEDE, &(awszSupersede))) { fResult = AEIfSupersede(pwsz, awszSupersede, pAE_General_Info);
if(awszSupersede) CAFreeCertTypeProperty( pCertType->hCertType, awszSupersede);
awszSupersede=NULL; if(TRUE == fResult) break; } } }
pwszArray++; }
return fResult; }
//--------------------------------------------------------------------------
//
// AEIsAnElement
//
//
//--------------------------------------------------------------------------
BOOL AEIsAnElement(LPWSTR pwsz, LPWSTR *awsz) { BOOL fResult = FALSE; LPWSTR *pwszArray = awsz; if((NULL==pwsz) || (NULL==awsz)) return FALSE;
while(*pwszArray) { if(0 == wcscmp(pwsz, *pwszArray)) { fResult = TRUE; break; } pwszArray++; }
return fResult; } //--------------------------------------------------------------------------
//
// AECopyCertStore
//
//
//--------------------------------------------------------------------------
BOOL AECopyCertStore(HCERTSTORE hSrcStore, HCERTSTORE hDesStore) { PCCERT_CONTEXT pCertContext=NULL;
if((NULL==hSrcStore) || (NULL==hDesStore)) return FALSE;
while(pCertContext = CertEnumCertificatesInStore(hSrcStore, pCertContext)) { CertAddCertificateContextToStore(hDesStore, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL); }
return TRUE; }
//--------------------------------------------------------------------------
//
// AEGetConfigDN
//
//
//--------------------------------------------------------------------------
HRESULT AEGetConfigDN( IN LDAP *pld, OUT LPWSTR *pwszConfigDn ) {
HRESULT hr; ULONG LdapError;
LDAPMessage *SearchResult = NULL; LDAPMessage *Entry = NULL; WCHAR *Attr = NULL; BerElement *BerElement; WCHAR **Values = NULL;
WCHAR *AttrArray[3]; struct l_timeval timeout;
WCHAR *ConfigurationNamingContext = L"configurationNamingContext"; WCHAR *ObjectClassFilter = L"objectCategory=*";
//
// Set the out parameters to null
//
if(pwszConfigDn) { *pwszConfigDn = NULL; }
timeout.tv_sec = 300; timeout.tv_usec = 0; //
// Query for the ldap server oerational attributes to obtain the default
// naming context.
//
AttrArray[0] = ConfigurationNamingContext; AttrArray[1] = NULL; // this is the sentinel
LdapError = ldap_search_ext_s(pld, NULL, LDAP_SCOPE_BASE, ObjectClassFilter, AttrArray, FALSE, NULL, NULL, &timeout, 10000, &SearchResult);
hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(LdapError));
if (S_OK == hr) {
Entry = ldap_first_entry(pld, SearchResult);
if (Entry) {
Values = ldap_get_values(pld, Entry, ConfigurationNamingContext);
if (Values && Values[0]) { (*pwszConfigDn) = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(Values[0])+1));
if(NULL==(*pwszConfigDn)) hr=E_OUTOFMEMORY; else wcscpy((*pwszConfigDn), Values[0]); }
ldap_value_free(Values); }
if (pwszConfigDn && (!(*pwszConfigDn))) { // We could not get the default domain or out of memory - bail out
if(E_OUTOFMEMORY != hr) hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO); }
if(SearchResult) { ldap_msgfree(SearchResult); } }
return hr; }
//--------------------------------------------------------------------------
//
// AERobustLdapBind
//
//--------------------------------------------------------------------------
HRESULT AERobustLdapBind( OUT LDAP ** ppldap, OUT LPWSTR *ppwszDCName) { DWORD dwErr = ERROR_SUCCESS; HRESULT hr = S_OK; BOOL fForceRediscovery = FALSE; DWORD dwGetDCFlags = DS_RETURN_DNS_NAME | DS_BACKGROUND_ONLY; PDOMAIN_CONTROLLER_INFO pDomainInfo = NULL; LDAP *pld = NULL; LPWSTR wszDomainControllerName = NULL; ULONG ulOptions = 0;
ULONG ldaperr;
do {
if(fForceRediscovery) { dwGetDCFlags |= DS_FORCE_REDISCOVERY; }
ldaperr = LDAP_SERVER_DOWN;
// Get the GC location
dwErr = DsGetDcNameW(NULL, // Delayload wrapped
NULL, NULL, NULL, dwGetDCFlags, &pDomainInfo);
if(dwErr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(dwErr); goto error; }
if((pDomainInfo == NULL) || ((pDomainInfo->Flags & DS_DNS_CONTROLLER_FLAG) == 0) || (pDomainInfo->DomainControllerName == NULL)) { if(!fForceRediscovery) { fForceRediscovery = TRUE; continue; } hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO); goto error; }
wszDomainControllerName = pDomainInfo->DomainControllerName;
// skip past forward slashes (why are they there?)
while(*wszDomainControllerName == L'\\') { wszDomainControllerName++; }
// bind to ds
if((pld = ldap_initW(wszDomainControllerName, LDAP_PORT)) == NULL) { ldaperr = LdapGetLastError(); } else { //reduce bogus DNS query
ulOptions = PtrToUlong(LDAP_OPT_ON); (void)ldap_set_optionW(pld, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions );
ldaperr = ldap_bind_sW(pld, NULL, NULL, LDAP_AUTH_NEGOTIATE); } hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(ldaperr));
if(fForceRediscovery) { break; } fForceRediscovery = TRUE;
} while(ldaperr == LDAP_SERVER_DOWN);
if(S_OK != hr) goto error;
if(ppwszDCName) { *ppwszDCName=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (wcslen(wszDomainControllerName) + 1));
if(NULL==*ppwszDCName) { hr=E_OUTOFMEMORY; goto error; }
wcscpy(*ppwszDCName, wszDomainControllerName); }
*ppldap = pld; pld = NULL;
hr=S_OK;
error:
if(pld) { ldap_unbind(pld); }
if(pDomainInfo) { NetApiBufferFree(pDomainInfo); } return hr; }
//---------------------------------------------------------------------------
//
// AEAllocAndCopy
//
//---------------------------------------------------------------------------
BOOL AEAllocAndCopy(LPWSTR pwszSrc, LPWSTR *ppwszDest) { if((NULL==ppwszDest) || (NULL==pwszSrc)) { SetLastError(E_INVALIDARG); return FALSE; }
*ppwszDest=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (wcslen(pwszSrc) + 1)); if(NULL==(*ppwszDest)) { SetLastError(E_OUTOFMEMORY); return FALSE; }
wcscpy(*ppwszDest, pwszSrc);
return TRUE; }
//--------------------------------------------------------------------------
// Name: AELogAutoEnrollmentEvent
//
// Description: This function registers an event in the event log of the
// local machine. Takes an optional argument list.
//
//--------------------------------------------------------------------------
void AELogAutoEnrollmentEvent(IN DWORD dwLogLevel, IN BOOL fError, IN HRESULT hr, IN DWORD dwEventId, IN BOOL fMachine, IN HANDLE hToken, IN DWORD dwParamCount, ... ) { BYTE FastBuffer[MAX_DN_SIZE]; DWORD cbUser =0; BOOL fAlloced = FALSE; PSID pSID = NULL; WORD dwEventType = 0; LPWSTR awszStrings[PENDING_ALLOC_SIZE + 3]; WORD cStrings = 0; LPWSTR wszString = NULL; WCHAR wszMsg[MAX_DN_SIZE]; WCHAR wszUser[MAX_DN_SIZE]; DWORD dwIndex=0; DWORD dwSize=0;
HANDLE hEventSource = NULL; LPWSTR wszHR=NULL; PTOKEN_USER ptgUser = NULL;
va_list ArgList;
//check the log level; log errors and success by default
if(((dwEventId >> 30) < dwLogLevel) && ((dwEventId >> 30) != STATUS_SEVERITY_SUCCESS)) return;
if(NULL==(hEventSource = RegisterEventSourceW(NULL, EVENT_AUTO_NAME))) return;
//copy the user/machine string
wszUser[0]=L'\0';
//use the user name for user case
if(FALSE == fMachine) { dwSize=MAX_DN_SIZE;
if(!GetUserNameEx( NameSamCompatible, // name format
wszUser, // name buffer
&dwSize)) // size of name buffer
{ LoadStringW(g_hmodThisDll, IDS_USER, wszUser, MAX_DN_SIZE); } } else { LoadStringW(g_hmodThisDll, IDS_MACHINE, wszUser, MAX_DN_SIZE); }
awszStrings[cStrings++] = wszUser;
//copy the variable strings if present
va_start(ArgList, dwParamCount);
for(dwIndex=0; dwIndex < dwParamCount; dwIndex++) { wszString = va_arg(ArgList, LPWSTR);
awszStrings[cStrings++] = wszString;
if(cStrings >= PENDING_ALLOC_SIZE) { break; } }
va_end(ArgList);
//copy the hr error code
if(fError) { if(S_OK == hr) hr=E_FAIL;
wsprintfW(wszMsg, L"0x%lx", hr); awszStrings[cStrings++] = wszMsg;
if(0 != FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (WCHAR *)&wszHR, 0, NULL)) { if(wszHR) awszStrings[cStrings++] = wszHR; } }
// check if the token is non zero is so then impersonating so get the SID
if((FALSE == fMachine) && (hToken)) { ptgUser = (PTOKEN_USER)FastBuffer; // try fast buffer first
cbUser = MAX_DN_SIZE;
if (!GetTokenInformation( hToken, // identifies access token
TokenUser, // TokenUser info type
ptgUser, // retrieved info buffer
cbUser, // size of buffer passed-in
&cbUser // required buffer size
)) { if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if (NULL != (ptgUser = (PTOKEN_USER)LocalAlloc(LPTR, cbUser))) { fAlloced = TRUE;
// get the user info and assign the sid if able to
if (GetTokenInformation( hToken, // identifies access token
TokenUser, // TokenUser info type
ptgUser, // retrieved info buffer
cbUser, // size of buffer passed-in
&cbUser // required buffer size
)) { pSID = ptgUser->User.Sid; } } }
} else { // assign the sid when fast buffer worked
pSID = ptgUser->User.Sid; } }
switch(dwEventId >> 30) { case 0: dwEventType = EVENTLOG_SUCCESS; break;
case 1: dwEventType = EVENTLOG_INFORMATION_TYPE; break;
case 2: dwEventType = EVENTLOG_WARNING_TYPE; break;
case 3: dwEventType = EVENTLOG_ERROR_TYPE; break; }
ReportEventW(hEventSource, // handle of event source
dwEventType, // event type
0, // event category
dwEventId, // event ID
pSID, // current user's SID
cStrings, // strings in lpszStrings
0, // no bytes of raw data
(LPCWSTR*)awszStrings, // array of error strings
NULL // no raw data
);
if (hEventSource) DeregisterEventSource(hEventSource);
if(fAlloced) { if(ptgUser) LocalFree(ptgUser); }
if(wszHR) LocalFree(wszHR);
return; }
//--------------------------------------------------------------------------
//
// FormatMessageUnicode
//
//--------------------------------------------------------------------------
BOOL FormatMessageUnicode(LPWSTR * ppwszFormat, UINT ids, ...) { // get format string from resources
WCHAR wszFormat[1000]; va_list argList; DWORD cbMsg=0; BOOL fResult=FALSE;
if(NULL == ppwszFormat) goto Ret;
if(!LoadStringW(g_hmodThisDll, ids, wszFormat, sizeof(wszFormat) / sizeof(wszFormat[0]))) goto Ret;
// format message into requested buffer
va_start(argList, ids);
cbMsg = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, wszFormat, 0, // dwMessageId
0, // dwLanguageId
(LPWSTR) (ppwszFormat), 0, // minimum size to allocate
&argList);
va_end(argList);
if(!cbMsg) goto Ret;
fResult=TRUE;
Ret: return fResult; }
//--------------------------------------------------------------------------
//
// AENetLogonUser
//
//Abstract:
//
// This module implements the network logon type by interfacing
// with the NT Lan Man Security Support Provider (NTLMSSP).
//
// If the logon succeds via the provided credentials, we duplicate
// the resultant Impersonation token to a Primary level token.
// This allows the result to be used in a call to CreateProcessAsUser
//
//Author:
//
// Scott Field (sfield) 09-Jun-96
//--------------------------------------------------------------------------
BOOL AENetLogonUser( LPTSTR UserName, LPTSTR DomainName, LPTSTR Password, PHANDLE phToken ) { SECURITY_STATUS SecStatus; CredHandle CredentialHandle1; CredHandle CredentialHandle2;
CtxtHandle ClientContextHandle; CtxtHandle ServerContextHandle; SecPkgCredentials_Names sNames;
ULONG ContextAttributes;
ULONG PackageCount; ULONG PackageIndex; PSecPkgInfo PackageInfo; DWORD cbMaxToken;
TimeStamp Lifetime; SEC_WINNT_AUTH_IDENTITY AuthIdentity;
SecBufferDesc NegotiateDesc; SecBuffer NegotiateBuffer;
SecBufferDesc ChallengeDesc; SecBuffer ChallengeBuffer;
HANDLE hImpersonationToken; BOOL bSuccess = FALSE ; // assume this function will fail
NegotiateBuffer.pvBuffer = NULL; ChallengeBuffer.pvBuffer = NULL; sNames.sUserName = NULL; ClientContextHandle.dwUpper = -1; ClientContextHandle.dwLower = -1; ServerContextHandle.dwUpper = -1; ServerContextHandle.dwLower = -1; CredentialHandle1.dwUpper = -1; CredentialHandle1.dwLower = -1; CredentialHandle2.dwUpper = -1; CredentialHandle2.dwLower = -1;
//
// << this section could be cached in a repeat caller scenario >>
//
//
// Get info about the security packages.
//
if(EnumerateSecurityPackages( &PackageCount, &PackageInfo ) != NO_ERROR) return FALSE;
//
// loop through the packages looking for NTLM
//
for(PackageIndex = 0 ; PackageIndex < PackageCount ; PackageIndex++ ) { if(PackageInfo[PackageIndex].Name != NULL) { if(lstrcmpi(PackageInfo[PackageIndex].Name, MICROSOFT_KERBEROS_NAME) == 0) { cbMaxToken = PackageInfo[PackageIndex].cbMaxToken; bSuccess = TRUE; break; } } }
FreeContextBuffer( PackageInfo );
if(!bSuccess) return FALSE;
bSuccess = FALSE; // reset to assume failure
//
// << end of cached section >>
//
//
// Acquire a credential handle for the server side
//
SecStatus = AcquireCredentialsHandle( NULL, // New principal
MICROSOFT_KERBEROS_NAME, // Package Name
SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &CredentialHandle1, &Lifetime );
if ( SecStatus != NO_ERROR ) { goto cleanup; }
//
// Acquire a credential handle for the client side
//
ZeroMemory( &AuthIdentity, sizeof(AuthIdentity) );
if ( DomainName != NULL ) { AuthIdentity.Domain = DomainName; AuthIdentity.DomainLength = lstrlen(DomainName); }
if ( UserName != NULL ) { AuthIdentity.User = UserName; AuthIdentity.UserLength = lstrlen(UserName); }
if ( Password != NULL ) { AuthIdentity.Password = Password; AuthIdentity.PasswordLength = lstrlen(Password); }
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
SecStatus = AcquireCredentialsHandle( NULL, // New principal
MICROSOFT_KERBEROS_NAME, // Package Name
SECPKG_CRED_OUTBOUND, NULL, (DomainName == NULL && UserName == NULL && Password == NULL) ? NULL : &AuthIdentity, NULL, NULL, &CredentialHandle2, &Lifetime );
if ( SecStatus != NO_ERROR ) { goto cleanup; }
SecStatus = QueryCredentialsAttributes(&CredentialHandle1, SECPKG_CRED_ATTR_NAMES, &sNames); if ( SecStatus != NO_ERROR ) { goto cleanup; } //
// Get the NegotiateMessage (ClientSide)
//
NegotiateDesc.ulVersion = 0; NegotiateDesc.cBuffers = 1; NegotiateDesc.pBuffers = &NegotiateBuffer;
NegotiateBuffer.cbBuffer = cbMaxToken; NegotiateBuffer.BufferType = SECBUFFER_TOKEN; NegotiateBuffer.pvBuffer = LocalAlloc( LMEM_FIXED, NegotiateBuffer.cbBuffer );
if ( NegotiateBuffer.pvBuffer == NULL ) { goto cleanup; }
SecStatus = InitializeSecurityContext( &CredentialHandle2, NULL, // No Client context yet
sNames.sUserName, // target name
ISC_REQ_SEQUENCE_DETECT, 0, // Reserved 1
SECURITY_NATIVE_DREP, NULL, // No initial input token
0, // Reserved 2
&ClientContextHandle, &NegotiateDesc, &ContextAttributes, &Lifetime ); if(SecStatus != NO_ERROR) { goto cleanup; }
//
// Get the ChallengeMessage (ServerSide)
//
NegotiateBuffer.BufferType |= SECBUFFER_READONLY; ChallengeDesc.ulVersion = 0; ChallengeDesc.cBuffers = 1; ChallengeDesc.pBuffers = &ChallengeBuffer;
ChallengeBuffer.cbBuffer = cbMaxToken; ChallengeBuffer.BufferType = SECBUFFER_TOKEN; ChallengeBuffer.pvBuffer = LocalAlloc( LMEM_FIXED, ChallengeBuffer.cbBuffer );
if ( ChallengeBuffer.pvBuffer == NULL ) { goto cleanup; }
SecStatus = AcceptSecurityContext( &CredentialHandle1, NULL, // No Server context yet
&NegotiateDesc, ISC_REQ_SEQUENCE_DETECT, SECURITY_NATIVE_DREP, &ServerContextHandle, &ChallengeDesc, &ContextAttributes, &Lifetime ); if(SecStatus != NO_ERROR) { goto cleanup; }
if(QuerySecurityContextToken(&ServerContextHandle, phToken) != NO_ERROR) goto cleanup;
bSuccess = TRUE;
cleanup:
//
// Delete context
//
if((ClientContextHandle.dwUpper != -1) || (ClientContextHandle.dwLower != -1)) { DeleteSecurityContext( &ClientContextHandle ); } if((ServerContextHandle.dwUpper != -1) || (ServerContextHandle.dwLower != -1)) { DeleteSecurityContext( &ServerContextHandle ); }
//
// Free credential handles
//
if((CredentialHandle1.dwUpper != -1) || (CredentialHandle1.dwLower != -1)) { FreeCredentialsHandle( &CredentialHandle1 ); } if((CredentialHandle2.dwUpper != -1) || (CredentialHandle2.dwLower != -1)) { FreeCredentialsHandle( &CredentialHandle2 ); }
if ( NegotiateBuffer.pvBuffer != NULL ) { ZeroMemory( NegotiateBuffer.pvBuffer, NegotiateBuffer.cbBuffer ); LocalFree( NegotiateBuffer.pvBuffer ); }
if ( ChallengeBuffer.pvBuffer != NULL ) { ZeroMemory( ChallengeBuffer.pvBuffer, ChallengeBuffer.cbBuffer ); LocalFree( ChallengeBuffer.pvBuffer ); }
if ( sNames.sUserName != NULL ) { FreeContextBuffer( sNames.sUserName ); }
return bSuccess; }
//--------------------------------------------------------------------------
//
// AEDebugLog
//
//--------------------------------------------------------------------------
#if DBG
void AEDebugLog(long Mask, LPCWSTR Format, ...) { va_list ArgList; int Level = 0; int PrefixSize = 0; int iOut; WCHAR wszOutString[MAX_DEBUG_BUFFER]; long OriginalMask = Mask;
if (Mask & g_AutoenrollDebugLevel) {
// Make the prefix first: "Process.Thread> GINA-XXX"
iOut = wsprintfW( wszOutString, L"%3d.%3d> AUTOENRL: ", GetCurrentProcessId(), GetCurrentThreadId());
va_start(ArgList, Format);
if (wvsprintfW(&wszOutString[iOut], Format, ArgList) < 0) { static WCHAR wszOverFlow[] = L"\n<256 byte OVERFLOW!>\n";
// Less than zero indicates that the string would not fit into the
// buffer. Output a special message indicating overflow.
wcscpy( &wszOutString[(sizeof(wszOutString) - sizeof(wszOverFlow))/sizeof(WCHAR)], wszOverFlow); } va_end(ArgList); OutputDebugStringW(wszOutString); } } #endif
//--------------------------------------------------------------------------
//
// AERemoveRegKey
//
// Remove the registry key for local system and all its sub keys.
//
//--------------------------------------------------------------------------
DWORD AERemoveRegKey(LPWSTR pwszRegKey) { DWORD dwLastError=0; //we should try to clean up as much as possible
DWORD dwIndex=0; DWORD dwSubKey=0; DWORD dwSubKeyLen=0; DWORD dwData=0; HKEY hDSKey=NULL; LPWSTR pwszSubKey=NULL;
//remove the optimization registry. OK if the key does not exist
if(ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, pwszRegKey, 0, KEY_ALL_ACCESS, &hDSKey)) goto Ret;
//remove all subkeys of hDSKey
if(ERROR_SUCCESS != (dwLastError = RegQueryInfoKey( hDSKey, NULL, NULL, NULL, &dwSubKey, &dwSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL))) goto Ret;
//terminating NULL
dwSubKeyLen++;
pwszSubKey=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * dwSubKeyLen); if(NULL == pwszSubKey) { dwLastError=ERROR_NOT_ENOUGH_MEMORY; goto Ret; }
for(dwIndex=0; dwIndex < dwSubKey; dwIndex++) { dwData = dwSubKeyLen;
if(ERROR_SUCCESS == (dwLastError = RegEnumKeyEx( hDSKey, 0, // As we delete, the index changes
pwszSubKey, &dwData, NULL, NULL, NULL, NULL))) { RegDeleteKey(hDSKey, pwszSubKey); } }
//remove the root registry key
dwLastError=RegDeleteKey(HKEY_LOCAL_MACHINE, pwszRegKey);
Ret:
if(pwszSubKey) LocalFree(pwszSubKey);
if(hDSKey) RegCloseKey(hDSKey);
return dwLastError; }
//--------------------------------------------------------------------------
//
// CertAutoRemove
//
// Function to remove enterprise specific public key trust upon domain disjoin.
// Should be called under local admin's context.
//
// The function will:
// remove autoenrollment directory cache registry;
// remove certificates under root enterprise store;
// remove certificates under NTAuth enterprise store;
// remove certificates under CA enterprise store;
//
//
// Parameters:
// IN dwFlags:
// CERT_AUTO_REMOVE_COMMIT
// CERT_AUTO_REMOVE_ROLL_BACK
//
// Return Value:
// BOOL: TURE is upon success
//
//--------------------------------------------------------------------------
BOOL WINAPI CertAutoRemove(IN DWORD dwFlags) { DWORD dwError=0; DWORD dwLastError=0; //we should try to clean up as much as possible
DWORD dwIndex=0; PCCERT_CONTEXT pContext=NULL; WCHAR wszNameBuf[64];
HANDLE hEvent=NULL; HCERTSTORE hLocalStore=NULL;
if((CERT_AUTO_REMOVE_COMMIT != dwFlags) && (CERT_AUTO_REMOVE_ROLL_BACK != dwFlags)) { dwLastError=ERROR_INVALID_PARAMETER; goto Ret; }
if(CERT_AUTO_REMOVE_ROLL_BACK == dwFlags) { //start machine autoenrollment
wcscpy(wszNameBuf, L"Global\\"); wcscat(wszNameBuf, MACHINE_AUTOENROLLMENT_TRIGGER_EVENT);
hEvent=OpenEvent(EVENT_MODIFY_STATE, FALSE, wszNameBuf); if (NULL == hEvent) { dwLastError=GetLastError(); goto Ret; }
if (!SetEvent(hEvent)) { dwLastError=GetLastError(); goto Ret; } } else { //remove all downloaded certificates
for(dwIndex =0; dwIndex < g_dwStoreInfo; dwIndex++) { hLocalStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE, g_rgStoreInfo[dwIndex].pwszStoreName);
if(hLocalStore) { while(pContext = CertEnumCertificatesInStore(hLocalStore, pContext)) { CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pContext)); }
CertCloseStore(hLocalStore,0); hLocalStore=NULL; } }
//remove the local machine's DC GUID cache
dwLastError=AERemoveRegKey(AUTO_ENROLLMENT_DS_KEY);
dwError=AERemoveRegKey(AUTO_ENROLLMENT_TEMPLATE_KEY);
if(0 == dwLastError) dwLastError=dwError; }
Ret:
if(hLocalStore) CertCloseStore(hLocalStore,0);
if (hEvent) CloseHandle(hEvent);
if(0 != dwLastError) { SetLastError(dwLastError); return FALSE; }
return TRUE; }
//--------------------------------------------------------------------------
//
// DLLMain
//
//
//--------------------------------------------------------------------------
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { BOOL fResult=TRUE; INITCOMMONCONTROLSEX initcomm = { sizeof(initcomm), ICC_NATIVEFNTCTL_CLASS | ICC_LISTVIEW_CLASSES | ICC_PROGRESS_CLASS };
switch( dwReason ) { case DLL_PROCESS_ATTACH: g_hmodThisDll=hInstance; DisableThreadLibraryCalls( hInstance );
//Init common control for progress bar
InitCommonControlsEx(&initcomm);
break;
case DLL_PROCESS_DETACH: break; }
return fResult; }
|