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