//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: scrdenr.cpp // //-------------------------------------------------------------------------- // SCrdEnr.cpp : Implementation of CSCrdEnr #define SECURITY_WIN32 //Or in the sources file -DSECURITY_WIN32 #include "stdafx.h" #include #include #include #include #include #include #include "certca.h" #include "certsrv.h" #include "security.h" #include #include "scrdenrl.h" #include "SCrdEnr.h" #include "enrlhelp.h" #include "xEnroll.h" #include "wzrdpvk.h" ///////////////////////////////////////////////////////////////////////////// // CSCrdEnr CSCrdEnr::CSCrdEnr(void) { DWORD dwIndex=0; DSOP_SCOPE_INIT_INFO ScopeInit; DSOP_INIT_INFO InitInfo; m_dwCTCount=0; m_dwCTIndex=0; m_rgCTInfo=NULL; m_pwszUserUPN=NULL; m_pwszUserSAM=NULL; m_pEnrolledCert=NULL; m_dwCSPCount=0; m_dwCSPIndex=0; m_rgCSPInfo=NULL; m_lEnrollmentStatus = CR_DISP_INCOMPLETE; m_pSigningCert=NULL; m_fSCardSigningCert=FALSE; m_pszCSPNameSigningCert=NULL; m_dwCSPTypeSigningCert=0; m_pszContainerSigningCert=NULL; m_pDsObjectPicker=NULL; m_pCachedCTEs = NULL; //no need to free m_pwszCachedCTEOid = NULL; m_pCachedCTE = NULL; // track whether our critsec is initialized m_fInitializedCriticalSection = FALSE; if(!FAILED(CoInitialize(NULL))) m_fInitialize=TRUE; // Initialize functions who's loading we've deferred. InitializeThunks(); __try { InitializeCriticalSection(&m_cSection); m_fInitializedCriticalSection = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { } //we now need to get the CSP list InitlializeCSPList(&m_dwCSPCount, &m_rgCSPInfo); //we now need to initialize the CA and its cert types InitializeCTList(&m_dwCTIndex, &m_dwCTCount, &m_rgCTInfo); //init for the user selection dialogue memset(&ScopeInit, 0, sizeof(DSOP_SCOPE_INIT_INFO)); memset(&InitInfo, 0, sizeof(InitInfo)); ScopeInit.cbSize = sizeof(DSOP_SCOPE_INIT_INFO); ScopeInit.flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN|DSOP_SCOPE_TYPE_GLOBAL_CATALOG; ScopeInit.flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT; //this will give us the SAM name for the user ScopeInit.FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS; ScopeInit.FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS; InitInfo.cbSize = sizeof(InitInfo); InitInfo.pwzTargetComputer = NULL; // NULL == local machine InitInfo.cDsScopeInfos = 1; InitInfo.aDsScopeInfos = &ScopeInit; InitInfo.flOptions = 0; //we are doing single select //create the COM object if (S_OK == CoCreateInstance (CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER, IID_IDsObjectPicker, (void **) &m_pDsObjectPicker)) { if(S_OK != (m_pDsObjectPicker->Initialize(&InitInfo))) { m_pDsObjectPicker->Release(); m_pDsObjectPicker=NULL; } } else m_pDsObjectPicker=NULL; } CSCrdEnr::~CSCrdEnr(void) { if(m_pDsObjectPicker) m_pDsObjectPicker->Release(); if(m_rgCTInfo) FreeCTInfo(m_dwCTCount, m_rgCTInfo); if(m_rgCSPInfo) FreeCSPInfo(m_dwCSPCount, m_rgCSPInfo); if(m_pwszUserUPN) SCrdEnrollFree(m_pwszUserUPN); if(m_pwszUserSAM) SCrdEnrollFree(m_pwszUserSAM); if(m_pSigningCert) CertFreeCertificateContext(m_pSigningCert); if(m_pszCSPNameSigningCert) SCrdEnrollFree(m_pszCSPNameSigningCert); if(m_pszContainerSigningCert) SCrdEnrollFree(m_pszContainerSigningCert); if(m_pEnrolledCert) CertFreeCertificateContext(m_pEnrolledCert); if (NULL != m_pwszCachedCTEOid) { LocalFree(m_pwszCachedCTEOid); } if (NULL != m_pCachedCTE) { LocalFree(m_pCachedCTE); } if(m_fInitialize) CoUninitialize(); if (m_fInitializedCriticalSection) DeleteCriticalSection(&m_cSection); } STDMETHODIMP CSCrdEnr::get_CSPCount(long * pVal) { if(NULL==pVal) return E_INVALIDARG; EnterCriticalSection(&m_cSection); *pVal = (long)m_dwCSPCount; LeaveCriticalSection(&m_cSection); return S_OK; } STDMETHODIMP CSCrdEnr::get_CSPName(BSTR * pVal) { HRESULT hr = S_OK; EnterCriticalSection(&m_cSection); if(NULL==m_rgCSPInfo || 0==m_dwCSPCount) { *pVal=NULL; hr=E_INVALIDARG; } else { if( NULL == (*pVal = SysAllocString(m_rgCSPInfo[m_dwCSPIndex].pwszCSPName))) hr = E_OUTOFMEMORY; } LeaveCriticalSection(&m_cSection); return(hr); } STDMETHODIMP CSCrdEnr::put_CSPName(BSTR newVal) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwIndex=0; EnterCriticalSection(&m_cSection); if(NULL == m_rgCSPInfo || 0 == m_dwCSPCount || NULL == newVal) goto InvalidArgErr; for(dwIndex=0; dwIndex < m_dwCSPCount; dwIndex++) { if(0 == _wcsicmp(newVal, m_rgCSPInfo[dwIndex].pwszCSPName)) { m_dwCSPIndex=dwIndex; break; } } if(dwIndex == m_dwCSPCount) goto InvalidArgErr; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); } STDMETHODIMP CSCrdEnr::selectUserName (/* [in] */ DWORD dwFlags) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); LPWSTR pwszSelectedUserSAM=NULL; LPWSTR pwszSelectedUserUPN=NULL; EnterCriticalSection(&m_cSection); if(NULL == m_pDsObjectPicker) goto InvalidArgErr; if(S_OK != (hr = GetSelectedUserName(m_pDsObjectPicker, &pwszSelectedUserSAM, &pwszSelectedUserUPN))) goto SelectUserErr; //we should at least have the UserSAM name if(NULL == pwszSelectedUserSAM) { if(pwszSelectedUserUPN) SCrdEnrollFree(pwszSelectedUserUPN); goto UnexpectedErr; } if(m_pwszUserSAM) { SCrdEnrollFree(m_pwszUserSAM); m_pwszUserSAM=NULL; } if(m_pwszUserUPN) { SCrdEnrollFree(m_pwszUserUPN); m_pwszUserUPN=NULL; } m_pwszUserSAM=pwszSelectedUserSAM; m_pwszUserUPN=pwszSelectedUserUPN; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR_VAR(SelectUserErr, hr); SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(UnexpectedErr, E_UNEXPECTED); } STDMETHODIMP CSCrdEnr::enroll (/* [in] */ DWORD dwFlags) { HRESULT hr = E_FAIL; DWORD errBefore = GetLastError(); ULONG cbSize = 0; SCrdEnroll_CA_INFO *pCAInfo = NULL; SCrdEnroll_CT_INFO *pCertTypeInfo = NULL; BSTR bstrAttribs = NULL; BSTR bstrCA = NULL; BSTR bstrCertificate = NULL; BSTR bstrReq = NULL; LPWSTR pwszRequesterName = NULL; ICertRequest2 *pICertRequest = NULL; IEnroll4 *pIEnroll = NULL; CRYPT_DATA_BLOB PKCS10Blob; CRYPT_DATA_BLOB PKCS7Request; CRYPT_DATA_BLOB PKCS7Response; DWORD dwDisposition; DWORD dwRequestID; LPWSTR pwszNewContainerName=NULL; PCCERT_CONTEXT pArchivalCert = NULL; LONG lKeySpec = XEKL_KEYSPEC_KEYX; LONG lKeyMin, lKeyMax; DWORD dwKeyMin, dwKeyMax, dwKeySize; //------------------------------------------------------------ // // Define locally scoped utility functions: // //------------------------------------------------------------ LocalScope(EnrollUtilities): BSTR bstrConcat(LPWSTR pwsz1, LPWSTR pwsz2, LPWSTR pwsz3, LPWSTR pwsz4) { // Note: assumes valid input parameters! BSTR bstrResult = NULL; LPWSTR pwszResult = NULL; pwszResult = (LPWSTR)SCrdEnrollAlloc(sizeof(WCHAR) * (wcslen(pwsz1) + wcslen(pwsz2) + wcslen(pwsz3) + wcslen(pwsz4) + 1)); if (pwszResult == NULL) { return NULL; } else { wcscpy(pwszResult, pwsz1); wcscat(pwszResult, pwsz2); wcscat(pwszResult, pwsz3); wcscat(pwszResult, pwsz4); // Convert the result to a BSTR bstrResult = SysAllocString(pwszResult); // Free the temporary storage. SCrdEnrollFree(pwszResult); // Return the result. return bstrResult; } } DWORD ICEnrollDispositionToCryptuiStatus(IN DWORD dwDisposition) { switch (dwDisposition) { case CR_DISP_INCOMPLETE: return CRYPTUI_WIZ_CERT_REQUEST_STATUS_CONNECTION_FAILED; case CR_DISP_DENIED: return CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_DENIED; case CR_DISP_ISSUED: return CRYPTUI_WIZ_CERT_REQUEST_STATUS_CERT_ISSUED; case CR_DISP_ISSUED_OUT_OF_BAND: return CRYPTUI_WIZ_CERT_REQUEST_STATUS_ISSUED_SEPARATELY; case CR_DISP_UNDER_SUBMISSION: return CRYPTUI_WIZ_CERT_REQUEST_STATUS_UNDER_SUBMISSION; case CR_DISP_ERROR: return CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_ERROR; default: // Should never happen return CRYPTUI_WIZ_CERT_REQUEST_STATUS_REQUEST_ERROR; } } EndLocalScope; //------------------------------------------------------------ // // Begin procedure body // //------------------------------------------------------------ memset(&PKCS10Blob, 0, sizeof(CRYPT_DATA_BLOB)); memset(&PKCS7Request, 0, sizeof(CRYPT_DATA_BLOB)); memset(&PKCS7Response, 0, sizeof(CRYPT_DATA_BLOB)); EnterCriticalSection(&m_cSection); //check for the status of the smart cards in the reader. //return the fully qualified container name for the new user //smart card if(S_OK != (hr = ChkSCardStatus(m_fSCardSigningCert, m_pSigningCert, m_pszCSPNameSigningCert, m_dwCSPTypeSigningCert, m_pszContainerSigningCert, m_rgCSPInfo[m_dwCSPIndex].pwszCSPName, &pwszNewContainerName))) goto StatusErr; //delete the old certificate if(m_pEnrolledCert) { CertFreeCertificateContext(m_pEnrolledCert); m_pEnrolledCert=NULL; } //init enrollment status m_lEnrollmentStatus = CR_DISP_INCOMPLETE; //make sure that we have the correct information for processing //the enrollment request if(0 == m_dwCTCount || NULL == m_rgCTInfo || 0 == m_dwCSPCount || NULL == m_rgCSPInfo || NULL == m_pSigningCert || ((NULL == m_pwszUserSAM) && (NULL == m_pwszUserUPN))) goto InvalidArgErr; //make sure that we have some CA pCertTypeInfo=&(m_rgCTInfo[m_dwCTIndex]); if(NULL == pCertTypeInfo->rgCAInfo || 0 == pCertTypeInfo->dwCACount) goto InvalidArgErr; pCAInfo=&(pCertTypeInfo->rgCAInfo[pCertTypeInfo->dwCAIndex]); if(NULL == (pIEnroll=MyPIEnroll4GetNoCOM())) goto TraceErr; //we use our own My store to store the enrolled certificate if(S_OK != (hr = pIEnroll->put_MyStoreNameWStr((LPWSTR)g_MyStoreName))) goto xEnrollErr; //we always use a new key if(S_OK != (hr=pIEnroll->put_UseExistingKeySet(FALSE))) goto xEnrollErr; //we the key container name if(S_OK != (hr=pIEnroll->put_ContainerNameWStr(pwszNewContainerName))) goto xEnrollErr; //set the CSP information if(S_OK != (hr=pIEnroll->put_ProviderType(m_rgCSPInfo[m_dwCSPIndex].dwCSPType))) goto xEnrollErr; if(S_OK !=(hr=pIEnroll->put_ProviderNameWStr(m_rgCSPInfo[m_dwCSPIndex].pwszCSPName))) goto xEnrollErr; //dwKeySpec if(S_OK !=(hr=pIEnroll->put_KeySpec(pCertTypeInfo->dwKeySpec))) goto xEnrollErr; //private key flags. Left half-word is the key size. //If the key size is 0, then specify a default key size. if (0 == (pCertTypeInfo->dwGenKeyFlags & 0xFFFF0000)) { // If min key size is not set, use 1024 bits. pCertTypeInfo->dwGenKeyFlags |= (1024 << 16); } dwKeySize = (pCertTypeInfo->dwGenKeyFlags & 0xFFFF0000) >> 16; if (0x0 != dwKeySize) { //make sure key size is in the range //let's get CSP key size information if (AT_SIGNATURE == pCertTypeInfo->dwKeySpec) { lKeySpec = XEKL_KEYSPEC_SIG; } hr = pIEnroll->GetKeyLenEx(XEKL_KEYSIZE_MIN, lKeySpec, &lKeyMin); // don't have error check because the CSP may not support it if (S_OK == hr) { hr = pIEnroll->GetKeyLenEx(XEKL_KEYSIZE_MAX, lKeySpec, &lKeyMax); if (S_OK != hr) { goto xEnrollErr; } dwKeyMin = (DWORD)lKeyMin; dwKeyMax = (DWORD)lKeyMax; if (dwKeySize < dwKeyMin) { //reset the current key size pCertTypeInfo->dwGenKeyFlags &= 0x0000FFFF; //set adjusted size pCertTypeInfo->dwGenKeyFlags |= ((dwKeyMin & 0x0000FFFF) << 16); } if (dwKeySize > dwKeyMax) { //reset the current key size pCertTypeInfo->dwGenKeyFlags &= 0x0000FFFF; //set adjusted size pCertTypeInfo->dwGenKeyFlags |= ((dwKeyMax & 0x0000FFFF) << 16); } } } if (S_OK !=(hr=pIEnroll->put_GenKeyFlags(pCertTypeInfo->dwGenKeyFlags))) goto xEnrollErr; // S/MIME supported? if (S_OK !=(hr=pIEnroll->put_EnableSMIMECapabilities (pCertTypeInfo->dwEnrollmentFlags & CT_FLAG_INCLUDE_SYMMETRIC_ALGORITHMS))) goto xEnrollErr; // Set archival cert, if one has been specified. // bstrCA <-- CA_location\CA_Name // bstrCA = local.bstrConcat (pCAInfo->pwszCALocation, L"\\", pCAInfo->pwszCADisplayName ? pCAInfo->pwszCADisplayName : pCAInfo->pwszCAName, L"\0"); if (NULL == bstrCA) goto MemoryErr; if (pCertTypeInfo->dwPrivateKeyFlags & CT_FLAG_ALLOW_PRIVATE_KEY_ARCHIVAL) { if (S_OK != (hr = this->GetCAExchangeCertificate(bstrCA, &pArchivalCert))) goto xEnrollErr; if (S_OK != (hr = pIEnroll->SetPrivateKeyArchiveCertificate(pArchivalCert))) goto xEnrollErr; } //cert Type extensions if(pCertTypeInfo->pCertTypeExtensions) { if(S_OK != (hr=pIEnroll->AddExtensionsToRequest (pCertTypeInfo->pCertTypeExtensions))) goto xEnrollErr; } //no smart card stuff if(S_OK != (hr=pIEnroll->put_ReuseHardwareKeyIfUnableToGenNew(FALSE))) goto xEnrollErr; //create a PKCS10 request if(FAILED(hr=pIEnroll->createPKCS10WStr(NULL, NULL, &PKCS10Blob))) goto xEnrollErr; //add the name value pair of the enroll-on-behalf pwszRequesterName=MkWStr(wszPROPREQUESTERNAME); if(NULL==pwszRequesterName) goto MemoryErr; if(S_OK != (hr=pIEnroll->AddNameValuePairToSignatureWStr( pwszRequesterName, m_pwszUserSAM))) goto xEnrollErr; //sign the request if(S_OK != (hr=pIEnroll->CreatePKCS7RequestFromRequest( &PKCS10Blob, m_pSigningCert, &PKCS7Request))) goto xEnrollErr; //send the request to the CA //we set the purpose to the renew so that the format //will be a PKCS7 bstrReq = SysAllocStringByteLen((LPCSTR)PKCS7Request.pbData, PKCS7Request.cbData); if (NULL == bstrReq) goto MemoryErr; bstrAttribs = NULL; // RECALL: bstrCA <-- CA_location\CA_Name if (pICertRequest == NULL) { if (S_OK != (hr = CoCreateInstance (CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest2, (void**)&pICertRequest))) goto xEnrollErr; } if (S_OK != (hr = pICertRequest->Submit (CR_IN_BINARY | CR_IN_PKCS7, bstrReq, bstrAttribs, bstrCA, (long *)&dwDisposition))) goto xEnrollErr; //use CR_DISP_ as enrollment status m_lEnrollmentStatus = dwDisposition; // check pending and save pending info // however, smart card enrollment station don't know how to deal with // this pending requests, may not necessary to do that if (CR_DISP_UNDER_SUBMISSION == m_lEnrollmentStatus) { hr = pICertRequest->GetRequestId((long *)&dwRequestID); if (S_OK != hr) { goto xEnrollErr; } hr = pIEnroll->setPendingRequestInfoWStr( dwRequestID, pCAInfo->pwszCALocation, NULL != pCAInfo->pwszCADisplayName ? pCAInfo->pwszCADisplayName : pCAInfo->pwszCAName, NULL); } if (CR_DISP_ISSUED != m_lEnrollmentStatus) { //if not issued, return goto CommonReturn; } //must be CR_DISP_ISSUED hr = pICertRequest->GetCertificate( CR_OUT_BINARY | CR_OUT_CHAIN, &bstrCertificate); if (S_OK != hr) { goto xEnrollErr; } // Marshal the cert into a CRYPT_DATA_BLOB, and install it: PKCS7Response.pbData = (LPBYTE)bstrCertificate; PKCS7Response.cbData = SysStringByteLen(bstrCertificate); m_pEnrolledCert = pIEnroll->getCertContextFromPKCS7(&PKCS7Response); if (NULL == m_pEnrolledCert) { hr = HRESULT_FROM_WIN32(GetLastError()); goto TraceErr; } hr=pIEnroll->acceptPKCS7Blob(&PKCS7Response); //we delete the enrolled certificate from the "My" store since it is added by //xEnroll. No need to check the error SearchAndDeleteCert(m_pEnrolledCert); if(S_OK != hr) { goto TraceErr; } hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwszNewContainerName) LocalFree((HLOCAL)pwszNewContainerName); if(pwszRequesterName) FreeWStr(pwszRequesterName); //the memory from xEnroll is freed via LocalFree //since we use the PIEnrollGetNoCOM function if(PKCS10Blob.pbData) LocalFree(PKCS10Blob.pbData); if(PKCS7Request.pbData) LocalFree(PKCS7Request.pbData); if (NULL != pArchivalCert) CertFreeCertificateContext(pArchivalCert); // PKCS7Respone's data is just an alias to m_pEnrolledCert's data: we don't need to free it. if (NULL != bstrAttribs) { SysFreeString(bstrAttribs); } if (NULL != bstrCA) { SysFreeString(bstrCA); } if (NULL != bstrCertificate) { SysFreeString(bstrCertificate); } if (NULL != pICertRequest) { pICertRequest->Release(); } if(pIEnroll) pIEnroll->Release(); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); //if an error has occurred, the free the enrolled certificate if(m_pEnrolledCert) { CertFreeCertificateContext(m_pEnrolledCert); m_pEnrolledCert=NULL; } goto CommonReturn; TRACE_ERROR(TraceErr); SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR_VAR(xEnrollErr, hr); SET_ERROR(MemoryErr, E_OUTOFMEMORY); SET_ERROR_VAR(StatusErr, hr); } HRESULT CSCrdEnr::GetCAExchangeCertificate(IN BSTR bstrCAQualifiedName, OUT PCCERT_CONTEXT *ppCert) { HRESULT hr = S_OK; ICertRequest2 *pICertRequest = NULL; VARIANT varExchangeCertificate; // Input validation: if (NULL == bstrCAQualifiedName || NULL == ppCert) return E_INVALIDARG; // Init: *ppCert = NULL; varExchangeCertificate.vt = VT_EMPTY; varExchangeCertificate.bstrVal = NULL; if (S_OK != (hr = CoCreateInstance (CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest2, (void**)&pICertRequest))) goto ErrorReturn; if (S_OK != (hr = pICertRequest->GetCAProperty (bstrCAQualifiedName, // CA Name/CA Location CR_PROP_CAXCHGCERT, // Get the exchange certificate from the CA. 0, // Unused PROPTYPE_BINARY, // CR_OUT_BINARY, // &varExchangeCertificate // Variant type representing the certificate. ))) goto ErrorReturn; if (VT_BSTR != varExchangeCertificate.vt || NULL == varExchangeCertificate.bstrVal) goto UnexpectedErr; *ppCert = CertCreateCertificateContext (X509_ASN_ENCODING, (LPBYTE)varExchangeCertificate.bstrVal, SysStringByteLen(varExchangeCertificate.bstrVal)); if (*ppCert == NULL) goto CertCliErr; CommonReturn: if (NULL != pICertRequest) { pICertRequest->Release(); } if (NULL != varExchangeCertificate.bstrVal) { SysFreeString(varExchangeCertificate.bstrVal); } return hr; ErrorReturn: if (ppCert != NULL && *ppCert != NULL) { CertFreeCertificateContext(*ppCert); *ppCert = NULL; } goto CommonReturn; SET_HRESULT(CertCliErr, HRESULT_FROM_WIN32(GetLastError())); SET_HRESULT(UnexpectedErr, E_UNEXPECTED); } STDMETHODIMP CSCrdEnr::selectSigningCertificate (/* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrCertTemplateName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); CRYPTUI_SELECTCERTIFICATE_STRUCT SelCert; BOOL fSCardSigningCert=FALSE; DWORD dwCSPTypeSigningCert=0; DWORD dwSize=0; DWORD dwImpType=0; SCrdEnroll_CERT_SELECT_INFO CertSelectInfo; HCRYPTPROV hProv=NULL; //no need to free it LPSTR pszContainerSigningCert=NULL; LPSTR pszCSPNameSigningCert=NULL; PCCERT_CONTEXT pSigningCert=NULL; HCERTSTORE hMyStore=NULL; CERT_CHAIN_PARA ChainParams; CERT_CHAIN_POLICY_PARA ChainPolicy; CERT_CHAIN_POLICY_STATUS PolicyStatus; CERT_CHAIN_CONTEXT const *pCertChain = NULL; memset(&SelCert, 0, sizeof(CRYPTUI_SELECTCERTIFICATE_STRUCT)); EnterCriticalSection(&m_cSection); //select a signing certificate in my store with private key hMyStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W, g_dwMsgAndCertEncodingType, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"my"); if(NULL==hMyStore) goto TraceErr; CertSelectInfo.dwFlags = dwFlags; CertSelectInfo.pwszCertTemplateName = bstrCertTemplateName; SelCert.dwSize=sizeof(CRYPTUI_SELECTCERTIFICATE_STRUCT); SelCert.cDisplayStores=1; SelCert.rghDisplayStores=&hMyStore; SelCert.pFilterCallback=SelectSignCertCallBack; SelCert.pvCallbackData=&CertSelectInfo; pSigningCert=CryptUIDlgSelectCertificate(&SelCert); if(NULL==pSigningCert) { //user clicks on the cancel button. hr=S_OK; goto CommonReturn; } //verification on the cert ZeroMemory(&ChainParams, sizeof(ChainParams)); ChainParams.cbSize = sizeof(ChainParams); ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; //get cert chain 1st if (!CertGetCertificateChain( HCCE_CURRENT_USER, //enrollment agent pSigningCert, //signing cert NULL, //use current system time NULL, //no additional stores &ChainParams, //chain params 0, //no crl check NULL, //reserved &pCertChain)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto CertGetCertificateChainError; } ZeroMemory(&ChainPolicy, sizeof(ChainPolicy)); ChainPolicy.cbSize = sizeof(ChainPolicy); ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG; ZeroMemory(&PolicyStatus, sizeof(PolicyStatus)); PolicyStatus.cbSize = sizeof(PolicyStatus); PolicyStatus.lChainIndex = -1; PolicyStatus.lElementIndex = -1; //verify the chain if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE, //basic pCertChain, &ChainPolicy, &PolicyStatus)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto CertVerifyCertificateChainPolicyError; } if (S_OK != PolicyStatus.dwError) { hr = PolicyStatus.dwError; goto CertVerifyCertificateChainPolicyError; } //get the hProv if(!CryptAcquireCertificatePrivateKey( pSigningCert, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &hProv, //this handle is cached and no need to be freed NULL, NULL)) goto TraceErr; //get related information //impType dwSize = sizeof(dwImpType); if(!CryptGetProvParam(hProv, PP_IMPTYPE, (BYTE *)(&dwImpType), &dwSize, 0)) goto TraceErr; if(CRYPT_IMPL_REMOVABLE & dwImpType) fSCardSigningCert=TRUE; //CSP Type dwSize = sizeof(dwCSPTypeSigningCert); if(!CryptGetProvParam(hProv, PP_PROVTYPE, (BYTE *)(&dwCSPTypeSigningCert), &dwSize, 0)) { goto TraceErr; } //CSP name dwSize = 0; if(!CryptGetProvParam(hProv, PP_NAME, NULL, &dwSize, 0) || (0==dwSize)) goto TraceErr; pszCSPNameSigningCert = (LPSTR) SCrdEnrollAlloc(dwSize); if(NULL == pszCSPNameSigningCert) goto MemoryErr; if(!CryptGetProvParam(hProv, PP_NAME, (BYTE *)pszCSPNameSigningCert, &dwSize, 0)) goto TraceErr; //Container name dwSize = 0; if(!CryptGetProvParam(hProv, PP_CONTAINER, NULL, &dwSize, 0) || (0==dwSize)) goto TraceErr; pszContainerSigningCert = (LPSTR) SCrdEnrollAlloc(dwSize); if(NULL == pszContainerSigningCert) goto MemoryErr; if(!CryptGetProvParam(hProv, PP_CONTAINER, (BYTE *)pszContainerSigningCert, &dwSize, 0)) goto TraceErr; //now, we need to perform a signig operation so that we //can invoke the smard card dialogue and cash the reader information //to the hProv handle. This operation is benign if the CSP of the signing //certificate is not on a smart card if(!SignWithCert(pszCSPNameSigningCert, dwCSPTypeSigningCert, pSigningCert)) goto TraceErr; //the certificate looks good if(m_pSigningCert) CertFreeCertificateContext(m_pSigningCert); if(m_pszContainerSigningCert) SCrdEnrollFree(m_pszContainerSigningCert); if(m_pszCSPNameSigningCert) SCrdEnrollFree(m_pszCSPNameSigningCert); m_pSigningCert=pSigningCert; m_fSCardSigningCert = fSCardSigningCert; m_pszCSPNameSigningCert = pszCSPNameSigningCert; m_dwCSPTypeSigningCert = dwCSPTypeSigningCert; m_pszContainerSigningCert = pszContainerSigningCert; pSigningCert=NULL; pszCSPNameSigningCert=NULL; pszContainerSigningCert=NULL; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pSigningCert) CertFreeCertificateContext(pSigningCert); if(hMyStore) CertCloseStore(hMyStore, 0); if(pszContainerSigningCert) SCrdEnrollFree(pszContainerSigningCert); if(pszCSPNameSigningCert) SCrdEnrollFree(pszCSPNameSigningCert); if (NULL != pCertChain) { CertFreeCertificateChain(pCertChain); } SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; TRACE_ERROR(TraceErr); SET_ERROR(MemoryErr, E_OUTOFMEMORY); TRACE_ERROR(CertGetCertificateChainError) TRACE_ERROR(CertVerifyCertificateChainPolicyError) } STDMETHODIMP CSCrdEnr::setSigningCertificate (/* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrCertTemplateName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); BOOL fSCardSigningCert=FALSE; DWORD dwCSPTypeSigningCert=0; DWORD dwSize=0; DWORD dwImpType=0; SCrdEnroll_CERT_SELECT_INFO CertSelectInfo; BOOL fSetCert=FALSE; HCRYPTPROV hProv=NULL; //no need to free it PCCERT_CONTEXT pPreCert=NULL; //no need to free it LPSTR pszContainerSigningCert=NULL; LPSTR pszCSPNameSigningCert=NULL; PCCERT_CONTEXT pSigningCert=NULL; HCERTSTORE hMyStore=NULL; EnterCriticalSection(&m_cSection); //mark if the signing cert is set previously if(m_pSigningCert) fSetCert=TRUE; //select a signing certificate in my store with private key hMyStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W, g_dwMsgAndCertEncodingType, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"my"); if(NULL==hMyStore) goto TraceErr; CertSelectInfo.dwFlags = dwFlags; CertSelectInfo.pwszCertTemplateName = bstrCertTemplateName; while(pSigningCert = CertEnumCertificatesInStore(hMyStore, pPreCert)) { //check for the certificate if(!SelectSignCertCallBack(pSigningCert, NULL, &CertSelectInfo)) goto NextCert; //this is a detaul NO-UI selection. We can not handle the case //when the signing certificate is on a smart card if(SmartCardCSP(pSigningCert)) goto NextCert; //get the hProv if(!CryptAcquireCertificatePrivateKey( pSigningCert, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &hProv, //this handle is cached and no need to be freed NULL, NULL)) goto NextCert; //get related information //impType dwSize = sizeof(dwImpType); if(!CryptGetProvParam(hProv, PP_IMPTYPE, (BYTE *)(&dwImpType), &dwSize, 0)) goto NextCert; if(CRYPT_IMPL_REMOVABLE & dwImpType) fSCardSigningCert=TRUE; //CSP Type dwSize = sizeof(dwCSPTypeSigningCert); if(!CryptGetProvParam(hProv, PP_PROVTYPE, (BYTE *)(&dwCSPTypeSigningCert), &dwSize, 0)) goto NextCert; //CSP name dwSize = 0; if(!CryptGetProvParam(hProv, PP_NAME, NULL, &dwSize, 0) || (0==dwSize)) goto NextCert; pszCSPNameSigningCert = (LPSTR) SCrdEnrollAlloc(dwSize); if(NULL == pszCSPNameSigningCert) goto MemoryErr; if(!CryptGetProvParam(hProv, PP_NAME, (BYTE *)pszCSPNameSigningCert, &dwSize, 0)) goto NextCert; //Container name dwSize = 0; if(!CryptGetProvParam(hProv, PP_CONTAINER, NULL, &dwSize, 0) || (0==dwSize)) goto NextCert; pszContainerSigningCert = (LPSTR) SCrdEnrollAlloc(dwSize); if(NULL == pszContainerSigningCert) goto MemoryErr; if(!CryptGetProvParam(hProv, PP_CONTAINER, (BYTE *)pszContainerSigningCert, &dwSize, 0)) goto NextCert; //now, we need to perform a signig operation so that we //can invoke the smard card dialogue and cash the reader information //to the hProv handle. This operation is benign if the CSP of the signing //certificate is not on a smart card if(!SignWithCert(pszCSPNameSigningCert, dwCSPTypeSigningCert, pSigningCert)) goto NextCert; //the certificate looks good if((NULL == m_pSigningCert) || (TRUE == fSetCert) || (IsNewerCert(pSigningCert, m_pSigningCert))) { fSetCert = FALSE; if(m_pSigningCert) { CertFreeCertificateContext(m_pSigningCert); m_pSigningCert = NULL; } m_pSigningCert=CertDuplicateCertificateContext(pSigningCert); if(NULL == m_pSigningCert) goto DupErr; //copy the data if(m_pszContainerSigningCert) SCrdEnrollFree(m_pszContainerSigningCert); if(m_pszCSPNameSigningCert) SCrdEnrollFree(m_pszCSPNameSigningCert); m_fSCardSigningCert = fSCardSigningCert; m_pszCSPNameSigningCert = pszCSPNameSigningCert; m_dwCSPTypeSigningCert = dwCSPTypeSigningCert; m_pszContainerSigningCert = pszContainerSigningCert; pszCSPNameSigningCert=NULL; pszContainerSigningCert=NULL; // should select the 1st matched cert break; //out of while loop } NextCert: if(pszContainerSigningCert) SCrdEnrollFree(pszContainerSigningCert); if(pszCSPNameSigningCert) SCrdEnrollFree(pszCSPNameSigningCert); pszCSPNameSigningCert=NULL; pszContainerSigningCert=NULL; fSCardSigningCert=FALSE; dwCSPTypeSigningCert=0; dwSize=0; dwImpType=0; pPreCert = pSigningCert; } //we should find a certificate if((NULL == m_pSigningCert) || (m_pSigningCert && (TRUE == fSetCert))) goto CryptNotFindErr; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pSigningCert) CertFreeCertificateContext(pSigningCert); if(hMyStore) CertCloseStore(hMyStore, 0); if(pszContainerSigningCert) SCrdEnrollFree(pszContainerSigningCert); if(pszCSPNameSigningCert) SCrdEnrollFree(pszCSPNameSigningCert); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; TRACE_ERROR(DupErr); SET_ERROR(CryptNotFindErr, CRYPT_E_NOT_FOUND); SET_ERROR(MemoryErr, E_OUTOFMEMORY); TRACE_ERROR(TraceErr); } STDMETHODIMP CSCrdEnr::get_EnrollmentStatus ( /* [retval][out] */ LONG * plEnrollmentStatus) { if (plEnrollmentStatus == NULL) { return E_INVALIDARG; } EnterCriticalSection(&m_cSection); *plEnrollmentStatus = m_lEnrollmentStatus; LeaveCriticalSection(&m_cSection); return S_OK; } STDMETHODIMP CSCrdEnr::getEnrolledCertificateName (/*[in] */ DWORD dwFlags, /* [retval][out] */ BSTR *pBstrCertName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwChar=0; LPWSTR pwsz=NULL; CRYPTUI_VIEWCERTIFICATE_STRUCT CertViewStruct; EnterCriticalSection(&m_cSection); if(NULL == m_pEnrolledCert) goto InvalidArgErr; *pBstrCertName=NULL; if(0 == (SCARD_ENROLL_NO_DISPLAY_CERT & dwFlags)) { //view the certificate memset(&CertViewStruct, 0, sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCT)); CertViewStruct.dwSize=sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCT); CertViewStruct.pCertContext=m_pEnrolledCert; CertViewStruct.dwFlags=CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE; CryptUIDlgViewCertificate(&CertViewStruct, NULL); } dwChar=CertGetNameStringW( m_pEnrolledCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); if ((dwChar != 0) && (NULL != (pwsz = (LPWSTR)SCrdEnrollAlloc(dwChar * sizeof(WCHAR))))) { CertGetNameStringW( m_pEnrolledCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pwsz, dwChar); if( NULL == (*pBstrCertName = SysAllocString(pwsz)) ) goto MemoryErr; } hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwsz) SCrdEnrollFree(pwsz); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(MemoryErr, E_OUTOFMEMORY); } STDMETHODIMP CSCrdEnr::resetUser() { EnterCriticalSection(&m_cSection); if(m_pwszUserUPN) { SCrdEnrollFree(m_pwszUserUPN); m_pwszUserUPN=NULL; } if(m_pwszUserSAM) { SCrdEnrollFree(m_pwszUserSAM); m_pwszUserSAM=NULL; } if(m_pEnrolledCert) { CertFreeCertificateContext(m_pEnrolledCert); m_pEnrolledCert=NULL; } LeaveCriticalSection(&m_cSection); return S_OK; } STDMETHODIMP CSCrdEnr::enumCSPName (/* [in] */ DWORD dwIndex, /* [in] */ DWORD dwFlags, /* [retval][out] */ BSTR *pbstrCSPName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); EnterCriticalSection(&m_cSection); if(NULL == pbstrCSPName) goto InvalidArgErr; *pbstrCSPName=NULL; if(0 == m_dwCSPCount || NULL == m_rgCSPInfo) goto InvalidArgErr; if(dwIndex >= m_dwCSPCount) goto NoItemErr; if( NULL == (*pbstrCSPName = SysAllocString(m_rgCSPInfo[dwIndex].pwszCSPName))) goto MemoryErr; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(MemoryErr, E_OUTOFMEMORY); SET_ERROR(NoItemErr,ERROR_NO_MORE_ITEMS); } STDMETHODIMP CSCrdEnr::getUserName (/* [in] */ DWORD dwFlags, /* [retval][out] */ BSTR *pbstrUserName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); EnterCriticalSection(&m_cSection); if(!pbstrUserName) goto InvalidArgErr; *pbstrUserName = NULL; if((NULL==m_pwszUserUPN) && (NULL==m_pwszUserSAM)) goto InvalidArgErr; if(SCARD_ENROLL_UPN_NAME & dwFlags) { if(NULL == m_pwszUserUPN) goto InvalidArgErr; if( NULL == (*pbstrUserName = SysAllocString(m_pwszUserUPN))) goto MemoryErr; } else { if(NULL == m_pwszUserSAM) goto InvalidArgErr; if( NULL == (*pbstrUserName = SysAllocString(m_pwszUserSAM))) goto MemoryErr; } hr= S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(MemoryErr, E_OUTOFMEMORY); } STDMETHODIMP CSCrdEnr::setUserName (/* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrUserName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); LPWSTR pwszSAM=NULL; EnterCriticalSection(&m_cSection); if(!bstrUserName) goto InvalidArgErr; if(SCARD_ENROLL_UPN_NAME & dwFlags) { //the UPN name has to have a corresponding SAM name if(!GetName(bstrUserName, NameUserPrincipal, NameSamCompatible, &pwszSAM)) goto TraceErr; if(m_pwszUserUPN) { SCrdEnrollFree(m_pwszUserUPN); m_pwszUserUPN=NULL; } if(m_pwszUserSAM) { SCrdEnrollFree(m_pwszUserSAM); m_pwszUserSAM=NULL; } if(NULL == (m_pwszUserUPN=CopyWideString(bstrUserName))) goto MemoryErr; m_pwszUserSAM=pwszSAM; pwszSAM = NULL; } else { if(m_pwszUserUPN) { SCrdEnrollFree(m_pwszUserUPN); m_pwszUserUPN=NULL; } if(m_pwszUserSAM) { SCrdEnrollFree(m_pwszUserSAM); m_pwszUserSAM=NULL; } if(NULL == (m_pwszUserSAM=CopyWideString(bstrUserName))) goto MemoryErr; GetName(m_pwszUserSAM, NameSamCompatible, NameUserPrincipal, &m_pwszUserUPN); } hr= S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwszSAM) SCrdEnrollFree(pwszSAM); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(MemoryErr, E_OUTOFMEMORY); TRACE_ERROR(TraceErr); } STDMETHODIMP CSCrdEnr::getCertTemplateCount (/* [in] */ DWORD dwFlags, /* [retval][out] */ long *pdwCertTemplateCount) { return CertTemplateCountOrName( 0, //index, doesn't matter what it is dwFlags, pdwCertTemplateCount, NULL); //count } STDMETHODIMP CSCrdEnr::getCertTemplateName (/* [in] */ DWORD dwFlags, /* [retval][out] */ BSTR *pbstrCertTemplateName) { HRESULT hr = S_OK; EnterCriticalSection(&m_cSection); if(NULL==m_rgCTInfo || 0==m_dwCTCount) { *pbstrCertTemplateName=NULL; hr=E_INVALIDARG; } else { if(dwFlags & SCARD_ENROLL_CERT_TEMPLATE_DISPLAY_NAME) { if( NULL == (*pbstrCertTemplateName = SysAllocString(m_rgCTInfo[m_dwCTIndex].pwszCTDisplayName))) hr = E_OUTOFMEMORY; } else { if( NULL == (*pbstrCertTemplateName = SysAllocString(m_rgCTInfo[m_dwCTIndex].pwszCTName))) hr = E_OUTOFMEMORY; } } LeaveCriticalSection(&m_cSection); return(hr); } STDMETHODIMP CSCrdEnr::setCertTemplateName (/* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrCertTemplateName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwIndex=0; EnterCriticalSection(&m_cSection); if(NULL==m_rgCTInfo || 0==m_dwCTCount) goto InvalidArgErr; for(dwIndex=0; dwIndex < m_dwCTCount; dwIndex++) { if(dwFlags & SCARD_ENROLL_CERT_TEMPLATE_DISPLAY_NAME) { if(0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwIndex].pwszCTDisplayName)) { m_dwCTIndex=dwIndex; break; } } else { if(0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwIndex].pwszCTName)) { m_dwCTIndex=dwIndex; break; } } } if(dwIndex == m_dwCTCount) goto InvalidArgErr; //we need to get the CA information for the newly selected cert type if(FALSE == m_rgCTInfo[m_dwCTIndex].fCAInfo) { GetCAInfoFromCertType(NULL, m_rgCTInfo[m_dwCTIndex].pwszCTName, &(m_rgCTInfo[m_dwCTIndex].dwCACount), &(m_rgCTInfo[m_dwCTIndex].rgCAInfo)); m_rgCTInfo[m_dwCTIndex].dwCAIndex=0; m_rgCTInfo[m_dwCTIndex].fCAInfo=TRUE; } hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); } HRESULT CSCrdEnr::CertTemplateCountOrName( IN DWORD dwIndex, IN DWORD dwFlags, OUT long *pdwCertTemplateCount, OUT BSTR *pbstrCertTemplateName) { HRESULT hr; DWORD errBefore = GetLastError(); DWORD dwIdx = 0; DWORD dwValidCount = 0; BOOL fCount; WCHAR *pwszName; DWORD const OFFLINE_SUBJECT_NAME_FLAGS = CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT | CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME; EnterCriticalSection(&m_cSection); if (NULL == pdwCertTemplateCount && NULL == pbstrCertTemplateName) { //can't be both goto InvalidParamErr; } //set flag for use of count or enum fCount = (NULL != pdwCertTemplateCount); if (fCount) { //init out *pdwCertTemplateCount = 0; } else { //init out *pbstrCertTemplateName = NULL; if (0 == m_dwCTCount || NULL == m_rgCTInfo) { //no templates goto InvalidArgErr; } if (dwIndex >= m_dwCTCount) { goto NoItemErr; } } //set default flags if not defined by caller if (0x0 == (dwFlags & SCARD_ENROLL_USER_CERT_TEMPLATE) && 0x0 == (dwFlags & SCARD_ENROLL_MACHINE_CERT_TEMPLATE)) { //assume both machine and user dwFlags |= SCARD_ENROLL_USER_CERT_TEMPLATE | SCARD_ENROLL_MACHINE_CERT_TEMPLATE; } if (0x0 == (dwFlags & SCARD_ENROLL_ENTERPRISE_CERT_TEMPLATE) && 0x0 == (dwFlags & SCARD_ENROLL_OFFLINE_CERT_TEMPLATE)) { //assume both enterprise and offline dwFlags |= SCARD_ENROLL_ENTERPRISE_CERT_TEMPLATE | SCARD_ENROLL_OFFLINE_CERT_TEMPLATE; } for (dwIdx = 0; dwIdx < m_dwCTCount; dwIdx++) { if (0x0 == (dwFlags & SCARD_ENROLL_CROSS_CERT_TEMPLATE) && 0 < m_rgCTInfo[dwIdx].dwRASignature) { //don't include template require signatures continue; } if((0x0 != (SCARD_ENROLL_USER_CERT_TEMPLATE & dwFlags) && FALSE == m_rgCTInfo[dwIdx].fMachine) || (0x0 != (SCARD_ENROLL_MACHINE_CERT_TEMPLATE & dwFlags) && TRUE == m_rgCTInfo[dwIdx].fMachine)) { if (0 != (SCARD_ENROLL_ENTERPRISE_CERT_TEMPLATE & dwFlags) && 0 == (OFFLINE_SUBJECT_NAME_FLAGS & m_rgCTInfo[dwIdx].dwSubjectNameFlags)) { //enterprise user/machine and no subject DN required dwValidCount++; } else if (0 != (SCARD_ENROLL_OFFLINE_CERT_TEMPLATE & dwFlags) && 0 != (OFFLINE_SUBJECT_NAME_FLAGS & m_rgCTInfo[dwIdx].dwSubjectNameFlags)) { //offline user/machine and subject DN required dwValidCount++; } } if (!fCount && dwValidCount == (dwIndex + 1)) { //get name & hit the one by index. get display or real name if (0x0 != (dwFlags & SCARD_ENROLL_CERT_TEMPLATE_DISPLAY_NAME)) { //display name pwszName = m_rgCTInfo[dwIdx].pwszCTDisplayName; } else { //real name pwszName = m_rgCTInfo[dwIdx].pwszCTName; } *pbstrCertTemplateName = SysAllocString(pwszName); if (NULL == *pbstrCertTemplateName) { goto MemoryErr; } else { //done break; } } } if(!fCount && dwIdx == m_dwCTCount) { //go beyond goto NoItemErr; } if (fCount) { *pdwCertTemplateCount = dwValidCount; } hr = S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG) SET_ERROR(InvalidParamErr, HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)) SET_ERROR(MemoryErr, E_OUTOFMEMORY) SET_ERROR(NoItemErr,ERROR_NO_MORE_ITEMS) } STDMETHODIMP CSCrdEnr::enumCertTemplateName (/* [in] */ DWORD dwIndex, /* [in] */ DWORD dwFlags, /* [retval][out] */ BSTR *pbstrCertTemplateName) { return CertTemplateCountOrName( dwIndex, dwFlags, NULL, //get name pbstrCertTemplateName); } HRESULT CSCrdEnr::_getCertTemplateExtensionInfo( IN CERT_EXTENSIONS *pCertTypeExtensions, IN LONG lType, OUT VOID *pExtInfo) { HRESULT hr; DWORD cwc = 0; DWORD dwCTE; DWORD i; BOOL fV2 = FALSE; //default v1 template BOOL fDword = TRUE; DWORD dwValue; EnterCriticalSection(&m_cSection); if (NULL == m_pCachedCTEs || m_pCachedCTEs != pCertTypeExtensions) { //new template, don't use cache //free the current cache if any if (NULL != m_pwszCachedCTEOid) { LocalFree(m_pwszCachedCTEOid); m_pwszCachedCTEOid = NULL; } if (NULL != m_pCachedCTE) { LocalFree(m_pCachedCTE); m_pCachedCTE = NULL; } //reset extension pointer m_pCachedCTEs = NULL; //loop to find CT extension for (i = 0; i < pCertTypeExtensions->cExtension; ++i) { if (0 == _stricmp(pCertTypeExtensions->rgExtension[i].pszObjId, szOID_CERTIFICATE_TEMPLATE)) { //v2 template fV2 = TRUE; //cache it m_pCachedCTEs = pCertTypeExtensions; break; } } if (!fV2) { //v1 template, return empty string m_pwszCachedCTEOid = (WCHAR*)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)); if (NULL == m_pwszCachedCTEOid) { hr = E_OUTOFMEMORY; goto MemoryErr; } m_pwszCachedCTEOid[0] = L'\0'; } else { //decode cert template extension if (!CryptDecodeObjectEx( X509_ASN_ENCODING, X509_CERTIFICATE_TEMPLATE, pCertTypeExtensions->rgExtension[i].Value.pbData, pCertTypeExtensions->rgExtension[i].Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, //use default LocalAlloc (void*)&m_pCachedCTE, &dwCTE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto CryptDecodeObjectExErr; } //have to convert asn to wchar while (TRUE) { cwc = MultiByteToWideChar( GetACP(), 0, m_pCachedCTE->pszObjId, -1, m_pwszCachedCTEOid, cwc); if (0 >= cwc) { hr = HRESULT_FROM_WIN32(GetLastError()); goto MultiByteToWideCharErr; } if (NULL != m_pwszCachedCTEOid) { //done break; } m_pwszCachedCTEOid = (WCHAR*)LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == m_pwszCachedCTEOid) { hr = E_OUTOFMEMORY; goto MemoryErr; } } } } //hit here, either from cached or new cache switch (lType) { case SCARD_CTINFO_EXT_OID: *(WCHAR**)pExtInfo = m_pwszCachedCTEOid; break; case SCARD_CTINFO_EXT_MAJOR: if (NULL != m_pCachedCTE) { *(LONG*)pExtInfo = m_pCachedCTE->dwMajorVersion; } else { //must be v1 *(LONG*)pExtInfo = 0; } break; case SCARD_CTINFO_EXT_MINOR: if (NULL != m_pCachedCTE) { *(LONG*)pExtInfo = m_pCachedCTE->dwMinorVersion; } else { //must be v1 *(LONG*)pExtInfo = 0; } break; case SCARD_CTINFO_EXT_MINOR_FLAG: if (NULL != m_pCachedCTE) { *(LONG*)pExtInfo = m_pCachedCTE->fMinorVersion; } else { //must be v1 *(LONG*)pExtInfo = 0; } break; default: hr = E_INVALIDARG; goto InvalidArgError; } hr = S_OK; ErrorReturn: LeaveCriticalSection(&m_cSection); return hr; TRACE_ERROR(CryptDecodeObjectExErr) TRACE_ERROR(MemoryErr) TRACE_ERROR(MultiByteToWideCharErr) TRACE_ERROR(InvalidArgError) } HRESULT CSCrdEnr::_getStrCertTemplateCSPList( IN DWORD dwIndex, IN DWORD dwFlag, OUT WCHAR **ppwszSupportedCSP) { HRESULT hr; DWORD i; WCHAR **ppwsz; WCHAR *pwszOut = NULL; EnterCriticalSection(&m_cSection); //init *ppwszSupportedCSP = NULL; if (SCARD_CTINFO_CSPLIST_FIRST == dwFlag) { //reset to first m_rgCTInfo[dwIndex].dwCurrentCSP = 0; } //get it ppwsz = m_rgCTInfo[dwIndex].rgpwszSupportedCSPs; for (i = 0; i < m_rgCTInfo[dwIndex].dwCurrentCSP && NULL != *ppwsz; ++i) { ++ppwsz; } if (NULL == *ppwsz) { //hit the end hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); goto NoMoreItemsErr; } // allocate buffer pwszOut = (WCHAR*)LocalAlloc(LMEM_FIXED, (wcslen(*ppwsz) + 1) * sizeof(WCHAR)); if (NULL == pwszOut) { hr = E_OUTOFMEMORY; goto MemoryErr; } //copy string wcscpy(pwszOut, *ppwsz); *ppwszSupportedCSP = pwszOut; pwszOut = NULL; ++m_rgCTInfo[dwIndex].dwCurrentCSP; hr = S_OK; ErrorReturn: LeaveCriticalSection(&m_cSection); if (NULL != pwszOut) { LocalFree(pwszOut); } return hr; TRACE_ERROR(MemoryErr) TRACE_ERROR(NoMoreItemsErr) } STDMETHODIMP CSCrdEnr::getCertTemplateInfo( /* [in] */ BSTR bstrCertTemplateName, /* [in] */ LONG lType, /* [retval][out] */ VARIANT *pvarCertTemplateInfo) { HRESULT hr; DWORD dwIndex; WCHAR *pwszInfo = NULL; BOOL fFound = FALSE; DWORD dwCSPFlag = SCARD_CTINFO_CSPLIST_NEXT; LONG lInfo; BOOL fStr = FALSE; //default to long BOOL fFree = TRUE; VARIANT varInfo; ZeroMemory(&varInfo, sizeof(varInfo)); EnterCriticalSection(&m_cSection); if (NULL == bstrCertTemplateName || 0 == m_dwCTCount || NULL == m_rgCTInfo) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); goto InvalidParamErr; } //get the CT information for (dwIndex=0; dwIndex < m_dwCTCount; dwIndex++) { if (0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwIndex].pwszCTName)) { // found it fFound = TRUE; break; } } if (!fFound) { //likely pass incorrect template name hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); goto NotFoundErr; } switch (lType) { case SCARD_CTINFO_KEYSPEC: lInfo = m_rgCTInfo[dwIndex].dwKeySpec; break; case SCARD_CTINFO_KEYFLAGS: lInfo = m_rgCTInfo[dwIndex].dwGenKeyFlags; break; case SCARD_CTINFO_EXT_OID: hr = _getCertTemplateExtensionInfo( m_rgCTInfo[dwIndex].pCertTypeExtensions, lType, &pwszInfo); if (S_OK != hr) { goto _getCertTemplateExtensionInfoErr; } fStr = TRUE; fFree = FALSE; //don't free cache break; case SCARD_CTINFO_EXT_MAJOR: case SCARD_CTINFO_EXT_MINOR: case SCARD_CTINFO_EXT_MINOR_FLAG: hr = _getCertTemplateExtensionInfo( m_rgCTInfo[dwIndex].pCertTypeExtensions, lType, &lInfo); if (S_OK != hr) { goto _getCertTemplateExtensionInfoErr; } break; case SCARD_CTINFO_CSPLIST_FIRST: dwCSPFlag = SCARD_CTINFO_CSPLIST_FIRST; //fall through case SCARD_CTINFO_CSPLIST_NEXT: hr = _getStrCertTemplateCSPList(dwIndex, dwCSPFlag, &pwszInfo); if (S_OK != hr) { goto _getStrCertTemplateCSPListErr; } fStr = TRUE; break; case SCARD_CTINFO_SUBJECTFLAG: lInfo = m_rgCTInfo[dwIndex].dwSubjectNameFlags; break; case SCARD_CTINFO_GENERALFLAGS: lInfo = m_rgCTInfo[dwIndex].dwGeneralFlags; break; case SCARD_CTINFO_ENROLLMENTFLAGS: lInfo = m_rgCTInfo[dwIndex].dwEnrollmentFlags; break; case SCARD_CTINFO_PRIVATEKEYFLAGS: lInfo = m_rgCTInfo[dwIndex].dwPrivateKeyFlags; break; case SCARD_CTINFO_RA_SIGNATURES: lInfo = m_rgCTInfo[dwIndex].dwRASignature; break; default: hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); goto InvalidParamErr; } if (fStr) { varInfo.vt = VT_BSTR; varInfo.bstrVal = SysAllocString(pwszInfo); if (NULL == varInfo.bstrVal) { hr = E_OUTOFMEMORY; goto MemoryErr; } } else { varInfo.vt = VT_I4; varInfo.lVal = lInfo; } //return *pvarCertTemplateInfo = varInfo; hr = S_OK; ErrorReturn: LeaveCriticalSection(&m_cSection); if (fFree && NULL != pwszInfo) { LocalFree(pwszInfo); } return hr; TRACE_ERROR(InvalidParamErr) TRACE_ERROR(NotFoundErr) TRACE_ERROR(MemoryErr) TRACE_ERROR(_getCertTemplateExtensionInfoErr) TRACE_ERROR(_getStrCertTemplateCSPListErr) } STDMETHODIMP CSCrdEnr::getCACount (/* [in] */ BSTR bstrCertTemplateName, /* [retval][out] */ long *pdwCACount) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwIndex=0; EnterCriticalSection(&m_cSection); if(NULL == bstrCertTemplateName || NULL == pdwCACount) goto InvalidArgErr; *pdwCACount=0; if(0 == m_dwCTCount || NULL == m_rgCTInfo) goto InvalidArgErr; //get the CT information for(dwIndex=0; dwIndex < m_dwCTCount; dwIndex++) { if(0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwIndex].pwszCTName)) break; } if(dwIndex == m_dwCTCount) goto InvalidArgErr; //we need to get the CA information for the newly selected cert type if(FALSE == m_rgCTInfo[dwIndex].fCAInfo) { GetCAInfoFromCertType(NULL, m_rgCTInfo[dwIndex].pwszCTName, &(m_rgCTInfo[dwIndex].dwCACount), &(m_rgCTInfo[dwIndex].rgCAInfo)); m_rgCTInfo[dwIndex].dwCAIndex=0; m_rgCTInfo[dwIndex].fCAInfo=TRUE; } *pdwCACount = (long)m_rgCTInfo[dwIndex].dwCACount; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); } STDMETHODIMP CSCrdEnr::getCAName (/* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrCertTemplateName, /* [retval][out] */ BSTR *pbstrCAName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwIndex=0; LPWSTR pwszName=NULL; SCrdEnroll_CT_INFO *pCTInfo=NULL; EnterCriticalSection(&m_cSection); if(NULL == bstrCertTemplateName || NULL == pbstrCAName) goto InvalidArgErr; *pbstrCAName=NULL; if(0 == m_dwCTCount || NULL == m_rgCTInfo) goto InvalidArgErr; //get the CT information for(dwIndex=0; dwIndex < m_dwCTCount; dwIndex++) { if(0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwIndex].pwszCTName)) break; } if(dwIndex == m_dwCTCount) goto InvalidArgErr; //we need to get the CA information for the newly selected cert type if(FALSE == m_rgCTInfo[dwIndex].fCAInfo) { GetCAInfoFromCertType(NULL, m_rgCTInfo[dwIndex].pwszCTName, &(m_rgCTInfo[dwIndex].dwCACount), &(m_rgCTInfo[dwIndex].rgCAInfo)); m_rgCTInfo[dwIndex].dwCAIndex=0; m_rgCTInfo[dwIndex].fCAInfo=TRUE; } pCTInfo=&(m_rgCTInfo[dwIndex]); if(NULL == pCTInfo->rgCAInfo) goto InvalidArgErr; if(!RetrieveCAName(dwFlags, &(pCTInfo->rgCAInfo[pCTInfo->dwCAIndex]), &pwszName)) goto TraceErr; if(NULL == (*pbstrCAName = SysAllocString(pwszName))) goto MemoryErr; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwszName) SCrdEnrollFree(pwszName); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(MemoryErr, E_OUTOFMEMORY); TRACE_ERROR(TraceErr); } STDMETHODIMP CSCrdEnr::setCAName (/* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrCertTemplateName, /* [in] */ BSTR bstrCAName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwIndex=0; DWORD dwCAIndex=0; LPWSTR pwszName=NULL; SCrdEnroll_CT_INFO *pCTInfo=NULL; EnterCriticalSection(&m_cSection); if(NULL == bstrCertTemplateName || NULL == bstrCAName) goto InvalidArgErr; if(0 == m_dwCTCount || NULL == m_rgCTInfo) goto InvalidArgErr; //get the CT information for(dwIndex=0; dwIndex < m_dwCTCount; dwIndex++) { if(0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwIndex].pwszCTName)) break; } if(dwIndex == m_dwCTCount) goto InvalidArgErr; //we need to get the CA information for the newly selected cert type if(FALSE == m_rgCTInfo[dwIndex].fCAInfo) { GetCAInfoFromCertType(NULL, m_rgCTInfo[dwIndex].pwszCTName, &(m_rgCTInfo[dwIndex].dwCACount), &(m_rgCTInfo[dwIndex].rgCAInfo)); m_rgCTInfo[dwIndex].dwCAIndex=0; m_rgCTInfo[dwIndex].fCAInfo=TRUE; } pCTInfo=&(m_rgCTInfo[dwIndex]); if(NULL == pCTInfo->rgCAInfo) goto InvalidArgErr; //search for the CA specified in the input for(dwCAIndex=0; dwCAIndex < pCTInfo->dwCACount; dwCAIndex++) { if(!RetrieveCAName(dwFlags, &(pCTInfo->rgCAInfo[dwCAIndex]), &pwszName)) continue; if(0 == _wcsicmp(pwszName, bstrCAName)) break; SCrdEnrollFree(pwszName); pwszName=NULL; } if(dwCAIndex == pCTInfo->dwCACount) goto InvalidArgErr; //remember the selected CA by its index pCTInfo->dwCAIndex = dwCAIndex; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwszName) SCrdEnrollFree(pwszName); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); } STDMETHODIMP CSCrdEnr::enumCAName (/* [in] */ DWORD dwIndex, /* [in] */ DWORD dwFlags, /* [in] */ BSTR bstrCertTemplateName, /* [retval][out] */ BSTR *pbstrCAName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwCTIndex=0; LPWSTR pwszName=NULL; SCrdEnroll_CT_INFO *pCTInfo=NULL; EnterCriticalSection(&m_cSection); if(NULL == bstrCertTemplateName || NULL == pbstrCAName) goto InvalidArgErr; *pbstrCAName=NULL; if(0 == m_dwCTCount || NULL == m_rgCTInfo) goto InvalidArgErr; //get the CT information for(dwCTIndex=0; dwCTIndex < m_dwCTCount; dwCTIndex++) { if(0 == _wcsicmp(bstrCertTemplateName, m_rgCTInfo[dwCTIndex].pwszCTName)) break; } if(dwCTIndex == m_dwCTCount) goto InvalidArgErr; //we need to get the CA information for the newly selected cert type if(FALSE == m_rgCTInfo[dwCTIndex].fCAInfo) { GetCAInfoFromCertType(NULL, m_rgCTInfo[dwCTIndex].pwszCTName, &(m_rgCTInfo[dwCTIndex].dwCACount), &(m_rgCTInfo[dwCTIndex].rgCAInfo)); m_rgCTInfo[dwCTIndex].dwCAIndex=0; m_rgCTInfo[dwCTIndex].fCAInfo=TRUE; } pCTInfo=&(m_rgCTInfo[dwCTIndex]); //search for the CA specified in the input if(dwIndex >= pCTInfo->dwCACount) goto InvalidArgErr; if(!RetrieveCAName(dwFlags, &(pCTInfo->rgCAInfo[dwIndex]), &pwszName)) goto TraceErr; if(NULL == (*pbstrCAName = SysAllocString(pwszName))) goto MemoryErr; hr=S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwszName) SCrdEnrollFree(pwszName); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(InvalidArgErr, E_INVALIDARG); SET_ERROR(MemoryErr, E_OUTOFMEMORY); TRACE_ERROR(TraceErr); } STDMETHODIMP CSCrdEnr::getSigningCertificateName (/* [in] */ DWORD dwFlags, /* [retval][out] */ BSTR *pbstrSigningCertName) { HRESULT hr= E_FAIL; DWORD errBefore= GetLastError(); DWORD dwChar=0; LPWSTR pwsz=NULL; CRYPTUI_VIEWCERTIFICATE_STRUCT CertViewStruct; *pbstrSigningCertName=NULL; EnterCriticalSection(&m_cSection); if(NULL == m_pSigningCert) goto InvalidArgErr; if(0 == (SCARD_ENROLL_NO_DISPLAY_CERT & dwFlags)) { //view the certificate memset(&CertViewStruct, 0, sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCT)); CertViewStruct.dwSize=sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCT); CertViewStruct.pCertContext=m_pSigningCert; CertViewStruct.dwFlags=CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE; CryptUIDlgViewCertificate(&CertViewStruct, NULL); } dwChar=CertGetNameStringW( m_pSigningCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); if ((dwChar != 0) && (NULL != (pwsz = (LPWSTR)SCrdEnrollAlloc(dwChar * sizeof(WCHAR))))) { CertGetNameStringW( m_pSigningCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pwsz, dwChar); if( NULL == (*pbstrSigningCertName = SysAllocString(pwsz))) goto MemoryErr; } hr= S_OK; CommonReturn: LeaveCriticalSection(&m_cSection); if(pwsz) SCrdEnrollFree(pwsz); SetLastError(errBefore); return hr; ErrorReturn: if(ERROR_SUCCESS == (errBefore = GetLastError())) errBefore=E_UNEXPECTED; hr = CodeToHR(errBefore); goto CommonReturn; SET_ERROR(MemoryErr, E_OUTOFMEMORY); SET_ERROR(InvalidArgErr, E_INVALIDARG); }