//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: usecert.cpp // // Contents: cert store and file operations // //-------------------------------------------------------------------------- #include "pch.cpp" #pragma hdrstop #include #include "initcert.h" #include "cscsp.h" #include "cspenum.h" #include "wizpage.h" #include "usecert.h" #define __dwFILE__ __dwFILE_OCMSETUP_USECERT_CPP__ typedef struct _EXISTING_CA_IDINFO { LPSTR pszObjId; WCHAR **ppwszIdInfo; } EXISTING_CA_IDINFO; /* EXISTING_CA_IDINFO g_ExistingCAIdInfo[] = { {szOID_COMMON_NAME, NULL}, {szOID_ORGANIZATION_NAME, NULL}, {szOID_ORGANIZATIONAL_UNIT_NAME, NULL}, {szOID_LOCALITY_NAME, NULL}, {szOID_STATE_OR_PROVINCE_NAME, NULL}, {szOID_RSA_emailAddr, NULL}, {szOID_COUNTRY_NAME, NULL}, {NULL, NULL}, }; */ HRESULT myMakeExprValidity( IN FILETIME const *pft, OUT LONG *plDayCount) { HRESULT hr; FILETIME ft; LONGLONG llDelta; *plDayCount = 0; // get current time GetSystemTimeAsFileTime(&ft); llDelta = mySubtractFileTimes(pft, &ft); llDelta /= 1000 * 1000 * 10; llDelta += 12 * 60 * 60; // half day more to avoid truncation llDelta /= 24 * 60 * 60; *plDayCount = (LONG) llDelta; if (0 > *plDayCount) { *plDayCount = 0; } hr = S_OK; //error: return hr; } //-------------------------------------------------------------------- // returns true if the CA type is root and the cert is self-signed, // or the CA type is subordinate and the cert is no self-signed HRESULT IsCertSelfSignedForCAType( IN CASERVERSETUPINFO * pServer, IN CERT_CONTEXT const * pccCert, OUT BOOL * pbOK) { CSASSERT(NULL!=pccCert); CSASSERT(NULL!=pServer); CSASSERT(NULL!=pbOK); HRESULT hr; DWORD dwVerificationFlags; BOOL bRetVal; // See if this cert is self-signed or not. // First, we flag what we want to check: "Use the public // key in the issuer's certificate to verify the signature on // the subject certificate." // We use the same certificate as issuer and subject dwVerificationFlags=CERT_STORE_SIGNATURE_FLAG; // perform the checks bRetVal=CertVerifySubjectCertificateContext( pccCert, pccCert, // issuer same as subject &dwVerificationFlags); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "CertVerifySubjectCertificateContext"); } // Every check that passed had its flag zeroed. See if our check passed if (CERT_STORE_SIGNATURE_FLAG&dwVerificationFlags){ // This cert is not self-signed. if (IsRootCA(pServer->CAType)) { // A root CA cert must be self-signed. *pbOK=FALSE; } else { // A subordinate CA cert must not be self-signed. *pbOK=TRUE; } } else { // This cert is self-signed. if (IsSubordinateCA(pServer->CAType)) { // A subordinate CA cert must not be self-signed. *pbOK=FALSE; } else { // A root CA cert must be self-signed. *pbOK=TRUE; } } hr=S_OK; error: return hr; } //-------------------------------------------------------------------- // find a certificate's hash algorithm in a CSP's list of hash algorithms HRESULT FindHashAlgorithm( IN CERT_CONTEXT const * pccCert, IN CSP_INFO * pCSPInfo, OUT CSP_HASH ** ppHash) { CSASSERT(NULL!=pccCert); CSASSERT(NULL!=pCSPInfo); CSASSERT(NULL!=ppHash); HRESULT hr; CSP_HASH * phTravel; const CRYPT_OID_INFO * pOIDInfo; // Initialize out param *ppHash=NULL; // get the AlgID from the hash algorithm OID // (returned pointer must not be freed) pOIDInfo=CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, pccCert->pCertInfo->SignatureAlgorithm.pszObjId, CRYPT_SIGN_ALG_OID_GROUP_ID ); if (NULL==pOIDInfo) { // function is not doc'd to set GetLastError() hr=CRYPT_E_NOT_FOUND; _JumpError(hr, error, "Signature algorithm not found"); } // find the hash algorithm in the list of hash algorithms the CSP supports for (phTravel=pCSPInfo->pHashList; NULL!=phTravel; phTravel=phTravel->next) { if (pOIDInfo->Algid==phTravel->idAlg) { *ppHash=phTravel; break; } } if (NULL==phTravel) { hr=CRYPT_E_NOT_FOUND; _JumpError(hr, error, "CSP does not support hash algorithm"); } hr=S_OK; error: return hr; } /* HRESULT HookExistingIdInfoData( CASERVERSETUPINFO *pServer) { HRESULT hr; int i = 0; while (NULL != g_ExistingCAIdInfo[i].pszObjId) { if (0 == strcmp(szOID_COMMON_NAME, g_ExistingCAIdInfo[i].pszObjId)) { g_ExistingCAIdInfo[i].ppwszIdInfo = &pServer->pwszCACommonName; } else if (0 == strcmp(szOID_ORGANIZATION_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead } else if (0 == strcmp(szOID_ORGANIZATIONAL_UNIT_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead } else if (0 == strcmp(szOID_LOCALITY_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead } else if (0 == strcmp(szOID_STATE_OR_PROVINCE_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead } else if (0 == strcmp(szOID_COUNTRY_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead } else if (0 == strcmp(szOID_RSA_emailAddr, g_ExistingCAIdInfo[i].pszObjId)) { // dead } else { hr = E_INVALIDARG; _JumpError(hr, error, "unsupported name"); } ++i; } hr = S_OK; error: return hr; } */ HRESULT DetermineExistingCAIdInfo( IN OUT CASERVERSETUPINFO *pServer, OPTIONAL IN CERT_CONTEXT const *pUpgradeCert) { CERT_NAME_INFO *pCertNameInfo = NULL; DWORD cbCertNameInfo; WCHAR const *pwszNameProp; HRESULT hr = E_FAIL; CERT_CONTEXT const *pCert = pServer->pccExistingCert; CSASSERT(NULL!=pServer->pccExistingCert || NULL != pUpgradeCert); if (NULL == pUpgradeCert) { myMakeExprValidity( &pServer->pccExistingCert->pCertInfo->NotAfter, &pServer->lExistingValidity); pServer->NotBefore = pServer->pccExistingCert->pCertInfo->NotBefore; pServer->NotAfter = pServer->pccExistingCert->pCertInfo->NotAfter; } if (NULL != pUpgradeCert) { pCert = pUpgradeCert; } if (!myDecodeName(X509_ASN_ENCODING, X509_UNICODE_NAME, pCert->pCertInfo->Subject.pbData, pCert->pCertInfo->Subject.cbData, CERTLIB_USE_LOCALALLOC, &pCertNameInfo, &cbCertNameInfo)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeName"); } /* // fill a data structure for existing key id info hr = HookExistingIdInfoData(pServer); _JumpIfError(hr, error, "HookExistingIdInfoData"); // load names from cert to the the data structure i = 0; while (NULL != g_ExistingCAIdInfo[i].pszObjId) { if (S_OK == myGetCertNameProperty( pCertNameInfo, g_ExistingCAIdInfo[i].pszObjId, &pwszNameProp)) { pwszExisting = (WCHAR*)LocalAlloc(LPTR, (wcslen(pwszNameProp) + 1) * sizeof(WCHAR)); _JumpIfOutOfMemory(hr, error, pwszExisting); // get name wcscpy(pwszExisting, pwszNameProp); // make sure free old if (NULL != *(g_ExistingCAIdInfo[i].ppwszIdInfo)) { LocalFree(*(g_ExistingCAIdInfo[i].ppwszIdInfo)); } *(g_ExistingCAIdInfo[i].ppwszIdInfo) = pwszExisting; } ++i; } */ hr = myGetCertNameProperty( FALSE, pCertNameInfo, szOID_COMMON_NAME, &pwszNameProp); if (hr == S_OK) { if(pServer->pwszCACommonName) { LocalFree(pServer->pwszCACommonName); pServer->pwszCACommonName = NULL; } // Common name exists, copy it out hr = myDupString(pwszNameProp, &(pServer->pwszCACommonName)); _JumpIfError(hr, error, "myDupString"); } if(pServer->pwszFullCADN) { LocalFree(pServer->pwszFullCADN); pServer->pwszFullCADN = NULL; } // now get everything else hr = myCertNameToStr( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCert->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_COMMA_FLAG | CERT_NAME_STR_REVERSE_FLAG, &pServer->pwszFullCADN); _JumpIfError(hr, error, "myCertNameToStr"); // No need for a DN suffix, the full DN is already in the cert if(pServer->pwszDNSuffix) { LocalFree(pServer->pwszDNSuffix); pServer->pwszDNSuffix = NULL; } hr = myDupString(L"", &(pServer->pwszDNSuffix)); _JumpIfError(hr, error, "myDupString"); hr = S_OK; error: if (NULL != pCertNameInfo) { LocalFree(pCertNameInfo); } return hr; } //-------------------------------------------------------------------- // find a cert that matches the currently selected CSP and key container name // returns CRYPT_E_NOT_FOUND if no certificate. Caller MUST free the returned // context. // Note: IT IS VERY IMPORTANT that pfx import maintains all the // invariants about CSP, key container, hash, cert validity, etc. // that the rest of the UI (including this function) maintains. HRESULT FindCertificateByKey( IN CASERVERSETUPINFO * pServer, OUT CERT_CONTEXT const ** ppccCertificateCtx) { CSASSERT(NULL!=pServer); CSASSERT(NULL!=ppccCertificateCtx); HRESULT hr; DWORD dwPublicKeySize; BOOL bRetVal; DWORD dwVerificationFlags; CERT_CONTEXT const *pccFound = NULL; // variables that must be cleaned up HCRYPTPROV hProv=NULL; CERT_PUBLIC_KEY_INFO * pcpkiKeyInfo=NULL; CERT_CONTEXT const * pccCurrentCert=NULL; // initialize out param *ppccCertificateCtx=NULL; // open certificate store if it is not already open if (NULL==pServer->hMyStore) { pServer->hMyStore=CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_ENUM_ARCHIVED_FLAG, wszMY_CERTSTORE); if (NULL==pServer->hMyStore) { hr=myHLastError(); _JumpError(hr, error, "CertOpenStore"); } } // // Get public key blob from key container // Note: This may fail if key is not // AT_SIGNATURE, but we will never actually use the key in // this case anyway so it's ok to not find any certs // DBGPRINT(( DBG_SS_CERTOCM, "FindCertificateByKey: key=%ws\n", pServer->pwszKeyContainerName)); // first, open the key container bRetVal=myCertSrvCryptAcquireContext( &hProv, pServer->pwszKeyContainerName, pServer->pCSPInfo->pwszProvName, pServer->pCSPInfo->dwProvType, CRYPT_SILENT, // we should never have to ask anything to get this info pServer->pCSPInfo->fMachineKeyset); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "myCertSrvCryptAcquireContext"); } // get the size of the blob bRetVal=CryptExportPublicKeyInfo( hProv, AT_SIGNATURE, X509_ASN_ENCODING, NULL, //determine size &dwPublicKeySize); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "CryptExportPublicKeyInfo (get data size)"); } // allocate the blob pcpkiKeyInfo=(CERT_PUBLIC_KEY_INFO *)LocalAlloc(LMEM_FIXED, dwPublicKeySize); _JumpIfOutOfMemory(hr, error, pcpkiKeyInfo); // get the public key info blob bRetVal=CryptExportPublicKeyInfo( hProv, AT_SIGNATURE, X509_ASN_ENCODING, pcpkiKeyInfo, &dwPublicKeySize); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "CryptExportPublicKeyInfo (get data)"); } // // Find a certificate that has a matching key, has not expired, // and is self-signed or not self-signed depending upon // the CA type we are trying to install // for (;;) { // find the next cert that has this public key // Note: the function will free the previously // used context when we pass it back pccCurrentCert=CertFindCertificateInStore( pServer->hMyStore, X509_ASN_ENCODING, 0, // flags CERT_FIND_PUBLIC_KEY, pcpkiKeyInfo, pccCurrentCert); // exit the loop when we can find no more matching certs if (NULL == pccCurrentCert) { hr = myHLastError(); if (NULL != pccFound) { break; } _JumpError(hr, error, "CertFindCertificateInStore"); } // check to make sure that the cert has not expired // first, we flag what we want to check dwVerificationFlags = CERT_STORE_TIME_VALIDITY_FLAG; // perform the checks bRetVal=CertVerifySubjectCertificateContext( pccCurrentCert, NULL, // issuer; not needed &dwVerificationFlags); if (FALSE==bRetVal) { _PrintError(myHLastError(), "CertVerifySubjectCertificateContext"); // this should not fail, but maybe we got a bad cert. Keep looking. continue; } // Every check that passed had its flag zeroed. See if our check passed if (CERT_STORE_TIME_VALIDITY_FLAG&dwVerificationFlags){ // This cert is expired and we can't use it. Keep looking. continue; } // verify to make sure no cert in chain is revoked, but don't kill // yourself if can't connect // allow untrusted cert if installing a root hr = myVerifyCertContext( pccCurrentCert, CA_VERIFY_FLAGS_IGNORE_OFFLINE | (IsRootCA(pServer->CAType)? CA_VERIFY_FLAGS_ALLOW_UNTRUSTED_ROOT : 0), 0, NULL, HCCE_LOCAL_MACHINE, NULL, NULL); if (S_OK != hr) { // At least one cert is revoked in chain _PrintError(hr, "myVerifyCertContext"); continue; } // See if this cert appropriately is self-signed or not. // A root CA cert must be self-signed, while // a subordinate CA cert must not be self-signed. hr=IsCertSelfSignedForCAType(pServer, pccCurrentCert, &bRetVal); if (FAILED(hr)) { // this should not fail, but maybe we got a bad cert. Keep looking. _PrintError(hr, "IsCertSelfSignedForCAType"); continue; } if (FALSE==bRetVal) { // this cert is not appropriate for this CA type _PrintError(S_FALSE, "bad CA Type"); continue; } // If we got here, the cert we have is a good one. // If we already found a good cert and this one expires later, // toss the old one and save this one. if (NULL != pccFound) { if (0 > CompareFileTime( &pccCurrentCert->pCertInfo->NotAfter, &pccFound->pCertInfo->NotAfter)) { continue; // old one is newer -- keep it } CertFreeCertificateContext(pccFound); pccFound = NULL; } pccFound = CertDuplicateCertificateContext(pccCurrentCert); if (NULL == pccFound) { hr = myHLastError(); _JumpError(hr, error, "CertDuplicateCertificateContext"); } } // <- End certificate finding loop CSASSERT(NULL != pccFound); *ppccCertificateCtx = pccFound; pccFound = NULL; hr = S_OK; error: if (NULL!=hProv) { CryptReleaseContext(hProv, 0); } if (NULL!=pcpkiKeyInfo) { LocalFree(pcpkiKeyInfo); } if (NULL != pccFound) { CertFreeCertificateContext(pccFound); } if (NULL!=pccCurrentCert) { CertFreeCertificateContext(pccCurrentCert); } if (S_OK!=hr && CRYPT_E_NOT_FOUND!=hr) { _PrintError(hr, "Ignoring error in FindCertificateByKey, returning CRYPT_E_NOT_FOUND") hr=CRYPT_E_NOT_FOUND; } return hr; } //-------------------------------------------------------------------- // Set which existing certificate we want to use HRESULT SetExistingCertToUse( IN CASERVERSETUPINFO * pServer, IN CERT_CONTEXT const * pccCertCtx) { CSASSERT(NULL!=pServer); CSASSERT(NULL!=pccCertCtx); HRESULT hr; CSP_HASH * pHash; // to use an existing cert, we must use an existing key CSASSERT(NULL!=pServer->pwszKeyContainerName); // find the hash algorithm that matches this cert, and use it if possible // otherwise, stick with what we are currently using. hr=FindHashAlgorithm(pccCertCtx, pServer->pCSPInfo, &pHash); if (S_OK==hr) { pServer->pHashInfo = pHash; } hr = myGetNameId(pccCertCtx, &pServer->dwCertNameId); _PrintIfError(hr, "myGetNameId"); if (MAXDWORD == pServer->dwCertNameId) { pServer->dwCertNameId = 0; } ClearExistingCertToUse(pServer); pServer->pccExistingCert=pccCertCtx; // We could assume that everything will work, but it doesn't take long to check //pServer->fValidatedHashAndKey=TRUE; hr=S_OK; //error: return hr; } //-------------------------------------------------------------------- // stop using an existing certificate void ClearExistingCertToUse( IN CASERVERSETUPINFO * pServer) { CSASSERT(NULL!=pServer); if (NULL!=pServer->pccExistingCert) { CertFreeCertificateContext(pServer->pccExistingCert); pServer->pccExistingCert=NULL; } }