windows-nt/Source/XPSP1/NT/ds/win32/ntcrypto/autoenrl/pautoenr/autoenro.cpp
2020-09-26 16:20:57 +08:00

8968 lines
264 KiB
C++

//+-------------------------------------------------------------------------
//
// 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;
}