//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 2000 // // File: autoenro.cpp // //-------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #define SECURITY_WIN32 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //******************************************************************************* // // // 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; }